views:

1928

answers:

8

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.

A: 

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

@RWC: I know where the GUI app is looking to: the Windows regional settings.
Quassnoi
+6  A: 

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.

laalto
Funny, it was my first idea, but I went overboard looking how to do it automatically...
PhiLho
Bad idea, except maybe as a backup. Most users are embarassinlgy culture-insensitive and won't even understand what a "decimal separator" is without explanation (and then will be pissed off at being forced to set something that "everyone knows").
Michael Borgwardt
@Iaalto: this would make a question almost as great as "Minimize database size (recommended) or Maximize search capabilities?"
Quassnoi
Well, that shouldn't be too hard. Just let the user choose the country and then select the desicmal separator and other options accordingly.
unbeknown
+1  A: 

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 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.

PhiLho
@PhiLho: would be nice to know. This webapp is a kind of a help system, so any ugly hack will do, it doesn't need to be elegant as long as it runs on IE, Firefox and Opera.
Quassnoi
+6  A: 

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.

Michael Borgwardt
A: 

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

okoman
Russian Federation: 100%.
Quassnoi
It would be necessary to allow the user to override this, though. A friend of mine works in the South of England; the company he works for routes all Internet access through a proxy server in Spain, so GeoIP always shows him as being many hundreds of miles from his actual location.
NickFitz
+1  A: 

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.

Ian Boyd
+2  A: 

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"&gt;&lt;/script&gt;
<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.

PhiLho
@PhiLho: it works, but still doesn't read the separator correctly from Windows. When it sees the following: `Locale for русский (Россия): country=Россия (RU / RUS), lang=русский (ru / rus), variant= ()` it uses a comma despite the fact that I've overridden it with a period in the Windows settings.
Quassnoi
Please, read my update of my first message about limitations of this system. I don't know if we can do better from a Web browser, except by using some native code perhaps.
PhiLho
+8  A: 

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

Here is a test page showing it in action.

Does this help?

Chris Nielsen
This worked for me in Firefox and IE8, but not Google Chrome. I don't have Opera.
Matthew Talbert
Dead on. Thanks.
Quassnoi
`@Matthew`: people that use `Google Chrome` usually know what decimal separator is.
Quassnoi
@Matthew - worked for me in Chrome. @Quassnoi - I don't get what that last comment means. If he's saying that the function doesn't work, then what does it matter what the person knows?
Matchu
straight to the point, in both senses.
bitcruncher