diff --git a/modules/firebase/localizedText.test.ts b/modules/firebase/localizedText.test.ts new file mode 100644 index 00000000..d82fcf03 --- /dev/null +++ b/modules/firebase/localizedText.test.ts @@ -0,0 +1,52 @@ +// +// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +import { createLocalizationHelpers } from '@/modules/firebase/localizedText' + +const { parseLocalizedText, parseNilLocalizedText } = + createLocalizationHelpers('pl-PL') + +describe('parseLocalizedText', () => { + const plPL = 'pl-PL' + const pl = 'pl' + const en = 'en' + const de = 'de' + + it('supports plain string', () => { + expect(parseLocalizedText(en)).toBe(en) + }) + + describe('localized text object', () => { + it('fallbacks to empty string when no localizations', () => { + expect(parseLocalizedText({})).toBe('') + }) + + it('fallbacks to "en" if no matching client languages', () => { + expect(parseLocalizedText({ de, en })).toBe(en) + }) + + it('fallbacks to any translation if there is no "en" available', () => { + expect(parseLocalizedText({ de })).toBe(de) + }) + + it('resolves ISO-639-1 standard', () => { + expect(parseLocalizedText({ pl, en })).toBe(pl) + }) + + it('prioritizes ISO-639-2 if exists', () => { + expect(parseLocalizedText({ 'pl-PL': plPL, pl, en })).toBe(plPL) + }) + }) +}) + +describe('parseNilLocalizedText', () => { + it('supports nil values', () => { + expect(parseNilLocalizedText(null)).toBe(null) + expect(parseNilLocalizedText(undefined)).toBe(null) + expect(parseNilLocalizedText('')).toBe('') + }) +}) diff --git a/modules/firebase/localizedText.ts b/modules/firebase/localizedText.ts index 273d9676..da2aea0c 100644 --- a/modules/firebase/localizedText.ts +++ b/modules/firebase/localizedText.ts @@ -9,11 +9,40 @@ import { isNil } from 'es-toolkit' import { isObject } from 'lodash' import { type LocalizedText } from '@/modules/firebase/models' import { type Nil } from '@/packages/design-system/src/utils/misc' +import { getNavigatorLanguage } from '@/packages/design-system/src/utils/navigator' -const locale = 'en' +export const createLocalizationHelpers = (locale: string) => { + const possibleLocalesToCheck = [ + locale, + locale.includes('-') ? locale.split('-').at(0) : null, + 'en', + 'en-US', + ].filter(Boolean) -export const parseLocalizedText = (text: LocalizedText) => - isObject(text) ? text[locale] : text + const parseLocalizedText = (localizedText: LocalizedText) => { + if (!isObject(localizedText)) return localizedText + for (const locale of possibleLocalesToCheck) { + const text = localizedText[locale] as string | undefined + if (text !== undefined) { + return text + } + } + const fallbackAnyText = Object.values(localizedText).at(0) + if (fallbackAnyText === undefined) { + console.error('localized text has no localisations provided') + return '' + } + return fallbackAnyText + } -export const parseNilLocalizedText = (text: Nil) => - isNil(text) ? null : parseLocalizedText(text) + const parseNilLocalizedText = (localizedText: Nil) => + isNil(localizedText) ? null : parseLocalizedText(localizedText) + + return { parseLocalizedText, parseNilLocalizedText } +} + +const { parseLocalizedText, parseNilLocalizedText } = createLocalizationHelpers( + getNavigatorLanguage(), +) + +export { parseLocalizedText, parseNilLocalizedText } diff --git a/packages/design-system/src/utils/navigator.ts b/packages/design-system/src/utils/navigator.ts new file mode 100644 index 00000000..d4b0b15a --- /dev/null +++ b/packages/design-system/src/utils/navigator.ts @@ -0,0 +1,16 @@ +// +// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ +/** + * Gets user locale, providing en-US as fallback + * */ +export const getNavigatorLanguage = () => { + if (typeof window === 'undefined') return 'en-US' // Fallback for SSR + return navigator.languages.at(0) ?? navigator.language ?? 'en-US' +} +/* eslint-enable @typescript-eslint/no-unnecessary-condition */