我已获得一台服务器来托管我的 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
我无法强制DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
(例如在项目文件中),因为我的应用程序需要不允许不变文化的 SQL Server。
同一应用程序在我的 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;
}
}