From a3662890abdcda4dea26ee67a7bb10ff24ea5813 Mon Sep 17 00:00:00 2001 From: jiezhengj <100029752+jiezhengj@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:49:47 +0800 Subject: [PATCH] feat(i18n): add English and Chinese UI localization Detect the system locale, allow runtime language switching, and localize renderer UI, native menus, updater dialogs, and installer metadata. --- .github/workflows/ci.yml | 3 + app/global-error.tsx | 22 +- app/layout.tsx | 6 + app/page.tsx | 7 +- components/DesktopAppBanner.tsx | 12 +- components/Editor.tsx | 18 +- components/ExportDialog.tsx | 80 ++-- components/I18nProvider.tsx | 84 ++++ components/ImportTranscriptOption.tsx | 25 +- components/LogoLoader.tsx | 4 +- components/ModelSelector.tsx | 11 +- components/SettingsMenu.tsx | 56 ++- components/SocialLinks.tsx | 12 +- components/SpeakerLabel.tsx | 62 +-- components/Timeline.tsx | 53 +-- components/TopBar.tsx | 4 +- components/TranscriptPanel.tsx | 56 +-- components/TranscriptToolsMenu.tsx | 35 +- components/UploadScreen.tsx | 64 +-- electron/locale.ts | 108 +++++ electron/main.ts | 11 + electron/menu.ts | 79 ++-- electron/preload.ts | 4 + electron/updater.ts | 9 +- hooks/useDesktopMenu.ts | 17 +- lib/i18n.ts | 542 ++++++++++++++++++++++++++ package.json | 6 + tests/i18n-test.ts | 48 +++ types/rescript-desktop.d.ts | 2 + 29 files changed, 1186 insertions(+), 254 deletions(-) create mode 100644 components/I18nProvider.tsx create mode 100644 electron/locale.ts create mode 100644 lib/i18n.ts create mode 100644 tests/i18n-test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54b109b..4113765 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,9 @@ jobs: - name: Typecheck (electron) run: npm run typecheck:electron + - name: UI localization tests + run: npm run test:i18n + - name: Build Next.js run: npm run build diff --git a/app/global-error.tsx b/app/global-error.tsx index 3e5b1d4..91acffd 100644 --- a/app/global-error.tsx +++ b/app/global-error.tsx @@ -2,6 +2,12 @@ import { useEffect } from "react"; import { reportError } from "@/lib/sentry"; +import { + loadUiLocalePreference, + resolveUiLocale, + systemLanguages, + translate, +} from "@/lib/i18n"; /** * Last-resort boundary for a crash that escaped the editor's own error handling. @@ -18,12 +24,17 @@ export default function GlobalError({ error: Error & { digest?: string }; unstable_retry: () => void; }) { + const locale = resolveUiLocale( + loadUiLocalePreference(), + systemLanguages() + ); + const t = (key: Parameters[1]) => translate(locale, key); useEffect(() => { reportError(error, "render"); }, [error]); return ( - + - Rescript — something went wrong + {t("globalError.title")}

- Something went wrong + {t("globalError.heading")}

- The editor hit an unexpected error. Your saved projects are still on - this device — reloading should bring them back. + {t("globalError.body")}

diff --git a/app/layout.tsx b/app/layout.tsx index 4bb0655..b3b52ab 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -64,6 +64,7 @@ export const metadata: Metadata = { /** Apply stored appearance before paint to avoid a light→dark flash. */ const appearanceBootScript = `(function(){try{if(localStorage.getItem("rescript.appearance")==="dark")document.documentElement.classList.add("dark")}catch(e){}})();`; +const localeBootScript = `(function(){try{var p=localStorage.getItem("rescript.ui-locale")||"system";var l=p;if(p==="system"){var a=navigator.languages&&navigator.languages.length?navigator.languages:[navigator.language];l="en";for(var i=0;i +