Fixed problems with user preferred languages
This commit is contained in:
parent
e0ee70814d
commit
62e64b9319
12
README.md
12
README.md
|
@ -67,7 +67,7 @@ A detailled look at the technologies used in this repository:
|
|||
- The website is built before running in production
|
||||
- Performances are great, and it's possible to deploy the app on a CDN
|
||||
- On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted
|
||||
- UI localizations are downloaded separetely into the `public/local-data` to avoid fetching the same static props for every pages.
|
||||
- Some widely used data (e.g: UI localizations) are downloaded separetely into `public/local-data` as some form of request deduping + it make this data hot-swappable without the need to rebuild the entire website.
|
||||
|
||||
- Queries: [GraphQL Code Generator](https://www.graphql-code-generator.com/)
|
||||
|
||||
|
@ -102,17 +102,15 @@ A detailled look at the technologies used in this repository:
|
|||
|
||||
- Multilingual
|
||||
|
||||
- By default, use the browser's language as the main language
|
||||
- Fallback languages are used for content which are not available in the main language
|
||||
- Main and fallback languages can be ordered manually by the user
|
||||
- At the content level, the user can know which language is available
|
||||
- Furthermore, the user can temporary select another language then the one that was automatically selected
|
||||
- Users are given a list of supported languages. The first language in this list is the primary language (the language of the UI), the others are fallback languages. The others are fallback languages.
|
||||
- By default, the list is ordered following the browser's languages (and most spoken languages woldwide for the remaining languages). The list can also be reordered manually.
|
||||
- Contents can be available in any number of languages. By default, the best matching language will be presented to the user. However, the user can also decide to temporary select another language for a specific content, without affecting their list of preferred languages.
|
||||
|
||||
- UI Localizations
|
||||
|
||||
- The translated wordings use [ICU Message Format](https://unicode-org.github.io/icu/userguide/format_parse/messages/) to include variables, plural, dates...
|
||||
- Use a custom ICU Typescript transformation script to provide type safety when formatting ICU wordings
|
||||
- Fallback to English if a specific working isn't available in the user's language
|
||||
- Fallback to English if the translation is missing.
|
||||
|
||||
- SEO
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@ import { useRouter } from "next/router";
|
|||
import { useEffect } from "react";
|
||||
import { atom } from "jotai";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import { atomPairing, useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||
import { getDefaultPreferredLanguages } from "helpers/locales";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { atomPairing, useAtomGetter, useAtomPair, useAtomSetter } from "helpers/atoms";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
import { usePrefersDarkMode } from "hooks/useMediaQuery";
|
||||
import { userAgent } from "contexts/userAgent";
|
||||
import { getLogger } from "helpers/logger";
|
||||
|
||||
export enum ThemeMode {
|
||||
Dark = "dark",
|
||||
|
@ -20,6 +20,8 @@ export enum PerfMode {
|
|||
Off = "off",
|
||||
}
|
||||
|
||||
const logger = getLogger("⚙️ [Settings Context]");
|
||||
|
||||
const preferredLanguagesAtom = atomPairing(atomWithStorage<string[]>("preferredLanguages", []));
|
||||
const themeModeAtom = atomPairing(atomWithStorage("themeMode", ThemeMode.Auto));
|
||||
const darkModeAtom = atomPairing(atom(false));
|
||||
|
@ -67,7 +69,7 @@ export const settings = {
|
|||
|
||||
export const useSettings = (): void => {
|
||||
const router = useRouter();
|
||||
const [preferredLanguages, setPreferredLanguages] = useAtomPair(preferredLanguagesAtom);
|
||||
const setPreferredLanguages = useAtomSetter(preferredLanguagesAtom);
|
||||
const fontSize = useAtomGetter(fontSizeAtom);
|
||||
const isDyslexic = useAtomGetter(dyslexicAtom);
|
||||
const [isDarkMode, setDarkMode] = useAtomPair(darkModeAtom);
|
||||
|
@ -114,24 +116,33 @@ export const useSettings = (): void => {
|
|||
}, [isDarkMode]);
|
||||
|
||||
/* PREFERRED LANGUAGES */
|
||||
|
||||
useEffect(() => {
|
||||
if (preferredLanguages.length === 0) {
|
||||
if (isDefinedAndNotEmpty(router.locale) && router.locales) {
|
||||
setPreferredLanguages(getDefaultPreferredLanguages(router.locale, router.locales));
|
||||
}
|
||||
} else if (router.locale !== preferredLanguages[0]) {
|
||||
/*
|
||||
* Using a timeout to the code getting stuck into a loop when reaching the website with a
|
||||
* different preferredLanguages[0] from router.locale
|
||||
*/
|
||||
setTimeout(
|
||||
async () =>
|
||||
router.replace(router.asPath, router.asPath, {
|
||||
locale: preferredLanguages[0],
|
||||
}),
|
||||
250
|
||||
if (!router.locale || !router.locales) return;
|
||||
const localStorageValue: string[] = JSON.parse(
|
||||
localStorage.getItem("preferredLanguages") ?? "[]"
|
||||
);
|
||||
|
||||
if (localStorageValue.length === 0) {
|
||||
const defaultLanguages = router.locales;
|
||||
defaultLanguages.sort((a, b) => {
|
||||
const evaluate = (value: string) =>
|
||||
navigator.languages.includes(value)
|
||||
? navigator.languages.findIndex((v) => value === v)
|
||||
: navigator.languages.length;
|
||||
return evaluate(a) - evaluate(b);
|
||||
});
|
||||
logger.log("First time visitor, initializing preferred languages to", defaultLanguages);
|
||||
setPreferredLanguages(defaultLanguages);
|
||||
} else if (router.locale !== localStorageValue[0]) {
|
||||
logger.log(
|
||||
"Router locale",
|
||||
router.locale,
|
||||
"doesn't correspond to preferred locale. Switching to",
|
||||
localStorageValue[0]
|
||||
);
|
||||
router.replace(router.asPath, router.asPath, {
|
||||
locale: localStorageValue[0],
|
||||
});
|
||||
}
|
||||
}, [preferredLanguages, router, setPreferredLanguages]);
|
||||
}, [router, setPreferredLanguages]);
|
||||
};
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
import { isDefined } from "./asserts";
|
||||
|
||||
export const getDefaultPreferredLanguages = (routerLocal: string, locales: string[]): string[] => {
|
||||
let defaultPreferredLanguages: string[] = [];
|
||||
if (routerLocal === "en") {
|
||||
defaultPreferredLanguages = [routerLocal];
|
||||
locales.map((locale) => {
|
||||
if (locale !== routerLocal) defaultPreferredLanguages.push(locale);
|
||||
});
|
||||
} else {
|
||||
defaultPreferredLanguages = [routerLocal, "en"];
|
||||
locales.map((locale) => {
|
||||
if (locale !== routerLocal && locale !== "en") defaultPreferredLanguages.push(locale);
|
||||
});
|
||||
}
|
||||
return defaultPreferredLanguages;
|
||||
const defaultPreferredLanguages: Set<string> = new Set();
|
||||
defaultPreferredLanguages.add(routerLocal);
|
||||
defaultPreferredLanguages.add("en");
|
||||
locales.forEach((locale) => defaultPreferredLanguages.add(locale));
|
||||
return [...defaultPreferredLanguages.values()];
|
||||
};
|
||||
|
||||
export const getPreferredLanguage = (
|
||||
|
|
Loading…
Reference in New Issue