С помощью браузера, Как узнать, какой десятичный разделитель использует операционная система?

Я разрабатываю веб-приложения.

мне нужно правильно отобразить некоторые десятичные данные, чтобы их можно было скопировать и вставить в определенный GUI приложение, которое не находится под моим контролем.

приложение GUI чувствительно к локали и принимает только правильный десятичный разделитель, который установлен в системе.

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

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

обновление:

весь смысл задачи - делать это автоматически.

на самом деле, это веб-приложение является своего рода онлайн-интерфейс к устаревшему GUI, который помогает правильно заполнить формы.

вид пользователей, которые используют его в основном не имеют представления о том, что такое десятичный разделитель.

на решение и работает, но я хотел бы улучшить его.

обновление 2:

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

Я имею дело с четырьмя видами операционных систем:

  1. Русские окна с запятой в качестве DS (80%).
  2. английские окна с периодом как DS (15%).
  3. Русские окна с периодом в качестве DS, чтобы сделать плохо написанные английские приложения работать (4%).
  4. английские окна с запятой в качестве DS, чтобы плохо написанные русские приложения работали (1%).

все 100% клиентов находятся в России, а устаревшее приложение имеет дело с российскими государственными формами, поэтому запрос на страну даст 100% Российской Федерации, а GeoIP даст 80% Российской Федерации и 20% других (неправильных) ответов.

12 ответов


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

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

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

console.log('You use "' + whatDecimalSeparator() + '" as Decimal seprator');

это поможет?


спросите пользователя, не угадайте. Есть настройка для него в вашем веб-приложении.

редактировать, чтобы добавить:

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


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;
    }

Я могу угадать десятичный разделитель из Accept-язык и догадка будет правильно в 95% случаев, но иногда неудачи.

Это ИМО лучший курс действий. Для обработки сбоев добавьте ссылку, чтобы установить ее вручную рядом с областью отображения.


почему

0.1.toLocaleString().replace(/\d/g, '')

извлечение разделителей для текущего или заданного языкового стандарта возможно с помощью 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')
","

бонус:

мы могли бы расширить его, чтобы получить либо decimal или группа разделитель данного locale:

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')
" "

Я думаю, вы должны полагаться на JavaScript, чтобы дать вам настройки локали.
Но, по-видимому, JS не имеет прямого доступа к этой информации.
Я вижу Dojo Toolkit использует внешнюю базу данных для поиска информации о локали, хотя она может не учитывать изменения настроек, например.
Другой обходной путь, который я вижу, - это иметь небольшой тихий апплет Java, который запрашивает эту информацию из системы, и JavaScript, чтобы получить ее из Java.
Я могу дайте больше информации, если вы не знаете, как это сделать (если вы хотите пройти этот запутанный маршрут, Конечно).

[редактирование] Поэтому я обновил свои знания о поддержке локализации на Java...
В отличие от того, что я думал изначально, у вас не будет непосредственно десятичного разделителя или тысячи символов разделителя, как вы сделали бы с разделителем строк или разделителем путей: вместо этого Java предлагает API для форматирования чисел или дат, которые вы предоставляете.
Как-то это имеет смысл: в Европе вы часто ставят символ валюты после номера, некоторые страны (Индия?) имеют более сложные правила для отдельных чисел и т. д.

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

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

Я заканчиваю и тестирую свой апплет и отправляю его туда в ближайшее время.


хорошо, у меня есть что показать, больше доказательство концепции, чем готовый продукт, но из-за отсутствия точных спецификаций я оставляю его таким образом (или я буду перепроектировать его). Я отправляю отдельное сообщение, потому что оно будет немного длинным. Я воспользовался возможностью, чтобы попробовать немного больше 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.access0(Unknown Source)
    at sun.plugin.liveconnect.SecureInvocation.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 работает в опере, но я пока не вижу как. Я буду исследовать немного больше.
[Update] я удалил ссылки на несуществующий файл jar (который не останавливает другие браузеры), и я получил трассировку вызовов, но он не обновляет страницу.
МММ, если я это сделаю alert(applet.GetLocaleInformation()); я получил информацию, так что может быть проблема с jQuery.


даже если бы вы знали, в какой локали работает это "приложение GUI", вам все равно нужно выяснить, как это получает текущую локаль, и как это определяет десятичный разделитель.

Я не знаю, как это делается на Mac, но в приложениях Windows предполагается опрашивать предпочтения пользователя, установленные через Панель управления. Вполне возможно, что это приложение mystery игнорирует эти настройки и использует собственную внутреннюю настройку вместо.

или, возможно, они берут текущую локаль и делают вывод об остальном, а не говорят.

даже тогда, по-английски,цифры приведены в группы из 3 цифр, с запятой, разделяющей группы. т. е.:

5,197,359,078

Если только число не было целым числом, содержащим номер телефона:

519-735-9078

Если, конечно, число не было целым числом, содержащим аккаунт номер:

5197359078

в этом случае вы возвращаетесь к жестко закодированной переопределенной логике.

Edit: удален пример валюты, так как валюта имеет свои собственные правила форматирования.


используя ответы других людей, я собрал следующие десятичные и тысячные функции утилиты разделителей:

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

наслаждайтесь!


"есть ли способ, чтобы сделать это на сервере сторона (предпочтительно, чтобы я мог сбор статистики), или на клиенте сторона?"

нет, вы не можете. Этот GUI смотрит на некоторые пользовательские или машинные настройки. Во-первых, вы, вероятно, не знаете, на какие настройки этот пользовательский интерфейс смотрит. Во-вторых, с помощью веб-приложения вы, вероятно, не сможете проверить эти настройки (clientside --> Javacsript).


другое возможное решение: вы можете использовать что-то вроде GeoIP (пример в PHP), чтобы определить местоположение пользователя и принять решение на основе этой информации.