找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1538

积分

0

好友

193

主题
发表于 3 天前 | 查看: 11| 回复: 0

由于项目后台系统主要服务于新加坡、马来西亚等海外自提站业务,国际化(i18n)配置成为了必不可少的基础设施。在基于 Vue3 技术栈的开发中,我们需要处理的语言切换内容主要包含两个维度:

  1. 业务文案:开发者自行定义的文本内容,通过 vue-i18n-next 进行管理。
  2. 组件库文案:Naive UI 组件内部的提示语(如分页、日期选择器等),通过 n-config-provider 进行全局配置。

本文将详细介绍如何在 Vite + Vue3 + TypeScript + Pinia + Naive UI 的架构下,优雅地实现国际化配置。

一、配置 vue-i18n-next

1. 安装依赖

在 Vue3 环境中,我们需要安装 vue-i18n 的 v9 版本:

npm install vue-i18n@9

2. 初始化 i18n 实例

创建 src\lang\index.ts 文件,使用 createI18n 初始化实例。这里我们需要特别注意 legacy 模式的配置以及语言包的按需加载。

// src\lang\index.ts
import { createI18n } from 'vue-i18n'
import { LANG_VALUE } from '@/common/enum'
import zhHans from './zh-Hans'
import en from './en'

const i18n = createI18n({
  legacy: false, // 必须设置为 false 以此支持 Composition API
  locale: getLanguage(), // 获取初始化语言
  messages: {
    [LANG_VALUE.Zh]: zhHans,
    [LANG_VALUE.En]: en
  }
})
export default i18n

配置项深度解析:

  • legacy:默认为 true。在使用 Composition API 时必须显式设置为 false,否则会抛出 API 调用错误。

    legacy error

  • locale:决定当前展示的语言。最佳实践是优先从本地缓存读取用户偏好,若无缓存则读取浏览器的 navigator.language

// src\lang\index.ts
import { localCache } from '@/utils'

export function getLanguage() {
  const chooseLanguage = localCache.getItem(LANGUAGE)
  if (chooseLanguage) return chooseLanguage

  // 如果没有缓存记录,则根据浏览器语言自动匹配
  const language = navigator.language.toLowerCase()
  const locales = [LANG_VALUE.En, LANG_VALUE.Zh]
  for (const locale of locales) {
    if (language.indexOf(locale) > -1) {
      return locale
    }
  }
  return LANG_VALUE.Zh
}

为了保持类型安全和代码维护性,建议使用 TypeScript 的枚举(enum)来管理语言常量:

// src\common\enum.ts
export enum LANG_VALUE {
  En = 'en',
  Zh = 'zh-Hans'
}
  • messages:挂载语言包文件。

中文语言包示例:

// src\lang\zh-Hans.ts
export default {
  baoguochuku: '包裹出库',
  sousuo: '搜索'
}

英文语言包示例:

// src\lang\en.ts
export default {
  baoguochuku: 'Outbound',
  sousuo: 'Search'
}

3. 全局注册

main.ts 中引入并挂载 i18n 实例:

// src\main.ts
import i18n from './lang/index'
app.use(i18n)

二、在业务代码中使用

1. 在 Vue 组件中使用

Script Setup 环境

script setup 中,我们需要从 vue-i18n 导入 useI18n 钩子。

注意:在定义响应式数据(如路由菜单配置)时,如果直接赋值 t('key'),切换语言时内容不会更新。必须使用 Getter 函数形式 () => t('key') 来保持响应性。

<!-- src\components\Navigation\Navigation.vue -->
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'

const { t } = useI18n()
// 错误写法:label: t(route.meta.title)
// 正确写法:label: () => t(route.meta.title)
const menuOptions: MenuOption[] = modulesRoutes.map(route => ({
  label: () => t(route.meta.title),
  // ...
}))
</script>

Template 模板环境

在模板中可以直接使用 $t,这得益于 globalInjection: true 的默认配置。

<template>
  <n-button>
    <slot name="submitBtnText">{{ $t('sousuo') }}</slot>
  </n-button>
</template>

如果将 globalInjection 设为 false,模板中的 $t 调用将会报错。
globalInjection error

2. 在非 Vue 文件中使用(如 TS 文件)

在纯 TypeScript 文件(如 API 拦截器、工具函数)中,无法使用 hooks,需要直接引入 i18n 实例,通过 i18n.global.t() 调用。

// src\service\request\index.ts
import i18n from '@/lang'

if (!axios.isCancel(error)) {
  dialog.error({
    title: i18n.global.t('tishi'),
    // ...
  })
}

三、配置 Naive UI 组件国际化

Naive UI 组件默认语言为英语。要切换为中文,需在根组件 App.vue 中使用 n-config-provider 组件,并动态绑定 locale(语言)和 date-locale(日期语言)。

1. Pinia 状态管理

我们将语言状态托管在 Pinia 中,以便全局管理和联动。

// src\stores\app.ts
import { defineStore } from 'pinia'
import { zhCN, dateZhCN } from 'naive-ui'
import { LANG_VALUE } from '@/common/enum'
import { getLanguage } from '@/lang'

interface IAppState {
  language: string
  // ...
}

const useAppStore = defineStore('app', {
  state: (): IAppState => ({
    language: getLanguage(),
    // ...
  }),
  getters: {
    // 根据当前语言状态返回对应的 Naive UI 语言包
    locale(state) {
      switch (state.language) {
        case LANG_VALUE.En:
          return null // Naive UI 默认是英文,传 null 即可
        case LANG_VALUE.Zh:
          return zhCN
        default:
          break
      }
    },
    dateLocale(state) {
      switch (state.language) {
        case LANG_VALUE.En:
          return null
        case LANG_VALUE.Zh:
          return dateZhCN
        default:
          break
      }
    }
  },
  // ...
})

export default useAppStore

2. 全局注入配置

App.vue 中,通过 storeToRefs 保持响应性,将 Pinia 计算出的 locale 对象传递给配置组件。

<!-- src\App.vue -->
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import useAppStore from '@/stores/app'

const appStore = useAppStore()
const { locale, dateLocale } = storeToRefs(appStore)
</script>

<template>
  <n-config-provider
    :locale="locale"
    :date-locale="dateLocale"
  >
    <RouterView />
  </n-config-provider>
</template>

四、开发语言切换控件

为了让用户方便地切换语言,我们结合 Naive UI 的 Popselect(弹出选择)和 Icon 组件封装一个切换控件。

1. 控件 UI 实现

<!-- src\components\LangSelect\LangSelect.vue -->
<template>
  <div class="lang-select">
    <n-popselect
      v-model:value="value"
      :options="options"
      trigger="click"
      @update:value="handleUpdateValue"
      >
      <n-icon>
        <Language />
      </n-icon>
    </n-popselect>
  </div>
</template>

<script lang="ts" setup>
import { Language } from '@vicons/ionicons5' // 引入图标
// ... 逻辑代码见下文
</script>

2. 逻辑处理

在逻辑层,我们需要处理以下几件事:

  1. 初始化时从 Pinia 获取当前语言回显。
  2. 切换时同步更新 Pinia、i18n 实例以及本地缓存。
<script lang="ts" setup>
import { ref } from 'vue'
import { storeToRefs } from 'pinia'
import useAppStore from '@/stores/app'
import { LANG_VALUE } from '@/common/enum'
import { setLocale } from '@/lang'
import type { SelectOption, SelectGroupOption } from 'naive-ui'

const ENGLISH = 'English'
const ZHONG_WEN = '中文'

const appStore = useAppStore()
const { language } = storeToRefs(appStore)

// 计算回显值
function getValue() {
  switch (language.value) {
    case LANG_VALUE.En: return ENGLISH
    case LANG_VALUE.Zh: return ZHONG_WEN
    default: return ZHONG_WEN
  }
}

const value = ref<string | null>(getValue())

const options: Array<SelectOption | SelectGroupOption> = [
  { label: ENGLISH, value: ENGLISH },
  { label: ZHONG_WEN, value: ZHONG_WEN }
]

// 处理切换事件
function handleUpdateValue(val: string) {
  let lang = LANG_VALUE.Zh
  if (val === ENGLISH) {
    lang = LANG_VALUE.En
  }
  // 调用统一的设置方法
  setLocale(lang)
}
</script>

3. 统一设置方法

src\lang\index.ts 中封装 setLocale 方法,确保状态变更的原子性:

// src\lang\index.ts
export function setLocale(lang: langType) {
  const appStore = useAppStore()
  const { language } = storeToRefs(appStore)

  i18n.global.locale.value = lang // 1. 修改 i18n 实例语言
  language.value = lang           // 2. 修改 Pinia 状态
  localCache.setItem(LANGUAGE, lang) // 3. 持久化到缓存
}

五、消除控制台警告

在配置完成后,浏览器控制台可能会出现关于 vue-i18n 的 Feature Flags 警告。

console warning

这是因为我们使用了 ESM 构建版本,需要显式定义特性标志。在 vite.config.ts 中添加 define 配置即可解决:

export default defineConfig({
  // ...
  define: {
    __VUE_I18N_FULL_INSTALL__: true,
    __VUE_I18N_LEGACY_API__: false, // 关闭 Legacy API 支持,减少打包体积
    __INTLIFY_PROD_DEVTOOLS__: false
  }
})

六、最终效果

经过上述配置,我们不仅实现了业务文案的动态切换,也完美兼容了 UI 组件库的国际化。

result gif




上一篇:java学习
下一篇:MT6735双安卓系统平板评测:物理隔离双路CPU,8英寸摸鱼神器
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-10 08:51 , Processed in 0.252203 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表