如何强制 .Net ToString 使用给定的全球化 / CultureInfo

How to force .Net ToString to use given Globalization / CultureInfo

提问人:tommijo 提问时间:12/22/2021 最后编辑:tommijo 更新时间:12/23/2021 访问量:1614

问:

我正在使用一个 .Net Core 6 库 (dll),其中除其他功能外,数字和日期/时间信息输出为字符串,并且此代码需要全球化,即。要有文化意识。该库用于文档生成系统,因此在同一个应用程序会话中,用户将生成用于 de-CH、de-FR、en-US 等的文档。所以基本上,我们希望让库文化感知,但输出的文化应该有它的“标准”格式,而不是在操作系统级别或用户级别自定义的格式。

例如,对于十进制值,库代码使用以下方法来输出内容:

public string ToString();
public string ToString(string? format);
public string ToString(IFormatProvider? provider)
public string ToString(string? format, IFormatProvider? provider)

因此,由具有不同区域性设置的不同计算机中的不同用户调用这些重载,将以不同的方式格式化输出:小数点分隔符可以是逗号或点,千分组分隔符可能是非制动空格或 ' 等。

但是,当使用特定区域性的实例(例如“fr-CH”(瑞士法语))并使用 CultureInfo.UseUserOverride == false 显式传递参数时,我期望始终使用相同的输出字符串,与用户和/或计算机无关。所以,我在想,.Net 将为所有区域类型的 CultureInfo 实例的所有属性提供“内置”值,并且通过参数,我可以强制格式设置使用这些“内置”值,因此将始终输出相同的字符串。provideruseUserOverride: false

但事实似乎并非如此!

代码:

CultureInfo cultInfo = new CultureInfo(name: "fr-CH", useUserOverride: false)       //* fr-CH" is Swiss-French
Decimal myDec = -123456789123456.987654321M; 
String output = myDec.ToString(format: "N4", provider: cultInfo);       // I was expecting always same output independent, which user is calling it in which computer, but this is not the case!

此示例代码是使用 .Net Core 6 作为控制台应用程序编译的,我在 a) Windows 10 Pro PC 和 b) Windows Server 2012R2 上运行它,它们具有不同的输出。

  • Fr-CH/Windows Pro 10 PC:-123 456 789 123 456,9877
  • Fr-CH/Windows Server 2012R2:-123'456'789'123'456.9877

通过设置相同:然后结果看起来总是相同的,这是预期的。CultureInfo cultInfo = CultureInfo.InvariantCulture

我还尝试在上面设置和使用-变量,然后调用,但没有效果:结果与未设置.我实际上从这里、这里这里了解到,设置与格式无关(我的情况),而是与翻译(resx 的东西)有关。Thread.CurrentThread.CurrentCultureThread.CurrentThread.CurrentUICulturecultInfoToString(format?, provider?)CurrentThreadThread.CurrentThread.CurrentUICulture

我知道这个关于 CultureInfo 类的大量 Microsoft 文档,但我看不到那里的森林中的树。

我的问题如下:

  • 是否有任何版本的 .Net Core(或任何版本的 .Net Framework)具有所有 CultureInfo 属性的“内置值”?
  • 我怎样才能强制我的代码始终完全使用独立于环境的给定文化?

为了能够以可靠的方式对库输出进行单元测试,这将非常重要。

当回读(利用 System.Convert(value, provider?) 任何计算机中任何用户输出的值时(只需知道它们输出的区域性),这可能很重要。

C# .NET 全球化 信息 不变文化

评论

1赞 Imir Hoxha 12/22/2021
在你的第二点中,是否试图说“独立于环境?
1赞 pfx 12/22/2021
您为看起来相当相同的值(?)-123,456,789,123,456.9877InvariantCulture
0赞 tommijo 12/22/2021
@ImirHoxha:谢谢鹰眼!我已经修正了错别字
0赞 tommijo 12/22/2021
@pfx:感谢您的发现:确实如此。我今天早上重复了我的测试,实际上不变的文化正如我所期望的那样工作:结果似乎总是一样的,独立于线程文化和用户/opsys。我想我太累了,无法检查我能想到的所有组合/可能性,我错过了这个。对不起。我已经修复了这篇文章

答:

-1赞 Imir Hoxha 12/22/2021 #1

将它设置在头上,让它工作,如果你不希望它在每个地方都重复:

CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("fr-CH");
.....
Decimal myDec = -123456789123456.987654321M;
String output = myDec.ToString(format: "N4");
Console.WriteLine(output);

在其他计算机上运行代码时,请尝试检查 CultureInfo 并查看它将返回的内容。我敢打赌它没有将其设置为 fr-CH:

CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
        Console.WriteLine(currentCulture.Name);

评论

0赞 tommijo 12/22/2021
我提到也尝试过这个。我还验证了这种文化是否真正得到应用。这就是我使用控制台应用程序的原因:我在控制台应用程序中运行命令,其中所有命令始终在单个线程上运行。我的目标是设置基于文化的用户需求,在同一应用程序会话中使用“fr-CH”输出文档#1,然后在“en-US”中生成文档#2。
0赞 pfx 12/23/2021 #2

您可以设置自定义区域性,并根据需要配置该区域性。

从现有的中性开始,例如 .
进行克隆以保持原样。
InvariantCultureInvariantCulture

var clone = CultureInfo.InvariantCulture.Clone() as CultureInfo;

然后对该克隆进行所需的更改 - 例如:

clone.NumberFormat.NumberDecimalSeparator = ",";
clone.NumberFormat.NumberGroupSeparator = ".";

可选但可取的是,您可能希望将其设置为只读版本。

var customCulture = CultureInfo.ReadOnly(clone);

在需要固定受控输出的任何地方使用该文化 - 例如:

var number = -123456789123456.987654321M;
var formattedNumber = number.ToString("N4", customCulture)); // -123.456.789.123.456,9877

在将格式化版本解析回 - 例如:stringdecimal

var parsedNumber = decimal.Parse(formattedNumber, customCulture);

如果您对 a 的设置方式感兴趣,请查看源代码
简而言之,它是一个具有固定设置的设置,而其他设置则依赖于操作系统和可选的用户覆盖。
CultureInfoInvariantCulture

文档中:

.NET 从各种来源之一派生其文化数据, 根据实现、平台和版本:

  • 在 .NET Framework 3.5 及更早版本中,区域性数据由 Windows 操作系统和 .NET Framework 提供。

  • 在 .NET Framework 4 及更高版本中,区域性数据由 Windows 操作系统提供。

  • 在 Windows 上运行的所有 .NET Core 版本中,区域性数据由 Windows 操作系统提供。

因此,特定 .NET 上可用的区域性 实现、平台或版本可能在 不同的 .NET 实现、平台或版本。

某些 CultureInfo 对象因基础平台而异。