diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index ec091d95b..a11ea59b3 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -1,5 +1,4 @@ import polyfill from "../packages/excalidraw/polyfill"; -import LanguageDetector from "i18next-browser-languagedetector"; import { useCallback, useEffect, useRef, useState } from "react"; import { trackEvent } from "../packages/excalidraw/analytics"; import { getDefaultAppState } from "../packages/excalidraw/appState"; @@ -22,7 +21,6 @@ import { useCallbackRefState } from "../packages/excalidraw/hooks/useCallbackRef import { t } from "../packages/excalidraw/i18n"; import { Excalidraw, - defaultLang, LiveCollaborationTrigger, TTDDialog, TTDDialogTrigger, @@ -93,7 +91,7 @@ import { import { AppMainMenu } from "./components/AppMainMenu"; import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; import { AppFooter } from "./components/AppFooter"; -import { atom, Provider, useAtom, useAtomValue } from "jotai"; +import { Provider, useAtom, useAtomValue } from "jotai"; import { useAtomWithInitialValue } from "../packages/excalidraw/jotai"; import { appJotaiStore } from "./app-jotai"; @@ -121,6 +119,8 @@ import { youtubeIcon, } from "../packages/excalidraw/components/icons"; import { appThemeAtom, useHandleAppTheme } from "./useHandleAppTheme"; +import { getPreferredLanguage } from "./app-language/language-detector"; +import { useAppLangCode } from "./app-language/language-state"; polyfill(); @@ -172,11 +172,6 @@ if (window.self !== window.top) { } } -const languageDetector = new LanguageDetector(); -languageDetector.init({ - languageUtils: {}, -}); - const shareableLinkConfirmDialog = { title: t("overwriteConfirm.modal.shareableLink.title"), description: ( @@ -322,19 +317,15 @@ const initializeScene = async (opts: { return { scene: null, isExternalScene: false }; }; -const detectedLangCode = languageDetector.detect() || defaultLang.code; -export const appLangCodeAtom = atom( - Array.isArray(detectedLangCode) ? detectedLangCode[0] : detectedLangCode, -); - const ExcalidrawWrapper = () => { const [errorMessage, setErrorMessage] = useState(""); - const [langCode, setLangCode] = useAtom(appLangCodeAtom); const isCollabDisabled = isRunningInIframe(); const [appTheme, setAppTheme] = useAtom(appThemeAtom); const { editorTheme } = useHandleAppTheme(); + const [langCode, setLangCode] = useAppLangCode(); + // initial state // --------------------------------------------------------------------------- @@ -490,11 +481,7 @@ const ExcalidrawWrapper = () => { if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) { const localDataState = importFromLocalStorage(); const username = importUsernameFromLocalStorage(); - let langCode = languageDetector.detect() || defaultLang.code; - if (Array.isArray(langCode)) { - langCode = langCode[0]; - } - setLangCode(langCode); + setLangCode(getPreferredLanguage()); excalidrawAPI.updateScene({ ...localDataState, storeAction: StoreAction.UPDATE, @@ -595,10 +582,6 @@ const ExcalidrawWrapper = () => { }; }, [excalidrawAPI]); - useEffect(() => { - languageDetector.cacheUserLanguage(langCode); - }, [langCode]); - const onChange = ( elements: readonly OrderedExcalidrawElement[], appState: AppState, diff --git a/excalidraw-app/components/LanguageList.tsx b/excalidraw-app/app-language/LanguageList.tsx similarity index 93% rename from excalidraw-app/components/LanguageList.tsx rename to excalidraw-app/app-language/LanguageList.tsx index e4065f589..08142b1f6 100644 --- a/excalidraw-app/components/LanguageList.tsx +++ b/excalidraw-app/app-language/LanguageList.tsx @@ -1,7 +1,7 @@ import { useSetAtom } from "jotai"; import React from "react"; -import { appLangCodeAtom } from "../App"; import { useI18n, languages } from "../../packages/excalidraw/i18n"; +import { appLangCodeAtom } from "./language-state"; export const LanguageList = ({ style }: { style?: React.CSSProperties }) => { const { t, langCode } = useI18n(); diff --git a/excalidraw-app/app-language/language-detector.ts b/excalidraw-app/app-language/language-detector.ts new file mode 100644 index 000000000..acf77d631 --- /dev/null +++ b/excalidraw-app/app-language/language-detector.ts @@ -0,0 +1,25 @@ +import LanguageDetector from "i18next-browser-languagedetector"; +import { defaultLang, languages } from "../../packages/excalidraw"; + +export const languageDetector = new LanguageDetector(); + +languageDetector.init({ + languageUtils: {}, +}); + +export const getPreferredLanguage = () => { + const detectedLanguages = languageDetector.detect(); + + const detectedLanguage = Array.isArray(detectedLanguages) + ? detectedLanguages[0] + : detectedLanguages; + + const initialLanguage = + (detectedLanguage + ? // region code may not be defined if user uses generic preferred language + // (e.g. chinese vs instead of chienese-simplified) + languages.find((lang) => lang.code.startsWith(detectedLanguage))?.code + : null) || defaultLang.code; + + return initialLanguage; +}; diff --git a/excalidraw-app/app-language/language-state.ts b/excalidraw-app/app-language/language-state.ts new file mode 100644 index 000000000..5198a8ea8 --- /dev/null +++ b/excalidraw-app/app-language/language-state.ts @@ -0,0 +1,15 @@ +import { atom, useAtom } from "jotai"; +import { useEffect } from "react"; +import { getPreferredLanguage, languageDetector } from "./language-detector"; + +export const appLangCodeAtom = atom(getPreferredLanguage()); + +export const useAppLangCode = () => { + const [langCode, setLangCode] = useAtom(appLangCodeAtom); + + useEffect(() => { + languageDetector.cacheUserLanguage(langCode); + }, [langCode]); + + return [langCode, setLangCode] as const; +}; diff --git a/excalidraw-app/components/AppMainMenu.tsx b/excalidraw-app/components/AppMainMenu.tsx index 9f99d03de..eb3f24caf 100644 --- a/excalidraw-app/components/AppMainMenu.tsx +++ b/excalidraw-app/components/AppMainMenu.tsx @@ -6,7 +6,7 @@ import { import type { Theme } from "../../packages/excalidraw/element/types"; import { MainMenu } from "../../packages/excalidraw/index"; import { isExcalidrawPlusSignedUser } from "../app_constants"; -import { LanguageList } from "./LanguageList"; +import { LanguageList } from "../app-language/LanguageList"; export const AppMainMenu: React.FC<{ onCollabDialogOpen: () => any;