[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-21":3},{"id":4,"title":5,"title_en":6,"abstract":7,"abstract_en":7,"content":8,"content_en":9,"category":10,"banner_id":11,"banner_path":12,"tags":13,"is_recommend":15,"prev_article":16,"next_article":20,"created_at":24},21,"Vue 3 + Vite 项目中实现 i18n","Implementation of i18n in Vue 3 + Vite project","# 在 Vue 3 + Vite 项目中实现 i18n（实战）\n\n## 摘要\n本文以一个真实的 Vue 3 + Vite + TypeScript 项目为例，逐步展示如何接入 `vue-i18n@9`、实现懒加载语言包、在组件与路由中使用翻译、与 Pinia 同步语言状态，并给出验证与发布注意点。示例代码位于项目文件：`src\u002Fi18n.ts`、`src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue` 等。\n\n## 目标读者\n前端工程师、Vue3 使用者、希望把国际化从 PoC 推到生产环境的团队。\n\n## 项目与前提\n- 技术栈：Vue 3 + Vite + TypeScript\n- 状态管理：Pinia（`src\u002Fstores\u002Findex.ts`）\n- UI：Arco Design（全局注册在 `src\u002Fmain.ts`）\n- 关键文件（项目内）：\n  - `src\u002Fi18n.ts` — i18n 初始化与懒加载实现\n  - `src\u002Flocales\u002Fzh.ts` \u002F `src\u002Flocales\u002Fen.ts` — 语言包\n  - `src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue` — 语言切换组件示例\n  - `src\u002Fmain.ts` — 在挂载前加载语言\n  - `src\u002Fstores\u002Findex.ts` — 可选的语言状态同步点\n  - `src\u002Frouter\u002Findex.ts` — 路由 title 国际化示例\n\n---\n\n## 1.。安装\n在项目根目录运行：\n```bash\nnpm install vue-i18n@9\n```\n\n## 2.。初始化（核心思路）\n使用 Composition API 模式实现 `loadLocale(lang)` 做懒加载语言包。核心要点：\n- 初始 messages 为空\n- 使用 `i18n.global.setLocaleMessage` 动态注入语言内容\n- 将用户选择的语言保存在 `localStorage` 中，避免刷新后丢失\n\n关键代码（摘自 `src\u002Fi18n.ts`）：\n```ts\nimport { createI18n } from 'vue-i18n'\nconst DEFAULT_LANG = localStorage.getItem('lang') || 'zh'\nconst i18n = createI18n({\n  legacy: false,\n  globalInjection: true,\n  locale: DEFAULT_LANG,\n  fallbackLocale: 'en',\n  messages: {}\n})\n\nexport async function loadLocale(lang: string) {\n  if (i18n.global.availableLocales.includes(lang)) {\n    i18n.global.locale.value = lang\n    localStorage.setItem('lang', lang)\n    return\n  }\n  const msgs = await import(\u002F* @vite-ignore *\u002F `.\u002Flocales\u002F${lang}.ts`)\n  i18n.global.setLocaleMessage(lang, msgs.default || msgs)\n  i18n.global.locale.value = lang\n  localStorage.setItem('lang', lang)\n}\nexport default i18n\n```\n\n## 3.。","\n# 在 Vue 3 + Vite 项目中实现 i18n（实战）\n\n## 摘要\n本文以一个真实的 Vue 3 + Vite + TypeScript 项目为例，逐步展示如何接入 `vue-i18n@9`、实现懒加载语言包、在组件与路由中使用翻译、与 Pinia 同步语言状态，并给出验证与发布注意点。示例代码位于项目文件：`src\u002Fi18n.ts`、`src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue` 等。\n\n## 目标读者\n前端工程师、Vue3 使用者、希望把国际化从 PoC 推到生产环境的团队。\n\n## 项目与前提\n- 技术栈：Vue 3 + Vite + TypeScript\n- 状态管理：Pinia（`src\u002Fstores\u002Findex.ts`）\n- UI：Arco Design（全局注册在 `src\u002Fmain.ts`）\n- 关键文件（项目内）：\n  - `src\u002Fi18n.ts` — i18n 初始化与懒加载实现\n  - `src\u002Flocales\u002Fzh.ts` \u002F `src\u002Flocales\u002Fen.ts` — 语言包\n  - `src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue` — 语言切换组件示例\n  - `src\u002Fmain.ts` — 在挂载前加载语言\n  - `src\u002Fstores\u002Findex.ts` — 可选的语言状态同步点\n  - `src\u002Frouter\u002Findex.ts` — 路由 title 国际化示例\n\n---\n\n## 1. 安装\n在项目根目录运行：\n```bash\nnpm install vue-i18n@9\n```\n\n## 2. 初始化（核心思路）\n使用 Composition API 模式实现 `loadLocale(lang)` 做懒加载语言包。核心要点：\n- 初始 messages 为空\n- 使用 `i18n.global.setLocaleMessage` 动态注入语言内容\n- 将用户选择的语言保存在 `localStorage` 中，避免刷新后丢失\n\n关键代码（摘自 `src\u002Fi18n.ts`）：\n```ts\nimport { createI18n } from 'vue-i18n'\nconst DEFAULT_LANG = localStorage.getItem('lang') || 'zh'\nconst i18n = createI18n({\n  legacy: false,\n  globalInjection: true,\n  locale: DEFAULT_LANG,\n  fallbackLocale: 'en',\n  messages: {}\n})\n\nexport async function loadLocale(lang: string) {\n  if (i18n.global.availableLocales.includes(lang)) {\n    i18n.global.locale.value = lang\n    localStorage.setItem('lang', lang)\n    return\n  }\n  const msgs = await import(\u002F* @vite-ignore *\u002F `.\u002Flocales\u002F${lang}.ts`)\n  i18n.global.setLocaleMessage(lang, msgs.default || msgs)\n  i18n.global.locale.value = lang\n  localStorage.setItem('lang', lang)\n}\nexport default i18n\n```\n\n## 3. 在入口注册并预加载语言\n在 `src\u002Fmain.ts` 中注册 i18n，并在挂载前加载默认语言，避免首屏闪烁：\n```ts\nimport i18n, { loadLocale } from '.\u002Fi18n'\napp.use(i18n)\nconst lang = localStorage.getItem('lang') || 'zh'\nloadLocale(lang).finally(() => app.mount('#app'))\n```\n\n## 4. 组件层切换（UI 示例）\n项目中示例组件：`src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue`，使用 Arco 图标（或按钮）点击切换语言，调用 `loadLocale` 并写入 `localStorage`。示例要点：\n- 切换时调用 `loadLocale(lang)`\n- 可选同步 Pinia：`useStore().setLanguage(lang)`（如果实现）\n- 可使用 `useI18n()` 监听 `locale`\n\n示例片段（摘录）：\n```vue\n\u003Ctemplate>\n  \u003Cdiv class=\"ub-language-switcher\" @click=\"toggleLanguage\">\n    \u003CIconChineseFill v-if=\"current === 'zh'\"\u002F>\n    \u003CIconEnglishFill v-else \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\nimport { useI18n } from 'vue-i18n'\nconst { locale } = useI18n()\n\u002F\u002F 切换时调用 loadLocale(...)\n\u003C\u002Fscript>\n```\n\n## 5. 懒加载与构建影响\n- 懒加载语言包会把每个语言打成独立 chunk（Vite 的动态 import 行为）。\n- 好处：首包体积小；坏处：构建输出语言包数量增多。\n- 验证：在浏览器 DevTools 的 Network 面板切换语言能看到 language chunk 请求。\n\n## 6. Pinia 同步（可选）\n在 `src\u002Fstores\u002Findex.ts` 中添加 `language` 状态与 `setLanguage` action，使语言成为全局状态：\n```ts\n\u002F\u002F state:\nlanguage: localStorage.getItem('lang') || 'zh',\n\n\u002F\u002F action:\nasync setLanguage(lang: string) {\n  this.language = lang\n  localStorage.setItem('lang', lang)\n  await import('@\u002Fi18n').then(m => m.loadLocale(lang))\n}\n```\n\n## 7. 路由 title 国际化\n把路由 `meta.title` 改为 `meta.titleKey`，并在切换后设置 `document.title`：\n```ts\nimport { tGlobal } from '@\u002Fi18n'\nrouter.afterEach((to) => {\n  const key = (to.meta as any).titleKey\n  if (key) document.title = tGlobal(key as string) as string\n})\n```\n\n## 8. 日期与数字本地化\n- 推荐使用 `dayjs` 或 Intl API。\n- 若使用 dayjs，可在 `loadLocale` 成功后动态加载 dayjs locale 并调用 `dayjs.locale(lang)`。\n\n## 9. 验证步骤\n```bash\nnpm install\nnpm run dev\n```\n验证点：\n- localStorage 中 `lang` 正确（`zh` 或 `en`）\n- 切换语言后页面文本变化\n- Network 面板出现懒加载的 language chunk\n- 如启用，浏览器标签页 title 随路由 key 变化\n\n## 10. 常见问题与调试\n- 未生效：确认 `i18n` 已在 `main.ts` 注册并在挂载前加载语言\n- 懒加载失败：检查语言包路径 `src\u002Flocales\u002F{lang}.ts`\n- 非同步请求需要 locale header：在请求时读取 `i18n.global.locale.value`\n\n## 11. SEO、可访问性与发布建议\n- 使用 `meta.titleKey` 和对应翻译填充 title\u002Fdescription（如需 SSR，需在服务端处理）\n- 为切换控件添加 `aria-label` 或 `a-tooltip` 提升可访问性\n- 发布时附带 2~3 张截图：语言切换、Network 懒加载、路由 title 变化\n\n## 12. 结论与扩展\n通过懒加载语言包和将语言状态与 Pinia 同步，可以把国际化做到既轻量又可维护。下一步可以考虑：机器翻译初稿 + 人工校验，或接入翻译管理平台（如 Crowdin、POEditor）。\n\n---\n","\n# Implementing i18n in Vue 3 + Vite Project\n\n## Summary\nThis article uses a real Vue 3 + Vite + TypeScript project as an example to show step-by-step how to access 'vue-i18n@9', implement lazy loading of language packs, use translation in components and routes, synchronize language state with Pinia, and give verification and release precautions. The sample code is located in the project files: 'src\u002Fi18n.ts', 'src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue', etc.\n\n## Target Audience\nFront-end engineers, Vue3 users, teams looking to take internationalization from PoC to production.\n\n## Project and premise\n- Tech stack: Vue 3 + Vite + TypeScript\n- Status Management: Pinia ('src\u002Fstores\u002Findex.ts')\n- UI: Arco Design (globally registered at 'src\u002Fmain.ts')\n- Key Documents (In-Project):\n  - 'src\u002Fi18n.ts' — i18n initialization and lazy loading implementation\n  - 'src\u002Flocales\u002Fzh.ts' \u002F 'src\u002Flocales\u002Fen.ts' — language pack\n  - 'src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue' — Language switching component example\n  - 'src\u002Fmain.ts' — Loads the language before mounting\n  - 'src\u002Fstores\u002Findex.ts' — Optional language state sync point\n  - 'src\u002Frouter\u002Findex.ts' — example of internationalization of route titles\n\n---\n\n## 1. Installation\nRun at the project root:\n```bash\nnpm install vue-i18n@9\n```\n\n## 2. Initialization (Core Idea)\nUse the Composition API pattern to implement 'loadLocale(lang)' to lazily load language packages. Key takeaways:\n- The initial message is empty\n- Dynamically inject language content using 'i18n.global.setLocaleMessage'\n- Save the user's selected language in 'localStorage' to avoid losing it after refreshing\n\nKey code (from 'src\u002Fi18n.ts'):\n```ts\nimport { createI18n } from 'vue-i18n'\nconst DEFAULT_LANG = localStorage.getItem('lang') || 'zh'\nconst i18n = createI18n({\n  legacy: false,\n  globalInjection: true,\n  locale: DEFAULT_LANG,\n  fallbackLocale: 'en',\n  messages: {}\n})\n\nexport async function loadLocale(lang: string) {\n  if (i18n.global.availableLocales.includes(lang)) {\n    i18n.global.locale.value = lang\n    localStorage.setItem('lang', lang)\n    return\n  }\n  const msgs = await import(\u002F* @vite-ignore *\u002F `.\u002Flocales\u002F${lang}.ts`)\n  i18n.global.setLocaleMessage(lang, msgs.default || msgs)\n  i18n.global.locale.value = lang\n  localStorage.setItem('lang', lang)\n}\nexport default i18n\n```\n\n## 3. Register at the entrance and preload the language\nRegister the i18n in 'src\u002Fmain.ts' and load the default language before mounting to avoid flickering above the fold:\n```ts\nimport i18n, { loadLocale } from '.\u002Fi18n'\napp.use(i18n)\nconst lang = localStorage.getItem('lang') || 'zh'\nloadLocale(lang).finally(() => app.mount('#app'))\n```\n\n## 4. Component layer switching (UI example)\nExample component in the project: 'src\u002Fcomponents\u002Fcommon\u002Fub_language_switcher.vue', use the Arco icon (or button) to click to switch language, call 'loadLocale' and write 'localStorage'. Example highlights:\n- Call 'loadLocale(lang)' on switchover\n- Optional synchronization Pinia: 'useStore().setLanguage(lang)' (if implemented)\n- 'useI18n()' can be used to listen for 'locale'\n\nExample snippet (excerpt):\n```vue\n\u003Ctemplate>\n  \u003Cdiv class=\"ub-language-switcher\" @click=\"toggleLanguage\">\n    \u003CIconChineseFill v-if=\"current === 'zh'\"\u002F>\n    \u003CIconEnglishFill v-else \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\nimport { useI18n } from 'vue-i18n'\nconst { locale } = useI18n()\nCall loadLocale(...) on switchover\n\u003C\u002Fscript>\n```\n\n## 5. Lazy loading vs. build impact\n- Lazy loading language packs will chunk each language as a separate chunk (Vite's dynamic import behavior).\n- Advantages: small size of the first pack; Disadvantage: Build more output language packages.\n- Verification: You can see the language chunk request by switching the language in the Network panel of the browser's DevTools.\n\n## 6. Pinia Sync (optional)\nAdd the 'language' state and the 'setLanguage' action to 'src\u002Fstores\u002Findex.ts' to make the language a global state:\n```ts\n\u002F\u002F state:\nlanguage: localStorage.getItem('lang') || 'zh',\n\n\u002F\u002F action:\nasync setLanguage(lang: string) {\n  this.language = lang\n  localStorage.setItem('lang', lang)\n  await import('@\u002Fi18n').then(m => m.loadLocale(lang))\n}\n```\n\n## 7. Route title internationalization\nChange the route 'meta.title' to 'meta.titleKey' and set 'document.title' after the switch:\n```ts\nimport { tGlobal } from '@\u002Fi18n'\nrouter.afterEach((to) => {\n  const key = (to.meta as any).titleKey\n  if (key) document.title = tGlobal(key as string) as string\n})\n```\n\n## 8. Date and number localization\n- 'dayjs' or Intl API is recommended.\n- If you are using dayjs, you can dynamically load dayjs locale and call dayjs.locale(lang)' after 'loadLocale' is successful.\n\n## 9. Verification steps\n```bash\nnpm install\nnpm run dev\n```\nVerification Points:\n- 'lang' in localStorage is correct ('zh' or 'en')\n- Page text changes after switching languages\n- Lazy loading language chunks appear in the Network panel\n- If enabled, the browser tab title changes with the route key\n\n## 10. FAQs and debugging\n- Not Effective: Confirm that 'i18n' is registered with 'main.ts' and load the language before mounting\n- Lazy loading failed: Check the language package path 'src\u002Flocales\u002F{lang}.ts'\n- Asynchronous request requires locale header: read 'i18n.global.locale.value' on request\n\n## 11. SEO, accessibility, and publishing recommendations\n- Populate the title\u002Fdescription with 'meta.titleKey' and the corresponding translation (if you need SSR, you need to handle it on the server side)\n- Add 'aria-label' or 'a-tooltip' to toggle controls to improve accessibility\n- Published with 2~3 screenshots: language switching, network lazy loading, route title change\n\n## 12. Conclusion and expansion\nBy lazily loading language packs and synchronizing language states with Pinia, internationalization can be both lightweight and maintainable. The next step is to consider machine translation first draft + manual verification, or connect to a translation management platform (such as Crowdin, POEditor).\n\n---\n","前端",0,"https:\u002F\u002Fblog4-1316398321.cos.ap-nanjing.myqcloud.com\u002Fblog5\u002F20250823013114__【哲风壁纸】清晨-雪山.png",[14],"Vue",false,{"id":17,"title":18,"title_en":19},20,"Redis实战指南：从基础概念到Go语言应用","Redis Practical Guide: From Basic Concepts to Go Language Applications",{"id":21,"title":22,"title_en":23},22,"Go 交叉编译缓存导致的诡异 Bug","Weird Bug caused by Go cross-compiled caching","2025-08-18T00:50:11+08:00"]