В браузере как узнать, какой десятичный разделитель использует операционная система?
Я разрабатываю веб-приложение.
Мне нужно правильно отобразить некоторые десятичные данные, чтобы их можно было скопировать и вставить в определенный GUI
приложение, которое не находится под моим контролем.
Приложение с графическим интерфейсом чувствительно к локали и принимает только правильный десятичный разделитель, установленный в системе.
Я могу угадать десятичный разделитель из Accept-Language
и предположение будет правильным в 95% случаев, но иногда это не удается.
Есть ли способ сделать это на стороне сервера (желательно, чтобы я мог собирать статистику) или на стороне клиента?
Обновить:
Весь смысл задачи состоит в том, чтобы делать это автоматически.
Фактически, это веб-приложение является своего рода онлайн-интерфейсом к устаревшему графическому интерфейсу, который помогает правильно заполнять формы.
Те пользователи, которые его используют, в основном не знают, что такое десятичный разделитель.
Accept-Language
Решение реализовано и работает, но я бы хотел его улучшить.
Update2:
Мне нужно получить очень специфическую настройку: десятичный разделитель установлен в Control Panel / Regional and Language Options / Regional Options / Customize
,
Я имею дело с четырьмя видами операционных систем:
- Русская винда с запятой как DS (80%).
- Английские окна с точкой в виде DS (15%).
- Русская Windows с точкой в виде DS для работы плохо написанных английских приложений (4%).
- Английский Windows с запятой в качестве DS для работы плохо написанных русских приложений (1%).
Все 100% клиентов находятся в России, и унаследованное приложение имеет дело с российскими формами, выданными правительством, поэтому запрос страны даст 100% Российской Федерации, а GeoIP - 80% Российской Федерации и 20% других (неверно) ответы.
14 ответов
Вот простая функция 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');
Это помогает?
Получение разделителей для текущей или заданной локали возможно с помощью Intl.NumberFormat#formatToParts
,
function getDecimalSeparator(locale) {
const numberWithDecimalSeparator = 1.1;
return Intl.NumberFormat(locale)
.formatToParts(numberWithDecimalSeparator)
.find(part => part.type === 'decimal')
.value;
}
Это работает только для браузеров, поддерживающих Intl API. В противном случае требуется международный заполнитель.
Примеры:
> 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')
" "
Спроси пользователя, не угадай. Настройте его в своем веб-приложении.
Отредактировано, чтобы добавить:
Я думаю, можно предположить, что настройка по умолчанию работает нормально, скажем, 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;
}
Используя ответы других людей, я скомпилировал следующие служебные функции десятичного разделителя и тысячи разделителей:
var decimalSeparator = function() {
return (1.1).toLocaleString().substring(1, 2);
};
var thousandSeparator = function() {
return (1000).toLocaleString().substring(1, 2);
};
Наслаждайтесь!
Я могу угадать десятичный разделитель от Accept-Language, и предположение будет правильным в 95% случаев, но иногда это не удается.
Это ИМО лучший курс действий. Для обработки ошибок добавьте ссылку, чтобы установить ее вручную рядом с областью отображения.
Подобно другим ответам, но сжато как константа:
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.
Даже если вы знали, под каким языком работает это "приложение с графическим интерфейсом", вам все равно придется выяснить, как оно получает текущую локаль и как она определяет десятичный разделитель.
я не знаю, как это делается на Mac, но в приложениях Windows предполагается опросить пользовательские настройки, установленные через панель управления. Вполне возможно, что это таинственное приложение игнорирует эти настройки и использует вместо этого свои собственные внутренние настройки.
Или, возможно, они принимают текущую локаль и выводят остальное, а не говорят.
Даже тогда, на английском языке, числа даны в группах из 3 цифр с запятой, разделяющей группы. то есть:
5,197,359,078
Если номер не был целым числом, содержащим номер телефона:
519-735-9078
Если, конечно, число не было целым числом, которое содержит номер счета:
5197359078
В этом случае вы вернетесь к жестко закодированной переопределенной логике.
Редактировать: пример удаленной валюты, так как валюта имеет свои собственные правила форматирования.
Я думаю, что вы должны полагаться на JavaScript, чтобы дать вам настройки локали.
Но, очевидно, у JS нет прямого доступа к этой информации.
Я вижу, что Dojo Toolkit использует внешнюю базу данных для поиска информации о локали, хотя, например, он может не учитывать изменения настроек.
Другой обходной путь, который я вижу, состоит в том, чтобы иметь небольшой тихий Java-апплет, который запрашивает эту информацию из системы, и JavaScript, чтобы получить ее из Java.
Я могу дать больше информации, если вы не знаете, как это сделать (если, конечно, хотите пойти по этому извилистому пути).
[EDIT] Итак, я обновил свои знания о поддержке локализации в 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.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.
"Есть ли способ сделать это на стороне сервера (желательно, чтобы я мог собирать статистику) или на стороне клиента?"
Нет, ты не можешь. Этот графический интерфейс смотрит на некоторые пользовательские или машинные настройки. Во-первых, вы, вероятно, не знаете, какие настройки ищет этот интерфейс. Во-вторых, с веб-приложением вы, вероятно, не сможете проверить эти настройки (клиентская сторона -> Javacsript).
Есть ли способ сделать это на стороне сервера (желательно, чтобы я мог собирать статистику) или на стороне клиента?
со стороны сервера. Это может получить десятичный разделитель из системы с помощью (.NET)
string x = CultureInfo.CurrentCulture.NumberFormat.NumberDsecimalSeparator;
Остальная часть работы - это проверочный разделитель для экспорта, который отличается от запятой x (",") или точки с запятой (";") в случае экспорта csv
Другое возможное решение: вы можете использовать что-то вроде GeoIP (пример в PHP), чтобы определить местоположение пользователя и принять решение на основе этой информации.