ユーザーのロケールに基づいて DotNet プログラムで数値書式を設定するのが難しい

ユーザーのロケールに基づいて DotNet プログラムで数値書式を設定するのが難しい

私はDotNet 7アプリケーションをホストするサーバーを手に入れましたが、ユーザーのロケールに応じてフォーマットされた数字を表示するのに問題があります。

オペレーティングシステムは次のとおりです。

$ cat os-release
NAME="Red Hat Enterprise Linux"
VERSION="8.7 (Ootpa)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="8.7"
PLATFORM_ID="platform:el8"
PRETTY_NAME="Red Hat Enterprise Linux 8.7 (Ootpa)"
...

私のロケールは次のとおりです。

$ locale
LANG=en_ZA.UTF-8
LC_CTYPE="en_ZA.UTF-8"
LC_NUMERIC="en_ZA.UTF-8"
LC_TIME="en_ZA.UTF-8"
LC_COLLATE="en_ZA.UTF-8"
LC_MONETARY="en_ZA.UTF-8"
LC_MESSAGES="en_ZA.UTF-8"
...

数字は次のように設定されます。

$ locale -k LC_NUMERIC
decimal_point="."
thousands_sep=","
grouping=3;3
numeric-decimal-point-wc=46
numeric-thousands-sep-wc=44
numeric-codeset="UTF-8"

123456789.9876だから、ロケールの数値形式でフォーマットされた数値が123,456,789.99

最も簡単なプログラムを作るなら:

static void Main(string[] args) {
    const decimal d = 123456789.9876M;
    Console.WriteLine($"{d:n}");
}

それdotnet run、それは印刷されます123 456 789,988

私はDotNetが使用すると信じています。Unicode(ICU)の内部コンポーネントただし、これはすべてのDotNetアプリケーションを実行するための要件であり、インストールされているようです。

$ dnf list installed \*libicu\*
libicu.x86_64    60.3-2.el8_1    @rhel-8-for-x86_64-baseos-rpms

私のアプリケーションには不変の文化を許可しないSQL Serverが必要なので、強制的に適用することはできませんDOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true(プロジェクトファイルなど)。

同じアプリケーションは、私のWSL / Ubuntuインスタンスと私が試したすべてのWindowsインスタンスで予想される形式で数字を印刷します。

頑張った特定バージョンのlibicuを含むアプリケーションで使用できますが、フォーマットには影響しません。

アプリケーション開発者がすべての展開で一貫性を確保できるように、.NET 5以降では、WindowsおよびUnixのアプリケーションが独自のICUコピーを運搬して使用できるようにします。

<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
<RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2.0.9" />

サーバー所有者はセキュリティを認識しており、ユーザーは最低限必要な権限を持っています。

私のアプリケーションに設定されたロケールを使用させる方法を知っている人はいますか?


単純なテストプログラムでより多くの出力を見ると、ロケールは正しいですが、上記のものとは異なるLC_NUMERIC / LC_MONETARY設定を使用しているようです。

The current culture is en-ZA
The decimal separator is ","
The group separator is " "
The decimal digits is "3"
Number formatted: 123 456 789,988
The currency symbol is "R"
The currency decimal separator is ","
The currency group separator is " "
The currency decimal digits is "2"
Currency formatted: R123 456 789,99

私の状況に対する回避策として、プログラムで文化別の設定を強制することができました。

public static void SetCustomZAThreadCulture(bool linuxOnly = true) {
    if (!linuxOnly || CrossPlatform.IsLinux()) {
        CultureInfo customCultureInfo = (CultureInfo)Thread.CurrentThread.CurrentCulture.Clone();
        customCultureInfo.NumberFormat = new NumberFormatInfo {
            NumberDecimalSeparator = ".",
            NumberGroupSeparator = ",",
            NumberDecimalDigits = 2,
            CurrencySymbol = "R",
            CurrencyDecimalSeparator = ".",
            CurrencyGroupSeparator = ",",
            CurrencyDecimalDigits = 2,
        };
        Thread.CurrentThread.CurrentCulture = customCultureInfo;
        CultureInfo.DefaultThreadCurrentCulture = customCultureInfo;
    }
}

関連情報