Skip to content

Commit

Permalink
chore: eliminate require cycle warning for intl-related code
Browse files Browse the repository at this point in the history
  • Loading branch information
achou11 committed May 13, 2024
1 parent 15224db commit 01a3666
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 55 deletions.
55 changes: 3 additions & 52 deletions src/frontend/contexts/IntlContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import * as React from 'react';
import {IntlProvider as IntlProviderOrig, CustomFormats} from 'react-intl';

import messages from '../../../translations/messages.json';
import languages from '../languages.json';
import {usePersistedLocale} from '../hooks/persistedState/usePersistedLocale';
import {TranslatedLocale} from '../lib/intl';

export const formats: CustomFormats = {
date: {
Expand All @@ -17,27 +17,15 @@ export const formats: CustomFormats = {
},
};

type TranslatedLocales = keyof typeof messages;
type SupportedLanguageLocales = keyof typeof languages;

interface LanguageName {
/** IETF BCP 47 langauge tag with region code. */
locale: SupportedLanguageLocales;
/** Localized name for language */
nativeName: string;
/** English name for language */
englishName: string;
}

export const IntlProvider = ({children}: {children: React.ReactNode}) => {
const appLocale = usePersistedLocale(store => store.locale);

const languageCode = appLocale.split('-')[0];

// Add fallbacks for non-regional locales (e.g. "en" for "en-GB")
const localeMessages = {
...messages[languageCode as TranslatedLocales],
...(messages[appLocale as TranslatedLocales] || {}),
...messages[languageCode as TranslatedLocale],
...(messages[appLocale as TranslatedLocale] || {}),
};

return (
Expand All @@ -56,40 +44,3 @@ export const IntlProvider = ({children}: {children: React.ReactNode}) => {
function onError(e: Error) {
console.log(e);
}

// Device locale can be regional e.g. `en-US` but we might only have
// translations for `en`. If we don't have translations for a given device
// language, then we ignore it and fallback to `en` or the user selected
// language for the app
export function getSupportedLocale(
locale: string,
): keyof typeof languages | undefined {
const translatedLocales = Object.keys(messages) as Array<TranslatedLocales>;
const supportedLanguages: LanguageName[] = translatedLocales
.filter(locale => {
const hasAtLeastOneTranslatedString =
Object.keys(messages[locale]).length > 0;
// This will show a typescript error if the language name does not exist
const hasTranslatedLanguageName = languages[locale];
if (!hasTranslatedLanguageName) {
console.warn(
`Locale "${locale}" is not available in Mapeo because we do not have
a language name and translations in \`src/frontend/languages.json\``,
);
}
return hasAtLeastOneTranslatedString && hasTranslatedLanguageName;
})
.map(locale => ({
locale,
...languages[locale],
}))
.sort((a, b) => {
return a.englishName.localeCompare(b.englishName);
});

if (supportedLanguages.find(lang => lang.locale === locale))
return locale as keyof typeof languages;
const nonRegionalLocale = locale.split('-')[0];
if (supportedLanguages.find(({locale}) => locale === nonRegionalLocale))
return nonRegionalLocale as keyof typeof languages;
}
7 changes: 4 additions & 3 deletions src/frontend/hooks/persistedState/usePersistedLocale.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import {StateCreator} from 'zustand';
import {createPersistedState} from './createPersistedState';
import {getSupportedLocale} from '../../contexts/IntlContext';
import {getLocales} from 'expo-localization';

import {getSupportedLocale} from '../../lib/intl';
import {createPersistedState} from './createPersistedState';

type LocaleSlice = {
locale: string;
setLocale: (locale: string) => void;
};

const localeSlice: StateCreator<LocaleSlice> = (set, get) => ({
const localeSlice: StateCreator<LocaleSlice> = set => ({
// We can use this non-null assertion with `getLocales()` because, according
// to [the docs][1], the result is "guaranteed to contain at least 1 element."
// [1]: https://github.com/expo/expo/blob/5585320eec9271038cd7c672b4cf9f0e945ca658/packages/expo-localization/src/Localization.ts#L123
Expand Down
51 changes: 51 additions & 0 deletions src/frontend/lib/intl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import messages from '../../../translations/messages.json';
import languages from '../languages.json';

export type TranslatedLocale = keyof typeof messages;
export type SupportedLanguageLocale = keyof typeof languages;

export interface LanguageName {
/** IETF BCP 47 langauge tag with region code. */
locale: SupportedLanguageLocale;
/** Localized name for language */
nativeName: string;
/** English name for language */
englishName: string;
}

// Device locale can be regional e.g. `en-US` but we might only have
// translations for `en`. If we don't have translations for a given device
// language, then we ignore it and fallback to `en` or the user selected
// language for the app
export function getSupportedLocale(
locale: string,
): keyof typeof languages | undefined {
const translatedLocales = Object.keys(messages) as Array<TranslatedLocale>;
const supportedLanguages: LanguageName[] = translatedLocales
.filter(translatedLocale => {
const hasAtLeastOneTranslatedString =
Object.keys(messages[translatedLocale]).length > 0;
// This will show a typescript error if the language name does not exist
const hasTranslatedLanguageName = languages[translatedLocale];
if (!hasTranslatedLanguageName) {
console.warn(
`Locale "${translatedLocale}" is not available in Mapeo because we do not have
a language name and translations in \`src/frontend/languages.json\``,
);
}
return hasAtLeastOneTranslatedString && hasTranslatedLanguageName;
})
.map(translatedLocale => ({
locale: translatedLocale,
...languages[translatedLocale],
}))
.sort((a, b) => {
return a.englishName.localeCompare(b.englishName);
});

if (supportedLanguages.find(lang => lang.locale === locale))
return locale as keyof typeof languages;
const nonRegionalLocale = locale.split('-')[0];
if (supportedLanguages.find(lang => lang.locale === nonRegionalLocale))
return nonRegionalLocale as keyof typeof languages;
}

0 comments on commit 01a3666

Please sign in to comment.