From 1a8cd56cbc0c6a774889e8e74d5aaae0b09d0e93 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bachorski <60391032+arkadiuszbachorski@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:25:06 +0200 Subject: [PATCH] Implement proper locale check (#60) # Implement proper locale check ## :recycle: Current situation & Problem Closes #20 ## :gear: Release Notes * Implement proper locale check ### Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md). --- modules/firebase/localizedText.test.ts | 52 +++++++++++++++++++ modules/firebase/localizedText.ts | 39 ++++++++++++-- packages/design-system/src/utils/navigator.ts | 16 ++++++ 3 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 modules/firebase/localizedText.test.ts create mode 100644 packages/design-system/src/utils/navigator.ts 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 */