i18n: initial separation of locales and languages

Bug: #123
This commit is contained in:
Jesse Chan
2020-12-14 11:19:55 +08:00
parent cea33d5668
commit 341630856b
3 changed files with 37 additions and 22 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ const FloodApp: FC = observer(() => {
return (
<Suspense fallback={<LoadingOverlay />}>
<AsyncIntlProvider locale={SettingStore.floodSettings.language}>
<AsyncIntlProvider language={SettingStore.floodSettings.language}>
<Router history={history}>
<AppWrapper className={ConfigStore.preferDark ? 'dark' : undefined}>
<Switch>
+12 -8
View File
@@ -8,6 +8,7 @@ import EN from './strings.compiled.json';
import Languages from '../constants/Languages';
import type {Language} from '../constants/Languages';
import type {LocaleConfig} from '../util/detectLocale';
const messagesCache: Partial<Record<Exclude<Language, 'auto'>, Record<string, MessageFormatElement[]>>> = {en: EN};
@@ -45,28 +46,31 @@ function getMessages(locale: Exclude<Language, 'auto'>) {
}
interface AsyncIntlProviderProps {
locale?: Language;
language?: Language;
children: React.ReactNode;
}
const AsyncIntlProvider: React.FC<AsyncIntlProviderProps> = ({locale, children}: AsyncIntlProviderProps) => {
let validatedLocale: Exclude<Language, 'auto'>;
if (locale == null || locale === 'auto' || !Object.prototype.hasOwnProperty.call(Languages, locale)) {
const AsyncIntlProvider: React.FC<AsyncIntlProviderProps> = ({language, children}: AsyncIntlProviderProps) => {
let validatedLocale: LocaleConfig;
if (language == null || language === 'auto' || !Object.prototype.hasOwnProperty.call(Languages, language)) {
validatedLocale = detectLocale();
} else {
validatedLocale = locale;
validatedLocale = {
locale: language,
language,
};
}
const messages = getMessages(validatedLocale);
const messages = getMessages(validatedLocale.language);
return (
<IntlProvider locale={validatedLocale === 'translate' ? 'en' : validatedLocale} messages={messages}>
<IntlProvider locale={validatedLocale.language === 'translate' ? 'en' : validatedLocale.locale} messages={messages}>
{children}
</IntlProvider>
);
};
AsyncIntlProvider.defaultProps = {
locale: 'en',
language: 'en',
};
export default AsyncIntlProvider;
+24 -13
View File
@@ -3,43 +3,54 @@ import Languages from '../constants/Languages';
import type {Language} from '../constants/Languages';
let detectedLocale: Exclude<Language, 'auto'> = 'en';
export interface LocaleConfig {
locale: string;
language: Exclude<Language, 'auto'>;
}
const detectedLocales: LocaleConfig = {
locale: 'en',
language: 'en',
};
let localeDetected = false;
function detectLocale(): Exclude<Language, 'auto'> {
const detectLocale = (): LocaleConfig => {
if (localeDetected) {
return detectedLocale;
return detectedLocales;
}
// Reverse loop to respect language priority of user
getUserLocales()
.reverse()
.forEach((userLocale): void => {
let locale = userLocale;
switch (locale) {
detectedLocales.locale = userLocale;
switch (detectedLocales.locale) {
// Special handlings for languages with variants
case 'zh':
case 'zh-CN':
case 'zh-SG':
case 'zh-MY':
locale = 'zh-Hans';
detectedLocales.locale = 'zh-Hans';
break;
case 'zh-TW':
case 'zh-HK':
case 'zh-MO':
locale = 'zh-Hant';
detectedLocales.locale = 'zh-Hant';
break;
default:
break;
}
if (Object.prototype.hasOwnProperty.call(Languages, locale)) {
detectedLocale = locale as Exclude<Language, 'auto'>;
} else if (Object.prototype.hasOwnProperty.call(Languages, locale.substr(0, 2))) {
if (Object.prototype.hasOwnProperty.call(Languages, detectedLocales.locale)) {
detectedLocales.language = detectedLocales.locale as Exclude<Language, 'auto'>;
} else if (Object.prototype.hasOwnProperty.call(Languages, detectedLocales.locale.substr(0, 2))) {
// In rare cases, user provides a locale (eg. en-US) without fallback (eg. en)
detectedLocale = locale.substr(0, 2) as Exclude<Language, 'auto'>;
detectedLocales.language = detectedLocales.locale.substr(0, 2) as Exclude<Language, 'auto'>;
}
});
localeDetected = true;
return detectedLocale;
}
return detectedLocales;
};
export default detectLocale;