尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

ISO 639与BCP 47语言代码实战指南:构建精准国际化应用

ISO 639与BCP 47语言代码实战指南:构建精准国际化应用 1. 项目缘起为什么我们需要一份靠谱的语言缩写列表在数字化的世界里处理多语言内容几乎成了开发、运营、产品经理乃至内容创作者的日常。无论是为网站配置国际化i18n语言包还是在数据库中存储用户的语言偏好又或者是在API接口中定义请求头里的Accept-Language我们总绕不开一个看似简单却极易出错的东西语言缩写。你可能遇到过这样的场景产品经理说“我们要支持法语”你兴冲冲地在代码里写上了fr。上线后法国用户反馈界面显示异常。一查才发现法国本土除了法语还有布列塔尼语、阿尔萨斯语等而瑞士法语区的用户期望的可能是fr-CH。又或者你从某个开源库的文档里复制了一段语言代码列表结果发现它把简体中文标成了zh-CN把繁体中文标成了zh-TW但在某些国际标准里它们更精确的表示是zh-Hans和zh-Hant。这些细微的差别轻则导致界面显示错误重则可能引发本地化内容推送失败影响用户体验。因此一份准确、全面、且附带使用场景说明的语言缩写列表绝不是简单的信息罗列而是一份能帮你避开无数坑的“地图”。它需要告诉你在什么情况下该用哪种标准不同标准之间有何差异以及在实际应用中如何选择。本文的目的就是为你绘制这样一张地图并结合我在多个跨国项目中的实战经验告诉你如何正确地使用它们。2. 语言缩写的核心标准ISO 639 与 IETF BCP 47在深入列表之前我们必须先理解支撑这些缩写的两大核心标准体系。不同的标准适用于不同的场景用错了标准就像拿着地图却看错了图例。2.1 ISO 639语言标识的基石ISO 639是国际标准化组织制定的一套用于表示语言名称的代码标准。它有几个部分最常用的是ISO 639-1 (两位字母代码)这是最广为人知、最常用的形式。它涵盖世界上主要语言代码简洁。例如en- 英语zh- 中文es- 西班牙语fr- 法语de- 德语ja- 日语ko- 韩语ru- 俄语使用场景适用于对语言区分粒度要求不高的场景如简单的网站语言切换、内容的大类分类。它的优点是极其简短易于记忆和传播。ISO 639-2 (三位字母代码)当639-1的两位代码不够用时例如某些没有广泛书面形式的语言或图书馆、文献领域需要更精细的分类时会使用三位代码。它又分为“术语型”(T)和“文献型”(B)两种通常我们使用术语型。例如eng- 英语 (术语型)chi/zho- 中文 (这里就体现了B/T的区别chi是文献型zho是术语型在实际技术应用中强烈建议使用zho以避免歧义)fre/fra- 法语 (fra为术语型)使用场景主要用于图书编目、学术研究等专业领域。在现代软件开发中除非对接特定传统系统否则较少直接使用。ISO 639-3 (三位字母代码)旨在覆盖全球所有已知的语言包括方言、古语等数量超过7000种。它是639-2的超集。例如cmn- 普通话 (官话)yue- 粤语wuu- 吴语使用场景语言学研究、非常精细的语言识别和分类。对于大多数应用产品来说粒度过于细致。实战经验一首选 ISO 639-1对于绝大多数商业软件和互联网产品ISO 639-1的两位代码是首选。它足够通用被几乎所有操作系统、浏览器、开发库所支持。在数据库设计时用一个CHAR(2)字段来存储用户语言偏好既节省空间又高效。记住一个原则如无特殊必要不要引入更复杂的代码体系。2.2 IETF BCP 47现代应用的事实标准如果说ISO 639定义了“语言”本身那么IETF的BCP 47通常表现为RFC 5646则定义了如何完整地描述一个“语言区域”。它更像一个构造规则通过子标签Subtags的组合来精确表达。一个BCP 47语言标签的格式通常为语言-脚本-地区-变体-扩展-私有。最常用的是“语言-地区”对。语言子标签通常使用ISO 639-1的两位代码首选或639-2/3的三位代码。脚本子标签可选使用ISO 15924四位字母代码用于区分书写系统。这是解决“简体中文”与“繁体中文”问题的关键。Hans- 简体中文Hant- 繁体中文Cyrl- 西里尔字母Latn- 拉丁字母地区子标签可选使用ISO 3166-1的两位字母国家代码或UN M.49的三位数字地区代码。CN- 中国TW- 中国台湾地区US- 美国GB- 英国001- 全球数字代码组合示例与解析zh-CN中文中国大陆地区。这是一个常见的“语言-地区”组合。但它隐含了“使用简体中文”的意思因为中国大陆主要使用简体。然而从标准角度看它并未明确指定脚本。zh-Hans中文简体脚本。这是表示“简体中文”更精确的方式它不绑定特定区域适用于所有使用简体中文的地区如中国大陆、新加坡、马来西亚华人社区。zh-Hans-CN中文简体脚本中国大陆地区。这是最完整、最无歧义的表示明确了语言、书写形式和地理区域。zh-Hant中文繁体脚本。表示“繁体中文”不绑定区域。zh-Hant-TW中文繁体脚本中国台湾地区。常用于台湾地区使用的繁体中文。zh-Hant-HK中文繁体脚本中国香港地区。香港地区使用的繁体中文在词汇、用语上与台湾地区略有不同。en-US英语美国地区。美式英语。en-GB英语英国地区。英式英语。es-ES西班牙语西班牙地区。欧洲西班牙语。es-MX西班牙语墨西哥地区。拉丁美洲西班牙语的一种。实战经验二理解“语言-地区”与“语言-脚本”的区别这是最容易混淆的地方。zh-CN和zh-Hans看似都指向简体中文但侧重点不同。zh-CN强调“在中国大陆使用的语言”其默认脚本是简体但理论上也可能包含其他脚本虽然极少。zh-Hans则纯粹强调“使用简体汉字书写的中文”与地域无关。在涉及内容本地化Localization时优先使用“语言-地区”如zh-CN,en-US因为它包含了地域文化习惯在涉及纯粹的文字呈现或字体选择时使用“语言-脚本”如zh-Hans,zh-Hant更准确。对于中文一个稳妥的实践是在系统内部使用zh-Hans和zh-Hant而在面向用户的界面上显示“简体中文中国”、“繁体中文台湾”等友好名称。3. 核心语言缩写列表与使用指南下面我将结合ISO 639-1和BCP 47整理一份在软件开发、内容管理中最常遇到的语言标签列表并附上关键说明。3.1 全球主要语言基于ISO 639-1ISO 639-1 代码英语名称本地名称示例典型BCP 47扩展语言-地区主要使用地区/说明arArabicالعربيةar-SA(沙特),ar-EG(埃及)阿拉伯语注意是从右向左书写(RTL)。deGermanDeutschde-DE(德国),de-AT(奥地利),de-CH(瑞士)德语。瑞士德语(de-CH)在日期、数字格式上有所不同。enEnglishEnglishen-US(美国),en-GB(英国),en-AU(澳大利亚)必须区分地区拼写、日期、货币格式差异大。esSpanishEspañoles-ES(西班牙),es-MX(墨西哥),es-AR(阿根廷)西班牙语变体极多词汇、发音、甚至语法有差异。frFrenchFrançaisfr-FR(法国),fr-CA(加拿大),fr-BE(比利时)加拿大法语(fr-CA)与法国法语在词汇和用语上区别明显。itItalianItalianoit-IT意大利语。jaJapanese日本語ja-JP日语。通常不需要进一步细分。koKorean한국어ko-KR韩语。ptPortuguesePortuguêspt-PT(葡萄牙),pt-BR(巴西)欧洲葡萄牙语和巴西葡萄牙语差异巨大务必区分。ruRussianРусскийru-RU俄语。使用西里尔字母。zhChinese中文见下方详细分解中文情况最复杂必须处理简繁体。3.2 中文的详细分解重点与难点中文处理是国际化中的重中之重也是最易出错的部分。下面这个表格清晰地展示了各种组合BCP 47 标签说明常见对应友好名称使用场景建议zh中文泛指中文避免单独使用歧义太大。zh-Hans中文简体脚本简体中文推荐。用于表示所有简体中文内容不绑定地区。zh-Hant中文繁体脚本繁体中文推荐。用于表示所有繁体中文内容不绑定地区。zh-CN中文中国大陆地区简体中文中国隐含简体。适用于针对中国大陆市场的完整本地化。zh-SG中文新加坡地区简体中文新加坡新加坡简体用词习惯与大陆略有不同。zh-TW中文中国台湾地区繁体中文台湾台湾繁体。注意政治敏感性在列表中宜与zh-Hant-TW等同。zh-HK中文中国香港地区繁体中文香港香港繁体。zh-MO中文中国澳门地区繁体中文澳门澳门繁体。zh-Hans-CN中文简体脚本中国大陆简体中文中国大陆最精确、最无歧义的表示法。zh-Hant-TW中文繁体脚本台湾地区繁体中文台湾最精确、最无歧义的表示法。zh-Hant-HK中文繁体脚本香港地区繁体中文香港最精确、最无歧义的表示法。实战经验三中文标签的存储与显示策略内部存储在数据库、配置文件、代码常量中强烈建议使用zh-Hans和zh-Hant。这分离了“书写系统”和“地域文化”逻辑更清晰。例如你可以用zh-Hans来标记一份文档的书写形式而用CN、SG来标记其目标市场。用户界面显示给用户选择时不要显示zh-Hans这样的代码。应该显示本地化的语言名称如“简体中文”、“繁體中文”。如果需要区分地区可以显示“简体中文中国大陆”、“繁體中文香港”。HTTPAccept-Language浏览器发送的通常是zh-CN,zh;q0.9,en;q0.8。你的后端服务应该能正确解析并将zh-CN映射到你的zh-Hans资源或者更精确的zh-Hans-CN资源如果你有。字体回退在CSS中你可以针对不同脚本指定字体font-family: “PingFang SC”, “Microsoft YaHei”, sans-serif;对应zh-Hans而font-family: “PingFang TC”, “Microsoft JhengHei”, sans-serif;对应zh-Hant。3.3 其他需要特别注意的语言塞尔维亚语它同时使用西里尔字母和拉丁字母书写。可以使用sr-Cyrl塞尔维亚语西里尔字母和sr-Latn塞尔维亚语拉丁字母来精确区分。维吾尔语在中国新疆地区使用阿拉伯字母书写。标准代码是ugISO 639-1完整标签可以是ug-Arab-CN。粤语作为中文的一种主要方言在ISO 639-3中有独立代码yue。在YouTube等平台你可以看到yue作为一个可选语言。但在大多数产品中它通常被归入zh-Hant或zh-Hans下的变体处理。库尔德语有ku库尔德语泛称、kmr北库尔德语常用、ckb中库尔德语等代码书写系统也有拉丁和阿拉伯之分。4. 在实战中应用从配置到代码理解了标准和列表关键在于如何用起来。下面我以几个典型场景为例说明如何实际操作。4.1 场景一网站/应用国际化i18n配置假设你使用流行的i18n库如 i18next, react-i18n, vue-i18n。1. 资源文件命名与组织一种清晰的结构是按BCP 47标签建立文件夹或文件名。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为什么这样组织这分离了语言和地区。zh-Hans下的资源可以被zh-CN和zh-SG共享基础翻译然后通过地区特定的扩展或覆写来调整用词差异例如“软件”在大陆叫“软件”在新加坡可能更常用“软体”虽然都是简体。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, }, });关键配置解析nonExplicitSupportedLngs: true这个选项至关重要。当浏览器语言是zh-CN时检测器会依次尝试加载zh-CN-zh-en的资源。由于我们支持zh-Hansi18next在zh-CN失败后会聪明地尝试去掉地区码然后匹配到zh但我们的支持列表里只有zh-Hans和zh-Hant。此时nonExplicitSupportedLngs会尝试将zh-CN转换为zh然后寻找最匹配的支持语言。通常你需要配置loadPath逻辑或使用后端映射将zh-CN的请求指向zh-Hans资源。更稳健的后端映射示例Node.js/Expressapp.get(/locales/:lng/:ns.json, (req, res) { let { lng, ns } req.params; // 语言标签映射 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 场景二数据库存储用户语言偏好在设计用户表时如何存储language字段方案A简单推荐使用VARCHAR(5)存储BCP 47语言标签。CREATE TABLE users ( id INT PRIMARY KEY, username VARCHAR(50), language VARCHAR(5) DEFAULT en -- 存储如 zh-Hans, en-US, ja );优点直接、灵活可以存储最精确的标签。前端提交什么就存什么。缺点查询和统计时需要处理例如统计所有中文用户需要查询LIKE zh%或更复杂的解析。方案B规范化分离语言和地区。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 );优点结构清晰便于基于语言、地区、脚本进行多维度的分析和查询。缺点复杂度高写入和读取时需要组合/解析。对于大多数应用来说过度设计。我的建议对于90%的应用方案A足够了。使用短字符串存储完整标签并在业务逻辑层建立一套清晰的映射和回退规则。例如当用户选择“中文简体”时前端可以提交zh-Hans当从浏览器获取到zh-CN时通过后端映射或配置将其关联到zh-Hans资源。4.3 场景三操作系统与浏览器环境识别在Node.js后端或浏览器端你需要获取系统或环境的语言设置。浏览器端// 获取浏览器优先语言数组 const browserLanguages navigator.languages; // 例如[zh-CN, zh, en-US, en] // 获取单个首选语言 const userLanguage navigator.language || navigator.userLanguage; // 例如zh-CN注意navigator.language返回的是单个字符串而navigator.languages返回一个按优先级排序的数组后者更可靠。你应该用这个数组作为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]); // 返回匹配的第一个实战经验四语言检测的优先级策略永远不要完全依赖自动检测。应提供一个明显的语言切换器让用户手动选择。自动检测的逻辑应该是1) 用户上次手动选择并保存的语言2) URL参数如?langzh-Hans3) Cookie或本地存储4) 浏览器Accept-Language头5) 应用默认语言如英语。用户的明确选择权必须高于系统的猜测。5. 常见陷阱与最佳实践总结在多年与多语言打交道的经历中我踩过不少坑也总结出一些铁律。陷阱一混淆“语言”与“区域设置”en是语言en-US是区域设置。区域设置除了语言还包含数字格式1,234.56 vs 1.234,56、日期格式MM/DD/YYYY vs DD/MM/YYYY、货币符号$ vs €、排序规则等。如果你的应用只做了文本翻译但数字、日期格式还是原来的样子体验会非常割裂。解决方案是使用完整的国际化库如JavaScript的IntlAPI它能根据语言标签自动处理这些格式。陷阱二硬编码语言列表不要在代码里写死const languages [en, zh-CN, ja]。一旦要新增一种语言就需要改代码、重新部署。最佳实践是将支持的语言列表作为配置文件如i18n.config.json或从后端API动态获取。这样运营人员或产品经理可以在后台管理界面直接添加新语言前端自动呈现。陷阱三翻译资源的键名使用源语言错误示例{ “Hello”: “你好”, “Goodbye”: “再见” }。当源语言如英语的键名需要修改时“Hello”改成“Hi”所有其他语言的翻译文件都需要同步修改键名极易出错。正确做法是使用业务逻辑相关的、语义化的键名。 正确示例{ “greeting.hello”: “你好”, “greeting.goodbye”: “再见” }。英文文件里是{ “greeting.hello”: “Hello”, “greeting.goodbye”: “Goodbye” }。这样无论源语言文本如何变化键名稳定不变。陷阱四忽略文本长度变化德语单词通常比英语长中文短语通常比英文短。UI设计时必须考虑文本扩展通常预留30%-50%的额外空间和收缩。使用CSS属性如text-overflow: ellipsis时要小心确保关键信息不被截断。对于按钮、标签等固定宽度的元素可以采用最小宽度min-width或弹性布局。陷阱五缺乏上下文给翻译者把一句“Save”扔给翻译者他可能翻译成“保存”动词或“储蓄”名词。必须提供上下文。专业的做法是使用带有描述性的键名并在翻译管理平台如Crowdin, Transifex或JSON文件中添加注释。{ “button.save”: { “description”: “The label for the button that saves a document”, “defaultMessage”: “Save” } } // 在中文文件中 { “button.save”: “保存” }最佳实践清单内部统一使用BCP 47标签优先使用“语言-脚本”如zh-Hans或“语言-地区”如en-US形式。建立中央映射表处理浏览器语言标签到内部标签的转换如zh-CN-zh-Hans。分离“文本翻译”和“区域格式化”使用Intl等标准API处理日期、数字、货币。设计弹性的UI适应文本长度变化。永远提供手动语言切换入口并持久化用户选择。对翻译资源进行版本控制并与代码版本关联。在开发早期就引入国际化框架而不是事后补救。
返回列表