Woher weiß ich mit einem Browser, welches Dezimaltrennzeichen das Betriebssystem verwendet?

81

Ich entwickle eine Webanwendung.

Ich muss einige Dezimaldaten korrekt anzeigen, damit sie kopiert und in eine bestimmte GUIAnwendung eingefügt werden können, die nicht unter meiner Kontrolle steht.

Die GUI-Anwendung ist vom Gebietsschema abhängig und akzeptiert nur das korrekte Dezimaltrennzeichen, das im System festgelegt ist.

Ich kann das Dezimaltrennzeichen von Accept-Languageerraten und die Vermutung wird in 95% der Fälle korrekt sein, aber manchmal schlägt es fehl.

Gibt es eine Möglichkeit, dies auf der Serverseite (vorzugsweise, damit ich Statistiken sammeln kann) oder auf der Clientseite zu tun?

Aktualisieren:

Der springende Punkt der Aufgabe ist es, sie automatisch zu erledigen.

Tatsächlich ist diese Webanwendung eine Art Online-Schnittstelle zu einer älteren Benutzeroberfläche, mit deren Hilfe die Formulare korrekt ausgefüllt werden können.

Die Art der Benutzer, die es verwenden, hat meistens keine Ahnung, was ein Dezimaltrennzeichen ist.

Die Accept-LanguageLösung ist implementiert und funktioniert, aber ich möchte sie verbessern.

Update2:

Ich muss eine ganz bestimmte Einstellung abrufen: Dezimaltrennzeichen gesetzt in Control Panel / Regional and Language Options / Regional Options / Customize.

Ich beschäftige mich mit vier Arten von Betriebssystemen:

  1. Russisches Windows mit einem Komma als DS (80%).
  2. Englisch Windows mit einem Zeitraum als DS (15%).
  3. Russisches Windows mit einem Zeitraum als DS, damit schlecht geschriebene englische Anwendungen funktionieren (4%).
  4. Englisches Windows mit einem Komma als DS, damit schlecht geschriebene russische Anwendungen funktionieren (1%).

Alle 100% der Kunden sind in Russland und der Legacy-Antrag befasst sich mit Formularen, die von der russischen Regierung ausgestellt wurden. Wenn Sie also nach einem Land fragen, erhalten Sie 100% der Russischen Föderation und GeoIP 80% der Russischen Föderation und 20% der anderen (falsch). Antworten.

Quassnoi
quelle

Antworten:

127

Hier ist eine einfache JavaScript-Funktion, die diese Informationen zurückgibt. Getestet in Firefox, IE6 und IE7. Ich musste meinen Browser zwischen jeder Änderung der Einstellung unter Systemsteuerung / Regional- und Sprachoptionen / Regionaloptionen / Anpassen schließen und neu starten. Es wurden jedoch nicht nur Komma und Punkt erfasst, sondern auch merkwürdige benutzerdefinierte Dinge wie der Buchstabe "a".

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

Hilft das?

Chris Nielsen
quelle
3
Dies funktionierte für mich in Firefox und IE8, aber nicht in Google Chrome. Ich habe keine Oper.
Matthew Talbert
@ Matthew - hat für mich in Chrome gearbeitet. @ Quassnoi - Ich verstehe nicht, was dieser letzte Kommentar bedeutet. Wenn er sagt, dass die Funktion nicht funktioniert, was macht es dann aus, was die Person weiß?
Matchu
Achtung! In Chrome funktioniert toLocaleString nur dann ordnungsgemäß, wenn es direkt über eine Nummer aufgerufen wird. Auf meinem System: [1.1,1.2] .toLocaleString () -> "1.1,1.2" | (1.1) .toLocaleString () -> "1,1"
Tarnay Kálmán
1
Die Funktion schlägt bei Gebietsschemas fehl, die mehr als ein Zeichen für ihre DecimalSeparator(z ,,. B. ) verwenden. In Windows LOCALE_SDECIMALkann ein Dezimaltrennzeichen bis zu drei Zeichen enthalten. (Deshalb schlägt es auf meinem PC fehl). Verwenden Sie Accept-Languagein diesem Fall besser den Browser. Was immer noch nicht die Fähigkeit erklärt, ihre eigenen zu spezifizieren, DecimalSeparatorzB\o/
Ian Boyd
4
@IanBoyd hat Recht mit Gebietsschemas mit einer Zeichenfolge aus mehr als einem Zeichen als Dezimaltrennzeichen, n = /^1(.+)1$/.exec(n.toLocaleString())[1]würde dies jedoch tun und ist einfacher als die Verwendung des Accept-LanguageHeaders.
Ygormutti
14

Das Abrufen von Trennzeichen für das aktuelle oder ein bestimmtes Gebietsschema ist mit möglich Intl.NumberFormat#formatToParts.

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

Es funktioniert nur für Browser, die die Intl-API unterstützen . Andernfalls ist eine Intl-Polyfüllung erforderlich

Beispiele:

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

Bonus:

Wir könnten es erweitern, um entweder das Dezimal- oder das Gruppentrennzeichen eines bestimmten Gebietsschemas abzurufen :

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

Beispiele:

> getSeparator('en-US', 'decimal')
"."
> getSeparator('en-US', 'group')
","
> getSeparator('fr-FR', 'decimal')
","
> getSeparator('fr-FR', 'group')
" "
JBE
quelle
Derzeit ist dies der beste Weg, um solche Informationen zu erhalten. BTW Intlwird sogar in IE 11 unterstützt: caniuse.com/#feat=internationalization
Konstantin
3
Dies funktioniert in IE 11 nicht, da formatToParts nicht unterstützt wird.
Gajendra Kumar
11

Fragen Sie den Benutzer, raten Sie nicht. Nehmen Sie eine Einstellung dafür in Ihrer Webanwendung vor.

Bearbeitet, um hinzuzufügen:

Ich denke, es ist in Ordnung, die Standardeinstellung zu erraten , die beispielsweise in 95% der Fälle in Ordnung ist . Was ich damit meinte war, dass der Benutzer immer noch in der Lage sein sollte, alle Vermutungen der Software zu überschreiben. Ich war schon zu oft frustriert, als eine Software versucht, zu intelligent zu sein und nicht korrigiert werden kann.

laalto
quelle
Komisch, es war meine erste Idee, aber ich ging über Bord und suchte, wie es automatisch geht ...
PhiLho
1
Schlechte Idee, außer vielleicht als Backup. Die meisten Benutzer sind peinlich kulturunempfindlich und verstehen nicht einmal, was ein "Dezimaltrennzeichen" ohne Erklärung ist (und sind dann sauer, wenn sie gezwungen werden, etwas festzulegen, das "jeder kennt").
Michael Borgwardt
2
@Iaalto: Dies würde eine Frage fast so groß machen wie "Datenbankgröße minimieren (empfohlen) oder Suchfunktionen maximieren?"
Quassnoi
Nun, das sollte nicht zu schwer sein. Lassen Sie den Benutzer einfach das Land auswählen und wählen Sie dann das Trockentrennzeichen und andere Optionen entsprechend aus.
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;
    }
Dr.Oblak
quelle
1
der Fallback auf "." im Falle einiger obskurer Browser ... auf andere Weise ist es so ziemlich dasselbe ...
Dr.Oblak
7

Warum nicht

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

user3023011
quelle
2
Vielleicht überspringt ein seltsames Gebietsschema die führende Null? Ich hätte lieber eine da, nur um sicherzugehen.
Juangui Jordán
5

Ich kann das Dezimaltrennzeichen von Accept-Language erraten und die Vermutung ist in 95% der Fälle korrekt, aber manchmal schlägt es fehl.

Dies ist IMO die beste Vorgehensweise. Um die Fehler zu beheben, fügen Sie einen Link hinzu, um ihn manuell neben dem Anzeigebereich festzulegen.

Michael Borgwardt
quelle
Wie würde das gemacht werden? Ich verstehe, Sie können eine Browser-Bibliothek wie diese verwenden github.com/dansingerman/jQuery-Browser-Language
Lime
@William: Die Acccept-Sprache, über die OP spricht, ist ein vom Browser gesendeter HTTP-Header, der dem Server mitteilt, welche Sprache der Benutzer bevorzugt, normalerweise die Sprache der Browser-Installation oder des Betriebssystems.
Michael Borgwardt
4

Unter Verwendung der Antworten anderer Personen habe ich die folgenden Dienstprogramme für Dezimal- und Tausendertrennzeichen zusammengestellt:

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

Genießen!

Juangui Jordán
quelle
Ja, ich habe diese Methode als Polyfill für Browser verwendet, die dies nicht unterstützen formatToParts(Safari und IE).
Marko Bonaci
1
Einige Gebietsschemas verwenden keine Tausendertrennzeichen unter 10000. Zum Beispiel(1000).toLocaleString("es-PE") # "1000"
Madacol
1

Ich denke, Sie müssen sich auf JavaScript verlassen, um die Gebietsschemaeinstellungen zu erhalten.
Aber anscheinend hat JS keinen direkten Zugriff auf diese Informationen.
Ich sehe, dass Dojo Toolkit auf eine externe Datenbank angewiesen ist, um die Gebietsschemainformationen zu finden, obwohl beispielsweise Änderungen an den Einstellungen möglicherweise nicht berücksichtigt werden.
Eine andere Problemumgehung, die ich sehe, besteht darin, ein kleines stilles Java-Applet zu haben, das diese Informationen vom System abfragt, und JavaScript, um sie aus Java herauszuholen.
Ich kann weitere Informationen geben, wenn Sie nicht wissen, wie es geht (wenn Sie diesen verschlungenen Weg gehen möchten, natürlich).

[BEARBEITEN] Also habe ich mein Wissen über die Lokalisierungsunterstützung in Java aktualisiert ...
Im Gegensatz zu dem, was ich ursprünglich dachte, haben Sie nicht direkt das Dezimaltrennzeichen oder tausend Trennzeichen direkt, wie Sie es mit dem Zeilen- oder Pfadtrennzeichen tun würden: stattdessen Java bietet APIs zum Formatieren der von Ihnen angegebenen Zahlen oder Daten.
Irgendwie macht es Sinn: In Europa setzt man oft das Währungssymbol nach der Zahl, einige Länder (Indien?) Haben eine komplexere Regel zum Trennen von Ziffern usw.

Eine andere Sache: Java findet das aktuelle Gebietsschema korrekt aus dem System, nimmt jedoch keine Informationen von dort entgegen (möglicherweise aus den oben genannten Gründen). Stattdessen verwendet es seine eigenen Regeln. Wenn Sie also ein spanisches Gebietsschema haben, in dem Sie das Dezimaltrennzeichen durch ein Ausrufezeichen ersetzt haben, wird Java es nicht verwenden (aber vielleicht auch nicht Ihre Anwendung ...).

Ich schreibe also ein Applet, das einen Dienst (Funktionen) für JavaScript verfügbar macht und es ermöglicht, Zahlen für das aktuelle Gebietsschema zu formatieren. Sie können es als solches verwenden, indem Sie JavaScript verwenden, um Zahlen im Browser zu formatieren. Oder Sie füttern es einfach mit einer Probennummer und extrahieren die Symbole von dort, verwenden sie lokal oder geben sie an den Server zurück.

Ich beende und teste mein Applet und poste es bald dort.

PhiLho
quelle
@PhiLho: wäre schön zu wissen. Diese Webanwendung ist eine Art Hilfesystem, sodass jeder hässliche Hack ausreicht. Sie muss nicht elegant sein, solange sie unter IE, Firefox und Opera ausgeführt wird.
Quassnoi
1

OK, ich habe etwas zu zeigen, eher einen Proof of Concept als ein fertiges Produkt, aber wegen des Mangels an genauen Spezifikationen lasse ich es so (oder ich werde es überentwickeln). Ich poste in einer separaten Nachricht, weil es ein bisschen lang sein wird. Ich nutzte die Gelegenheit, um ein bisschen mehr jQuery zu probieren ...

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

Ein Beispiel für eine HTML-Seite mit dem obigen 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>

Getestet unter Firefox 3.0, IE 6, Safari 3.1 und Opera 9.50 unter Windows XP Pro SP3. Es funktioniert problemlos mit den ersten beiden, auf Safari habe ich nach dem Aufruf von init () einen seltsamen Fehler:

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)

aber es funktioniert immer noch.

Ich kann es nicht mit Opera zum Laufen bringen: Das Applet wird korrekt geladen, da ich die Ablaufverfolgung des Aufrufs init () in der Java-Konsole sehen kann. Ich habe keine Fehler, wenn JavaScript die Java-Funktionen aufruft (außer wenn ich eine Methode hinzufüge und aufrufe seltsamerweise einen JSObject-Parameter erhalten), aber die Java-Funktionen werden nicht aufgerufen (ich habe eine Ablaufverfolgung der Aufrufe hinzugefügt).
Ich glaube, Liveconnect funktioniert in Opera, aber ich sehe noch nicht, wie. Ich werde ein bisschen mehr recherchieren.
[Update] Ich habe Verweise auf nicht vorhandene JAR-Dateien entfernt (was andere Browser nicht stoppt) und habe eine Spur der Aufrufe erhalten, aber die Seite wird nicht aktualisiert.
Mmmm, wenn ich das tue, habe alert(applet.GetLocaleInformation());ich die Informationen, also könnte es ein jQuery-Problem sein.

PhiLho
quelle
@PhiLho: Es funktioniert, liest aber das Trennzeichen von Windows immer noch nicht richtig. Wenn Folgendes Locale for русский (Россия): country=Россия (RU / RUS), lang=русский (ru / rus), variant= ()angezeigt wird : Es wird ein Komma verwendet, obwohl ich es in den Windows-Einstellungen mit einem Punkt überschrieben habe.
Quassnoi
Bitte lesen Sie mein Update meiner ersten Nachricht über Einschränkungen dieses Systems. Ich weiß nicht, ob wir es mit einem Webbrowser besser machen können, außer wenn wir vielleicht nativen Code verwenden.
PhiLho
1

Auch wenn Sie wissen , was diese „GUI - Anwendung“ locale unter ausgeführt wird , müssen Sie noch herausfinden , wie es ist , die aktuelle locale bekommen, und wie es ist , das Dezimaltrennzeichen zu bestimmen.

Ich weiß nicht, wie es auf einem Mac gemacht wird, aber unter Windows sollen Anwendungen die über die Systemsteuerung festgelegten Benutzereinstellungen abfragen. Es ist durchaus möglich, dass diese mysteriöse Anwendung diese Einstellungen ignoriert und stattdessen ihr eigenes internes Setup verwendet.

Oder vielleicht nehmen sie das aktuelle Gebietsschema und schließen auf den Rest, anstatt es zu erfahren.

Selbst dann werden auf Englisch Zahlen in dreistelligen Gruppen angegeben, wobei ein Komma die Gruppen trennt. dh:

5,197,359,078

Es sei denn, die Nummer war eine Ganzzahl, die eine Telefonnummer enthält :

519-735-9078

Es sei denn natürlich, die Nummer war eine Ganzzahl, die eine Kontonummer enthält :

5197359078

In diesem Fall kehren Sie zur fest codierten überschriebenen Logik zurück.

Bearbeiten: Währungsbeispiel entfernt, da die Währung ihre eigenen Formatierungsregeln hat.

Ian Boyd
quelle
0

Ähnlich wie bei anderen Antworten, jedoch als Konstante komprimiert :

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

Um das Tausendertrennzeichen zu erhalten :

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

Platzieren Sie einfach den Code oben in Ihrem JS und rufen Sie nach Bedarf auf, um das Symbol zurückzugeben.


Zum Beispiel (wo ich wohne), um Kommas zu entfernen von "1,234,567":

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

"Gibt es eine Möglichkeit, dies auf der Serverseite (vorzugsweise, damit ich Statistiken sammeln kann) oder auf der Clientseite zu tun?"

Nein, kannst du nicht. Diese GUI betrachtet einige benutzer- oder maschinenspezifische Einstellungen. Erstens wissen Sie wahrscheinlich nicht, nach welchen Einstellungen diese Benutzeroberfläche sucht. Zweitens können Sie mit einer Webanwendung diese Einstellungen wahrscheinlich nicht überprüfen (clientseitig -> Javacsript).

Ian Boyd
quelle
@RWC: Ich weiß, wohin die GUI-App sucht: die regionalen Windows-Einstellungen.
Quassnoi
-4

Eine andere mögliche Lösung: Sie könnten etwas wie GeoIP (Beispiel in PHP) verwenden, um den Standort des Benutzers zu bestimmen und basierend auf diesen Informationen zu entscheiden.

Okoman
quelle
1
Es wäre jedoch notwendig, dem Benutzer zu erlauben, dies zu überschreiben. Ein Freund von mir arbeitet im Süden Englands. Das Unternehmen, für das er arbeitet, leitet den gesamten Internetzugang über einen Proxyserver in Spanien weiter. GeoIP zeigt ihm daher immer, dass er viele hundert Kilometer von seinem tatsächlichen Standort entfernt ist.
NickFitz