feat: 国际化

This commit is contained in:
万纯 2020-12-31 12:59:11 +08:00
parent 297b48503d
commit 60cdf2fbec
19 changed files with 540 additions and 23 deletions

View File

@ -13,7 +13,8 @@ const headPkgs = [
"fes-plugin-access",
"fes-plugin-model",
"fes-plugin-layout",
"fes-plugin-icon"
"fes-plugin-icon",
"fes-plugin-locale"
];
const tailPkgs = [];
// const otherPkgs = readdirSync(join(__dirname, 'packages')).filter(

View File

@ -0,0 +1,3 @@
export default {
disableTypeCheck: false,
};

View File

@ -1,17 +0,0 @@
import { createI18n, useI18n } from 'vue-i18n';
// 注入 i18n 上下文
// 动态变更 local
// 其他组件能拿到 t 函数
// local 变更后,能通知到其他函数
// locales目录下以语言简称为子文件下存放配置信息
// 其他插件可以运行时修改配置
// 所有插件使用一个语言和配置
export default {
install(app, options, ctx) {
const i18n = createI18n(options);
ctx.useI18n = useI18n;
app.use(i18n);
}
};

View File

@ -2,7 +2,11 @@
"name": "@webank/fes-plugin-locale",
"version": "1.0.0",
"description": "",
"main": "index.js",
"main": "lib/index.js",
"files": [
"lib"
],
"module": "dist/index.esm.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
@ -10,6 +14,10 @@
"author": "",
"license": "MIT",
"dependencies": {
"@umijs/utils": "3.3.3",
"vue-i18n": "^9.0.0-beta.15"
},
"peerDependencies": {
"@webank/fes": "^2.0.0"
}
}

View File

@ -0,0 +1,71 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { getLocalesJSON } from './utils';
const namespace = 'plugin-locale';
export default (api) => {
const {
utils: { Mustache }
} = api;
api.describe({
config: {
schema(joi) {
return joi.object();
},
default: {}
}
});
const absoluteFilePath = join(namespace, 'core.js');
const absRuntimeFilePath = join(namespace, 'runtime.js');
function getLocaleFileBasePath() {
return join(api.paths.absSrcPath, api.config.singular ? 'locale' : 'locales');
}
// 监听 locale 文件改变,重新生成文件
api.addTmpGenerateWatcherPaths(getLocaleFileBasePath);
api.onGenerateFiles(() => {
const loacleConfigFileBasePath = getLocaleFileBasePath();
// 文件写出
const defaultOptions = api.config.locale || {};
const locales = getLocalesJSON(loacleConfigFileBasePath);
api.writeTmpFile({
path: absoluteFilePath,
content: Mustache.render(
readFileSync(join(__dirname, 'template/core.tpl'), 'utf-8'),
{
REPLACE_LOCALES: locales,
REPLACE_DEFAULT_OPTIONS: JSON.stringify(defaultOptions)
}
)
});
api.writeTmpFile({
path: absRuntimeFilePath,
content: readFileSync(
join(__dirname, 'template/runtime.tpl'),
'utf-8'
)
});
});
api.addPluginExports(() => [
{
specifiers: ['useI18n', 'setLocale'],
source: absoluteFilePath
}
]);
// api.addRuntimePluginKey(() => 'access');
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
};

View File

@ -0,0 +1,351 @@
// 注入 i18n 上下文
// 动态变更 local
// 其他组件能拿到 t 函数
// local 变更后,能通知到其他函数
// locales目录下以语言简称为子文件下存放配置信息
// 其他插件可以运行时修改配置
// 所有插件使用一个语言和配置
import { createI18n, useI18n } from 'vue-i18n/dist/vue-i18n.esm-bundler.js';
const defaultLangUConfigMap = {
'ar-EG': {
lang: 'ar-EG',
label: 'العربية',
icon: '🇪🇬',
title: 'لغة'
},
'az-AZ': {
lang: 'az-AZ',
label: 'Azərbaycan dili',
icon: '🇦🇿',
title: 'Dil'
},
'bg-BG': {
lang: 'bg-BG',
label: 'Български език',
icon: '🇧🇬',
title: 'език'
},
'ca-ES': {
lang: 'ca-ES',
label: 'Catalá',
icon: '🇨🇦',
title: 'llengua'
},
'cs-CZ': {
lang: 'cs-CZ',
label: 'Čeština',
icon: '🇨🇿',
title: 'Jazyk'
},
'da-DK': {
lang: 'da-DK',
label: 'Dansk',
icon: '🇩🇰',
title: 'Sprog'
},
'de-DE': {
lang: 'de-DE',
label: 'Deutsch',
icon: '🇩🇪',
title: 'Sprache'
},
'el-GR': {
lang: 'el-GR',
label: 'Ελληνικά',
icon: '🇬🇷',
title: 'Γλώσσα'
},
'en-GB': {
lang: 'en-GB',
label: 'English',
icon: '🇬🇧',
title: 'Language'
},
'en-US': {
lang: 'en-US',
label: 'English',
icon: '🇺🇸',
title: 'Language'
},
'es-ES': {
lang: 'es-ES',
label: 'Español',
icon: '🇪🇸',
title: 'Idioma'
},
'et-EE': {
lang: 'et-EE',
label: 'Eesti',
icon: '🇪🇪',
title: 'Keel'
},
'fa-IR': {
lang: 'fa-IR',
label: 'فارسی',
icon: '🇮🇷',
title: 'زبان'
},
'fi-FI': {
lang: 'fi-FI',
label: 'Suomi',
icon: '🇫🇮',
title: 'Kieli'
},
'fr-BE': {
lang: 'fr-BE',
label: 'Français',
icon: '🇧🇪',
title: 'Langue'
},
'fr-FR': {
lang: 'fr-FR',
label: 'Français',
icon: '🇫🇷',
title: 'Langue'
},
'ga-IE': {
lang: 'ga-IE',
label: 'Gaeilge',
icon: '🇮🇪',
title: 'Teanga'
},
'he-IL': {
lang: 'he-IL',
label: 'עברית',
icon: '🇮🇱',
title: 'שפה'
},
'hi-IN': {
lang: 'hi-IN',
label: 'हिन्दी, हिंदी',
icon: '🇮🇳',
title: 'भाषा: हिन्दी'
},
'hr-HR': {
lang: 'hr-HR',
label: 'Hrvatski jezik',
icon: '🇭🇷',
title: 'Jezik'
},
'hu-HU': {
lang: 'hu-HU',
label: 'Magyar',
icon: '🇭🇺',
title: 'Nyelv'
},
'hy-AM': {
lang: 'hu-HU',
label: 'Հայերեն',
icon: '🇦🇲',
title: 'Լեզու'
},
'id-ID': {
lang: 'id-ID',
label: 'Bahasa Indonesia',
icon: '🇮🇩',
title: 'Bahasa'
},
'it-IT': {
lang: 'it-IT',
label: 'Italiano',
icon: '🇮🇹',
title: 'Linguaggio'
},
'is-IS': {
lang: 'is-IS',
label: 'Íslenska',
icon: '🇮🇸',
title: 'Tungumál'
},
'ja-JP': {
lang: 'ja-JP',
label: '日本語',
icon: '🇯🇵',
title: '言語'
},
'ku-IQ': {
lang: 'ku-IQ',
label: 'کوردی',
icon: '🇮🇶',
title: 'Ziman'
},
'kn-IN': {
lang: 'zh-TW',
label: 'ಕನ್ನಡ',
icon: '🇮🇳',
title: 'ಭಾಷೆ'
},
'ko-KR': {
lang: 'ko-KR',
label: '한국어',
icon: '🇰🇷',
title: '언어'
},
'lv-LV': {
lang: 'lv-LV',
label: 'Latviešu valoda',
icon: '🇱🇮',
title: 'Kalba'
},
'mk-MK': {
lang: 'mk-MK',
label: 'македонски јазик',
icon: '🇲🇰',
title: 'Јазик'
},
'mn-MN': {
lang: 'mn-MN',
label: 'Монгол хэл',
icon: '🇲🇳',
title: 'Хэл'
},
'ms-MY': {
lang: 'ms-MY',
label: 'بهاس ملايو‎',
icon: '🇲🇾',
title: 'Bahasa'
},
'nb-NO': {
lang: 'nb-NO',
label: 'Norsk',
icon: '🇳🇴',
title: 'Språk'
},
'ne-NP': {
lang: 'ne-NP',
label: 'नेपाली',
icon: '🇳🇵',
title: 'भाषा'
},
'nl-BE': {
lang: 'nl-BE',
label: 'Vlaams',
icon: '🇧🇪',
title: 'Taal'
},
'nl-NL': {
lang: 'nl-NL',
label: 'Vlaams',
icon: '🇳🇱',
title: 'Taal'
},
'pt-BR': {
lang: 'pt-BR',
label: 'Português',
icon: '🇧🇷',
title: 'Idiomas'
},
'pt-PT': {
lang: 'pt-PT',
label: 'Português',
icon: '🇵🇹',
title: 'Idiomas'
},
'ro-RO': {
lang: 'ro-RO',
label: 'Română',
icon: '🇷🇴',
title: 'Limba'
},
'ru-RU': {
lang: 'ru-RU',
label: 'русский',
icon: '🇷🇺',
title: 'язык'
},
'sk-SK': {
lang: 'sk-SK',
label: 'Slovenčina',
icon: '🇸🇰',
title: 'Jazyk'
},
'sr-RS': {
lang: 'sr-RS',
label: 'српски језик',
icon: '🇸🇷',
title: 'Језик'
},
'sl-SI': {
lang: 'sl-SI',
label: 'Slovenščina',
icon: '🇸🇱',
title: 'Jezik'
},
'sv-SE': {
lang: 'sv-SE',
label: 'Svenska',
icon: '🇸🇪',
title: 'Språk'
},
'ta-IN': {
lang: 'ta-IN',
label: 'தமிழ்',
icon: '🇮🇳',
title: 'மொழி'
},
'th-TH': {
lang: 'th-TH',
label: 'ไทย',
icon: '🇹🇭',
title: 'ภาษา'
},
'tr-TR': {
lang: 'tr-TR',
label: 'Türkçe',
icon: '🇹🇷',
title: 'Dil'
},
'uk-UA': {
lang: 'uk-UA',
label: 'Українська',
icon: '🇺🇰',
title: 'Мова'
},
'vi-VN': {
lang: 'vi-VN',
label: 'Tiếng Việt',
icon: '🇻🇳',
title: 'Ngôn ngữ'
},
'zh-CN': {
lang: 'zh-CN',
label: '简体中文',
icon: '🇨🇳',
title: '语言'
},
'zh-TW': {
lang: 'zh-TW',
label: '繁体中文',
icon: '🇭🇰',
title: '語言'
}
};
const locales = {{{REPLACE_LOCALES}}};
const defaultOptions = {{{REPLACE_DEFAULT_OPTIONS}}}
const messages = {};
if (Array.isArray(locales)) {
locales.forEach((item) => {
messages[item.locale] = item.message;
});
}
const i18n = createI18n({ ...defaultOptions, messages });
const setLocale = (locale)=>{
i18n.global.locale = locale
};
const addLocale = (locale, messages)=>{};
const getAllLocales = ()=>{};
const install = (app)=>{
app.use(i18n);
}
export { useI18n, setLocale, addLocale, getAllLocales, install }

View File

@ -0,0 +1,5 @@
import { install } from "./core";
export function onAppCreated({ app }) {
install(app)
}

View File

@ -0,0 +1,33 @@
import { glob } from '@umijs/utils';
import { join, basename } from 'path';
export function getLocales(cwd) {
const files = glob
.sync('*.js', {
cwd
})
.filter(
file => !file.endsWith('.d.ts')
&& !file.endsWith('.test.js')
&& !file.endsWith('.test.jsx')
).map((fileName) => {
const locale = basename(fileName, '.js');
return {
locale,
message: `require('${join(cwd, fileName)}').default`
};
});
return files;
}
export function getLocalesJSON(cwd) {
const locales = getLocales(cwd);
return JSON.stringify(locales)
.replace(
/"message":("(.+?)")/g,
(global, m1, m2) => `"message": ${m2.replace(/\^/g, '"')}`
)
.replace(/\\r\\n/g, '\r\n')
.replace(/\\n/g, '\r\n');
}

View File

@ -27,7 +27,7 @@ export default (api) => {
...getModels(srcModelsPath),
...getModels(
paths.absPagesPath,
`**/${getModelDir()}/**/*.{js,jsx}`,
`**/${getModelDir()}/**/*.{js,jsx}`
),
...getModels(paths.absPagesPath, '**/*.model.{js,jsx}')
]);

View File

@ -2,7 +2,7 @@ export default (api) => {
api.describe({
key: 'singular',
config: {
default: {},
default: true,
schema(joi) {
return joi
.boolean();

View File

@ -21,6 +21,10 @@ export default {
name: 'onepiece'
}]
},
locale: {
locale: 'zh-CN',
fallbackLocale: 'zh-CN'
},
devServer: {
port: 8080
}

View File

@ -39,6 +39,7 @@
"@webank/fes-plugin-access": "^1.0.0",
"@webank/fes-plugin-model": "^1.0.0",
"@webank/fes-plugin-layout": "^1.0.0",
"@webank/fes-plugin-locale": "^1.0.0",
"ant-design-vue": "2.0.0-rc.3"
}
}

View File

@ -0,0 +1,11 @@
export default {
test: 'test',
'navBar.lang': 'Languages',
'layout.user.link.help': 'Help',
'layout.user.link.privacy': 'Privacy',
'layout.user.link.terms': 'Terms',
'app.preview.down.block': 'Download this page to your local project',
'app.welcome.link.fetch-blocks': 'Get all block',
'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development'
};

View File

@ -0,0 +1,11 @@
export default {
'navbar.lang': 'Bahasa',
'layout.user.link.help': 'Bantuan',
'layout.user.link.privacy': 'Privasi',
'layout.user.link.terms': 'Ketentuan',
'app.preview.down.block': 'Unduh halaman ini dalam projek lokal anda',
'app.welcome.link.fetch-blocks': 'Dapatkan semua blok',
'app.welcome.link.block-list':
'Buat standar dengan cepat, halaman-halaman berdasarkan pengembangan `block`'
};

View File

@ -0,0 +1,8 @@
export default {
'navBar.lang': 'Idiomas',
'layout.user.link.help': 'ajuda',
'layout.user.link.privacy': 'política de privacidade',
'layout.user.link.terms': 'termos de serviços',
'app.preview.down.block': 'Download this page to your local project'
};

View File

@ -0,0 +1,11 @@
export default {
test: '测试',
'navBar.lang': '语言',
'layout.user.link.help': '帮助',
'layout.user.link.privacy': '隐私',
'layout.user.link.terms': '条款',
'app.preview.down.block': '下载此页面到本地项目',
'app.welcome.link.fetch-blocks': '获取全部区块',
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面'
};

View File

@ -0,0 +1,8 @@
export default {
'navBar.lang': '語言',
'layout.user.link.help': '幫助',
'layout.user.link.privacy': '隱私',
'layout.user.link.terms': '條款',
'app.preview.down.block': '下載此頁面到本地項目'
};

View File

@ -1,5 +1,6 @@
<template>
<div class="haizekuo">
<div>国际化 {{t("test")}}</div>
fes & 拉夫德鲁 <br />
accessOnepicess: {{accessOnepicess}}
</div>
@ -12,18 +13,24 @@
</config>
<script>
import { ref, onMounted } from 'vue';
import { useAccess, useModel, useRouter } from '@webank/fes';
import {
useAccess, useModel, useRouter, useI18n, setLocale
} from '@webank/fes';
export default {
setup() {
const fes = ref('fes upgrade to vue3');
const accessOnepicess = useAccess('/onepiece');
const { initialState } = useModel('@@initialState');
const { t } = useI18n();
const router = useRouter();
onMounted(() => {
console.log(router);
console.log(initialState);
console.log('mounted1!!');
setTimeout(() => {
setLocale('en-US');
}, 2000);
// router.push('/onepiece');
});
onMounted(() => {
@ -31,7 +38,8 @@ export default {
});
return {
fes,
accessOnepicess
accessOnepicess,
t
};
}
};