語系問題 - FrankNine/franknine.github.io GitHub Wiki

大小寫轉換在土耳其語系問題

土耳其有兩種大小寫 i

  • 有點 İ U+0130i U+0069 成對
  • 無點 I U+0049ı U+0131 成對

當我們預期 I U+0049 經過 ToLower 會變成 i U+0069,在 CultureInfo("tr-TR") 會變成 ı U+0131
造成 Parsing 或是 Enum ToString 比對時只在土耳其使用者上出現不相等結果

參考資料:

測試程式:

System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("tr-TR");

Console.WriteLine("SETTINGS".ToLower()); // settıngs 注意沒有點
Console.WriteLine("SETTINGS".ToLower() == "settings"); // False
Console.WriteLine("SETTINGS".ToLowerInvariant() == "settings"); // True

解決方法:

  • 不是給人閱讀的字串都使用 InvariantCulture 版本的 C# API 略過語系特有的轉換
  • 邏輯或是 Enum 不要從文字格式 Parse,使用 Binary 格式來儲存 Enum/Flag

浮點數字串在法語跟印尼語的問題

一些語言如法語與印尼語的小數點不是慣用的 . 而是 , 當浮點數資料以字串形式儲存 Parsing 時又沒設定成 InvariantCulture 會導致 Parsing 失敗讀出錯誤的值或是吃 Exception。
神魔之塔的 案例

參考資料:

解決方法:

  • 同上,數值資料在 Production 儘量不要使用文字格式儲存(文字 Parsing 通常也效率低 GC 高),或是設定 InvariantCulture
  • 設定 NumberFormatInfo.NumberDecimalSeparator 可以覆寫該語系的小數點(還是建議整個迴避掉 Parsing)
// 法文語系強制使用 `.` 作為小數點
System.Globalization.NumberFormatInfo numberFormatInfo = new System.Globalization.CultureInfo("fr-FR").NumberFormat;
Console.WriteLine(float.Parse("123.45", numberFormatInfo)); // System.FormatException: Input string was not in a correct format

numberFormatInfo.NumberDecimalSeparator = ".";
Console.WriteLine(float.Parse("123.45", numberFormatInfo)); // 123.45

影響整個 Thread 行為的設定方式

Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator = ".";

影響整個 AppDomain 行為的設定方式

CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;

DateTime.ToString() 在泰文吃 Exception

DateTime.ToString() 需要使用曆法物件,但是泰國預設的佛曆物件 ThaiBuddhistCalendar 可能會被 Unity Stripping 機制剔除,導致 DateTime.ToString() 失敗。

參考資料:

解決方法:
節錄自參考資料,故意在不會到的 code 使用特殊曆法類別讓 Unity 不要剔除(或是使用 link.xml
除了泰國佛曆這段程式還有其他少見曆法可以看情形使用

// These classes won't be linked away because of the code,
// but we also won't have to construct unnecessarily either,
// hence the if statement with (hopefully) impossible
// runtime condition.
//
// This is to resolve crash at CultureInfo.CurrentCulture
// when language is set to Thai. See
// https://github.com/xamarin/Xamarin.Forms/issues/4037
if (Environment.CurrentDirectory == "_never_POSSIBLE_")
{
    new System.Globalization.ChineseLunisolarCalendar();
    new System.Globalization.HebrewCalendar();
    new System.Globalization.HijriCalendar();
    new System.Globalization.JapaneseCalendar();
    new System.Globalization.JapaneseLunisolarCalendar();
    new System.Globalization.KoreanCalendar();
    new System.Globalization.KoreanLunisolarCalendar();
    new System.Globalization.PersianCalendar();
    new System.Globalization.TaiwanCalendar();
    new System.Globalization.TaiwanLunisolarCalendar();
    new System.Globalization.ThaiBuddhistCalendar();
    new System.Globalization.UmAlQuraCalendar();
}

升級 iOS 13 之後泰文無法顯示

蘋果在 iOS 13 將泰文從預設系統字型移除,獨立成 Thonburi,Unity 不願意支援這項改動
Unity Issue (Won't Fix): [IOS 13.0] SOME LANGUAGES SYMBOLS ARE REPLACED WITH A [?] MARKS

解決方法: 創造一個自製的字體,加入以下 fallback:
Arial, Liberation Sans, Droid Sans, Droid Sans Fallback, Noto Sans Thai Bold(需要加入 Noto Sans Thai)
用這個字體取代 Unity 預設的 Arial 系統字

日文玩家抱怨標點符號錯誤

Unicode CJK 全形標點符號共用 Code point 然後依照環境語系選擇 Glyph,但是 UGUI Text 沒有實作以語系選擇 Glyph 功能。遇到這種同 Code point 不同 Glyph 的情況只會用預設 Glyph。

  • 日文、簡體中文全形標點 Glyph 位置:左下
  • 繁體中文全形標點 Glyph 位置:正中

解決方法:

  • 繼續使用 UGUI Text 要特地為日文玩家置入 Noto Sans CJK JP 字體。Noto Sans CJK 字型系列包含的 Glyph Set 大致相同,只是預設語系不同。為了解決 Unity 不實作該有的功能,只能這樣浪費空間解決。或是只放 Noto Sans CJK JP,這樣中文標點符號會變成日文位置,但是以過往經驗日本玩家反應比較多。
  • TextMesh Pro 可以以 Font Face 設定控制,但是 SDF 要重複生成 Unity 討論串