diff --git a/filemanager/package-lock.json b/filemanager/package-lock.json index 300d7d78d..72f3c29ce 100644 --- a/filemanager/package-lock.json +++ b/filemanager/package-lock.json @@ -16,6 +16,7 @@ "primevue": "^3.29.2", "superagent": "^8.0.9", "vue": "^3.3.4", + "vue-i18n": "^9.2.2", "vue-router": "^4.2.2" }, "devDependencies": { @@ -391,6 +392,63 @@ "resolved": "https://registry.npmjs.org/@fontsource/noto-sans/-/noto-sans-5.0.3.tgz", "integrity": "sha512-x6M139l0kSik4GcIquZk30yj6fjwBRzqdjcnqSAwCJ0AGk32TqZd1OysHrew31IzHUxUmPoq3YByO+4pDPRBxg==" }, + "node_modules/@intlify/core-base": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz", + "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==", + "dependencies": { + "@intlify/devtools-if": "9.2.2", + "@intlify/message-compiler": "9.2.2", + "@intlify/shared": "9.2.2", + "@intlify/vue-devtools": "9.2.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@intlify/devtools-if": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz", + "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==", + "dependencies": { + "@intlify/shared": "9.2.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz", + "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==", + "dependencies": { + "@intlify/shared": "9.2.2", + "source-map": "0.6.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@intlify/shared": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz", + "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@intlify/vue-devtools": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz", + "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==", + "dependencies": { + "@intlify/core-base": "9.2.2", + "@intlify/shared": "9.2.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", @@ -970,6 +1028,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -1058,6 +1124,23 @@ "@vue/shared": "3.3.4" } }, + "node_modules/vue-i18n": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz", + "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==", + "dependencies": { + "@intlify/core-base": "9.2.2", + "@intlify/shared": "9.2.2", + "@intlify/vue-devtools": "9.2.2", + "@vue/devtools-api": "^6.2.1" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-router": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.2.tgz", diff --git a/filemanager/package.json b/filemanager/package.json index 1bf809044..ebd764b2f 100644 --- a/filemanager/package.json +++ b/filemanager/package.json @@ -17,6 +17,7 @@ "primevue": "^3.29.2", "superagent": "^8.0.9", "vue": "^3.3.4", + "vue-i18n": "^9.2.2", "vue-router": "^4.2.2" }, "devDependencies": { diff --git a/filemanager/src/main.js b/filemanager/src/main.js index 40ff9e904..415690119 100644 --- a/filemanager/src/main.js +++ b/filemanager/src/main.js @@ -1,4 +1,6 @@ import { createApp } from 'vue'; +import { createI18n } from 'vue-i18n'; + import './style.css'; import "@fontsource/noto-sans"; @@ -9,6 +11,7 @@ import 'primeicons/primeicons.css'; import PrimeVue from 'primevue/config'; import ConfirmationService from 'primevue/confirmationservice'; +import superagent from 'superagent'; import { createRouter, createWebHashHistory } from 'vue-router'; @@ -28,8 +31,45 @@ const router = createRouter({ routes, }); +const translations = {}; +const i18n = createI18n({ + locale: 'en', // set locale + fallbackLocale: 'en', // set fallback locale + messages: translations +}); + +// https://vue-i18n.intlify.dev/guide/advanced/lazy.html +(async function loadLanguages() { + const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? 'https://' + import.meta.env.VITE_API_ORIGIN : ''; + + async function loadLanguage(lang) { + try { + const result = await superagent.get(`${API_ORIGIN}/translation/${lang}.json`); + + // we do not deliver as application/json :/ + translations[lang] = JSON.parse(result.text); + } catch (e) { + console.error(`Failed to load language file for ${lang}`, e); + } + } + + await loadLanguage('en'); + + const locale = window.localStorage.NG_TRANSLATE_LANG_KEY; + if (locale && locale !== 'en') { + await loadLanguage(locale); + + if (i18n.mode === 'legacy') { + i18n.global.locale = locale; + } else { + i18n.global.locale.value = locale; + } + } +})(); + const app = createApp(App); +app.use(i18n); app.use(router); app.use(PrimeVue, { ripple: true }); app.use(ConfirmationService); diff --git a/filemanager/src/views/Home.vue b/filemanager/src/views/Home.vue index 7ea981205..d04474c0b 100644 --- a/filemanager/src/views/Home.vue +++ b/filemanager/src/views/Home.vue @@ -8,23 +8,23 @@ @@ -37,13 +37,13 @@ @@ -172,20 +172,20 @@ export default { }], // contextMenuModel will have activeItem attached if any command() is called createMenuModel: [{ - label: 'File', + label: () => this.$t('filemanager.toolbar.newFile'), icon: 'pi pi-file', command: this.onNewFile }, { - label: 'Folder', + label: () => this.$t('filemanager.toolbar.newFolder'), icon: 'pi pi-folder', command: this.onNewFolder }], uploadMenuModel: [{ - label: 'File', + label: () => this.$t('filemanager.toolbar.uploadFile'), icon: 'pi pi-file', command: this.onUploadFile }, { - label: 'Folder', + label: () => this.$t('filemanager.toolbar.newFolder'), icon: 'pi pi-folder', command: this.onUploadFolder }]