AIAI Smart Summary (TL;DR)
Collapse

Implementing i18n in Vue 3 + Vite Project

Summary

This 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/i18n.ts', 'src/components/common/ub_language_switcher.vue', etc.

Target Audience

Front-end engineers, Vue3 users, teams looking to take internationalization from PoC to production.

Project and premise

  • Tech stack: Vue 3 + Vite + TypeScript
  • Status Management: Pinia ('src/stores/index.ts')
  • UI: Arco Design (globally registered at 'src/main.ts')
  • Key Documents (In-Project):
    • 'src/i18n.ts' — i18n initialization and lazy loading implementation
    • 'src/locales/zh.ts' / 'src/locales/en.ts' — language pack
    • 'src/components/common/ub_language_switcher.vue' — Language switching component example
    • 'src/main.ts' — Loads the language before mounting
    • 'src/stores/index.ts' — Optional language state sync point
    • 'src/router/index.ts' — example of internationalization of route titles

1. Installation

Run at the project root:

bash 复制代码
npm install vue-i18n@9

2. Initialization (Core Idea)

Use the Composition API pattern to implement 'loadLocale(lang)' to lazily load language packages. Key takeaways:

  • The initial message is empty
  • Dynamically inject language content using 'i18n.global.setLocaleMessage'
  • Save the user's selected language in 'localStorage' to avoid losing it after refreshing

Key code (from 'src/i18n.ts'):

ts 复制代码
import { createI18n } from 'vue-i18n'
const DEFAULT_LANG = localStorage.getItem('lang') || 'zh'
const i18n = createI18n({
  legacy: false,
  globalInjection: true,
  locale: DEFAULT_LANG,
  fallbackLocale: 'en',
  messages: {}
})

export async function loadLocale(lang: string) {
  if (i18n.global.availableLocales.includes(lang)) {
    i18n.global.locale.value = lang
    localStorage.setItem('lang', lang)
    return
  }
  const msgs = await import(/* @vite-ignore */ `./locales/${lang}.ts`)
  i18n.global.setLocaleMessage(lang, msgs.default || msgs)
  i18n.global.locale.value = lang
  localStorage.setItem('lang', lang)
}
export default i18n

3. Register at the entrance and preload the language

Register the i18n in 'src/main.ts' and load the default language before mounting to avoid flickering above the fold:

ts 复制代码
import i18n, { loadLocale } from './i18n'
app.use(i18n)
const lang = localStorage.getItem('lang') || 'zh'
loadLocale(lang).finally(() => app.mount('#app'))

4. Component layer switching (UI example)

Example component in the project: 'src/components/common/ub_language_switcher.vue', use the Arco icon (or button) to click to switch language, call 'loadLocale' and write 'localStorage'. Example highlights:

  • Call 'loadLocale(lang)' on switchover
  • Optional synchronization Pinia: 'useStore().setLanguage(lang)' (if implemented)
  • 'useI18n()' can be used to listen for 'locale'

Example snippet (excerpt):

vue 复制代码
<template>
  <div class="ub-language-switcher" @click="toggleLanguage">
    <IconChineseFill v-if="current === 'zh'"/>
    <IconEnglishFill v-else />
  </div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
const { locale } = useI18n()
Call loadLocale(...) on switchover
</script>

5. Lazy loading vs. build impact

  • Lazy loading language packs will chunk each language as a separate chunk (Vite's dynamic import behavior).
  • Advantages: small size of the first pack; Disadvantage: Build more output language packages.
  • Verification: You can see the language chunk request by switching the language in the Network panel of the browser's DevTools.

6. Pinia Sync (optional)

Add the 'language' state and the 'setLanguage' action to 'src/stores/index.ts' to make the language a global state:

ts 复制代码
// state:
language: localStorage.getItem('lang') || 'zh',

// action:
async setLanguage(lang: string) {
  this.language = lang
  localStorage.setItem('lang', lang)
  await import('@/i18n').then(m => m.loadLocale(lang))
}

7. Route title internationalization

Change the route 'meta.title' to 'meta.titleKey' and set 'document.title' after the switch:

ts 复制代码
import { tGlobal } from '@/i18n'
router.afterEach((to) => {
  const key = (to.meta as any).titleKey
  if (key) document.title = tGlobal(key as string) as string
})

8. Date and number localization

  • 'dayjs' or Intl API is recommended.
  • If you are using dayjs, you can dynamically load dayjs locale and call dayjs.locale(lang)' after 'loadLocale' is successful.

9. Verification steps

bash 复制代码
npm install
npm run dev

Verification Points:

  • 'lang' in localStorage is correct ('zh' or 'en')
  • Page text changes after switching languages
  • Lazy loading language chunks appear in the Network panel
  • If enabled, the browser tab title changes with the route key

10. FAQs and debugging

  • Not Effective: Confirm that 'i18n' is registered with 'main.ts' and load the language before mounting
  • Lazy loading failed: Check the language package path 'src/locales/{lang}.ts'
  • Asynchronous request requires locale header: read 'i18n.global.locale.value' on request

11. SEO, accessibility, and publishing recommendations

  • Populate the title/description with 'meta.titleKey' and the corresponding translation (if you need SSR, you need to handle it on the server side)
  • Add 'aria-label' or 'a-tooltip' to toggle controls to improve accessibility
  • Published with 2~3 screenshots: language switching, network lazy loading, route title change

12. Conclusion and expansion

By 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).


Comments & Discussions

Write a comment...
0 / 500
Comments & Discussions (0)
暂无评论,快来抢占第一条沙发吧~
0
0
AIAI