From 23b0214a2adc4354c84c05c949c776cad53ca1ec Mon Sep 17 00:00:00 2001 From: rvveber Date: Tue, 25 Feb 2025 15:21:32 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20add=20language=20utility?= =?UTF-8?q?=20for=20"locale"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adds a helper for working with locales - More details in their annotations - Unnecessary, if in the future, the backend uses the same locales as the keys in the translations (ISO 639-1) --- .../src/features/language/utils/locale.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/frontend/apps/impress/src/features/language/utils/locale.ts diff --git a/src/frontend/apps/impress/src/features/language/utils/locale.ts b/src/frontend/apps/impress/src/features/language/utils/locale.ts new file mode 100644 index 00000000..19868b94 --- /dev/null +++ b/src/frontend/apps/impress/src/features/language/utils/locale.ts @@ -0,0 +1,49 @@ +/** + * @param {string} locale - The locale string, which can be in formats like de-DE, de-CH, de_DE, de-de, de_de, or de. + * @returns {string} The regionless ISO 639-1 code. => de + */ +export function convertLocaleToISO639_1(locale: string): string { + // Split the locale string by either a hyphen (-) or an underscore (_) + return locale.split(/[-_]/)[0].toLowerCase(); +} + +/** + * Filters the available locales to find those that match the search criteria. + * + * @param {readonly string[]} localesAvailable - The list of available locale strings to match against. + * @param {readonly string[]} localesToSearch - The list of locale strings to search for. + * @returns {string[]} Filtered list of localesAvailable that match localesToSearch, ordered by closest match. + */ +export function getMatchingLocales( + localesAvailable: readonly string[], + localesToSearch: readonly string[], +): string[] { + const matchingLocales: string[] = []; + for (const localeToSearch of localesToSearch) { + // Exact match (case-insensitive) + const exactMatches = localesAvailable.filter( + (localeAvailable) => + localeAvailable.toLowerCase() === localeToSearch.toLowerCase(), + ); + if (exactMatches.length > 0) { + for (const matchingLocale of exactMatches) { + if (!matchingLocales.includes(matchingLocale)) { + matchingLocales.push(matchingLocale); + } + } + continue; + } + // Soft match (ISO639-1 code) + const softMatches = localesAvailable.filter( + (localeAvailable) => + convertLocaleToISO639_1(localeAvailable) === + convertLocaleToISO639_1(localeToSearch), + ); + for (const matchingLocale of softMatches) { + if (!matchingLocales.includes(matchingLocale)) { + matchingLocales.push(matchingLocale); + } + } + } + return matchingLocales; +}