From 45d5b5ef519b1f5984c404841b6b3f88e9eeea6b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 14 Aug 2026 06:26:21 -0700 Subject: [PATCH] fix(ui): empty-string i18n params rendered the raw {placeholder} (#123474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit t() interpolation used || so a provided empty-string param fell through to the visible {placeholder} fallback meant for missing params — e.g. the devices page rendered 'Bound to {node}' whenever an agent binding was the empty string. ?? keeps the missing-param debugging aid and renders provided empties as empty. --- ui/src/i18n/lib/translate.ts | 4 +++- ui/src/i18n/test/translate.test.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/src/i18n/lib/translate.ts b/ui/src/i18n/lib/translate.ts index 59c7b60dfa5b..81b4218150d9 100644 --- a/ui/src/i18n/lib/translate.ts +++ b/ui/src/i18n/lib/translate.ts @@ -264,7 +264,9 @@ class I18nManager { } if (params) { - return value.replace(/\{(\w+)\}/g, (_, k) => params[k] || `{${k}}`); + // ?? not ||: an empty-string param is a provided value (render empty), + // while a missing param keeps the visible {placeholder} for debugging. + return value.replace(/\{(\w+)\}/g, (_, k) => params[k] ?? `{${k}}`); } return value; diff --git a/ui/src/i18n/test/translate.test.ts b/ui/src/i18n/test/translate.test.ts index a6d5580dc3d9..a1219b629fc6 100644 --- a/ui/src/i18n/test/translate.test.ts +++ b/ui/src/i18n/test/translate.test.ts @@ -111,6 +111,14 @@ describe("i18n", () => { ); }); + it("renders a provided empty-string param as empty, not the raw placeholder", () => { + expect(translate.t("connection.help.copyCommandAria", { command: "" })).toBe("Copy command: "); + }); + + it("keeps the visible placeholder when the param is missing", () => { + expect(translate.t("connection.help.copyCommandAria", {})).toBe("Copy command: {command}"); + }); + it("should fallback to English if key is missing in another locale", async () => { translate.i18n.registerTranslation("zh-CN", { common: {} } as never); await translate.i18n.setLocale("zh-CN");