replexica 仓库解析:@lingo.dev/locales 的 locale 解析、校验与多语言名称解析实战指南
2026/9/18 4:44:02 网站建设 项目流程

replexica 仓库解析:@lingo.dev/locales 的 locale 解析、校验与多语言名称解析实战指南

【免费下载链接】replexicaOpen-source localization engineering tools. Connects to Lingo.dev localization engineering platform for consistent, quality translations.项目地址: https://gitcode.com/GitHub_Trending/re/replexica

本文基于 replexica 仓库中的 packages/locales/README.md 及packages/locales包源码,系统讲解@lingo.dev/locales的核心能力:把"en-US""zh-Hans-CN"这类 locale 字符串拆解为语言/文字/地区组件、按 ISO 标准校验其真实性、并获取 200+ 语言的国名/语言名/文字名。读完本文,你将能在任何 Node.js / TypeScript 国际化项目中直接落地这套解析、校验与名称解析方案,并理解其底层数据流与性能设计。

一、认识 @lingo.dev/locales:面向国际化工程的 locale 工具包

@lingo.dev/locales是 replexica 仓库中位于 packages/locales 的一个轻量 JavaScript/TypeScript 包(源码包名为@lingo.dev/_locales,详见 package.json)。在 replexica 这套开源本地化工程工具链中,它承担着"语言代码(locale code)处理"这一基础职责:无论是配置多语言项目、校验用户提交的翻译目标语言,还是为语言切换器生成可读的语言名称,都需要先把 locale 字符串变成结构化的、可验证的、可展示的数据。

它提供四类能力:

  • Locale 解析(Locale Parsing):把"en-US""zh-Hans-CN"拆解为 language(语言)、script(文字)、region(地区)三个组件;
  • 校验(Validation):判断 locale 字符串格式是否规范,且其中的语言、文字、地区代码是否对应真实的 ISO 标准代码;
  • 名称解析(Name Resolution):异步获取国家、语言、文字在 200+ 种显示语言下的本地化名称;
  • 工程友好:核心包约 12KB(ESM)/ 14KB(CJS),数据按需加载,完整 TypeScript 类型支持。

安装

npm install @lingo.dev/locales

安装后即可在项目中直接导入(包以 ESM 为主,package.json"type": "module""module": "build/index.mjs""main": "build/index.cjs",同时提供"types": "build/index.d.ts",Node 与打包器均可使用)。该包还声明了"sideEffects": false,配合 tsup 产出的 ESM/CJS 双格式,可以放心交给打包器做 tree-shaking。

二、Locale 解析:从字符串到结构化组件

解析 API 是整套工具的地基。包从 src/index.ts 统一导出parseLocalegetLanguageCodegetScriptCodegetRegionCode

2.1 parseLocale:一次性拆解完整 locale

import { parseLocale, getLanguageCode, getScriptCode, getRegionCode, } from "@lingo.dev/locales"; // 解析完整 locale parseLocale("en-US"); // { language: "en", region: "US" } parseLocale("zh-Hans-CN"); // { language: "zh", script: "Hans", region: "CN" } parseLocale("sr-Cyrl-RS"); // { language: "sr", script: "Cyrl", region: "RS" } parseLocale("es"); // { language: "es" }

返回值是LocaleComponents对象:language必填,scriptregion仅在存在时出现。

2.2 单独提取组件

getLanguageCode("en-US"); // "en" getScriptCode("zh-Hans-CN"); // "Hans" getScriptCode("en-US"); // null(无 script 时返回 null) getRegionCode("en-US"); // "US" getRegionCode("es"); // null(无 region 时返回 null)

getScriptCodegetRegionCode在没有对应组件时返回null,而非空字符串,便于在类型层面区分"缺失"状态。

2.3 源码解析:大小写规范化 + 正则匹配

从源码看,parseLocale的实现位于 src/parser.ts,内部分两步走:

  1. 大小写规范化(normalizeLocaleCase)"EN-US""en-US""en-us""en-US""zh-hans-cn""zh-hans-CN"。规则是:语言部分一律转小写,地区部分一律转大写,文字部分保留原样(脚本代码如Hanshans均按原样保留后交给正则)。
  2. 正则匹配:使用 src/constants.ts 中导出的LOCALE_REGEX
export const LOCALE_REGEX = /^([a-z]{2,3})(?:-_)?(?:-_)?$/;

该正则对每个组件的形态做了明确约束:

组件匹配规则说明
language[a-z]{2,3}2–3 位小写字母,覆盖 ISO 639-1 两位码与 ISO 639-2/3 三位码(如filbar
script[A-Za-z]{4}4 位字母,对应 ISO 15924 文字代码(如HansLatn
region[A-Z]{2}[0-9]{3}2 位大写字母(ISO 3166-1 alpha-2)或 3 位数字(UN M.49,如419

这也解释了为什么parseLocale("es-419")能正确解析出{ language: "es", region: "419" }——地区部分支持数字形式的联合国 M.49 编码。

2.4 补充 API:parseLocaleWithDetails

除了 README 主打的四个函数,源码还导出了一个带诊断信息的解析函数 parseLocaleWithDetails。它不会抛异常,而是返回包含组件、分隔符、是否合法及错误信息的ParseResult,适合需要容错处理的场景:

parseLocaleWithDetails("en_US"); // { // components: { language: "en", region: "US" }, // delimiter: "_", // isValid: true, // } parseLocaleWithDetails("bad input"); // { // components: { language: "" }, // delimiter: null, // isValid: false, // error: "Invalid locale format: bad input", // }

2.5 测试佐证

src/parser.spec.ts 用 Vitest 覆盖了各种形态:连字符/下划线、纯语言、语言-文字-地区、数字地区码、大小写规范化,以及空字符串、非字符串输入抛出对应错误的边界情况("Locale cannot be empty""Locale must be a string")。这些断言与实际源码行为完全一致,可作为理解解析规则的权威参考。

三、校验:确保 locale 代码真实可用

格式合法不等于代码真实。@lingo.dev/locales的校验层会在正则通过后,进一步对照真实 ISO 数据逐项核对。

import { isValidLocale, isValidLanguageCode, isValidScriptCode, isValidRegionCode, } from "@lingo.dev/locales"; // 校验完整 locale isValidLocale("en-US"); // true isValidLocale("en_US"); // true(下划线同样支持) isValidLocale("en-FAKE"); // false(地区代码不存在) isValidLocale("xyz-US"); // false(语言代码不存在) isValidLocale("invalid"); // false(格式不合法) // 校验单个组件 isValidLanguageCode("en"); // true isValidLanguageCode("xyz"); // false isValidScriptCode("Hans"); // true isValidScriptCode("Fake"); // false isValidRegionCode("US"); // true isValidRegionCode("ZZ"); // false

3.1 校验背后的标准数据

实现集中在 src/validation.ts,核心是三个预构建的常量集合:

  • 语言代码集合:通过依赖iso-639-3(见 package.json 的 dependencies)把 ISO 639-1 两位码、ISO 639-2 书目/术语三位码、ISO 639-3 三位码全部展开为小写后放入Set。这意味着isValidLocale("fil")(菲律宾语)、isValidLocale("bar")(巴伐利亚语)、isValidLocale("nap")(那不勒斯语)这类没有两位码等价物的三位码语言同样被接受——这正是 validation.spec.ts 中专门验证的场景。
  • 文字代码集合:内嵌 ISO 15924 常用代码表(HansHantLatnCyrlArab等),并区分大小写匹配。
  • 地区代码集合:ISO 3166-1 alpha-2 国家/地区码(如USCNGB)与 UN M.49 数字地区码(如001世界、419拉丁美洲、142亚洲)两个集合取并集,因此isValidLocale("es-419")isValidLocale("en-001")均返回true

isValidLocale的执行顺序是:先做类型与空值检查 → 正则匹配 → 依次校验语言/文字/地区组件,任何一步失败都返回false而非抛异常,天然适合表单校验等"不要中断流程"的调用方。

3.2 测试佐证

src/validation.spec.ts 系统覆盖了合法/非法两面的用例:连字符与下划线、纯语言、三位码语言、三位码 + 文字 + 地区组合、数字地区码;同时拒绝"en-""-US""en-US-"等残缺格式,以及xyz-USen-Fake-USen-ZZ等"格式合法但代码不存在"的输入。

四、名称解析:异步获取多语言的国家/语言/文字名称

解析与校验解决"代码是否真实",名称解析解决"代码如何展示"。三个异步函数getCountryNamegetLanguageNamegetScriptName通过第二个可选参数displayLanguage(默认"en")指定展示语言:

import { getCountryName, getLanguageName, getScriptName, } from "@lingo.dev/locales"; // 国家名称(默认英语) await getCountryName("US"); // "United States" // 指定展示语言 await getCountryName("US", "es"); // "Estados Unidos" await getCountryName("CN", "fr"); // "Chine" // 语言名称 await getLanguageName("en"); // "English" await getLanguageName("en", "es"); // "inglés" await getLanguageName("zh", "fr"); // "chinois" // 文字名称 await getScriptName("Hans"); // "Simplified Han" await getScriptName("Hans", "es"); // "han simplificado" await getScriptName("Latn", "zh"); // "拉丁文"

4.1 实现细节:代码规范化

从 src/names/index.ts 可以看到,三个函数在查询前都会先规范化入参:国家码统一toUpperCase(),语言码统一toLowerCase(),因此getCountryName("us")getCountryName("US")结果一致(集成测试integration.spec.ts中对此有专门断言)。若查询不到对应名称,函数会抛出Country code "XX" not found之类的错误。

4.2 底层数据加载:CLDR + 内存缓存 + 英文回退

名称数据来自 Unicode CLDR(Common Locale Data Repository),加载逻辑在 src/names/loader.ts:

  • 按需拉取loadTerritoryNames/loadLanguageNames/loadScriptNames分别以fetch请求unicode-org/cldr-json仓库中对应语言的territories.json/languages.json/scripts.json,并从data.main[displayLanguage].localeDisplayNames下提取名称映射表——因此包体积不会随语言数量膨胀;
  • 内存缓存:模块级Mapterritories-enlanguages-fr这样的 key 缓存已加载数据,避免同一语言的重复网络请求;
  • 英文回退:若指定语言加载失败,先console.warn提示,再递归回退到英语数据,保证"graceful degradation";
  • 可配置数据源:读取process.env.CLDR_BASE_URL环境变量可覆盖默认的 CDN 地址,便于在离线/内网环境自建镜像;
  • 文字名增强:加载脚本名称时会把Hans-alt-stand-aloneHant-alt-stand-alone这类独立形式的名称提升为Hans/Hant的主名称,使中文简繁体文字的展示名更完整。

由于名称解析涉及网络请求,务必使用await;集成测试 src/names/integration.spec.ts 通过 mock loader 验证了不同展示语言下的查询结果、国家码大写规范化以及加载失败时的错误透传。

五、支持的格式与分隔符

包同时支持连字符(-)与下划线(_)两种分隔符,且可以混合出现在同一 locale 的不同位置:

输入解析结果
en-USen_US{ language: "en", region: "US" }
zh-Hans-CNzh_Hans_CN{ language: "zh", script: "Hans", region: "CN" }

这与LOCALE_REGEX中每段分隔符独立匹配[-_]的设计一致。parseLocaleWithDetails还会在返回结果中记录原始字符串实际使用的分隔符(delimiter: "-" | "_" | null),方便需要还原原始格式的场景。

六、数据来源与性能设计

6.1 三层数据来源

  • Locale 解析:基于正则(LOCALE_REGEX)+ ISO 标准校验,纯本地、零网络开销;
  • 名称解析:使用 Unicode CLDR 数据,覆盖 200+ 种显示语言的国家/语言/文字名称;
  • 校验:以官方 ISO 639-1、ISO 639-2/3(经iso-639-3包)、ISO 15924、ISO 3166-1、UN M.49 标准为依据。

6.2 性能特征

按 packages/locales/README.md 的说明与源码印证:

  • 包体积:核心包约 12KB(ESM)/ 14KB(CJS),本地化名称数据不打包进核心产物,而是按需从远端加载;
  • 运行时数据:按需、按语言、按类别(territories/languages/scripts)分别请求,用多少拉多少;
  • 缓存:内存级Map缓存已加载的数据映射表,重复查询零网络请求;
  • 回退:指定语言数据不可用时优雅回退到英语,保证接口始终可用。

这套设计的直接收益是:解析与校验是同步的、零依赖网络;名称解析是异步的、按需的,两者组合可以在国际化 UI 中做到"首屏只有核心包、展开语言选择器时才拉名称数据"。

七、错误处理:可预测的失败模式

所有函数都有明确的错误处理约定,官方 README 给出了两个典型示例:

try { parseLocale("invalid"); } catch (error) { console.log(error.message); // "Invalid locale format: invalid" } try { await getCountryName("XX"); } catch (error) { console.log(error.message); // "Country code "XX" not found" }

结合源码可以总结出完整的失败模式表:

场景行为错误/返回值
parseLocale传入非字符串抛错Locale must be a string
parseLocale传入空字符串抛错Locale cannot be empty
parseLocale格式不合法抛错Invalid locale format: <输入>
parseLocaleWithDetails任意失败返回结果而非抛错{ isValid: false, error: "..." }
isValid*系列返回boolean,不抛错false
getCountryName/getLanguageName/getScriptName查询不到抛错Country code "XX" not found
名称数据加载失败非英语自动回退英语;英语失败则抛错回退或抛错

因此推荐的用法是:需要硬校验的流程用isValidLocale(返回布尔值);需要解析强保证的用parseLocale(失败即抛错);需要容错解析的用parseLocaleWithDetails(返回结构化结果);需要展示名称的用三个异步名称函数(记得await并捕获错误)。

八、TypeScript 支持与公开类型

包内置完整类型定义(build/index.d.ts),公开的核心类型定义在 src/types.ts:

interface LocaleComponents { language: string; script?: string; region?: string; } type LocaleDelimiter = "-" | "_"; interface ParseResult { components: LocaleComponents; delimiter: LocaleDelimiter | null; isValid: boolean; error?: string; }

同时 src/index.ts 作为统一出口,将所有类型、常量与函数按类别组织导出:类型(LocaleComponents等)、常量(LOCALE_REGEX)、解析函数、校验函数、异步名称函数,便于使用者按需导入并享受完整的编辑器类型提示。

九、综合实战:构建一个"解析 + 校验 + 名称"完整流程

把三组 API 串起来,可以轻松实现国际化项目中常见的"语言配置面板"逻辑:校验用户输入的 locale、拆解出结构化信息、并生成人类可读的展示名。

import { parseLocale, isValidLocale, getCountryName, getLanguageName, } from "@lingo.dev/locales"; async function describeLocale(locale: string, displayLanguage = "en") { if (!isValidLocale(locale)) { return { ok: false as const, reason: `invalid locale: ${locale}` }; } const { language, script, region } = parseLocale(locale); const languageName = await getLanguageName(language, displayLanguage); const countryName = region ? await getCountryName(region, displayLanguage) : null; return { ok: true as const, language, script: script ?? null, region: region ?? null, languageName, countryName, }; } await describeLocale("zh-Hans-CN", "zh"); // { // ok: true, // language: "zh", // script: "Hans", // region: "CN", // languageName: "中文", // countryName: "中国", // }

校验失败时流程立即返回ok: false,不会触发任何网络请求;校验通过后才发起名称解析,与包"按需加载"的性能设计完全一致。

十、源码导航

想深入研读实现,可按以下路径在仓库内继续探索:

  • 统一导出入口:packages/locales/src/index.ts
  • 类型定义:packages/locales/src/types.ts
  • 正则常量:packages/locales/src/constants.ts
  • 解析实现与单测:packages/locales/src/parser.ts、packages/locales/src/parser.spec.ts
  • 校验实现与单测:packages/locales/src/validation.ts、packages/locales/src/validation.spec.ts
  • 名称解析与数据加载:packages/locales/src/names/index.ts、packages/locales/src/names/loader.ts、packages/locales/src/names/integration.spec.ts
  • 构建与依赖配置:packages/locales/package.json
  • 官方文档:packages/locales/README.md

结语

@lingo.dev/locales在 replexica 仓库中是一个小而精的"基础设施型"包:同步的解析与校验保证了正确性,异步按需的名称解析保证了轻量,sideEffects: false+ 完整类型定义保证了工程集成体验。无论你是要在 CLI 工具里校验--lang参数、在前端语言切换器里展示"Español / Français / 中文",还是为翻译流水线规范化 locale 输入,这套 API 都能直接复用——它既是 replexica 本地化工具链的基石,也可以独立成为你自己国际化项目的地基。

【免费下载链接】replexicaOpen-source localization engineering tools. Connects to Lingo.dev localization engineering platform for consistent, quality translations.项目地址: https://gitcode.com/GitHub_Trending/re/replexica

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询