Localization set up

This commit is contained in:
Max 2024-09-07 18:10:57 +03:00
parent 290f0babb0
commit 0467fbd5b1
7 changed files with 359 additions and 22 deletions

239
enshi/package-lock.json generated
View File

@ -9,11 +9,17 @@
"version": "0.0.0",
"dependencies": {
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-form": "^0.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@radix-ui/themes": "^3.1.3",
"@tanstack/react-query": "^5.55.0",
"i18n": "^0.15.1",
"i18next": "^23.14.0",
"i18next-browser-languagedetector": "^8.0.0",
"jotai": "^2.9.3",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-i18next": "^15.0.1"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
@ -312,6 +318,18 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.25.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.25.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
@ -1039,6 +1057,50 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@messageformat/core": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.3.0.tgz",
"integrity": "sha512-YcXd3remTDdeMxAlbvW6oV9d/01/DZ8DHUFwSttO3LMzIZj3iO0NRw+u1xlsNNORFI+u0EQzD52ZX3+Udi0T3g==",
"license": "MIT",
"dependencies": {
"@messageformat/date-skeleton": "^1.0.0",
"@messageformat/number-skeleton": "^1.0.0",
"@messageformat/parser": "^5.1.0",
"@messageformat/runtime": "^3.0.1",
"make-plural": "^7.0.0",
"safe-identifier": "^0.4.1"
}
},
"node_modules/@messageformat/date-skeleton": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz",
"integrity": "sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==",
"license": "MIT"
},
"node_modules/@messageformat/number-skeleton": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz",
"integrity": "sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==",
"license": "MIT"
},
"node_modules/@messageformat/parser": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz",
"integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==",
"license": "MIT",
"dependencies": {
"moo": "^0.5.1"
}
},
"node_modules/@messageformat/runtime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz",
"integrity": "sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==",
"license": "MIT",
"dependencies": {
"make-plural": "^7.0.0"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -3162,6 +3224,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/boolean": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -3399,7 +3467,6 @@
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@ -3837,6 +3904,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/fast-printf": {
"version": "1.6.9",
"resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz",
"integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==",
"license": "BSD-3-Clause",
"dependencies": {
"boolean": "^3.1.4"
},
"engines": {
"node": ">=10.0"
}
},
"node_modules/fastq": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@ -4089,6 +4168,67 @@
"node": ">= 0.4"
}
},
"node_modules/html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
"license": "MIT",
"dependencies": {
"void-elements": "3.1.0"
}
},
"node_modules/i18n": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/i18n/-/i18n-0.15.1.tgz",
"integrity": "sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA==",
"license": "MIT",
"dependencies": {
"@messageformat/core": "^3.0.0",
"debug": "^4.3.3",
"fast-printf": "^1.6.9",
"make-plural": "^7.0.0",
"math-interval-parser": "^2.0.1",
"mustache": "^4.2.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/mashpie"
}
},
"node_modules/i18next": {
"version": "23.14.0",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.14.0.tgz",
"integrity": "sha512-Y5GL4OdA8IU2geRrt2+Uc1iIhsjICdHZzT9tNwQ3TVqdNzgxHToGCKf/TPRP80vTCAP6svg2WbbJL+Gx5MFQVA==",
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.23.2"
}
},
"node_modules/i18next-browser-languagedetector": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.0.0.tgz",
"integrity": "sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.23.2"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -4250,6 +4390,27 @@
"jiti": "bin/jiti.js"
}
},
"node_modules/jotai": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.9.3.tgz",
"integrity": "sha512-IqMWKoXuEzWSShjd9UhalNsRGbdju5G2FrqNLQJT+Ih6p41VNYe2sav5hnwQx4HJr25jq9wRqvGSWGviGG6Gjw==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
"@types/react": ">=17.0.0",
"react": ">=17.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"react": {
"optional": true
}
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -4402,6 +4563,21 @@
"yallist": "^3.0.2"
}
},
"node_modules/make-plural": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.4.0.tgz",
"integrity": "sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg==",
"license": "Unicode-DFS-2016"
},
"node_modules/math-interval-parser": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz",
"integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -4449,13 +4625,27 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
"license": "BSD-3-Clause"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/mustache": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
"license": "MIT",
"bin": {
"mustache": "bin/mustache"
}
},
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@ -4931,6 +5121,28 @@
"react": "^18.3.1"
}
},
"node_modules/react-i18next": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.0.1.tgz",
"integrity": "sha512-NwxLqNM6CLbeGA9xPsjits0EnXdKgCRSS6cgkgOdNcPXqL+1fYNl8fBg1wmnnHvFy812Bt4IWTPE9zjoPmFj3w==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.24.8",
"html-parse-stringify": "^3.0.1"
},
"peerDependencies": {
"i18next": ">= 23.2.3",
"react": ">= 16.8.0"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@ -5034,6 +5246,12 @@
"node": ">=8.10.0"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT"
},
"node_modules/resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@ -5133,6 +5351,12 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/safe-identifier": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz",
"integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==",
"license": "ISC"
},
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@ -5676,6 +5900,15 @@
}
}
},
"node_modules/void-elements": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@ -11,11 +11,17 @@
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-form": "^0.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"@radix-ui/themes": "^3.1.3",
"@tanstack/react-query": "^5.55.0",
"i18n": "^0.15.1",
"i18next": "^23.14.0",
"i18next-browser-languagedetector": "^8.0.0",
"jotai": "^2.9.3",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-i18next": "^15.0.1"
},
"devDependencies": {
"@eslint/js": "^9.9.0",

View File

@ -5,44 +5,100 @@ import {
Box,
Button,
Callout,
Card,
Container,
Flex,
Text,
TextArea,
TextField,
Theme,
ThemePanel,
} from "@radix-ui/themes";
import React, { PropsWithChildren } from "react";
import * as Tooltip from "@radix-ui/react-tooltip";
const MyButton = React.forwardRef<HTMLButtonElement, PropsWithChildren>((props, forwardedRef) => (
<Button {...props} ref={forwardedRef} />
));
import { useTranslation } from "react-i18next";
import * as Form from "@radix-ui/react-form";
function App() {
const { t } = useTranslation();
return (
<Theme className="h-fit" accentColor="indigo" grayColor="slate">
<Flex className="w-full">
<Callout.Root>
<Text>Hello world!</Text>
<Button highContrast variant="soft">
Let's goooo!
{t("hello")}
</Button>
<Button variant="soft">Let's goooo!</Button>
<Badge color="red">What</Badge>
</Callout.Root>
</Flex>
<Tooltip.Root>
<Tooltip.Trigger>
<MyButton>Open dialog</MyButton>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content>
apsofjiosfwiuhfwei
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
<Container>
<Card className="w-full absolute top-10">
<Form.Root className="FormRoot">
<Form.Field className="FormField" name="email">
<div
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
}}
>
<Form.Label className="FormLabel">
Email
</Form.Label>
<Form.Message
className="FormMessage"
match="valueMissing"
>
Please enter your email
</Form.Message>
<Form.Message
className="FormMessage"
match="typeMismatch"
>
Please provide a valid email
</Form.Message>
</div>
<Form.Control asChild>
<TextField.Root
className="Input"
type="email"
required
/>
</Form.Control>
</Form.Field>
<Form.Field className="FormField" name="question">
<div
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
}}
>
<Form.Label className="FormLabel">
Question
</Form.Label>
<Form.Message
className="FormMessage"
match="valueMissing"
>
Please enter a question
</Form.Message>
</div>
<Form.Control asChild>
<TextArea className="Textarea" required />
</Form.Control>
</Form.Field>
<Form.Submit asChild>
<Button variant="soft" style={{ marginTop: 10 }}>
Post question
</Button>
</Form.Submit>
</Form.Root>
</Card>
</Container>
<ThemePanel />
</Theme>

5
enshi/src/locale/en.ts Normal file
View File

@ -0,0 +1,5 @@
const en = {
hello: "hello!"
}
export default en;

28
enshi/src/locale/i18n.ts Normal file
View File

@ -0,0 +1,28 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import ru from "./ru";
import en from "./en";
i18n
.use(initReactI18next)
.init({
fallbackLng: "ru",
lng: "ru",
debug: true,
resources: {
ru: {
translation: {...ru},
},
en: {
translation: {...en},
},
},
interpolation: {
escapeValue: false,
},
});
export default i18n;

5
enshi/src/locale/ru.ts Normal file
View File

@ -0,0 +1,5 @@
const ru = {
hello: "Привет!"
}
export default ru;

View File

@ -1,6 +1,10 @@
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import './locale/i18n.ts'
import i18n from './locale/i18n.ts'
i18n.changeLanguage(navigator.language)
createRoot(document.getElementById('root')!).render(
<App />