網路城邦
上一篇 回創作列表 下一篇   字體:
.NET 全球化API 和 ICU 對CultureInfo的影響
2023/11/02 09:18:57瀏覽65|回應0|推薦0

前陣子實作貨幣的數字中文大寫轉換時,發現Microsoft Visual Studio International Feature Pack 2.0中就有內建的數字轉中文大寫,實作後使用上並沒有什麼問題,直到近期Windows更新後發現,在轉換時會出現 The specified format is not supported in this culture”的錯誤訊息,研究後發現似乎與.NET全球化和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”:

IsNeutralCulturefalse,因此在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-CHTIsNeutralCulture應為true,而在我們的程式中卻為false,經查發現,Windows 10 20195月更新和更新版本包含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更新前,僅能手動設定回舊版的程式庫,才能繼續使用微軟尚未更新的功能,而官方提供了三種方法讓使用者修改設定,分別為

1.在專案檔新增:

<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" />
</ItemGroup>

2.在 runtimeconfig.json 檔案新增:

{
  "runtimeOptions": {
     "configProperties": {
       "System.Globalization.UseNls": true
      }
  }
}

3.將環境變數 DOTNET_SYSTEM_GLOBALIZATION_USENLS的值設定為true或1。

修改後可以看到,IsNeutralCulture變回true了。

5.結語:

使用ICU可能會導致某些全球化相關作業的差異,微軟官網也針對此部分了提供了「全球化最佳做法、當地語系化最佳做法、ASP.NET 應用程式的全球化最佳做法」等的建議,如果使用了官網提供的建議後程式依然出現錯誤或無法執行,也可以依照官方提供的設定方式嘗試改回使用NLS而非ICU。

Reference

  1. https://learn.microsoft.com/zh-tw/dotnet/api/system.globalization.cultureinfo.isneutralculture?view=net-6.0
  2. https://learn.microsoft.com/zh-tw/dotnet/core/extensions/globalization-icu
  3. https://learn.microsoft.com/zh-tw/dotnet/core/compatibility/globalization/5.0/icu-globalization-api   
( 知識學習其他 )
回應 推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:https://classic-blog.udn.com/article/trackback.jsp?uid=acaj2018&aid=180001231