国际化
安装与配置
软件版本
项目 | 版本 |
---|---|
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.title | menu.meta.i18nTitle[language] |
child.meta.title | child.meta.i18nTitle[language] |
item.title | item.i18nTitle[language] |