diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json index 744e14d2..aaba2b3b 100644 --- a/.eslintrc-auto-import.json +++ b/.eslintrc-auto-import.json @@ -173,6 +173,7 @@ "useFullscreen": true, "useGamepad": true, "useGeolocation": true, + "useI18n": true, "useIdle": true, "useImage": true, "useInfiniteScroll": true, diff --git a/auto-imports.d.ts b/auto-imports.d.ts index 9dccb44a..9a69b9d2 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -170,6 +170,7 @@ declare global { const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] const useGamepad: typeof import('@vueuse/core')['useGamepad'] const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] + const useI18n: typeof import('vue-i18n')['useI18n'] const useIdle: typeof import('@vueuse/core')['useIdle'] const useImage: typeof import('@vueuse/core')['useImage'] const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] @@ -459,6 +460,7 @@ declare module 'vue' { readonly useFullscreen: UnwrapRef readonly useGamepad: UnwrapRef readonly useGeolocation: UnwrapRef + readonly useI18n: UnwrapRef readonly useIdle: UnwrapRef readonly useImage: UnwrapRef readonly useInfiniteScroll: UnwrapRef diff --git a/locales/en.yml b/locales/en.yml new file mode 100644 index 00000000..4982dc53 --- /dev/null +++ b/locales/en.yml @@ -0,0 +1,3 @@ +home: + categories: + newestTools: "Newest tools" diff --git a/locales/fr.yml b/locales/fr.yml new file mode 100644 index 00000000..2846356d --- /dev/null +++ b/locales/fr.yml @@ -0,0 +1,3 @@ +home: + categories: + newestTools: "Nouveaux outils" diff --git a/package.json b/package.json index 277da558..268d0152 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "ua-parser-js": "^1.0.35", "uuid": "^8.3.2", "vue": "^3.2.47", + "vue-i18n": "^9.2.2", "vue-router": "^4.1.6", "xml-formatter": "^3.3.2", "yaml": "^2.2.1" @@ -82,6 +83,7 @@ "devDependencies": { "@antfu/eslint-config": "^0.39.3", "@iconify-json/mdi": "^1.1.50", + "@intlify/unplugin-vue-i18n": "^0.11.0", "@playwright/test": "^1.32.3", "@rushstack/eslint-patch": "^1.2.0", "@types/bcryptjs": "^2.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99488feb..d0149e08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,8 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: '@it-tools/bip39': @@ -127,6 +131,9 @@ dependencies: vue: specifier: ^3.2.47 version: 3.2.47 + vue-i18n: + specifier: ^9.2.2 + version: 9.2.2(vue@3.2.47) vue-router: specifier: ^4.1.6 version: 4.1.6(vue@3.2.47) @@ -144,6 +151,9 @@ devDependencies: '@iconify-json/mdi': specifier: ^1.1.50 version: 1.1.50 + '@intlify/unplugin-vue-i18n': + specifier: ^0.11.0 + version: 0.11.0(rollup@2.79.1)(vue-i18n@9.2.2) '@playwright/test': specifier: ^1.32.3 version: 1.32.3 @@ -1731,6 +1741,110 @@ packages: - supports-color dev: true + /@intlify/bundle-utils@6.0.1(vue-i18n@9.2.2): + resolution: {integrity: sha512-BkeZNKZiC0B7K3OYMwiPLoAqsZmKH3SxTL75vYAkuQ//XWR8WO0NpfjXhTxgLTVFHxMcNb2agAopC0DP6fqDrg==} + engines: {node: '>= 14.16'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + dependencies: + '@intlify/message-compiler': 9.3.0-beta.17 + '@intlify/shared': 9.3.0-beta.17 + acorn: 8.8.2 + escodegen: 2.0.0 + estree-walker: 2.0.2 + jsonc-eslint-parser: 1.4.1 + magic-string: 0.30.0 + mlly: 1.2.0 + source-map: 0.6.1 + vue-i18n: 9.2.2(vue@3.2.47) + yaml-eslint-parser: 0.3.2 + dev: true + + /@intlify/core-base@9.2.2: + resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==} + engines: {node: '>= 14'} + dependencies: + '@intlify/devtools-if': 9.2.2 + '@intlify/message-compiler': 9.2.2 + '@intlify/shared': 9.2.2 + '@intlify/vue-devtools': 9.2.2 + + /@intlify/devtools-if@9.2.2: + resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.2.2 + + /@intlify/message-compiler@9.2.2: + resolution: {integrity: sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.2.2 + source-map: 0.6.1 + + /@intlify/message-compiler@9.3.0-beta.17: + resolution: {integrity: sha512-i7hvVIRk1Ax2uKa9xLRJCT57to08OhFMhFXXjWN07rmx5pWQYQ23MfX1xgggv9drnWTNhqEiD+u4EJeHoS5+Ww==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.3.0-beta.17 + source-map: 0.6.1 + dev: true + + /@intlify/shared@9.2.2: + resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==} + engines: {node: '>= 14'} + + /@intlify/shared@9.3.0-beta.17: + resolution: {integrity: sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ==} + engines: {node: '>= 14'} + dev: true + + /@intlify/unplugin-vue-i18n@0.11.0(rollup@2.79.1)(vue-i18n@9.2.2): + resolution: {integrity: sha512-ivcLZo08fvepHWV8o5lcKfhcKFSWqhwrqIAU6pUIbvq2ICo9fnXnIPYIZj7FeuHDLW1G3ADm44ZhQC3nYmvDlg==} + engines: {node: '>= 14.16'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + vue-i18n-bridge: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + vue-i18n-bridge: + optional: true + dependencies: + '@intlify/bundle-utils': 6.0.1(vue-i18n@9.2.2) + '@intlify/shared': 9.3.0-beta.17 + '@rollup/pluginutils': 5.0.2(rollup@2.79.1) + '@vue/compiler-sfc': 3.2.47 + debug: 4.3.4 + fast-glob: 3.2.12 + js-yaml: 4.1.0 + json5: 2.2.3 + pathe: 1.1.0 + picocolors: 1.0.0 + source-map: 0.6.1 + unplugin: 1.3.1 + vue-i18n: 9.2.2(vue@3.2.47) + transitivePeerDependencies: + - rollup + - supports-color + dev: true + + /@intlify/vue-devtools@9.2.2: + resolution: {integrity: sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==} + engines: {node: '>= 14'} + dependencies: + '@intlify/core-base': 9.2.2 + '@intlify/shared': 9.2.2 + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -3032,7 +3146,6 @@ packages: /@vue/devtools-api@6.5.0: resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} - dev: false /@vue/reactivity-transform@3.2.47: resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} @@ -3182,6 +3295,14 @@ packages: acorn-walk: 7.2.0 dev: true + /acorn-jsx@5.3.2(acorn@7.4.1): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 7.4.1 + dev: true + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -4777,6 +4898,18 @@ packages: estraverse: 5.3.0 dev: true + /eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + /eslint-visitor-keys@3.4.0: resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4831,6 +4964,15 @@ packages: - supports-color dev: true + /espree@6.2.1: + resolution: {integrity: sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==} + engines: {node: '>=6.0.0'} + dependencies: + acorn: 7.4.1 + acorn-jsx: 5.3.2(acorn@7.4.1) + eslint-visitor-keys: 1.3.0 + dev: true + /espree@9.5.1: resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5951,6 +6093,17 @@ packages: engines: {node: '>=6'} hasBin: true + /jsonc-eslint-parser@1.4.1: + resolution: {integrity: sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==} + engines: {node: '>=8.10.0'} + dependencies: + acorn: 7.4.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 1.3.0 + espree: 6.2.1 + semver: 6.3.0 + dev: true + /jsonc-eslint-parser@2.3.0: resolution: {integrity: sha512-9xZPKVYp9DxnM3sd1yAsh/d59iIaswDkai8oTxbursfKYbg/ibjX0IzFt35+VZ8iEW453TVTXztnRvYUQlAfUQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -8498,6 +8651,18 @@ packages: - supports-color dev: true + /vue-i18n@9.2.2(vue@3.2.47): + resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==} + engines: {node: '>= 14'} + peerDependencies: + vue: ^3.0.0 + dependencies: + '@intlify/core-base': 9.2.2 + '@intlify/shared': 9.2.2 + '@intlify/vue-devtools': 9.2.2 + '@vue/devtools-api': 6.5.0 + vue: 3.2.47 + /vue-router@4.1.6(vue@3.2.47): resolution: {integrity: sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==} peerDependencies: @@ -8928,6 +9093,14 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yaml-eslint-parser@0.3.2: + resolution: {integrity: sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==} + dependencies: + eslint-visitor-keys: 1.3.0 + lodash: 4.17.21 + yaml: 1.10.2 + dev: true + /yaml-eslint-parser@1.2.2: resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==} engines: {node: ^14.17.0 || >=16.0.0} @@ -8937,6 +9110,11 @@ packages: yaml: 2.2.1 dev: true + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + /yaml@2.2.1: resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} engines: {node: '>= 14'} diff --git a/src/main.ts b/src/main.ts index e23cb912..36ba3b7f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,7 @@ import { naive } from './plugins/naive.plugin'; import App from './App.vue'; import router from './router'; +import { i18nPlugin } from './plugins/i18n.plugin'; registerSW(); @@ -18,6 +19,7 @@ const app = createApp(App); app.use(createPinia()); app.use(createHead()); +app.use(i18nPlugin); app.use(router); app.use(naive); app.use(plausible); diff --git a/src/pages/Home.page.vue b/src/pages/Home.page.vue index 01a7296f..5c7c3c4f 100644 --- a/src/pages/Home.page.vue +++ b/src/pages/Home.page.vue @@ -9,6 +9,7 @@ import { config } from '@/config'; const toolStore = useToolStore(); useHead({ title: 'IT Tools - Handy online tools for developers' }); +const { t } = useI18n();