必须决定这是外观问题还是转变问题。还必须确定这是一个涉及字符级语义还是数字表示的问题。以下是我的想法:
如果我们遇到 Unicode 没有分离出数字字符的代码的情况,那么这个问题将具有完全不同的语义。然后,适当地显示不同的字形只需使用适当的字体即可。另一方面,如果不更改字体,就不可能像我在下面所做的那样简单地写出不同的字符。(这种情况并不完全完美,因为字体不一定涵盖 16 位 Unicode 集的整个范围,更不用说 32 位 Unicode 集了。)
9, ٩ (Arabic), ۹ (Urdu), 玖 (Chinese, complex), ๙ (Thai), ௯ (Tamil) etc.
现在,假设我们接受 Unicode 语义,即 '9' 、'٩' 和 '۹' 是不同的字符,我们可以得出结论,问题不在于外观(本应属于 CSS 的范围),而在于转换——稍后对此有一些想法,现在让我们假设是这种情况。当关注字符级语义时,情况与字母表和字母的情况并没有太大的不同。例如,希腊语“α”和拉丁语“a”被认为是不同的,尽管拉丁字母表与 Euboea 中使用的希腊字母表几乎相同。或许更引人注目的是,对应的大写变体“Α”(希腊语)和“A”(拉丁语)在几乎所有支持这两种脚本的字体中在视觉上都是相同的,
在陈述了基本规则之后,让我们看看如何通过忽略它们来回答问题,特别是忽略(字符级)Unicode 语义。
(可怕、讨厌和不向后兼容) 解决方案: 使用将“0”到“9”映射到所需字形的字体。我不知道有任何这样的字体。你必须使用@font-face 和一些被适当破解的字体来做你想做的事。
不用说,我不是特别喜欢这个解决方案。但是,这是我知道的唯一简单的解决方案,它可以在服务器或客户端“不更改字符代码”的情况下完成问题。(从技术上讲,我在下面提出的 Cufon 解决方案也不会更改字符代码,但它所做的是将文本绘制到画布中要复杂得多,并且还需要调整开源代码)。
注意: 任何转换解决方案,即任何更改 DOM 并将“0”到“9”范围内的字符替换为,例如,它们的阿拉伯等价物的解决方案都会破坏希望数字以原始形式出现在 DOM 中的代码。当然,在讨论表单和输入时,这个问题是最糟糕的。
采用转型方法的答案示例是:
$("[lang='fa']").find("*").andSelf().contents().each(function() {
if (this.nodeType === 3)
{
this.nodeValue = this.nodeValue.replace(/\d/g, function(v) {
return String.fromCharCode(v.charCodeAt(0) + 0x0630);
});
}
});
注意:代码取自 VisioN 的第二个 jsFiddle。如果这是您喜欢此答案的唯一部分,请确保您支持 VisioN 的答案,而不是我的!!!!:-)
这有两个问题:
- 它与 DOM 混淆,因此可能会破坏过去假设它会找到“标准”形式的数字(使用数字“0”到“9”)的代码。请参阅此处的问题:http: //jsfiddle.net/bKEbR/10/ 例如,如果您有一个字段包含用户输入的一些整数的总和,那么当您尝试获取它的值时,您可能会感到惊讶。 ..
input
它没有解决(and textarea
) 元素内部发生的事情的问题。如果输入字段初始化为“42”,它将零售该值。这可以很容易地解决,但随后存在实际输入的问题......人们可能会决定在字符出现时更改字符,在字符更改时转换值等等。如果进行了这样的转换,那么客户端和服务器端都需要准备好处理不同类型的数字。如果输入非标准格式的数字,Javascript、jQuery 甚至 Globalize(客户端)和 ASP.NET、PHP 等(服务器端)中开箱即用的内容将会中断......
一个稍微更全面的解决方案(还要注意 input/textarea 元素,包括它们的初始值和用户输入)可能是:
//before the DOM change, test1 holds a numeral parseInt can understand
alert("Before: test holds the value:" +parseInt($("#test1").text()));
function convertNumChar(c) {
return String.fromCharCode(c.charCodeAt(0) + 0x0630);
}
function convertNumStr(s) {
return s.replace(/\d/g, convertNumChar);
}
//the change in the DOM
$("[lang='fa']").find("*").andSelf().contents()
.each(function() {
if (this.nodeType === 3)
this.nodeValue = convertNumStr(this.nodeValue);
})
.filter("input:text,textarea")
.each(function() {
this.value = convertNumStr(this.value)
})
.change(function () {this.value = convertNumStr(this.value)});
//test1 now holds a numeral parseInt cannot understand
alert("After: test holds the value:" +parseInt($("#test1").text()))
整个 jsFiddle 可以在这里找到:http: //jsfiddle.net/bKEbR/13/
不用说,这只是部分地解决了上述问题。客户端和/或服务器端代码必须识别非标准数字并将它们适当地转换为标准格式或它们的实际值。
这不是几行javascript就能解决的简单问题。这只是这种可能转换的最简单情况,因为需要应用简单的字符到字符映射才能从一种数字形式转换到另一种形式。
另一个基于外观的方法:
基于 Cufon 的解决方案(Overkill、Non-Backwards Compatible(需要画布)等): 可以相对轻松地调整像 Cufon 这样的库来完成设想的工作。Cufon 可以做它的事情并在画布对象上绘制字形,除了调整将确保当元素具有特定属性时,将使用所需的字形而不是通常选择的字形。Cufon 和其他同类库倾向于将元素添加到 DOM 并更改现有元素的外观但不触及其文本,因此不应该应用转换方法的问题。事实上,有趣的是,虽然(经过调整的)Cufon 就整体 DOM 而言提供了一种明显的转型方法,但就其思维方式而言,它是一种基于外观的解决方案;我将其称为混合解决方案。
替代混合解决方案: 使用阿拉伯文内容创建新的 DOM 元素,隐藏旧元素但保持其 id 和内容不变。将阿拉伯语内容元素与其对应的隐藏元素同步。
让我们尝试跳出框框思考(框框是当前的网络标准)。
某些字符是独一无二的这一事实并不意味着它们是不相关的。而且,这并不一定意味着它们的区别是一种外观。例如,“a”和“A”是同一个字母;在某些情况下,它们被认为是相同的,而在另一些情况下则不同。有了,Unicode(以及之前的 ASCII 和 ISO-Latin-1 等)的区别意味着需要付出一些努力来克服它。CSS 提供了一种快速简便的方法来更改字母的大小写。例如,body {text-transform:uppercase}
会将页面正文中的文本中的所有字母转换为大写。请注意,这也是外观更改而不是转换的情况:body 元素的 DOM 不会改变,只是呈现方式。
注意:如果 CSS 支持类似的东西numerals-transform: 'ar'
,那么它可能是问题的理想答案。
但是,在我们急于告诉 CSS 委员会添加此功能之前,我们可能需要考虑一下这意味着什么。在这里,我们正在解决一个小问题,但他们必须处理大局。
输出:这个数字转换功能是否允许“10”(2 个字符)显示为十(中文,简单)、拾(中文,复杂)、X(拉丁文)(全 1 个字符)等等'ar',给出了适当的论据?
输入:这个数字转换特征会将“十”(中文,简单)转换为对应的阿拉伯语,还是只是针对“10”?它会以某种方式巧妙地检测到“MMXI”(2012 年的拉丁数字)是一个数字而不是一个单词并相应地转换它吗?
数字表示的问题并不像人们想象的那么简单。
那么,这一切给我们留下了怎样的印象:
- 没有简单的基于演示的解决方案。如果将来出现,它将无法向后兼容。
- 现在可以有一个变革性的“解决方案”,但即使这也适用于我所做的表单元素(http://jsfiddle.net/bKEbR/13/),也需要服务器端和客户端对所使用的非标准格式的认识。
- 可能有复杂的混合解决方案。它们很复杂,但在某些情况下提供了基于表示的方法的一些优点。
一个 CSS 解决方案会很好,但实际上当一个人看到涉及其他数字系统(与标准系统之间的简单转换较少)、小数点、符号等的大图时,问题是大而复杂的。
归根结底,我认为现实且向后兼容的解决方案将是 Globalize(和服务器端等效项)的扩展,可能带有一些额外的代码来处理用户输入。这个想法是,这在字符级别不是问题(因为一旦你考虑大局就不是问题)并且必须以与处理千位和小数分隔符的差异相同的方式处理它:作为格式化/解析问题。