ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Vue3+Vue-i18n+I18N ALLY+VSCODE 自动翻译多国语言:把 i18n 配置改到 TaoToken

Vue3+Vue-i18n+I18N ALLY+VSCODE 自动翻译多国语言:把 i18n 配置改到 TaoToken 1. Vue3 项目多语言自动翻译为什么总卡在翻译通道上做 Vue3 国际化的同学大概率都经历过这个流程vue-i18n装好、lang目录建好、setupI18n跑通页面切换语言没问题然后打开 VS Code 里的 I18N ALLY 插件准备把中文文案批量翻成英文、日文。结果点下那个地球图标进度条转两圈弹出一句翻译失败或者干脆静默不动。问题基本不在 Vue3也不在vue-i18n的配置而是卡在 I18N ALLY 的翻译引擎通道上。I18N ALLY 默认走的是 Google Translate、DeepL 这类公共翻译服务这些服务在国内网络环境下直连不稳定插件又没有独立的超时重试和错误提示所以表现就是「点了没反应」。很多人第一反应是去改i18n-ally.translate.engines的引擎顺序把 google 换成 deepl换完发现还是一样因为根因是请求根本发不出去不是引擎选得不对。另一类常见情况是团队里有人能翻、有人不能翻。同一个仓库、同一份.vscode/settings.jsonA 同学点翻译秒出结果B 同学一直失败。这种差异通常来自本机网络环境不同而不是配置写错了。对团队协作来说这种「看运气」的翻译链路非常难受尤其是文案量大、需要中英日三语同步迭代的项目。我试过把翻译请求统一收口到一个稳定的 API 通道上让 I18N ALLY 不再依赖公共翻译服务的直连质量。具体做法是把插件的翻译引擎指向一个兼容 OpenAI 接口规范的端点用统一的 Key 和 Base URL 发请求。这样翻译链路的稳定性就只取决于这个通道本身和本机网络环境解耦团队里每个人拿同一个 Key 就能跑出一样的结果。这篇就按这个思路走先把 Vue3 vue-i18n的基础配置补齐保证 I18N ALLY 能正确识别你的语言文件结构再把 I18N ALLY 的翻译通道改到 TaoToken 的统一 API 上最后做一次真实的批量翻译验证确认中英日三语文案能自动生成并落盘。全程给可复制的配置片段路径和字段名都按实际项目写你照着改就能跑。适合谁看已经在用 Vue3 vue-i18n、语言文件是 JSON 或 TS、想让 I18N ALLY 批量翻译真正跑起来的前端。如果你还没搭 i18n 基础第 2 节会把最小可用的目录结构和入口文件给全跟着建就行。2. 先把 Vue3 vue-i18n 的语言文件结构搭对I18N ALLY 能不能正确识别你的翻译文件取决于两件事localesPaths指向的目录结构以及pathMatcher能不能匹配上文件名。所以这一步不是走流程是直接决定后面插件能不能读到 key。先把结构定下来后面配置才有依据。先装依赖vue-i18n用 9 以上的版本9 之后是 Composition API 风格legacy: false才生效pnpm add vue-i18n9 # 或者 npm install vue-i18n9目录按下面这样建src/locales/lang下每个语言一个入口文件具体文案放在同名子目录里用 JSON 分模块src/ locales/ helper.ts setupI18n.ts useLocale.ts lang/ zh_CN.ts en.ts ja.ts zh_CN/ common.json menu.json en/ common.json menu.json ja/ common.json menu.jsonhelper.ts负责把import.meta.glob扫到的 JSON 模块拼成嵌套对象genMessage的 prefix 参数要和目录名对上// src/locales/helper.ts import type { LocaleType } from /types/i18n; import { set } from lodash-es; export const loadLocalePool: LocaleType[] []; export function setHtmlPageLang(locale: LocaleType) { document.querySelector(html)?.setAttribute(lang, locale); } export function setLoadLocalePool(cb: (loadLocalePool: LocaleType[]) void) { cb(loadLocalePool); } export function genMessage( langs: Recordstring, Recordstring, any, prefix lang ) { const obj: Recordable {}; Object.keys(langs).forEach((key) { const langFileModule langs[key].default; let fileName key.replace(./${prefix}/, ).replace(/^\.\//, ); const lastIndex fileName.lastIndexOf(.); fileName fileName.substring(0, lastIndex); const keyList fileName.split(/); const moduleName keyList.shift(); const objKey keyList.join(.); if (moduleName) { if (objKey) { set(obj, moduleName, obj[moduleName] || {}); set(obj[moduleName], objKey, langFileModule); } else { set(obj, moduleName, langFileModule || {}); } } }); return obj; }setupI18n.ts里availableLocales要把三种语言都列上fallbackLocale设成zh_CN这样某个 key 在日文里缺失时会回退到中文而不是直接显示 key// src/locales/setupI18n.ts import type { App } from vue; import type { I18n, I18nOptions } from vue-i18n; import { createI18n } from vue-i18n; import { setHtmlPageLang, setLoadLocalePool } from ./helper; import { useLocaleStore } from /store/modules/locale; export let i18n: ReturnTypetypeof createI18n; async function createI18nOptions(): PromiseI18nOptions { const store useLocaleStore(); const locale store.getLocalInfo; const defaultLocal await import(./lang/${locale}.ts); const message defaultLocal.default?.message ?? {}; setHtmlPageLang(locale); setLoadLocalePool((loadLocalePool) { loadLocalePool.push(locale); }); return { legacy: false, locale, fallbackLocale: zh_CN, messages: { [locale]: message, }, availableLocales: [zh_CN, en, ja], sync: true, silentTranslationWarn: true, missingWarn: false, silentFallbackWarn: true, }; } export async function setupI18n(app: App) { const options await createI18nOptions(); i18n createI18n(options) as I18n; app.use(i18n); }语言入口文件en.ts里import.meta.globEager的路径要和实际目录一致genMessage的第二个参数传en// src/locales/lang/en.ts import { genMessage } from ../helper; import antdLocale from ant-design-vue/es/locale/en_US; const modules: Recordstring, Recordstring, any import.meta.globEager(./en/**/*.json); export default { message: { ...genMessage(modules, en), antdLocale, }, dateLocale: null, dateLocaleName: en, };zh_CN.ts和ja.ts同理把路径和dateLocaleName换成对应语言。main.ts里在挂载前调用import { setupI18n } from /locales/setupI18n; await setupI18n(app); app.mount(#app);到这里vue-i18n的运行时链路就通了。注意一个容易踩的点import.meta.globEager是 Vite 的写法如果你用的是 webpack 或者 Vite 5 以上globEager已经废弃要换成import.meta.glob(./en/**/*.json, { eager: true })。这个不换modules会是空对象I18N ALLY 读到的语言文件就是空的后面翻译自然没内容可翻。3. 把 I18N ALLY 的翻译通道改到 TaoToken这一步是核心。I18N ALLY 的翻译引擎配置在.vscode/settings.json里默认引擎是 google、deepl 这些。我们要做的是让它走一个兼容 OpenAI 接口规范的端点把 Base URL 和 Key 指向 TaoToken 的统一通道。先拿 Key。打开 TaoToken 控制台在 API Keys 页面创建一个 Key复制出来。这个 Key 后面要写进 VS Code 的配置里所以别直接提交到仓库用环境变量或者本地 settings 隔离。TaoToken 的 API 地址是https://taotoken.net/api兼容 OpenAI 的/v1/chat/completions规范。I18N ALLY 从某个版本开始支持自定义 OpenAI 兼容引擎配置字段是i18n-ally.translate.openai相关的一组参数。完整配置片段如下直接复制到.vscode/settings.json{ i18n-ally.localesPaths: [src/locales/lang], i18n-ally.pathMatcher: {locale}/{namespaces}.{ext}, i18n-ally.enabledParsers: [json, ts], i18n-ally.sourceLanguage: zh-CN, i18n-ally.displayLanguage: zh-CN, i18n-ally.enabledFrameworks: [vue], i18n-ally.keystyle: nested, i18n-ally.sortKeys: true, i18n-ally.namespace: true, i18n-ally.extract.keygenStyle: camelCase, i18n-ally.translate.engines: [openai], i18n-ally.translate.openai.apiKey: ${env:TAOTOKEN_API_KEY}, i18n-ally.translate.openai.baseURL: https://taotoken.net/api, i18n-ally.translate.openai.model: gpt-4o-mini, i18n-ally.translate.openai.maxTokens: 2048, i18n-ally.translate.openai.temperature: 0.2 }几个字段说明一下。i18n-ally.translate.engines只留openai把 google、deepl 去掉避免插件在失败时回退到直连引擎。apiKey用${env:TAOTOKEN_API_KEY}引用环境变量这样 Key 不进仓库。baseURL填https://taotoken.net/api注意不要带/v1插件内部会拼路径。model填你在 TaoToken 上可用的模型 IDgpt-4o-mini这类小模型翻译够用且便宜长文案多的话可以换更大的。环境变量在 macOS/Linux 下写进~/.zshrc或~/.bashrcexport TAOTOKEN_API_KEYsk-你的KeyWindows 用 PowerShell 设置用户级环境变量[Environment]::SetEnvironmentVariable(TAOTOKEN_API_KEY, sk-你的Key, User)设完重启 VS Code让插件读到新的环境变量。如果你不想用环境变量也可以直接把 Key 写进settings.json但一定要把.vscode/settings.json加进.gitignore或者用.vscode/settings.local.json这种本地文件。团队协作时推荐前者每个人本地设自己的 Key。这里有个细节i18n-ally.pathMatcher我写的是{locale}/{namespaces}.{ext}对应的是lang/zh_CN/common.json这种结构。如果你的文案是直接放在lang/zh_CN.ts里而不是子目录 JSON那pathMatcher要改成{locale}.{ext}localesPaths也要相应调整。这个匹配规则和你的实际目录必须一致否则插件读不到文件翻译按钮是灰的。配置改完VS Code 右下角状态栏会出现 I18N ALLY 的图标鼠标悬停能看到当前识别的语言数量和 key 数量。如果显示 0 个 key先检查localesPaths和pathMatcher这两个对不上是最常见的原因。4. 验证一次自动翻译从中文批量生成英日文案配置就绪后做一次真实的批量翻译验证。先准备一份中文文案放在src/locales/lang/zh_CN/common.json{ common: { a: 欢迎使用, b: 保存成功, c: 操作失败请重试, d: 确认删除, e: 加载中 } }对应的en/common.json和ja/common.json先建空对象{}让插件知道这两个语言文件存在。然后在 VS Code 里打开zh_CN/common.json你会看到每个 value 左边有一个小图标鼠标悬停显示当前翻译状态。批量翻译的入口在 VS Code 左下角点 I18N ALLY 的图标打开翻译面板。面板里会列出所有语言和未翻译的 key 数量。找到en和ja右边有一个地球图标点它就会触发批量翻译。插件会把中文 value 作为源文本通过配置的 OpenAI 兼容通道发请求拿到译文后写回对应的 JSON 文件。翻译过程中VS Code 底部状态栏会显示进度。如果配置正确几秒钟内en/common.json会变成{ common: { a: Welcome, b: Saved successfully, c: Operation failed, please try again, d: Confirm deletion, e: Loading } }ja/common.json同理生成日文。翻译完成后打开页面切换语言t(common.a)会随语言变化显示对应文案。这一步验证的是整条链路I18N ALLY 读到中文源文件 → 通过 TaoToken 通道发翻译请求 → 译文写回目标语言文件 →vue-i18n运行时加载。如果你想在命令行里单独验证通道是否通可以用 curl 直接打一次请求确认 Key 和 Base URL 没问题curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -d { model: gpt-4o-mini, messages: [ {role: system, content: Translate the following Chinese text to English. Output only the translation.}, {role: user, content: 欢迎使用} ], temperature: 0.2 }返回的 JSON 里choices[0].message.content就是译文。这个请求通了说明通道没问题插件那边失败就是配置字段的问题不是网络问题。这一步能把「通道不通」和「插件配置错」两类问题分开排障时非常省时间。翻译完成后建议做一次 diff 检查。I18N ALLY 写回 JSON 时会按sortKeys排序如果原来文件里 key 顺序是手写的翻译后顺序会变这是正常的。但要注意它不会覆盖已有译文只填空白 value所以如果你手动改过某个英文文案批量翻译不会把它冲掉。5. 常见报错排查401、local proxy failed、reading choices翻译链路跑不通时报错信息通常藏在 VS Code 的输出面板里。打开「输出」面板右上角下拉选「I18N Ally」能看到每次翻译请求的详细日志。下面按真实遇到的报错逐个排。401 Unauthorized。这个最直接Key 不对或者没读到。先确认环境变量在当前 VS Code 进程里生效在 VS Code 里打开终端echo $TAOTOKEN_API_KEY如果为空说明 VS Code 是从旧的环境启动的重启 VS Code 或者从终端code .启动。如果环境变量有值但还是 401检查 Key 有没有多余空格以及baseURL是不是写成了https://taotoken.net/api/v1多写/v1会导致路径拼成/v1/v1/chat/completions服务端返回 404 或 401。local proxy failed / connect ETIMEDOUT。这个报错说明插件在尝试直连某个地址但连不上。如果你已经把engines改成只留openai还出现这个检查是不是有旧的i18n-ally.translate.google或deepl配置残留插件在 openai 失败时会尝试回退。把settings.json里所有非 openai 的翻译引擎配置删干净。另外确认baseURL是https://taotoken.net/api不要带端口或多余路径。Cannot read properties of undefined (reading choices)。这个报错说明请求发出去了但返回体结构不对插件拿不到choices字段。常见原因是model填的模型 ID 在 TaoToken 上不存在服务端返回了错误 JSON插件按成功响应解析就报这个。去 TaoToken 控制台确认模型 ID 拼写或者先用第 4 节的 curl 命令验证一次看返回体里有没有choices。另一个原因是maxTokens设得太小翻译长文案时被截断返回体不完整适当调大到 2048 或 4096。OAuth / token expired。如果你用的是 Claude Code 或 Codex 这类带 OAuth 的工具链报错可能来自它们的鉴权层而不是 I18N ALLY 本身。这种情况要确认你用的是 API Key 模式而不是 OAuth 模式。TaoToken 的 API Key 在控制台的 API Keys 页面创建和 OAuth 是两套东西。I18N ALLY 只认 API Key不认 OAuth token。翻译按钮灰色 / 显示 0 个 key。这不是翻译通道的问题是插件没读到语言文件。回到第 2 节检查localesPaths和pathMatcher。一个快速验证方法在 VS Code 里按CmdShiftPWindows 是CtrlShiftP输入I18N Ally: Show Status会显示当前识别的 locales 路径和 key 数量。如果路径不对直接改settings.json里的localesPaths。译文写回了但页面不生效。检查vue-i18n的messages是不是在初始化时只加载了当前语言。第 2 节的setupI18n里messages只放了[locale]: message切换语言时靠useLocale里的setLocaleMessage动态加载。如果切换后没生效确认changeLocale里import(\./lang/${locale}.ts)的路径和实际文件对得上以及loadLocalePool 有没有正确 push。排障时记住一个顺序先用 curl 验证通道再看插件输出日志最后查语言文件结构。这三层分开基本能定位到具体是哪一环出的问题。6. 把翻译通道固定下来团队协作才不靠运气多语言项目的翻译链路最怕的不是配置复杂而是「每个人环境不一样」。A 同学能翻、B 同学不能翻问题排查起来没有共同基线。把 I18N ALLY 的翻译请求统一收口到 TaoToken 的 API 通道后团队里每个人拿同一个 Key、同一份settings.json翻译结果就是一致的和本机网络环境无关。实际用下来这套配置有几个值得固化的点。Key 走环境变量.vscode/settings.json进仓库但 Key 不进新同学 clone 下来只需要设一个环境变量就能跑。engines只留openai去掉所有直连引擎避免插件在失败时静默回退到不稳定的通道。model选小模型做翻译成本和速度都合适长文案多的项目再按需换大模型。如果你还在用 Claude Code 做代码补全、用 Cline 做 Agent 任务这些工具的 Base URL 和 Key 也可以指向同一个通道配置方式类似都是填 Base URL API Key Model ID 三件套。统一到一个通道后Key 管理、用量查看、额度控制都在一个地方比每个工具单独配一套省心。翻译文件落盘后建议在 CI 里加一步校验检查各语言文件的 key 是否对齐。I18N ALLY 只负责翻译不负责检查缺失 key某个语言漏翻了它不会报错只会在运行时回退到fallbackLocale。写个简单的 Node 脚本对比zh_CN、en、ja的 key 集合缺 key 就 fail能挡住大部分「上线后发现某语言少文案」的问题。最后留一个实用技巧I18N ALLY 的翻译是按 key 逐个请求的文案多的时候会发很多次请求。如果项目有几百个 key第一次批量翻译会比较慢耐心等进度条走完。后续新增文案时只翻新增的 key速度就快了。翻译完成后记得 review 一遍译文尤其是带占位符的文案比如{count} items机器翻译有时会把占位符顺序调换这个在运行时才会暴露提前看一眼能省不少事。
返回列表