diff --git a/package-lock.json b/package-lock.json index 23719a6..82e3cd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "fastify-helmet": "^7.0.1", "fastify-static": "^4.5.0", "vue": "^3.2.27", + "vue-i18n": "^9.2.0-beta.30", "vue-router": "^4.0.12" }, "devDependencies": { @@ -284,6 +285,63 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@intlify/core-base": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.0-beta.30.tgz", + "integrity": "sha512-tnOuI8gs4S7vv4WjG8oFL7vbZ4PM7Is/Ld3lRHQlBO7UjpnCVcQ94AgP/4F0cUPFn9JSPMQRN0aOOahW1BXvSA==", + "dependencies": { + "@intlify/devtools-if": "9.2.0-beta.30", + "@intlify/message-compiler": "9.2.0-beta.30", + "@intlify/shared": "9.2.0-beta.30", + "@intlify/vue-devtools": "9.2.0-beta.30" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@intlify/devtools-if": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.0-beta.30.tgz", + "integrity": "sha512-3OxGFi6ooya9DFqX/JsxFjrj9nGYcDoo4CRGYSDqnC+xv4bnsyB5ekmaYBiVZtagCdZdSUMxbTFphl1WbtgNLQ==", + "dependencies": { + "@intlify/shared": "9.2.0-beta.30" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.0-beta.30.tgz", + "integrity": "sha512-2kj/0nLIFrgiO86f9VifcUUcV8LdzXt4YYPIujx/LkTEQOuSFUo/bNiMaG1hyfiU/8mfq6tsaWKjoOZjeao1eQ==", + "dependencies": { + "@intlify/shared": "9.2.0-beta.30", + "source-map": "0.6.1" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@intlify/shared": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.0-beta.30.tgz", + "integrity": "sha512-E1WHRTIlUEse3d/6t1pAagSXRxmeVeNIhx5kT80dfpYxw8lOnCWV9wLve2bq9Fkv+3TD2I5j+CdN7jvSl3LdsA==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@intlify/vue-devtools": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.0-beta.30.tgz", + "integrity": "sha512-hcqDfwP/oXVmVCaJ0RA+uv1WSCcd42/Y13S0bySmWZv2KamLcxiD7wYxp/MaECG/D4KZcSLkq/wDHTG7lhYf5Q==", + "dependencies": { + "@intlify/core-base": "9.2.0-beta.30", + "@intlify/shared": "9.2.0-beta.30" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -6620,6 +6678,23 @@ "node": ">=4.0" } }, + "node_modules/vue-i18n": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.0-beta.30.tgz", + "integrity": "sha512-5DqrgG9ffgC7j3RRAfViC0WUcdz0C3Ix1qq1AyQItpF7UkSB6iSJGEjBG6KdspbRQq/8t1YzDx4JRXbL05l6ow==", + "dependencies": { + "@intlify/core-base": "9.2.0-beta.30", + "@intlify/shared": "9.2.0-beta.30", + "@intlify/vue-devtools": "9.2.0-beta.30", + "@vue/devtools-api": "^6.0.0-beta.13" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-router": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.12.tgz", @@ -7117,6 +7192,48 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@intlify/core-base": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.0-beta.30.tgz", + "integrity": "sha512-tnOuI8gs4S7vv4WjG8oFL7vbZ4PM7Is/Ld3lRHQlBO7UjpnCVcQ94AgP/4F0cUPFn9JSPMQRN0aOOahW1BXvSA==", + "requires": { + "@intlify/devtools-if": "9.2.0-beta.30", + "@intlify/message-compiler": "9.2.0-beta.30", + "@intlify/shared": "9.2.0-beta.30", + "@intlify/vue-devtools": "9.2.0-beta.30" + } + }, + "@intlify/devtools-if": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.0-beta.30.tgz", + "integrity": "sha512-3OxGFi6ooya9DFqX/JsxFjrj9nGYcDoo4CRGYSDqnC+xv4bnsyB5ekmaYBiVZtagCdZdSUMxbTFphl1WbtgNLQ==", + "requires": { + "@intlify/shared": "9.2.0-beta.30" + } + }, + "@intlify/message-compiler": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.0-beta.30.tgz", + "integrity": "sha512-2kj/0nLIFrgiO86f9VifcUUcV8LdzXt4YYPIujx/LkTEQOuSFUo/bNiMaG1hyfiU/8mfq6tsaWKjoOZjeao1eQ==", + "requires": { + "@intlify/shared": "9.2.0-beta.30", + "source-map": "0.6.1" + } + }, + "@intlify/shared": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.0-beta.30.tgz", + "integrity": "sha512-E1WHRTIlUEse3d/6t1pAagSXRxmeVeNIhx5kT80dfpYxw8lOnCWV9wLve2bq9Fkv+3TD2I5j+CdN7jvSl3LdsA==" + }, + "@intlify/vue-devtools": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.0-beta.30.tgz", + "integrity": "sha512-hcqDfwP/oXVmVCaJ0RA+uv1WSCcd42/Y13S0bySmWZv2KamLcxiD7wYxp/MaECG/D4KZcSLkq/wDHTG7lhYf5Q==", + "requires": { + "@intlify/core-base": "9.2.0-beta.30", + "@intlify/shared": "9.2.0-beta.30" + } + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -11904,6 +12021,17 @@ } } }, + "vue-i18n": { + "version": "9.2.0-beta.30", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.0-beta.30.tgz", + "integrity": "sha512-5DqrgG9ffgC7j3RRAfViC0WUcdz0C3Ix1qq1AyQItpF7UkSB6iSJGEjBG6KdspbRQq/8t1YzDx4JRXbL05l6ow==", + "requires": { + "@intlify/core-base": "9.2.0-beta.30", + "@intlify/shared": "9.2.0-beta.30", + "@intlify/vue-devtools": "9.2.0-beta.30", + "@vue/devtools-api": "^6.0.0-beta.13" + } + }, "vue-router": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.12.tgz", diff --git a/package.json b/package.json index bff752f..2b4ef6a 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "fastify-helmet": "^7.0.1", "fastify-static": "^4.5.0", "vue": "^3.2.27", + "vue-i18n": "^9.2.0-beta.30", "vue-router": "^4.0.12" }, "devDependencies": { diff --git a/src/App.vue b/src/App.vue index a1020ad..e86ca95 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,7 +9,7 @@ class="flex flex-row space-x-2 items-center content-center justify-center m-20 text-red-500" > - Es ist ein Fehler aufgetreten... + {{ t('errors.generic.text') }} @@ -20,7 +20,7 @@ class="flex flex-row space-x-2 items-center content-center justify-center m-20" > - Lade... + {{ t('common.loading.text') }} @@ -34,6 +34,8 @@ @@ -41,14 +43,16 @@ defineProps<{ class="text-sm mt-1 text-stone-500 flex flex-row items-center w-fit" > - Produktseite öffnen + {{ + t('components.wishlist-item.external-product-page-link.text') + }} Gekauft{{ t('components.wishlist-item.bought-button.text') }} diff --git a/src/composables/useModal.ts b/src/composables/useModal.ts index 795c8de..10f4428 100644 --- a/src/composables/useModal.ts +++ b/src/composables/useModal.ts @@ -7,8 +7,15 @@ const callback = (confirmed: boolean) => { _resolve(confirmed) } -const show = (title: string, text = '') => { +const show = ( + title: string, + confirmText: string, + cancelText: string, + text = '' +) => { data.title = title + data.confirmText = confirmText + data.cancelText = cancelText data.text = text data.isShown = true return new Promise((resolve) => { @@ -21,9 +28,9 @@ const data = reactive({ show, title: '', text: '', - confirmText: 'Ja', + confirmText: '', confirm: () => callback(true), - cancelText: 'Nein', + cancelText: '', cancel: () => callback(false), }) diff --git a/src/config/i18n.ts b/src/config/i18n.ts new file mode 100644 index 0000000..e95e4ee --- /dev/null +++ b/src/config/i18n.ts @@ -0,0 +1,15 @@ +import { createI18n } from 'vue-i18n' +import enUS from './locales/en-US.json' +import deDE from './locales/de-DE.json' + +export default createI18n({ + legacy: false, + locale: navigator.language || 'en', + fallbackLocale: 'en-US', + messages: { + 'en-US': enUS, + en: enUS, + 'de-DE': deDE, + de: deDE, + }, +}) diff --git a/src/config/index.ts b/src/config/index.ts index 2917c8d..b0f1cbb 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1 +1,2 @@ export { default as apiConfig } from './apiConfig' +export { default as i18n } from './i18n' diff --git a/src/config/locales/de-DE.json b/src/config/locales/de-DE.json new file mode 100644 index 0000000..bda0610 --- /dev/null +++ b/src/config/locales/de-DE.json @@ -0,0 +1,46 @@ +{ + "common": { + "app-title": { + "text": "Wunschlisten" + }, + "loading": { + "text": "Lade..." + } + }, + "errors": { + "not-found": { + "text": "Ups, es sieht so aus, als ob die Seite, die du suchst, nicht existiert." + }, + "generic": { + "text": "Es ist ein Fehler aufgetreten..." + } + }, + "pages": { + "detail-view": { + "confirmation-modal": { + "title": { + "text": "Möchten Sie den Gegenstand von der Liste nehmen?" + }, + "body": { + "text": "Durch das das runternehmen von der Liste ist dieser Gegenstand nicht mehr andere sichtbar." + }, + "confirm-button": { + "text": "Ja" + }, + "cancel-button": { + "text": "Nein" + } + } + } + }, + "components": { + "wishlist-item": { + "external-product-page-link": { + "text": "Produktseite öffnen" + }, + "bought-button": { + "text": "Gekauft" + } + } + } +} diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json new file mode 100644 index 0000000..fc1f61e --- /dev/null +++ b/src/config/locales/en-US.json @@ -0,0 +1,46 @@ +{ + "common": { + "app-title": { + "text": "Wishlists" + }, + "loading": { + "text": "Loading..." + } + }, + "errors": { + "not-found": { + "text": "Oops, it looks like the page you're looking for doesn't exist." + }, + "generic": { + "text": "An error has occurred..." + } + }, + "pages": { + "detail-view": { + "confirmation-modal": { + "title": { + "text": "Do you want to remove the item from the list?" + }, + "body": { + "text": "By taking it down from the list, this item is no longer visible to others." + }, + "confirm-button": { + "text": "Yes" + }, + "cancel-button": { + "text": "No" + } + } + } + }, + "components": { + "wishlist-item": { + "external-product-page-link": { + "text": "Open product page" + }, + "bought-button": { + "text": "Bought" + } + } + } +} diff --git a/src/main.ts b/src/main.ts index a48e54d..d1be3dc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,10 +4,12 @@ import './assets/tailwind.css' import App from './App.vue' import router from './router' import Modal from '@/components/Modal.vue' +import { i18n } from '@/config' const app = createApp(App) app.use(router) +app.use(i18n) app.component('modalOverlay', Modal) app.mount('#app') diff --git a/src/views/DetailView.vue b/src/views/DetailView.vue index e047c6b..fe89828 100644 --- a/src/views/DetailView.vue +++ b/src/views/DetailView.vue @@ -1,4 +1,5 @@ - Wunschlisten + {{ t('common.app-title.text') }} - Oops, it looks like the page you're looking for doesn't exist. + {{ t('errors.not-found.text') }} +