From b5b945fc2ceddaf8fca3ded8ab55b3830286cb66 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Mon, 18 May 2026 12:34:38 +1000 Subject: [PATCH 1/9] silence MISSING_TRANSLATION warnings for fallback locales --- ui/desktop/src/i18n/i18n.test.ts | 42 ++++++++++++++++++++++++++++---- ui/desktop/src/i18n/index.ts | 17 +++++++++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index 808883e32e39..95c45459fbc8 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -1,6 +1,15 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { getLocale } from './index'; +const englishMessages = { + shared: 'English shared message', + englishOnly: 'English fallback message', +}; + +const zhCnMessages = { + shared: 'Chinese shared message', +}; + // Helper to mock window.appConfig for tests function mockAppConfig(values: Record) { (window as unknown as Record).appConfig = { @@ -61,17 +70,40 @@ describe('getLocale', () => { }); describe('loadMessages', () => { - it('returns empty object for English locale', async () => { + afterEach(() => { + vi.doUnmock('./compiled/en.json'); + vi.doUnmock('./compiled/zh-CN.json'); + vi.resetModules(); + }); + + async function loadMessagesWithMockedCatalogs() { + vi.resetModules(); + vi.doMock('./compiled/en.json', () => ({ default: englishMessages })); + vi.doMock('./compiled/zh-CN.json', () => ({ default: zhCnMessages })); + const { loadMessages } = await import('./index'); + return loadMessages; + } + + it('returns compiled English messages for English locale', async () => { + const loadMessages = await loadMessagesWithMockedCatalogs(); const messages = await loadMessages('en'); - expect(messages).toEqual({}); + expect(messages).toEqual(englishMessages); }); - it('returns empty object for unsupported locale (with warning)', async () => { + it('merges locale messages over English fallback messages', async () => { + const loadMessages = await loadMessagesWithMockedCatalogs(); + const messages = await loadMessages('zh-CN'); + + expect(messages.shared).toBe(zhCnMessages.shared); + expect(messages.englishOnly).toBe(englishMessages.englishOnly); + }); + + it('returns English messages for unsupported locale (with warning)', async () => { const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const { loadMessages } = await import('./index'); + const loadMessages = await loadMessagesWithMockedCatalogs(); const messages = await loadMessages('xx'); - expect(messages).toEqual({}); + expect(messages).toEqual(englishMessages); expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('No message catalog found')); warnSpy.mockRestore(); }); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index 07a6d23c828d..bc2043971e2a 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -92,22 +92,29 @@ export const currentMessageLocale = resolvedLocale.messageLocale; /** * Load compiled messages for a given locale. - * Returns an empty object for English (react-intl uses defaultMessage as fallback). + * English messages are always loaded as the fallback catalog so regional + * English locales can keep their locale for date/number formatting without + * triggering missing translation warnings for every message. */ export async function loadMessages(locale: string): Promise> { + const englishMod = await import('./compiled/en.json'); + const englishMessages = englishMod.default ?? englishMod; + if (locale === 'en') { - // English strings live in source code as defaultMessage — no catalog needed. - return {}; + return englishMessages; } try { // Dynamic import so compiled translation bundles are code-split. const mod = await import(`./compiled/${locale}.json`); - return mod.default ?? mod; + return { + ...englishMessages, + ...(mod.default ?? mod), + }; } catch { console.warn( `[i18n] No message catalog found for locale "${locale}", falling back to English.` ); - return {}; + return englishMessages; } } From eb2c2bf8735f62e4df2b706f72b049c7640de946 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Mon, 18 May 2026 13:03:56 +1000 Subject: [PATCH 2/9] fixed typecheck --- ui/desktop/src/i18n/index.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index bc2043971e2a..098ae1872c54 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -90,6 +90,11 @@ export const currentLocale = resolvedLocale.locale; /** Base language for loading message catalogs (e.g. "en"). */ export const currentMessageLocale = resolvedLocale.messageLocale; +async function loadCompiledMessages(locale: string): Promise> { + const mod = await import(`./compiled/${locale}.json`); + return (mod.default ?? mod) as Record; +} + /** * Load compiled messages for a given locale. * English messages are always loaded as the fallback catalog so regional @@ -97,8 +102,7 @@ export const currentMessageLocale = resolvedLocale.messageLocale; * triggering missing translation warnings for every message. */ export async function loadMessages(locale: string): Promise> { - const englishMod = await import('./compiled/en.json'); - const englishMessages = englishMod.default ?? englishMod; + const englishMessages = await loadCompiledMessages('en'); if (locale === 'en') { return englishMessages; @@ -106,10 +110,10 @@ export async function loadMessages(locale: string): Promise Date: Mon, 18 May 2026 13:21:10 +1000 Subject: [PATCH 3/9] fixed tests --- ui/desktop/src/i18n/i18n.test.ts | 42 +++++++++++++++----------------- ui/desktop/src/i18n/index.ts | 13 +++++++--- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index 95c45459fbc8..70b6ecdec40f 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; -import { getLocale } from './index'; +import { getLocale, loadMessagesWithCatalogLoader } from './index'; const englishMessages = { shared: 'English shared message', @@ -70,39 +70,37 @@ describe('getLocale', () => { }); describe('loadMessages', () => { - afterEach(() => { - vi.doUnmock('./compiled/en.json'); - vi.doUnmock('./compiled/zh-CN.json'); - vi.resetModules(); - }); - - async function loadMessagesWithMockedCatalogs() { - vi.resetModules(); - vi.doMock('./compiled/en.json', () => ({ default: englishMessages })); - vi.doMock('./compiled/zh-CN.json', () => ({ default: zhCnMessages })); - - const { loadMessages } = await import('./index'); - return loadMessages; - } - it('returns compiled English messages for English locale', async () => { - const loadMessages = await loadMessagesWithMockedCatalogs(); - const messages = await loadMessages('en'); + const loadCatalog = vi.fn().mockResolvedValue(englishMessages); + const messages = await loadMessagesWithCatalogLoader('en', loadCatalog); + expect(messages).toEqual(englishMessages); + expect(loadCatalog).toHaveBeenCalledOnce(); + expect(loadCatalog).toHaveBeenCalledWith('en'); }); it('merges locale messages over English fallback messages', async () => { - const loadMessages = await loadMessagesWithMockedCatalogs(); - const messages = await loadMessages('zh-CN'); + const loadCatalog = vi.fn(async (locale: string) => + locale === 'zh-CN' ? zhCnMessages : englishMessages + ); + const messages = await loadMessagesWithCatalogLoader('zh-CN', loadCatalog); expect(messages.shared).toBe(zhCnMessages.shared); expect(messages.englishOnly).toBe(englishMessages.englishOnly); + expect(loadCatalog).toHaveBeenCalledWith('en'); + expect(loadCatalog).toHaveBeenCalledWith('zh-CN'); }); it('returns English messages for unsupported locale (with warning)', async () => { const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const loadMessages = await loadMessagesWithMockedCatalogs(); - const messages = await loadMessages('xx'); + const loadCatalog = vi.fn(async (locale: string) => { + if (locale === 'xx') { + throw new Error('missing catalog'); + } + return englishMessages; + }); + const messages = await loadMessagesWithCatalogLoader('xx', loadCatalog); + expect(messages).toEqual(englishMessages); expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('No message catalog found')); warnSpy.mockRestore(); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index 098ae1872c54..e5d9846839ab 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -90,7 +90,7 @@ export const currentLocale = resolvedLocale.locale; /** Base language for loading message catalogs (e.g. "en"). */ export const currentMessageLocale = resolvedLocale.messageLocale; -async function loadCompiledMessages(locale: string): Promise> { +export async function loadCompiledMessages(locale: string): Promise> { const mod = await import(`./compiled/${locale}.json`); return (mod.default ?? mod) as Record; } @@ -102,7 +102,14 @@ async function loadCompiledMessages(locale: string): Promise> { - const englishMessages = await loadCompiledMessages('en'); + return loadMessagesWithCatalogLoader(locale, loadCompiledMessages); +} + +export async function loadMessagesWithCatalogLoader( + locale: string, + loadCatalog: (locale: string) => Promise> +): Promise> { + const englishMessages = await loadCatalog('en'); if (locale === 'en') { return englishMessages; @@ -110,7 +117,7 @@ export async function loadMessages(locale: string): Promise Date: Mon, 18 May 2026 14:41:47 +1000 Subject: [PATCH 4/9] address comments --- ui/desktop/src/i18n/i18n.test.ts | 14 ++++++++++++++ ui/desktop/src/i18n/index.ts | 13 +++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index 70b6ecdec40f..012f3a3b6061 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -105,4 +105,18 @@ describe('loadMessages', () => { expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('No message catalog found')); warnSpy.mockRestore(); }); + + it('falls back to default messages when the English catalog is unavailable', async () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + const loadCatalog = vi.fn(async () => { + throw new Error('missing catalog'); + }); + const messages = await loadMessagesWithCatalogLoader('en', loadCatalog); + + expect(messages).toEqual({}); + expect(warnSpy).toHaveBeenCalledWith( + '[i18n] No English fallback catalog found; missing messages will use source defaultMessage values.' + ); + warnSpy.mockRestore(); + }); }); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index e5d9846839ab..cd3411731dd0 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -109,7 +109,16 @@ export async function loadMessagesWithCatalogLoader( locale: string, loadCatalog: (locale: string) => Promise> ): Promise> { - const englishMessages = await loadCatalog('en'); + let englishMessages: Record; + + try { + englishMessages = await loadCatalog('en'); + } catch { + console.warn( + '[i18n] No English fallback catalog found; missing messages will use source defaultMessage values.' + ); + englishMessages = {}; + } if (locale === 'en') { return englishMessages; @@ -124,7 +133,7 @@ export async function loadMessagesWithCatalogLoader( }; } catch { console.warn( - `[i18n] No message catalog found for locale "${locale}", falling back to English.` + `[i18n] No message catalog found for locale "${locale}"; using fallback messages.` ); return englishMessages; } From 27bc22a4e51d9ec319c6f1f91738016910dce3f8 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Mon, 18 May 2026 20:35:22 +1000 Subject: [PATCH 5/9] Revert "address comments" This reverts commit 1218484f8e1fc5b4ebf2ccdad8ec58e813f63136. --- ui/desktop/src/i18n/i18n.test.ts | 14 -------------- ui/desktop/src/i18n/index.ts | 13 ++----------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index 012f3a3b6061..70b6ecdec40f 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -105,18 +105,4 @@ describe('loadMessages', () => { expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('No message catalog found')); warnSpy.mockRestore(); }); - - it('falls back to default messages when the English catalog is unavailable', async () => { - const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const loadCatalog = vi.fn(async () => { - throw new Error('missing catalog'); - }); - const messages = await loadMessagesWithCatalogLoader('en', loadCatalog); - - expect(messages).toEqual({}); - expect(warnSpy).toHaveBeenCalledWith( - '[i18n] No English fallback catalog found; missing messages will use source defaultMessage values.' - ); - warnSpy.mockRestore(); - }); }); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index cd3411731dd0..e5d9846839ab 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -109,16 +109,7 @@ export async function loadMessagesWithCatalogLoader( locale: string, loadCatalog: (locale: string) => Promise> ): Promise> { - let englishMessages: Record; - - try { - englishMessages = await loadCatalog('en'); - } catch { - console.warn( - '[i18n] No English fallback catalog found; missing messages will use source defaultMessage values.' - ); - englishMessages = {}; - } + const englishMessages = await loadCatalog('en'); if (locale === 'en') { return englishMessages; @@ -133,7 +124,7 @@ export async function loadMessagesWithCatalogLoader( }; } catch { console.warn( - `[i18n] No message catalog found for locale "${locale}"; using fallback messages.` + `[i18n] No message catalog found for locale "${locale}", falling back to English.` ); return englishMessages; } From a31ff32cde7caae4715d39bb3ffb96cedf4751fc Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Mon, 18 May 2026 20:35:30 +1000 Subject: [PATCH 6/9] Revert "fixed tests" This reverts commit d58242c3c5f42393e9019eadbe864fe3fd193b7c. --- ui/desktop/src/i18n/i18n.test.ts | 42 +++++++++++++++++--------------- ui/desktop/src/i18n/index.ts | 13 +++------- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index 70b6ecdec40f..95c45459fbc8 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; -import { getLocale, loadMessagesWithCatalogLoader } from './index'; +import { getLocale } from './index'; const englishMessages = { shared: 'English shared message', @@ -70,37 +70,39 @@ describe('getLocale', () => { }); describe('loadMessages', () => { - it('returns compiled English messages for English locale', async () => { - const loadCatalog = vi.fn().mockResolvedValue(englishMessages); - const messages = await loadMessagesWithCatalogLoader('en', loadCatalog); + afterEach(() => { + vi.doUnmock('./compiled/en.json'); + vi.doUnmock('./compiled/zh-CN.json'); + vi.resetModules(); + }); + + async function loadMessagesWithMockedCatalogs() { + vi.resetModules(); + vi.doMock('./compiled/en.json', () => ({ default: englishMessages })); + vi.doMock('./compiled/zh-CN.json', () => ({ default: zhCnMessages })); + const { loadMessages } = await import('./index'); + return loadMessages; + } + + it('returns compiled English messages for English locale', async () => { + const loadMessages = await loadMessagesWithMockedCatalogs(); + const messages = await loadMessages('en'); expect(messages).toEqual(englishMessages); - expect(loadCatalog).toHaveBeenCalledOnce(); - expect(loadCatalog).toHaveBeenCalledWith('en'); }); it('merges locale messages over English fallback messages', async () => { - const loadCatalog = vi.fn(async (locale: string) => - locale === 'zh-CN' ? zhCnMessages : englishMessages - ); - const messages = await loadMessagesWithCatalogLoader('zh-CN', loadCatalog); + const loadMessages = await loadMessagesWithMockedCatalogs(); + const messages = await loadMessages('zh-CN'); expect(messages.shared).toBe(zhCnMessages.shared); expect(messages.englishOnly).toBe(englishMessages.englishOnly); - expect(loadCatalog).toHaveBeenCalledWith('en'); - expect(loadCatalog).toHaveBeenCalledWith('zh-CN'); }); it('returns English messages for unsupported locale (with warning)', async () => { const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const loadCatalog = vi.fn(async (locale: string) => { - if (locale === 'xx') { - throw new Error('missing catalog'); - } - return englishMessages; - }); - const messages = await loadMessagesWithCatalogLoader('xx', loadCatalog); - + const loadMessages = await loadMessagesWithMockedCatalogs(); + const messages = await loadMessages('xx'); expect(messages).toEqual(englishMessages); expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('No message catalog found')); warnSpy.mockRestore(); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index e5d9846839ab..098ae1872c54 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -90,7 +90,7 @@ export const currentLocale = resolvedLocale.locale; /** Base language for loading message catalogs (e.g. "en"). */ export const currentMessageLocale = resolvedLocale.messageLocale; -export async function loadCompiledMessages(locale: string): Promise> { +async function loadCompiledMessages(locale: string): Promise> { const mod = await import(`./compiled/${locale}.json`); return (mod.default ?? mod) as Record; } @@ -102,14 +102,7 @@ export async function loadCompiledMessages(locale: string): Promise> { - return loadMessagesWithCatalogLoader(locale, loadCompiledMessages); -} - -export async function loadMessagesWithCatalogLoader( - locale: string, - loadCatalog: (locale: string) => Promise> -): Promise> { - const englishMessages = await loadCatalog('en'); + const englishMessages = await loadCompiledMessages('en'); if (locale === 'en') { return englishMessages; @@ -117,7 +110,7 @@ export async function loadMessagesWithCatalogLoader( try { // Dynamic import so compiled translation bundles are code-split. - const messages = await loadCatalog(locale); + const messages = await loadCompiledMessages(locale); return { ...englishMessages, ...messages, From f052b9a012f12fb656a441c89b3ab94833966d7e Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Mon, 18 May 2026 20:35:39 +1000 Subject: [PATCH 7/9] Revert "fixed typecheck" This reverts commit eb2c2bf8735f62e4df2b706f72b049c7640de946. --- ui/desktop/src/i18n/index.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index 098ae1872c54..bc2043971e2a 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -90,11 +90,6 @@ export const currentLocale = resolvedLocale.locale; /** Base language for loading message catalogs (e.g. "en"). */ export const currentMessageLocale = resolvedLocale.messageLocale; -async function loadCompiledMessages(locale: string): Promise> { - const mod = await import(`./compiled/${locale}.json`); - return (mod.default ?? mod) as Record; -} - /** * Load compiled messages for a given locale. * English messages are always loaded as the fallback catalog so regional @@ -102,7 +97,8 @@ async function loadCompiledMessages(locale: string): Promise> { - const englishMessages = await loadCompiledMessages('en'); + const englishMod = await import('./compiled/en.json'); + const englishMessages = englishMod.default ?? englishMod; if (locale === 'en') { return englishMessages; @@ -110,10 +106,10 @@ export async function loadMessages(locale: string): Promise Date: Mon, 18 May 2026 20:36:01 +1000 Subject: [PATCH 8/9] Revert "silence MISSING_TRANSLATION warnings for fallback locales" This reverts commit b5b945fc2ceddaf8fca3ded8ab55b3830286cb66. --- ui/desktop/src/i18n/i18n.test.ts | 42 ++++---------------------------- ui/desktop/src/i18n/index.ts | 17 ++++--------- 2 files changed, 10 insertions(+), 49 deletions(-) diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index 95c45459fbc8..808883e32e39 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -1,15 +1,6 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { getLocale } from './index'; -const englishMessages = { - shared: 'English shared message', - englishOnly: 'English fallback message', -}; - -const zhCnMessages = { - shared: 'Chinese shared message', -}; - // Helper to mock window.appConfig for tests function mockAppConfig(values: Record) { (window as unknown as Record).appConfig = { @@ -70,40 +61,17 @@ describe('getLocale', () => { }); describe('loadMessages', () => { - afterEach(() => { - vi.doUnmock('./compiled/en.json'); - vi.doUnmock('./compiled/zh-CN.json'); - vi.resetModules(); - }); - - async function loadMessagesWithMockedCatalogs() { - vi.resetModules(); - vi.doMock('./compiled/en.json', () => ({ default: englishMessages })); - vi.doMock('./compiled/zh-CN.json', () => ({ default: zhCnMessages })); - + it('returns empty object for English locale', async () => { const { loadMessages } = await import('./index'); - return loadMessages; - } - - it('returns compiled English messages for English locale', async () => { - const loadMessages = await loadMessagesWithMockedCatalogs(); const messages = await loadMessages('en'); - expect(messages).toEqual(englishMessages); + expect(messages).toEqual({}); }); - it('merges locale messages over English fallback messages', async () => { - const loadMessages = await loadMessagesWithMockedCatalogs(); - const messages = await loadMessages('zh-CN'); - - expect(messages.shared).toBe(zhCnMessages.shared); - expect(messages.englishOnly).toBe(englishMessages.englishOnly); - }); - - it('returns English messages for unsupported locale (with warning)', async () => { + it('returns empty object for unsupported locale (with warning)', async () => { const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - const loadMessages = await loadMessagesWithMockedCatalogs(); + const { loadMessages } = await import('./index'); const messages = await loadMessages('xx'); - expect(messages).toEqual(englishMessages); + expect(messages).toEqual({}); expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('No message catalog found')); warnSpy.mockRestore(); }); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index bc2043971e2a..07a6d23c828d 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -92,29 +92,22 @@ export const currentMessageLocale = resolvedLocale.messageLocale; /** * Load compiled messages for a given locale. - * English messages are always loaded as the fallback catalog so regional - * English locales can keep their locale for date/number formatting without - * triggering missing translation warnings for every message. + * Returns an empty object for English (react-intl uses defaultMessage as fallback). */ export async function loadMessages(locale: string): Promise> { - const englishMod = await import('./compiled/en.json'); - const englishMessages = englishMod.default ?? englishMod; - if (locale === 'en') { - return englishMessages; + // English strings live in source code as defaultMessage — no catalog needed. + return {}; } try { // Dynamic import so compiled translation bundles are code-split. const mod = await import(`./compiled/${locale}.json`); - return { - ...englishMessages, - ...(mod.default ?? mod), - }; + return mod.default ?? mod; } catch { console.warn( `[i18n] No message catalog found for locale "${locale}", falling back to English.` ); - return englishMessages; + return {}; } } From f1032c115b683c771266b55c0d45e407cc433623 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Mon, 18 May 2026 20:51:17 +1000 Subject: [PATCH 9/9] suppressed excessive warning --- ui/desktop/src/renderer.tsx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ui/desktop/src/renderer.tsx b/ui/desktop/src/renderer.tsx index c9df7f1ea991..3e6533188a15 100644 --- a/ui/desktop/src/renderer.tsx +++ b/ui/desktop/src/renderer.tsx @@ -17,6 +17,20 @@ const App = lazy(() => import('./App')); const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED'; +let warnedFallbackLocale = false; +function handleIntlError(err: { code: string; message?: string }) { + if (err.code === 'MISSING_TRANSLATION' && currentLocale !== currentMessageLocale) { + if (!warnedFallbackLocale) { + warnedFallbackLocale = true; + console.warn( + `[i18n] Locale "${currentLocale}" has no translations; falling back to "${currentMessageLocale}".` + ); + } + return; + } + console.error(err); +} + (async () => { // Check if we're in the launcher view (doesn't need goosed connection) const isLauncher = window.location.hash === '#/launcher'; @@ -50,7 +64,12 @@ const TELEMETRY_CONFIG_KEY = 'GOOSE_TELEMETRY_ENABLED'; ReactDOM.createRoot(document.getElementById('root')!).render( - +