diff --git a/README.ja.md b/README.ja.md index 2f43128c..f0bc0ac1 100644 --- a/README.ja.md +++ b/README.ja.md @@ -138,7 +138,7 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s ```bash # clone the project -git clone https://github.com/PanJiaChen/vue-element-admin.git +git clone -b i18n git@github.com:PanJiaChen/vue-element-admin.git # enter the project directory cd vue-element-admin diff --git a/README.md b/README.md index a6a10447..360407c9 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s ```bash # clone the project -git clone https://github.com/PanJiaChen/vue-element-admin.git +git clone -b i18n git@github.com:PanJiaChen/vue-element-admin.git # enter the project directory cd vue-element-admin diff --git a/README.zh-CN.md b/README.zh-CN.md index bbb94a48..78a88e4a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -157,7 +157,7 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s ```bash # 克隆项目 -git clone https://github.com/PanJiaChen/vue-element-admin.git +git clone -b i18n git@github.com:PanJiaChen/vue-element-admin.git # 进入项目目录 cd vue-element-admin diff --git a/mock/role/routes.js b/mock/role/routes.js index d718919c..c4654566 100644 --- a/mock/role/routes.js +++ b/mock/role/routes.js @@ -41,7 +41,7 @@ export const constantRoutes = [ path: 'dashboard', component: 'views/dashboard/index', name: 'Dashboard', - meta: { title: 'Dashboard', icon: 'dashboard', affix: true } + meta: { title: 'dashboard', icon: 'dashboard', affix: true } } ] }, @@ -53,7 +53,7 @@ export const constantRoutes = [ path: 'index', component: 'views/documentation/index', name: 'Documentation', - meta: { title: 'Documentation', icon: 'documentation', affix: true } + meta: { title: 'documentation', icon: 'documentation', affix: true } } ] }, @@ -66,7 +66,7 @@ export const constantRoutes = [ path: 'index', component: 'views/guide/index', name: 'Guide', - meta: { title: 'Guide', icon: 'guide', noCache: true } + meta: { title: 'guide', icon: 'guide', noCache: true } } ] } @@ -79,7 +79,7 @@ export const asyncRoutes = [ redirect: '/permission/index', alwaysShow: true, meta: { - title: 'Permission', + title: 'permission', icon: 'lock', roles: ['admin', 'editor'] }, @@ -89,7 +89,7 @@ export const asyncRoutes = [ component: 'views/permission/page', name: 'PagePermission', meta: { - title: 'Page Permission', + title: 'pagePermission', roles: ['admin'] } }, @@ -98,7 +98,7 @@ export const asyncRoutes = [ component: 'views/permission/directive', name: 'DirectivePermission', meta: { - title: 'Directive Permission' + title: 'directivePermission' } }, { @@ -106,7 +106,7 @@ export const asyncRoutes = [ component: 'views/permission/role', name: 'RolePermission', meta: { - title: 'Role Permission', + title: 'rolePermission', roles: ['admin'] } } @@ -121,7 +121,7 @@ export const asyncRoutes = [ path: 'index', component: 'views/icons/index', name: 'Icons', - meta: { title: 'Icons', icon: 'icon', noCache: true } + meta: { title: 'icons', icon: 'icon', noCache: true } } ] }, @@ -132,7 +132,7 @@ export const asyncRoutes = [ redirect: 'noRedirect', name: 'ComponentDemo', meta: { - title: 'Components', + title: 'components', icon: 'component' }, children: [ @@ -140,49 +140,49 @@ export const asyncRoutes = [ path: 'tinymce', component: 'views/components-demo/tinymce', name: 'TinymceDemo', - meta: { title: 'Tinymce' } + meta: { title: 'tinymce' } }, { path: 'markdown', component: 'views/components-demo/markdown', name: 'MarkdownDemo', - meta: { title: 'Markdown' } + meta: { title: 'markdown' } }, { path: 'json-editor', component: 'views/components-demo/json-editor', name: 'JsonEditorDemo', - meta: { title: 'Json Editor' } + meta: { title: 'jsonEditor' } }, { path: 'split-pane', component: 'views/components-demo/split-pane', name: 'SplitpaneDemo', - meta: { title: 'SplitPane' } + meta: { title: 'splitPane' } }, { path: 'avatar-upload', component: 'views/components-demo/avatar-upload', name: 'AvatarUploadDemo', - meta: { title: 'Avatar Upload' } + meta: { title: 'avatarUpload' } }, { path: 'dropzone', component: 'views/components-demo/dropzone', name: 'DropzoneDemo', - meta: { title: 'Dropzone' } + meta: { title: 'dropzone' } }, { path: 'sticky', component: 'views/components-demo/sticky', name: 'StickyDemo', - meta: { title: 'Sticky' } + meta: { title: 'sticky' } }, { path: 'count-to', component: 'views/components-demo/count-to', name: 'CountToDemo', - meta: { title: 'Count To' } + meta: { title: 'countTo' } }, { path: 'mixin', @@ -194,31 +194,31 @@ export const asyncRoutes = [ path: 'back-to-top', component: 'views/components-demo/back-to-top', name: 'BackToTopDemo', - meta: { title: 'Back To Top' } + meta: { title: 'backToTop' } }, { path: 'drag-dialog', component: 'views/components-demo/drag-dialog', name: 'DragDialogDemo', - meta: { title: 'Drag Dialog' } + meta: { title: 'dragDialog' } }, { path: 'drag-select', component: 'views/components-demo/drag-select', name: 'DragSelectDemo', - meta: { title: 'Drag Select' } + meta: { title: 'dragSelect' } }, { path: 'dnd-list', component: 'views/components-demo/dnd-list', name: 'DndListDemo', - meta: { title: 'Dnd List' } + meta: { title: 'dndList' } }, { path: 'drag-kanban', component: 'views/components-demo/drag-kanban', name: 'DragKanbanDemo', - meta: { title: 'Drag Kanban' } + meta: { title: 'dragKanban' } } ] }, @@ -228,7 +228,7 @@ export const asyncRoutes = [ redirect: 'noRedirect', name: 'Charts', meta: { - title: 'Charts', + title: 'charts', icon: 'chart' }, children: [ @@ -236,19 +236,19 @@ export const asyncRoutes = [ path: 'keyboard', component: 'views/charts/keyboard', name: 'KeyboardChart', - meta: { title: 'Keyboard Chart', noCache: true } + meta: { title: 'keyboardChart', noCache: true } }, { path: 'line', component: 'views/charts/line', name: 'LineChart', - meta: { title: 'Line Chart', noCache: true } + meta: { title: 'lineChart', noCache: true } }, { path: 'mixchart', component: 'views/charts/mixChart', name: 'MixChart', - meta: { title: 'Mix Chart', noCache: true } + meta: { title: 'mixChart', noCache: true } } ] }, @@ -258,7 +258,7 @@ export const asyncRoutes = [ redirect: '/nested/menu1/menu1-1', name: 'Nested', meta: { - title: 'Nested', + title: 'nested', icon: 'nested' }, children: [ @@ -266,33 +266,33 @@ export const asyncRoutes = [ path: 'menu1', component: 'views/nested/menu1/index', name: 'Menu1', - meta: { title: 'Menu1' }, + meta: { title: 'menu1' }, redirect: '/nested/menu1/menu1-1', children: [ { path: 'menu1-1', component: 'views/nested/menu1/menu1-1', name: 'Menu1-1', - meta: { title: 'Menu1-1' } + meta: { title: 'menu1-1' } }, { path: 'menu1-2', component: 'views/nested/menu1/menu1-2', name: 'Menu1-2', redirect: '/nested/menu1/menu1-2/menu1-2-1', - meta: { title: 'Menu1-2' }, + meta: { title: 'menu1-2' }, children: [ { path: 'menu1-2-1', component: 'views/nested/menu1/menu1-2/menu1-2-1', name: 'Menu1-2-1', - meta: { title: 'Menu1-2-1' } + meta: { title: 'menu1-2-1' } }, { path: 'menu1-2-2', component: 'views/nested/menu1/menu1-2/menu1-2-2', name: 'Menu1-2-2', - meta: { title: 'Menu1-2-2' } + meta: { title: 'menu1-2-2' } } ] }, @@ -300,7 +300,7 @@ export const asyncRoutes = [ path: 'menu1-3', component: 'views/nested/menu1/menu1-3', name: 'Menu1-3', - meta: { title: 'Menu1-3' } + meta: { title: 'menu1-3' } } ] }, @@ -308,7 +308,7 @@ export const asyncRoutes = [ path: 'menu2', name: 'Menu2', component: 'views/nested/menu2/index', - meta: { title: 'Menu2' } + meta: { title: 'menu2' } } ] }, @@ -319,7 +319,7 @@ export const asyncRoutes = [ redirect: '/example/list', name: 'Example', meta: { - title: 'Example', + title: 'example', icon: 'example' }, children: [ @@ -327,20 +327,20 @@ export const asyncRoutes = [ path: 'create', component: 'views/example/create', name: 'CreateArticle', - meta: { title: 'Create Article', icon: 'edit' } + meta: { title: 'createArticle', icon: 'edit' } }, { path: 'edit/:id(\\d+)', component: 'views/example/edit', name: 'EditArticle', - meta: { title: 'Edit Article', noCache: true }, + meta: { title: 'editArticle', noCache: true }, hidden: true }, { path: 'list', component: 'views/example/list', name: 'ArticleList', - meta: { title: 'Article List', icon: 'list' } + meta: { title: 'articleList', icon: 'list' } } ] }, @@ -353,7 +353,7 @@ export const asyncRoutes = [ path: 'index', component: 'views/tab/index', name: 'Tab', - meta: { title: 'Tab', icon: 'tab' } + meta: { title: 'tab', icon: 'tab' } } ] }, @@ -364,7 +364,7 @@ export const asyncRoutes = [ redirect: 'noRedirect', name: 'ErrorPages', meta: { - title: 'Error Pages', + title: 'errorPages', icon: '404' }, children: [ @@ -372,13 +372,13 @@ export const asyncRoutes = [ path: '401', component: 'views/error-page/401', name: 'Page401', - meta: { title: 'Page 401', noCache: true } + meta: { title: 'page401', noCache: true } }, { path: '404', component: 'views/error-page/404', name: 'Page404', - meta: { title: 'Page 404', noCache: true } + meta: { title: 'page404', noCache: true } } ] }, @@ -392,7 +392,7 @@ export const asyncRoutes = [ path: 'log', component: 'views/error-log/index', name: 'ErrorLog', - meta: { title: 'Error Log', icon: 'bug' } + meta: { title: 'errorLog', icon: 'bug' } } ] }, @@ -403,7 +403,7 @@ export const asyncRoutes = [ redirect: '/excel/export-excel', name: 'Excel', meta: { - title: 'Excel', + title: 'excel', icon: 'excel' }, children: [ @@ -411,25 +411,25 @@ export const asyncRoutes = [ path: 'export-excel', component: 'views/excel/export-excel', name: 'ExportExcel', - meta: { title: 'Export Excel' } + meta: { title: 'exportExcel' } }, { path: 'export-selected-excel', component: 'views/excel/select-excel', name: 'SelectExcel', - meta: { title: 'Select Excel' } + meta: { title: 'selectExcel' } }, { path: 'export-merge-header', component: 'views/excel/merge-header', name: 'MergeHeader', - meta: { title: 'Merge Header' } + meta: { title: 'mergeHeader' } }, { path: 'upload-excel', component: 'views/excel/upload-excel', name: 'UploadExcel', - meta: { title: 'Upload Excel' } + meta: { title: 'uploadExcel' } } ] }, @@ -439,13 +439,13 @@ export const asyncRoutes = [ component: 'layout/Layout', redirect: '/zip/download', alwaysShow: true, - meta: { title: 'Zip', icon: 'zip' }, + meta: { title: 'zip', icon: 'zip' }, children: [ { path: 'download', component: 'views/zip/index', name: 'ExportZip', - meta: { title: 'Export Zip' } + meta: { title: 'exportZip' } } ] }, @@ -459,7 +459,7 @@ export const asyncRoutes = [ path: 'index', component: 'views/pdf/index', name: 'PDF', - meta: { title: 'PDF', icon: 'pdf' } + meta: { title: 'pdf', icon: 'pdf' } } ] }, @@ -478,7 +478,7 @@ export const asyncRoutes = [ path: 'index', component: 'views/theme/index', name: 'Theme', - meta: { title: 'Theme', icon: 'theme' } + meta: { title: 'theme', icon: 'theme' } } ] }, @@ -492,7 +492,7 @@ export const asyncRoutes = [ path: 'index', component: 'views/clipboard/index', name: 'ClipboardDemo', - meta: { title: 'Clipboard Demo', icon: 'clipboard' } + meta: { title: 'clipboardDemo', icon: 'clipboard' } } ] }, @@ -505,7 +505,7 @@ export const asyncRoutes = [ path: 'index', component: 'views/i18n-demo/index', name: 'I18n', - meta: { title: 'I18n', icon: 'international' } + meta: { title: 'i18n', icon: 'international' } } ] }, @@ -516,7 +516,7 @@ export const asyncRoutes = [ children: [ { path: 'https://github.com/PanJiaChen/vue-element-admin', - meta: { title: 'External Link', icon: 'link' } + meta: { title: 'externalLink', icon: 'link' } } ] }, diff --git a/package.json b/package.json index 9942fbac..02b8aa58 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "tui-editor": "1.3.3", "vue": "2.6.10", "vue-count-to": "1.0.13", + "vue-i18n": "7.3.2", "vue-router": "3.0.2", "vue-splitpane": "1.0.4", "vuedraggable": "2.20.0", diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue index e224ff73..2d1265fb 100644 --- a/src/components/Breadcrumb/index.vue +++ b/src/components/Breadcrumb/index.vue @@ -2,14 +2,17 @@ - {{ item.meta.title }} - {{ item.meta.title }} + + {{ generateTitle(item.meta.title) }} + + {{ generateTitle(item.meta.title) }} diff --git a/src/components/Tinymce/index.vue b/src/components/Tinymce/index.vue index 0c6174c4..670a0e53 100644 --- a/src/components/Tinymce/index.vue +++ b/src/components/Tinymce/index.vue @@ -71,6 +71,9 @@ export default { } }, computed: { + language() { + return this.languageTypeList[this.$store.getters.language] + }, containerWidth() { const width = this.width if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'` @@ -85,6 +88,10 @@ export default { this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val || '')) } + }, + language() { + this.destroyTinymce() + this.$nextTick(() => this.initTinymce()) } }, mounted() { @@ -115,8 +122,8 @@ export default { initTinymce() { const _this = this window.tinymce.init({ + language: this.language, selector: `#${this.tinymceId}`, - language: this.languageTypeList['en'], height: this.height, body_class: 'panel-body ', object_resizing: false, diff --git a/src/lang/en.js b/src/lang/en.js new file mode 100644 index 00000000..ae221ba8 --- /dev/null +++ b/src/lang/en.js @@ -0,0 +1,175 @@ +export default { + route: { + dashboard: 'Dashboard', + documentation: 'Documentation', + guide: 'Guide', + permission: 'Permission', + pagePermission: 'Page Permission', + rolePermission: 'Role Permission', + directivePermission: 'Directive Permission', + icons: 'Icons', + components: 'Components', + tinymce: 'Tinymce', + markdown: 'Markdown', + jsonEditor: 'JSON Editor', + dndList: 'Dnd List', + splitPane: 'SplitPane', + avatarUpload: 'Avatar Upload', + dropzone: 'Dropzone', + sticky: 'Sticky', + countTo: 'Count To', + componentMixin: 'Mixin', + backToTop: 'Back To Top', + dragDialog: 'Drag Dialog', + dragSelect: 'Drag Select', + dragKanban: 'Drag Kanban', + charts: 'Charts', + keyboardChart: 'Keyboard Chart', + lineChart: 'Line Chart', + mixChart: 'Mix Chart', + example: 'Example', + nested: 'Nested Routes', + menu1: 'Menu 1', + 'menu1-1': 'Menu 1-1', + 'menu1-2': 'Menu 1-2', + 'menu1-2-1': 'Menu 1-2-1', + 'menu1-2-2': 'Menu 1-2-2', + 'menu1-3': 'Menu 1-3', + menu2: 'Menu 2', + Table: 'Table', + dynamicTable: 'Dynamic Table', + dragTable: 'Drag Table', + inlineEditTable: 'Inline Edit', + complexTable: 'Complex Table', + tab: 'Tab', + form: 'Form', + createArticle: 'Create Article', + editArticle: 'Edit Article', + articleList: 'Article List', + errorPages: 'Error Pages', + page401: '401', + page404: '404', + errorLog: 'Error Log', + excel: 'Excel', + exportExcel: 'Export Excel', + selectExcel: 'Export Selected', + mergeHeader: 'Merge Header', + uploadExcel: 'Upload Excel', + zip: 'Zip', + pdf: 'PDF', + exportZip: 'Export Zip', + theme: 'Theme', + clipboardDemo: 'Clipboard', + i18n: 'I18n', + externalLink: 'External Link', + profile: 'Profile' + }, + navbar: { + dashboard: 'Dashboard', + github: 'Github', + logOut: 'Log Out', + profile: 'Profile', + theme: 'Theme', + size: 'Global Size' + }, + login: { + title: 'Login Form', + logIn: 'Login', + username: 'Username', + password: 'Password', + any: 'any', + thirdparty: 'Or connect with', + thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !' + }, + documentation: { + documentation: 'Documentation', + github: 'Github Repository' + }, + permission: { + addRole: 'New Role', + editPermission: 'Edit', + roles: 'Your roles', + switchRoles: 'Switch roles', + tips: 'In some cases, using v-permission will have no effect. For example: Element-UI el-tab or el-table-column and other scenes that dynamically render dom. You can only do this with v-if.', + delete: 'Delete', + confirm: 'Confirm', + cancel: 'Cancel' + }, + guide: { + description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ', + button: 'Show Guide' + }, + components: { + documentation: 'Documentation', + tinymceTips: 'Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.', + dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.', + stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.', + backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner', + backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally', + imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.' + }, + table: { + dynamicTips1: 'Fixed header, sorted by header order', + dynamicTips2: 'Not fixed header, sorted by click order', + dragTips1: 'The default order', + dragTips2: 'The after dragging order', + title: 'Title', + importance: 'Imp', + type: 'Type', + remark: 'Remark', + search: 'Search', + add: 'Add', + export: 'Export', + reviewer: 'reviewer', + id: 'ID', + date: 'Date', + author: 'Author', + readings: 'Readings', + status: 'Status', + actions: 'Actions', + edit: 'Edit', + publish: 'Publish', + draft: 'Draft', + delete: 'Delete', + cancel: 'Cancel', + confirm: 'Confirm' + }, + example: { + warning: 'Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all pages directly. See details' + }, + errorLog: { + tips: 'Please click the bug icon in the upper right corner', + description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.', + documentation: 'Document introduction' + }, + excel: { + export: 'Export', + selectedExport: 'Export Selected Items', + placeholder: 'Please enter the file name (default excel-list)' + }, + zip: { + export: 'Export', + placeholder: 'Please enter the file name (default file)' + }, + pdf: { + tips: 'Here we use window.print() to implement the feature of downloading PDF.' + }, + theme: { + change: 'Change Theme', + documentation: 'Theme documentation', + tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.' + }, + tagsView: { + refresh: 'Refresh', + close: 'Close', + closeOthers: 'Close Others', + closeAll: 'Close All' + }, + settings: { + title: 'Page style setting', + theme: 'Theme Color', + tagsView: 'Open Tags-View', + fixedHeader: 'Fixed Header', + sidebarLogo: 'Sidebar Logo' + } +} diff --git a/src/lang/es.js b/src/lang/es.js new file mode 100755 index 00000000..8187bfe7 --- /dev/null +++ b/src/lang/es.js @@ -0,0 +1,175 @@ +export default { + route: { + dashboard: 'Panel de control', + documentation: 'Documentación', + guide: 'Guía', + permission: 'Permisos', + rolePermission: 'Permisos de rol', + pagePermission: 'Permisos de la página', + directivePermission: 'Permisos de la directiva', + icons: 'Iconos', + components: 'Componentes', + tinymce: 'Tinymce', + markdown: 'Markdown', + jsonEditor: 'Editor JSON', + dndList: 'Lista Dnd', + splitPane: 'Panel dividido', + avatarUpload: 'Subir avatar', + dropzone: 'Subir ficheros', + sticky: 'Sticky', + countTo: 'CountTo', + componentMixin: 'Mixin', + backToTop: 'Ir arriba', + dragDialog: 'Drag Dialog', + dragSelect: 'Drag Select', + dragKanban: 'Drag Kanban', + charts: 'Gráficos', + keyboardChart: 'Keyboard Chart', + lineChart: 'Gráfico de líneas', + mixChart: 'Mix Chart', + example: 'Ejemplo', + nested: 'Rutas anidadass', + menu1: 'Menu 1', + 'menu1-1': 'Menu 1-1', + 'menu1-2': 'Menu 1-2', + 'menu1-2-1': 'Menu 1-2-1', + 'menu1-2-2': 'Menu 1-2-2', + 'menu1-3': 'Menu 1-3', + menu2: 'Menu 2', + Table: 'Tabla', + dynamicTable: 'Tabla dinámica', + dragTable: 'Arrastrar tabla', + inlineEditTable: 'Editor', + complexTable: 'Complex Table', + tab: 'Pestaña', + form: 'Formulario', + createArticle: 'Crear artículo', + editArticle: 'Editar artículo', + articleList: 'Listado de artículos', + errorPages: 'Páginas de error', + page401: '401', + page404: '404', + errorLog: 'Registro de errores', + excel: 'Excel', + exportExcel: 'Exportar a Excel', + selectExcel: 'Export seleccionado', + mergeHeader: 'Merge Header', + uploadExcel: 'Subir Excel', + zip: 'Zip', + pdf: 'PDF', + exportZip: 'Exportar a Zip', + theme: 'Tema', + clipboardDemo: 'Clipboard', + i18n: 'I18n', + externalLink: 'Enlace externo', + profile: 'Profile' + }, + navbar: { + logOut: 'Salir', + dashboard: 'Panel de control', + github: 'Github', + theme: 'Tema', + size: 'Tamaño global', + profile: 'Profile' + }, + login: { + title: 'Formulario de acceso', + logIn: 'Acceso', + username: 'Usuario', + password: 'Contraseña', + any: 'nada', + thirdparty: 'Conectar con', + thirdpartyTips: 'No se puede simular en local, así que combine su propia simulación de negocios. ! !' + }, + documentation: { + documentation: 'Documentación', + github: 'Repositorio Github' + }, + permission: { + addRole: 'Nuevo rol', + editPermission: 'Permiso de edición', + roles: 'Tus permisos', + switchRoles: 'Cambiar permisos', + tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.', + delete: 'Borrar', + confirm: 'Confirmar', + cancel: 'Cancelar' + }, + guide: { + description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ', + button: 'Ver guía' + }, + components: { + documentation: 'Documentación', + tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.', + dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.', + stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.', + backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner', + backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally', + imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.' + }, + table: { + dynamicTips1: 'Fixed header, sorted by header order', + dynamicTips2: 'Not fixed header, sorted by click order', + dragTips1: 'Orden por defecto', + dragTips2: 'The after dragging order', + title: 'Título', + importance: 'Importancia', + type: 'Tipo', + remark: 'Remark', + search: 'Buscar', + add: 'Añadir', + export: 'Exportar', + reviewer: 'reviewer', + id: 'ID', + date: 'Fecha', + author: 'Autor', + readings: 'Lector', + status: 'Estado', + actions: 'Acciones', + edit: 'Editar', + publish: 'Publicar', + draft: 'Draft', + delete: 'Eliminar', + cancel: 'Cancelar', + confirm: 'Confirmar' + }, + example: { + warning: 'Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all pages directly. See details' + }, + errorLog: { + tips: 'Please click the bug icon in the upper right corner', + description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.', + documentation: 'Documento de introducción' + }, + excel: { + export: 'Exportar', + selectedExport: 'Exportar seleccionados', + placeholder: 'Por favor escribe un nombre de fichero' + }, + zip: { + export: 'Exportar', + placeholder: 'Por favor escribe un nombre de fichero' + }, + pdf: { + tips: 'Here we use window.print() to implement the feature of downloading pdf.' + }, + theme: { + change: 'Cambiar tema', + documentation: 'Documentación del tema', + tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.' + }, + tagsView: { + refresh: 'Actualizar', + close: 'Cerrar', + closeOthers: 'Cerrar otros', + closeAll: 'Cerrar todos' + }, + settings: { + title: 'Page style setting', + theme: 'Theme Color', + tagsView: 'Open Tags-View', + fixedHeader: 'Fixed Header', + sidebarLogo: 'Sidebar Logo' + } +} diff --git a/src/lang/index.js b/src/lang/index.js new file mode 100644 index 00000000..1799ae7f --- /dev/null +++ b/src/lang/index.js @@ -0,0 +1,55 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementEsLocale from 'element-ui/lib/locale/lang/es'// element-ui lang +import elementJaLocale from 'element-ui/lib/locale/lang/ja'// element-ui lang +import enLocale from './en' +import zhLocale from './zh' +import esLocale from './es' +import jaLocale from './ja' + +Vue.use(VueI18n) + +const messages = { + en: { + ...enLocale, + ...elementEnLocale + }, + zh: { + ...zhLocale, + ...elementZhLocale + }, + es: { + ...esLocale, + ...elementEsLocale + }, + ja: { + ...jaLocale, + ...elementJaLocale + } +} +export function getLanguage() { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'en' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/src/lang/ja.js b/src/lang/ja.js new file mode 100644 index 00000000..7dccd124 --- /dev/null +++ b/src/lang/ja.js @@ -0,0 +1,175 @@ +export default { + route: { + dashboard: 'トップ', + documentation: 'ドキュメント', + guide: 'ガイド', + permission: '権限', + rolePermission: '権限ロール', + pagePermission: 'ページ権限', + directivePermission: 'ディレクティブ権限', + icons: 'アイコン', + components: 'コンポーネント', + tinymce: 'TinyMCE', + markdown: 'Markdown', + jsonEditor: 'JSON Editor', + dndList: 'Drag-And-Drop', + splitPane: 'パネル', + avatarUpload: 'アバターアップロード', + dropzone: 'Dropzone', + sticky: 'Sticky', + countTo: 'Count To', + componentMixin: 'コンポーネントMixin', + backToTop: 'BackToTop', + dragDialog: 'Drag Dialog', + dragSelect: 'Drag Select', + dragKanban: 'Drag 看板', + charts: 'チャート', + keyboardChart: 'Keyboardチャート', + lineChart: 'Lineチャート', + mixChart: 'Mixチャート', + example: 'Example', + nested: 'Nested Routes', + menu1: 'メニュー1', + 'menu1-1': 'メニュー 1-1', + 'menu1-2': 'メニュー 1-2', + 'menu1-2-1': 'メニュー 1-2-1', + 'menu1-2-2': 'メニュー 1-2-2', + 'menu1-3': 'メニュー 1-3', + menu2: 'メニュー 2', + Table: 'Table', + dynamicTable: '可変 Table', + dragTable: 'Drag Table', + inlineEditTable: 'Inline Edit Table', + complexTable: 'Complex Table', + tab: 'Tab', + form: 'フォーム', + createArticle: '投稿作成', + editArticle: '投稿編集', + articleList: '投稿リスト', + errorPages: 'エラーページ', + page401: '401', + page404: '404', + errorLog: 'エラーログ', + excel: 'Excel', + exportExcel: '一括エクスポート', + selectExcel: '複数選択エクスポート', + mergeHeader: 'ヘッダーマージ', + uploadExcel: 'アップロード', + zip: 'Zip', + pdf: 'PDF', + exportZip: 'Export Zip', + theme: 'テーマ変更', + clipboardDemo: 'Clipboard', + i18n: '多言語', + externalLink: '外部リンク', + profile: 'プロフィール' + }, + navbar: { + dashboard: 'トップ', + github: 'GitHub', + logOut: 'ログアウト', + profile: 'プロフィール', + theme: 'テーマ変更', + size: '画面サイズ' + }, + login: { + title: 'ユーザログイン', + logIn: 'ログイン', + username: 'ユーザ名', + password: 'パスワード', + any: 'password', + thirdparty: '外部IDでログイン', + thirdpartyTips: 'ローカル環境ではログインできません。実装が必要です。' + }, + documentation: { + documentation: 'ドキュメント', + github: 'Github Link' + }, + permission: { + addRole: 'ロール追加', + editPermission: 'ロール変更', + roles: 'ロール', + switchRoles: 'ロール切替', + tips: 'v-permissionは使えない時があります。例えば: Element-UI の el-tab、 el-table-column 及び他の dom。v-ifを使う必要があります。', + delete: '削除', + confirm: '確認', + cancel: 'キャンセル' + }, + guide: { + description: 'ガイドは各機能の説明です。', + button: 'ガイドを見る' + }, + components: { + documentation: 'ドキュメント', + tinymceTips: 'tinymceは管理画面に重要な機能ですが、その同時に落とし穴がありあす。tinymceを使う道のりが大変でした。Tinymceを使う時に各自のプロジェクト状況で判断が必要です。ドキュメントはこちら', + dropzoneTips: 'Third partyのパッケージを使わず、独自の実装しています。詳細は @/components/Dropzone', + stickyTips: 'ページの指定位置へスクロールした場合、表示されます。', + backToTopTips1: 'トップへスクロールが表示されます。', + backToTopTips2: 'ボタンのスタイルはカスタマイズできます。例えば、show/hide、height、position。 またはElementのel-tooltipを使って、ツールチップを実装できます。', + imageUploadTips: 'mockjsは使えないため、カスタマイズしています。公式の最新バージョンを使ってください。' + }, + table: { + dynamicTips1: '先頭は固定、最後に追加', + dynamicTips2: '戦後に追加せず、指定列に追加', + dragTips1: 'デフォルト順番', + dragTips2: 'Drag後の順番', + title: 'タイトル', + importance: '重要', + type: 'タイプ', + remark: '評価', + search: '検索', + add: '追加', + export: 'エクスポート', + reviewer: 'レビュアー', + id: '番号', + date: '日時', + author: '作成者', + readings: '閲覧数', + status: 'ステータス', + actions: '操作', + edit: '編集', + publish: '公開', + draft: '下書き', + delete: 'キャンセル', + cancel: 'キャンセル', + confirm: '確認' + }, + example: { + warning: '新規作成と編集画面は keep-alive を使えないです。keep-alive の include はrouteのキャッシュは使えないです。そのため、component name を使ってキャッシュさせるようにします。このようなキャッシュ機能を作りたい場合,localStorageを使う手があります。もしくは keep-alive の includeを使って、全ページキャッシュする方法はあります。' + }, + errorLog: { + tips: '右上のbugアイコンをクリックしてください。', + description: '管理画面はspaを使う場合が多い、ユーザ体現向上はできますが、想定外エラーが発生する場合があります。Vueはそのエラーハンドリング機能を提供し、エラーレポートができます。', + documentation: 'ドキュメント' + }, + excel: { + export: 'エクスポート', + selectedExport: 'エクスポート対象を選択してください。', + placeholder: 'ファイル名を入力してください。' + }, + zip: { + export: 'エクスポート', + placeholder: 'ファイル名を入力してください。' + }, + pdf: { + tips: 'window.print() を使ってPDFダウンロードしています。' + }, + theme: { + change: 'テーマ切替', + documentation: 'ドキュメント', + tips: 'Tips: テーマの切り替え方法はnavbarのtheme-pickと異なります、使い方はドキュメントを確認してください。' + }, + tagsView: { + refresh: '更新', + close: '閉じる', + closeOthers: 'その他閉じる', + closeAll: 'すべて閉じる' + }, + settings: { + title: 'システムテーマ', + theme: 'テーマ色', + tagsView: 'Tags-View 開く', + fixedHeader: 'Fixed Header', + sidebarLogo: 'Sidebar Logo' + } +} diff --git a/src/lang/zh.js b/src/lang/zh.js new file mode 100644 index 00000000..dee804d2 --- /dev/null +++ b/src/lang/zh.js @@ -0,0 +1,175 @@ +export default { + route: { + dashboard: '首页', + documentation: '文档', + guide: '引导页', + permission: '权限测试页', + rolePermission: '角色权限', + pagePermission: '页面权限', + directivePermission: '指令权限', + icons: '图标', + components: '组件', + tinymce: '富文本编辑器', + markdown: 'Markdown', + jsonEditor: 'JSON 编辑器', + dndList: '列表拖拽', + splitPane: 'Splitpane', + avatarUpload: '头像上传', + dropzone: 'Dropzone', + sticky: 'Sticky', + countTo: 'Count To', + componentMixin: '小组件', + backToTop: '返回顶部', + dragDialog: '拖拽 Dialog', + dragSelect: '拖拽 Select', + dragKanban: '可拖拽看板', + charts: '图表', + keyboardChart: '键盘图表', + lineChart: '折线图', + mixChart: '混合图表', + example: '综合实例', + nested: '路由嵌套', + menu1: '菜单1', + 'menu1-1': '菜单 1-1', + 'menu1-2': '菜单 1-2', + 'menu1-2-1': '菜单 1-2-1', + 'menu1-2-2': '菜单 1-2-2', + 'menu1-3': '菜单 1-3', + menu2: '菜单 2', + Table: 'Table', + dynamicTable: '动态 Table', + dragTable: '拖拽 Table', + inlineEditTable: 'Table 内编辑', + complexTable: '综合 Table', + tab: 'Tab', + form: '表单', + createArticle: '创建文章', + editArticle: '编辑文章', + articleList: '文章列表', + errorPages: '错误页面', + page401: '401', + page404: '404', + errorLog: '错误日志', + excel: 'Excel', + exportExcel: '导出 Excel', + selectExcel: '导出 已选择项', + mergeHeader: '导出 多级表头', + uploadExcel: '上传 Excel', + zip: 'Zip', + pdf: 'PDF', + exportZip: 'Export Zip', + theme: '换肤', + clipboardDemo: 'Clipboard', + i18n: '国际化', + externalLink: '外链', + profile: '个人中心' + }, + navbar: { + dashboard: '首页', + github: '项目地址', + logOut: '退出登录', + profile: '个人中心', + theme: '换肤', + size: '布局大小' + }, + login: { + title: '系统登录', + logIn: '登录', + username: '账号', + password: '密码', + any: '随便填', + thirdparty: '第三方登录', + thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!' + }, + documentation: { + documentation: '文档', + github: 'Github 地址' + }, + permission: { + addRole: '新增角色', + editPermission: '编辑权限', + roles: '你的权限', + switchRoles: '切换权限', + tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 el-tab 或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。', + delete: '删除', + confirm: '确定', + cancel: '取消' + }, + guide: { + description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于', + button: '打开引导' + }, + components: { + documentation: '文档', + tinymceTips: '富文本是管理后台一个核心的功能,但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路,市面上常见的富文本都基本用过了,最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见', + dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone', + stickyTips: '当页面滚动到预设的位置会吸附在顶部', + backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮', + backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示,可在外部使用Element的el-tooltip元素', + imageUploadTips: '由于我在使用时它只有vue@1版本,而且和mockjs不兼容,所以自己改造了一下,如果大家要使用的话,优先还是使用官方版本。' + }, + table: { + dynamicTips1: '固定表头, 按照表头顺序排序', + dynamicTips2: '不固定表头, 按照点击顺序排序', + dragTips1: '默认顺序', + dragTips2: '拖拽后顺序', + title: '标题', + importance: '重要性', + type: '类型', + remark: '点评', + search: '搜索', + add: '添加', + export: '导出', + reviewer: '审核人', + id: '序号', + date: '时间', + author: '作者', + readings: '阅读数', + status: '状态', + actions: '操作', + edit: '编辑', + publish: '发布', + draft: '草稿', + delete: '删除', + cancel: '取 消', + confirm: '确 定' + }, + example: { + warning: '创建和编辑页面是不能被 keep-alive 缓存的,因为keep-alive 的 include 目前不支持根据路由来缓存,所以目前都是基于 component name 来进行缓存的。如果你想类似的实现缓存效果,可以使用 localStorage 等浏览器缓存方案。或者不要使用 keep-alive 的 include,直接缓存所有页面。详情见' + }, + errorLog: { + tips: '请点击右上角bug小图标', + description: '现在的管理后台基本都是spa的形式了,它增强了用户体验,但同时也会增加页面出问题的可能性,可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。', + documentation: '文档介绍' + }, + excel: { + export: '导出', + selectedExport: '导出已选择项', + placeholder: '请输入文件名(默认excel-list)' + }, + zip: { + export: '导出', + placeholder: '请输入文件名(默认file)' + }, + pdf: { + tips: '这里使用 window.print() 来实现下载pdf的功能' + }, + theme: { + change: '换肤', + documentation: '换肤文档', + tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。' + }, + tagsView: { + refresh: '刷新', + close: '关闭', + closeOthers: '关闭其它', + closeAll: '关闭所有' + }, + settings: { + title: '系统布局配置', + theme: '主题色', + tagsView: '开启 Tags-View', + fixedHeader: '固定 Header', + sidebarLogo: '侧边栏 Logo' + } +} diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue index d9075a24..9d5c6658 100644 --- a/src/layout/components/Navbar.vue +++ b/src/layout/components/Navbar.vue @@ -12,10 +12,12 @@ - + + + @@ -25,19 +27,25 @@ - Profile + + {{ $t('navbar.profile') }} + - Dashboard + + {{ $t('navbar.dashboard') }} + - Github + + {{ $t('navbar.github') }} + Docs - Log Out + {{ $t('navbar.logOut') }} @@ -52,6 +60,7 @@ import Hamburger from '@/components/Hamburger' import ErrorLog from '@/components/ErrorLog' import Screenfull from '@/components/Screenfull' import SizeSelect from '@/components/SizeSelect' +import LangSelect from '@/components/LangSelect' import Search from '@/components/HeaderSearch' export default { @@ -61,6 +70,7 @@ export default { ErrorLog, Screenfull, SizeSelect, + LangSelect, Search }, computed: { diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue index 32ef0189..8dab8cbf 100644 --- a/src/layout/components/Settings/index.vue +++ b/src/layout/components/Settings/index.vue @@ -1,27 +1,34 @@ + + diff --git a/src/views/i18n-demo/local.js b/src/views/i18n-demo/local.js new file mode 100644 index 00000000..ae38dd28 --- /dev/null +++ b/src/views/i18n-demo/local.js @@ -0,0 +1,83 @@ + +export default { + zh: { + i18nView: { + title: '切换语言', + note: '本项目国际化基于 vue-i18n', + datePlaceholder: '请选择日期', + selectPlaceholder: '请选择', + tableDate: '日期', + tableName: '姓名', + tableAddress: '地址', + default: '默认按钮', + primary: '主要按钮', + success: '成功按钮', + info: '信息按钮', + warning: '警告按钮', + danger: '危险按钮', + one: '一', + two: '二', + three: '三' + } + }, + en: { + i18nView: { + title: 'Switch Language', + note: 'The internationalization of this project is based on vue-i18n', + datePlaceholder: 'Pick a day', + selectPlaceholder: 'Select', + tableDate: 'tableDate', + tableName: 'tableName', + tableAddress: 'tableAddress', + default: 'default:', + primary: 'primary', + success: 'success', + info: 'info', + warning: 'warning', + danger: 'danger', + one: 'One', + two: 'Two', + three: 'Three' + } + }, + es: { + i18nView: { + title: 'Switch Language', + note: 'The internationalization of this project is based on vue-i18n', + datePlaceholder: 'Pick a day', + selectPlaceholder: 'Select', + tableDate: 'tableDate', + tableName: 'tableName', + tableAddress: 'tableAddress', + default: 'default:', + primary: 'primary', + success: 'success', + info: 'info', + warning: 'warning', + danger: 'danger', + one: 'One', + two: 'Two', + three: 'Three' + } + }, + ja: { + i18nView: { + title: '言語切替', + note: 'vue-i18nを使っています', + datePlaceholder: '日時選択', + selectPlaceholder: '選択してください', + tableDate: '日時', + tableName: '姓名', + tableAddress: '住所', + default: 'default', + primary: 'primary', + success: 'success', + info: 'info', + warning: 'warning', + danger: 'danger', + one: '1', + two: '2', + three: '3' + } + } +} diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 5fb3f6e3..e21455ef 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -3,7 +3,10 @@ - Login + + {{ $t('login.logIn') }} +
- Username : admin - Password : any + {{ $t('login.username') }} : admin + {{ $t('login.password') }} : {{ $t('login.any') }}
- Username : editor - Password : any + + {{ $t('login.username') }} : editor + + {{ $t('login.password') }} : {{ $t('login.any') }}
- Or connect with + {{ $t('login.thirdparty') }}
- - Can not be simulated on local, so please combine you own business simulation! ! ! + + {{ $t('login.thirdpartyTips') }}


@@ -75,11 +82,12 @@