- 前端
- CMS
【免费下载链接】wp-calypso
The JavaScript and API powered WordPress.com
@automattic/i18n-utils是 WordPress.com(wp-calypso 仓库)中面向 client、server、apps 与 packages 统一提供的国际化工具包,负责 URL 本地化、语言环境上下文(locale context)、时区猜测、日期/交付时段换算等核心能力。本文以其 CHANGELOG 的版本演进为骨架,逐条对照 src 目录下的真实源码实现,帮助读者理解每个 API 的用途、行为边界与适用场景,掌握在 Calypso 生态内做多语言改造的完整工具箱。
一、包概览:定位与消费方式
该包在package.json中描述为 "WordPress.com i18n utils.",当前版本1.3.0,采用双构建产物(dist/cjs/index.js与dist/esm/index.js)并声明了sideEffects: false,支持 tree-shaking。其依赖包括@automattic/calypso-config、@automattic/calypso-url、@automattic/languages、@wordpress/compose、@wordpress/i18n等 workspace 内包,对外则要求react ^18.3.1 || ^19.0.0。
从 src/index.ts 的导出可见,包的能力被划分为五个模块:
- URL 本地化:
localizeUrl/useLocalizeUrl/withLocalizeUrl/urlLocalizationMapping; - 语言环境上下文:
LocaleProvider/useLocale/withLocale/useIsEnglishLocale/useHasEnTranslation; - 语言元数据与路径工具:
locales.ts中的语言名单常量与utils.ts中的路径增删、语言判定工具; - 时区工具:
guessTimezone; - 交付窗口(delivery window):UTC 与本地时间之间的换算系列函数;
- 日期工具:相对时间、短日期、数字日期、周首日等格式化函数。
二、React 环境搭建:LocaleProvider 与 locale slug
2.1 使用方式
包内不少 React 工具依赖组件树顶部的 locale slug。README 给出了标准接入方式:
import { LocaleProvider } from '@automattic/i18n-utils'; function renderApp() { return ( <LocaleProvider localeSlug="en"> <div>... your app components go here ...</div> </LocaleProvider> ); }一旦在组件树根部挂载<LocaleProvider>,各组件即可通过useLocale()获取当前语言。
2.2 底层实现:三级回退链
locale-context.tsx 中useLocale的实现揭示了语言 slug 的获取优先级:
<LocaleProvider>提供的值(来自localeContext);@wordpress/i18n的 locale data:通过getWpI18nLocaleSlug()读取i18n.getLocaleData()中的lang/language字段,并订阅其变化(i18n.subscribe);window._currentUserLocale(仅浏览器环境);- 兜底
'en'。
其中mapWpI18nLangToLocaleSlug处理了特殊映射:pt_br/pt-br、zh_tw/zh-tw、zh_cn/zh-cn、zh_sg/zh-sg这类带区域的语言会被保留为带连字符的形式(如pt-br、zh-tw),而其他语言则去掉-_后的后缀(如en_US→en)。
2.3 派生 hooks 与高阶组件
useIsEnglishLocale():判断当前语言是否属于englishLocales(['en', 'en-gb']);useHasEnTranslation():返回一个函数,用于判断某条文案在当前非英文环境下是否已有翻译——英文环境直接返回true,否则委托i18n.hasTranslation;withLocale( InnerComponent ):基于@wordpress/compose的createHigherOrderComponent封装,向组件注入localeprop(locale-context.tsx)。
三、核心主角:localizeUrl 的 URL 本地化机制
3.1 演进脉络(CHANGELOG 视角)
CHANGELOG 记录了该函数的能力演进:
- 1.2.0:新增
preserveTrailingSlashVariation参数,支持在本地化时保留原 URL 是否带尾斜杠的差异; - 1.1.0:新增对
https://apps.wordpress.com类型 URL 的支持; - 1.0.0:初始发布。
3.2 函数签名与核心流程
源码见 localize-url.tsx:
localizeUrl( fullUrl: string, locale?: Locale, // 默认取 getDefaultLocale(),即 wp i18n slug 或 'en' isLoggedIn?: boolean, // 默认 true,影响部分站点是否改写 preserveTrailingSlashVariation?: boolean // 默认 false ): string处理流程可分为五步:
- URL 解析与兜底:用
new URL( fullUrl, 'http://__domain__.invalid' )解析;解析失败或属于无 host 的相对路径(origin等于无效域名)时,原样返回fullUrl; - 统一协议与斜杠:协议统一为
https:;对非.php结尾的路径执行 "trailingslashit"(路径以单斜杠结尾),使后续匹配表能以统一的带尾斜杠形式工作; - 特殊情况:
en.wordpress.com重写为wordpress.com;如果路径首段已经是/{locale}/,则直接返回原 URL(避免重复本地化); - 映射表查找:按"域名 + 首段路径 + 完整路径"构造 lookup 数组,从后往前匹配
urlLocalizationMapping中的键,命中后调用对应的改写器; - 尾斜杠还原:若开启
preserveTrailingSlashVariation,则根据原始 URL 是否带尾斜杠,对改写结果做同样的尾斜杠处理。
3.3 映射表详解:三种改写策略
urlLocalizationMapping(localize-url.tsx)按 host 与路径组织规则,底层复用三类改写器:
| 改写器 | 行为 | 典型应用 |
|---|---|---|
prefixLocalizedUrlPath | 在路径前插入 locale(/es/support) | support、forums、blog、pricing、tos、jetpack.com 等 |
suffixLocalizedUrlPath | 在路径末尾追加 locale(/start/es、/log-in/es) | log-in、start、setup、learn、plans 等 |
setLocalizedUrlHost | 将 host 改写为{locale-subdomain}.{hostname} | wp-admin、wp-login.php |
映射表的关键规则示例:
wordpress.com/support/、wordpress.com/forums/:仅对supportSiteLocales/forumLocales语言前缀化;wordpress.com/go/:非首页内容只对es重写(西班牙语是当时唯一翻译内容的语言);wordpress.com/wp-admin/与wordpress.com/wp-login.php:对magnificentNonEnLocales/wpLoginLocales语言改写子域(如es.wordpress.com/wp-admin/);en.support.wordpress.com、en.blog.wordpress.com、en.forums.wordpress.com:先将 host 归一到wordpress.com,再以路径前缀形式本地化;apps.wordpress.com:对magnificentNonEnLocales前缀化(1.1.0 新增);developer.wordpress.com:/studio/路径在所有支持语言下本地化,其余路径仅localesWithWpcomDeveloperSiteFullySupported(en、es);wordpress.com首页规则:/checkout、/me开头的路径不改写;结尾形如域名的路径(如站点 URL)不改写;其余路径对magnificentNonEnLocales前缀化;wordpress.com/log-in/、wordpress.com/start/、wordpress.com/setup/:已登录用户不改写(isLoggedIn为 true 时直接返回),否则后缀化;wordpress.com/theme/、wordpress.com/themes/、wordpress.com/plugins/:同样遵循"已登录不改写"策略;wordpress.com/learn/:webinars 路径仅es特殊改写,其余走localesWithLearn后缀化;wordpress.com/plans/:仅当路径恰为/plans/且未登录时前缀化;automattic.com/privacy/、automattic.com/cookies/:仅对localesWithPrivacyPolicy/localesWithCookiePolicy前缀化;wordpress.com/help/contact/:未登录时先把/help/替换为/support/再前缀化。
3.4 语言名单常量(locales.ts)
locales.ts 中定义了各类语言白名单,多数对应config中原本的配置项(源码注释中标明 "replaces config(...)"):
i18nDefaultLocaleSlug = 'en';englishLocales = ['en', 'en-gb'];supportSiteLocales(17 种)、forumLocales(20 种)、magnificentNonEnLocales(16 种)、jetpackComLocales、wpLoginLocales、localesForPricePlans、localesWithLearn、localesWithBlog、localesWithGoBlog等;localesToSubdomains子域映射:pt-br → br、br → bre、zh → zh-cn、zh-hk → zh-tw、zh-sg → zh-cn、kr → ko。
3.5 React 封装:useLocalizeUrl 与 withLocalizeUrl
useLocalizeUrl()(localize-url.tsx)从useLocale()获取 provider locale 作为默认语言,返回记忆化的回调,未显式传locale时自动使用当前环境语言;withLocalizeUrl则基于createHigherOrderComponent注入localizeUrlprop,并将组件 displayName 设为LocalizedUrl(...),便于调试。
四、语言与路径工具集(utils.ts)
utils.ts 提供了一批与语言 slug、路径相关的纯函数:
- 语言判定:
isDefaultLocale(对照config('i18n_default_locale_slug'))、isLocaleVariant(是否有parentLangSlug)、isLocaleRtl(是否 RTL 语言)、canBeTranslated(排除en、sr_latin等无翻译集语言)、isMagnificentLocale、isTranslatedIncompletely; - 语言元数据:
getLanguageSlugs()(返回@automattic/languages的全部langSlug)、getLanguage()(按 slug 查找,未命中时拆出父语言 slug 再查)、getMappedLanguageSlug()(no → nb的映射); - 路由参数:
getLanguageRouteParam(name, optional)生成如:lang(cs|de|fr|pl)?的路由参数说明符;getAnyLanguageRouteParam()生成匹配任意类语言代码的宽松参数; - 路径处理:
getLocaleFromPath(取路径末尾的语言 slug)、addLocaleToPath/removeLocaleFromPath(语言在末尾时)、addLocaleToPathLocaleInFront/removeLocaleFromPathLocaleInFront/retrieveLocaleFromPathLocaleInFront(语言在开头时,如/fr/plugins); - 翻译存在性:
translationExists(phrase)复用getWpI18nLocaleSlug(),默认语言返回 true,否则委托hasTranslation; - 语言修订过滤:
filterLanguageRevisions只保留数值型且属于已知语言 slug 的修订号。
其中localeRegexString = '[a-zA-Z]{2,3}(-[a-zA-Z]{2,3})?(_[a-zA-Z]{2,6})?'定义了"语言码(必填)— 区域码(可选)— 变体后缀(可选)"的形态,是getLanguage校验的基础。
五、时区与日期工具
5.1 guessTimezone:基于 Intl 的时区猜测
guess-timezone.ts 通过Intl.DateTimeFormat().resolvedOptions().timeZone获取当前时区,并用一张源于 IANA tzdata backward 文件的linkedTimezones映射表(如Asia/Calcutta → Asia/Kolkata、Europe/Belfast → Europe/London)重写为服务器端使用的规范名称;Intl 不可用时返回undefined。
5.2 date-utils.ts:本地化日期格式化
date-utils.ts 提供四组函数:
getRelativeTimeString({ timestamp, locale, now, style }):基于Intl.RelativeTimeFormat的过去时间相对描述,style支持long/short/narrow;未来时间或异常返回空字符串;getISODateString:输出2020-12-20形式的 ISO 日期;getShortDateString/getNumericDateString:分别用Intl.DateTimeFormat输出"Dec 20, 2021"与"12/20/2021"式本地化日期,失败时回退到 ISO 格式。
5.3 CHANGELOG 1.3.0 重点:getNumericFirstDayOfWeek
1.3.0 引入的getNumericFirstDayOfWeek(locale)(date-utils.ts)是new Intl.Locale(locale).getWeekInfo()的封装,返回 1–7 的周首日数字(1 = 周一,7 = 周日)。其兼容策略为:
- 优先使用新 API
Intl.Locale.prototype.getWeekInfo; - 回退到旧 API
Intl.Locale.prototype.weekInfo; - 两者都不可用时(如截至 2025 年 5 月 Firefox 仍不支持),返回默认值
1(周一),异常同样兜底为 1。
此外 1.3.0 还移除了隐式的 lodash 依赖,并将@wordpress/compose改为 npm 可安装的依赖范围(^8.2.0),方便包外消费者使用。
六、交付窗口(Delivery Window)换算模块
该模块专门服务于"投递时段"类 UI(如订阅投递时间设置),核心目标是在后端 UTC 存储值与用户本地时间展示值之间做正确换算,源码位于 delivery-window 目录。
6.1 数据模型与常量
DeliveryWindow由hour(0–23)与day(0 = 周日,6 = 周六)组成(conversion.ts)。STANDARD_DELIVERY_HOUR_BUCKETS是选择器展示的偶数 2 小时桶(0、2、…、22)。
6.2 关键换算函数
getDeliveryWindowOffsetHours(timezone, reference):用Intl.DateTimeFormat计算给定 IANA 时区相对 UTC 的整小时偏移(正值为早于 UTC,负值为晚于);半小时偏移(如Asia/Kolkata+5:30)会向零方向舍入到最近整小时;时区缺失或非法返回null;fromUtcDeliveryWindow(utc, offsetHours):将存储的 UTC 窗口转为本地展示窗口,小时向下吸附到最近的偶数桶,跨午夜时日序号回绕;toUtcDeliveryWindow(local, offsetHours):将选择器里的本地时间(恒为偶数桶)转回 UTC 存储值,结果不吸附,因而 UTC 小时可能是奇数(保证投递时刻准确);getDisplayDeliveryWindow(utc, offsetHours):offsetHours为null(无法探测设备时区)时直接返回原始存储值,避免仅改日期的编辑意外改动未触碰的小时;applyDeliveryWindowEdit(storedUtc, edit, offsetHours):把选择器的部分编辑合并回 UTC 窗口;UTC 回退模式下只更新edit中出现的字段;getDeliveryHourPickerHours(displayHour, utcFallback):UTC 回退模式返回 0–23 全部整点;本地模式返回标准偶数桶,必要时把当前展示小时补入列表(conversion.ts)。
配套的useDeliveryWindowTimezonehook(use-delivery-window-timezone.ts)为组件提供设备时区的探测与接入。该模块的边界行为(半小时舍入、UTC 回退不吸附、day 回绕)均有单元测试覆盖,可参见 conversion.test.ts。
七、测试与工程实践
包的测试配置在 jest.config.js,脚本yarn test即运行 jest。围绕核心 API 的测试用例包括:
- test/localize-url.js:验证
localizeUrl对各映射规则的改写行为; - test/utils.js:覆盖路径增删、语言判定等工具函数;
- test/locale-context.tsx:验证
LocaleProvider/useLocale的上下文行为; - delivery-window/test/conversion.test.ts:覆盖交付窗口换算的边界情况。
八、快速上手指南
// 1. 组件根部注入 locale import { LocaleProvider, useLocale } from '@automattic/i18n-utils'; // 2. 本地化链接(普通函数,任何环境可用) import { localizeUrl } from '@automattic/i18n-utils'; localizeUrl( 'https://wordpress.com/support/', 'es' ); // → https://wordpress.com/es/support/ // 3. React hook 形式,自动跟随当前 locale import { useLocalizeUrl } from '@automattic/i18n-utils'; const localizeUrl = useLocalizeUrl(); // 4. 交付时段换算 import { fromUtcDeliveryWindow, toUtcDeliveryWindow } from '@automattic/i18n-utils'; // 5. 周首日与相对时间 import { getNumericFirstDayOfWeek, getRelativeTimeString } from '@automattic/i18n-utils';九、总结
从 CHANGELOG 的演进可以看到,@automattic/i18n-utils始终围绕"让多语言体验自动化"这一目标迭代:1.x 早期补齐依赖与localizeUrl的域名/尾斜杠能力,1.2.x 拥抱 React 19 并摆脱 i18n-calypso 与隐式 lodash 依赖,1.3.0 引入原生Intl.Locale.getWeekInfo封装并实现依赖范围的 npm 化。对应源码(localize-url.tsx、locale-context.tsx、utils.ts、date-utils.ts、delivery-window)构成了一个职责清晰、可在 client 与 server 环境复用的国际化工具矩阵,是 wp-calypso 及其衍生应用处理"语言感知 URL、本地化日期时间、时区换算"时可直接引用的标准答案。
- 前端
- CMS
【免费下载链接】wp-calypso
The JavaScript and API powered WordPress.com
相关推荐
wp-calypso 的 js-utils 工具集:从 CHANGELOG 看 Calypso 去 Lodash 化的演进路线
wp calypso 的 js utils 工具集:从 CHANGELOG 看 Calypso 去 Lodash 化的演进路线 @automattic/js u
前端CMSwp-calypso i18n-calypso 国际化指南:从 translate() 到货币格式化的完整实践
wp calypso i18n calypso 国际化指南:从 translate 到货币格式化的完整实践 导读 本文以 wp calypso 仓库中 i18n
前端CMSAG Kit 的 i18n-localization Skill:国际化与本地化的完整实践与检测工具解析
AG Kit 的 i18n localization Skill:国际化与本地化的完整实践与检测工具解析 本文基于 AG Kit 技能库中的 .agents/s
人工智能AI 技能AI 插件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考