提交 56c79d67 编写于 作者: aaronchen2k2k's avatar aaronchen2k2k

i18n mechanism and language switch

上级 11a96df7
......@@ -14,9 +14,12 @@
"less": "^2.x",
"less-loader": "^3.x",
"lodash.debounce": "^4.0.8",
"moment": "^2.29.1",
"store": "^2.0.12",
"vue": "^2.6.11",
"vue-i18n": "^8.22.1",
"vue-router": "^3.4.8"
"vue-router": "^3.4.8",
"vuex": "^3.6.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
......
......@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<title></title>
</head>
<body>
<noscript>
......
......@@ -8,11 +8,9 @@
<script>
import zh_CN from 'ant-design-vue/lib/locale-provider/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';
import { domTitle, setDocumentTitle } from './utils/dom'
import { i18nRender } from '@/locales'
moment.locale('zh-cn');
export default {
name: 'App',
components: {
......@@ -20,9 +18,16 @@ export default {
data() {
return {
locale: zh_CN,
};
},
computed: {
locale () {
// 切换语言时,更新标题
const { title } = this.$route.meta
title && (setDocumentTitle(`${i18nRender(title)} - ${domTitle}`))
return this.$i18n.getLocaleMessage(this.$store.getters.lang).antLocale
}
}
}
</script>
......
import { i18nRender } from '@/locales'
import i18nMixin from '../../store/i18nMixin'
import { Dropdown, Icon, Menu } from 'ant-design-vue'
import './index.less'
const locales = ['zh-CN', 'en-US']
const languageLabels = {
'zh-CN': '简体中文',
'en-US': 'English'
}
const languageIcons = {
'zh-CN': '🇨🇳',
'en-US': '🇺🇸'
}
const SelectLang = {
props: {
prefixCls: {
type: String,
}
},
name: 'SelectLang',
mixins: [i18nMixin],
render () {
const { prefixCls } = this
const changeLang = ({ key }) => {
this.setLang(key)
}
const langMenu = (
<Menu class={['menu', 'ant-pro-header-menu']} selectedKeys={[this.currentLang]} onClick={changeLang}>
{locales.map(locale => (
<Menu.Item key={locale}>
<span role="img" aria-label={languageLabels[locale]}>
{languageIcons[locale]}
</span>{' '}
{languageLabels[locale]}
</Menu.Item>
))}
</Menu>
)
return (
<Dropdown overlay={langMenu} placement="bottomRight">
<span class={prefixCls}>
<Icon type="global" title={i18nRender('navBar.lang')} />
</span>
</Dropdown>
)
}
}
export default SelectLang
@import "~ant-design-vue/es/style/themes/default";
@header-menu-prefix-cls: ~'@{ant-prefix}-pro-header-menu';
@header-drop-down-prefix-cls: ~'@{ant-prefix}-pro-drop-down';
.@{header-menu-prefix-cls} {
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 160px;
}
}
.@{header-drop-down-prefix-cls} {
line-height: @layout-header-height;
vertical-align: top;
cursor: pointer;
> i {
font-size: 16px !important;
transform: none !important;
svg {
position: relative;
top: -1px;
}
}
}
export default {
title: 'ZenData',
}
......@@ -6,6 +6,9 @@
<div class="center"></div>
<div class="right">
<span class="dir">工作目录:{{workDir}}</span>
<select-lang :prefixCls="'select-lang'" />
<a href="https://www.zendata.cn/book/zendata/" target="_blank">帮助</a>
</div>
</div>
......@@ -15,10 +18,12 @@
import {getWorkDir} from "../api/manage";
import {config} from "../utils/vari";
import SelectLang from '../components/SelectLang'
export default {
name: 'Header',
components: {
SelectLang
},
data () {
return {
......@@ -71,5 +76,10 @@ export default {
padding-right: 20px;
}
}
.select-lang {
padding-right: 10px;
cursor: pointer;
}
}
</style>
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import storage from 'store'
import moment from 'moment'
// default lang
import enUS from './lang/en-US'
Vue.use(VueI18n)
export const defaultLang = 'en-US'
const messages = {
'en-US': {
...enUS
}
}
const i18n = new VueI18n({
silentTranslationWarn: true,
locale: defaultLang,
fallbackLocale: defaultLang,
messages
})
const loadedLanguages = [defaultLang]
function setI18nLanguage (lang) {
i18n.locale = lang
// request.headers['Accept-Language'] = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguageAsync (lang = defaultLang) {
return new Promise(resolve => {
// 缓存语言设置
storage.set('lang', lang)
if (i18n.locale !== lang) {
if (!loadedLanguages.includes(lang)) {
return import(/* webpackChunkName: "lang-[request]" */ `./lang/${lang}`).then(msg => {
const locale = msg.default
i18n.setLocaleMessage(lang, locale)
loadedLanguages.push(lang)
moment.updateLocale(locale.momentName, locale.momentLocale)
return setI18nLanguage(lang)
})
}
return resolve(setI18nLanguage(lang))
}
return resolve(lang)
})
}
export function i18nRender (key) {
return i18n.t(`${key}`)
}
export default i18n
import antdEnUS from 'ant-design-vue/es/locale-provider/en_US'
import momentEU from 'moment/locale/eu'
const components = {
antLocale: antdEnUS,
momentName: 'eu',
momentLocale: momentEU
}
const locale = {
'site.title': 'ZenData',
'menu.data.list': 'Data List',
'menu.data.edit': 'Data Edit',
'menu.config.list': 'Config List',
'menu.config.edit': 'Config Edit',
'menu.ranges.list': 'Ranges List',
'menu.ranges.edit': 'Ranges Edit',
'menu.instances.list': 'Instances List',
'menu.instances.edit': 'Instances Edit',
'menu.excel.list': 'Excel List',
'menu.excel.edit': 'Excel Edit',
'menu.text.list': 'Text List',
'menu.text.edit': 'Text Edit',
}
export default {
...components,
...locale
}
import antd from 'ant-design-vue/es/locale-provider/zh_CN'
import momentCN from 'moment/locale/zh-cn'
const components = {
antLocale: antd,
momentName: 'zh-cn',
momentLocale: momentCN
}
const locale = {
'site.title': 'ZenData数据生成工具',
'menu.data.list': '数据列表',
'menu.data.edit': '数据编辑',
'menu.config.list': '字段列表',
'menu.config.edit': '字段编辑',
'menu.ranges.list': '序列列表',
'menu.ranges.edit': '序列编辑',
'menu.instances.list': '实例列表',
'menu.instances.edit': '实例编辑',
'menu.excel.list': '表格列表',
'menu.excel.edit': '表格编辑',
'menu.text.list': '文本列表',
'menu.text.edit': '文本编辑',
}
export default {
...components,
...locale
}
import Vue from 'vue'
import App from './App.vue'
// import { Button, message } from 'ant-design-vue';
import store from './store/'
import VueI18n from 'vue-i18n'
import ConfigProvider from "ant-design-vue/lib/config-provider";
......@@ -61,9 +60,6 @@ import 'ant-design-vue/lib/popover/style';
import Tooltip from "ant-design-vue/lib/tooltip";
import 'ant-design-vue/lib/tooltip/style';
// import TreeSelect from "ant-design-vue/lib/tree-select";
// import 'ant-design-vue/lib/tree-select/style';
import Pagination from "ant-design-vue/lib/pagination";
import 'ant-design-vue/lib/pagination/style';
......@@ -104,7 +100,6 @@ Vue.use(Radio)
Vue.use(Spin)
Vue.use(Popover)
Vue.use(Tooltip)
// Vue.use(TreeSelect)
Vue.use(Pagination)
const i18n = new VueI18n({
......@@ -116,6 +111,7 @@ const i18n = new VueI18n({
new Vue({
router,
store,
i18n,
render: h => h(App),
}).$mount('#app')
......@@ -27,12 +27,14 @@ const routes = [
{
path: 'list',
name: 'mine-list',
component: () => import('../views/data/mine/List')
component: () => import('../views/data/mine/List'),
meta: { title: 'menu.data.list' }
},
{
path: 'edit/:id',
name: 'mine-edit',
component: () => import('../views/data/mine/Edit')
component: () => import('../views/data/mine/Edit'),
meta: { title: 'menu.data.edit' }
},
],
},
......@@ -49,12 +51,14 @@ const routes = [
{
path: 'list',
name: 'ranges-list',
component: () => import('../views/data/buildin/ranges/List')
component: () => import('../views/data/buildin/ranges/List'),
meta: { title: 'menu.ranges.list' }
},
{
path: 'edit/:id',
name: 'ranges-edit',
component: () => import('../views/data/buildin/ranges/Edit')
component: () => import('../views/data/buildin/ranges/Edit'),
meta: { title: 'menu.ranges.edit' }
},
],
},
......@@ -66,12 +70,14 @@ const routes = [
{
path: 'list',
name: 'instances-list',
component: () => import('../views/data/buildin/instances/List')
component: () => import('../views/data/buildin/instances/List'),
meta: { title: 'menu.instances.list' }
},
{
path: 'edit/:id',
name: 'instances-edit',
component: () => import('../views/data/buildin/instances/Edit')
component: () => import('../views/data/buildin/instances/Edit'),
meta: { title: 'menu.instances.edit' }
},
],
},
......@@ -83,12 +89,14 @@ const routes = [
{
path: 'list',
name: 'excel-list',
component: () => import('../views/data/buildin/excel/List')
component: () => import('../views/data/buildin/excel/List'),
meta: { title: 'menu.excel.list' }
},
{
path: 'edit/:id',
name: 'excel-edit',
component: () => import('../views/data/buildin/excel/Edit')
component: () => import('../views/data/buildin/excel/Edit'),
meta: { title: 'menu.excel.edit' }
},
],
},
......@@ -100,12 +108,14 @@ const routes = [
{
path: 'list',
name: 'config-list',
component: () => import('../views/data/buildin/config/List')
component: () => import('../views/data/buildin/config/List'),
meta: { title: 'menu.config.list' }
},
{
path: 'edit/:id',
name: 'config-edit',
component: () => import('../views/data/buildin/config/Edit')
component: () => import('../views/data/buildin/config/Edit'),
meta: { title: 'menu.config.edit' }
},
],
},
......@@ -117,12 +127,14 @@ const routes = [
{
path: 'list',
name: 'text-list',
component: () => import('../views/data/buildin/text/List')
component: () => import('../views/data/buildin/text/List'),
meta: { title: 'menu.text.list' }
},
{
path: 'edit/:id',
name: 'text-edit',
component: () => import('../views/data/buildin/text/Edit')
component: () => import('../views/data/buildin/text/Edit'),
meta: { title: 'menu.text.edit' }
},
],
},
......
const getters = {
lang: state => state.app.lang,
}
export default getters
import { mapState } from 'vuex'
const i18nMixin = {
computed: {
...mapState({
currentLang: state => state.app.lang
})
},
methods: {
setLang (lang) {
this.$store.dispatch('setLang', lang)
}
}
}
export default i18nMixin
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
app,
},
state: {
},
mutations: {
},
actions: {
},
getters
})
import { loadLanguageAsync } from '@/locales'
import storage from 'store'
import { APP_LANGUAGE } from '@/store/mutation-types'
const app = {
state: {
lang: 'zh-CN',
_antLocale: {}
},
mutations: {
[APP_LANGUAGE]: (state, lang, antd = {}) => {
state.lang = lang
state._antLocale = antd
storage.set(APP_LANGUAGE, lang)
},
},
actions: {
setLang ({ commit }, lang) {
return new Promise((resolve, reject) => {
commit(APP_LANGUAGE, lang)
loadLanguageAsync(lang)
.then(() => {
resolve()
})
.catch(e => {
reject(e)
})
})
}
}
}
export default app
export const APP_LANGUAGE = 'app_language'
import config from '../config/config'
export const setDocumentTitle = function (title) {
document.title = title
const ua = navigator.userAgent
// eslint-disable-next-line
const regex = /\bMicroMessenger\/([\d\.]+)/
if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) {
const i = document.createElement('iframe')
i.src = '/favicon.ico'
i.style.display = 'none'
i.onload = function () {
setTimeout(function () {
i.remove()
}, 9)
}
document.body.appendChild(i)
}
}
export const domTitle = config.title
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册