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


82

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

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

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

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

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

Обновить:

Вся суть задачи в том, чтобы делать это автоматически.

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

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

Accept-LanguageРешение реализовано и работает, но я хотел бы, чтобы улучшить его.

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

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

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

  1. Русская винда с запятой как 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. У меня нет Opera.
— Мэтью Талберт,

@Matthew - у меня работал в Chrome. @Quassnoi - Я не понимаю, что означает этот последний комментарий. Если он говорит, что функция не работает, какое значение имеет то, что этот человек знает?
— Matchu

Осторожно! В Chrome toLocaleString работает правильно, только если вызывается непосредственно для Number. В моей системе: [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

15

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

Примеры:

> 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 не поддерживается.
— Гаджендра Кумар

11

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

Отредактировано для добавления:

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


Забавно, это была моя первая идея, но я переборщил, пытаясь сделать это автоматически ...
— PhiLho

1
Плохая идея, разве что как запасной вариант. Большинство пользователей неловко нечувствительны к культуре и даже не поймут, что такое «десятичный разделитель» без объяснения (а затем будут возмущены тем, что им приходится устанавливать что-то, что «все знают»).
— Майкл Боргвардт

2
@Iaalto: это сделало бы вопрос почти таким же важным, как "Минимизировать размер базы данных (рекомендуется) или Увеличить возможности поиска?"
— Quassnoi 02

Что ж, это не должно быть слишком сложно. Просто позвольте пользователю выбрать страну, а затем выбрать десикмальный сепаратор и другие параметры соответственно.

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
возврат к "." в случае некоторых непонятных браузеров ... в остальном это почти то же самое ...
— Доктор Облак


5

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

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


Как это сделать? Насколько я понимаю, вы можете использовать такую ​​библиотеку браузера, как эта github.com/dansingerman/jQuery-Browser-Language
— Lime

@William: Acccept-Language, о котором говорит OP, - это HTTP-заголовок, отправленный браузером, который сообщает серверу, какой язык предпочитает пользователь, обычно это язык установки браузера или ОС.
— Майкл Боргвардт

4

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

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

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


Да, я использовал этот метод как полифил для браузеров, которые не поддерживают formatToParts(Safari и IE).
— Марко Боначи

1
В некоторых регионах не используется разделитель тысяч ниже 10000. Например(1000).toLocaleString("es-PE") # "1000"
— Madacol

1

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

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

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

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

Я доделываю и тестирую свой апплет и вскоре выкладываю его туда.


@PhiLho: было бы неплохо узнать. Это веб-приложение представляет собой своего рода справочную систему, поэтому подойдет любой уродливый взлом, он не должен быть элегантным, если работает в IE, Firefox и Opera.
— Quassnoi 02

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

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

Я не знаю, как это делается на 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.