.NET 全球化API 和 ICU 對CultureInfo的影響 - acaj2018 的部落格 - udn部落格
acaj2018 的部落格
作家:資訊小幫手
文章分類
    Top
    .NET 全球化API 和 ICU 對CultureInfo的影響
    2023/11/02 09:18:57
    瀏覽:162
    迴響:0
    推薦: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   
    回應

    限會員,要發表迴響,請先登入