Webアプリケーションを開発しています。
自分の制御下にない特定のGUI
アプリケーションにコピーして貼り付けることができるように、いくつかの10進数データを正しく表示する必要があります。
GUIアプリケーションはロケールに依存し、システムに設定されている正しい小数点区切り文字のみを受け入れます。
Accept-Language
から小数点区切り文字を推測できますが、95%の場合は推測が正しいでしょうが、失敗する場合があります。
サーバー側で(できれば統計を収集できるように)、クライアント側でそれを行う方法はありますか?
更新:
タスクの重要なポイントは、それを自動的に行うことです。
実際、このwebappは、フォームを正しく入力するのに役立つレガシーGUIへの一種のオンラインインターフェイスです。
それを使用するユーザーの種類は、ほとんど小数点区切りが何であるかを知らない。
Accept-Language
ソリューションが実装されて機能しますが、改善したいと思います。
pdate2:
Control Panel / Regional and Language Options / Regional Options / Customize
に設定された小数点区切り記号という非常に具体的な設定を取得する必要があります。
私は4種類のオペレーティングシステムを扱っています。
クライアントの100%はすべてロシアにあり、レガシーアプリケーションはロシア政府発行のフォームを処理するため、国を求めるとロシア連邦の100%が得られ、GeoIPはロシア連邦の80%と他の20%(誤った)を生じます答えます。
この情報を返す簡単なJavaScript関数を次に示します。 Firefox、IE6、およびIE7でテスト済み。 [コントロールパネル]/[地域と言語のオプション]/[地域のオプション]/[カスタマイズ]で設定を変更するたびに、ブラウザを閉じて再起動する必要がありました。ただし、カンマとピリオドだけでなく、文字「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');
これは役立ちますか?
ユーザーに尋ねる、推測しないでください。 Webアプリケーションで設定してください。
追加して編集:
デフォルト設定を推測しても問題ないと思います。たとえば、95%の時間です。私が意図したのは、ユーザーがソフトウェアが行った推測を上書きできるようにすることです。ソフトウェアがあまりにもスマートになろうとしていて、修正できない場合、私はすでに何度もイライラしています。
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 polyfill が必要です
例:
> getDecimalSeparator()
"."
> getDecimalSeparator('fr-FR')
","
ボーナス:
これを拡張して、decimalまたはgroupセパレーターを取得できます指定されたロケール:
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')
" "
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;
}
何故なの
0.1.toLocaleString().replace(/\d/g, '')
Accept-Languageから小数点区切り文字を推測できます。95%の場合、推測は正しいでしょうが、失敗する場合があります。
これがIMOの最良の行動方針です。障害を処理するには、表示領域の横に手動で設定するリンクを追加します。
この「GUIアプリケーション」がどのロケールで実行されているかを知っていたとしても、itが現在のロケールを取得する方法とitは小数点を決定しています。
macでそれがどのように行われるかはわかりませんが、Windowsアプリケーションでは、コントロールパネルで設定されたユーザーの設定を調べることになっています。このミステリーアプリケーションがこれらの設定を無視し、代わりに独自の内部セットアップを使用している可能性は十分にあります。
または、彼らは現在のロケールを使用し、残りの部分を推測されるのではなく、推測しているのかもしれません。
それでも、英語ではnumbersは3桁のグループで与えられ、グループをコンマで区切ります。すなわち:
5,197,359,078
番号が電話番号を含む整数でない場合:
519-735-9078
もちろん、番号がアカウント番号を含む整数でない限り:
5197359078
この場合、ハードコードされたオーバーライドされたロジックに戻ります。
編集:通貨には独自のフォーマットルールがあるため、通貨の例を削除しました。
完成品というよりも概念を証明するものがありますが、正確な仕様がないため、そのままにしておきます(または、過剰に設計します)。少し長くなるので、別のメッセージで投稿します。もう少し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でテスト済み。最初の2つでは問題なく動作します。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で動作させることはできません。アプレットは正しく読み込まれます。Javaコンソールでinit()呼び出しのトレースを確認できるため、JavaScriptがJava関数を呼び出してもエラーはありません(奇妙なことに、JSObjectパラメーターを取得するメソッドを追加して呼び出した場合を除き、Java関数は呼び出されません(呼び出しのトレースを追加しました)。
LiveconnectはOperaで動作すると考えていますが、まだどのように動作するのかわかりません。もう少し研究します。
[更新]存在しないjarファイルへの参照を削除し(他のブラウザーを停止しません)、呼び出しのトレースを取得しましたが、ページを更新しません。
うーん、もしalert(applet.GetLocaleInformation());
をしたら、情報を得たので、jQueryの問題かもしれません。
他の人の答えを使用して、次の10進数および千単位のユーティリティユーティリティ関数をコンパイルしました。
var decimalSeparator = function() {
return (1.1).toLocaleString().substring(1, 2);
};
var thousandSeparator = function() {
return (1000).toLocaleString().substring(1, 2);
};
楽しい!
ロケール設定を提供するにはJavaScriptに依存する必要があると思います。
しかし、明らかにJSはこの情報に直接アクセスできません。
参照 Dojo Toolkit は、外部データベースに依存してロケール情報を検索しますが、たとえば、アカウント設定の変更が反映されない場合があります。
別の回避策は、システムからこの情報を照会する小さなサイレントJavaアプレット、およびJavaから情報を取得するJavaScript)を使用することです。
方法がわからない場合は、さらに情報を提供できます(もちろん、この複雑なルートに行きたい場合)。
[編集]そこで、Javaでのローカリゼーションサポートに関する知識を更新しました...
当初考えていたものとは異なり、行区切り文字やパス区切り文字の場合のように、小数点区切り文字や桁区切り文字を直接使用することはできません。代わりにJava APIを提供します指定した数字または日付をフォーマットします。
どういうわけか、それは理にかなっています。ヨーロッパでは、通貨記号を数字の後に付けることがよくあります。一部の国(インド?)では、数字を区切るより複雑な規則があります。
もう1つ:Javaはシステムから現在のロケールを正しく検出しますが、そこから情報を取得しません(おそらく上記の理由のため)。代わりに独自のルールセットを使用します。小数点記号を感嘆符で置き換えたスペイン語のロケール、Javaはそれを使用しません(とにかく、おそらくアプリケーションも使用しません...)。
そこで、サービス(関数)をJavaScriptに公開して、現在のロケールに数値をフォーマットできるようにするアプレットを書いています。 JavaScriptを使用してブラウザで数値をフォーマットし、そのまま使用できます。または、サンプル番号を入力して、そこからシンボルを抽出し、ローカルで使用するか、サーバーにフィードバックすることができます。
アプレットを終了してテストし、すぐに投稿します。