i18n支持传入变量标识可以做变更文字显示,但如果要让这个变量显示组件的话需要用到插槽
在vue3中需要用i18n-t标签,keypath为i18n值的路径,<template #name>里面就可以放我们想要的组件
代码如下
i18n已全局引入,无需再引入i18n-t。
// routes.operConfig.json
{"low": "换电前电量低于{number1}%,换电后电量高于{number2}%,为有效换电"}
<i18n-t keypath="routes.operConfig.low">
<template #number1>
<InputNumber :max="100" v-model:value="formData.sysOperPowerReplace.beforeRate" />
</template>
<template #number2>
<InputNumber :max="100" v-model:value="formData.sysOperPowerReplace.afterRate" />
</template>
</i18n-t>
如何配置全局i18n
// main.js
import { setupI18n } from '@/locales/setupI18n';
// 异步案例:语言文件可能从服务器端获取
await setupI18n(app);
@/locales/setupI18n
import type { App } from 'vue';
import type { I18nOptions } from 'vue-i18n';
import { createI18n } from 'vue-i18n';
import { setHtmlPageLang, setLoadLocalePool } from './helper';
import { localeSetting } from '@/settings/localeSetting';
import { useLocaleStoreWithOut } from '@/store/modules/locale';
const { fallback, availableLocales } = localeSetting;
export let i18n: ReturnType<typeof createI18n>;
async function createI18nOptions(): Promise<I18nOptions> {
const localeStore = useLocaleStoreWithOut();
const locale = localeStore.getLocale;
const defaultLocal = await import(`./lang/${locale}.ts`);
const message = defaultLocal.default?.message ?? {};
setHtmlPageLang(locale);
setLoadLocalePool((loadLocalePool) => {
loadLocalePool.push(locale);
});
return {
legacy: false,
locale,
fallbackLocale: fallback,
messages: {
[locale]: message,
},
availableLocales: availableLocales,
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
silentTranslationWarn: true, // true - warning off
missingWarn: false,
silentFallbackWarn: true,
};
}
// setup i18n instance with glob
export async function setupI18n(app: App) {
const options = await createI18nOptions();
i18n = createI18n(options);
app.use(i18n);
}
./helper.ts
import type { LocaleType } from '#/config';
export const loadLocalePool: LocaleType[] = [];
export function setHtmlPageLang(locale: LocaleType) {
document.querySelector('html')?.setAttribute('lang', locale);
}
export function setLoadLocalePool(cb: (loadLocalePool: LocaleType[]) => void) {
cb(loadLocalePool);
}
@/settings/localeSetting
import type { LocaleSetting, LocaleType } from '#/config';
export const LOCALE: { [key: string]: LocaleType } = {
ZH_CN: 'zhCn',
EN_US: 'enUs',
KM_KH: 'kmKh',
TH_TH: 'thTh',
};
export const localeSetting: LocaleSetting = {
showPicker: true,
// Locale
locale: LOCALE.ZH_CN,
// Default locale
fallback: LOCALE.ZH_CN,
// available Locales
availableLocales: [LOCALE.ZH_CN, LOCALE.EN_US],
};
@/store/modules/locale
import type { LocaleSetting, LocaleType } from '#/config';
import { defineStore } from 'pinia';
import { store } from '@/store';
import { LOCALE_KEY } from '@/enums/cacheEnum';
import { createLocalStorage } from '@/utils/cache';
import { localeSetting, LOCALE } from '@/settings/localeSetting';
const ls = createLocalStorage();
const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
interface LocaleState {
localInfo: LocaleSetting;
}
export const useLocaleStore = defineStore({
id: 'app-locale',
state: (): LocaleState => ({
localInfo: lsLocaleSetting,
}),
getters: {
getShowPicker(state): boolean {
return !!state.localInfo?.showPicker;
},
getLocale(state): LocaleType {
return state.localInfo?.locale ?? 'zhCn';
},
},
actions: {
/**
* getValue 函数用于从给定对象中获取值。
* 它首先尝试获取 this.getLocale 的值,如果不存在,它将按照 LOCALE 对象的顺序获取值。
* 如果所有尝试都失败,它将返回空字符串。
*
* @param {Object} obj - 需要获取值的对象,对象的键是字符串,值也是字符串
* @returns {string} 返回获取到的值,如果没有找到则返回空字符串
*/
getValue(obj: { [key: string]: string }) {
if (obj) {
if (Object.prototype.hasOwnProperty.call(obj, this.getLocale)) {
return obj[this.getLocale];
}
for (const key in LOCALE) {
if (Object.prototype.hasOwnProperty.call(obj, LOCALE[key])) {
return obj[LOCALE[key]];
}
}
}
return '';
},
/**
* Set up multilingual information and cache
* @param info multilingual info
*/
setLocaleInfo(info: Partial<LocaleSetting>) {
this.localInfo = { ...this.localInfo, ...info };
ls.set(LOCALE_KEY, this.localInfo);
},
/**
* Initialize multilingual information and load the existing configuration from the local cache
*/
initLocale() {
this.setLocaleInfo({
...localeSetting,
...this.localInfo,
});
},
},
});
// Need to be used outside the setup
export function useLocaleStoreWithOut() {
return useLocaleStore(store);
}
@/locales/useLocale
import type { LocaleType } from '#/config';
import { i18n } from './setupI18n';
import { useLocaleStoreWithOut } from '@/store/modules/locale';
import { unref, computed } from 'vue';
import { loadLocalePool, setHtmlPageLang } from './helper';
import { Locale } from 'ant-design-vue/es/locale';
interface LangModule {
message: Recordable;
dateLocale: Recordable;
dateLocaleName: string;
}
function setI18nLanguage(locale: LocaleType) {
const localeStore = useLocaleStoreWithOut();
if (i18n.mode === 'legacy') {
i18n.global.locale = locale;
} else {
(i18n.global.locale as any).value = locale;
}
localeStore.setLocaleInfo({ locale });
setHtmlPageLang(locale);
}
export function useLocale() {
const localeStore = useLocaleStoreWithOut();
const getLocale = computed(() => localeStore.getLocale);
const getShowLocalePicker = computed(() => localeStore.getShowPicker);
const getAntdLocale = computed((): any => {
const localeMessage = i18n.global.getLocaleMessage<{ antdLocale: Locale }>(unref(getLocale));
return localeMessage?.antdLocale ?? {};
});
// Switching the language will change the locale of useI18n
// And submit to configuration modification
async function changeLocale(locale: LocaleType) {
const globalI18n = i18n.global;
const currentLocale = unref(globalI18n.locale);
if (currentLocale === locale) {
return locale;
}
if (loadLocalePool.includes(locale)) {
setI18nLanguage(locale);
return locale;
}
const langModule = ((await import(`./lang/${locale}.ts`)) as any).default as LangModule;
if (!langModule) return;
const { message } = langModule;
globalI18n.setLocaleMessage(locale, message);
loadLocalePool.push(locale);
setI18nLanguage(locale);
return locale;
}
return {
getLocale,
getShowLocalePicker,
changeLocale,
getAntdLocale,
};
}
App.vue
<template>
<ConfigProvider :locale="getAntdLocale" :theme="themeConfig">
<AppProvider>
<RouterView />
</AppProvider>
</ConfigProvider>
</template>
<script lang="ts" setup>
import { AppProvider } from '@/components/Application';
import { useTitle } from '@/hooks/web/useTitle';
import { useLocale } from '@/locales/useLocale';
import { ConfigProvider } from 'ant-design-vue';
import { useDarkModeTheme } from '@/hooks/setting/useDarkModeTheme';
import 'dayjs/locale/zh-cn';
import { computed } from 'vue';
// support Multi-language
const { getAntdLocale } = useLocale();
const { isDark, darkTheme } = useDarkModeTheme();
const themeConfig = computed(() =>
Object.assign(
{
token: {
colorPrimary: '#0062F7',
colorSuccess: '#00B042',
colorWarning: '#FF9200',
colorError: '#FF5219',
colorInfo: '#0062F7',
},
},
isDark.value ? darkTheme : {},
),
);
// Listening to page changes and dynamically changing site titles
useTitle();
</script>
语言包文件夹locales/lang/zhCn.ts
import { genMessage } from '../helper';
import antdLocale from 'ant-design-vue/es/locale/zh_CN';
import { deepMerge } from '@/utils';
const modules = import.meta.glob('./zh-CN/**/*.json', { eager: true });
export default {
message: {
...genMessage(modules as Recordable<Recordable>, 'zh-CN'),
antdLocale: {
...antdLocale,
DatePicker: deepMerge(
antdLocale.DatePicker,
genMessage(modules as Recordable<Recordable>, 'zh-CN').antdLocale.DatePicker,
),
},
},
};
// /locales/lang/zh-CN/common.json
{
"action": "操作",
"add": "新增"
}
递归合并两个对象
/**
* Recursively merge two objects.
* 递归合并两个对象。
*
* @param source The source object to merge from. 要合并的源对象。
* @param target The target object to merge into. 目标对象,合并后结果存放于此。
* @param mergeArrays How to merge arrays. Default is "replace".
* 如何合并数组。默认为replace。
* - "union": Union the arrays. 对数组执行并集操作。
* - "intersection": Intersect the arrays. 对数组执行交集操作。
* - "concat": Concatenate the arrays. 连接数组。
* - "replace": Replace the source array with the target array. 用目标数组替换源数组。
* @returns The merged object. 合并后的对象。
*/
export function deepMerge<T extends object | null | undefined, U extends object | null | undefined>(
source: T,
target: U,
mergeArrays: 'union' | 'intersection' | 'concat' | 'replace' = 'replace',
): T & U {
if (!target) {
return source as T & U;
}
if (!source) {
return target as T & U;
}
return mergeWith({}, source, target, (sourceValue, targetValue) => {
if (isArray(targetValue) && isArray(sourceValue)) {
switch (mergeArrays) {
case 'union':
return unionWith(sourceValue, targetValue, isEqual);
case 'intersection':
return intersectionWith(sourceValue, targetValue, isEqual);
case 'concat':
return sourceValue.concat(targetValue);
case 'replace':
return targetValue;
default:
throw new Error(`Unknown merge array strategy: ${mergeArrays as string}`);
}
}
if (isObject(targetValue) && isObject(sourceValue)) {
return deepMerge(sourceValue, targetValue, mergeArrays);
}
return undefined;
});
}
@/hooks/web/useI18n
import { i18n } from '@/locales/setupI18n';
type I18nGlobalTranslation = {
(key: string): string;
(key: string, locale: string): string;
(key: string, locale: string, list: unknown[]): string;
(key: string, locale: string, named: Record<string, unknown>): string;
(key: string, list: unknown[]): string;
(key: string, named: Record<string, unknown>): string;
};
type I18nTranslationRestParameters = [string, any];
function getKey(namespace: string | undefined, key: string) {
if (!namespace) {
return key;
}
if (key.startsWith(namespace)) {
return key;
}
return `${namespace}.${key}`;
}
export function useI18n(namespace?: string): {
t: I18nGlobalTranslation;
} {
const normalFn = {
t: (key: string) => {
return getKey(namespace, key);
},
};
if (!i18n) {
return normalFn;
}
const { t, ...methods } = i18n.global;
const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => {
if (!key) return '';
if (!key.includes('.') && !namespace) return key;
return (t as (arg0: string, ...arg: I18nTranslationRestParameters) => string)(
getKey(namespace, key),
...(arg as I18nTranslationRestParameters),
);
};
return {
...methods,
t: tFn,
};
}
// 为什么要编写此函数?
// 主要用于配合vscode i18nn ally插件。此功能仅用于路由和菜单。请在其他地方使用useI18n
export const t = (key: string) => key;
页面引用i18n
<template>
{{t('common.is')}}
</template>
<script setup lang="ts">
import { useI18n } from '@/hooks/web/useI18n';
const { t } = useI18n();
</script>