Internationalization and Localization
Internationalization and Localization
国际化与本地化
Consider this scenario: You’re a person from the US, staying in Canada, and looking to book a hotel room in Jaipur, India from my Travel site. You’re looking at a card like this: Jaipur grand hotel --- 04/05 -> 04/07 --- $1,12,107.43 --- फ्री कैंसिलेशन 想象一下这个场景:你是一个美国人,目前待在加拿大,正通过我的旅游网站预订印度斋浦尔的一家酒店。你看到的卡片信息如下:Jaipur grand hotel --- 04/05 -> 04/07 --- $1,12,107.43 --- फ्री कैंसिलेशन
“ok, why is 2 days of stay showing up as a million Dollars?” “what does it say in Hindi?” “wait, that’s not a million, why are the commas all wrong?” “is the rate in Canadian Dollars or US Dollars?” “aaah I’m going to book from a different website!” “好吧,为什么住2天显示要一百万美元?”“印地语写的是什么?”“等等,那不是一百万,为什么逗号的位置全错了?”“这个价格是加元还是美元?”“啊,我还是去别的网站订吧!”
If my site happened to have supported good localization (l10n), you would’ve switched to the exact format that you’d understand, and seen this card instead: Jaipur grand hotel --- 05/04 -> 07/04 --- CAD 112,107.43 --- Free cancellation 如果我的网站支持良好的本地化(l10n),你本可以切换到你熟悉的格式,从而看到这样的卡片:Jaipur grand hotel --- 05/04 -> 07/04 --- CAD 112,107.43 --- Free cancellation
You’d realise that you had accidentally chosen 2 months of stay instead of 2 days, that the currency was in Canadian Dollars, and that it said “Free cancellation”. And perhaps would’ve continued booking a room on my Travel OTA. This is a small glimpse into why supporting l10n is super important if you want to take your product global. 你会意识到自己不小心把入住时间选成了2个月而不是2天,货币单位是加元,而且上面写着“免费取消”。这样你或许就会继续在我的在线旅游平台(OTA)上完成预订。这只是为什么如果你想让产品走向全球,支持本地化(l10n)至关重要的一个缩影。
i18n or Internationalization is the codebase architecture set up part of supporting l10n. i18n(国际化)是支持本地化所需的代码库架构设置部分。
Step 0
第0步
Never hardcode user-facing strings. Always pass them through a localization function. An important note is to also never force preferences onto a user. For example, a person located in Japan could be a tourist and not be able to understand Japanese text. Or an Indian located in India may not be able to understand Hindi. Always give the user an option to change their interface localisation settings, and make the setting obvious and accessible. 永远不要硬编码面向用户的字符串。始终通过本地化函数来处理它们。重要的一点是,永远不要强行向用户施加偏好设置。例如,身处日本的人可能是游客,可能看不懂日文;或者身处印度的印度人可能看不懂印地语。务必给用户提供更改界面本地化设置的选项,并确保该设置显眼且易于访问。
Translation
翻译
This one starts easy. A bunch of JSON files per key, categorised and boxed into directories based on function, think Listing.Cancellation.Free_Cancellation, with sub-keys being languages and then the values. One thing to note here is that you should probably not load all the translations into memory, as this is going to end up being very very fat as you scale.
这部分起步很容易。为每个键准备一堆 JSON 文件,按功能分类并放入目录中,例如 Listing.Cancellation.Free_Cancellation,子键为语言,然后是对应的值。需要注意的一点是,你不应该一次性将所有翻译加载到内存中,因为随着规模扩大,这会变得非常臃肿。
Conditionals
条件逻辑
Complexity starts when you realise languages have their own rules for plurals, grammar, etc. Apart from not making the silly mistake of adding an “s” at the end, a way to solve this is to have conditions in your translations. This way you can abstract away conditional complexity into your translation function instead of muddying your code. 当你意识到不同语言在复数、语法等方面有各自的规则时,复杂性就开始了。除了避免在词尾加“s”这种愚蠢的错误外,解决这个问题的方法是在翻译中加入条件逻辑。这样你可以将条件复杂性抽象到翻译函数中,而不是让代码变得混乱。
Layout
布局
Then come text length and layout considerations. You’d ideally want to let your components grow with padding and margins instead of tight fixed widths. Similarly, with truncation, you should consider if the text is important information or not, and let it perhaps wrap to the next line gracefully. RTL (Arabic, Hebrew) is a whole different beast that I’m going to skip at the moment. 接下来是文本长度和布局的考量。理想情况下,你应该让组件通过内边距(padding)和外边距(margin)来自动扩展,而不是使用严格的固定宽度。同样,对于截断文本,你应该考虑该文本是否为重要信息,并让它优雅地换行。RTL(从右向左书写的语言,如阿拉伯语、希伯来语)是完全不同的挑战,我在此暂且跳过。
CJK Zenkaku
中日韩全角字符
When Keyboard layout is set to these, afaik the characters that are output use “Zenkaku” or extra-width, such that the individual characters are as wide as a typical Latin letter. The problem is when a CJK user types in English in the middle, all your validation is going to fail! 当键盘布局设置为这些语言时,据我所知,输出的字符会使用“全角”或额外宽度,使得单个字符与典型的拉丁字母一样宽。问题在于,当中日韩用户在中间输入英文时,你所有的验证逻辑都会失效!
Numbers
数字
Numbers of any kind must always pass through a formatter function. Numbers can be monetary values, distances, ratings, percentages. Example keys in your config to save: measurementSystem, numberSeparator.decimals, currency.decimalPlaces, currency.symbol.domestic.
任何类型的数字都必须通过格式化函数处理。数字可以是货币值、距离、评分、百分比等。配置中需要保存的示例键包括:measurementSystem(度量系统)、numberSeparator.decimals(小数分隔符)、currency.decimalPlaces(货币小数位)、currency.symbol.domestic(本地货币符号)。
Separator
分隔符
Comma separators are different in different countries, and yes they’re not always commas! Many countries group digits in threes, some European ones swap commas with dots, and India has its own lakh and crore system. 逗号分隔符在不同国家各不相同,而且没错,它们并不总是逗号!许多国家按三位一组对数字进行分组,一些欧洲国家用点代替逗号,而印度则有其独特的“十万(lakh)”和“千万(crore)”系统。
Money
货币
Money notation differs by placement of currency symbols, space between symbol and amount, local-global symbols, and fraction amount or the lack of. Some currencies have a local version - Japanese use “円” locally, while yen is denoted by ”¥” internationally. Also, many countries using dollars use ”$” locally, but for an international traveler it’s important to know which dollar they are looking at - USD, SGD, HKD, etc. 货币符号的表示方式因货币符号的位置、符号与金额之间的空格、本地/国际符号以及是否有小数部分而异。有些货币有本地版本——日本人本地使用“円”,而国际上日元用“¥”表示。此外,许多使用美元的国家在本地都用“$”,但对于国际旅客来说,明确他们看到的是哪种美元(美元、新元、港币等)非常重要。
Dates and times
日期和时间
Back to our 2 months vs 2 days confusion. Some countries use DD/MM, some MM/DD. Also, some use an AM/PM format, while others a 24-hr one. It’s generally a good idea to also denote what timezone (IST, JST, PST) a time is in, so that it’s super clear (think: should check-in time be user’s timezone or hotel’s timezone?). 回到我们之前关于2个月和2天的困惑。有些国家使用日/月格式,有些使用月/日格式。此外,有些使用上午/下午格式,而另一些使用24小时制。通常最好注明时间所属的时区(IST、JST、PST),以便非常清晰(思考:入住时间应该是用户的时区还是酒店的时区?)。