From 17cdb7efa433bc19723613a16a40d41b5f201add Mon Sep 17 00:00:00 2001 From: daniel31x13 Date: Mon, 27 May 2024 17:42:29 -0400 Subject: [PATCH] initial commit for i18n --- next-i18next.config.js | 7 ++++ next.config.js | 2 ++ package.json | 3 ++ pages/_app.tsx | 8 +++-- pages/admin.tsx | 62 +++++++++++++++++++++++++++++++++-- public/locales/de/common.json | 3 ++ public/locales/en/common.json | 3 ++ yarn.lock | 57 +++++++++++++++++++++++++++++++- 8 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 next-i18next.config.js create mode 100644 public/locales/de/common.json create mode 100644 public/locales/en/common.json diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 0000000..02de282 --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,7 @@ +/** @type {import('next-i18next').UserConfig} */ +module.exports = { + i18n: { + defaultLocale: "en", + locales: ["en", "de"], + }, +}; diff --git a/next.config.js b/next.config.js index 665803e..79d2d0f 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,9 @@ /** @type {import('next').NextConfig} */ const { version } = require("./package.json"); +const { i18n } = require("./next-i18next.config"); const nextConfig = { + i18n, reactStrictMode: true, images: { // For fetching the favicons diff --git a/package.json b/package.json index 8222cb2..0045802 100644 --- a/package.json +++ b/package.json @@ -50,12 +50,14 @@ "framer-motion": "^10.16.4", "handlebars": "^4.7.8", "himalaya": "^1.1.0", + "i18next": "^23.11.5", "jimp": "^0.22.10", "jsdom": "^22.1.0", "lottie-web": "^5.12.2", "micro": "^10.0.1", "next": "13.4.12", "next-auth": "^4.22.1", + "next-i18next": "^15.3.0", "node-fetch": "^2.7.0", "nodemailer": "^6.9.3", "playwright": "^1.43.1", @@ -63,6 +65,7 @@ "react-colorful": "^5.6.1", "react-dom": "18.2.0", "react-hot-toast": "^2.4.1", + "react-i18next": "^14.1.2", "react-image-file-resizer": "^0.4.8", "react-masonry-css": "^1.0.16", "react-select": "^5.7.4", diff --git a/pages/_app.tsx b/pages/_app.tsx index 9c6b527..0cb79ff 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -9,9 +9,11 @@ import toast from "react-hot-toast"; import { Toaster, ToastBar } from "react-hot-toast"; import { Session } from "next-auth"; import { isPWA } from "@/lib/client/utils"; -import useInitialData from "@/hooks/useInitialData"; +// import useInitialData from "@/hooks/useInitialData"; +import { appWithTranslation } from "next-i18next"; +import nextI18nextConfig from "../next-i18next.config"; -export default function App({ +function App({ Component, pageProps, }: AppProps<{ @@ -96,6 +98,8 @@ export default function App({ ); } +export default appWithTranslation(App); + // function GetData({ children }: { children: React.ReactNode }) { // const status = useInitialData(); // return typeof window !== "undefined" && status !== "loading" ? ( diff --git a/pages/admin.tsx b/pages/admin.tsx index d7b9fda..35e14ed 100644 --- a/pages/admin.tsx +++ b/pages/admin.tsx @@ -3,7 +3,12 @@ import NewUserModal from "@/components/ModalContent/NewUserModal"; import useUserStore from "@/store/admin/users"; import { User as U } from "@prisma/client"; import Link from "next/link"; -import { Fragment, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; +import { useTranslation } from "next-i18next"; +import { GetServerSideProps } from "next"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; +import { useRouter } from "next/router"; +import { i18n } from "next-i18next.config"; interface User extends U { subscriptions: { @@ -17,6 +22,10 @@ type UserModal = { }; export default function Admin() { + const { t } = useTranslation(); + + const router = useRouter(); + const { users, setUsers } = useUserStore(); const [searchQuery, setSearchQuery] = useState(""); @@ -30,6 +39,7 @@ export default function Admin() { const [newUserModal, setNewUserModal] = useState(false); useEffect(() => { + console.log(router); setUsers(); }, []); @@ -44,7 +54,7 @@ export default function Admin() {

- User Administration + {t("user_administration")}

@@ -172,3 +182,51 @@ const UserListing = ( ); }; + +// Take this into a separate file, it's for logged out users +// For logged in users, we'll use their preferred language from the database (default: en) +export const getServerSideProps: GetServerSideProps = async (ctx) => { + console.log("CONTEXT", ctx); + + const acceptLanguageHeader = ctx.req.headers["accept-language"]; + const availableLanguages = i18n.locales; + + console.log("ACCEPT LANGUAGE", acceptLanguageHeader); + console.log("AVAILABLE LANGUAGES", availableLanguages); + + // Parse the accept-language header to get an array of languages + const acceptedLanguages = acceptLanguageHeader + ?.split(",") + .map((lang) => lang.split(";")[0]); + + console.log(acceptedLanguages); + + // Find the best match between the accepted languages and available languages + let bestMatch = acceptedLanguages?.find((lang) => + availableLanguages.includes(lang) + ); + + console.log(bestMatch); + + // If no direct match, find the best partial match + if (!bestMatch) { + acceptedLanguages?.some((acceptedLang) => { + const partialMatch = availableLanguages.find((lang) => + lang.startsWith(acceptedLang) + ); + if (partialMatch) { + bestMatch = partialMatch; + return true; + } + return false; + }); + } + + console.log("BEST MATCH", bestMatch); + + return { + props: { + ...(await serverSideTranslations(bestMatch ?? "en", ["common"])), + }, + }; +}; diff --git a/public/locales/de/common.json b/public/locales/de/common.json new file mode 100644 index 0000000..b4cce72 --- /dev/null +++ b/public/locales/de/common.json @@ -0,0 +1,3 @@ +{ + "user_administration": "TEST, TEST, TEST" +} \ No newline at end of file diff --git a/public/locales/en/common.json b/public/locales/en/common.json new file mode 100644 index 0000000..ff33803 --- /dev/null +++ b/public/locales/en/common.json @@ -0,0 +1,3 @@ +{ + "user_administration": "User Administration" +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index fea7154..55074b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -666,6 +666,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.6.tgz#5b76eb89ad45e2e4a0a8db54c456251469a3358e" + integrity sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.24.1": version "7.24.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.5.tgz#230946857c053a36ccc66e1dd03b17dd0c4ed02c" @@ -1960,7 +1967,7 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/hoist-non-react-statics@^3.3.0": +"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.4": version "3.3.5" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== @@ -2683,6 +2690,11 @@ core-js@^2.6.12: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== +core-js@^3: + version "3.37.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.1.tgz#d21751ddb756518ac5a00e4d66499df981a62db9" + integrity sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw== + core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3833,6 +3845,13 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + http-errors@1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" @@ -3870,6 +3889,18 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" +i18next-fs-backend@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.3.1.tgz#0c7d2459ff4a039e2b3228131809fbc0e74ff1a8" + integrity sha512-tvfXskmG/9o+TJ5Fxu54sSO5OkY6d+uMn+K6JiUGLJrwxAVfer+8V3nU8jq3ts9Pe5lXJv4b1N7foIjJ8Iy2Gg== + +i18next@^23.11.5: + version "23.11.5" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.11.5.tgz#d71eb717a7e65498d87d0594f2664237f9e361ef" + integrity sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA== + dependencies: + "@babel/runtime" "^7.23.2" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4569,6 +4600,17 @@ next-auth@^4.22.1: preact-render-to-string "^5.1.19" uuid "^8.3.2" +next-i18next@^15.3.0: + version "15.3.0" + resolved "https://registry.yarnpkg.com/next-i18next/-/next-i18next-15.3.0.tgz#b4530c80573854d00f95229af405e1e5beedbf18" + integrity sha512-bq7Cc9XJFcmGOCLnyEtHaeJ3+JJNsI/8Pkj9BaHAnhm4sZ9vNNC4ZsaqYnlRZ7VH5ypSo73fEqLK935jLsmCvQ== + dependencies: + "@babel/runtime" "^7.23.2" + "@types/hoist-non-react-statics" "^3.3.4" + core-js "^3" + hoist-non-react-statics "^3.3.2" + i18next-fs-backend "^2.3.1" + next@13.4.12: version "13.4.12" resolved "https://registry.yarnpkg.com/next/-/next-13.4.12.tgz#809b21ea0aabbe88ced53252c88c4a5bd5af95df" @@ -5195,6 +5237,14 @@ react-hot-toast@^2.4.1: dependencies: goober "^2.1.10" +react-i18next@^14.1.2: + version "14.1.2" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-14.1.2.tgz#cd57a755f25a32a5fcc3dbe546cf3cc62b4f3ebd" + integrity sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg== + dependencies: + "@babel/runtime" "^7.23.9" + html-parse-stringify "^3.0.1" + react-image-file-resizer@^0.4.8: version "0.4.8" resolved "https://registry.yarnpkg.com/react-image-file-resizer/-/react-image-file-resizer-0.4.8.tgz#85f4ae4469fd2867d961568af660ef403d7a79af" @@ -6176,6 +6226,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + w3c-xmlserializer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"