多语言踩坑汇总

ko-fi

土耳其语大小写转换

土耳其语的大小写转换规则与英语(及大多数拉丁字母语言)存在显著差异,这在计算机科学界被称为“土耳其语 I 问题”,其实还有阿塞拜疆语也有这个问题。

1. 核心差异:有点与无点的“I”

在英语中,字母 i 的大写是 I。但在土耳其语中,这两个字母分别属于两组不同的对应关系 :

  • 有点字母组:小写 i 对应大写 İ(带点的 I)。

  • 无点字母组:小写 ı(无点 i)对应大写 I(无点 I)。

这意味着:

  • 在英语环境下:iI

  • 在土耳其语环境下:iİ 以及 ıI

2. 技术与软件开发中的问题

由于许多编程语言(如 C#、Java等)的字符串转换函数(如 toLowerCase()toUpperCase())在默认情况下会参考系统的本地化设置(Locale),这导致了大量的软件缺陷 :

  • 关键字失效:如果一个编译器或解析器在土耳其语环境下运行,将大写的英文单词 “INFO” 转换为小写,结果会变成 “ınfo”(使用无点 ı),而不是预期的 “info”。这会导致程序无法识别指令或配置 。

  • 标签损坏:在游戏引擎(如 Unreal Engine)中,如果对包含富文本标签的代码进行文化敏感的大小写转换,<img id="..."> 可能会变成 <İMG İD="...">,导致系统无法识别标签。

  • 文件系统错误:如果文件名包含 “I”,在土耳其语环境下转换为小写后,可能因为字符不匹配而导致无法找到文件

3. 历史与设计原因

  • Unicode 设计:Unicode 为了保持与早期字符集(如 ISO-8859-9)的兼容性,将土耳其语的无点大写 I 与英语的大写 I 统一使用了同一个码点(U+0049),这使得大小写转换必须依赖于语言上下文(Locale)才能正确执行 。

  • 语言改革:土耳其语在 20 世纪 20 年代拉丁化改革时,为了实现“一个字母对应一个声音”的原则,特意区分了这两种元音 。

4. 解决方案

为了避免这类 Bug,当需要将字符串转换为统一的大小写以便后续处理(如比较或存储)时,不应直接使用 ToUpper()ToLower(),因为它们默认使用系统的当前文化设置(CurrentCulture)。

浮点数转换字符串小数点.变成,

在许多欧洲国家或地区的语言设置中,默认的数字小数点分隔符是逗号(,)而不是点(.)。在执行 ToString() 操作时,.NET 会根据当前线程的文化环境自动选择分隔符。

解决方法

使用固定文化(CultureInfo.InvariantCulture)

InvariantCulture 的默认小数点分隔符始终是 .,且不会随系统语言环境的变化而改变

1
2
3
double value = 1.5;
string result = value.ToString(System.Globalization.CultureInfo.InvariantCulture);
// 结果始终为 "1.5"

Application.systemLanguage返回语言标识错误

一些语言在调用Application.systemLanguage查询时会被错误的认为成马来语

解决方法

  • iOS - Preferred Language: 使用 iOS Preferred Language

    • 根据 iOS 本地化指南,用户的 preferredLanguages 是在 iOS 设置(设置 ➞ 通用 ➞ 语言与地区)中列出的语言顺序,项目中只取第一个即可。
1
2
[System.Runtime.InteropServices.DllImport("__Internal")]
extern static string getPreferredLanguage();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extern "C"
{
const char* getPreferredLanguage()
{
NSString* language = [[NSLocale preferredLanguages]firstObject];
if (language == NULL)
return NULL;

const char* languageCode = [language UTF8String];
const size_t len = strlen(languageCode) + 1;
char* pl = (char*)malloc(len);
strlcpy(pl, languageCode, len);
return pl;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static string GetAndroidDeviceLanguage()
{
using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale"))
{
if (cls != null)
{
using (AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault"))
{
if (locale != null)
{
return locale.Call<string>("toLanguageTag");
}
}
}
}
return null;
}
1
CultureInfo.CurrentUICulture.TwoLetterISOLanguageName
1
Application.systemLanguage

阿拉伯语处理

Arabic Support for Unity

Tashkeel (发音/变音符号)

Tashkeel(阿拉伯语:تشكيل)是指阿拉伯语中的变音符号发音符号。由于阿拉伯字母主要是辅音,Tashkeel 符号被添加在字母的上方或下方,用以指示短元音、音节重复或静音。

  • 常见类型:包括 Fathah (开口符)、Kasrah (齐齿符)、Dammah (合口符)、Sukun (静符)、Shadda (叠音符) 等。

  • 作用:这些符号对于准确的读音和理解句意至关重要。在普通的现代阿拉伯语出版物中,Tashkeel 有时会被省略,但在宗教文献(如古兰经)、诗歌和初级语言教材中是必须显示的。

  • Unity 开发中的挑战:并非所有字体都能完美支持这些符号的渲染。在使用 Arabic Support for Unity 插件时,开发者可以选择显示或隐藏这些符号。

Hindu numbers (印地数字 / 东阿拉伯数字)

在 Unity 插件(如 ArabicSupport)的语境下,Hindu numbers 指的是东阿拉伯数字(Eastern Arabic numerals),即在阿拉伯语、波斯语和乌尔都语地区广泛使用的数字形状 。

  • 数字形态:它们看起来像这样:٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩

  • 命名困惑

    • 我们平时用的 0, 1, 2… 在国际上被称为“西阿拉伯数字”(Western Arabic numerals)。

    • 插件中提到的 Hindu numbers 其实是指在阿拉伯语国家常见的“东阿拉伯数字”。

  • Unity 开发中的功能

    • 自动转换Arabic Support for Unity 插件提供选项,可以将标准英文字符(0-9)自动转换为这种特定的数字形状。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using UnityEngine;
using System.Collections;
using ArabicSupport;

public class FixArabic3DText : MonoBehaviour {

public bool showTashkeel = true;
public bool useHinduNumbers = true;

void Start ()
{
TextMesh textMesh = gameObject.GetComponent<TextMesh>();
string fixedText = ArabicFixer.Fix(textMesh.text, showTashkeel, useHinduNumbers);
gameObject.GetComponent<TextMesh>().text = fixedText;
Debug.Log(fixedText);
}
}

CJK 字体缺失

中文等东亚字符显示为方块(□)的主要原因是字体文件不包含 CJK(中日韩)字符集,或者 TextMeshPro (TMP) 的字体资产(Font Asset)未包含对应字符的位图信息。

  • 选择支持 CJK 的字体

  • 建议将 Atlas Resolution 设置为 2048x20484096x4096,因为中文等东亚字符数量巨大,需要更大的贴图空间

  • 对于聊天系统等无法预知内容的文本,可以使用代码在运行时动态添加字符到字体资产中(如使用 chineseFont.TryAddCharacters(c))并强制更新