Vue国际化实现多语言方案

一、目录结构设计

建议将多语言资源集中管理,典型目录结构:

plaintext 复制代码
src/
├── lang/                # 多语言核心目录
│   ├── index.js         # i18n 配置入口
│   ├── zh.js            # 中文
│   ├── en.js            # 英文
│   └── ar.js            # 阿拉伯
├── main.js              # Vue 入口
├── components/          # 业务组件
└── views/               # 页面组件

二、编写语言包

每个语言包导出键值对格式的文本资源,键名保持统一,值为对应语言的文本。

  • src/lang/zh.js(中文):
js 复制代码
export default {
  common: {
    title: 'Vue2 项目',
    welcome: '欢迎 {name} 登录,今日剩余次数:{count}',
    cancel: '取消',
    confirm: '确认',
    placeholder: '请输入内容',
    item: '项目', // 单数 | 复数
    count: '您有 {count} 个项目 | 您有 {count} 个项目',
  },
  login: {
    title: '用户登录',
    user: {
      name: '用户名',
    },
  },
}
  • src/lang/en.js(英文):
js 复制代码
export default {
  common: {
    title: 'Vue2 Lighthouse Demo',
    welcome: 'welcome {name} login, today remaining times: {count}',
    cancel: 'Cancel',
    confirm: 'Confirm',
    placeholder: 'Please enter content',
    item: 'item | items', // 单数 | 复数
    count: 'You have {count} item | You have {count} items',
  },
  login: {
    title: 'User Login',
    user: {
      name: 'Username',
    },
  }
}
  • src/lang/ar.js(阿拉伯):
js 复制代码
// 阿拉伯语语言包 - 注意:阿拉伯语是RTL(从右到左)语言

export default {
  common: {
    title: 'عرض Vue2 لـ Lighthouse',
    welcome: 'مرحبًا بك {name} تسجيل الدخول، المرات المتبقية اليوم: {count}',
    cancel: 'إلغاء',
    confirm: 'تأكيد',
    submit: 'تأكيدتأ تأكيدتأ تأكيدتأ كيدتأكيد',
    placeholder: 'الرجاء إدخال المحتوى',
    item: 'عنصر | عناصر', // مفرد | جمع
    count: 'لديك {count} عنصر | لديك {count} عناصر',
    price: 'السعر: {amount}', // 带变量的文本(变量位置不影响RTL)
    mixedText: "الرقم الطلبي: <span dir='ltr'>#12345</span>", // 混合文本(数字强制LTR)
  },
  login: {
    title: 'تسجيل دخول المستخدم',
    user: {
      name: 'اسم المستخدم',
    },
  },
}

三、配置 vue-i18n

src/lang/index.js 中初始化 i18n 实例:

javascript 复制代码
import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

// 按需引入配置
// const messages = {
//   en: () => import('./en'),
//   zh: () => import('./zh'),
// }

import en from './en'
import zh from './zh'
import ar from './ar' // 导入阿拉伯语
const messages = {
  en,
  zh,
  ar, // 注册阿拉伯语
}
// 获取默认语言(优先级:本地存储 > 浏览器语言 > 简体中文)
const getDefaultLang = () => {
  const lang =
    localStorage.getItem('lang') || navigator.language || navigator.userLanguage
  console.log('lang', lang)

  if (lang && messages[lang]) {
    // 设置html根节点的dir和lang属性(核心!)
    document.documentElement.dir = lang === 'ar' ? 'rtl' : 'ltr'
    document.documentElement.lang = lang
    return lang
  } else {
    // 兜底:如果浏览器语言不支持,默认使用简体中文
    return 'zh'
  }
}
const i18n = new VueI18n({
  locale: getDefaultLang(), // 当前语言标识
  fallbackLocale: 'zh', // 语言不存在时的兜底语言
  // 按需加载
  // messages: {},
  // 同步加载
  messages,
  // 防止页面闪烁
  silentTranslationWarn: true,
  dateTimeFormats: {
    zh: {
      short: {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
      },
    },
    en: {
      short: {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
      },
    },
    ar: {
      short: {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
      },
    },
  },
  numberFormats: {
    zh: {
      currency: {
        style: 'currency',
        currency: 'CNY',
      },
    },
    en: {
      currency: {
        style: 'currency',
        currency: 'USD',
      },
    },
    ar: {
      currency: {
        style: 'currency',
        currency: 'EGP', // 埃及镑,阿拉伯语国家常用货币
      },
    },
  },
})

// 动态加载默认语言包
// const loadLang = async (lang) => {
//   const langModule = await messages[lang]()
//   console.log('langModule', langModule)
//   i18n.setLocaleMessage(lang, langModule.default)
//   i18n.locale = lang
// }
// loadLang(getDefaultLang())

export default i18n

在 Vue 入口注册 i18n

修改 src/main.js

js 复制代码
import Vue from 'vue'
import App from './App.vue'
import i18n from './lang' // 导入 i18n 实例

Vue.config.productionTip = false

new Vue({
  i18n, // 注入到 Vue 实例
  render: h => h(App)
}).$mount('#app')

四、在项目中使用多语言

1. 模板中使用(核心用法)

  • 基础用法:$t('键名')
  • 嵌套键名:$t('common.submit')
html 复制代码
<template>
  <div>
    <el-select v-model="value" placeholder="请选择语言" @change="handleChange">
      <el-option label="中文" value="zh"> </el-option>
      <el-option label="英文" value="en"> </el-option>
      <el-option label="阿拉伯" value="ar"> </el-option>
    </el-select>
    <div class="content">
      <p>传参:{{ $t('common.welcome', { name: '生华', count: '10' }) }}</p>
      <p>{{ $t('common.cancel') }}</p>
      <p>{{ $t('common.confirm') }}</p>
      <p>{{ $t('login.title') }}</p>
      <p>三层翻译:{{ $t('login.user.name') }}</p>
      <!-- 基本用法 -->
      <p>单数:{{ $tc('common.item', 1) }}</p>
      <!-- 输出: item -->
      <p>复数:{{ $tc('common.item', 3) }}</p>
      <!-- 输出: items -->

      <!-- 带变量的用法 -->
      <p>{{ $tc('common.count', 1, { count: 1 }) }}</p>
      <!-- 输出: You have 1 item -->
      <p>{{ $tc('common.count', 5, { count: 5 }) }}</p>
      <!-- 输出: You have 5 items -->
      <div>
        水印<el-input
          style="width: 300px"
          v-model="lang"
          :placeholder="$t('common.placeholder')"
        />
      </div>
      <p>
        <span>日期:</span>
        <b>{{ $d(new Date()) }}</b>
      </p>
      <p>
        数字:
        {{ $n(123.22, 'currency') }}
      </p>
      <p>
        箭头图标:
        <i class="arrow-icon el-icon-right"></i>
      </p>
      <!-- 带变量的文本(数字格式化) -->
      <p>{{ $t('common.price', { amount: formatArabicNumber(99.99) }) }}</p>

      <!-- 混合文本(阿拉伯语+数字,需保留 HTML) -->
      <p v-html="$t('common.mixedText')"></p>
    </div>
    <div>组件内自定义文案:<language-private /></div>
    <!-- 适配多语言文本长度的按钮 -->
    <button class="flex-btn">{{ $t('common.submit') }}</button>
  </div>
</template>

2. 脚本中使用

  • 实例内:this.$t('键名')
  • 全局 / 非组件环境:导入 i18n 实例,使用 i18n.t('键名')
javascript 复制代码
// 组件内
export default {
  methods: {
    submit() {
      if (!this.username) {
        alert(this.$t('error.empty'))
      }
    }
  }
}

// 非组件环境(如 router.js)
  // 异步加载语言包路由也要使用异步
  // setTimeout(() => {
  document.title = i18n.t(String(to.meta.title))
  // }, 500)

3. 动态切换语言

实现语言切换按钮,修改 i18n.locale 即可:

js 复制代码
<template>
  <div>
    <el-select v-model="value" placeholder="请选择语言" @change="handleChange">
      <el-option label="中文" value="zh"> </el-option>
      <el-option label="英文" value="en"> </el-option>
      <el-option label="阿拉伯" value="ar"> </el-option>
    </el-select>
  <div>    
<template>
<script>
  methods: {
    handleChange (val) {
      this.$i18n.locale = val
      localStorage.setItem('lang', val)
      // 可选:刷新页面(部分场景需要,如路由、组件内硬编码文本)
      window.location.reload()
    },
    // 格式化数字为阿拉伯-Indic数字(٩٩.٩٩ 而非 99.99)
    formatArabicNumber (num) {
      if (this.$i18n.locale !== 'ar') return num;
      // 阿拉伯数字映射表
      const digitMap = {
        '0': '٠', '1': '١', '2': '٢', '3': '٣', '4': '٤',
        '5': '٥', '6': '٦', '7': '٧', '8': '٨', '9': '٩', '.': '.'
      };
      return num.toString().split('').map(d => digitMap[d] || d).join('');
    }
  },
}
</script>

五、高级用法

1. 带参数的翻译(插值)

如果文本需要动态替换内容,语言包中使用 {变量名},调用时传入参数:

  • 语言包(zh-CN.js):
js 复制代码
export default {
  common: {
    welcome: '欢迎 {name} 登录,今日剩余次数:{count}'
  }
}
  • 使用:
js 复制代码
<!-- 模板中 -->
<div>{{ $t('common.welcome', { name: '张三', count: 5 }) }}</div>

<!-- 脚本中 -->
this.$t('common.welcome', { name: '张三', count: 5 })

2. 复数处理

不同语言的复数规则不同(如英文有单复数,中文无),vue-i18n 支持复数格式化:

  • 语言包(en-US.js):
js 复制代码
export default {
  common: {
    item: 'item | items', // 单数 | 复数
    count: 'You have {count} {count, plural, one {item} other {items}}'
  }
}
  • 使用:
js 复制代码
<div>{{ $t('common.count', { count: 1 }) }}</div> <!-- You have 1 item -->
<div>{{ $t('common.count', { count: 5 }) }}</div> <!-- You have 5 items -->

3. 日期 / 数字格式化

vue-i18n 内置日期、数字格式化能力,需结合 $d(日期)、$n(数字)使用:

  • 模板中:
html 复制代码
<!-- 日期格式化 -->
<div>{{ $d(new Date(), 'short') }}</div> <!-- 2025/12/17(中文)| 12/17/2025(英文) -->

<!-- 数字格式化 -->
<div>{{ $n(12345.67, 'currency') }}</div> <!-- ¥12,345.67(中文)| $12,345.67(英文) -->
  • 配置格式化规则(在 lang/index.js 中):
js 复制代码
const i18n = new VueI18n({
  // ... 其他配置
  dateTimeFormats: {
    'zh-CN': {
      short: {
        year: 'numeric', month: '2-digit', day: '2-digit'
      }
    },
    'en-US': {
      short: {
        year: 'numeric', month: '2-digit', day: '2-digit'
      }
    }
  },
  numberFormats: {
    'zh-CN': {
      currency: {
        style: 'currency', currency: 'CNY'
      }
    },
    'en-US': {
      currency: {
        style: 'currency', currency: 'USD'
      }
    }
  }
})

4. 路由国际化

如果某个组件的文本仅在内部使用,可在组件内定义局部语言包:

js 复制代码
  // 异步加载语言包路由也要使用异步
  // setTimeout(() => {
  document.title = i18n.t(String(to.meta.title))
  // }, 500)

5. 动态加载

如果某个组件的文本仅在内部使用,可在组件内定义局部语言包:

js 复制代码
// lang/index.js

// 按需引入配置
const messages = {
  en: () => import('./en'),
  zh: () => import('./zh'),
}

// 动态加载默认语言包  
const loadLang = async (lang) => {
  const langModule = await messages[lang]()
  console.log('langModule', langModule)
  i18n.setLocaleMessage(lang, langModule.default)
  i18n.locale = lang
}
loadLang(getDefaultLang())

5. 阿拉伯语言特殊处理

  1. 切换阿拉伯语后,页面文本从右向左排列,字符无乱码;
  2. 混合文本(如 "السعر: ٩٩.٩٩")中数字保持 LTR 方向,整体排版正常;
  3. 图标、表单、按钮等组件方向适配,交互逻辑自洽。
js 复制代码
// index.html

      /* 全局样式:阿拉伯语字体 + 行高适配 */
      html[lang='ar'] {
        font-family: 'Noto Naskh Arabic', serif;
        line-height: 2.4; /* 阿拉伯字符高度高,避免文字重叠 */
      }
      html[lang='ar'] .arrow-icon {
        transform: scaleX(-1); /* 水平翻转 */
      }
      /* 全局 RTL 布局基础 */
      html[dir='rtl'] {
        direction: rtl;
        unicode-bidi: embed; /* 处理混合文本(阿拉伯+数字/英文) */
        text-align: right; /* 文本默认右对齐 */
      }
      
      <p>
        箭头图标:
        <i class="arrow-icon el-icon-right"></i>
      </p>
      <!-- 带变量的文本(数字格式化) -->
      <p>{{ $t('common.price', { amount: formatArabicNumber(99.99) }) }}</p>  
      
    formatArabicNumber (num) {
      if (this.$i18n.locale !== 'ar') return num;
      // 阿拉伯数字映射表
      const digitMap = {
        '0': '٠', '1': '١', '2': '٢', '3': '٣', '4': '٤',
        '5': '٥', '6': '٦', '7': '٧', '8': '٨', '9': '٩', '.': '.'
      };
      return num.toString().split('').map(d => digitMap[d] || d).join('');
    }      

6. 长度适配

  1. 所有容器「弹性宽度 + 最小 / 最大宽度」替代固定尺寸;
  2. 文本「单行省略 / 多行截断」作为兜底;
  3. 针对不同语言动态调整字体大小 / 内边距;
  4. 关键组件通过 JS 计算文本宽度,精准适配;
  5. 测试覆盖最长 / 最短语言 + 小屏场景。

五、注意事项

  1. 语言标识规范 :建议使用 zh-CNen-US 等 BCP 47 规范标识,避免自定义(如 zhen)导致浏览器语言匹配问题。

  2. 避免硬编码 :所有展示文本必须抽离到语言包,禁止在模板 / 脚本中直接写死(如 alert('提交成功') 需改为 alert(this.$t('common.success')))。

  3. 第三方组件适配 :如果使用 Element UI 等组件库,需单独配置其多语言(Element UI 有内置语言包,可结合 vue-i18n 整合):

js 复制代码
// main.js
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import enLocale from 'element-ui/lib/locale/lang/en'
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
import i18n from './lang'

// 整合 Element UI 语言包
ElementUI.i18n((key, value) => i18n.t(key, value))
Vue.use(ElementUI, {
  locale: i18n.locale === 'en-US' ? enLocale : zhLocale
})
相关推荐
一 乐6 小时前
婚纱摄影网站|基于ssm + vue婚纱摄影网站系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot·后端
C_心欲无痕6 小时前
ts - tsconfig.json配置讲解
linux·前端·ubuntu·typescript·json
清沫6 小时前
Claude Skills:Agent 能力扩展的新范式
前端·ai编程
yinuo7 小时前
前端跨页面通信终极指南:方案拆解、对比分析
前端
北辰alk7 小时前
Vue 模板引擎深度解析:基于 HTML 的声明式渲染
vue.js
北辰alk7 小时前
Vue 自定义指令完全指南:定义与应用场景详解
vue.js
yinuo7 小时前
前端跨页面通讯终极指南⑨:IndexedDB 用法全解析
前端
北辰alk8 小时前
Vue 动态路由完全指南:定义与参数获取详解
vue.js
北辰alk8 小时前
Vue Router 完全指南:作用与组件详解
vue.js
北辰alk8 小时前
Vue 中使用 this 的完整指南与注意事项
vue.js