因为你的 i18n 配置存在于你的记忆和配置文件里,而不在 Agent 正在读的代码中。Cursor 根据眼前的上下文重新生成组件,而直接写 JSX 文本是最快跑通的方式。仓库里没有任何东西报错,字面量就这样上线了。globalize.now 是 AI 驱动的本地化基础设施,能补上这个闭环\:提取新字符串、生成翻译键,并在每次 Git 推送时同步翻译。
这是一种没人提醒过你的失效模式。配置 i18n 只需一个下午;真正的难题是,在 Agent 每天重写两次组件的情况下,让它一直保持配置到位。
i18n 配置好之后,Cursor 为什么还总是添加硬编码字符串?
三个事实同时成立,合在一起就注定会出现遗漏。
Agent 看到的是文件,而不是约定。 你让 Cursor 加一个设置面板时,它只会读取正在编辑的组件,最多再加几个相邻文件。你的 locale 目录、命名空间规则,以及“所有面向用户的字符串都必须经过翻译函数”这条规矩,除非你主动放进去,否则都不在它的上下文窗口里。
未翻译的文本是统计意义上的默认选项。 任何模型的训练数据里,绝大多数 React 代码都是直接内联写字符串。Agent 为按钮文案选择概率最高的下一个 token 时,Save changes 光凭出现频率就胜过 t('settings.save')。你要求它每一次都选那个不常见的写法,而它选错了也得不到任何反馈。
什么都不会报错。 这一点最关键。硬编码字符串能通过类型检查,能构建,能渲染,也能通过测试。你的流水线里没有任何红色标记提醒你:德国用户即将看到一条英文 toast。这个缺陷一直是隐形的,直到有人切换语言往下滚动页面。
对比一下缺失的 import 或错误的 prop 类型,Agent 几秒内就会被纠正。未翻译文本是工具链唯一默默放行的一类 bug,所以也是唯一会不断累积的一类。
哪些字符串最容易遗漏?
最后添加的、审查最少的那些。
翻一遍任何起初就支持多语言的 AI 辅助代码库,残留的英文总出现在可预见的位置\:
- 错误和成功提示 toast,尤其是修复其他 bug 时顺手加的
- 空状态和加载文案,写完就没人再审查
- 表单校验消息,常常直接内联在 schema 里
- 无障碍属性\:
aria-label、alt、title - 确认对话框和破坏性操作的文案
catch块里的所有内容
规律很一致。主要流程会被翻译,因为你会看;边缘位置会被硬编码,因为没人用第二种语言去打开它们。
还有一种二阶问题。Agent 在已翻译的代码旁添加字面量时,有时会“好心”自创一个与你命名空间规则不符的翻译键,结果一个文件里是 errors.saveFailed,另一个文件里是 settings_save_error。这样既有未翻译的字符串,翻译键空间也变得支离破碎。如果你还处在这个过程的早期,我们的 AI 生成应用的本地化指南 介绍了最初的清理步骤。
.cursor/rules 或 AGENTS.md 能阻止这种情况吗?
它们有帮助,你应该写一份,但它们只是引导,而非强制约束。
指令文件这一层最近已经标准化。AGENTS.md 于 2025 年作为一种厂商中立的约定出现,现由 Linux 基金会旗下的 Agentic AI Foundation 维护。Cursor、Codex、Copilot、Gemini CLI、Aider、Windsurf 和 Zed 都能原生读取它,也就是说,仓库根目录的一个文件就能覆盖团队用到的所有 Agent。Cursor 还支持限定范围的项目规则,以 .mdc 文件的形式放在 .cursor/rules/ 下,通过 frontmatter 控制描述、文件 glob,以及规则是否始终生效。2026 年的实用配置是:根目录放一份 AGENTS.md 保证通用性,再在适合自动附加的地方配上限定范围的 Cursor 规则。
一份可用的 i18n 配置块如下\:
## Internationalization
- Never write user-facing text as a literal in JSX or in a string passed to a UI component.
- All user-facing text goes through the translation function from `next-intl`.
- Keys follow `feature.element.state` in lowerCamelCase, e.g. `settings.save.error`.
- Add the English value to `messages/en.json` only. Never hand-edit other locale files.
- This includes toasts, empty states, validation messages, `aria-label`, `alt` and `title`.
这确实能明显降低遗漏率,但无法降到零,因为规则文件要和上下文窗口里的其他内容争夺注意力,而且长时间的 Agent 会话会逐渐偏离。把这一层当作降低出现频率的手段,而不是保证。
怎样让未翻译的字符串直接导致构建失败?
添加一条把 JSX 中的字面量视为错误的 lint 规则,再在持续集成中运行它。
标准做法是使用 eslint-plugin-i18next 提供的 no-literal-string 规则。它默认只校验 JSX 标记中的纯文本,而不是文件里的每个字面量,这让它能用在真实的代码库上,而不会被一大堆误报淹没。它支持用正则表达式设置忽略模式,支持排除已知安全的调用函数,还提供仅检查 JSX 标记的模式。
一份起步配置\:
// eslint.config.js
import i18next from 'eslint-plugin-i18next';
export default [
{
files: ['**/*.{jsx,tsx}'],
plugins: { i18next },
rules: {
'i18next/no-literal-string': [
'error',
{
markupOnly: true,
ignoreAttribute: ['data-testid', 'className'],
ignore: ['^[0-9]+$', '^\\s*$'],
},
],
},
},
];
在流水线里运行它,而不只是在编辑器里\:
npx eslint . --max-warnings=0
现在,这个隐形缺陷有了红色标记。对于未翻译的字符串,Agent 得到的反馈闭环和类型错误一样,这意味着它能在同一次会话中自行修复,而不是把问题留给另一种语言的用户去发现。如果你想了解从零开始为 Cursor 项目接入 i18n 的完整流程,可以看我们的 15 分钟 Cursor 与 Next.js 实战教程 和 Cursor 集成页面。
为什么光有一条会失败的 lint 规则还不够?
因为构建变红只能告诉你有一个字符串,却不会替你翻译它。
规则触发之后,仍然得有人选一个符合现有命名空间的翻译键,补上英文源文案,再生成其他语言的翻译。在每天推送好几次的个人项目里,这些工作会被一拖再拖。拖得够久,最“省事”的修法就变成把文件加进忽略列表,结果你又回到起点,还多了一堆配置。
正是这个缺口,把一个已解决的问题变成了反复出现的技术债。第一层和第二层只改变了问题出现的频率和发现的速度,都没有真正干活。
三层方案具体是什么样的?
指令、约束、自动化。每一层都兜住上一层漏掉的部分。
第一层,指令。 在仓库根目录的 AGENTS.md 中写入 i18n 章节,再添加一个限定范围的 .cursor/rules/i18n.mdc,用 globs 指向组件目录。成本:十五分钟,一次性。效果:从源头减少字面量的产生。
第二层,约束。 启用 no-literal-string,并让持续集成在违规时失败。成本:在现有代码库上花一个下午调整忽略模式。效果:没有字面量能悄悄进入主分支,Agent 也能自行纠正。
第三层,自动化。 让提取、生成翻译键和翻译都无需你动手。这正是 globalize.now 所在的一层。它位于 i18n 运行时之上,而不是取而代之:渲染字符串的仍然是 next-intl 或 i18next。globalize.now 会检测新增的硬编码文本,按项目现有的命名空间生成风格一致的翻译键,翻译成所有已配置的 locale,并在每次 Git 推送时把更新后的语言文件提交回仓库。只需配置一次——在 globalize.now 连接仓库——就没有任何手动步骤可以被跳过。你也可以用下面的命令把它安装到 Agent 里\:
npx skills add globalize-now/globalize-skills
套餐每月 5 欧元起;20 欧元的 Starter 套餐每月包含 20 欧元的翻译额度(约 20 万词),超出包含用量的部分按相同的单字符费率计费。不按席位收费,也不按语言收费,注册即送 5 欧元翻译额度,无需绑定银行卡。更多面向工程师的细节见 开发者页面,面向使用 AI 工具开发的人的精简版见 vibe coder 页面。
这三层方案的关键在于,没有任何一层依赖你在上百次 Agent 会话中始终保持自律。规则文件降低出现频率,lint 规则让失败一目了然,自动同步让修复不费力气。这套组合能让多语言应用在 Agent 不断重写的情况下依然保持多语言。
脆弱的并不是你的 i18n 配置,而是 Agent 写下字符串到该字符串进入语言文件之间的那个闭环。globalize.now 在每次推送时补上这个闭环。
globalize.now 把硬编码的应用文案转换为可直接翻译的语言文件,并在你持续发布时保持同步更新。
免费试用 globalize.now