From 0302c466e85f3ffe295af16e77f595bfe04123a6 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 2 Oct 2026 23:42:32 +0200 Subject: [PATCH] fix(i18n): a failed RSS poll over one channel reads as one, and keeps its stops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rss_all_failed is a plural pair counted on the raw total (the values are formatted strings, which pick no plural form); both sentences end with a stop. The Hungarian 'show N more' uses the house's megjelenítése. --- frontend/src/i18n/locales/en/scheduler.json | 3 ++- frontend/src/i18n/locales/hu/plex.json | 4 ++-- frontend/src/i18n/locales/hu/scheduler.json | 3 ++- frontend/src/lib/jobResult.dom.test.ts | 13 ++++++++++++- frontend/src/lib/jobResult.ts | 5 ++++- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/frontend/src/i18n/locales/en/scheduler.json b/frontend/src/i18n/locales/en/scheduler.json index 1f81ed2a..b9ce0990 100644 --- a/frontend/src/i18n/locales/en/scheduler.json +++ b/frontend/src/i18n/locales/en/scheduler.json @@ -190,7 +190,8 @@ "busy": "another sync is running" }, "failure": { - "rss_all_failed": "The RSS feed answered for none of the {{total}} channels — the first: {{channel}}, {{cause}}. Read through the API instead: {{read}}" + "rss_all_failed_one": "The RSS feed did not answer for the one channel, {{channel}}: {{cause}}. Read through the API instead: {{read}}.", + "rss_all_failed_other": "The RSS feed answered for none of the {{total}} channels — the first: {{channel}}, {{cause}}. Read through the API instead: {{read}}." }, "rssCause": { "http": "HTTP {{status}}", diff --git a/frontend/src/i18n/locales/hu/plex.json b/frontend/src/i18n/locales/hu/plex.json index 4ada8f17..eeb79d5f 100644 --- a/frontend/src/i18n/locales/hu/plex.json +++ b/frontend/src/i18n/locales/hu/plex.json @@ -277,8 +277,8 @@ "audience": "Közönség", "keywords": "Kulcsszavak", "crew": "Stáb", - "crewMore_one": "Még {{count}} mutatása", - "crewMore_other": "Még {{count}} mutatása", + "crewMore_one": "További {{count}} megjelenítése", + "crewMore_other": "További {{count}} megjelenítése", "crewLess": "Kevesebb", "filterValue": "Szűrés erre: „{{name}}”", "facts": { diff --git a/frontend/src/i18n/locales/hu/scheduler.json b/frontend/src/i18n/locales/hu/scheduler.json index c6f79124..8daa8884 100644 --- a/frontend/src/i18n/locales/hu/scheduler.json +++ b/frontend/src/i18n/locales/hu/scheduler.json @@ -190,7 +190,8 @@ "busy": "egy másik szinkron fut" }, "failure": { - "rss_all_failed": "Az RSS-hírcsatorna egyik csatornánál sem válaszolt ({{total}}) — az első: {{channel}}, {{cause}}. Az API-n át beolvasva helyette: {{read}}" + "rss_all_failed_one": "Az RSS-hírcsatorna az egyetlen csatornánál sem válaszolt ({{channel}}): {{cause}}. Az API-n át beolvasva helyette: {{read}}.", + "rss_all_failed_other": "Az RSS-hírcsatorna egyik csatornánál sem válaszolt ({{total}}) — az első: {{channel}}, {{cause}}. Az API-n át beolvasva helyette: {{read}}." }, "rssCause": { "http": "HTTP {{status}}", diff --git a/frontend/src/lib/jobResult.dom.test.ts b/frontend/src/lib/jobResult.dom.test.ts index 01044f74..9ef045d4 100644 --- a/frontend/src/lib/jobResult.dom.test.ts +++ b/frontend/src/lib/jobResult.dom.test.ts @@ -81,13 +81,24 @@ describe("a failed run", () => { const t = i18n.getFixedT("hu"); expect(jobRunText(t, run, "hu").body).toBe( "Az RSS-hírcsatorna egyik csatornánál sem válaszolt (1399) — az első: UCx, HTTP 404. " + - "Az API-n át beolvasva helyette: 0", + "Az API-n át beolvasva helyette: 0.", ); expect(jobFailureText(t, { ...run.result, cause: "transport" }, "hu")).toContain( "UCx, nem érhető el.", ); }); + // E31 S5: the server raises it for an instance with one subscription too ("none of the 1 channels"). + it("reads one channel as one, and keeps its stop", () => { + const t = i18n.getFixedT("en"); + expect(jobFailureText(t, { ...run.result, total: 1 }, "en")).toBe( + "The RSS feed did not answer for the one channel, UCx: HTTP 404. Read through the API instead: 0.", + ); + expect(jobFailureText(t, run.result, "en")).toMatch( + /^The RSS feed answered for none of the 1,399 channels .*: 0\.$/, + ); + }); + it("keeps any other failure's own text", () => { const t = i18n.getFixedT("hu"); expect(jobRunText(t, { ...run, summary: "Plex returned 500", result: null }, "hu").body).toBe( diff --git a/frontend/src/lib/jobResult.ts b/frontend/src/lib/jobResult.ts index 823ccfac..ff22e8ef 100644 --- a/frontend/src/lib/jobResult.ts +++ b/frontend/src/lib/jobResult.ts @@ -48,7 +48,10 @@ export function jobFailureText(t: TFunction, result: unknown, lang: string): str for (const [k, v] of Object.entries(params)) values[k] = typeof v === "number" ? formatNumber(v, lang) : String(v); if (typeof params.cause === "string") values.cause = rssCause(t, params.cause); - return t(`scheduler.failure.${failure}`, { ...values, defaultValue: "" }) || null; + // A failure counted over `total` reads as a plural pair: i18next picks the form from a NUMERIC + // count, and the formatted values above are strings ("1 399" would pick none). + const count = typeof params.total === "number" ? params.total : undefined; + return t(`scheduler.failure.${failure}`, { ...values, count, defaultValue: "" }) || null; } /** Why a feed did not answer, from the poll's reason code (`http_404`, `transport`, …). */