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 +