Prechádzať zdrojové kódy

feat: smarter preferred lang detection (#8205)

David Luzar 1 rok pred
rodič
commit
148b895f46

+ 6 - 23
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,

+ 1 - 1
excalidraw-app/components/LanguageList.tsx → 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();

+ 25 - 0
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;
+};

+ 15 - 0
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;
+};

+ 1 - 1
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;