Подтвердить что ты не робот

Локализация номеров в веб-приложениях

Как я могу установить вариант арабскую цифру без изменения кодов символов?

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

Вот пример кода:

<!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>

Как это сделать, используя только клиентские технологии (HTML, CSS, JS)?
Решение не должно отрицательно сказываться на странице SEO-оценки.

Обратите внимание, что в текстовых окнах Windows (например, Run) номера отображаются правильно в соответствии с языком окружающего текста.

См. также: Локализация номеров в настольных приложениях

4b9b3361

Ответ 1

Нужно решить, является ли это вопросом внешнего вида или трансформации. Нужно также решить, является ли это вопросом, включающим семантику символьного уровня или числовые представления. Вот мои мысли:


Вопрос имел бы совершенно другую семантику, если бы у нас была ситуация, когда Unicode не разгласил коды для числовых символов. Затем отображение различных глифов, если это было необходимо, просто связано с использованием соответствующего шрифта. С другой стороны, было бы невозможно просто написать разные символы, как я сделал ниже, не меняя шрифтов. (Ситуация не совсем идеальна, поскольку шрифты не обязательно охватывают весь диапазон 16-разрядного Unicode-набора, не говоря уже о 32-разрядном Unicode-наборе.)

9, ٩ (Arabic), ۹ (Urdu), &#29590; (Chinese, complex), ๙ (Thai), ௯ (Tamil) etc.  

Теперь, предполагая, что мы принимаем семантику Unicode, т.е. 9 ', 9 и 9 являются отдельными символами, мы можем заключить, что речь идет не о внешности (что-то, что было бы в поле зрения CSS), а трансформации - несколько соображений об этом позже, а теперь предположим, что это так. Если сосредоточиться на символьной семантике, ситуация не слишком отличается от того, что происходит с алфавитами и буквами. Например, греческий "α" и латинский "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/ Например, если у вас есть поле, содержащее сумму некоторых целых чисел, пользовательские входы, вы можете быть в удивление, когда вы пытаетесь получить его ценность...
  • Он не затрагивает вопрос о том, что происходит внутри элементов inputtextarea). Если поле ввода инициализировано, скажем, "42", оно будет продавать это значение. Это можно легко исправить, но тогда возникает вопрос о фактическом вводе... Можно принять решение об изменении символов по мере их поступления, преобразовать значения при их изменении и т.д. И т.д. Если такое преобразование сделано, то и клиентская сторона, и сторона сервера должны быть готовы иметь дело с разными типами цифр. Что происходит из коробки в Javascript, jQuery и даже с глобализацией (на стороне клиента) и ASP.NET, PHP и т.д. (На стороне сервера), будут разбиты, если их кормить цифрами в нестандартных форматах...

Несколько более комплексное решение (заботясь также об элементах ввода /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-Backward Compatible (требует холста) и т.д.):. Можно было бы относительно легко настроить библиотеку, такую ​​как Cufon, чтобы сделать то, что предусмотрено. Cufon может выполнить свою задачу и нарисовать глифы на объекте canvas, за исключением того, что настройка будет гарантировать, что когда элементы имеют определенное свойство, вместо обычных выбираются нужные глифы. Cufon и другие библиотеки такого типа, как правило, добавляют элементы в DOM и изменяют внешний вид существующих элементов, но не затрагивают их текст, поэтому проблемы с трансформационными подходами не должны применяться. На самом деле интересно отметить, что в то время как (улучшенный) Cufon обеспечивает явно трансформационный подход в отношении общей DOM, это решение на основе внешнего вида, насколько это касается его менталитета; Я бы назвал это гибридным решением.

Альтернативное гибридное решение: Создайте новые элементы DOM с арабским контентом, скройте старые элементы, но оставите их идентификаторы и содержимое неповрежденными. Синхронизируйте арабские элементы контента со своими соответствующими, скрытыми элементами.


Попробуйте подумать вне коробки (поле является текущими веб-стандартами).

Тот факт, что определенные символы уникальны, не означает, что они не связаны друг с другом. Более того, это не обязательно означает, что их различие является одним из видов. Например, "a" и "A" - это одна и та же буква; в некоторых контекстах они считаются одинаковыми, а в других - разными. Имея, различие в Unicode (и ASCII и ISO-Latin-1 и т.д. Перед ним) означает, что для его преодоления требуются некоторые усилия. CSS позволяет быстро и легко изменить регистр букв. Например, body {text-transform:uppercase} превратит все буквы в тексте в теле страницы в верхний регистр. Обратите внимание, что это также случай изменения вида, а не преобразования: DOM элемента body не изменяется, так же, как он отображается.

Примечание.. Если CSS поддерживает что-то вроде numerals-transform: 'ar', это, вероятно, было бы идеальным ответом на вопрос, поскольку он был сформулирован.

Однако, прежде чем мы спешим сообщить комитету CSS о добавлении этой функции, мы можем подумать, что это будет означать. Здесь мы решаем небольшую проблему, но им приходится иметь дело с большой картиной.

Вывод: Будет ли это работать с цифрами-преобразованиями, чтобы "10" (2-символьные) отображались как 十 (китайский, простой), 拾 (китайский, сложный), X (латинский) (все 1-символьный) и т.д., Если вместо ' ar ', были приведены соответствующие аргументы?

Input: Будет ли эта функция преобразования цифр "十" (китайская, простая) в ее арабский эквивалент или просто нацелена на "10"? Будет ли это каким-то умным образом обнаруживать, что "MMXI" (латинская цифра на 2012 год) является числом, а не словом и соответствующим образом конвертирует его?

Вопрос о представлении чисел не так прост, как можно было подумать, просто глядя на этот вопрос.


Итак, где все это оставляет нас:

  • Нет простого решения на основе презентации. Если он появится в будущем, он не будет обратно совместим.
  • Здесь и сейчас может быть трансформационное "решение", но даже если это сделано для работы с элементами формы, как я это делал (http://jsfiddle.net/bKEbR/13/), должен быть сервер -сторонняя и клиентская осведомленность о нестандартном формате.
  • Могут быть сложные гибридные решения. В некоторых случаях они сложны, но в некоторых случаях предлагают некоторые преимущества подходов, основанных на презентации.

Решение с CSS было бы неплохо, но на самом деле проблема большая и сложная, когда вы смотрите на общую картину, которая включает в себя другие числовые системы (с менее тривиальными преобразованиями и от стандартной системы), десятичные точки, знаки и т.д.

В конце концов, решение, которое я вижу как реалистичное и обратное совместимое, было бы продолжением Globalize (и эквивалентов на стороне сервера), возможно, с некоторым дополнительным кодом, чтобы заботиться о вводе пользователя. Идея заключается в том, что это не проблема на уровне персонажа (потому что, как только вы считаете, что это большая картина), и что ее нужно будет обрабатывать так же, как были рассмотрены различия с тысячами и десятичными разделителями: как проблемы форматирования/разбора.

Ответ 2

Вот подход с переключением кода:

// 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);
});  // "۰۱۲۳۴۵۶۷۸۹"

DEMO: http://jsfiddle.net/bKEbR/

Здесь мы используем сдвиг Unicode, поскольку цифры в любой группе Unicode помещаются в том же порядке, что и в латинской группе (т.е. [0x0030 ... 0x0039]). Так, например, для сдвига группы Arab-Indic является 0x0630.

Заметьте, мне сложно различать восточных персонажей, поэтому, если я допустил ошибку (в Юникоде много разных восточных символов), вы всегда можете вычислить сдвиг, используя любую онлайн-таблицу Unicode. Вы можете использовать официальные Unicode Character Code Charts или Unicode Online Chartable.

Ответ 3

Я предполагаю, что лучший способ - использовать regexp для поиска того, какие числовые символы следует изменить, добавив в div значение class name, для которого требуется другое числовой набор.

Вы можете сделать это, используя jQuery довольно легко.

jsfiddle DEMO


РЕДАКТИРОВАТЬ: И если вы не хотите использовать переменную, см. эту новую версию:

jsfiddle DEMO 2

Ответ 4

Я работал над общей техникой локализации веб-страницы, которая делает больше, чем просто номера (это похоже на файлы .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"

Ответ 5

вы можете попробовать следующее: Это исходный код 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 dehhaheto be site ezafe koni".

Ответ 6

Я создал плагин jquery, который может конвертировать западные арабские цифры в восточные (только для персидских). Но его можно расширить, чтобы преобразовать число в любую желаемую систему цифр. Мой плагин jQuery имеет два преимущества:

  • Определить и преобразовать числа правильно в дочерние узлы.
  • Определить и преобразовать символы точки соответствующим образом.

Вы можете клонировать этот плагин из github. Мой код плагина:

(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/

Ответ 7

Вы можете конвертировать числа следующим образом:

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

convertedNumber = String(number).replace(/\d/g, function(digit) {
    return persianDigits[digit]
})
console.log(convertedNumber) // ۴۴۶۵۳۴۲۰