diff --git a/.eslintrc.json b/.eslintrc.json
index f620c98..c58c1d5 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -50,10 +50,6 @@
"error",
"unix"
],
- "quotes": [
- "error",
- "single"
- ],
"semi": [
"error",
"never"
diff --git a/README.md b/README.md
index 12bb0c1..767b5c5 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
[![vike][badge-vike-img]][badge-vike-href]
[![vuetify][badge-vuetify-img]][badge-vuetify-href]
[![pinia][badge-pinia-img]][badge-pinia-href]
+[![vue-i18n][badge-vue-i18n-img]][badge-vue-i18n-href]
[![eslint][badge-eslint-img]][badge-eslint-href]
[![vitest][badge-vitest-img]][badge-vitest-href]
[![storybook][badge-storybook-img]][badge-storybook-href]
@@ -48,9 +49,9 @@ The following commands are available:
- [x] storybook
- [x] eslint
- [x] vitest
+- [x] vue-i18n
- [ ] figma
- [ ] chromatic
-- [ ] localization?
- [ ] documentation?
- [ ] docker
- [ ] github actions
@@ -87,6 +88,9 @@ See [vite-plugin-ssr-vuetify](https://github.com/brillout/vite-plugin-ssr-vuetif
[badge-pinia-img]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FIT4Change%2Fboilerplate-frontend%2Fmaster%2Fpackage.json&query=dependencies.pinia&label=pinia&color=green
[badge-pinia-href]: https://pinia.vuejs.org/
+[badge-vue-i18n-img]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FIT4Change%2Fboilerplate-frontend%2Fmaster%2Fpackage.json&query=dependencies.vue-i18n&label=vue-i18n&color=green
+[badge-vue-i18n-href]: https://vue-i18n.intlify.dev/
+
[badge-eslint-img]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FIT4Change%2Fboilerplate-frontend%2Fmaster%2Fpackage.json&query=devDependencies.eslint&label=eslint&color=yellow
[badge-eslint-href]: https://eslint.org/
diff --git a/package-lock.json b/package-lock.json
index 097618a..7332889 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,6 +28,7 @@
"vike": "^0.4.145",
"vite": "^4.4.9",
"vue": "^3.3.4",
+ "vue-i18n": "^9.7.0",
"vuetify": "^3.4.0"
},
"devDependencies": {
@@ -2946,6 +2947,47 @@
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
},
+ "node_modules/@intlify/core-base": {
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.7.0.tgz",
+ "integrity": "sha512-1tBnfnCI23jXqGW15cagCjn2GgD487VST1dMG8P5LRzrSfx+kUzqFyTrjMNIwgq1tVaF4HnDpFMUuyrzTLKphw==",
+ "dependencies": {
+ "@intlify/message-compiler": "9.7.0",
+ "@intlify/shared": "9.7.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/message-compiler": {
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.7.0.tgz",
+ "integrity": "sha512-/YdZCio2L2tCM5bZ2eMHbSEIQNPh1QqvZIOLI/yCVKXLscis7O0SsR2nmuU/DfCJ3iSeI8juw82C2wLvfsAeww==",
+ "dependencies": {
+ "@intlify/shared": "9.7.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/shared": {
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.7.0.tgz",
+ "integrity": "sha512-PUkEuk//YKu4CHS5ah3mNa3XL/+TZj6rAY/6yYN+GCNFd2u+uWUkeuwE4Q6t8dydRWlErOePHHS0KyNoof/oBw==",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -18177,6 +18219,25 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
+ "node_modules/vue-i18n": {
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.7.0.tgz",
+ "integrity": "sha512-8Z8kSz9U2juzuAf+6mjW1HTd5pIlYuFJZkC+HvYOglFdpzwc2rTUGjxKwN8xGdtGur1MFnyJ44TSr+TksJtY8A==",
+ "dependencies": {
+ "@intlify/core-base": "9.7.0",
+ "@intlify/shared": "9.7.0",
+ "@vue/devtools-api": "^6.5.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
"node_modules/vue-inbrowser-compiler-independent-utils": {
"version": "4.71.1",
"resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-independent-utils/-/vue-inbrowser-compiler-independent-utils-4.71.1.tgz",
diff --git a/package.json b/package.json
index 954ffa9..1050820 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,8 @@
"pinia",
"storybook",
"eslint",
- "vitest"
+ "vitest",
+ "vue-i18n"
],
"author": "Ulf Gebhardt",
"license": "Apache-2.0",
@@ -60,6 +61,7 @@
"vike": "^0.4.145",
"vite": "^4.4.9",
"vue": "^3.3.4",
+ "vue-i18n": "^9.7.0",
"vuetify": "^3.4.0"
},
"devDependencies": {
diff --git a/renderer/app.ts b/renderer/app.ts
index cb5eb43..70385e3 100644
--- a/renderer/app.ts
+++ b/renderer/app.ts
@@ -2,7 +2,8 @@ import { createSSRApp, defineComponent, h } from 'vue'
import PageShell from './PageShell.vue'
import { setPageContext } from './usePageContext'
import type { Component, PageContext, PageProps } from './types'
-import vuetify from './vuetify'
+import i18n from './i18n'
+import CreateVuetify from './vuetify'
import { createPinia } from 'pinia'
export { createApp }
@@ -25,7 +26,8 @@ function createApp(Page: Component, pageProps: PageProps | undefined, pageContex
const pinia = createPinia()
const app = createSSRApp(PageWithLayout)
app.use(pinia)
- app.use(vuetify)
+ app.use(i18n)
+ app.use(CreateVuetify(i18n))
// Make pageContext available from any Vue component
setPageContext(app, pageContext)
diff --git a/renderer/i18n.ts b/renderer/i18n.ts
new file mode 100644
index 0000000..6093b29
--- /dev/null
+++ b/renderer/i18n.ts
@@ -0,0 +1,10 @@
+import { createI18n } from 'vue-i18n'
+import de from '../src/locales/de'
+import en from '../src/locales/en'
+
+export default createI18n({
+ legacy: false, // Vuetify does not support the legacy mode of vue-i18n
+ locale: 'de',
+ fallbackLocale: 'en',
+ messages: { de, en },
+})
diff --git a/renderer/vuetify.ts b/renderer/vuetify.ts
index 2640009..ddb4b0b 100644
--- a/renderer/vuetify.ts
+++ b/renderer/vuetify.ts
@@ -4,7 +4,16 @@ import { createVuetify } from 'vuetify'
import * as components from 'vuetify/lib/components/index.mjs'
import * as directives from 'vuetify/lib/directives/index.mjs'
-export default createVuetify({
- components,
- directives,
-})
+import { createVueI18nAdapter } from 'vuetify/locale/adapters/vue-i18n'
+
+import { useI18n } from 'vue-i18n'
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export default (i18n: any) =>
+ createVuetify({
+ locale: {
+ adapter: createVueI18nAdapter({ i18n, useI18n }),
+ },
+ components,
+ directives,
+ })
diff --git a/src/locales/de.ts b/src/locales/de.ts
new file mode 100644
index 0000000..46135f0
--- /dev/null
+++ b/src/locales/de.ts
@@ -0,0 +1,6 @@
+import { de as $vuetify } from 'vuetify/locale'
+
+export default {
+ counter: 'Zähler',
+ $vuetify,
+}
diff --git a/src/locales/en.ts b/src/locales/en.ts
new file mode 100644
index 0000000..69dab4c
--- /dev/null
+++ b/src/locales/en.ts
@@ -0,0 +1,6 @@
+import { en as $vuetify } from 'vuetify/locale'
+
+export default {
+ counter: 'Counter',
+ $vuetify,
+}
diff --git a/src/pages/index/ClickCounter.spec.ts b/src/pages/index/ClickCounter.spec.ts
index 8eaa13a..648bb9c 100644
--- a/src/pages/index/ClickCounter.spec.ts
+++ b/src/pages/index/ClickCounter.spec.ts
@@ -7,6 +7,6 @@ describe('ClickCounter', () => {
it('renders Button with a Counter of 0', () => {
expect(wrapper.find('v-btn').exists()).toBe(true)
- expect(wrapper.text()).toBe('Counter 0')
+ expect(wrapper.text()).toBe("$t('counter') 0")
})
})
diff --git a/src/pages/index/ClickCounter.vue b/src/pages/index/ClickCounter.vue
index 458a3f9..63a3ac2 100644
--- a/src/pages/index/ClickCounter.vue
+++ b/src/pages/index/ClickCounter.vue
@@ -1,5 +1,5 @@
- Counter {{ state.count }}
+ {{ $t('counter') }} {{ state.count }}