前陣子實作貨幣的數字中文大寫轉換時,發現Microsoft Visual Studio International Feature Pack 2.0中就有內建的數字轉中文大寫,實作後使用上並沒有什麼問題,直到近期Windows更新後發現,在轉換時會出現” The specified format is not supported in this culture”的錯誤訊息,研究後發現似乎與.NET 全球化API 和 ICU 有關。
1.原始程式:
原本使用Microsoft Visual Studio International Feature Pack 2.0內建的” EastAsiaNumericFormatter.FormatWithCulture”功能來做轉換,程式碼如下。
decimal m = 840;
string str = EastAsiaNumericFormatter.FormatWithCulture(“L”, m, null, new CultureInfo(“zh-CHT”));
當輸入為840時,輸出的大寫中文字為「捌佰肆拾」。
2.Windows Update後開始出現錯誤訊息:
在某次的Windows Update後,當程式執行到” EastAsiaNumericFormatter.FormatWithCulture”時,便會出現以下錯誤訊息” The specified format is not supported in this culture”。
查看了微軟所提供的程式碼。
internal static EastAsiaFormatter Create(CultureInfo culture, string format)
{
while (!culture.IsNeutralCulture && culture.Parent != null)
{
culture = culture.Parent;
}
if (culture.Equals(new CultureInfo(“zh-CHS”)))
{
return format switch
{
“L” => new CHSStdFmt(),
“Ln” => new CHSNorFmt(),
“Lc” => new CHSCurFmt(),
_ => null,
};
}
if (culture.Equals(new CultureInfo(“zh-CHT”)))
{
return format switch
{
“L” => new CHTStdFmt(),
“Ln” => new CHTNorFmt(),
“Lc” => new CHTCurFmt(),
_ => null,
};
}
if (culture.Equals(new CultureInfo(“ja”)))
{
return format switch
{
“L” => new JPNStdFmt(),
“Ln” => new JPNNorFmt(),
“Lt” => new JPNTraFmt(),
_ => null,
};
}
if (culture.Equals(new CultureInfo(“ko”)))
{
return format switch
{
“L” => new KORStdFmt(),
“Lc” => new KORCurFmt(),
_ => null,
};
}
return null;
}
發現CultureInfo也有包含原先我們使用的(”zh-CHT”),看上去似乎沒什麼問題。
再接著查看” culture.IsNeutralCulture”與”culture.Parent”:
IsNeutralCulture為false,因此在while判斷時,culture變成了culture.Parent的”zh-Hant”,又下方的if條件並沒有”zh-Hant”,因此被判斷為不支援的culture format。
3. 全球化API 在 Windows 10上使用 ICU 程式庫:
zh-Hans | Chinese (Simplified) | neutral |
zh-TW | Chinese (Traditional, Taiwan) | specific |
zh-CN | Chinese (Simplified, PRC) | specific |
zh-HK | Chinese (Traditional, Hong Kong S.A.R.) | specific |
zh-SG | Chinese (Simplified, Singapore) | specific |
zh-MO | Chinese (Traditional, Macao S.A.R.) | specific |
zh | Chinese | neutral |
zh-Hant | Chinese (Traditional) | neutral |
zh-CHS | Chinese (Simplified) Legacy | neutral |
zh-CHT | Chinese (Traditional) Legacy | neutral |
查看上方微軟所提供的結果,根據微軟提供的CultureInfo.IsNeutralCulture 屬性,zh-CHT的IsNeutralCulture應為true,而在我們的程式中卻為false,經查發現,Windows 10 2019年5月更新和更新版本包含icu.dll作為OS的一部分,而.NET 5和更新版本預設會使用ICU。在Windows上執行時,.NET 5和更新版本會嘗試載入icu.dll,如果找不到或無法載入ICU程式庫,.NET 5和更新版本會回復為以NLS為基礎的實作。
查看上方實際本機實作輸出結果,由於載入了ICU程式庫,而ICU程式庫中的Culture已不包含zh-CHT,因此IsNeutralCulture變成false,導致使用FormatWithCulture時無法在程式中if條件上找到對應的項目。
4.解決方式 使用 NLS 而非 ICU:
因ICU程式庫的影響,在Microsoft Visual Studio International Feature Pack 2.0更新前,僅能手動設定回舊版的程式庫,才能繼續使用微軟尚未更新的功能,而官方提供了三種方法讓使用者修改設定,分別為
- 在專案檔新增
<ItemGroup>
<RuntimeHostConfigurationOption Include=”System.Globalization.UseNls” Value=”true” />
</ItemGroup> - 在json 檔案新增:
{
“runtimeOptions”: {
“configProperties”: {
“System.Globalization.UseNls”: true
}
}
} - 將環境變數 DOTNET_SYSTEM_GLOBALIZATION_USENLS的值設定為true或1。
修改後可以看到,IsNeutralCulture變回true了。
5.結語:
使用ICU可能會導致某些全球化相關作業的差異,微軟官網也針對此部分了提供了「全球化最佳做法、當地語系化最佳做法、ASP.NET 應用程式的全球化最佳做法」等的建議,如果使用了官網提供的建議後程式依然出現錯誤或無法執行,也可以依照官方提供的設定方式嘗試改回使用NLS而非ICU。