From e05da2650bc14b4b1c6379f977175d7cce22c46b Mon Sep 17 00:00:00 2001 From: Pan Date: Fri, 3 Nov 2017 18:37:49 +0800 Subject: [PATCH] feature:add i18n --- package.json | 1 + src/components/Screenfull/index.vue | 2 +- src/components/SvgIcon/index.vue | 12 ++- src/icons/svg/international.svg | 1 + src/icons/svg/language.svg | 1 + src/lang/en.js | 49 +++++++++ src/lang/index.js | 27 +++++ src/lang/zh.js | 49 +++++++++ src/main.js | 6 +- src/router/index.js | 90 ++++++++-------- src/store/getters.js | 1 + src/store/modules/app.js | 8 ++ src/views/i18n/index.vue | 107 ++++++++++++++++++++ src/views/i18n/local.js | 36 +++++++ src/views/layout/components/Navbar.vue | 35 +++++-- src/views/layout/components/SidebarItem.vue | 11 +- 16 files changed, 381 insertions(+), 55 deletions(-) create mode 100644 src/icons/svg/international.svg create mode 100644 src/icons/svg/language.svg create mode 100644 src/lang/en.js create mode 100644 src/lang/index.js create mode 100644 src/lang/zh.js create mode 100644 src/views/i18n/index.vue create mode 100644 src/views/i18n/local.js diff --git a/package.json b/package.json index 8008adde..7a10633c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "sortablejs": "1.6.1", "vue": "2.5.2", "vue-count-to": "1.0.5", + "vue-i18n": "7.3.2", "vue-multiselect": "2.0.6", "vue-router": "3.0.1", "vue-splitpane": "1.0.0", diff --git a/src/components/Screenfull/index.vue b/src/components/Screenfull/index.vue index c872ce10..89d25817 100644 --- a/src/components/Screenfull/index.vue +++ b/src/components/Screenfull/index.vue @@ -47,7 +47,7 @@ export default { .screenfull-svg { display: inline-block; cursor: pointer; - fill: #48576a; + fill: #5a5e66;; width: 20px; height: 20px; vertical-align: 10px; diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue index b8f3219d..e331a27e 100644 --- a/src/components/SvgIcon/index.vue +++ b/src/components/SvgIcon/index.vue @@ -1,5 +1,5 @@ @@ -11,11 +11,21 @@ export default { iconClass: { type: String, required: true + }, + className: { + type: String } }, computed: { iconName() { return `#icon-${this.iconClass}` + }, + svgClass() { + if (this.className) { + return 'svg-icon ' + this.className + } else { + return 'svg-icon' + } } } } diff --git a/src/icons/svg/international.svg b/src/icons/svg/international.svg new file mode 100644 index 00000000..6912767d --- /dev/null +++ b/src/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/language.svg b/src/icons/svg/language.svg new file mode 100644 index 00000000..2baf7431 --- /dev/null +++ b/src/icons/svg/language.svg @@ -0,0 +1 @@ + diff --git a/src/lang/en.js b/src/lang/en.js new file mode 100644 index 00000000..e6e5a9e9 --- /dev/null +++ b/src/lang/en.js @@ -0,0 +1,49 @@ +export default { + route: { + dashboard: 'dashboard', + introduction: 'introduction', + permission: 'permission', + icons: 'icons', + components: 'components:', + componentIndex: 'introduction', + tinymce: 'tinymce', + markdown: 'Markdown', + jsonEditor: 'JSON Editor', + dndList: 'dnd list', + splitPane: 'Splitpane', + avatarUpload: 'avatar upload', + dropzone: 'Dropzone', + sticky: 'Sticky', + countTo: 'CountTo', + componentMixin: 'Mixin', + backToTop: 'backToTop', + charts: 'charts', + chartsIndex: 'introduction', + keyboardChart: 'keyboard chart', + keyboardChart2: 'keyboard chart2', + lineChart: 'line chart', + mixChart: 'mix chart', + example: 'example', + Table: 'Table', + dynamicTable: '动态table', + dragTable: '拖拽table', + inlineEditTable: 'table内编辑', + complexTabl: '综合table', + tab: 'Tab', + form: 'Form', + createForm: 'create form', + editForm: 'edit form', + errorPages: 'error pages', + page401: '401', + page404: '404', + errorLog: 'error log', + excel: 'excel', + exportExcel: 'export excel', + selectExcel: 'export selected', + uploadExcel: 'upload excel', + exportZip: 'zip', + theme: 'theme', + clipboardDemo: 'clipboard', + i18n: 'i18n' + } +} diff --git a/src/lang/index.js b/src/lang/index.js new file mode 100644 index 00000000..2c55ddc3 --- /dev/null +++ b/src/lang/index.js @@ -0,0 +1,27 @@ +import Vue from 'vue' +import elementEnLocale from 'element-ui/lib/locale/lang/en' +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN' +import enLocale from './en' +import zhLocale from './zh' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' + +Vue.use(VueI18n) + +const messages = { + en: { + ...enLocale, + ...elementEnLocale + }, + zh: { + ...zhLocale, + ...elementZhLocale + } +} + +const i18n = new VueI18n({ + locale: Cookies.get('language') || 'zh', // set locale + messages // set locale messages +}) + +export default i18n diff --git a/src/lang/zh.js b/src/lang/zh.js new file mode 100644 index 00000000..ce6edbb1 --- /dev/null +++ b/src/lang/zh.js @@ -0,0 +1,49 @@ +export default { + route: { + dashboard: '首页', + introduction: '简述', + permission: '权限测试页', + icons: '图标', + components: '组件', + componentIndex: '介绍', + tinymce: '富文本编辑器', + markdown: 'Markdown', + jsonEditor: 'JSON编辑器', + dndList: '列表拖拽', + splitPane: 'Splitpane', + avatarUpload: '头像上传', + dropzone: 'Dropzone', + sticky: 'Sticky', + countTo: 'CountTo', + componentMixin: '小组件', + backToTop: '返回顶部', + charts: '图表', + chartsIndex: '介绍', + keyboardChart: '键盘图表', + keyboardChart2: '键盘图表2', + lineChart: '折线图', + mixChart: '混合图表', + example: '综合实例', + Table: 'Table', + dynamicTable: '动态table', + dragTable: '拖拽table', + inlineEditTable: 'table内编辑', + complexTabl: '综合table', + tab: 'Tab', + form: '表单', + createForm: '创建表单', + editForm: '编辑表单', + errorPages: '错误页面', + page401: '401', + page404: '404', + errorLog: '错误日志', + excel: 'excel', + exportExcel: 'export excel', + selectExcel: 'export selected', + uploadExcel: 'upload excel', + exportZip: 'zip', + theme: '换肤', + clipboardDemo: 'clipboard', + i18n: '国际化' + } +} diff --git a/src/main.js b/src/main.js index 94a5a99b..1a63658e 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,7 @@ import Vue from 'vue' import Element from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' +import i18n from './lang' // 国际化 import App from './App' import router from './router' import store from './store' @@ -10,7 +11,9 @@ import './errorLog'// error log import './permission' // 权限 import './mock' // 该项目所有请求使用mockjs模拟 -Vue.use(Element) +Vue.use(Element, { + i18n: (key, value) => i18n.t(key, value) +}) // register global utility filters. Object.keys(filters).forEach(key => { @@ -23,6 +26,7 @@ new Vue({ el: '#app', router, store, + i18n, template: '', components: { App } }) diff --git a/src/router/index.js b/src/router/index.js index 992209d0..cd116f34 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -33,7 +33,7 @@ export const constantRouterMap = [ path: 'dashboard', component: _import('dashboard/index'), name: 'dashboard', - meta: { title: '首页', icon: 'dashboard' } + meta: { title: 'dashboard', icon: 'dashboard' } }] }, @@ -45,7 +45,7 @@ export const constantRouterMap = [ path: 'index', component: _import('introduction/index'), name: 'introduction', - meta: { title: '简述', icon: 'people' } + meta: { title: 'introduction', icon: 'people' } }] } ] @@ -67,7 +67,7 @@ export const asyncRouterMap = [ component: _import('permission/index'), name: 'permission', meta: { - title: '权限测试页', + title: 'permission', icon: 'lock', role: ['admin'] } @@ -81,7 +81,7 @@ export const asyncRouterMap = [ path: 'index', component: _import('svg-icons/index'), name: 'icons', - meta: { title: '图标', icon: 'icon' } + meta: { title: 'icons', icon: 'icon' } }] }, @@ -91,22 +91,22 @@ export const asyncRouterMap = [ redirect: '/components/index', name: 'components', meta: { - title: '组件', + title: 'components', icon: 'component' }, children: [ - { path: 'index', component: _import('components/index'), name: 'componentIndex', meta: { title: '介绍' }}, - { path: 'tinymce', component: _import('components/tinymce'), name: 'tinymce', meta: { title: '富文本编辑器' }}, - { path: 'markdown', component: _import('components/markdown'), name: 'markdown', meta: { title: 'Markdown' }}, - { path: 'json-editor', component: _import('components/jsonEditor'), name: 'jsonEditor', meta: { title: 'JSON编辑器' }}, - { path: 'dnd-list', component: _import('components/dndList'), name: 'dndList', meta: { title: '列表拖拽' }}, - { path: 'splitpane', component: _import('components/splitpane'), name: 'splitpane', meta: { title: 'SplitPane' }}, - { path: 'avatar-upload', component: _import('components/avatarUpload'), name: 'avatar-upload', meta: { title: '头像上传' }}, - { path: 'dropzone', component: _import('components/dropzone'), name: 'dropzone', meta: { title: 'Dropzone' }}, - { path: 'sticky', component: _import('components/sticky'), name: 'sticky', meta: { title: 'Sticky' }}, - { path: 'count-to', component: _import('components/countTo'), name: 'count-to', meta: { title: 'CountTo' }}, - { path: 'mixin', component: _import('components/mixin'), name: 'componentMixin', meta: { title: '小组件' }}, - { path: 'back-to-top', component: _import('components/backToTop'), name: 'backToTop', meta: { title: '返回顶部' }} + { path: 'index', component: _import('components/index'), name: 'componentIndex', meta: { title: 'componentIndex' }}, + { path: 'tinymce', component: _import('components/tinymce'), name: 'tinymce', meta: { title: 'tinymce' }}, + { path: 'markdown', component: _import('components/markdown'), name: 'markdown', meta: { title: 'markdown' }}, + { path: 'json-editor', component: _import('components/jsonEditor'), name: 'jsonEditor', meta: { title: 'jsonEditor' }}, + { path: 'dnd-list', component: _import('components/dndList'), name: 'dndList', meta: { title: 'dndList' }}, + { path: 'splitpane', component: _import('components/splitpane'), name: 'splitpane', meta: { title: 'splitPane' }}, + { path: 'avatar-upload', component: _import('components/avatarUpload'), name: 'avatarUpload', meta: { title: 'avatarUpload' }}, + { path: 'dropzone', component: _import('components/dropzone'), name: 'dropzone', meta: { title: 'dropzone' }}, + { path: 'sticky', component: _import('components/sticky'), name: 'sticky', meta: { title: 'sticky' }}, + { path: 'count-to', component: _import('components/countTo'), name: 'countTo', meta: { title: 'countTo' }}, + { path: 'mixin', component: _import('components/mixin'), name: 'componentMixin', meta: { title: 'componentMixin' }}, + { path: 'back-to-top', component: _import('components/backToTop'), name: 'backToTop', meta: { title: 'backToTop' }} ] }, @@ -116,15 +116,15 @@ export const asyncRouterMap = [ redirect: '/charts/index', name: 'charts', meta: { - title: '图表', + title: 'charts', icon: 'chart' }, children: [ - { path: 'index', component: _import('charts/index'), name: 'chartsIndex', meta: { title: '介绍' }}, - { path: 'keyboard', component: _import('charts/keyboard'), name: 'keyboardChart', meta: { title: '键盘图表' }}, - { path: 'keyboard2', component: _import('charts/keyboard2'), name: 'keyboardChart2', meta: { title: '键盘图表2' }}, - { path: 'line', component: _import('charts/line'), name: 'lineChart', meta: { title: '折线图' }}, - { path: 'mixchart', component: _import('charts/mixChart'), name: 'mixChart', meta: { title: '混合图表' }} + { path: 'index', component: _import('charts/index'), name: 'chartsIndex', meta: { title: 'chartsIndex' }}, + { path: 'keyboard', component: _import('charts/keyboard'), name: 'keyboardChart', meta: { title: 'keyboardChart' }}, + { path: 'keyboard2', component: _import('charts/keyboard2'), name: 'keyboardChart2', meta: { title: 'keyboardChart2' }}, + { path: 'line', component: _import('charts/line'), name: 'lineChart', meta: { title: 'lineChart' }}, + { path: 'mixchart', component: _import('charts/mixChart'), name: 'mixChart', meta: { title: 'mixChart' }} ] }, @@ -134,7 +134,7 @@ export const asyncRouterMap = [ redirect: 'noredirect', name: 'example', meta: { - title: '综合实例', + title: 'example', icon: 'example' }, children: [ @@ -148,13 +148,13 @@ export const asyncRouterMap = [ icon: 'table' }, children: [ - { path: 'dynamic-table', component: _import('example/table/dynamicTable/index'), name: 'dynamicTable', meta: { title: '动态table' }}, - { path: 'drag-table', component: _import('example/table/dragTable'), name: 'dragTable', meta: { title: '拖拽table' }}, - { path: 'inline-edit-table', component: _import('example/table/inlineEditTable'), name: 'inlineEditTable', meta: { title: 'table内编辑' }}, - { path: 'complex-table', component: _import('example/table/complexTable'), name: 'complexTable', meta: { title: '综合table' }} + { path: 'dynamic-table', component: _import('example/table/dynamicTable/index'), name: 'dynamicTable', meta: { title: 'dynamicTable' }}, + { path: 'drag-table', component: _import('example/table/dragTable'), name: 'dragTable', meta: { title: 'dragTable' }}, + { path: 'inline-edit-table', component: _import('example/table/inlineEditTable'), name: 'inlineEditTable', meta: { title: 'inlineEditTable' }}, + { path: 'complex-table', component: _import('example/table/complexTable'), name: 'complexTable', meta: { title: 'complexTabl' }} ] }, - { path: 'tab/index', icon: 'tab', component: _import('example/tab/index'), name: 'tab', meta: { title: 'Tab' }} + { path: 'tab/index', icon: 'tab', component: _import('example/tab/index'), name: 'tab', meta: { title: 'tab' }} ] }, @@ -164,12 +164,12 @@ export const asyncRouterMap = [ redirect: 'noredirect', name: 'form', meta: { - title: '表单', + title: 'form', icon: 'form' }, children: [ - { path: 'create-form', component: _import('form/create'), name: 'createForm', meta: { title: '创建表单', icon: 'table' }}, - { path: 'edit-form', component: _import('form/edit'), name: 'editForm', meta: { title: '编辑表单', icon: 'table' }} + { path: 'create-form', component: _import('form/create'), name: 'createForm', meta: { title: 'createForm', icon: 'table' }}, + { path: 'edit-form', component: _import('form/edit'), name: 'editForm', meta: { title: 'editForm', icon: 'table' }} ] }, @@ -179,12 +179,12 @@ export const asyncRouterMap = [ redirect: 'noredirect', name: 'errorPages', meta: { - title: '错误页面', + title: 'errorPages', icon: '404' }, children: [ - { path: '401', component: _import('errorPage/401'), name: 'page401', meta: { title: '401', noCache: true }}, - { path: '404', component: _import('errorPage/404'), name: 'page404', meta: { title: '404', noCache: true }} + { path: '401', component: _import('errorPage/401'), name: 'page401', meta: { title: 'page401', noCache: true }}, + { path: '404', component: _import('errorPage/404'), name: 'page404', meta: { title: 'page404', noCache: true }} ] }, @@ -192,7 +192,7 @@ export const asyncRouterMap = [ path: '/error-log', component: Layout, redirect: 'noredirect', - children: [{ path: 'log', component: _import('errorLog/index'), name: 'errorLog', meta: { title: '错误日志', icon: 'bug' }}] + children: [{ path: 'log', component: _import('errorLog/index'), name: 'errorLog', meta: { title: 'errorLog', icon: 'bug' }}] }, { @@ -205,9 +205,9 @@ export const asyncRouterMap = [ icon: 'excel' }, children: [ - { path: 'export-excel', component: _import('excel/exportExcel'), name: 'exportExcel', meta: { title: 'export excel', noCache: true }}, - { path: 'export-selected-excel', component: _import('excel/selectExcel'), name: 'selectExcel', meta: { title: 'export selected', noCache: true }}, - { path: 'upload-excel', component: _import('excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'upload excel', noCache: true }} + { path: 'export-excel', component: _import('excel/exportExcel'), name: 'exportExcel', meta: { title: 'exportExcel', noCache: true }}, + { path: 'export-selected-excel', component: _import('excel/selectExcel'), name: 'selectExcel', meta: { title: 'selectExcel', noCache: true }}, + { path: 'upload-excel', component: _import('excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'uploadExcel', noCache: true }} ] }, @@ -215,21 +215,27 @@ export const asyncRouterMap = [ path: '/zip', component: Layout, redirect: '/zip/download', - children: [{ path: 'download', component: _import('zip/index'), name: 'exportZip', meta: { title: 'zip', icon: 'zip' }}] + children: [{ path: 'download', component: _import('zip/index'), name: 'exportZip', meta: { title: 'exportZip', icon: 'zip' }}] }, { path: '/theme', component: Layout, redirect: 'noredirect', - children: [{ path: 'index', component: _import('theme/index'), name: 'theme', meta: { title: '换肤', icon: 'theme' }}] + children: [{ path: 'index', component: _import('theme/index'), name: 'theme', meta: { title: 'theme', icon: 'theme' }}] }, { path: '/clipboard', component: Layout, redirect: 'noredirect', - children: [{ path: 'index', component: _import('clipboard/index'), name: 'clipboardDemo', meta: { title: 'clipboard', icon: 'clipboard' }}] + children: [{ path: 'index', component: _import('clipboard/index'), name: 'clipboardDemo', meta: { title: 'clipboardDemo', icon: 'clipboard' }}] + }, + + { + path: '/i18n', + component: Layout, + children: [{ path: 'index', component: _import('i18n/index'), name: 'i18n', meta: { title: 'i18n', icon: 'international' }}] }, { path: '*', redirect: '/404', hidden: true } diff --git a/src/store/getters.js b/src/store/getters.js index 07183a43..4acc4bbe 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -1,5 +1,6 @@ const getters = { sidebar: state => state.app.sidebar, + language: state => state.app.language, visitedViews: state => state.app.visitedViews, cachedViews: state => state.app.cachedViews, token: state => state.user.token, diff --git a/src/store/modules/app.js b/src/store/modules/app.js index 749916d6..f2c58489 100644 --- a/src/store/modules/app.js +++ b/src/store/modules/app.js @@ -5,6 +5,7 @@ const app = { sidebar: { opened: !+Cookies.get('sidebarStatus') }, + language: Cookies.get('language') || 'zh', visitedViews: [], cachedViews: [] }, @@ -17,6 +18,10 @@ const app = { } state.sidebar.opened = !state.sidebar.opened }, + SET_LANGUAGE: (state, language) => { + state.language = language + Cookies.set('language', language) + }, ADD_VISITED_VIEWS: (state, view) => { if (state.visitedViews.some(v => v.path === view.path)) return state.visitedViews.push({ @@ -48,6 +53,9 @@ const app = { ToggleSideBar({ commit }) { commit('TOGGLE_SIDEBAR') }, + setLanguage({ commit }, language) { + commit('SET_LANGUAGE', language) + }, addVisitedViews({ commit }, view) { commit('ADD_VISITED_VIEWS', view) }, diff --git a/src/views/i18n/index.vue b/src/views/i18n/index.vue new file mode 100644 index 00000000..c2e89374 --- /dev/null +++ b/src/views/i18n/index.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/views/i18n/local.js b/src/views/i18n/local.js new file mode 100644 index 00000000..a4c07203 --- /dev/null +++ b/src/views/i18n/local.js @@ -0,0 +1,36 @@ + +export default { + zh: { + i18nView: { + title: '切换语言', + note: '目前只翻译了当前页面和侧边栏和导航,未完待续,敬请期待...', + datePlaceholder: '请选择日期', + tableDate: '日期', + tableName: '姓名', + tableAddress: '地址', + default: '默认按钮', + primary: '主要按钮', + success: '成功按钮', + info: '信息按钮', + warning: '警告按钮', + danger: '危险按钮' + } + + }, + en: { + i18nView: { + title: 'Switch Language', + note: 'Currently only translated the i18n page and the sidebar and levelbar, please look forword to...', + datePlaceholder: 'Pick a day', + tableDate: 'tableDate', + tableName: 'tableName', + tableAddress: 'tableAddress', + default: 'default:', + primary: 'primary', + success: 'success', + info: 'info', + warning: 'warning', + danger: 'danger' + } + } +} diff --git a/src/views/layout/components/Navbar.vue b/src/views/layout/components/Navbar.vue index 26fc67a3..ee6d5c6a 100644 --- a/src/views/layout/components/Navbar.vue +++ b/src/views/layout/components/Navbar.vue @@ -12,6 +12,14 @@ + + + + 中文 + English + + + @@ -21,8 +29,8 @@ - - + + 首页 @@ -41,10 +49,6 @@ - - - -