З браузером, як мені дізнатися, який десятковий роздільник використовує операційна система?


82

Я розробляю веб-додаток.

Мені потрібно правильно відобразити деякі десяткові дані, щоб їх можна було скопіювати та вставити в певну GUIпрограму, яка не знаходиться під моїм контролем.

Додаток графічного інтерфейсу чутливий до мови і приймає лише правильний десятковий роздільник, встановлений у системі.

Я можу здогадатися про десятковий роздільник Accept-Languageі припущення буде правильним у 95% випадків, але іноді це не вдається.

Чи є спосіб зробити це на стороні сервера (бажано, щоб я міг збирати статистику) або на стороні клієнта?

Оновлення:

Вся справа в завданні - виконувати його автоматично.

Насправді цей веб-додаток є своєрідним онлайн-інтерфейсом до застарілого графічного інтерфейсу, який допомагає правильно заповнювати форми.

Користувачі, які використовують його, здебільшого не уявляють, що таке десятковий роздільник.

Accept-LanguageРішення реалізовано і працює, але я хотів би, щоб поліпшити його.

Оновлення2:

Мені потрібно отримати дуже конкретне налаштування: встановлено десятковий роздільник Control Panel / Regional and Language Options / Regional Options / Customize.

Я маю справу з чотирма видами операційних систем:

  1. Російська Windows з комою як DS (80%).
  2. Англійська Windows з крапкою як DS (15%).
  3. Російська Windows з періодом DS, щоб погано писати англійські програми працювали (4%).
  4. Англійська Windows з комою як DS для роботи погано написаних російських додатків (1%).

Усі 100% клієнтів перебувають у Росії, і застаріла заявка стосується російських державних форм, тому запит на країну дасть 100% Російської Федерації, а GeoIP 80% Російської Федерації та 20% інших (неправильно) відповіді.

Відповіді:


129

Ось проста функція JavaScript, яка поверне цю інформацію. Перевірено у Firefox, IE6 та IE7. Мені довелося закрити та перезапустити браузер між кожною зміною налаштувань у Панелі управління / Регіональні та Мовні параметри / Регіональні параметри / Налаштування. Однак він підбирав не лише кому та крапку, але й дивні звичні речі, наприклад букву "а".

function whatDecimalSeparator() {
    var n = 1.1;
    n = n.toLocaleString().substring(1, 2);
    return n;
}

Це допомагає?


3
Це працювало для мене у Firefox та IE8, але не Google Chrome. У мене немає Опери.
Метью Талберт,

@Matthew - працював у мене в Chrome. @Quassnoi - Я не розумію, що означає цей останній коментар. Якщо він каже, що функція не працює, то яке значення має те, що людина знає?
Matchu

Стережись! У Chrome toLocaleString працює належним чином, лише якщо його викликати безпосередньо на номер. У моїй системі: [1.1,1.2] .toLocaleString () -> "1.1,1.2" | (1.1) .toLocaleString () -> "1,1"
Тарней Калман

1
Помилка функції в локалях, які використовують більше одного символу для їх DecimalSeparator(наприклад ,,). Windows LOCALE_SDECIMALдозволяє десятковий роздільник містити до трьох символів. (Саме тому він не працює на моєму ПК). Accept-LanguageВ такому випадку краще використовувати браузер. Що все ще не враховує здатність визначати власні, DecimalSeparatorнаприклад\o/
Ян Бойд

4
@IanBoyd має рацію щодо локалей із рядком більше одного символу як десятковий роздільник, але n = /^1(.+)1$/.exec(n.toLocaleString())[1]зробив би це, і це простіше, ніж використання Accept-Languageзаголовка.
ygormutti

16

Отримати роздільники для поточної або заданої мови можна за допомогою Intl.NumberFormat#formatToParts.

function getDecimalSeparator(locale) {
    const numberWithDecimalSeparator = 1.1;
    return Intl.NumberFormat(locale)
        .formatToParts(numberWithDecimalSeparator)
        .find(part => part.type === 'decimal')
        .value;
}

Він працює лише для браузерів, що підтримують Intl API . В іншому випадку для цього потрібен Intl polyfill

Приклади:

> getDecimalSeparator()
"."
> getDecimalSeparator('fr-FR')
","

Бонус:

Ми могли б розширити його, щоб отримати десятковий або груповий роздільник даної локалі:

function getSeparator(locale, separatorType) {
        const numberWithGroupAndDecimalSeparator = 1000.1;
        return Intl.NumberFormat(locale)
            .formatToParts(numberWithGroupAndDecimalSeparator)
            .find(part => part.type === separatorType)
            .value;
    }

Приклади:

> getSeparator('en-US', 'decimal')
"."
> getSeparator('en-US', 'group')
","
> getSeparator('fr-FR', 'decimal')
","
> getSeparator('fr-FR', 'group')
" "

Наразі це найбільш правильний спосіб отримати таку інформацію. BTW Intlпідтримується навіть в IE 11: caniuse.com/#feat=internationalization
Костянтин Смолянін

3
Це не буде працювати в IE 11, оскільки formatToParts не підтримується.
Gajendra Kumar

11

Запитайте користувача, не вгадуйте. Установіть це налаштування у своєму веб-додатку.

Відредаговано, щоб додати:

Я думаю, це нормально вгадувати налаштування за замовчуванням , які працюють нормально, скажімо, 95% часу. Я мав на увазі те, що користувач все одно повинен мати змогу замінити будь-які здогади, зроблені програмним забезпеченням. Я вже був розчарований занадто багато разів, коли програмне забезпечення намагається бути надто розумним і не дозволяє виправити.


Смішно, це була моя перша ідея, але я
переборщив,

1
Погана ідея, за винятком, можливо, як резервної копії. Більшість користувачів не чутливі до культури амбарасингії і навіть не розуміють, що таке "десятковий роздільник" без пояснень (а потім будуть озлоблені тим, що їх змусять встановити щось, що "всі знають").
Майкл Борґвардт

2
@Iaalto: це зробить питання майже таким самим великим, як "Зменшити розмір бази даних (рекомендується) або Збільшити можливості пошуку?"
Quassnoi

Ну, це не повинно бути надто складно. Просто дозвольте користувачеві вибрати країну, а потім відповідно вибрати роздільник для осушення та інші параметри.

7
function getDecimalSeparator() {
    //fallback  
       var decSep = ".";

        try {
            // this works in FF, Chrome, IE, Safari and Opera
            var sep = parseFloat(3/2).toLocaleString().substring(1,2);
            if (sep === '.' || sep === ',') {
                decSep = sep;
            }
        } catch(e){}

        return decSep;
    }

1
відновлення до "." у випадку з деякими неясними браузерами ... іншими способами це майже те саме ...
Dr.Oblak


5

Я можу здогадатися про десятковий роздільник від Accept-Language, і в 95% випадків припущення буде правильним, але іноді це не вдається.

Це найкращий курс дій ІМО. Щоб усунути несправності, додайте посилання, щоб встановити його вручну біля області відображення.


Як це зробити? Я розумію, ви можете використовувати бібліотеку браузера, як ця github.com/dansingerman/jQuery-Browser-Language
Lime

@William: Мова Acccept, про яку говорить OP, - це заголовок HTTP, надісланий браузером, який повідомляє серверу про те, якою мовою користувач віддає перевагу, як правило, мовою встановлення браузера або ОС.
Michael Borgwardt

4

Використовуючи відповіді інших людей, я скомпонував такі функції утиліти з роздільниками та тисячами:

var decimalSeparator = function() {
    return (1.1).toLocaleString().substring(1, 2);
};
var thousandSeparator = function() {
    return (1000).toLocaleString().substring(1, 2);
};

Насолоджуйтесь!


Так, я використовував цей метод як поліфіл для браузерів, які не підтримують formatToParts(Safari та IE).
Marko Bonaci

1
У деяких регіонах не використовується тисяча роздільників нижче 10000. Наприклад(1000).toLocaleString("es-PE") # "1000"
Мадаколь

1

Я думаю, що вам потрібно покластись на JavaScript, щоб надати вам налаштування мови.
Але очевидно JS не має прямого доступу до цієї інформації.
Я бачу, що Dojo Toolkit покладається на зовнішню базу даних для пошуку інформації про локаль, хоча, можливо, вона не враховує зміни налаштувань обліку, наприклад.
Ще одним обхідним шляхом, яке я бачу, є наявність невеликого безшумного аплету Java, який запитує цю інформацію із системи, і JavaScript, щоб вивести її з Java.
Я можу надати більше інформації, якщо ви не знаєте, як це зробити (звичайно, якщо ви хочете піти цим заплутаним маршрутом).

[EDIT] Тож я оновив свої знання про підтримку локалізації в Java ...
На відміну від того, що я спочатку думав, у вас не буде безпосередньо десяткового роздільника або тисячі роздільних символів, як це було б зроблено з роздільником рядків або роздільником шляху: натомість Java пропонує API для форматування номерів або дат, які ви надаєте.
Якось це має сенс: в Європі ви часто ставите символ валюти після цифри, у деяких країнах (Індія?) Існує більш складне правило розділення цифр тощо.

Інша справа: Java правильно знаходить поточний регіон із системи, але не бере звідти інформацію (можливо, з вищевказаних причин). Натомість він використовує власний набір правил. Отже, якщо у вас є іспанська мова, де ви замінили десятковий роздільник знаком оклику, Java не використовуватиме його (але, можливо, ні вашу програму, так чи інакше ...).

Отже, я пишу аплет, який виставляє послугу (функції) на JavaScript, дозволяючи форматувати номери в поточній локалі. Ви можете використовувати його як такий, використовуючи JavaScript для форматування чисел у браузері. Або ви можете просто подати його з деяким номером зразка і витягти звідти символи, використовуючи їх локально або повертаючи назад на сервер.

Я закінчую і тестую свій аплет і незабаром розміщую його там.


@PhiLho: було б приємно знати. Цей веб-додаток є своєрідною довідковою системою, тому будь-який потворний хакер буде робити, він не повинен бути елегантним, поки він працює на IE, Firefox та Opera.
Quassnoi

1

Добре, мені є що показати, більше доказ концепції, аніж готовий продукт, але через відсутність точних специфікацій я залишаю це так (або я буду надто інженерним). Я публікую в окремому повідомленні, оскільки це буде трохи довгим. Я скористався можливістю спробувати трохи більше jQuery ...

Код Java: GetLocaleInfo.java

import java.applet.*;
import java.util.Locale;
import java.text.*;

public class GetLocaleInfo extends Applet
{
  Locale loc;
  NumberFormat nf;
  NumberFormat cnf;
  NumberFormat pnf;

  // For running as plain application
  public static void main(String args[])
  {
    final Applet applet = new GetLocaleInfo();
    applet.init();
    applet.start();
  }

  public void init() // Applet is loaded
  {
    // Use current locale
    loc = Locale.getDefault();
    nf = NumberFormat.getInstance();
    cnf = NumberFormat.getCurrencyInstance();
    pnf = NumberFormat.getPercentInstance();
  }

  public void start() // Applet should start
  {
    // Following output goes to Java console
    System.out.println(GetLocaleInformation());
    System.out.println(nf.format(0.1));
    System.out.println(cnf.format(1.0));
    System.out.println(pnf.format(0.01));
  }

  public String GetLocaleInformation()
  {
    return String.format("Locale for %s: country=%s (%s / %s), lang=%s (%s / %s), variant=%s (%s)",
        loc.getDisplayName(),
        loc.getDisplayCountry(),
        loc.getCountry(),
        loc.getISO3Country(),

        loc.getDisplayLanguage(),
        loc.getLanguage(),
        loc.getISO3Language(),

        loc.getDisplayVariant(),
        loc.getVariant()
    );
  }

  public String FormatNumber(String number)
  {
    double value = 0;
    try
    {
      value = Double.parseDouble(number);
    }
    catch (NumberFormatException nfe)
    {
      return "!";
    }
    return nf.format(value);
  }

  public String FormatCurrency(String number)
  {
    double value = 0;
    try
    {
      value = Double.parseDouble(number);
    }
    catch (NumberFormatException nfe)
    {
      return "!";
    }
    return cnf.format(value);
  }

  public String FormatPercent(String number)
  {
    double value = 0;
    try
    {
      value = Double.parseDouble(number);
    }
    catch (NumberFormatException nfe)
    {
      return "!";
    }
    return pnf.format(value);
  }
}

Приклад HTML-сторінки з використанням наведеного вище аплету: GetLocaleInfo.html

<!-- Header skipped for brevity -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.js"></script>
<script type="text/javascript">
var applet;
$(document).ready(function()
{
  applet = document.getElementById('LocaleInfo');
  $('#Results').text(applet.GetLocaleInformation());
});
</script>
<script type="text/javascript">
function DoFormatting()
{
  $('table.toFormat').each(function()
  {
    var table = $(this);
    $('td', table).each(function(cellId)
    {
      var val = $(this);
      if (val.is('.number'))
      {
        val.text(applet.FormatNumber(val.text()));
      }
      else if (val.is('.currency'))
      {
        val.text(applet.FormatCurrency(val.text()));
      }
      else if (val.is('.percent'))
      {
        val.text(applet.FormatPercent(val.text()));
      }
    });
  });
}
</script>
</head>
<body>
  <div id="Container">
    <p>Page to demonstrate how JavaScript can get locale information from Java</p>
    <div id="AppletContainer">
      <object classid="java:GetLocaleInfo.class"
          type="application/x-java-applet" codetype="application/java"
          name="LocaleInfo" id="LocaleInfo" width="0" height="0">
        <param name="code" value="GetLocaleInfo"/>
        <param name="mayscript" value="true"/>
        <param name="scriptable" value="true"/>
        <p><!-- Displayed if object isn't supported -->
          <strong>This browser does not have Java enabled.</strong>
          <br>
          <a href="http://java.sun.com/products/plugin/downloads/index.html" title="Download Java plug-in">
          Get the latest Java plug-in here
          </a> (or enable Java support).
        </p>
      </object>
    </div><!-- AppletContainer -->
    <p>
    Click on the button to format the table content to the locale rules of the user.
    </p>
    <input type="button" name="DoFormatting" id="DoFormatting" value="Format the table" onclick="javascript:DoFormatting()"/>
    <div id="Results">
    </div><!-- Results -->
<table class="toFormat">
<caption>Synthetic View</caption>
<thead><tr>
<th>Name</th><th>Value</th><th>Cost</th><th>Discount</th>
</tr></thead>
<tbody>
<tr><td>Foo</td><td class="number">3.1415926</td><td class="currency">21.36</td><td class="percent">0.196</td></tr>
<tr><td>Bar</td><td class="number">159263.14</td><td class="currency">33</td><td class="percent">0.33</td></tr>
<tr><td>Baz</td><td class="number">15926</td><td class="currency">12.99</td><td class="percent">0.05</td></tr>
<tr><td>Doh</td><td class="number">0.01415926</td><td class="currency">5.1</td><td class="percent">0.1</td></tr>
</tbody>
</table>
  </div><!-- Container -->
</body>
</html>

Перевірено на Firefox 3.0, IE 6, Safari 3.1 та Opera 9.50, на Windows XP Pro SP3. Це працює без проблем з першими двома, у Safari у мене є дивна помилка після виклику init ():

java.net.MalformedURLException: no protocol:
    at java.net.URL.<init>(Unknown Source)
    at java.net.URL.<init>(Unknown Source)
    at java.net.URL.<init>(Unknown Source)
    at sun.plugin.liveconnect.SecureInvocation.checkLiveConnectCaller(Unknown Source)
    at sun.plugin.liveconnect.SecureInvocation.access$000(Unknown Source)
    at sun.plugin.liveconnect.SecureInvocation$2.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.plugin.liveconnect.SecureInvocation.CallMethod(Unknown Source)

але це все одно працює.

Я не можу змусити його працювати з Opera: аплет завантажується правильно, оскільки я бачу слід виклику init () у консолі Java, у мене не виникає помилок, коли JavaScript викликає функції Java (крім випадків, коли я додаю і викликаю метод отримання параметра JSObject, що цікаво), але функції Java не викликаються (я додав трафік викликів).
Я вважаю, що Liveconnect працює в Opera, але поки не бачу, як. Я досліджу трохи більше.
[Оновлення] Я видалив посилання на неіснуючий файл jar (що не зупиняє інші браузери), і я отримав слід дзвінків, але він не оновлює сторінку.
Ммм, якщо я alert(applet.GetLocaleInformation());отримаю інформацію, то це може бути проблемою з jQuery.


@PhiLho: він працює, але все одно неправильно читає роздільник із Windows. Коли він бачить наступне: Locale for русский (Россия): country=Россия (RU / RUS), lang=русский (ru / rus), variant= ()він використовує кому, незважаючи на те, що я замінив її крапкою в налаштуваннях Windows.
Quassnoi 07

Будь ласка, прочитайте моє оновлення мого першого повідомлення про обмеження цієї системи. Я не знаю, чи можемо ми зробити щось краще за допомогою веб-браузера, крім використання, можливо, якогось власного коду.
PhiLho

1

Навіть якщо ви знали , який стандарт це «GUI додаток» працює під управлінням , ви повинні з'ясувати , як він отримує поточну локаль, і як воно є визначенням десяткового роздільника.

Я не знаю, як це робиться на Mac, але на Windows програми повинні випитувати налаштування користувача, встановлені через Панель керування. Цілком можливо, що цей загадковий додаток ігнорує ці налаштування і замість цього використовує власні внутрішні налаштування.

Або, можливо, вони беруть поточну локаль і роблять висновок про решту, а не про те, щоб їм сказали.

Навіть тоді, англійською мовою, цифри даються групами з 3 цифр, комами розділяючи групи. тобто:

5,197,359,078

Якщо номер не був цілим числом, що містить номер телефону :

519-735-9078

Якщо, звичайно, число не було цілим числом, яке містить номер рахунку :

5197359078

У такому випадку ви повернетесь до жорстко закодованої перевизначеної логіки.

Редагувати: приклад вилученої валюти, оскільки валюта має власні правила форматування.


0

Подібний до інших відповідей, але стислий як константа :

const decimal=.1.toLocaleString().substr(1,1);      //returns "." in Canada

Крім того, щоб отримати роздільник тисяч :

const thousands=1234..toLocaleString().substr(1,1);   //returns "," in Canada

Просто розмістіть код у верхній частині вашого JS, а потім зателефонуйте, як потрібно, щоб повернути символ.


Наприклад (там, де я живу), щоб видалити коми з "1,234,567":

console.log( "1,234,567".replaceAll(thousands,"") ); //prints "1234567" to console.  

-1

"Чи є спосіб зробити це на стороні сервера (бажано, щоб я міг збирати статистику) або на стороні клієнта?"

Ні, ти не можеш. Цей графічний інтерфейс розглядає деякі налаштування користувача чи машини. По-перше, ви, мабуть, не знаєте, на які налаштування шукає цей інтерфейс. По-друге, з веб-додатком ви, мабуть, не зможете перевірити ці налаштування (на стороні клієнта -> Javacsript).


@RWC: Я знаю, куди шукає програму графічного інтерфейсу: регіональні налаштування Windows.
Quassnoi 02

-4

Ще одне можливе рішення: Ви можете використовувати щось на зразок GeoIP (приклад у PHP), щоб визначити місцезнаходження користувача та прийняти рішення на основі цієї інформації.


1
Однак потрібно було б дозволити користувачеві перевизначити це. Мій друг працює на півдні Англії; компанія, в якій він працює, спрямовує весь доступ до Інтернету через проксі-сервер в Іспанії, тому GeoIP завжди показує, що він знаходиться на відстані сотень миль від його фактичного місцезнаходження.
NickFitz 02
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.