import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { createFauxCore } from "@earendil-works/pi-ai"; import { createTeacherAgent } from "../src/agent/teacher-agent.ts"; import { createPracticeAgent } from "../src/agent/teacher-agent.ts"; import { sanitizeUserSettings } from "../src/core/settings.ts"; import { parseGeneralSettingsBody } from "../src/server/settings-body.ts"; import { countLabel, dictationUrl, resolveLanguage, systemLanguage, translate } from "../src/core/i18n.ts"; import { sessionMarkdown } from "../src/core/session-markdown.ts"; import type { LhMessage } from "../src/core/native-messages.ts"; import { I18nProvider, LanguageSelect } from "../components/i18n.tsx"; import { KnowledgeSummary } from "../components/minimal-home.tsx"; import { AbortTurnBody } from "../components/abort-turn-dialog.tsx"; import en from "../src/core/locales/en.json"; import ru from "../src/core/locales/ru.json"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); const native = require("../electron/language.cjs"); describe("language preferences", () => { it("uses the preferred browser/system language and English for unsupported languages", () => { assert.equal(systemLanguage("ru-RU,ru;q=0.9,en;q=0.8"), "ru"); assert.equal(systemLanguage("en;q=0.4,ru;q=0.9"), "ru"); assert.equal(systemLanguage("ru;q=0,en;q=1"), "en"); assert.equal(systemLanguage(["de-DE", "ru"]), "en"); assert.equal(systemLanguage(undefined), "en"); assert.equal(resolveLanguage("en", "ru-RU"), "en"); assert.equal(resolveLanguage("ru", "en-US"), "ru"); }); it("validates both independent preferences without changing existing general options", () => { const general = { autoUpdate: false, language: "ru", sttLanguage: "en" }; assert.deepEqual(parseGeneralSettingsBody(general), { ok: true, settings: general }); assert.deepEqual(sanitizeUserSettings({ version: 1, general })?.general, general); assert.deepEqual(parseGeneralSettingsBody({}), { ok: true, settings: {} }); assert.equal(parseGeneralSettingsBody({ language: "auto" }).ok, false); assert.equal(parseGeneralSettingsBody({ sttLanguage: "fr" }).ok, false); assert.deepEqual(sanitizeUserSettings({ version: 1, general: { language: null, sttLanguage: "fr", autoUpdate: false } })?.general, { autoUpdate: false }); }); it("pins an explicit supported language in the STT provider request", () => { for (const language of ["en", "ru"] as const) { const url = new URL(dictationUrl(language)); assert.equal(url.protocol, "wss:"); assert.equal(url.hostname, "api.deepgram.com"); assert.equal(url.searchParams.get("language"), language); assert.equal(url.searchParams.get("model"), "nova-3"); assert.equal(url.searchParams.get("encoding"), "linear16"); assert.equal(url.searchParams.get("sample_rate"), "16000"); } }); }); describe("translation quality and rendering", () => { it("keeps catalogs complete and parameter names valid", () => { assert.deepEqual(Object.keys(en).sort(), Object.keys(ru).sort()); for (const [key, value] of Object.entries(ru)) { const variants = typeof value === "string" ? [value] : Object.values(value); assert(variants.length > 0, key); for (const text of variants) { assert(text.trim(), key); for (const placeholder of text.matchAll(/\{(\w+)\}/g)) assert(key.includes(placeholder[0]), `${key}: unexpected ${placeholder[0]}`); } } }); it("uses Russian plural forms including 11 and 21 while retaining English wording", () => { for (const [count, expected] of [[1, "1 утверждение"], [2, "2 утверждения"], [5, "5 утверждений"], [11, "11 утверждений"], [21, "21 утверждение"], [22, "22 утверждения"], [25, "25 утверждений"]] as const) { assert.equal(countLabel("ru", count, "proposition", "propositions"), expected); } assert.equal(countLabel("en", 1, "proposition", "propositions"), "1 proposition"); assert.equal(countLabel("en", 5, "proposition", "propositions"), "5 propositions"); assert.equal(translate("en", "· read {count} pages", { count: 1 }), "· read 1 page"); }); it("localizes canonical protocol errors without changing provider diagnostics or parameters", () => { assert.equal(translate("ru", "No OpenAI key is set — add one in Settings → Keys"), "Ключ OpenAI не задан — добавьте его в разделе Настройки → Провайдеры"); assert.equal(translate("ru", "Save failed (503)"), "Не удалось сохранить (503)"); assert.equal(translate("ru", JSON.stringify({ error: "Unknown chat" })), "Занятие не найдено"); assert.equal(translate("ru", "Provider diagnostic 9f1c"), "Provider diagnostic 9f1c"); assert.equal(translate("ru", "Select node {v0}", { v0: "English