Skip to main content

国际化

安装与配置

软件版本

项目版本
Vue> =3.0.0
Vue-I18n> =9.0.0
Ant-Design-vue> =2.0.0

安装 Vue-I18n

Vue 3.0 仅支持 Vue-I18n 9.0 以上版本

$ npm install vue-i18n@next

语言包文件示例

Common语言包 common.json

{
"actions": {
"new": "新建",
"add": "添加",
"delete": "删除",
"edit": "编辑",
"search": "查询",
"refresh": "刷新",
"detail": "详情",
"confirm": "确认",
"cancel": "取消",
"submit": "提交",
"export": "导出",
"import": "导入",
"next": "下一步",
"previous": "上一步"
},
"messages": {
"success": "操作成功",
"delete": "删除成功"
},
"others": {
"pagination": "共 { 0 } 条,每页显示 { 1 } 条"
}
}

模块语言包 demo.json

{
// 共通部分语言包
"common": {
"columns": {
"time": "操作时间",
"orderNo": "订单编号",
"...": "..."
},
"labels": {},
"status": {},
"extra": {},
"action": {},
"tabs": {}
},
// list 页面内语言包 共通部分使用引用方式
"list": {
"columns": {
"time": "@:modules.demo.common.columns.time",
"orderNo": "@:modules.demo.common.columns.orderNo",
"...": "..."
},
"status": {},
"placeholder": {
"user/action": "用户/操作"
}
},
"detail": {},
"add": {},
"edit": {}
}

语言包的组合

src/i18n/zh_CN/index.ts

import common from './common.json';     // common语言包
// import 模块名 from './modules/模块名.json'
import demo from './modules/demo.json'; // 模块语言包


const en = {
common, // 在根语言包中,引入共通语言文件
modules: {
demo // 在根语言包中,引入模块语言文件
}
}
export default en;

注册i8n实例

src/i18n/index.ts

//vue-i18n组件
import {createI18n} from 'vue-i18n'

// 引入本地语言包
import zh from "@/i18n/zh_CN";
import ja from "@/i18n/ja_JP";
import en from "@/i18n/en_GB";

// 引入AntV语言包
import ZH_CN from "ant-design-vue/lib/locale-provider/zh_CN";
import EN_GB from "ant-design-vue/lib/locale-provider/en_GB";
import JA_JP from "ant-design-vue/lib/locale-provider/ja_JP";

export const AntLanguageMap = {
'zh-cn': ZH_CN,
'en-gb': EN_GB,
'ja-jp': JA_JP,
};

export const LanguageNameMap = {
'zh-cn': '简体中文',
'en-gb': 'English',
'ja-jp': '日本語'
}

// 默认语言
export const DefaultLanguage = 'en-gb';

//注册i8n实例并引入语言文件
export const i18n = createI18n({
locale: localStorage.getItem('language') || DefaultLanguage, // 默认显示的语言
fallbackLocale: DefaultLanguage, // 缺省时默认语言
messages: {
'zh-cn': zh,
'ja-jp': ja,
'en-gb': en
},
});

货币国际化

// 使用方式: {{ $n(100, 'currency') }}
const numberFormats = {
'zh-cn': {
currency: {
style: 'currency',
currency: 'CNY',
minimumFractionDigits: 2
}
},
'ja-jp': {
currency: {
style: 'currency',
currency: 'JPY',
minimumFractionDigits: 2
}
},
'en-gb': {
currency: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2
}
}
}

export const i18n = createI18n({
numberFormats, // 导入货币格式化
});

相关字段参数,参照 https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js

时间国际化

// 使用方式 $d(new Date(),'long')
const datetimeFormats = {
'zh-cn': {
short: {
year: 'numeric',
month: 'numeric',
day: 'numeric'
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: false
}
},
'ja-jp': {
short: {
year: 'numeric',
month: 'numeric',
day: 'numeric'
},
long: {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: false
}
},
'en-gb': {
short: {
year: 'numeric',
month: 'numeric',
day: 'numeric'
},
long: {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: false
}
}
}

export const i18n = createI18n({
datetimeFormats, // 导入时间格式化
});

相关字段参数,参照 https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js

引入main.ts

import {i18n} from "@/i18n";

app.use(i18n).mount('#app');

配置Store

创建 i18n.actions.ts

export const I18nActions = {
Set: '[I18n] Set Language',
}

创建 i18n.ts

import {Module} from 'vuex';
import {I18nActions} from './i18n.actions';

export interface I18nState {
language: string;
}

export const I18nStore: Module<I18nState, unknown> = {
namespaced: true,
state: () => ({
language: ''
}),
mutations: {
/**
* 设置语言
* @param state
* @param { string } value
*/
[I18nActions.Set]: (state, value) => {
state.language = value;
},
},
}

配置App.vue

Vue-i18n 部分

// app.vue
import {I18nActions} from "@/store/modules/i18n/i18n.actions";
import {DefaultLanguage} from "@/i18n";
import {computed, getCurrentInstance, readonly, ref, watch} from "vue";
import {useStore} from "vuex";

export default {
name: 'App',
components: {},
setup() {
const store = useStore();

// 初始化 Store 中的 language 信息
store.commit('i18n/' + I18nActions.Set, localStorage.getItem('language') || DefaultLanguage);

// 取得 Store 中的 language 信息
const language = computed(() => store.state.i18n.language) || DefaultLanguage;

// 获取根组件实例
const {ctx} = getCurrentInstance();

// 监听 Store 中 language 的变化,实现热更新
watch(language, value => {

// 修改Vue-i18n的全局配置
ctx.$i18n.locale = value;

// 更新localStorage
localStorage.setItem('language', value);
});
}
};

AntV 及 Moment 部分

相关资料:AntV 官方文档Moment 官方文档

// app.vue
<template>
<a-config-provider :locale = "locale">
<router-view />
</a-config-provider>
</template>

<script>
import {ConfigProvider} from 'ant-design-vue';
import moment from 'moment';
import {I18nActions} from "@/store/modules/i18n/i18n.actions";
import {AntLanguageMap, DefaultLanguage} from "@/i18n";
import {computed, getCurrentInstance, readonly, ref, watch} from "vue";
import {useStore} from "vuex";
// moment 语言包
import 'moment/locale/zh-cn';
import 'moment/locale/en-gb';
import 'moment/locale/ja';

export default {
name: 'App',
components: {
[ConfigProvider.name]: ConfigProvider
},
setup() {
const store = useStore();

// 初始化 Store 中的 language 信息
store.commit('i18n/' + I18nActions.Set, localStorage.getItem('language') || DefaultLanguage);

// 取得 Store 中的 language 信息
const language = computed(() => store.state.i18n.language) || DefaultLanguage;

// 组件国际化
const locale = ref(readonly(AntLanguageMap[language.value]));

// 国际化 moment
moment.locale(language.value);

// 监听 Store 中 language 的变化,实现热更新
watch(language, value => {

// 修改AntV组件的全局配置
locale.value = readonly(AntLanguageMap[value]);

// 修改moment的全局配置
moment.locale(value);

// 更新localStorage
localStorage.setItem('language', value);
});

return {
locale
}
}
};
</script>

使用方法

在template中使用

<template>
<!-- 共通部分引用 -->
<button>{{$t('common.actions.submit')}}</button>
<!-- 组件部分引用 -->
<span>{{$t('modules.demo.detail.label.orderNo')}}</span>
<!-- 时间国际化 -->
<span>{{$d(new Date(), 'long')}}</span>
<!-- 货币国际化 -->
<span>{{$n(1000, 'currency')}}</span>
</template>

在script中使用

import {useI18n} from "vue-i18n";

// 模块名
const moduleName = 'demo.list';
// 获取国际化文言
const getI18nText = (path: string) => i18n.t(`modules.${moduleName}.${path}`);

导航菜单及面包屑

在routes.ts中增加i18nTitle,如下所示

export const MENU_ROUTES: Array<RouteRecordRaw> = [
{
path: '',
redirect: '/demo'
},
{
path: '/demo',
name: 'Demo',
component: NestedLayout,
meta: {
title: '示例', icon: 'ToolOutlined',
i18nTitle: {
'zh-cn': '示例',
'en-gb': 'Demo',
'ja-jp': '例'
}
},
children: DEMO_CHILDREN
},
];

在navigator.vue的script中增加:

const language = computed(() => store.state.i18n.language) || DefaultLanguage;

并替换template中的部分内容:

替换前替换后
menu.meta.titlemenu.meta.i18nTitle[language]
child.meta.titlechild.meta.i18nTitle[language]
item.titleitem.i18nTitle[language]