fix(webui): sync localized preboot copy
This commit is contained in:
+18
-11
@@ -90,6 +90,17 @@
|
||||
} catch {}
|
||||
})();
|
||||
</script>
|
||||
<title>nanobot</title>
|
||||
</head>
|
||||
<body class="bg-background text-foreground antialiased">
|
||||
<div id="root">
|
||||
<div class="boot-splash">
|
||||
<div class="boot-splash-inner">
|
||||
<span class="boot-dot" aria-hidden="true"></span>
|
||||
<span data-boot-copy>Loading nanobot…</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var localeKey = "nanobot.locale";
|
||||
@@ -122,6 +133,10 @@
|
||||
boot: "Cargando nanobot…",
|
||||
description: "Interfaz web de nanobot: conversa con tu espacio de trabajo de nanobot."
|
||||
},
|
||||
"pt-BR": {
|
||||
boot: "Carregando nanobot…",
|
||||
description: "Interface web do nanobot — converse com o seu workspace do nanobot."
|
||||
},
|
||||
vi: {
|
||||
boot: "Đang tải nanobot…",
|
||||
description: "Giao diện web nanobot — trò chuyện với workspace nanobot của bạn."
|
||||
@@ -149,6 +164,9 @@
|
||||
) {
|
||||
return "zh-TW";
|
||||
}
|
||||
if (lower === "pt" || lower.indexOf("pt-") === 0) {
|
||||
return "pt-BR";
|
||||
}
|
||||
var base = lower.split("-")[0];
|
||||
return copy[base] ? base : "en";
|
||||
}
|
||||
@@ -170,17 +188,6 @@
|
||||
} catch {}
|
||||
})();
|
||||
</script>
|
||||
<title>nanobot</title>
|
||||
</head>
|
||||
<body class="bg-background text-foreground antialiased">
|
||||
<div id="root">
|
||||
<div class="boot-splash">
|
||||
<div class="boot-splash-inner">
|
||||
<span class="boot-dot" aria-hidden="true"></span>
|
||||
<span data-boot-copy>Loading nanobot…</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
"webuiDefaultAccess": "Acesso padrão",
|
||||
"cliAppsCatalog": "Catálogo",
|
||||
"cliAppsFilter": "Filtro",
|
||||
"engine": "Engine",
|
||||
"engine": "Motor",
|
||||
"logs": "Logs",
|
||||
"diagnostics": "Diagnóstico",
|
||||
"contextWindow": "Janela de contexto",
|
||||
@@ -588,7 +588,7 @@
|
||||
"editTitle": "Editar automação",
|
||||
"save": "Salvar",
|
||||
"deleteTitle": "Excluir automação",
|
||||
"deleteDescription": "Isso remove {{name}} do store de cron. As mensagens anteriores da conversa permanecem na sessão.",
|
||||
"deleteDescription": "Isso remove {{name}} do armazenamento do cron. As mensagens anteriores da conversa permanecem na sessão.",
|
||||
"cancel": "Cancelar",
|
||||
"status": {
|
||||
"system": "Sistema",
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { runInNewContext } from "node:vm";
|
||||
|
||||
import { act, render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
@@ -5,7 +9,11 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
|
||||
import { ThreadComposer } from "@/components/thread/ThreadComposer";
|
||||
import { resources } from "@/i18n";
|
||||
import { LOCALE_STORAGE_KEY, resolveInitialLocale } from "@/i18n/config";
|
||||
import {
|
||||
LOCALE_STORAGE_KEY,
|
||||
resolveInitialLocale,
|
||||
supportedLocales,
|
||||
} from "@/i18n/config";
|
||||
|
||||
const QUICK_ACTION_KEYS = ["plan", "analyze", "brainstorm", "code", "summarize", "more"];
|
||||
const IMAGE_QUICK_ACTION_KEYS = ["icon", "sticker", "poster", "product", "portrait", "edit"];
|
||||
@@ -128,6 +136,46 @@ const LOCALIZED_WORKSPACE_COPY_KEYS = [
|
||||
"workspace.dialog.usePath",
|
||||
"workspace.dialog.absolutePathRequired",
|
||||
];
|
||||
const INDEX_HTML = readFileSync(resolve(process.cwd(), "index.html"), "utf8");
|
||||
const PREBOOT_SCRIPT = INDEX_HTML.match(
|
||||
/<script>\s*(\(function \(\) \{\s*var localeKey = "nanobot\.locale";[\s\S]*?\}\)\(\);)\s*<\/script>/,
|
||||
)?.[1];
|
||||
const BOOT_COPY_MARKUP = '<span data-boot-copy>Loading nanobot…</span>';
|
||||
|
||||
function runPrebootLocale(storedLocale: string) {
|
||||
if (!PREBOOT_SCRIPT) throw new Error("Could not find the preboot locale script in index.html");
|
||||
|
||||
const documentElement = { lang: "" };
|
||||
const meta = {
|
||||
content: "",
|
||||
setAttribute(name: string, value: string) {
|
||||
if (name === "content") this.content = value;
|
||||
},
|
||||
};
|
||||
const boot = { textContent: "" };
|
||||
|
||||
runInNewContext(PREBOOT_SCRIPT, {
|
||||
localStorage: {
|
||||
getItem: (key: string) => key === LOCALE_STORAGE_KEY ? storedLocale : null,
|
||||
},
|
||||
navigator: { languages: [], language: "en" },
|
||||
document: {
|
||||
documentElement,
|
||||
querySelector: (selector: string) => {
|
||||
if (selector === '[data-i18n-meta="description"]') return meta;
|
||||
if (selector === "[data-boot-copy]") return boot;
|
||||
return null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
lang: documentElement.lang,
|
||||
boot: boot.textContent,
|
||||
description: meta.content,
|
||||
};
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
@@ -156,6 +204,39 @@ function interpolationKeys(value: unknown): string[] {
|
||||
}
|
||||
|
||||
describe("webui i18n", () => {
|
||||
it("runs preboot localization after the splash copy exists", () => {
|
||||
expect(INDEX_HTML.indexOf(BOOT_COPY_MARKUP)).toBeGreaterThan(-1);
|
||||
expect(INDEX_HTML.indexOf(PREBOOT_SCRIPT ?? "")).toBeGreaterThan(
|
||||
INDEX_HTML.indexOf(BOOT_COPY_MARKUP),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps preboot copy aligned with every registered locale", () => {
|
||||
for (const { code } of supportedLocales) {
|
||||
const result = runPrebootLocale(code);
|
||||
const expected = resources[code].common.app;
|
||||
|
||||
expect({ code, ...result }).toEqual({
|
||||
code,
|
||||
lang: code,
|
||||
boot: expected.loading.boot,
|
||||
description: expected.meta.description,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("normalizes Portuguese locales before the app bundle loads", () => {
|
||||
const expected = resources["pt-BR"].common.app;
|
||||
|
||||
for (const locale of ["pt", "pt-PT"]) {
|
||||
expect(runPrebootLocale(locale)).toEqual({
|
||||
lang: "pt-BR",
|
||||
boot: expected.loading.boot,
|
||||
description: expected.meta.description,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("defaults to English until the user chooses another language", () => {
|
||||
localStorage.removeItem(LOCALE_STORAGE_KEY);
|
||||
expect(resolveInitialLocale()).toBe("en");
|
||||
|
||||
Reference in New Issue
Block a user