mirror of
https://github.com/zoriya/flood.git
synced 2026-06-03 03:12:14 +00:00
i18n: implement dynamic language loading
This commit is contained in:
@@ -139,6 +139,7 @@ module.exports = {
|
||||
resolve: {
|
||||
extensions: ['*', '.js', '.jsx', '.ts', '.tsx', '.json'],
|
||||
alias: {
|
||||
'react-intl': 'react-intl/react-intl-no-parser.umd.min.js',
|
||||
'@shared': path.resolve('./shared'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {Router} from 'react-router-dom';
|
||||
import {FormattedMessage, IntlProvider} from 'react-intl';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {Route} from 'react-router';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import detectLocale from './util/detectLocale';
|
||||
import * as i18n from './i18n/languages';
|
||||
import {AsyncIntlProvider} from './i18n/languages';
|
||||
import connectStores from './util/connectStores';
|
||||
import AppWrapper from './components/AppWrapper';
|
||||
import AuthActions from './actions/AuthActions';
|
||||
import FloodActions from './actions/FloodActions';
|
||||
import history from './util/history';
|
||||
import Languages from './constants/Languages';
|
||||
import Login from './components/views/Login';
|
||||
import Register from './components/views/Register';
|
||||
import SettingsStore from './stores/SettingsStore';
|
||||
@@ -20,7 +20,7 @@ import UIStore from './stores/UIStore';
|
||||
import '../sass/style.scss';
|
||||
|
||||
interface FloodAppProps {
|
||||
locale?: keyof typeof i18n.languages;
|
||||
locale?: keyof typeof Languages;
|
||||
}
|
||||
|
||||
const initialize = (): void => {
|
||||
@@ -81,15 +81,10 @@ class FloodApp extends React.Component<FloodAppProps> {
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let {locale} = this.props;
|
||||
if (locale == null || locale === 'auto' || !Object.prototype.hasOwnProperty.call(i18n.languages, locale)) {
|
||||
locale = detectLocale();
|
||||
}
|
||||
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={i18n.languages[locale]}>
|
||||
{appRoutes}
|
||||
</IntlProvider>
|
||||
<React.Suspense fallback={<AsyncIntlProvider locale="en">{appRoutes}</AsyncIntlProvider>}>
|
||||
<AsyncIntlProvider locale={this.props.locale}>{appRoutes}</AsyncIntlProvider>
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import EN from './compiled/strings.json';
|
||||
|
||||
import CS from './compiled/cs.json';
|
||||
import DE from './compiled/de.json';
|
||||
import ES from './compiled/es.json';
|
||||
import FR from './compiled/fr.json';
|
||||
import IT from './compiled/it.json';
|
||||
import HU from './compiled/hu.json';
|
||||
import NL from './compiled/nl.json';
|
||||
import NO from './compiled/no.json';
|
||||
import PL from './compiled/pl.json';
|
||||
import PT from './compiled/pt.json';
|
||||
import RU from './compiled/ru.json';
|
||||
import RO from './compiled/ro.json';
|
||||
import SV from './compiled/sv.json';
|
||||
import UK from './compiled/uk.json';
|
||||
import KO from './compiled/ko.json';
|
||||
import JA from './compiled/ja.json';
|
||||
import ZH_HANS from './compiled/zh-Hans.json';
|
||||
import ZH_HANT from './compiled/zh-Hant.json';
|
||||
import AR from './compiled/ar.json';
|
||||
|
||||
export const languages = {
|
||||
auto: EN,
|
||||
en: EN,
|
||||
cs: {
|
||||
...EN,
|
||||
...CS,
|
||||
},
|
||||
de: {
|
||||
...EN,
|
||||
...DE,
|
||||
},
|
||||
es: {
|
||||
...EN,
|
||||
...ES,
|
||||
},
|
||||
fr: {
|
||||
...EN,
|
||||
...FR,
|
||||
},
|
||||
it: {
|
||||
...EN,
|
||||
...IT,
|
||||
},
|
||||
hu: {
|
||||
...EN,
|
||||
...HU,
|
||||
},
|
||||
nl: {
|
||||
...EN,
|
||||
...NL,
|
||||
},
|
||||
no: {
|
||||
...EN,
|
||||
...NO,
|
||||
},
|
||||
pl: {
|
||||
...EN,
|
||||
...PL,
|
||||
},
|
||||
pt: {
|
||||
...EN,
|
||||
...PT,
|
||||
},
|
||||
ru: {
|
||||
...EN,
|
||||
...RU,
|
||||
},
|
||||
ro: {
|
||||
...EN,
|
||||
...RO,
|
||||
},
|
||||
sv: {
|
||||
...EN,
|
||||
...SV,
|
||||
},
|
||||
uk: {
|
||||
...EN,
|
||||
...UK,
|
||||
},
|
||||
ko: {
|
||||
...EN,
|
||||
...KO,
|
||||
},
|
||||
ja: {
|
||||
...EN,
|
||||
...JA,
|
||||
},
|
||||
'zh-Hans': {
|
||||
...EN,
|
||||
...ZH_HANT,
|
||||
...ZH_HANS,
|
||||
},
|
||||
'zh-Hant': {
|
||||
...EN,
|
||||
...ZH_HANS,
|
||||
...ZH_HANT,
|
||||
},
|
||||
ar: {
|
||||
...EN,
|
||||
...AR,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import {IntlProvider} from 'react-intl';
|
||||
import React from 'react';
|
||||
|
||||
import type {MessageFormatElement} from 'intl-messageformat-parser';
|
||||
|
||||
import detectLocale from '../util/detectLocale';
|
||||
import EN from './strings.compiled.json';
|
||||
import Languages from '../constants/Languages';
|
||||
|
||||
const messagesCache: Partial<Record<
|
||||
Exclude<keyof typeof Languages, 'auto'>,
|
||||
Record<string, MessageFormatElement[]>
|
||||
>> = {en: EN};
|
||||
|
||||
async function loadMessages(locale: Exclude<keyof typeof Languages, 'auto' | 'en'>) {
|
||||
const messages: Record<string, MessageFormatElement[]> = await import(`./compiled/${locale}.json`);
|
||||
messagesCache[locale] = messages;
|
||||
return messages;
|
||||
}
|
||||
|
||||
function getMessages(locale: Exclude<keyof typeof Languages, 'auto'>) {
|
||||
if (messagesCache[locale]) {
|
||||
return messagesCache[locale];
|
||||
}
|
||||
throw loadMessages(locale as Exclude<keyof typeof Languages, 'auto' | 'en'>);
|
||||
}
|
||||
|
||||
export const AsyncIntlProvider = ({locale, children}: {locale?: keyof typeof Languages; children: React.ReactNode}) => {
|
||||
if (locale == null || locale === 'auto') {
|
||||
locale = detectLocale();
|
||||
}
|
||||
|
||||
const messages = getMessages(locale as Exclude<keyof typeof Languages, 'auto'>);
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={messages}>
|
||||
{children}
|
||||
</IntlProvider>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
import {getUserLocales} from 'get-user-locale';
|
||||
import * as i18n from '../i18n/languages';
|
||||
import Languages from '../constants/Languages';
|
||||
|
||||
let detectedLocale: keyof typeof i18n.languages = 'en';
|
||||
let detectedLocale: keyof typeof Languages = 'en';
|
||||
let localeDetected = false;
|
||||
|
||||
export default function (): keyof typeof i18n.languages {
|
||||
export default function (): keyof typeof Languages {
|
||||
if (localeDetected) {
|
||||
return detectedLocale;
|
||||
}
|
||||
@@ -28,11 +28,11 @@ export default function (): keyof typeof i18n.languages {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(i18n.languages, userLocale)) {
|
||||
detectedLocale = userLocale as keyof typeof i18n.languages;
|
||||
} else if (Object.prototype.hasOwnProperty.call(i18n.languages, userLocale.substr(0, 2))) {
|
||||
if (Object.prototype.hasOwnProperty.call(Languages, userLocale)) {
|
||||
detectedLocale = userLocale as keyof typeof Languages;
|
||||
} else if (Object.prototype.hasOwnProperty.call(Languages, userLocale.substr(0, 2))) {
|
||||
// In rare cases, user provides a locale (eg. en-US) without fallback (eg. en)
|
||||
detectedLocale = userLocale.substr(0, 2) as keyof typeof i18n.languages;
|
||||
detectedLocale = userLocale.substr(0, 2) as keyof typeof Languages;
|
||||
}
|
||||
});
|
||||
localeDetected = true;
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
"build-assets": "node client/scripts/build.js",
|
||||
"build-ts": "tsc-transpile-only -p server/tsconfig.json && cat config.js > dist/config.js && chmod 755 dist/server/bin/start.js",
|
||||
"build-docs": "jsdoc -c ./.jsdoc.json",
|
||||
"build-i18n": "formatjs compile-folder --ast --format simple client/src/javascript/i18n client/src/javascript/i18n/compiled && formatjs compile-folder --ast --format simple client/src/javascript/i18n/translations client/src/javascript/i18n/compiled",
|
||||
"build-i18n": "formatjs compile --ast --format simple client/src/javascript/i18n/strings.json --out-file client/src/javascript/i18n/strings.compiled.json && formatjs compile-folder --ast --format simple client/src/javascript/i18n/translations client/src/javascript/i18n/compiled",
|
||||
"deprecated-warning": "node client/scripts/deprecated-warning.js && sleep 10",
|
||||
"format-source": "node scripts/prettier.js formatSource",
|
||||
"check-compiled-i18n": "npm run build-i18n && npm run format-source && git diff --quiet client/src/javascript/i18n/compiled",
|
||||
@@ -164,6 +164,6 @@
|
||||
"npm": ">=6.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%"
|
||||
"> 5%"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"target": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"allowJs": true,
|
||||
"module": "esnext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
Reference in New Issue
Block a user