如何使用i18next处理大量翻译文件并自动生成唯一key注入代码
i18next 自动生成唯一翻译key落地方案
核心逻辑是把「手动写key、手动维护翻译文件」的步骤放到编译/开发阶段自动完成,代码里只需要写原始文案即可,和react-intl的开发体验一致,针对1000+条文案的项目能减少90%以上的重复工作量。
方案一:Babel插件编译期注入(生产级项目首选,零侵入)
通过Babel在代码编译阶段扫描所有文案位置,自动生成唯一key、替换成i18next的t调用、同步写入多语言文件,全程不需要手动干预。
- 基础依赖用
babel-plugin-i18next-extract,不要用默认的key生成规则,自定义「文件相对路径+行号+原文短hash」的规则,保证key唯一、可溯源,不会因为文案微调丢失历史翻译关联。 - 配置示例:
// babel.config.js const crypto = require('crypto') module.exports = { plugins: [ [ "i18next-extract", { keyAsDefaultValue: true, // 翻译缺失时直接展示原文,不会出现空白占位 keyGenerator: (filePath, line, column, originalText) => { const shortPath = filePath.replace(process.cwd(), '').replace(/\//g, '.').replace(/^\./, '') const textHash = crypto.createHash('md5').update(originalText).digest('hex').slice(0, 6) return `${shortPath}:${line}:${textHash}` }, outputPath: "src/locales/{{locale}}.json", // 提取到的新key自动写入对应语言包 locales: ["zh-CN", "en-US", "ja-JP"], // 配置项目需要的语言列表 discardOldKeys: true // 构建时自动清除代码里已经不存在的冗余key,避免翻译文件膨胀 } ] ] }
- 开发时直接写原生文案即可,不需要手动写t函数和key:
// 开发者编写的代码 <div>提交订单</div> <button>确认支付</button> // 编译后自动转换的代码 <div>{t('src.pages.order.index:24:a1b2c3', '提交订单')}</div> <button>{t('src.pages.order.index:25:d4e5f6', '确认支付')}</button>
*如果项目用TypeScript,搭配i18next-parser生成key的类型声明,编译时就能校验key是否存在,避免线上出问题。如果用react-i18next的Trans组件写嵌套JSX文案(比如带链接、加粗标签的文本),插件也能自动识别提取,不需要手动拆分文案。
方案二:轻量封装方案(适合不想改构建配置的中小项目)
自己封装一层翻译函数,在开发环境自动扫描调用位置生成key、写入翻译映射文件,生产环境直接读取预生成的key映射,改造成本极低。
- 封装示例:
// src/utils/i18n.js import i18n from 'i18next' const crypto = require('crypto') const fs = require('fs') const path = require('path') const keyMapPath = path.resolve(__dirname, '../locales/keyMap.json') const keyMap = require(keyMapPath) const generateKey = (filePath, line, text) => { const shortPath = filePath.replace(process.cwd(), '').replace(/\//g, '.').replace(/^\./, '') const textHash = crypto.createHash('md5').update(text).digest('hex').slice(0, 6) return `${shortPath}:${line}:${textHash}` } export const $t = (text, options = {}) => { if (process.env.NODE_ENV === 'development') { // 从调用栈取当前调用的文件和行号 const callStack = new Error().stack.split('\n')[2] const matchRes = callStack.match(/\((.+):(\d+):\d+\)$/) || callStack.match(/at (.+):(\d+):\d+$/) if (matchRes) { const [, filePath, line] = matchRes const key = generateKey(filePath, line, text) if (!keyMap[key]) { keyMap[key] = { 'zh-CN': text } fs.writeFileSync(keyMapPath, JSON.stringify(keyMap, null, 2)) } return i18n.t(key, { defaultValue: text, ...options }) } } // 生产环境直接读取预先生成好的key映射 const existKey = Object.keys(keyMap).find(k => keyMap[k]['zh-CN'] === text) return i18n.t(existKey || text, { defaultValue: text, ...options }) }
- 使用时直接调用
$t('提交订单')即可,不需要手动维护key。
关键避坑点
- 不要直接用原文作为key:一旦文案修改一个字,所有语言的翻译关联会直接断开,带位置+短hash的key就算文案调整,只会生成新key,旧翻译可以批量迁移
- 不要用自增数字作为key:多人协作时极易出现key冲突,合并代码时冲突率极高
- 每次构建加key扫描逻辑:自动清理代码中已经不再引用的旧key,避免翻译文件堆到几千条无效内容
内容的提问来源于stack exchange,提问作者w x
相关产品推荐
相关产品推荐

