diff --git a/packages/core/src/modules/IcuFormatter.test.ts b/packages/core/src/modules/IcuFormatter.test.ts new file mode 100644 index 0000000000..91b681b35b --- /dev/null +++ b/packages/core/src/modules/IcuFormatter.test.ts @@ -0,0 +1,21 @@ +jest.autoMockOff(); +import { IcuFormatter } from './IcuFormatter'; + +describe('icu formatter', () => { + it('formats simple string', () => { + const formatter = new IcuFormatter(); + const result = formatter.format({ + translation: 'result is {number, number}', + params: { number: 42000 }, + language: 'en', + }); + expect(result).toEqual('result is 42,000'); + }); + + it('fixes invalid locale', () => { + const formatter = new IcuFormatter() as any; + expect(formatter.getLocale('en_GB')).toEqual('en-GB'); + expect(formatter.getLocale('en_GB-nonsenceeeee')).toEqual('en-GB'); + expect(formatter.getLocale('cs CZ')).toEqual('cs-CZ'); + }); +}); diff --git a/packages/core/src/modules/IcuFormatter.ts b/packages/core/src/modules/IcuFormatter.ts index ddcc2b5375..9dd4a27480 100644 --- a/packages/core/src/modules/IcuFormatter.ts +++ b/packages/core/src/modules/IcuFormatter.ts @@ -3,14 +3,36 @@ import { TolgeeModule } from '../types'; export const IcuFormatter: TolgeeModule = class { static type = 'formatter' as const; - cache = new Map(); + private locales = new Map() as Map; + + isLocaleValid(locale: string) { + try { + return Boolean(Intl.NumberFormat.supportedLocalesOf(locale).length); + } catch { + return false; + } + } + + getLocale(language: string) { + if (!this.locales.get(language)) { + let localeCandidate: string = String(language).replace(/[^a-zA-Z]/g, '-'); + while (!this.isLocaleValid(localeCandidate)) { + localeCandidate = + localeCandidate.split('-').slice(0, -1).join('-') || 'en'; + } + this.locales.set(language, localeCandidate); + } + return this.locales.get(language); + } format({ translation, language, params }) { const ignoreTag = !Object.values(params).find( (p) => typeof p === 'function' ); - return new IntlMessageFormat(translation, language, undefined, { + const locale = this.getLocale(language); + + return new IntlMessageFormat(translation, locale, undefined, { ignoreTag, }).format(params) as string; }