27

如何在不更改字符代码的情况下设置阿拉伯数字的变体?

Eastern Arabic      ۰   ۱   ۲   ۳   ٦   ٥   ٤   ۷   ۸   ۹
Persian variant     ۰   ۱   ۲   ۳   ۴   ۵   ۶   ۷   ۸   ۹
Western Arabic      0   1   2   3   4   5   6   7   8   9 
(And other numeral systems)

这是一个示例代码:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
</head>
<body>

<div lang="fa">0123456789</div>
<div lang="ar">0123456789</div>
<div lang="en">0123456789</div>

</body>
</html>

如何仅使用客户端技术(、、、)来做到HTMLCSS一点JS
该解决方案应该对页面的 SEO 分数没有负面影响。

请注意,在 Windows 文本框(例如运行)中,数字会根据周围文本的语言正确显示。

另请参阅:桌面应用程序中的数字本地化

注意:使用这个 PHP 包https://github.com/salarmehr/cosmopolitan在后端非常容易对数字进行本地化

4

9 回答 9

16

这是一种代码转换的方法:

// Eastern Arabic (officially "Arabic-Indic digits")
"0123456789".replace(/\d/g, function(v) {
    return String.fromCharCode(v.charCodeAt(0) + 0x0630);
});  // "٠١٢٣٤٥٦٧٨٩"

// Persian variant (officially "Eastern Arabic-Indic digits (Persian and Urdu)")
"0123456789".replace(/\d/g, function(v) {
    return String.fromCharCode(v.charCodeAt(0) + 0x06C0);
});  // "۰۱۲۳۴۵۶۷۸۹"

演示:http: //jsfiddle.net/bKEbR/

这里我们使用 Unicode 移位,因为任何 Unicode 组中的数字都按照与拉丁组中相同的顺序放置(即[0x0030 ... 0x0039])。因此,例如,对于阿拉伯语-印度语组移位是0x0630

请注意,我很难区分东方字符,所以如果我弄错了(Unicode 中有许多不同的东方字符组),您总是可以使用任何在线 Unicode 表来计算移位。您可以使用官方Unicode 字符代码图表Unicode Online Chartable

于 2012-12-09T11:35:36.383 回答
6

必须决定这是外观问题还是转变问题。还必须确定这是一个涉及字符级语义还是数字表示的问题。以下是我的想法:


如果我们遇到 Unicode 没有分离出数字字符的代码的情况,那么这个问题将具有完全不同的语义。然后,适当地显示不同的字形只需使用适当的字体即可。另一方面,如果不更改字体,就不可能像我在下面所做的那样简单地写出不同的字符。(这种情况并不完全完美,因为字体不一定涵盖 16 位 Unicode 集的整个范围,更不用说 32 位 Unicode 集了。)

9, ٩ (Arabic), ۹ (Urdu), &#29590; (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 的答案,而不是我的!!!!:-)

这有两个问题:

  1. 它与 DOM 混淆,因此可能会破坏过去假设它会找到“标准”形式的数字(使用数字“0”到“9”)的代码。请参阅此处的问题:http: //jsfiddle.net/bKEbR/10/ 例如,如果您有一个字段包含用户输入的一些整数的总和,那么当您尝试获取它的值时,您可能会感到惊讶。 ..
  2. 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 年的拉丁数字)是一个数字而不是一个单词并相应地转换它吗?

数字表示的问题并不像人们想象的那么简单。


那么,这一切给我们留下了怎样的印象:

  1. 没有简单的基于演示的解决方案。如果将来出现,它将无法向后兼容。
  2. 现在可以有一个变革性的“解决方案”,但即使这也适用于我所做的表单元素(http://jsfiddle.net/bKEbR/13/),也需要服务器端和客户端对所使用的非标准格式的认识。
  3. 可能有复杂的混合解决方案。它们很复杂,但在某些情况下提供了基于表示的方法的一些优点。

一个 CSS 解决方案会很好,但实际上当一个人看到涉及其他数字系统(与标准系统之间的简单转换较少)、小数点、符号等的大图时,问题是大而复杂的。

归根结底,我认为现实且向后兼容的解决方案将是 Globalize(和服务器端等效项)的扩展,可能带有一些额外的代码来处理用户输入。这个想法是,这在字符级别不是问题(因为一旦你考虑大局就不是问题)并且必须以与处理千位和小数分隔符的差异相同的方式处理它:作为格式化/解析问题。

于 2012-12-16T03:30:07.047 回答
2

我一直在研究一种通用的网页本地化技术,它不仅仅是数字(它类似于 .po 文件)

本地化文件很简单(如果需要,字符串可以包含 html)

/* Localization file - save as document_url.lang.js ... index.html.en.js: */
items=[
{"id":"string1","value":"Localized text of string1 here."},
{"id":"string2", "value":"۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ "}
];
rtl=false; /* set to true for rtl languages */

这种格式对于翻译人员(或机械土耳其人)来说非常有用

和一个基本的页面模板

<html><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<head><title>My title</title>
<style>.txt{float:left;margin-left:10px}</style>
</head>
<body onload='setLang()'>
<div id="string1" class="txt">This is the default text of string1.</div>
<div id="string2" class="txt">0 1 2 3 4 5 6 7 8 9 </div>
</body></html>
<script>
   function setLang(){
      for(var i=0;i<items.length;i++){
         term=document.getElementById(items[i].id)
         if(term)term.innerHTML=items[i].value
         if(rtl){  /* for rtl languages */ 
            term.style.styleFloat="right"
            term.style.cssFloat="right"
            term.style.textAlign="right"
         }
      }
   }
   var lang=navigator.userLanguage || navigator.language;
   var script=document.createElement("script");
   script.src=document.URL+"-"+lang.substring(0,2)+".js"
   var head = document.getElementsByTagName('head')[0]
   head.insertBefore(script,head.firstChild)
</script>

我试图保持它非常简单,但覆盖尽可能多的语言环境,因此可能需要额外的 css(我不得不承认缺乏对 rtl 语言的接触,因此可能需要设置更多样式)

如果您知道哪些字体可以很好地支持您的字符代码,我确实有字体检查代码会很有用

function hasFont(f){
    var s=document.createElement("span")
    s.style.fontSize="72px"
    s.innerHTML="MWMWM"
    s.style.visibility="hidden"
    s.style.fontFamily=[(f=="monospace")?'':'monospace','sans-serif','serif']
    document.body.appendChild(s)
    var w=s.offsetWidth
    s.style.fontFamily=[f,'monospace','sans-serif','serif']
    document.body.lastChild=s
    return s.offsetWidth!=w
}

用法:if(hasFont("myfont"))myelement.style.fontFamily="myfont"

于 2012-12-14T20:56:50.593 回答
2

我想最好的方法是使用 aregexp来搜索应该通过将 a 添加class name到需要不同数字集的 div 来更改哪些数字字符。

你可以很容易地使用 jQuery 来做到这一点。

jsfiddle 演示


编辑:如果您不想使用变量,请查看此修改后的演示:

jsfiddle 演示 2

于 2012-12-09T09:16:04.723 回答
1

你可以试试这个: 这是 CSS 源代码:

@font-face
 {
   font-family: A1Tahoma;
    src: url(yourfont.eot) format('eot')
    , url(yourfont.ttf) format('truetype')
   , url(yourfont.woff) format('woff')
    , url(yourfont.svg) format('svg');
}

p{font-family:A1Tahoma; font-size:30px;}  

这是 HTML 代码:

<p>سلام به همه</p>

<p>1234567890</p>  

最后你会看到你的结果。记住 4 种字体类型可用于任何浏览器,如 IE、FIREFOX 等。
“salam reza , to mituni in karo anjam bedi ta un fonte dekhaheto be site ezafe koni。”

于 2012-12-09T10:16:10.650 回答
1

一个新的(迄今为止)和简单的 JS 解决方案是使用Intl.NumberFormat。它支持数字本地化、格式变化以及本地货币(有关更多示例,请参阅文档)。

使用与 MDN 自己的非常相似的示例:

const val = 1234567809;
console.log('Eastern Arabic (Arabic-Egyptian)', new Intl.NumberFormat('ar-EG').format(val));
console.log('Persian variant (Farsi)',new Intl.NumberFormat('fa').format(val));
console.log('English (US)',new Intl.NumberFormat('en-US').format(val));

Intl.NumberFormat 似乎也支持字符串数值,并指示它何时不是本地语言中的数字。

const val1 = '456';
const val2 = 'Numeric + string example, 123';
console.log('Eastern Arabic', new Intl.NumberFormat('ar-EG').format(val1));
console.log('Eastern Arabic', new Intl.NumberFormat('ar-EG').format(val2));
console.log('Persian variant',new Intl.NumberFormat('fa').format(val1));
console.log('Persian variant',new Intl.NumberFormat('fa').format(val2));
console.log('English',new Intl.NumberFormat('en-US').format(val1));
console.log('English', new Intl.NumberFormat('en-US').format(val2));

对于语言环境标识符(传递给NumberFormat构造函数的指示语言环境的字符串),我尝试了上面的值,它们看起来很好。我尝试找到所有可能值的列表,并通过 MDN 找到了这个文档这个可能有用的列表。

我不熟悉 SEO,因此不确定这如何回答这部分问题。

于 2021-04-29T20:15:30.983 回答
0

I have created a jquery plugin that can convert Western Arabic numbers to Eastern ones (Persian only). But it can be extended to convert a number to any desired numeral system. My jQuery plugin has two advantages:

  1. Detect and convert numbers properly in child nodes.
  2. Detect and convert point characters appropriately.

You can clone this plugin from github. My plugin code:

(function( $ ){
  $.fn.persiaNumber = function() {
      var groupSelection = this;
      for(i=0; i< groupSelection.length ; i++){
          var htmlTxt = $(groupSelection[i]).html();
          var trueTxt = convertDecimalPoint(htmlTxt);
          trueTxt = convertToPersianNum(trueTxt);
          $(groupSelection[i]).html(trueTxt);
      }
function convertToPersianNum(htmlTxt){
          var otIndex = htmlTxt.indexOf("<");
          var ctIndex = htmlTxt.indexOf(">");
          if(otIndex == -1 && ctIndex == -1 && htmlTxt.length > 0){
            var trueTxt = htmlTxt.replace(/1/gi, "۱").replace(/2/gi, "۲").replace(/3/gi, "۳").replace(/4/gi, "۴").replace(/5/gi, "۵").replace(/6/gi, "۶").replace(/7/gi, "۷").replace(/8/gi, "۸").replace(/9/gi, "۹").replace(/0/gi, "۰");
            return trueTxt;
          }
              var tag = htmlTxt.substring(otIndex,ctIndex + 1);
              var str = htmlTxt.substring(0,otIndex);
              str = convertDecimalPoint(str);
              str = str.replace(/1/gi, "۱").replace(/2/gi, "۲").replace(/3/gi, "۳").replace(/4/gi, "۴").replace(/5/gi, "۵").replace(/6/gi, "۶").replace(/7/gi, "۷").replace(/8/gi, "۸").replace(/9/gi, "۹").replace(/0/gi, "۰");
              var refinedHtmlTxt = str + tag;
              var htmlTxt = htmlTxt.substring(ctIndex + 1, htmlTxt.length);
              if(htmlTxt.length > 0 && otIndex != -1 || ctIndex != -1){
                  var trueTxt = refinedHtmlTxt;
                  var trueTxt =  trueTxt + convertToPersianNum(htmlTxt); 
              }else{
                    return refinedHtmlTxt+ htmlTxt;
              }
              return trueTxt;         

    }
function convertDecimalPoint(str){
    for(j=1;j<str.length - 1; j++){
        if(str.charCodeAt(j-1) > 47 &&  str.charCodeAt(j-1) < 58 && str.charCodeAt(j+1) > 47 &&  str.charCodeAt(j+1) < 58 && str.charCodeAt(j) == 46)
            str = str.substring(0,j) + '٫' + str.substring(j+1,str.length);
    }
    return str;
}
};
})( jQuery );

http://jsfiddle.net/VPWmq/2/

于 2012-12-09T15:51:37.840 回答
0

如果有人正在寻找使用代码转换方法本地化为孟加拉数字的人:

    $("[lang='bang']").text(function(i, val) {
        return val.replace(/\d/g, function(v) {
            return String.fromCharCode(v.charCodeAt(0) + 0x09B6);
        });
    });

您也可以访问这里查看孟加拉语的 ASCII 十六进制代码的 UNICODE

于 2020-12-13T12:22:16.830 回答
0

您可以通过这种方式转换数字:

const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
const number = 44653420;

convertedNumber = String(number).replace(/\d/g, function(digit) {
    return persianDigits[digit]
})
console.log(convertedNumber) // ۴۴۶۵۳۴۲۰
于 2019-07-21T07:58:33.233 回答