
前端CMS【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址https://gitcode.com/gh_mirrors/wp/wp-calypso点击查看免费下载本文围绕 packages/babel-plugin-i18n-calypso/README.md 展开深入剖析这个 Babel 插件如何在构建期将代码中的translate、__、_n、_x、_nx等翻译调用静态提取为 POT 文件。读完本文你将掌握它的安装配置、dir/base两个核心选项的语义与默认值、五种翻译函数的参数映射规则以及 translator 注释、复数、上下文msgctxt等提取细节背后的源码级实现原理能够把同样的 POT 提取管线复用到任意 JavaScript/TypeScript 项目中。一、插件定位静态分析管线中的提取器在 wp-calypsoWordPress.com 的 JavaScript 前端中国际化流程是一条完整的自动化管线开发者在源码中调用translate()来自 i18n-calypso或__/_n/_x/_nx来自wordpress/i18n书写待翻译文案构建期由 Babel 插件对源码做静态分析static analysis把翻译调用抽出来每个源文件生成一个.pot文件再由 packages/wp-babel-makepot 这类工具按 preset 批量处理、合并POT 交给 GlotPress 等平台翻译最终生成本地化运行时使用的 locale JSON。babel-plugin-i18n-calypso正是第 2 步的核心实现——它本身是 Babel 的一个 visitor 插件源码仅有一个文件 packages/babel-plugin-i18n-calypso/src/index.js入口在 package.json 中声明为main: src/index.js依赖gettext-parser^4.0.3完成 POT 数据编译并以babel/core^7.27.1作为 peer 依赖。之所以必须是静态提取是因为 i18n-calypso 的 README 明确要求翻译字符串只能以字面量形式传入translate()禁止使用变量、三元表达式、函数调用等动态形式唯一的例外是用拼接长字符串。这与本插件的提取策略完全呼应——它只认 AST 中的字符串字面量节点。二、安装与基础配置UsageREADME 给出的用法非常直接把插件加入 Babel 配置的plugins数组即可。{ plugins: [ automattic/babel-plugin-i18n-calypso ] }在 wp-calypso 主仓库中这一插件并非直接手写进根 babel.config.js而是通过automattic/calypso-babel-config统一装配并显式指定了 POT 输出目录module.exports babelConfig( { isBrowser: process.env.BROWSERSLIST_ENV ! server, outputPOT: path.join( __dirname, build/i18n-calypso/ ), importSource: emotion/react, } );可以看到Calypso 将提取出的 POT 集中输出到build/i18n-calypso/目录同时插件还有配套的批量处理 CLI 工具 wp-babel-makepot支持 glob 输入、ignore 模式、并行 job 数与最终合并例如wp-babel-makepot ./src/**/*.{js,jsx,ts,tsx} -i **/*.d.ts -b ./src -d ./build -o ./build/bundle-strings.pot -j auto三、Options 详解dir与baseREADME 只公开了两个配置项但它们决定了 POT 文件的落盘位置与参考注释reference comment的可读性。源码中的默认值与实现逻辑如下见 src/index.js选项作用默认值源码位置dirPOT 文件输出目录build/DEFAULT_DIRL52base计算源码相对路径的基准目录用于 reference comment 与输出文件名.state.opts.base || .L3063.1dirPOT 落到哪里每次处理完一个文件Program 节点退出时插件都会执行L367-373const dir state.opts.dir || DEFAULT_DIR; ! existsSync( dir ) mkdirSync( dir, { recursive: true } ); const pathname relative( base, filename ).split( sep ).join( - ); writeFileSync( dir pathname .pot, compiled );三个关键点目录不存在时会自动递归创建mkdirSync( ..., { recursive: true } )每个源文件独立产出一个.pot输出文件名是把源码相对路径中的所有路径分隔符替换为-。例如client/my-sites/foo.js会生成build/client-my-sites-foo.js.pot这也解释了为什么需要 wp-babel-makepot 这样的上层工具做最终的 POT 合并见 packages/wp-babel-makepot/utils/concat-pot.js 与 merge-with.js。3.2basereference comment 的路径基准每个被提取的翻译条目都会附带一行参考注释格式为相对路径:行号L305-308const { filename } this.file.opts; const base state.opts.base || .; const pathname relative( base, filename ).split( sep ).join( / ); translation.comments.reference pathname : path.node.loc.start.line;例如以仓库根为base时位于client/my-sites/foo.js第 42 行的翻译调用会生成#: client/my-sites/foo.js:42 msgid My hat has three corners. msgstr 参考注释对翻译流程非常重要翻译平台可以借此把某条译文精确对应回源码位置方便审校与追踪。设置恰当的base能剔除机器本地的绝对路径前缀保证不同开发者、CI 环境下生成的 POT 路径一致。3.3 源码支持的隐藏选项除 README 公开的两项外从源码可以看到插件还支持另外两个选项headersL273覆盖 POT 文件头部的元信息。默认值为DEFAULT_HEADERSL43-46const DEFAULT_HEADERS { content-type: text/plain; charsetUTF-8, x-generator: babel-plugin-i18n-calypso, };functionsL310覆盖/追加函数参数顺序映射可让插件识别自定义翻译函数。四、五种翻译函数的参数映射DEFAULT_FUNCTIONS_ARGUMENTS_ORDER插件如何知道某个函数调用的哪个实参是复数形式、哪个是上下文答案在 L58-64 的映射表中const DEFAULT_FUNCTIONS_ARGUMENTS_ORDER { __: [], _n: [ msgid_plural ], _x: [ msgctxt ], _nx: [ msgid_plural, null, msgctxt ], translate: [ msgid_plural, options_object ], };提取逻辑CallExpression visitorL243-352的规则是第一个实参永远作为msgid原文从第二个实参开始按上表逐位对应msgid_plural→ 存入复数形式msgctxt→ 存入上下文null表示该位置参数如_nx的 count 数字不参与提取options_object即translate()的第三个对象参数会进入对象属性遍历L316-329提取context属性写入msgctxt提取comment属性写入译者注释comments.extracted。各函数实际提取效果如下表调用示例提取结果__( My hat has three corners. )msgid: My hat has three corners._n( day, days, n )msgid: day、msgid_plural: days_x( post, verb )msgid: post、msgctxt: verb_nx( day, days, n, calendar )msgid: day、msgid_plural: days、msgctxt: calendartranslate( day, days, { count: n } )msgid: day、msgid_plural: daystranslate( post, { context: verb, comment: ... } )msgid: post、msgctxt: verb、译者注释i18n-calypso的translate()还支持把comment、context放进 options 对象见 i18n-calypso 的 Options 说明插件正是通过对ObjectExpression属性的遍历ObjectProperty且键名为context/comment把它们落到 POT 条目上。4.1 别名alias自动识别如果开发者对i18n-calypso的translate做了重命名导入如import { translate as t } from i18n-calypso插件会在 ImportDeclaration visitor 中自动注册别名L229-241if ( i18n-calypso ! path.node.source.value ) { return; } path.node.specifiers.forEach( ( specifier ) { if ( specifier.imported translate specifier.imported.name specifier.local ) { functions[ specifier.local.name ] functions.translate; } } );这意味着只要别名确实来自i18n-calypso的具名导入后续t( ... )也能被正常提取。函数名判定则发生在 CallExpression visitor 中L246-255支持直接调用translate( ... )与属性调用i18n.translate( ... )通过MemberExpression.property取名再经isValidFunctionName校验是否存在于映射表L160-162。五、字符串取值规则getNodeAsString提取 msgid 等字符串时插件并非直接读 AST 文本而是通过 getNodeAsString 对节点做递归取值AST 节点类型取值方式StringLiteral直接取node.valueBinaryExpression递归拼接左、右操作数即支持a b字面量拼接TemplateLiteral拼接所有quasis的element.value.cooked模板字符串其他类型返回空字符串条目被跳过这正好印证了 i18n-calypso 的规范translate()的参数必须是字符串字面量唯一允许的动态写法就是用拼接多个子串。变量、三元表达式、函数调用传入的字符串不会被提取返回值长度为空时L264-266 会直接return丢弃该条目。这也是所有基于静态分析的翻译提取工具共有的硬性约束。六、translator 注释提取给翻译者的话除了translate( ..., { comment: ... } )中的 comment 选项插件还支持源码中translators:前缀注释gettext 生态的通用约定。正则定义在 L70const REGEXP_TRANSLATOR_COMMENT /^\s*translators:\s*([\s\S])/im;例如// translators: 显示在个人资料页的按钮文案 translate( Update Profile )提取出的注释会作为comments.extracted写入 POT 条目。核心逻辑在 getExtractedComment该函数被单独导出以便单测只接受与调用节点同行、或紧邻上一行line _originalNodeLine - 1则跳过的leadingComments命中多个匹配时保留第一个keeps the first matching translator comment when several are adjacent匹配失败时会沿parentPath向上递归但仅当父节点也位于同一行或上一行保证注释确实紧贴调用而非属于外层代码块。test/get-extracted-comment.js 中专门有对应的单元测试验证了相邻多个 translator 注释时保留第一个这一行为。七、POT 数据组装与落盘细节7.1 头部信息与复数形式插件首次捕获有效条目时初始化baseDataL270-297默认头部使用DEFAULT_HEADERScontent-type: text/plain; charsetUTF-8与x-generator: babel-plugin-i18n-calypso可被state.opts.headers覆盖若plural-forms头部中带有npluralsN;会解析出复数槽位数默认2复数条目的msgstr会初始化为nplurals个空字符串L337-339保证.pot中形如msgstr[0] 、msgstr[1] 的占位。7.2 同源条目的合并mergeStrings同一字符串在多个位置出现时POT 中应该只有一条记录但带多个参考注释。这由 mergeStrings 完成参考注释按行追加#: fileA.js:10\n#: fileB.js:20extracted译者注释去重合并若已存在的单数条目遇到新的复数条目或反之会补上msgid_pluralIn PO files those are merged。最终由 buildPotData 把头部条目空 context 下的空 msgid与提取结果按 context 分组合并再交给po.compile序列化为标准 POT 文本。一个典型的输出片段msgid msgstr content-type: text/plain; charsetUTF-8;\n x-generator: babel-plugin-i18n-calypso;\n #: client/my-sites/foo.js:12 msgctxt verb msgid post msgstr #: client/my-sites/foo.js:42 msgid My hat has three corners. msgid_plural My hats have three corners. msgstr[0] msgstr[1] 7.3 生命周期Program 进入清场、退出落盘插件在 Program enter 时重置strings与functions保证跨文件无残留在 Program exit 时若没有任何可提取条目则直接跳过否则编译 POT 数据、创建目录并写出.pot文件。八、工程化与测试该插件是一个独立 npm 包automattic/babel-plugin-i18n-calypso当前版本1.2.0见 package.json发布配置为公开访问publishConfig.access: public可在任意 Babel 工程中直接复用。仓库内还包含完整的包级工程配置jest.config.js单元测试配置tsconfig.jsonTypeScript 类型环境用于 IDE 与工具链插件本体为 CommonJStest/get-extracted-comment.js针对 translator 注释提取逻辑的回归测试验证相邻多个注释保留第一个源码末尾通过module.exports.getExtractedComment与module.exports.buildPotDataL381-383暴露内部函数供测试直接注入 AST-path 形状的数据无需完整 Babel 遍历即可验证核心算法。九、在实际项目中复用的建议结合 README 与源码把该插件接入自有项目的要点可归纳为配置在 Babel 配置中注册插件按需设置dir输出目录默认build/与base参考路径基准默认当前目录多文件场景建议配一个build/之外的隔离目录以便后续合并。书写规范翻译函数只能接收字符串字面量长文案用拼接复数必须同时提供单复数两个参数需要给翻译者提示时优先用// translators: ...前缀注释同行或紧邻上一行或在translate()的 options 中传comment。上下文同一个英文词在不同场景含义不同时如post的名词/动词用context选项区分插件会将其写入msgctxt。合并与分发插件按文件产出多个.pot可结合 packages/wp-babel-makepot 的 CLI 与合并工具统一收敛为单个 POT 交付翻译平台。至此从源码中的一句translate( Hello! )到构建产物里的#: path/to/file.js:12msgid Hello!整条静态提取链路的每一个环节都已打通——这正是 wp-calypso 数十个业务包共用一套提取语义、保证 GlotPress 导入一致性的底层保障。赞分享前端CMS【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址https://gitcode.com/gh_mirrors/wp/wp-calypso点击查看免费下载相关推荐wp-calypso 国际化指南i18n-calypso、翻译分块与多语言布局实战wp calypso 国际化指南i18n calypso、翻译分块与多语言布局实战 导读 wp calypso https://link.gitcode.co前端CMSwp-calypso 新版 Dashboard 国际化i18n实践指南wordpress/i18n 翻译规范与 CSS 逻辑属性wp calypso 新版 Dashboard 国际化i18n实践指南wordpress/i18n 翻译规范与 CSS 逻辑属性 本文聚焦 wp cal前端CMSwp-calypso i18n-utils 模块解析客户端与服务端通用的国际化工具及「加载用户待审翻译」调试指南wp calypso i18n utils 模块解析客户端与服务端通用的国际化工具及「加载用户待审翻译」调试指南 本文围绕 wp calypso 仓库中 cl前端CMS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考