feat: setup i18next
All checks were successful
Deploy to VPS (dist) / deploy (push) Successful in 1m27s
All checks were successful
Deploy to VPS (dist) / deploy (push) Successful in 1m27s
This commit is contained in:
0
.env.production
Normal file
0
.env.production
Normal file
0
.env.staging
Normal file
0
.env.staging
Normal file
@@ -65,7 +65,7 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
run: pnpm run build:staging
|
||||
|
||||
- name: Deploy dist to VPS dist
|
||||
run: |
|
||||
|
||||
21
i18next.config.ts
Normal file
21
i18next.config.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { readdirSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
export const LOCALES_PATH = join("locales");
|
||||
export const LOCAL_LOCALES_PATH = join("public", LOCALES_PATH);
|
||||
export const DEFAULT_LANGUAGE = "en";
|
||||
|
||||
export default {
|
||||
locales: readdirSync(LOCAL_LOCALES_PATH)
|
||||
.filter((file) => file.endsWith(".json"))
|
||||
.map((locale) => locale.replace(".json", "")),
|
||||
defaultLocale: DEFAULT_LANGUAGE,
|
||||
fallbackLng: DEFAULT_LANGUAGE,
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
extract: {
|
||||
input: "src/**/*.{js,jsx,ts,tsx}",
|
||||
output: join(LOCAL_LOCALES_PATH, "{{language}}.json"),
|
||||
},
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"build:staging": "tsc -b && vite build --mode staging",
|
||||
"lint": "oxlint",
|
||||
"lint:fix": "oxlint --fix",
|
||||
"fmt": "oxfmt",
|
||||
@@ -21,12 +22,16 @@
|
||||
"@tanstack/react-router": "^1.166.3",
|
||||
"@tanstack/react-router-devtools": "^1.166.3",
|
||||
"arktype": "^2.2.0",
|
||||
"i18next": "^25.8.17",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"motion": "^12.35.1",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-i18next": "^16.5.6",
|
||||
"tailwindcss": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@i18next-selector/vite-plugin": "^0.0.18",
|
||||
"@tanstack/router-plugin": "^1.166.3",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/react": "^19.2.7",
|
||||
|
||||
166
pnpm-lock.yaml
generated
166
pnpm-lock.yaml
generated
@@ -32,6 +32,12 @@ importers:
|
||||
arktype:
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
i18next:
|
||||
specifier: ^25.8.17
|
||||
version: 25.8.17(typescript@5.9.3)
|
||||
i18next-http-backend:
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2
|
||||
motion:
|
||||
specifier: ^12.35.1
|
||||
version: 12.35.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -41,10 +47,16 @@ importers:
|
||||
react-dom:
|
||||
specifier: ^19.2.4
|
||||
version: 19.2.4(react@19.2.4)
|
||||
react-i18next:
|
||||
specifier: ^16.5.6
|
||||
version: 16.5.6(i18next@25.8.17(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||
tailwindcss:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1
|
||||
devDependencies:
|
||||
'@i18next-selector/vite-plugin':
|
||||
specifier: ^0.0.18
|
||||
version: 0.0.18(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))
|
||||
'@tanstack/router-plugin':
|
||||
specifier: ^1.166.3
|
||||
version: 1.166.3(@tanstack/react-router@1.166.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))
|
||||
@@ -161,6 +173,10 @@ packages:
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/runtime@7.28.6':
|
||||
resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/template@7.28.6':
|
||||
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -332,6 +348,11 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@i18next-selector/vite-plugin@0.0.18':
|
||||
resolution: {integrity: sha512-jCdVJdaYDqa3dE8LCscCa7OzCN7UvUP+FDgSuc3L3mkPRlmhiXwNpCSWNUpSgGfubGBcpIE9vexHaLcS2KU0Bg==}
|
||||
peerDependencies:
|
||||
vite: 6 - 7
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
@@ -1159,6 +1180,18 @@ packages:
|
||||
resolution: {integrity: sha512-42WoRePf8v690qG8yGRe/YOh+oHni9vUaUUfoqlS91U2scd3a5rkLtVsc6b7z60w3RogH0I00vdrC5AaeiZ18w==}
|
||||
engines: {node: '>=20.19'}
|
||||
|
||||
'@traversable/json@0.0.26':
|
||||
resolution: {integrity: sha512-oXKX0eNxbbHGLjLV27nTuV0uyR6uSoWi0BF+FYJu4jXRcSsWqCHOqNjIb2x/0usKd70rnKLGyHxurlTBTpQVOw==}
|
||||
peerDependencies:
|
||||
'@traversable/registry': ^0.0.25
|
||||
fast-check: ^3
|
||||
peerDependenciesMeta:
|
||||
fast-check:
|
||||
optional: true
|
||||
|
||||
'@traversable/registry@0.0.25':
|
||||
resolution: {integrity: sha512-idu2DhzoHOeqO+FZSpnDTgrFQWZL+poyxO9KozHeW7KdVqecrtYwR10vCVB/dItVdMBVZbavbNWO6PgUYN1KLg==}
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
@@ -1204,6 +1237,9 @@ packages:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
arkregex@0.0.5:
|
||||
resolution: {integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==}
|
||||
|
||||
@@ -1278,6 +1314,9 @@ packages:
|
||||
cookie-es@2.0.0:
|
||||
resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==}
|
||||
|
||||
cross-fetch@4.0.0:
|
||||
resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
@@ -1395,11 +1434,25 @@ packages:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
html-parse-stringify@3.0.1:
|
||||
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
|
||||
|
||||
husky@9.1.7:
|
||||
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
i18next-http-backend@3.0.2:
|
||||
resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==}
|
||||
|
||||
i18next@25.8.17:
|
||||
resolution: {integrity: sha512-vWtCttyn5bpOK4hWbRAe1ZXkA+Yzcn2OcACT+WJavtfGMcxzkfvXTLMeOU8MUhRmAySKjU4VVuKlo0sSGeBokA==}
|
||||
peerDependencies:
|
||||
typescript: ^5
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
immutable@5.1.5:
|
||||
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
|
||||
|
||||
@@ -1434,6 +1487,10 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
||||
hasBin: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -1579,6 +1636,15 @@ packages:
|
||||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
node-releases@2.0.36:
|
||||
resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==}
|
||||
|
||||
@@ -1633,6 +1699,22 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^19.2.4
|
||||
|
||||
react-i18next@16.5.6:
|
||||
resolution: {integrity: sha512-Ua7V2/efA88ido7KyK51fb8Ki8M/sRfW8LR/rZ/9ZKr2luhuTI7kwYZN5agT1rWG7aYm5G0RYE/6JR8KJoCMDw==}
|
||||
peerDependencies:
|
||||
i18next: '>= 25.6.2'
|
||||
react: '>= 16.8.0'
|
||||
react-dom: '*'
|
||||
react-native: '*'
|
||||
typescript: ^5
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
react@19.2.4:
|
||||
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1894,6 +1976,9 @@ packages:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
@@ -1968,9 +2053,19 @@ packages:
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
void-elements@3.1.0:
|
||||
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
webpack-virtual-modules@0.6.2:
|
||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
|
||||
wrap-ansi@9.0.2:
|
||||
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -2095,6 +2190,8 @@ snapshots:
|
||||
'@babel/core': 7.29.0
|
||||
'@babel/helper-plugin-utils': 7.28.6
|
||||
|
||||
'@babel/runtime@7.28.6': {}
|
||||
|
||||
'@babel/template@7.28.6':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.29.0
|
||||
@@ -2199,6 +2296,15 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@i18next-selector/vite-plugin@0.0.18(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@traversable/json': 0.0.26(@traversable/registry@0.0.25)
|
||||
'@traversable/registry': 0.0.25
|
||||
js-yaml: 4.1.1
|
||||
vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- fast-check
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -2810,6 +2916,12 @@ snapshots:
|
||||
|
||||
'@tanstack/virtual-file-routes@1.161.4': {}
|
||||
|
||||
'@traversable/json@0.0.26(@traversable/registry@0.0.25)':
|
||||
dependencies:
|
||||
'@traversable/registry': 0.0.25
|
||||
|
||||
'@traversable/registry@0.0.25': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/node@24.12.0':
|
||||
@@ -2849,6 +2961,8 @@ snapshots:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
arkregex@0.0.5:
|
||||
dependencies:
|
||||
'@ark/util': 0.56.0
|
||||
@@ -2931,6 +3045,12 @@ snapshots:
|
||||
|
||||
cookie-es@2.0.0: {}
|
||||
|
||||
cross-fetch@4.0.0:
|
||||
dependencies:
|
||||
node-fetch: 2.7.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
dayjs@1.11.19: {}
|
||||
@@ -3032,8 +3152,24 @@ snapshots:
|
||||
has-flag@4.0.0:
|
||||
optional: true
|
||||
|
||||
html-parse-stringify@3.0.1:
|
||||
dependencies:
|
||||
void-elements: 3.1.0
|
||||
|
||||
husky@9.1.7: {}
|
||||
|
||||
i18next-http-backend@3.0.2:
|
||||
dependencies:
|
||||
cross-fetch: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
i18next@25.8.17(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
immutable@5.1.5:
|
||||
optional: true
|
||||
|
||||
@@ -3059,6 +3195,10 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
@@ -3179,6 +3319,10 @@ snapshots:
|
||||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
node-releases@2.0.36: {}
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
@@ -3254,6 +3398,17 @@ snapshots:
|
||||
react: 19.2.4
|
||||
scheduler: 0.27.0
|
||||
|
||||
react-i18next@16.5.6(i18next@25.8.17(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
html-parse-stringify: 3.0.1
|
||||
i18next: 25.8.17(typescript@5.9.3)
|
||||
react: 19.2.4
|
||||
use-sync-external-store: 1.6.0(react@19.2.4)
|
||||
optionalDependencies:
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
typescript: 5.9.3
|
||||
|
||||
react@19.2.4: {}
|
||||
|
||||
readdirp@3.6.0:
|
||||
@@ -3500,6 +3655,8 @@ snapshots:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tsx@4.21.0:
|
||||
@@ -3551,8 +3708,17 @@ snapshots:
|
||||
tsx: 4.21.0
|
||||
yaml: 2.8.2
|
||||
|
||||
void-elements@3.1.0: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
webpack-virtual-modules@0.6.2: {}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
|
||||
wrap-ansi@9.0.2:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.3
|
||||
|
||||
1
public/locales/en.d.ts
vendored
Normal file
1
public/locales/en.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const resources: { hello: "Hello World!" };
|
||||
3
public/locales/en.json
Normal file
3
public/locales/en.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"hello": "Hello World!"
|
||||
}
|
||||
1
public/locales/ru.d.ts
vendored
Normal file
1
public/locales/ru.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const resources: { hello: "Привет мир" };
|
||||
3
public/locales/ru.json
Normal file
3
public/locales/ru.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"hello": "Привет мир"
|
||||
}
|
||||
9
src/i18next.d.ts
vendored
Normal file
9
src/i18next.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import "i18next";
|
||||
|
||||
import type { resources } from "../public/locales/en.d.ts";
|
||||
|
||||
declare module "i18next" {
|
||||
interface CustomTypeOptions {
|
||||
resources: { translation: typeof resources };
|
||||
}
|
||||
}
|
||||
27
src/i18next.ts
Normal file
27
src/i18next.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import i18next from "i18next";
|
||||
import Backend, { type HttpBackendOptions } from "i18next-http-backend";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
declare const __LANGS__: string[];
|
||||
declare const __LOCALES_PATH__: string;
|
||||
declare const __DEFAULT_LANG__: string;
|
||||
|
||||
export const languages = __LANGS__.map((key) => ({
|
||||
key,
|
||||
label: key.toUpperCase(),
|
||||
}));
|
||||
|
||||
i18next
|
||||
.use(Backend)
|
||||
.use(initReactI18next)
|
||||
.init<HttpBackendOptions>({
|
||||
backend: {
|
||||
loadPath: `/${__LOCALES_PATH__}/{{lng}}.json`,
|
||||
},
|
||||
fallbackLng: __DEFAULT_LANG__,
|
||||
supportedLngs: __LANGS__,
|
||||
debug: import.meta.env.DEV,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
@@ -1,10 +1,11 @@
|
||||
import { StrictMode } from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
import "./index.css";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import "./i18next";
|
||||
|
||||
const router = createRouter({ routeTree });
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ export const Route = createRootRoute({
|
||||
<hr />
|
||||
<Outlet />
|
||||
<TanStackDevtools
|
||||
config={{
|
||||
defaultOpen: false,
|
||||
panelLocation: "bottom",
|
||||
theme: "dark",
|
||||
position: "middle-left",
|
||||
}}
|
||||
plugins={[
|
||||
{
|
||||
name: "TanStack Query",
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useEffect, useState } from "react";
|
||||
import { languages } from "../i18next";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const [langIdx, setLangIdx] = useState(0);
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(languages[langIdx].key);
|
||||
}, [langIdx, i18n]);
|
||||
|
||||
return (
|
||||
<div className="p-2">
|
||||
<h3>Welcome Home!</h3>
|
||||
<h3>
|
||||
{t("hello")}
|
||||
<button onClick={() => setLangIdx((langIdx + 1) % languages.length)}>
|
||||
{i18n.language}
|
||||
</button>
|
||||
</h3>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
11
src/vite-env.d.ts
vendored
Normal file
11
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
interface ViteTypeOptions {
|
||||
strictImportMetaEnv: unknown;
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
MODE: "development" | "production" | "staging";
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
||||
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }],
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,18 @@ import react from "@vitejs/plugin-react-swc";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import tanstackRouter from "@tanstack/router-plugin/vite";
|
||||
import { devtools as tanstackDevtools } from "@tanstack/devtools-vite";
|
||||
import { i18nextVitePlugin } from "@i18next-selector/vite-plugin";
|
||||
import i18nextConfig, {
|
||||
DEFAULT_LANGUAGE,
|
||||
LOCALES_PATH,
|
||||
LOCAL_LOCALES_PATH,
|
||||
} from "./i18next.config";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
i18nextVitePlugin({
|
||||
sourceDir: LOCAL_LOCALES_PATH,
|
||||
}),
|
||||
tanstackDevtools({
|
||||
removeDevtoolsOnBuild: true,
|
||||
logging: true,
|
||||
@@ -32,4 +41,9 @@ export default defineConfig({
|
||||
react(),
|
||||
tailwindcss(),
|
||||
],
|
||||
define: {
|
||||
__LANGS__: JSON.stringify(i18nextConfig.locales),
|
||||
__LOCALES_PATH__: JSON.stringify(LOCALES_PATH),
|
||||
__DEFAULT_LANG__: JSON.stringify(DEFAULT_LANGUAGE),
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user