指南:構(gòu)建精準(zhǔn)國際化應(yīng)用)
1. 項目緣起為什么我們需要一份靠譜的語言縮寫列表在數(shù)字化的世界里處理多語言內(nèi)容幾乎成了開發(fā)、運營、產(chǎn)品經(jīng)理乃至內(nèi)容創(chuàng)作者的日常。無論是為網(wǎng)站配置國際化i18n語言包還是在數(shù)據(jù)庫中存儲用戶的語言偏好又或者是在API接口中定義請求頭里的Accept-Language我們總繞不開一個看似簡單卻極易出錯的東西語言縮寫。你可能遇到過這樣的場景產(chǎn)品經(jīng)理說“我們要支持法語”你興沖沖地在代碼里寫上了fr。上線后法國用戶反饋界面顯示異常。一查才發(fā)現(xiàn)法國本土除了法語還有布列塔尼語、阿爾薩斯語等而瑞士法語區(qū)的用戶期望的可能是fr-CH。又或者你從某個開源庫的文檔里復(fù)制了一段語言代碼列表結(jié)果發(fā)現(xiàn)它把簡體中文標(biāo)成了zh-CN把繁體中文標(biāo)成了zh-TW但在某些國際標(biāo)準(zhǔn)里它們更精確的表示是zh-Hans和zh-Hant。這些細(xì)微的差別輕則導(dǎo)致界面顯示錯誤重則可能引發(fā)本地化內(nèi)容推送失敗影響用戶體驗。因此一份準(zhǔn)確、全面、且附帶使用場景說明的語言縮寫列表絕不是簡單的信息羅列而是一份能幫你避開無數(shù)坑的“地圖”。它需要告訴你在什么情況下該用哪種標(biāo)準(zhǔn)不同標(biāo)準(zhǔn)之間有何差異以及在實際應(yīng)用中如何選擇。本文的目的就是為你繪制這樣一張地圖并結(jié)合我在多個跨國項目中的實戰(zhàn)經(jīng)驗告訴你如何正確地使用它們。2. 語言縮寫的核心標(biāo)準(zhǔn)ISO 639 與 IETF BCP 47在深入列表之前我們必須先理解支撐這些縮寫的兩大核心標(biāo)準(zhǔn)體系。不同的標(biāo)準(zhǔn)適用于不同的場景用錯了標(biāo)準(zhǔn)就像拿著地圖卻看錯了圖例。2.1 ISO 639語言標(biāo)識的基石ISO 639是國際標(biāo)準(zhǔn)化組織制定的一套用于表示語言名稱的代碼標(biāo)準(zhǔn)。它有幾個部分最常用的是ISO 639-1 (兩位字母代碼)這是最廣為人知、最常用的形式。它涵蓋世界上主要語言代碼簡潔。例如en- 英語zh- 中文es- 西班牙語fr- 法語de- 德語ja- 日語ko- 韓語ru- 俄語使用場景適用于對語言區(qū)分粒度要求不高的場景如簡單的網(wǎng)站語言切換、內(nèi)容的大類分類。它的優(yōu)點是極其簡短易于記憶和傳播。ISO 639-2 (三位字母代碼)當(dāng)639-1的兩位代碼不夠用時例如某些沒有廣泛書面形式的語言或圖書館、文獻領(lǐng)域需要更精細(xì)的分類時會使用三位代碼。它又分為“術(shù)語型”(T)和“文獻型”(B)兩種通常我們使用術(shù)語型。例如eng- 英語 (術(shù)語型)chi/zho- 中文 (這里就體現(xiàn)了B/T的區(qū)別chi是文獻型zho是術(shù)語型在實際技術(shù)應(yīng)用中強烈建議使用zho以避免歧義)fre/fra- 法語 (fra為術(shù)語型)使用場景主要用于圖書編目、學(xué)術(shù)研究等專業(yè)領(lǐng)域。在現(xiàn)代軟件開發(fā)中除非對接特定傳統(tǒng)系統(tǒng)否則較少直接使用。ISO 639-3 (三位字母代碼)旨在覆蓋全球所有已知的語言包括方言、古語等數(shù)量超過7000種。它是639-2的超集。例如cmn- 普通話 (官話)yue- 粵語wuu- 吳語使用場景語言學(xué)研究、非常精細(xì)的語言識別和分類。對于大多數(shù)應(yīng)用產(chǎn)品來說粒度過于細(xì)致。實戰(zhàn)經(jīng)驗一首選 ISO 639-1對于絕大多數(shù)商業(yè)軟件和互聯(lián)網(wǎng)產(chǎn)品ISO 639-1的兩位代碼是首選。它足夠通用被幾乎所有操作系統(tǒng)、瀏覽器、開發(fā)庫所支持。在數(shù)據(jù)庫設(shè)計時用一個CHAR(2)字段來存儲用戶語言偏好既節(jié)省空間又高效。記住一個原則如無特殊必要不要引入更復(fù)雜的代碼體系。2.2 IETF BCP 47現(xiàn)代應(yīng)用的事實標(biāo)準(zhǔn)如果說ISO 639定義了“語言”本身那么IETF的BCP 47通常表現(xiàn)為RFC 5646則定義了如何完整地描述一個“語言區(qū)域”。它更像一個構(gòu)造規(guī)則通過子標(biāo)簽Subtags的組合來精確表達。一個BCP 47語言標(biāo)簽的格式通常為語言-腳本-地區(qū)-變體-擴展-私有。最常用的是“語言-地區(qū)”對。語言子標(biāo)簽通常使用ISO 639-1的兩位代碼首選或639-2/3的三位代碼。腳本子標(biāo)簽可選使用ISO 15924四位字母代碼用于區(qū)分書寫系統(tǒng)。這是解決“簡體中文”與“繁體中文”問題的關(guān)鍵。Hans- 簡體中文Hant- 繁體中文Cyrl- 西里爾字母Latn- 拉丁字母地區(qū)子標(biāo)簽可選使用ISO 3166-1的兩位字母國家代碼或UN M.49的三位數(shù)字地區(qū)代碼。CN- 中國TW- 中國臺灣地區(qū)US- 美國GB- 英國001- 全球數(shù)字代碼組合示例與解析zh-CN中文中國大陸地區(qū)。這是一個常見的“語言-地區(qū)”組合。但它隱含了“使用簡體中文”的意思因為中國大陸主要使用簡體。然而從標(biāo)準(zhǔn)角度看它并未明確指定腳本。zh-Hans中文簡體腳本。這是表示“簡體中文”更精確的方式它不綁定特定區(qū)域適用于所有使用簡體中文的地區(qū)如中國大陸、新加坡、馬來西亞華人社區(qū)。zh-Hans-CN中文簡體腳本中國大陸地區(qū)。這是最完整、最無歧義的表示明確了語言、書寫形式和地理區(qū)域。zh-Hant中文繁體腳本。表示“繁體中文”不綁定區(qū)域。zh-Hant-TW中文繁體腳本中國臺灣地區(qū)。常用于臺灣地區(qū)使用的繁體中文。zh-Hant-HK中文繁體腳本中國香港地區(qū)。香港地區(qū)使用的繁體中文在詞匯、用語上與臺灣地區(qū)略有不同。en-US英語美國地區(qū)。美式英語。en-GB英語英國地區(qū)。英式英語。es-ES西班牙語西班牙地區(qū)。歐洲西班牙語。es-MX西班牙語墨西哥地區(qū)。拉丁美洲西班牙語的一種。實戰(zhàn)經(jīng)驗二理解“語言-地區(qū)”與“語言-腳本”的區(qū)別這是最容易混淆的地方。zh-CN和zh-Hans看似都指向簡體中文但側(cè)重點不同。zh-CN強調(diào)“在中國大陸使用的語言”其默認(rèn)腳本是簡體但理論上也可能包含其他腳本雖然極少。zh-Hans則純粹強調(diào)“使用簡體漢字書寫的中文”與地域無關(guān)。在涉及內(nèi)容本地化Localization時優(yōu)先使用“語言-地區(qū)”如zh-CN,en-US因為它包含了地域文化習(xí)慣在涉及純粹的文字呈現(xiàn)或字體選擇時使用“語言-腳本”如zh-Hans,zh-Hant更準(zhǔn)確。對于中文一個穩(wěn)妥的實踐是在系統(tǒng)內(nèi)部使用zh-Hans和zh-Hant而在面向用戶的界面上顯示“簡體中文中國”、“繁體中文臺灣”等友好名稱。3. 核心語言縮寫列表與使用指南下面我將結(jié)合ISO 639-1和BCP 47整理一份在軟件開發(fā)、內(nèi)容管理中最常遇到的語言標(biāo)簽列表并附上關(guān)鍵說明。3.1 全球主要語言基于ISO 639-1ISO 639-1 代碼英語名稱本地名稱示例典型BCP 47擴展語言-地區(qū)主要使用地區(qū)/說明arArabic???????ar-SA(沙特),ar-EG(埃及)阿拉伯語注意是從右向左書寫(RTL)。deGermanDeutschde-DE(德國),de-AT(奧地利),de-CH(瑞士)德語。瑞士德語(de-CH)在日期、數(shù)字格式上有所不同。enEnglishEnglishen-US(美國),en-GB(英國),en-AU(澳大利亞)必須區(qū)分地區(qū)拼寫、日期、貨幣格式差異大。esSpanishEspa?oles-ES(西班牙),es-MX(墨西哥),es-AR(阿根廷)西班牙語變體極多詞匯、發(fā)音、甚至語法有差異。frFrenchFran?aisfr-FR(法國),fr-CA(加拿大),fr-BE(比利時)加拿大法語(fr-CA)與法國法語在詞匯和用語上區(qū)別明顯。itItalianItalianoit-IT意大利語。jaJapanese日本語ja-JP日語。通常不需要進一步細(xì)分。koKorean???ko-KR韓語。ptPortuguesePortuguêspt-PT(葡萄牙),pt-BR(巴西)歐洲葡萄牙語和巴西葡萄牙語差異巨大務(wù)必區(qū)分。ruRussianРусскийru-RU俄語。使用西里爾字母。zhChinese中文見下方詳細(xì)分解中文情況最復(fù)雜必須處理簡繁體。3.2 中文的詳細(xì)分解重點與難點中文處理是國際化中的重中之重也是最易出錯的部分。下面這個表格清晰地展示了各種組合BCP 47 標(biāo)簽說明常見對應(yīng)友好名稱使用場景建議zh中文泛指中文避免單獨使用歧義太大。zh-Hans中文簡體腳本簡體中文推薦。用于表示所有簡體中文內(nèi)容不綁定地區(qū)。zh-Hant中文繁體腳本繁體中文推薦。用于表示所有繁體中文內(nèi)容不綁定地區(qū)。zh-CN中文中國大陸地區(qū)簡體中文中國隱含簡體。適用于針對中國大陸市場的完整本地化。zh-SG中文新加坡地區(qū)簡體中文新加坡新加坡簡體用詞習(xí)慣與大陸略有不同。zh-TW中文中國臺灣地區(qū)繁體中文臺灣臺灣繁體。注意政治敏感性在列表中宜與zh-Hant-TW等同。zh-HK中文中國香港地區(qū)繁體中文香港香港繁體。zh-MO中文中國澳門地區(qū)繁體中文澳門澳門繁體。zh-Hans-CN中文簡體腳本中國大陸簡體中文中國大陸最精確、最無歧義的表示法。zh-Hant-TW中文繁體腳本臺灣地區(qū)繁體中文臺灣最精確、最無歧義的表示法。zh-Hant-HK中文繁體腳本香港地區(qū)繁體中文香港最精確、最無歧義的表示法。實戰(zhàn)經(jīng)驗三中文標(biāo)簽的存儲與顯示策略內(nèi)部存儲在數(shù)據(jù)庫、配置文件、代碼常量中強烈建議使用zh-Hans和zh-Hant。這分離了“書寫系統(tǒng)”和“地域文化”邏輯更清晰。例如你可以用zh-Hans來標(biāo)記一份文檔的書寫形式而用CN、SG來標(biāo)記其目標(biāo)市場。用戶界面顯示給用戶選擇時不要顯示zh-Hans這樣的代碼。應(yīng)該顯示本地化的語言名稱如“簡體中文”、“繁體中文”。如果需要區(qū)分地區(qū)可以顯示“簡體中文中國大陸”、“繁體中文香港”。HTTPAccept-Language瀏覽器發(fā)送的通常是zh-CN,zh;q0.9,en;q0.8。你的后端服務(wù)應(yīng)該能正確解析并將zh-CN映射到你的zh-Hans資源或者更精確的zh-Hans-CN資源如果你有。字體回退在CSS中你可以針對不同腳本指定字體font-family: “PingFang SC”, “Microsoft YaHei”, sans-serif;對應(yīng)zh-Hans而font-family: “PingFang TC”, “Microsoft JhengHei”, sans-serif;對應(yīng)zh-Hant。3.3 其他需要特別注意的語言塞爾維亞語它同時使用西里爾字母和拉丁字母書寫。可以使用sr-Cyrl塞爾維亞語西里爾字母和sr-Latn塞爾維亞語拉丁字母來精確區(qū)分。維吾爾語在中國新疆地區(qū)使用阿拉伯字母書寫。標(biāo)準(zhǔn)代碼是ugISO 639-1完整標(biāo)簽可以是ug-Arab-CN。粵語作為中文的一種主要方言在ISO 639-3中有獨立代碼yue。在YouTube等平臺你可以看到y(tǒng)ue作為一個可選語言。但在大多數(shù)產(chǎn)品中它通常被歸入zh-Hant或zh-Hans下的變體處理。庫爾德語有ku庫爾德語泛稱、kmr北庫爾德語常用、ckb中庫爾德語等代碼書寫系統(tǒng)也有拉丁和阿拉伯之分。4. 在實戰(zhàn)中應(yīng)用從配置到代碼理解了標(biāo)準(zhǔn)和列表關(guān)鍵在于如何用起來。下面我以幾個典型場景為例說明如何實際操作。4.1 場景一網(wǎng)站/應(yīng)用國際化i18n配置假設(shè)你使用流行的i18n庫如 i18next, react-i18n, vue-i18n。1. 資源文件命名與組織一種清晰的結(jié)構(gòu)是按BCP 47標(biāo)簽建立文件夾或文件名。locales/ ├── en-US/ │ ├── common.json │ └── product.json ├── zh-Hans/ │ ├── common.json │ └── product.json ├── zh-Hant/ │ ├── common.json │ └── product.json └── ja-JP/ ├── common.json └── product.json為什么這樣組織這分離了語言和地區(qū)。zh-Hans下的資源可以被zh-CN和zh-SG共享基礎(chǔ)翻譯然后通過地區(qū)特定的擴展或覆寫來調(diào)整用詞差異例如“軟件”在大陸叫“軟件”在新加坡可能更常用“軟體”雖然都是簡體。2. 初始化i18n庫// 以 i18next 為例 import i18n from i18next; import { initReactI18next } from react-i18next; import Backend from i18next-http-backend; // 從后端加載資源 import LanguageDetector from i18next-browser-languagedetector; // 檢測瀏覽器語言 i18n .use(Backend) .use(LanguageDetector) .use(initReactI18next) .init({ fallbackLng: en, // 回退語言 supportedLngs: [en, zh-Hans, zh-Hant, ja], // 支持的語言列表 nonExplicitSupportedLngs: true, // 重要允許檢測到zh-CN時回退到zh-Hans backend: { loadPath: /locales/{{lng}}/{{ns}}.json, // 資源路徑 }, detection: { order: [querystring, cookie, localStorage, navigator, htmlTag], caches: [cookie], }, interpolation: { escapeValue: false, }, });關(guān)鍵配置解析nonExplicitSupportedLngs: true這個選項至關(guān)重要。當(dāng)瀏覽器語言是zh-CN時檢測器會依次嘗試加載zh-CN-zh-en的資源。由于我們支持zh-Hansi18next在zh-CN失敗后會聰明地嘗試去掉地區(qū)碼然后匹配到zh但我們的支持列表里只有zh-Hans和zh-Hant。此時nonExplicitSupportedLngs會嘗試將zh-CN轉(zhuǎn)換為zh然后尋找最匹配的支持語言。通常你需要配置loadPath邏輯或使用后端映射將zh-CN的請求指向zh-Hans資源。更穩(wěn)健的后端映射示例Node.js/Expressapp.get(/locales/:lng/:ns.json, (req, res) { let { lng, ns } req.params; // 語言標(biāo)簽映射 const languageMap { zh-CN: zh-Hans, zh-SG: zh-Hans, zh-TW: zh-Hant, zh-HK: zh-Hant, zh-MO: zh-Hant, en-US: en, en-GB: en, // ... 其他映射 }; const mappedLng languageMap[lng] || lng; // 檢查 mappedLng 是否在支持列表中 if (supportedLngs.includes(mappedLng)) { const filePath path.join(__dirname, locales, mappedLng, ${ns}.json); res.sendFile(filePath); } else { res.status(404).send(Language not found); } });4.2 場景二數(shù)據(jù)庫存儲用戶語言偏好在設(shè)計用戶表時如何存儲language字段方案A簡單推薦使用VARCHAR(5)存儲BCP 47語言標(biāo)簽。CREATE TABLE users ( id INT PRIMARY KEY, username VARCHAR(50), language VARCHAR(5) DEFAULT en -- 存儲如 zh-Hans, en-US, ja );優(yōu)點直接、靈活可以存儲最精確的標(biāo)簽。前端提交什么就存什么。缺點查詢和統(tǒng)計時需要處理例如統(tǒng)計所有中文用戶需要查詢LIKE zh%或更復(fù)雜的解析。方案B規(guī)范化分離語言和地區(qū)。CREATE TABLE users ( id INT PRIMARY KEY, username VARCHAR(50), language_code CHAR(2), -- ISO 639-1, 如 zh, en region_code CHAR(2), -- ISO 3166-1, 如 CN, US, NULLable script_code VARCHAR(4) -- ISO 15924, 如 Hans, Hant, NULLable );優(yōu)點結(jié)構(gòu)清晰便于基于語言、地區(qū)、腳本進行多維度的分析和查詢。缺點復(fù)雜度高寫入和讀取時需要組合/解析。對于大多數(shù)應(yīng)用來說過度設(shè)計。我的建議對于90%的應(yīng)用方案A足夠了。使用短字符串存儲完整標(biāo)簽并在業(yè)務(wù)邏輯層建立一套清晰的映射和回退規(guī)則。例如當(dāng)用戶選擇“中文簡體”時前端可以提交zh-Hans當(dāng)從瀏覽器獲取到zh-CN時通過后端映射或配置將其關(guān)聯(lián)到zh-Hans資源。4.3 場景三操作系統(tǒng)與瀏覽器環(huán)境識別在Node.js后端或瀏覽器端你需要獲取系統(tǒng)或環(huán)境的語言設(shè)置。瀏覽器端// 獲取瀏覽器優(yōu)先語言數(shù)組 const browserLanguages navigator.languages; // 例如[zh-CN, zh, en-US, en] // 獲取單個首選語言 const userLanguage navigator.language || navigator.userLanguage; // 例如zh-CN注意navigator.language返回的是單個字符串而navigator.languages返回一個按優(yōu)先級排序的數(shù)組后者更可靠。你應(yīng)該用這個數(shù)組作為Accept-Language的模擬交給你的語言檢測邏輯處理。Node.js 后端可以從HTTP請求頭Accept-Language中解析。const acceptLanguage req.headers[accept-language]; // 例如zh-CN,zh;q0.9,en;q0.8,en-GB;q0.7 // 需要使用類似 accepts 或 locale 這樣的庫來解析 const Accept require(accepts); const accept Accept(req); const locale accept.languages([en, zh-Hans, zh-Hant, ja]); // 返回匹配的第一個實戰(zhàn)經(jīng)驗四語言檢測的優(yōu)先級策略永遠(yuǎn)不要完全依賴自動檢測。應(yīng)提供一個明顯的語言切換器讓用戶手動選擇。自動檢測的邏輯應(yīng)該是1) 用戶上次手動選擇并保存的語言2) URL參數(shù)如?langzh-Hans3) Cookie或本地存儲4) 瀏覽器Accept-Language頭5) 應(yīng)用默認(rèn)語言如英語。用戶的明確選擇權(quán)必須高于系統(tǒng)的猜測。5. 常見陷阱與最佳實踐總結(jié)在多年與多語言打交道的經(jīng)歷中我踩過不少坑也總結(jié)出一些鐵律。陷阱一混淆“語言”與“區(qū)域設(shè)置”en是語言en-US是區(qū)域設(shè)置。區(qū)域設(shè)置除了語言還包含數(shù)字格式1,234.56 vs 1.234,56、日期格式MM/DD/YYYY vs DD/MM/YYYY、貨幣符號$ vs €、排序規(guī)則等。如果你的應(yīng)用只做了文本翻譯但數(shù)字、日期格式還是原來的樣子體驗會非常割裂。解決方案是使用完整的國際化庫如JavaScript的IntlAPI它能根據(jù)語言標(biāo)簽自動處理這些格式。陷阱二硬編碼語言列表不要在代碼里寫死const languages [en, zh-CN, ja]。一旦要新增一種語言就需要改代碼、重新部署。最佳實踐是將支持的語言列表作為配置文件如i18n.config.json或從后端API動態(tài)獲取。這樣運營人員或產(chǎn)品經(jīng)理可以在后臺管理界面直接添加新語言前端自動呈現(xiàn)。陷阱三翻譯資源的鍵名使用源語言錯誤示例{ “Hello”: “你好”, “Goodbye”: “再見” }。當(dāng)源語言如英語的鍵名需要修改時“Hello”改成“Hi”所有其他語言的翻譯文件都需要同步修改鍵名極易出錯。正確做法是使用業(yè)務(wù)邏輯相關(guān)的、語義化的鍵名。 正確示例{ “greeting.hello”: “你好”, “greeting.goodbye”: “再見” }。英文文件里是{ “greeting.hello”: “Hello”, “greeting.goodbye”: “Goodbye” }。這樣無論源語言文本如何變化鍵名穩(wěn)定不變。陷阱四忽略文本長度變化德語單詞通常比英語長中文短語通常比英文短。UI設(shè)計時必須考慮文本擴展通常預(yù)留30%-50%的額外空間和收縮。使用CSS屬性如text-overflow: ellipsis時要小心確保關(guān)鍵信息不被截斷。對于按鈕、標(biāo)簽等固定寬度的元素可以采用最小寬度min-width或彈性布局。陷阱五缺乏上下文給翻譯者把一句“Save”扔給翻譯者他可能翻譯成“保存”動詞或“儲蓄”名詞。必須提供上下文。專業(yè)的做法是使用帶有描述性的鍵名并在翻譯管理平臺如Crowdin, Transifex或JSON文件中添加注釋。{ “button.save”: { “description”: “The label for the button that saves a document”, “defaultMessage”: “Save” } } // 在中文文件中 { “button.save”: “保存” }最佳實踐清單內(nèi)部統(tǒng)一使用BCP 47標(biāo)簽優(yōu)先使用“語言-腳本”如zh-Hans或“語言-地區(qū)”如en-US形式。建立中央映射表處理瀏覽器語言標(biāo)簽到內(nèi)部標(biāo)簽的轉(zhuǎn)換如zh-CN-zh-Hans。分離“文本翻譯”和“區(qū)域格式化”使用Intl等標(biāo)準(zhǔn)API處理日期、數(shù)字、貨幣。設(shè)計彈性的UI適應(yīng)文本長度變化。永遠(yuǎn)提供手動語言切換入口并持久化用戶選擇。對翻譯資源進行版本控制并與代碼版本關(guān)聯(lián)。在開發(fā)早期就引入國際化框架而不是事后補救。