From 9e0f43d5425210c7f8eb8ec6bbbfe6676d398bc3 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 25 Apr 2022 16:45:02 +0200 Subject: [PATCH] lazy load locales (#1940) * lazy load locales * fix translation getter * prettier ignore all generated i18n files * fix menu translation reactivity * put language and country translations into namespace * use Intl.DisplayNames to provide language and region translations * update typesafe-i18n * fix newly added translations * remove unused translations * add fallback to locale code when Intl.DisplayNames is unavailable --- front/.prettierignore | 4 +- front/.typesafe-i18n.json | 2 +- front/package.json | 2 +- front/src/Components/Menu/Menu.svelte | 21 +++----- .../Components/Menu/SettingsSubMenu.svelte | 6 +-- front/src/Connexion/ConnectionManager.ts | 14 +++-- front/src/Phaser/Entity/RemotePlayer.ts | 8 +-- front/src/i18n/.gitignore | 4 +- front/src/i18n/de-DE/index.ts | 2 - front/src/i18n/en-US/index.ts | 2 - front/src/i18n/formatters.ts | 4 +- front/src/i18n/fr-FR/index.ts | 2 - front/src/i18n/locales.ts | 54 ++++++++----------- front/src/i18n/zh-CN/index.ts | 2 - front/tsconfig.json | 2 +- front/yarn.lock | 8 +-- 16 files changed, 55 insertions(+), 82 deletions(-) diff --git a/front/.prettierignore b/front/.prettierignore index 8d8c68de..bef7f3b6 100644 --- a/front/.prettierignore +++ b/front/.prettierignore @@ -1,5 +1,3 @@ src/Messages/generated src/Messages/JsonMessages -src/i18n/i18n-svelte.ts -src/i18n/i18n-types.ts -src/i18n/i18n-util.ts +src/i18n/i18n-*.ts diff --git a/front/.typesafe-i18n.json b/front/.typesafe-i18n.json index 0cecbe32..2f7fe9a4 100644 --- a/front/.typesafe-i18n.json +++ b/front/.typesafe-i18n.json @@ -1,5 +1,5 @@ { - "$schema": "https://unpkg.com/typesafe-i18n@2.59.0/schema/typesafe-i18n.json", + "$schema": "https://unpkg.com/typesafe-i18n@5.3.5/schema/typesafe-i18n.json", "baseLocale": "en-US", "adapter": "svelte" } \ No newline at end of file diff --git a/front/package.json b/front/package.json index 4ddae384..eba55d46 100644 --- a/front/package.json +++ b/front/package.json @@ -60,7 +60,7 @@ "standardized-audio-context": "^25.2.4", "ts-deferred": "^1.0.4", "ts-proto": "^1.96.0", - "typesafe-i18n": "^2.59.0", + "typesafe-i18n": "^5.3.5", "uuidv4": "^6.2.10", "zod": "^3.14.3" }, diff --git a/front/src/Components/Menu/Menu.svelte b/front/src/Components/Menu/Menu.svelte index 2583c208..78419867 100644 --- a/front/src/Components/Menu/Menu.svelte +++ b/front/src/Components/Menu/Menu.svelte @@ -88,16 +88,11 @@ } } - function translateMenuName(menu: MenuItem) { - if (menu.type === "scripting") { - return menu.label; - } - - // Bypass the proxy of typesafe for getting the menu name : https://github.com/ivanhofer/typesafe-i18n/issues/156 - const getMenuName = $LL.menu.sub[menu.key]; - - return getMenuName(); - } + $: subMenuTranslations = $subMenusStore.map((subMenu) => + subMenu.type === "scripting" ? subMenu.label : $LL.menu.sub[subMenu.key]() + ); + $: activeSubMenuTranslation = + activeSubMenu.type === "scripting" ? activeSubMenu.label : $LL.menu.sub[activeSubMenu.key](); @@ -106,20 +101,20 @@ diff --git a/front/src/Components/Menu/SettingsSubMenu.svelte b/front/src/Components/Menu/SettingsSubMenu.svelte index 41fd425a..b08a645c 100644 --- a/front/src/Components/Menu/SettingsSubMenu.svelte +++ b/front/src/Components/Menu/SettingsSubMenu.svelte @@ -28,12 +28,12 @@ let previewCameraPrivacySettings = valueCameraPrivacySettings; let previewMicrophonePrivacySettings = valueMicrophonePrivacySettings; - function saveSetting() { + async function saveSetting() { let change = false; if (valueLocale !== previewValueLocale) { previewValueLocale = valueLocale; - setCurrentLocale(valueLocale as Locales); + await setCurrentLocale(valueLocale as Locales); } if (valueVideo !== previewValueVideo) { @@ -174,7 +174,7 @@
diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index d01c39a6..bd305413 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -363,15 +363,13 @@ class ConnectionManager { if (locale) { try { - if (locales.indexOf(locale) == -1) { - locales.forEach((l) => { - if (l.startsWith(locale.split("-")[0])) { - setCurrentLocale(l); - return; - } - }); + if (locales.indexOf(locale) !== -1) { + await setCurrentLocale(locale as Locales); } else { - setCurrentLocale(locale as Locales); + const nonRegionSpecificLocale = locales.find((l) => l.startsWith(locale.split("-")[0])); + if (nonRegionSpecificLocale) { + await setCurrentLocale(nonRegionSpecificLocale); + } } } catch (err) { console.warn("Could not set locale", err); diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index f820b578..ae0d1196 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -4,7 +4,7 @@ import { Character } from "../Entity/Character"; import type { GameScene } from "../Game/GameScene"; import type { PointInterface } from "../../Connexion/ConnexionModels"; import type { PlayerAnimationDirections } from "../Player/Animation"; -import type { Unsubscriber } from "svelte/store"; +import { get, Unsubscriber } from "svelte/store"; import type { ActivatableInterface } from "../Game/ActivatableInterface"; import type CancelablePromise from "cancelable-promise"; import LL from "../../i18n/i18n-svelte"; @@ -113,7 +113,7 @@ export class RemotePlayer extends Character implements ActivatableInterface { const actions: ActionsMenuAction[] = []; if (this.visitCardUrl) { actions.push({ - actionName: LL.woka.menu.businessCard(), + actionName: get(LL).woka.menu.businessCard(), protected: true, priority: 1, callback: () => { @@ -125,8 +125,8 @@ export class RemotePlayer extends Character implements ActivatableInterface { actions.push({ actionName: blackListManager.isBlackListed(this.userUuid) - ? LL.report.block.unblock() - : LL.report.block.block(), + ? get(LL).report.block.unblock() + : get(LL).report.block.block(), protected: true, priority: -1, style: "is-error", diff --git a/front/src/i18n/.gitignore b/front/src/i18n/.gitignore index 19f1f0f5..e2c9bddb 100644 --- a/front/src/i18n/.gitignore +++ b/front/src/i18n/.gitignore @@ -1,3 +1 @@ -i18n-svelte.ts -i18n-types.ts -i18n-util.ts \ No newline at end of file +i18n-*.ts diff --git a/front/src/i18n/de-DE/index.ts b/front/src/i18n/de-DE/index.ts index a72ecc1f..7c0dfb5b 100644 --- a/front/src/i18n/de-DE/index.ts +++ b/front/src/i18n/de-DE/index.ts @@ -16,8 +16,6 @@ import trigger from "./trigger"; const de_DE: Translation = { ...(en_US as Translation), - language: "Deutsch", - country: "Deutschland", audio, camera, chat, diff --git a/front/src/i18n/en-US/index.ts b/front/src/i18n/en-US/index.ts index b1a0e422..c0e018a2 100644 --- a/front/src/i18n/en-US/index.ts +++ b/front/src/i18n/en-US/index.ts @@ -14,8 +14,6 @@ import emoji from "./emoji"; import trigger from "./trigger"; const en_US: BaseTranslation = { - language: "English", - country: "United States", audio, camera, chat, diff --git a/front/src/i18n/formatters.ts b/front/src/i18n/formatters.ts index 00695fd6..da775944 100644 --- a/front/src/i18n/formatters.ts +++ b/front/src/i18n/formatters.ts @@ -1,8 +1,8 @@ -import type { AsyncFormattersInitializer } from "typesafe-i18n"; +import type { FormattersInitializer } from "typesafe-i18n"; import type { Locales, Formatters } from "./i18n-types"; // eslint-disable-next-line @typescript-eslint/require-await -export const initFormatters: AsyncFormattersInitializer = async () => { +export const initFormatters: FormattersInitializer = async () => { const formatters: Formatters = { // add your formatter functions here }; diff --git a/front/src/i18n/fr-FR/index.ts b/front/src/i18n/fr-FR/index.ts index 77acbb4a..c378a3bb 100644 --- a/front/src/i18n/fr-FR/index.ts +++ b/front/src/i18n/fr-FR/index.ts @@ -16,8 +16,6 @@ import trigger from "./trigger"; const fr_FR: Translation = { ...(en_US as Translation), - language: "Français", - country: "France", audio, camera, chat, diff --git a/front/src/i18n/locales.ts b/front/src/i18n/locales.ts index bff2f8e6..c18a1e5b 100644 --- a/front/src/i18n/locales.ts +++ b/front/src/i18n/locales.ts @@ -1,52 +1,44 @@ import { detectLocale, navigatorDetector, initLocalStorageDetector } from "typesafe-i18n/detectors"; import { FALLBACK_LOCALE } from "../Enum/EnvironmentVariable"; -import { initI18n, setLocale } from "./i18n-svelte"; +import { setLocale } from "./i18n-svelte"; import type { Locales } from "./i18n-types"; -import { baseLocale, getTranslationForLocale, locales } from "./i18n-util"; +import { baseLocale, locales } from "./i18n-util"; +import { loadLocaleAsync } from "./i18n-util.async"; -const fallbackLocale = FALLBACK_LOCALE || baseLocale; +const fallbackLocale = (FALLBACK_LOCALE || baseLocale) as Locales; const localStorageProperty = "language"; export const localeDetector = async () => { const exist = localStorage.getItem(localStorageProperty); - let detectedLocale: Locales = fallbackLocale as Locales; + let detectedLocale: Locales = fallbackLocale; if (exist) { const localStorageDetector = initLocalStorageDetector(localStorageProperty); - detectedLocale = detectLocale(fallbackLocale, locales, localStorageDetector) as Locales; + detectedLocale = detectLocale(fallbackLocale, locales, localStorageDetector); } else { - detectedLocale = detectLocale(fallbackLocale, locales, navigatorDetector) as Locales; + detectedLocale = detectLocale(fallbackLocale, locales, navigatorDetector); } - await initI18n(detectedLocale); + await setCurrentLocale(detectedLocale); }; -export const setCurrentLocale = (locale: Locales) => { +export const setCurrentLocale = async (locale: Locales) => { localStorage.setItem(localStorageProperty, locale); - setLocale(locale).catch(() => { - console.log("Cannot reload the locale!"); - }); + await loadLocaleAsync(locale); + setLocale(locale); }; -export type DisplayableLocale = { id: Locales; language: string; country: string }; +export const displayableLocales: { id: Locales; language: string; region: string }[] = locales.map((locale) => { + const [language, region] = locale.split("-"); -function getDisplayableLocales() { - const localesObject: DisplayableLocale[] = []; - locales.forEach((locale) => { - getTranslationForLocale(locale) - .then((translations) => { - localesObject.push({ - id: locale, - language: translations.language, - country: translations.country, - }); - }) - .catch((error) => { - console.log(error); - }); - }); + // backwards compatibility + if (!Intl.DisplayNames) { + return { id: locale, language, region }; + } - return localesObject; -} - -export const displayableLocales = getDisplayableLocales(); + return { + id: locale, + language: new Intl.DisplayNames(locale, { type: "language" }).of(language), + region: new Intl.DisplayNames(locale, { type: "region" }).of(region), + }; +}); diff --git a/front/src/i18n/zh-CN/index.ts b/front/src/i18n/zh-CN/index.ts index 23e2a24c..58816346 100644 --- a/front/src/i18n/zh-CN/index.ts +++ b/front/src/i18n/zh-CN/index.ts @@ -16,8 +16,6 @@ import trigger from "./trigger"; const zh_CN: Translation = { ...(en_US as Translation), - language: "中文", - country: "中国", audio, camera, chat, diff --git a/front/tsconfig.json b/front/tsconfig.json index edb99eb5..e5ac9819 100644 --- a/front/tsconfig.json +++ b/front/tsconfig.json @@ -8,7 +8,7 @@ "moduleResolution": "node", //"module": "CommonJS", "module": "ESNext", - "target": "ES2017", + "target": "ES2020", "declaration": false, "downlevelIteration": true, "jsx": "react", diff --git a/front/yarn.lock b/front/yarn.lock index 142e6c2c..f2b5e14d 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -2991,10 +2991,10 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -typesafe-i18n@^2.59.0: - version "2.59.0" - resolved "https://registry.yarnpkg.com/typesafe-i18n/-/typesafe-i18n-2.59.0.tgz#09a9a32e61711418d927a389fa52e1c06a5fa5c4" - integrity sha512-Qv3Mrwmb8b73VNzQDPHPECzwymdBRVyDiZ3w2qnp4c2iv/7TGuiJegNHT/l3MooEN7IPbSpc5tbXw2x3MbGtFg== +typesafe-i18n@^5.3.5: + version "5.3.5" + resolved "https://registry.yarnpkg.com/typesafe-i18n/-/typesafe-i18n-5.3.5.tgz#8561648a2be0df660404aa087993f3eee584cb87" + integrity sha512-ZjCCQ2lCyyvUThtxJblXoxwpr62paOjMRi/Kia1PSEh3gRfwPvEorABS0zTdF6lZ75MQXoz0WqtobChVjkO5mQ== typescript@*: version "4.3.2"