fix(notifications): the English log formats its numbers in English; the ui/ contract sees msg() keys
A formatted value took the reader's language even when the notice was said in English for the
log. The platform-namespace contract matched only t("…"), so a domain key passed through msg()
in components/ui/ went unseen. Three comments pointed at localizeDetail's old home.
This commit is contained in:
@@ -19,8 +19,9 @@ const PLATFORM_NAMESPACES = new Set([
|
||||
]);
|
||||
|
||||
const files = readSources(__dirname, "./**/*.{ts,tsx}");
|
||||
// `t("ns.…")` and `t(\`ns.…\`)` — the namespace is everything before the first dot.
|
||||
const KEY = /\bt\(\s*["`]([A-Za-z]+)\./g;
|
||||
// `t("ns.…")` and `t(\`ns.…\`)`, and a notice's `msg("ns.…")` (the same key, said when shown) — the
|
||||
// namespace is everything before the first dot.
|
||||
const KEY = /\b(?:t|msg)\(\s*["`]([A-Za-z]+)\./g;
|
||||
|
||||
describe("components/ui/ speaks only platform i18n namespaces", () => {
|
||||
it("names no feature namespace", () => {
|
||||
@@ -37,5 +38,6 @@ describe("components/ui/ speaks only platform i18n namespaces", () => {
|
||||
|
||||
it("sees a feature namespace when one is used", () => {
|
||||
expect([...'t("plex.loading")'.matchAll(KEY)].map((m) => m[1])).toEqual(["plex"]);
|
||||
expect([...'msg("plex.loading")'.matchAll(KEY)].map((m) => m[1])).toEqual(["plex"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,8 +42,10 @@ export function sayNotif(text: NotifText, t: TFunction = i18n.t.bind(i18n)): str
|
||||
|
||||
function sayValue(v: NotifValue, t: TFunction): string | number {
|
||||
if (typeof v !== "object") return v;
|
||||
if ("number" in v) return formatNumber(v.number, i18n.language);
|
||||
if ("bytes" in v) return formatBytes(v.bytes, i18n.language);
|
||||
// In the language `t` speaks: a fixed one (the English log) names it, a component's follows i18n.
|
||||
const lang = (t as { lng?: string }).lng ?? i18n.language;
|
||||
if ("number" in v) return formatNumber(v.number, lang);
|
||||
if ("bytes" in v) return formatBytes(v.bytes, lang);
|
||||
return sayNotif(v, t);
|
||||
}
|
||||
|
||||
|
||||
@@ -643,7 +643,7 @@ export function DownloadAuth() {
|
||||
notify({ level: "success", message: msg("settings.downloadAuth.saved") });
|
||||
},
|
||||
// Validation errors (empty / not-a-cookies.txt) surface via the global error modal, which
|
||||
// localizes the structured { code: "cookies" } detail (see lib/api core localizeDetail); the
|
||||
// localizes the structured { code: "cookies" } detail (see lib/api/refusalText.ts localizeDetail); the
|
||||
// statuses it does not cover go to the app-wide reporter.
|
||||
meta: { failTitle: "settings.downloadAuth.failed" },
|
||||
});
|
||||
|
||||
@@ -7,7 +7,8 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { cleanup, render } from "@testing-library/react";
|
||||
|
||||
import { clear, events } from "../lib/log";
|
||||
import { notify, raw } from "../lib/notifications";
|
||||
import { msg, notify, raw } from "../lib/notifications";
|
||||
import i18n from "../i18n";
|
||||
import { reportError } from "../lib/errorDialog";
|
||||
import { createQueryClient } from "../lib/queryClient";
|
||||
import ErrorBoundary from "./ErrorBoundary";
|
||||
@@ -31,6 +32,27 @@ describe("client log wiring", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("records a notice in English whatever the reader's language, its numbers included", async () => {
|
||||
await i18n.changeLanguage("hu");
|
||||
try {
|
||||
notify({
|
||||
message: msg("settings.takeout.read", {
|
||||
name: raw("watch-history.json"),
|
||||
count: 12345,
|
||||
n: { number: 12345 },
|
||||
}),
|
||||
});
|
||||
} finally {
|
||||
await i18n.changeLanguage("en");
|
||||
}
|
||||
const english = i18n.getFixedT("en")("settings.takeout.read", {
|
||||
name: "watch-history.json",
|
||||
count: 12345,
|
||||
n: "12,345",
|
||||
});
|
||||
expect(logged("ui.notify").map((e) => e.msg)).toEqual([english]);
|
||||
});
|
||||
|
||||
it("records the blocking error dialog", () => {
|
||||
reportError("The server refused", "Couldn't save");
|
||||
expect(logged("ui.error_dialog")).toMatchObject([
|
||||
|
||||
Reference in New Issue
Block a user