With a browser, how do I know which decimal separator does the operating system use?

HtmlInternationalizationLocale

Html Problem Overview


I'm developing a web application.

I need to display some decimal data correctly so that it can be copied and pasted into a certain GUI application that is not under my control.

The GUI application is locale sensitive and it accepts only the correct decimal separator which is set in the system.

I can guess the decimal separator from Accept-Language and the guess will be correct in 95% cases, but sometimes it fails.

Is there any way to do it on server side (preferably, so that I can collect statistics), or on client side?

Update:

The whole point of the task is doing it automatically.

In fact, this webapp is a kind of online interface to a legacy GUI which helps to fill the forms correctly.

The kind of users that use it mostly have no idea on what a decimal separator is.

The Accept-Language solution is implemented and works, but I'd like to improve it.

Update2:

I need to retrive a very specific setting: decimal separator set in Control Panel / Regional and Language Options / Regional Options / Customize.

I deal with four kinds of operating systems:

  1. Russian Windows with a comma as a DS (80%).
  2. English Windows with a period as a DS (15%).
  3. Russian Windows with a period as a DS to make poorly written English applications work (4%).
  4. English Windows with a comma as a DS to make poorly written Russian applications work (1%).

All 100% of clients are in Russia and the legacy application deals with Russian goverment-issued forms, so asking for a country will yield 100% of Russian Federation, and GeoIP will yield 80% of Russian Federation and 20% of other (incorrect) answers.

Html Solutions


Solution 1 - Html

Here is a simple JavaScript function that will return this information. Tested in Firefox, IE6, and IE7. I had to close and restart my browser in between every change to the setting under Control Panel / Regional and Language Options / Regional Options / Customize. However, it picked up not only the comma and period, but also oddball custom things, like the letter "a".

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

Does this help?

Solution 2 - Html

Retrieving separators for the current or a given locale is possible using Intl.NumberFormat#formatToParts.

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

It only works for browsers supporting the Intl API. Otherwise it requires an Intl polyfill

Examples:

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

Bonus:

We could extend it to retrieve either the decimal or group separator of a given locale:

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

Examples:

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

Solution 3 - Html

Ask the user, do not guess. Have a setting for it in your web application.

Edited to add:

I think it is ok to guess the default setting that works ok, say, 95% of the time. What I meant was that the user should still be able to override whatever guesses the software made. I've been frustrated too many times already when a software tries to be too smart and does not allow to be corrected.

Solution 4 - Html

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

Solution 5 - Html

Why not

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

Solution 6 - Html

> I can guess the decimal separator from > Accept-Language and the guess will be > correct in 95% cases, but sometimes it > fails.

This is IMO the best course of action. In order to handle the failures, add a link to set it manually next to the display area.

Solution 7 - Html

Using other people answers I compiled the following decimal and thousand separators utility functions:

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

Enjoy!

Solution 8 - Html

I think you have to rely on JavaScript to give you the locale settings.
But apparently JS doesn't have direct access to this information.
I see [Dojo Toolkit](http://api.dojotoolkit.org/jsdoc/HEAD/dojo.currency.format "dojo.currency.format | Dojo Toolkit") relies on an external database to find the locale information, although it might not take in account setting changes, for example.
Another workaround I see is to have a small silent Java applet that query this information from the system, and JavaScript to get it out of Java.
I can give more information if you don't know how to do it (if you want to go this convoluted route, of course).

[EDIT] So I updated my knowledge of localization support in Java...
Unlike what I thought originally, you won't have directly the decimal separator or thousand separator characters directly, like you would do with line separator or path separator: instead Java offers APIs to format the numbers or dates you provide.
Somehow, it makes sense: in Europe you often put the currency symbol after the number, some countries (India?) have a more complex rule to separate digits, etc.

Another thing: Java correctly finds the current locale from the system, but doesn't take information from there (perhaps for above reasons). Instead it uses its own set of rules. So if you have a Spanish locale where you replaced decimal separator with an exclamation sign, Java won't use it (but perhaps neither your application, anyway...).

So I am writing an applet exposing a service (functions) to JavaScript, allowing to format numbers to the current locale. You can use it as such, using JavaScript to format numbers on the browser. Or you can just feed it with some sample number and extract the symbols from there, using them locally or feeding them back to the server.

I finish and test my applet and post it there soon.

Solution 9 - Html

Even if you knew what locale this "GUI Application" is running under, you still have to figure out how it is getting the current locale, and how it is determining the decimal separator.

i don't know how it is done on a Mac, but on Windows applications are supposed to interrogte the user's preferences set via the Control Panel. It's quite possible this mystery applicaiton is ignoring those settings, and using their own internal setup instead.

Or perhaps they're taking the current locale, and inferring the rest, rather than being told.

Even then, in english, numbers are given in groups of 3 digits, with a comma separating the groups. i.e.:

5,197,359,078

Unless the number was an integer that contains a phone number:

519-735-9078

Unless of course the number was an integer that contains an account number:

5197359078

In which case, you're back to hard-coded overridden logic.

Edit: Removed currency example, since currency has its own formatting rules.

Solution 10 - Html

OK, I have something to show, more a proof of concept than a finished product, but because of lack of precise specifications, I leave it this way (or I will over-engineer it). I post in a separate message because it will be a bit long. I took the opportunity to try a bit more jQuery...

The Java code: 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);
  }
}

An example of HTML page using the above applet: 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>

Tested on Firefox 3.0, IE 6, Safari 3.1 and Opera 9.50, on Windows XP Pro SP3. It works without problem with the first two, on Safari I have a strange error after init() call:

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)

but it still works.

I can't get it work with Opera: the applet loads correctly, as I can see the trace of init() call in the Java console, I have no errors when JavaScript calls the Java functions (except if I add and call a method getting a JSObject parameter, curiously), but the Java functions are not called (I added trace of the calls).
I believe Liveconnect works in Opera, but I don't see yet how. I will research a bit more.
[Update] I removed references to non-existing jar file (which doesn't stop other browsers) and I got a trace of the calls, but it doesn't update the page.
Mmm, if I do alert(applet.GetLocaleInformation()); I got the information, so it might be a jQuery issue.

Solution 11 - Html

Similar to other answers, but compressed as a constant:

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

Also, to get the thousands separator:

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

Just place the code at the top of your JS and then call as required to return the symbol.


For example (where I live), to remove commas from "1,234,567":

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

Solution 12 - Html

> Is there any way to do it on server side (preferably, so that I can collect statistics), or on client side?

from Server side. That could get decimal separator from system by (.NET)

string x = CultureInfo.CurrentCulture.NumberFormat.NumberDsecimalSeparator;

The rest of work is check delimiter for exporting which is different from x comma (",") or semicolon (";") in case csv export

Solution 13 - Html

> "Is there any way to do it on server > side (preferably, so that I can > collect statistics), or on client > side?"

No you can't. That GUI is looking at some user or machine specific settings. First, you probably do not know at what settings this UI is looking. Second, with a webapplication you will probably not be able to check these settings (clientside --> Javacsript).

Solution 14 - Html

Another possible solution: You could use something like GeoIP (example in PHP) to determine the user's location and decide based on these information.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionQuassnoiView Question on Stackoverflow
Solution 1 - HtmlChris NielsenView Answer on Stackoverflow
Solution 2 - HtmlJBEView Answer on Stackoverflow
Solution 3 - HtmllaaltoView Answer on Stackoverflow
Solution 4 - HtmlDr.OblakView Answer on Stackoverflow
Solution 5 - Htmluser3023011View Answer on Stackoverflow
Solution 6 - HtmlMichael BorgwardtView Answer on Stackoverflow
Solution 7 - HtmlJuangui JordánView Answer on Stackoverflow
Solution 8 - HtmlPhiLhoView Answer on Stackoverflow
Solution 9 - HtmlIan BoydView Answer on Stackoverflow
Solution 10 - HtmlPhiLhoView Answer on Stackoverflow
Solution 11 - HtmlashleedawgView Answer on Stackoverflow
Solution 12 - HtmlnhnamidxView Answer on Stackoverflow
Solution 13 - HtmlRWCView Answer on Stackoverflow
Solution 14 - HtmlokomanView Answer on Stackoverflow