fix(ui): sync document locale metadata

This commit is contained in:
Vincent Koc
2026-07-28 09:39:21 +02:00
parent 2f5c70ec24
commit 4e8c2c5248
2 changed files with 39 additions and 0 deletions
+12
View File
@@ -19,6 +19,16 @@ type LocaleTranslationLoader = (locale: Locale) => Promise<TranslationMap | null
export { SUPPORTED_LOCALES, isSupportedLocale };
const RTL_LOCALES = new Set<Locale>(["ar", "fa"]);
function syncDocumentLocale(locale: Locale): void {
if (typeof document === "undefined") {
return;
}
document.documentElement.lang = locale;
document.documentElement.dir = RTL_LOCALES.has(locale) ? "rtl" : "ltr";
}
class I18nManager {
private locale: Locale = DEFAULT_LOCALE;
private translations: Partial<Record<Locale, TranslationMap>> = { [DEFAULT_LOCALE]: en };
@@ -75,6 +85,7 @@ class I18nManager {
const initialLocale = this.resolveInitialLocale();
if (initialLocale === DEFAULT_LOCALE) {
this.locale = DEFAULT_LOCALE;
syncDocumentLocale(DEFAULT_LOCALE);
return;
}
// Use the normal locale setter so startup locale loading follows the same
@@ -128,6 +139,7 @@ class I18nManager {
}
this.pendingLocale = null;
this.locale = locale;
syncDocumentLocale(locale);
this.persistLocale(locale);
this.notify();
}
+27
View File
@@ -57,6 +57,12 @@ async function importFreshTranslate() {
);
}
function stubDocumentLocaleMetadata() {
const documentElement = { lang: "", dir: "" };
vi.stubGlobal("document", { documentElement } as unknown as Document);
return documentElement;
}
describe("i18n", () => {
function flatten(value: Record<string, string | Record<string, unknown>>, prefix = ""): string[] {
return Object.entries(value).flatMap(([key, nested]) => {
@@ -135,6 +141,27 @@ describe("i18n", () => {
expect(fresh.t("common.health")).toBe("健康状况");
});
it("syncs canonical document locale metadata on startup", async () => {
const documentElement = stubDocumentLocaleMetadata();
vi.stubGlobal("navigator", { language: "fa-IR" } as Navigator);
localStorage.removeItem("openclaw.i18n.locale");
const fresh = await importFreshTranslate();
await vi.waitFor(() => expect(fresh.i18n.getLocale()).toBe("fa"));
expect(documentElement).toEqual({ lang: "fa", dir: "rtl" });
});
it("syncs document locale metadata when the locale changes", async () => {
const documentElement = stubDocumentLocaleMetadata();
await translate.i18n.setLocale("ar");
expect(documentElement).toEqual({ lang: "ar", dir: "rtl" });
await translate.i18n.setLocale("de");
expect(documentElement).toEqual({ lang: "de", dir: "ltr" });
});
it.each([
["zh-Hant", "zh-TW"],
["zh-Hant-TW", "zh-TW"],