Move to new enum

This commit is contained in:
Manav Rathi 2024-02-23 10:51:17 +05:30
parent 85aaf1d27e
commit 1b6f020b23
5 changed files with 81 additions and 58 deletions

View file

@ -34,7 +34,6 @@
"ffmpeg-wasm": "file:./thirdparty/ffmpeg-wasm",
"file-type": "^16.5.4",
"formik": "^2.1.5",
"get-user-locale": "^2.1.3",
"hdbscan": "0.0.1-alpha.5",
"heic-convert": "^2.0.0",
"idb": "^7.1.1",

View file

@ -2,41 +2,50 @@ import DropdownInput, { DropdownOption } from 'components/DropdownInput';
import { useLocalState } from '@ente/shared/hooks/useLocalState';
import { t } from 'i18next';
import { useRouter } from 'next/router';
import { Language, getBestPossibleUserLocale } from '@/ui/i18n';
import {
type SupportedLocale,
supportedLocales,
closestSupportedLocale,
} from '@/ui/i18n';
import { LS_KEYS } from '@ente/shared/storage/localStorage';
import { getUserLocaleString } from '@ente/shared/storage/localStorage/helpers';
const getLocaleDisplayName = (l: Language) => {
switch (l) {
case Language.en:
/**
* Human readable name for each supported locale
*
* TODO (MR): This names themselves should be localized.
*/
export const localeName = (locale: SupportedLocale) => {
switch (locale) {
case 'en':
return 'English';
case Language.fr:
case 'fr':
return 'Français';
case Language.zh:
case 'zh':
return '中文';
case Language.nl:
case 'nl':
return 'Nederlands';
case Language.es:
case 'es':
return 'Español';
}
};
const getLanguageOptions = (): DropdownOption<Language>[] => {
return Object.values(Language).map((lang) => ({
label: getLocaleDisplayName(lang),
value: lang,
const getLanguageOptions = (): DropdownOption<SupportedLocale>[] => {
return supportedLocales.map((locale) => ({
label: localeName(locale),
value: locale,
}));
};
export const LanguageSelector = () => {
const [userLocale, setUserLocale] = useLocalState(
LS_KEYS.LOCALE,
getBestPossibleUserLocale(getUserLocaleString())
closestSupportedLocale(getUserLocaleString())
);
const router = useRouter();
const updateCurrentLocale = (newLocale: Language) => {
const updateCurrentLocale = (newLocale: SupportedLocale) => {
setUserLocale(newLocale);
router.reload();
};

View file

@ -5,6 +5,22 @@ import { isDevBuild } from "@/utils/env";
import { getUserLocales } from "get-user-locale";
import { includes } from "@/utils/type-guards";
/**
* List of all {@link SupportedLocale}s.
*
* Locales are combinations of a language code, and an optional region code.
*
* For example, "en", "en-US", "en-IN" (Indian English), "pt" (Portuguese),
* "pt-BR" (Brazilian Portuguese).
*
* In our Crowdin Project, we have work-in-progress translations into more
* languages than this. When a translation reaches a high enough coverage, say
* 90%, then we manually add it to this list of supported languages.
*/
export const supportedLocales = ["en", "fr", "zh", "nl", "es"] as const;
/** The type of {@link supportedLocale}s. */
export type SupportedLocale = (typeof supportedLocales)[number];
/**
* Load translations.
*
@ -19,7 +35,8 @@ import { includes } from "@/utils/type-guards";
* - react-i18next, which adds React specific APIs
*/
export const setupI18n = async (savedLocaleString?: string) => {
const lng = getBestPossibleUserLocale(savedLocaleString);
const locale = closestSupportedLocale(savedLocaleString);
// https://www.i18next.com/overview/api
await i18n
// i18next-http-backend: Asynchronously loads translations over HTTP
@ -34,7 +51,8 @@ export const setupI18n = async (savedLocaleString?: string) => {
debug: isDevBuild,
returnEmptyString: false,
fallbackLng: "en",
lng: lng,
// i18next calls it language, but it really is the locale
lng: locale,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
@ -62,22 +80,6 @@ export const setupI18n = async (savedLocaleString?: string) => {
});
};
/**
* List of all {@link SupportedLocale}s.
*
* Locales are combinations of a language code, and an optional region code.
*
* For example, "en", "en-US", "en-IN" (Indian English), "pt" (Portuguese),
* "pt-BR" (Brazilian Portuguese).
*
* In our Crowdin Project, we have work-in-progress translations into more
* languages than this. When a translation reaches a high enough coverage, say
* 90%, then we manually add it to this list of supported languages.
*/
export const supportedLocales = ["en", "fr", "zh", "nl", "es"] as const;
/** The type of {@link supportedLocale}s. */
export type SupportedLocale = (typeof supportedLocales)[number];
/**
* Return the current locale in which our user interface is being shown.
*
@ -90,18 +92,24 @@ export const currentLocale = () => {
return locale && includes(supportedLocales, locale) ? locale : "en";
};
/** Enums of supported locale */
export enum Language {
en = "en",
fr = "fr",
zh = "zh",
nl = "nl",
es = "es",
}
export function getBestPossibleUserLocale(
/**
* Return the closest / best matching {@link SupportedLocale}.
*
* It takes as input a {@link savedLocaleString}, which denotes the user's
* explicitly chosen preference (which we then persist in local storage).
* Subsequently, we use this to (usually literally) return the supported locale
* that it represents.
*
* If {@link savedLocaleString} is `undefined`, it tries to deduce the closest
* {@link SupportedLocale} that matches the browser's locale.
*/
export function closestSupportedLocale(
savedLocaleString?: string,
): Language {
): SupportedLocale {
const ss = savedLocaleString;
if (ss && includes(supportedLocales, ss)) return ss;
/*
switch (savedLocaleString) {
case "en":
return Language.en;
@ -114,20 +122,26 @@ export function getBestPossibleUserLocale(
case "es":
return Language.es;
}
*/
const userLocales = getUserLocales();
for (const lc of userLocales) {
if (lc.startsWith("en")) {
return Language.en;
} else if (lc.startsWith("fr")) {
return Language.fr;
} else if (lc.startsWith("zh")) {
return Language.zh;
} else if (lc.startsWith("nl")) {
return Language.nl;
} else if (lc.startsWith("es")) {
return Language.es;
for (const us of getUserLocales()) {
// Exact match
if (us && includes(supportedLocales, us)) return us;
// Language match
if (us.startsWith("en")) {
return "en";
} else if (us.startsWith("fr")) {
return "fr";
} else if (us.startsWith("zh")) {
return "zh";
} else if (us.startsWith("nl")) {
return "nl";
} else if (us.startsWith("es")) {
return "es";
}
}
return Language.en;
// Fallback
return "en";
}

View file

@ -11,6 +11,7 @@
"@emotion/styled": "^11.11",
"@mui/icons-material": "^5.15",
"@mui/material": "^5.15",
"get-user-locale": "^2.3.1",
"i18next": "^23.10.0",
"i18next-http-backend": "^2.5.0",
"react": "18.2.0",

View file

@ -2322,7 +2322,7 @@ get-tsconfig@^4.5.0:
dependencies:
resolve-pkg-maps "^1.0.0"
get-user-locale@^2.1.3:
get-user-locale@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-2.3.1.tgz#fc7319429c8a70fac01b3b2a0b08b0c71c1d3fe2"
integrity sha512-VEvcsqKYx7zhZYC1CjecrDC5ziPSpl1gSm0qFFJhHSGDrSC+x4+p1KojWC/83QX//j476gFhkVXP/kNUc9q+bQ==