mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-10-31 19:04:35 +01:00 
			
		
		
		
	feat: smarter preferred lang detection (#8205)
This commit is contained in:
		| @@ -1,5 +1,4 @@ | |||||||
| import polyfill from "../packages/excalidraw/polyfill"; | import polyfill from "../packages/excalidraw/polyfill"; | ||||||
| import LanguageDetector from "i18next-browser-languagedetector"; |  | ||||||
| import { useCallback, useEffect, useRef, useState } from "react"; | import { useCallback, useEffect, useRef, useState } from "react"; | ||||||
| import { trackEvent } from "../packages/excalidraw/analytics"; | import { trackEvent } from "../packages/excalidraw/analytics"; | ||||||
| import { getDefaultAppState } from "../packages/excalidraw/appState"; | import { getDefaultAppState } from "../packages/excalidraw/appState"; | ||||||
| @@ -22,7 +21,6 @@ import { useCallbackRefState } from "../packages/excalidraw/hooks/useCallbackRef | |||||||
| import { t } from "../packages/excalidraw/i18n"; | import { t } from "../packages/excalidraw/i18n"; | ||||||
| import { | import { | ||||||
|   Excalidraw, |   Excalidraw, | ||||||
|   defaultLang, |  | ||||||
|   LiveCollaborationTrigger, |   LiveCollaborationTrigger, | ||||||
|   TTDDialog, |   TTDDialog, | ||||||
|   TTDDialogTrigger, |   TTDDialogTrigger, | ||||||
| @@ -93,7 +91,7 @@ import { | |||||||
| import { AppMainMenu } from "./components/AppMainMenu"; | import { AppMainMenu } from "./components/AppMainMenu"; | ||||||
| import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; | import { AppWelcomeScreen } from "./components/AppWelcomeScreen"; | ||||||
| import { AppFooter } from "./components/AppFooter"; | 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 { useAtomWithInitialValue } from "../packages/excalidraw/jotai"; | ||||||
| import { appJotaiStore } from "./app-jotai"; | import { appJotaiStore } from "./app-jotai"; | ||||||
|  |  | ||||||
| @@ -121,6 +119,8 @@ import { | |||||||
|   youtubeIcon, |   youtubeIcon, | ||||||
| } from "../packages/excalidraw/components/icons"; | } from "../packages/excalidraw/components/icons"; | ||||||
| import { appThemeAtom, useHandleAppTheme } from "./useHandleAppTheme"; | import { appThemeAtom, useHandleAppTheme } from "./useHandleAppTheme"; | ||||||
|  | import { getPreferredLanguage } from "./app-language/language-detector"; | ||||||
|  | import { useAppLangCode } from "./app-language/language-state"; | ||||||
|  |  | ||||||
| polyfill(); | polyfill(); | ||||||
|  |  | ||||||
| @@ -172,11 +172,6 @@ if (window.self !== window.top) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| const languageDetector = new LanguageDetector(); |  | ||||||
| languageDetector.init({ |  | ||||||
|   languageUtils: {}, |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| const shareableLinkConfirmDialog = { | const shareableLinkConfirmDialog = { | ||||||
|   title: t("overwriteConfirm.modal.shareableLink.title"), |   title: t("overwriteConfirm.modal.shareableLink.title"), | ||||||
|   description: ( |   description: ( | ||||||
| @@ -322,19 +317,15 @@ const initializeScene = async (opts: { | |||||||
|   return { scene: null, isExternalScene: false }; |   return { scene: null, isExternalScene: false }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const detectedLangCode = languageDetector.detect() || defaultLang.code; |  | ||||||
| export const appLangCodeAtom = atom( |  | ||||||
|   Array.isArray(detectedLangCode) ? detectedLangCode[0] : detectedLangCode, |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| const ExcalidrawWrapper = () => { | const ExcalidrawWrapper = () => { | ||||||
|   const [errorMessage, setErrorMessage] = useState(""); |   const [errorMessage, setErrorMessage] = useState(""); | ||||||
|   const [langCode, setLangCode] = useAtom(appLangCodeAtom); |  | ||||||
|   const isCollabDisabled = isRunningInIframe(); |   const isCollabDisabled = isRunningInIframe(); | ||||||
|  |  | ||||||
|   const [appTheme, setAppTheme] = useAtom(appThemeAtom); |   const [appTheme, setAppTheme] = useAtom(appThemeAtom); | ||||||
|   const { editorTheme } = useHandleAppTheme(); |   const { editorTheme } = useHandleAppTheme(); | ||||||
|  |  | ||||||
|  |   const [langCode, setLangCode] = useAppLangCode(); | ||||||
|  |  | ||||||
|   // initial state |   // initial state | ||||||
|   // --------------------------------------------------------------------------- |   // --------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| @@ -490,11 +481,7 @@ const ExcalidrawWrapper = () => { | |||||||
|         if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) { |         if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) { | ||||||
|           const localDataState = importFromLocalStorage(); |           const localDataState = importFromLocalStorage(); | ||||||
|           const username = importUsernameFromLocalStorage(); |           const username = importUsernameFromLocalStorage(); | ||||||
|           let langCode = languageDetector.detect() || defaultLang.code; |           setLangCode(getPreferredLanguage()); | ||||||
|           if (Array.isArray(langCode)) { |  | ||||||
|             langCode = langCode[0]; |  | ||||||
|           } |  | ||||||
|           setLangCode(langCode); |  | ||||||
|           excalidrawAPI.updateScene({ |           excalidrawAPI.updateScene({ | ||||||
|             ...localDataState, |             ...localDataState, | ||||||
|             storeAction: StoreAction.UPDATE, |             storeAction: StoreAction.UPDATE, | ||||||
| @@ -595,10 +582,6 @@ const ExcalidrawWrapper = () => { | |||||||
|     }; |     }; | ||||||
|   }, [excalidrawAPI]); |   }, [excalidrawAPI]); | ||||||
|  |  | ||||||
|   useEffect(() => { |  | ||||||
|     languageDetector.cacheUserLanguage(langCode); |  | ||||||
|   }, [langCode]); |  | ||||||
|  |  | ||||||
|   const onChange = ( |   const onChange = ( | ||||||
|     elements: readonly OrderedExcalidrawElement[], |     elements: readonly OrderedExcalidrawElement[], | ||||||
|     appState: AppState, |     appState: AppState, | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import { useSetAtom } from "jotai"; | import { useSetAtom } from "jotai"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| import { appLangCodeAtom } from "../App"; |  | ||||||
| import { useI18n, languages } from "../../packages/excalidraw/i18n"; | import { useI18n, languages } from "../../packages/excalidraw/i18n"; | ||||||
|  | import { appLangCodeAtom } from "./language-state"; | ||||||
| 
 | 
 | ||||||
| export const LanguageList = ({ style }: { style?: React.CSSProperties }) => { | export const LanguageList = ({ style }: { style?: React.CSSProperties }) => { | ||||||
|   const { t, langCode } = useI18n(); |   const { t, langCode } = useI18n(); | ||||||
							
								
								
									
										25
									
								
								excalidraw-app/app-language/language-detector.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								excalidraw-app/app-language/language-detector.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								excalidraw-app/app-language/language-state.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								excalidraw-app/app-language/language-state.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||||
|  | }; | ||||||
| @@ -6,7 +6,7 @@ import { | |||||||
| import type { Theme } from "../../packages/excalidraw/element/types"; | import type { Theme } from "../../packages/excalidraw/element/types"; | ||||||
| import { MainMenu } from "../../packages/excalidraw/index"; | import { MainMenu } from "../../packages/excalidraw/index"; | ||||||
| import { isExcalidrawPlusSignedUser } from "../app_constants"; | import { isExcalidrawPlusSignedUser } from "../app_constants"; | ||||||
| import { LanguageList } from "./LanguageList"; | import { LanguageList } from "../app-language/LanguageList"; | ||||||
|  |  | ||||||
| export const AppMainMenu: React.FC<{ | export const AppMainMenu: React.FC<{ | ||||||
|   onCollabDialogOpen: () => any; |   onCollabDialogOpen: () => any; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 David Luzar
					David Luzar