From 9223573653e778494db931c78cc72f36406c9c0a Mon Sep 17 00:00:00 2001 From: Sergei Ivashchenko Date: Tue, 5 Aug 2025 23:04:34 +0100 Subject: [PATCH 1/2] Add localization for next.js --- next-i18next.config.js | 6 + next.config.mjs | 12 ++ package.json | 7 +- public/locales/en/common.json | 7 + public/locales/ru/common.json | 7 + .../filter-panel/filter-panel-normal.js | 6 +- src/pages/_app.tsx | 3 +- src/pages/iframes/map.tsx | 11 + yarn.lock | 204 ++++++++++++++++-- 9 files changed, 236 insertions(+), 27 deletions(-) create mode 100644 next-i18next.config.js create mode 100644 public/locales/en/common.json create mode 100644 public/locales/ru/common.json diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 00000000..cff8bdd0 --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,6 @@ +module.exports = { + i18n: { + defaultLocale: "ru", + locales: ["ru", "en"], + }, +}; diff --git a/next.config.mjs b/next.config.mjs index 7d73012d..1f84d8db 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -21,6 +21,18 @@ const nextConfig = { compiler: { styledComponents: true, }, + i18n: { + defaultLocale: "ru", + locales: ["ru", "en"], + localeDetection: false, // отключить автоопределение по Accept-Language + localeCookie: { + name: "django_language", + httpOnly: false, + sameSite: false, // Django uses None, Next.js uses false for None + path: "/", + secure: false, // same as Django DEBUG mode + }, + }, productionBrowserSourceMaps: true, reactStrictMode: true, diff --git a/package.json b/package.json index a787dde4..ce401bbe 100644 --- a/package.json +++ b/package.json @@ -32,19 +32,22 @@ "@fortawesome/free-brands-svg-icons": "^6.0.0", "@fortawesome/free-solid-svg-icons": "^6.0.0", "@fortawesome/react-fontawesome": "^0.1.17", - "@prisma/client": "^3.9.2", + "@prisma/client": "latest", "@sentry/nextjs": "^6.17.9", "date-fns": "^2.28.0", "dayjs": "^1.10.7", "gravatar-url": "^4.0.1", + "i18next": "^25.3.2", "lru-cache": "^7.4.0", "mobx": "^6.4.0", "mobx-devtools-mst": "^0.9.30", "mobx-react": "^7.3.0", "mobx-state-tree": "^5.1.3", "next": "^12.1.0", + "next-i18next": "^15.4.2", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-i18next": "^15.6.1", "react-linkify": "^1.0.0-alpha", "react-redux": "^7.2.6", "react-tooltip": "^4.2.21", @@ -78,7 +81,7 @@ "prettier-plugin-packagejson": "^2.2.15", "prettier-plugin-prisma": "^3.9.0", "prettier-plugin-sh": "^0.8.1", - "prisma": "^3.9.2", + "prisma": "latest", "suppress-exit-code": "^1.0.0", "typescript": "^4.5.5" }, diff --git a/public/locales/en/common.json b/public/locales/en/common.json new file mode 100644 index 00000000..0c87a245 --- /dev/null +++ b/public/locales/en/common.json @@ -0,0 +1,7 @@ +{ + "welcome": "Welcome", + "dataPeriod": "Data Period", + "forPeriod": "For period:", + "filters": "Filters", + "hide": "Hide" +} diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json new file mode 100644 index 00000000..6566cfd8 --- /dev/null +++ b/public/locales/ru/common.json @@ -0,0 +1,7 @@ +{ + "welcome": "Добро пожаловать", + "dataPeriod": "Период данных", + "forPeriod": "За период:", + "filters": "Фильтры", + "hide": "Скрыть" +} diff --git a/src/components/inherited-map/components/filter-panel/filter-panel-normal.js b/src/components/inherited-map/components/filter-panel/filter-panel-normal.js index 4c084341..62c19710 100644 --- a/src/components/inherited-map/components/filter-panel/filter-panel-normal.js +++ b/src/components/inherited-map/components/filter-panel/filter-panel-normal.js @@ -1,4 +1,5 @@ import { observer } from "mobx-react"; +import { useTranslation } from "next-i18next"; import * as React from "react"; import { useStore } from "../../models/root-store"; @@ -65,6 +66,7 @@ const CategoryTag = observer(({ filter }) => { export const FilterPanelNormal = observer(() => { const { filterStore } = useStore(); const { filters } = filterStore; + const { t } = useTranslation("common"); const mainFilters = filters.filter( (currentFilter) => currentFilter.name !== "category", @@ -83,7 +85,7 @@ export const FilterPanelNormal = observer(() => { ))}
-

Фильтры

+

{t("filters")}

{categoryFilters.map((currentFilter) => ( @@ -100,7 +102,7 @@ export const FilterPanelNormal = observer(() => { - Скрыть + {t("hide")}
); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 5c208875..bb14aacf 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -2,6 +2,7 @@ import "../styles/inherited-scss/style.scss"; import { UserProvider } from "@auth0/nextjs-auth0"; import { AppProps } from "next/app"; +import { appWithTranslation } from "next-i18next"; import * as React from "react"; import { ThemeProvider } from "styled-components"; @@ -24,4 +25,4 @@ const App: React.VoidFunctionComponent = ({ ); }; -export default App; +export default appWithTranslation(App); diff --git a/src/pages/iframes/map.tsx b/src/pages/iframes/map.tsx index 5752195a..9bb989aa 100644 --- a/src/pages/iframes/map.tsx +++ b/src/pages/iframes/map.tsx @@ -1,6 +1,8 @@ import { NextPage } from "next"; import dynamic from "next/dynamic"; import Script from "next/script"; +import { useTranslation } from "next-i18next"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import * as React from "react"; import { usePostLocationSearchChangesToIframeParent } from "../../shared/django-helpers"; @@ -13,6 +15,7 @@ const InheritedMap = dynamic( const MapIframePage: NextPage = () => { usePostLocationSearchChangesToIframeParent(); + const { t } = useTranslation("common"); return ( <> @@ -29,4 +32,12 @@ const MapIframePage: NextPage = () => { ); }; +export async function getServerSideProps({ locale }: { locale: string }) { + return { + props: { + ...(await serverSideTranslations(locale ?? "ru", ["common"])), + }, + }; +} + export default MapIframePage; diff --git a/yarn.lock b/yarn.lock index 86421de6..8368aea4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -147,6 +147,13 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.27.6": + version: 7.28.2 + resolution: "@babel/runtime@npm:7.28.2" + checksum: 8673eb2311752929f5b0167f42cff4cc1d5fadddd0394baca27d06c1618680ffcf95e9f01061f5c4dc3f6a32b6bbf500e7762c02dc22bcd273c2947b9774ddad + languageName: node + linkType: hard + "@babel/template@npm:^7.16.7": version: 7.16.7 resolution: "@babel/template@npm:7.16.7" @@ -568,31 +575,73 @@ __metadata: languageName: node linkType: hard -"@prisma/client@npm:^3.9.2": - version: 3.9.2 - resolution: "@prisma/client@npm:3.9.2" - dependencies: - "@prisma/engines-version": 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009 +"@prisma/client@npm:latest": + version: 6.12.0 + resolution: "@prisma/client@npm:6.12.0" peerDependencies: prisma: "*" + typescript: ">=5.1.0" peerDependenciesMeta: prisma: optional: true - checksum: 5b4b8f252624250f609d57ed3939f3dbecb174c297691476050f48d6c24dd41ca2f70f9e5a52729a4e875527e56a2d564a116c0ada8f5fc935c558f09d2759e3 + typescript: + optional: true + checksum: 3d6d9354b57872b58b97c01e3e90d1359d9dc47fd081f0db8ce81af0b3a1f5277678424c5a5a2f6a801ec0c87db3612da1e8be18913ee62ec54e6b4428eb6c96 languageName: node linkType: hard -"@prisma/engines-version@npm:3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009": - version: 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009 - resolution: "@prisma/engines-version@npm:3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" - checksum: 413d478fc1980604de1f46a0d53ae6c752965283a7d8c51d8fb3881c371b4a71ab4aa10002dba411d961286caf1141871c3c85d209a316421cdf1e1fdac71b4e +"@prisma/config@npm:6.12.0": + version: 6.12.0 + resolution: "@prisma/config@npm:6.12.0" + dependencies: + jiti: 2.4.2 + checksum: b06b8fb9f8f07e44dcba250e059551d0439c82b5c2957385d5132e9040e7e40e5bc7bc3620cc671092b880552ad6f7247ae76d8288851ab06c8d01bfba6c7dfc languageName: node linkType: hard -"@prisma/engines@npm:3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009": - version: 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009 - resolution: "@prisma/engines@npm:3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" - checksum: f934a02f0d12b67a8b878da5b854a2df0b1d5adbedb9d6df552dddcb4f58af9401268c2066180a337daa0fd22d9005eae2197bf8a4cc7b9e1a20764fe93f1244 +"@prisma/debug@npm:6.12.0": + version: 6.12.0 + resolution: "@prisma/debug@npm:6.12.0" + checksum: eebf9aed967964f8187cc7b3df0205e25f99a2874163b09031a7283748f038d841a076809b1c593a01dc198d1736f562f11b06e66be01edbf1642a3a2f357174 + languageName: node + linkType: hard + +"@prisma/engines-version@npm:6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc": + version: 6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc + resolution: "@prisma/engines-version@npm:6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc" + checksum: 108e213a2cc3f85cf11c74c8b17f16f89b36cfc1a2146f918c935da0b9f9f3b0b1c59cdb4edf30c81767182ab9de5730330105af25d8b9cb6eacf400be4f7dfd + languageName: node + linkType: hard + +"@prisma/engines@npm:6.12.0": + version: 6.12.0 + resolution: "@prisma/engines@npm:6.12.0" + dependencies: + "@prisma/debug": 6.12.0 + "@prisma/engines-version": 6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc + "@prisma/fetch-engine": 6.12.0 + "@prisma/get-platform": 6.12.0 + checksum: ab52544ce07ca375f4dbda448952a9ae7a920f9aa0385a7a02a082a12392c20fa2a0e450951dd4255a9918ec92ef4acc87290993c3bb9fa2cd55c4600b6c8919 + languageName: node + linkType: hard + +"@prisma/fetch-engine@npm:6.12.0": + version: 6.12.0 + resolution: "@prisma/fetch-engine@npm:6.12.0" + dependencies: + "@prisma/debug": 6.12.0 + "@prisma/engines-version": 6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc + "@prisma/get-platform": 6.12.0 + checksum: 16ebe12e4d9ae03defcc08fee43cc8f4cd29edf57032df1c88070576fb23dd99de3dbc10a6dea702c8e5ff24e57e99efc050a7c65821b1c1a4575eac64a19540 + languageName: node + linkType: hard + +"@prisma/get-platform@npm:6.12.0": + version: 6.12.0 + resolution: "@prisma/get-platform@npm:6.12.0" + dependencies: + "@prisma/debug": 6.12.0 + checksum: 64a9594088eb840fa10f3e42e3ef6428a136cb5736cb133824ce220216b5179352254e37e296568bf2afaa0db3eb8a600f120d9e91e08a1b18fb515474140112 languageName: node linkType: hard @@ -851,6 +900,17 @@ __metadata: languageName: node linkType: hard +"@types/hoist-non-react-statics@npm:^3.3.6": + version: 3.3.7 + resolution: "@types/hoist-non-react-statics@npm:3.3.7" + dependencies: + hoist-non-react-statics: ^3.3.0 + peerDependencies: + "@types/react": "*" + checksum: 13f610572c073970b3f43cc446396974fed786fee6eac2d6fd4b0ca5c985f13e79d4a0de58af4e5b4c68470d808567c3a14108d98edb7d526d4d46c8ec851ed1 + languageName: node + linkType: hard + "@types/http-cache-semantics@npm:*": version: 4.0.1 resolution: "@types/http-cache-semantics@npm:4.0.1" @@ -1805,6 +1865,13 @@ __metadata: languageName: node linkType: hard +"core-js@npm:^3": + version: 3.44.0 + resolution: "core-js@npm:3.44.0" + checksum: 1a9fca319aaeb8ae58764f9566a92d5fe7f609749dd1e5e0af4e1a0e4c6f0402d51b5ad379decb6ee722df0a65f0897a1ed1da397888e53437a52d3c105724a5 + languageName: node + linkType: hard + "core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -3061,6 +3128,15 @@ __metadata: languageName: node linkType: hard +"html-parse-stringify@npm:^3.0.1": + version: 3.0.1 + resolution: "html-parse-stringify@npm:3.0.1" + dependencies: + void-elements: 3.1.0 + checksum: 334fdebd4b5c355dba8e95284cead6f62bf865a2359da2759b039db58c805646350016d2017875718bc3c4b9bf81a0d11be5ee0cf4774a3a5a7b97cde21cfd67 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0": version: 4.1.0 resolution: "http-cache-semantics@npm:4.1.0" @@ -3151,6 +3227,27 @@ __metadata: languageName: node linkType: hard +"i18next-fs-backend@npm:^2.6.0": + version: 2.6.0 + resolution: "i18next-fs-backend@npm:2.6.0" + checksum: 2a34a6f0563f7cc5c98de93e66fff66696eaf19b295d073fb1fef426db75783ecfab10b0251272957c307cbb3ffb4e9b5073c17ddb10be2e352d9b6e6ee71a5a + languageName: node + linkType: hard + +"i18next@npm:^25.3.2": + version: 25.3.2 + resolution: "i18next@npm:25.3.2" + dependencies: + "@babel/runtime": ^7.27.6 + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + checksum: e0ee33b6d7f1e8ce3aa2a6a139e673686a5651903fdfeec47d10b32737342045be6607da73020f94b07edcee9173d940fe4e8062d1e2183bbf482141624bda3e + languageName: node + linkType: hard + "iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -3606,6 +3703,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:2.4.2": + version: 2.4.2 + resolution: "jiti@npm:2.4.2" + bin: + jiti: lib/jiti-cli.mjs + checksum: c6c30c7b6b293e9f26addfb332b63d964a9f143cdd2cf5e946dbe5143db89f7c1b50ad9223b77fb1f6ddb0b9c5ecef995fea024ecf7d2861d285d779cde66e1e + languageName: node + linkType: hard + "joi@npm:^17.5.0": version: 17.6.0 resolution: "joi@npm:17.6.0" @@ -4418,6 +4524,24 @@ __metadata: languageName: node linkType: hard +"next-i18next@npm:^15.4.2": + version: 15.4.2 + resolution: "next-i18next@npm:15.4.2" + dependencies: + "@babel/runtime": ^7.23.2 + "@types/hoist-non-react-statics": ^3.3.6 + core-js: ^3 + hoist-non-react-statics: ^3.3.2 + i18next-fs-backend: ^2.6.0 + peerDependencies: + i18next: ">= 23.7.13" + next: ">= 12.0.0" + react: ">= 17.0.2" + react-i18next: ">= 13.5.0" + checksum: d5c3d675b66ae4c65f96c040a031df0c81c23d2562c398f560c3e629e47fb67061053c0bb58cce41b9e45106a0c9cf7db05ef464991a6908ed5bb8fd85f50634 + languageName: node + linkType: hard + "next@npm:^12.0.10, next@npm:^12.1.0": version: 12.1.0 resolution: "next@npm:12.1.0" @@ -5205,15 +5329,20 @@ __metadata: languageName: node linkType: hard -"prisma@npm:^3.9.2": - version: 3.9.2 - resolution: "prisma@npm:3.9.2" +"prisma@npm:latest": + version: 6.12.0 + resolution: "prisma@npm:6.12.0" dependencies: - "@prisma/engines": 3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009 + "@prisma/config": 6.12.0 + "@prisma/engines": 6.12.0 + peerDependencies: + typescript: ">=5.1.0" + peerDependenciesMeta: + typescript: + optional: true bin: prisma: build/index.js - prisma2: build/index.js - checksum: 4070b61b09f45229680e1beeb14a95716353493032abaac9f6664bc60eba8279e165d446e8a12457a6d6a0e80d100c09228d00e7e1986e504fc8ceb9a16316f2 + checksum: a8f18bfe55514048ceb2263467472032a0d0b8f2ac95510bc493c903efa57cf06dfa32bf5fdd16da360e11e86e207f1e24b258215a01efbcace4c45b39dd958c languageName: node linkType: hard @@ -5333,6 +5462,27 @@ __metadata: languageName: node linkType: hard +"react-i18next@npm:^15.6.1": + version: 15.6.1 + resolution: "react-i18next@npm:15.6.1" + dependencies: + "@babel/runtime": ^7.27.6 + html-parse-stringify: ^3.0.1 + peerDependencies: + i18next: ">= 23.2.3" + react: ">= 16.8.0" + typescript: ^5 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + checksum: f4342cc8ad6c4f2b5e4e8ac663da8848ad6402c3713ca542f4ad3a84b21ca10678c443ff9ca8250fa512e6e5c0000fc83b68797ae734d7d15b7ff09d5c5e9004 + languageName: node + linkType: hard + "react-is@npm:^16.13.1, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -5672,7 +5822,7 @@ __metadata: "@kachkaev/markdownlint-config": ^0.3.0 "@netlify/plugin-nextjs": ^4.2.7 "@next/eslint-plugin-next": ^12.1.0 - "@prisma/client": ^3.9.2 + "@prisma/client": latest "@sentry/nextjs": ^6.17.9 "@types/lru-cache": ^5.1.1 "@types/node": ^17.0.18 @@ -5688,6 +5838,7 @@ __metadata: execa: ^6.1.0 gravatar-url: ^4.0.1 husky: ^7.0.4 + i18next: ^25.3.2 lint-staged: ^12.3.4 lru-cache: ^7.4.0 markdownlint-cli: ^0.31.1 @@ -5696,14 +5847,16 @@ __metadata: mobx-react: ^7.3.0 mobx-state-tree: ^5.1.3 next: ^12.1.0 + next-i18next: ^15.4.2 npm-run-all: ^4.1.5 prettier: ^2.5.1 prettier-plugin-packagejson: ^2.2.15 prettier-plugin-prisma: ^3.9.0 prettier-plugin-sh: ^0.8.1 - prisma: ^3.9.2 + prisma: latest react: ^17.0.2 react-dom: ^17.0.2 + react-i18next: ^15.6.1 react-linkify: ^1.0.0-alpha react-redux: ^7.2.6 react-tooltip: ^4.2.21 @@ -6761,6 +6914,13 @@ __metadata: languageName: node linkType: hard +"void-elements@npm:3.1.0": + version: 3.1.0 + resolution: "void-elements@npm:3.1.0" + checksum: 0390f818107fa8fce55bb0a5c3f661056001c1d5a2a48c28d582d4d847347c2ab5b7f8272314cac58acf62345126b6b09bea623a185935f6b1c3bbce0dfd7f7f + languageName: node + linkType: hard + "vscode-json-languageservice@npm:^4.1.6": version: 4.2.0 resolution: "vscode-json-languageservice@npm:4.2.0" From d8776bfd2d9fa280f0bfddce58fd20e3b657be4b Mon Sep 17 00:00:00 2001 From: Sergei Ivashchenko Date: Thu, 7 Aug 2025 22:26:39 +0100 Subject: [PATCH 2/2] trigger ci