From e9eba366f5e15fb8eae0a679938c2567f6c43448 Mon Sep 17 00:00:00 2001 From: Anton Akhatov Date: Sun, 2 Apr 2023 21:59:25 +0100 Subject: [PATCH] feature: add i18n --- next.config.js | 4 ++ package.json | 1 + pnpm-lock.yaml | 86 +++++++++++++++++++++++++++++++++ src/components/header/Header.js | 18 +++---- src/locales/en.json | 7 +++ src/locales/ru.json | 7 +++ src/pages/_app.tsx | 9 +++- src/pages/index.tsx | 14 +++++- 8 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 src/locales/en.json create mode 100644 src/locales/ru.json diff --git a/next.config.js b/next.config.js index 50f8420..d6be20a 100644 --- a/next.config.js +++ b/next.config.js @@ -4,6 +4,10 @@ const nextConfig = { images: { unoptimized: true, }, + i18n: { + locales: ["ru", "en"], + defaultLocale: "ru", + }, }; module.exports = nextConfig; diff --git a/package.json b/package.json index 2db5031..8c63ca0 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "eslint": "8.36.0", "eslint-config-next": "13.2.4", "next": "13.2.4", + "next-intl": "^2.12.0", "postcss": "^8.4.21", "postcss-nesting": "^11.2.1", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9587057..7078666 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,7 @@ specifiers: eslint: 8.36.0 eslint-config-next: 13.2.4 next: 13.2.4 + next-intl: ^2.12.0 postcss: ^8.4.21 postcss-nesting: ^11.2.1 react: ^18.2.0 @@ -26,6 +27,7 @@ dependencies: eslint: 8.36.0 eslint-config-next: 13.2.4_j4766f7ecgqbon3u7zlxn5zszu next: 13.2.4_biqbaboplfbrettd7655fr4n2y + next-intl: 2.12.0_next@13.2.4+react@18.2.0 postcss: 8.4.21 postcss-nesting: 11.2.1_postcss@8.4.21 react: 18.2.0 @@ -290,6 +292,53 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: false + /@formatjs/ecma402-abstract/1.11.4: + resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==} + dependencies: + '@formatjs/intl-localematcher': 0.2.25 + tslib: 2.5.0 + dev: false + + /@formatjs/ecma402-abstract/1.14.3: + resolution: {integrity: sha512-SlsbRC/RX+/zg4AApWIFNDdkLtFbkq3LNoZWXZCE/nHVKqoIJyaoQyge/I0Y38vLxowUn9KTtXgusLD91+orbg==} + dependencies: + '@formatjs/intl-localematcher': 0.2.32 + tslib: 2.5.0 + dev: false + + /@formatjs/fast-memoize/1.2.1: + resolution: {integrity: sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==} + dependencies: + tslib: 2.5.0 + dev: false + + /@formatjs/icu-messageformat-parser/2.1.0: + resolution: {integrity: sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==} + dependencies: + '@formatjs/ecma402-abstract': 1.11.4 + '@formatjs/icu-skeleton-parser': 1.3.6 + tslib: 2.5.0 + dev: false + + /@formatjs/icu-skeleton-parser/1.3.6: + resolution: {integrity: sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==} + dependencies: + '@formatjs/ecma402-abstract': 1.11.4 + tslib: 2.5.0 + dev: false + + /@formatjs/intl-localematcher/0.2.25: + resolution: {integrity: sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==} + dependencies: + tslib: 2.5.0 + dev: false + + /@formatjs/intl-localematcher/0.2.32: + resolution: {integrity: sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==} + dependencies: + tslib: 2.5.0 + dev: false + /@humanwhocodes/config-array/0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} @@ -757,6 +806,10 @@ packages: eslint-visitor-keys: 3.3.0 dev: false + /accept-language-parser/1.5.0: + resolution: {integrity: sha512-QhyTbMLYo0BBGg1aWbeMG4ekWtds/31BrEU+DONOg/7ax23vxpL03Pb7/zBmha2v7vdD3AyzZVWBVGEZxKOXWw==} + dev: false + /acorn-jsx/5.3.2_acorn@8.8.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1853,6 +1906,15 @@ packages: side-channel: 1.0.4 dev: false + /intl-messageformat/9.13.0: + resolution: {integrity: sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==} + dependencies: + '@formatjs/ecma402-abstract': 1.11.4 + '@formatjs/fast-memoize': 1.2.1 + '@formatjs/icu-messageformat-parser': 2.1.0 + tslib: 2.5.0 + dev: false + /is-arguments/1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -2172,6 +2234,19 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: false + /next-intl/2.12.0_next@13.2.4+react@18.2.0: + resolution: {integrity: sha512-UWKLA3BIsezFo/OJelGYmJL3BsztApo2mgvmwHMDbTsuBJx0x4SaIIPapnPHPzMhUbppZIbY70xNIULHwn++hg==} + engines: {node: '>=10'} + peerDependencies: + next: ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + accept-language-parser: 1.5.0 + next: 13.2.4_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + use-intl: 2.12.0_react@18.2.0 + dev: false + /next/13.2.4_biqbaboplfbrettd7655fr4n2y: resolution: {integrity: sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw==} engines: {node: '>=14.6.0'} @@ -2855,6 +2930,17 @@ packages: tslib: 2.5.0 dev: false + /use-intl/2.12.0_react@18.2.0: + resolution: {integrity: sha512-LcmAqn78o6Ii9wsLERKAXI9QfiSVBzY3u+mSh6YCLUKhDhr7WHqTBfhEV1gZvdwy7mydhwWQUyQ9Zy5XHuUOAQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@formatjs/ecma402-abstract': 1.14.3 + intl-messageformat: 9.13.0 + react: 18.2.0 + dev: false + /use-sidecar/1.1.2_pmekkgnqduwlme35zpnqhenc34: resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} diff --git a/src/components/header/Header.js b/src/components/header/Header.js index 29e7fb0..a29689a 100644 --- a/src/components/header/Header.js +++ b/src/components/header/Header.js @@ -1,9 +1,12 @@ import { useEffect } from "react"; +import { useTranslations } from "next-intl"; import cn from "classnames"; import styles from "./Header.module.css"; import { initSmoothScroll } from "./smooth-scroll"; export function Header() { + const t = useTranslations("Dev"); + useEffect(() => { initSmoothScroll(); }, []); @@ -11,19 +14,16 @@ export function Header() { return (
-

- Это Код Екатеринбурга — команда, которая сделает - невозможное. Невыполнимое, сложное и безумное. Сделает то, - что никто не сможет. Это тот проект, о котором - вы мечтали. Это — проект судьбы. -

+

- Узнать, что мы будем делать - + dangerouslySetInnerHTML={{ __html: t.raw("learnMore") }} + />
); diff --git a/src/locales/en.json b/src/locales/en.json new file mode 100644 index 0000000..7c71829 --- /dev/null +++ b/src/locales/en.json @@ -0,0 +1,7 @@ +{ + "Dev": { + "title": "Ekaterinburg code", + "header": "This is the Yekaterinburg Code - a team that will achieve the impossible. The unattainable, complex, and insane. They will do what no one else can. This is the project you've dreamed of. It is the project of destiny.", + "learnMore": "Find out what we will be doing" + } +} diff --git a/src/locales/ru.json b/src/locales/ru.json new file mode 100644 index 0000000..42ea290 --- /dev/null +++ b/src/locales/ru.json @@ -0,0 +1,7 @@ +{ + "Dev": { + "title": "Код Екатеринбурга", + "header": "Это Код Екатеринбурга — команда, которая сделает невозможное. Невыполнимое, сложное и безумное. Сделает то, что никто не сможет. Это тот проект, о котором вы мечтали. Это — проект судьбы.", + "learnMore": "Узнать, что мы будем делать" + } +} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 52cf446..fc20b58 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,8 +1,13 @@ +import { NextIntlProvider } from "next-intl"; +import type { AppProps } from "next/app"; import "@/styles/globals.css"; import "../../public/fonts/index.css"; import "../../public/font-jetbrains-mono/index.css"; -import type { AppProps } from "next/app"; export default function App({ Component, pageProps }: AppProps) { - return ; + return ( + + + + ); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index e047c21..5a10349 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,5 +1,6 @@ /* eslint-disable @next/next/no-img-element */ import Head from "next/head"; +import { useTranslations } from "next-intl"; import cn from "classnames"; import styles from "@/components/page/Page.module.css"; import { Preview } from "@/components/preview/Preview"; @@ -9,12 +10,15 @@ import { Road } from "@/components/road/Road"; import { Iframe } from "@/components/iframe/Iframe"; import { Team } from "@/components/team/Team"; import { MainAction } from "@/components/action/MainAction"; +import { GetStaticProps } from "next"; export default function Home() { + const t = useTranslations("Dev"); + return ( <> - Код Екатеринбурга + {t("title")}
@@ -146,3 +150,11 @@ export default function Home() { ); } + +export const getStaticProps: GetStaticProps = async (context) => { + return { + props: { + messages: (await import(`../locales/${context.locale}.json`)).default, + }, + }; +};