需要决定的是这个问题涉及外观还是转换。还需要确定这是否涉及字符级语义或数字表示。以下是我的想法:
如果Unicode没有将数字符号分开,那么这个问题就会具有完全不同的语义。此时,显示适当的字形只是使用适当字体的问题。另一方面,如果不更改字体,我无法像下面那样简单地将不同的字符写出。(情况并非完美,因为字体不一定覆盖16位Unicode集的整个范围,更不用说32位Unicode集了。)
9, ٩ (Arabic), ۹ (Urdu), 玖 (Chinese, complex), ๙ (Thai), ௯ (Tamil) etc.
假设我们接受Unicode语义,即 '9','٩'和'۹'是不同的字符,我们可以得出结论,问题不在于外观(这是CSS所涵盖的范畴),而在于转换-稍后我们会讨论一些想法,现在假设这是情况。 在关注字符级语义时,情况与字母表和字母发生的情况并没有太大差异。例如,希腊字母'α'和拉丁字母'a'被认为是不同的,即使拉丁字母表与在欧布阿使用的希腊字母表几乎相同。也许更具戏剧性的是,对应的大写变体'Α'(希腊)和'A'(拉丁)在几乎支持两种脚本的所有字体中都是视觉上相同的,但在Unicode方面是不同的。
规则已经说明,现在看看如何回答问题,方法是忽略它们,特别是忽略(字符级)Unicode语义。
(可怕、恶心和不向后兼容的)解决方案:使用将“0”到“9”映射到所需字形的字体。我不知道有这样的字体。您必须使用@font-face和一些已被适当破解以实现您想要的功能的字体。不用说,我对这个解决方案并不特别喜欢。但是,这是我所知道的唯一一个简单解决问题的方法,而不会在服务器或客户端上更改字符代码。 (严格来说,我下面提出的Cufon解决方案也不会更改字符代码,但是它所做的,将文本绘制到画布中,要复杂得多,并且需要调整开源代码)。
注意:任何转换解决方案,即将“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 的答案点赞,而不是我的! :-)
这有两个问题:
1. 它会干扰 DOM,因此可能会破坏原本能够在“标准”形式(使用数字 '0' 到 '9')中找到数字的代码。请参见此处的问题:
http://jsfiddle.net/bKEbR/10/例如,如果您有一个包含用户输入的整数之和的字段,则在尝试获取其值时可能会出现意外情况...
2. 它没有解决
input
(和
textarea
)元素内部发生的问题。如果一个输入字段初始化为,比如说,“42”,它将保留该值。这可以很容易地修复,但随后就会涉及实际输入的问题...人们可能会决定更改随着时间推移而到来的字符,转换值以及诸如此类的操作。如果进行了这样的转换,则客户端和服务器端都需要准备好处理不同种类的数字。JavaScript、jQuery 甚至 Globalize(客户端)以及 ASP.NET、PHP 等(服务器端)提供的内容,如果输入非标准格式的数字,将会出现问题...
稍微更全面的解决方案(还考虑了 input/textarea 元素,包括它们的初始值和用户输入)可能是:
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);
}
$("[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)});
alert("After: test holds the value:" +parseInt($("#test1").text()))
整个jsFiddle可以在这里找到:
http://jsfiddle.net/bKEbR/13/
不用说,这只是部分解决上述问题。客户端和/或服务器端代码必须识别非标准数字,并适当地将它们转换为标准格式或其实际值。
这不是几行javascript可以解决的简单问题。而且,这只是这种可能的转换的最简单情况,因为需要应用简单的字符对字符映射才能从一种数字形式转换为另一种数字形式。
另一个外观为基础的方法:
Cufon解决方案(过度设计,不向后兼容(需要画布)等):人们可以相对容易地调整像Cufon这样的库以完成所需的工作。 Cufon可以在画布对象上执行其操作并绘制字形,除了调整将确保当元素具有某些属性时,将使用所需的字形而不是通常选择的字形。 Cufon和其他此类库往往会向DOM添加元素并更改现有元素的外观,但不会触及其文本,因此转换方法的问题不应适用。 实际上,有趣的是要注意,虽然(调整后的)Cufon在整个DOM方面提供了明显的转换方法,但从其思维方式来看,它是一种基于外观的解决方案;我将其称为混合解决方案。
另一种混合解决方案:创建具有阿拉伯内容的新DOM元素,隐藏旧元素但保留其ID和内容。 将阿拉伯语内容元素与其对应的隐藏元素同步。
让我们尝试跳出当前的Web标准(所谓的“盒子”)。
某些字符是唯一的并不意味着它们无关。此外,这并不一定意味着它们的区别在于外观。例如,“a”和“A”是相同的字母;在某些情况下,它们被认为是相同的,在其他情况下则被认为是不同的。Unicode(以及之前的ASCII和ISO-Latin-1等)中的区别意味着需要付出一些努力才能克服它。
CSS提供了一种快速简便的方法来更改字母的大小写。例如,
body {text-transform:uppercase}
会将页面正文中的所有字母都转换为大写。请注意,这也是外观更改而不是转换:body元素的DOM不会改变,只是呈现方式改变。
注意:如果CSS支持类似于
numerals-transform:'ar'
这样的东西,那可能就是根据问题的提法的理想答案。
然而,在我们急切地要求CSS委员会添加此功能之前,我们可能需要考虑一下这意味着什么。在这里,我们正在解决一个微小的问题,但他们必须处理整体情况。
输出:
如果给定适当的参数,这种数字转换功能是否允许'10'(2个字符)显示为十(中文,简体),拾(中文,繁体),X(拉丁)(所有1个字符)等?
输入:
这种数字转换功能是否会将“十”(中文,简体)更改为其阿拉伯语等价物,还是仅针对“10”?它是否会聪明地检测到“MMXI”(2012年的拉丁数字)是一个数字而不是单词,并相应地进行转换?
数字表示的问题并不像看起来那么简单,这需要我们认真考虑。
所以,这一切都给了我们什么启示:
- 没有简单的基于表现的解决方案。即使未来出现了这样的解决方案,也将无法向后兼容。
- 可以进行转换“解决方案”,但即使像我一样使其与表单元素一起工作(http://jsfiddle.net/bKEbR/13/),需要服务器端和客户端意识到使用的非标准格式。
- 可能存在复杂的混合解决方案。它们是复杂的,但在某些情况下提供了基于表现方法的某些优势。
CSS解决方案是不错的,但实际上,当我们考虑涉及其他数字系统(从和标准系统的转换不那么琐碎)的大局面、小数点、符号等时,问题就变得更加复杂。
最终我认为现实且向后兼容的解决方案将是扩展Globalize(和服务器端等效项),可能需要一些额外的代码来处理用户输入。思想是这不是一个字符级的问题(因为一旦你考虑到整个情况,它就不是),而且它将被视为与千位分隔符和小数点的差异一样处理:作为格式化/解析问题。