Skip to content

Commit

Permalink
feat(examples/with-react-intl): add locale negotation to client side (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
longlho authored Sep 16, 2020
1 parent 4ba768e commit 67b67b2
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 24 deletions.
7 changes: 7 additions & 0 deletions examples/with-react-intl/lang/en-GB.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"AvQcw8": "React Intl Next.js Example, in en-GB",
"N015Sp": "Hello, World, from the UK!",
"ejEGdx": "Home (British version)",
"fnfXnF": "An example app integrating React Intl with Next.js",
"g5pX+a": "About (British version)"
}
7 changes: 7 additions & 0 deletions examples/with-react-intl/lang/zh-Hans-CN.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"AvQcw8": " React Intl Next.js示例",
"N015Sp": "你好,世界!",
"ejEGdx": "首页",
"fnfXnF": "一个将React Intl与Next.js集成的示例应用程序",
"g5pX+a": "关于"
}
7 changes: 7 additions & 0 deletions examples/with-react-intl/lang/zh-Hant-HK.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"AvQcw8": "React Intl Next.js示例",
"N015Sp": "你好,世界!",
"ejEGdx": "首頁",
"fnfXnF": "一個將React Intl與Next.js集成的示例應用程序",
"g5pX+a": "關於"
}
4 changes: 3 additions & 1 deletion examples/with-react-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
"version": "1.0.0",
"scripts": {
"dev": "cross-env NODE_ICU_DATA=node_modules/full-icu ts-node --project tsconfig.server.json server.ts",
"dev-no-custom-server": "next dev",
"build": "npm run extract:i18n && npm run compile:i18n && next build && tsc -p tsconfig.server.json",
"extract:i18n": "formatjs extract '{pages,components}/*.{js,ts,tsx}' --format simple --id-interpolation-pattern '[sha512:contenthash:base64:6]' --out-file lang/en.json",
"compile:i18n": "formatjs compile-folder --ast --format simple lang/ compiled-lang/",
"start": "cross-env NODE_ENV=production NODE_ICU_DATA=node_modules/full-icu node dist/server"
"start": "cross-env NODE_ENV=production NODE_ICU_DATA=node_modules/full-icu node dist/server",
"start-no-custom-server": "next start"
},
"dependencies": {
"@formatjs/cli": "^2.7.3",
Expand Down
66 changes: 51 additions & 15 deletions examples/with-react-intl/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,73 @@ import App from 'next/app';

function MyApp({Component, pageProps, locale, messages}) {
return (
<IntlProvider locale={locale} messages={messages}>
<IntlProvider locale={locale} defaultLocale="en" messages={messages}>
<Component {...pageProps} />
</IntlProvider>
);
}

// We need to load and expose the translations on the request for the user's
// locale. These will only be used in production, in dev the `defaultMessage` in
// each message description in the source code will be used.
const getMessages = (locale: string = 'en') => {
switch (locale) {
default:
return import('../compiled-lang/en.json');
case 'fr':
return import('../compiled-lang/fr.json');
/**
* Get the messages and also do locale negotiation. A multi-lingual user
* can specify locale prefs like ['ja', 'en-GB', 'en'] which is interpreted as
* Japanese, then British English, then English
* @param locales list of requested locales
* @returns {[string, Promise]} A tuple containing the negotiated locale
* and the promise of fetching the translated messages
*/
function getMessages(locales: string | string[] = ['en']) {
if (!Array.isArray(locales)) {
locales = [locales];
}
};
let langBundle;
let locale;
for (let i = 0; i < locales.length && !locale; i++) {
locale = locales[i];
switch (locale) {
case 'fr':
langBundle = import('../compiled-lang/fr.json');
break;
case 'en-GB':
langBundle = import('../compiled-lang/en-GB.json');
break;
case 'zh-Hans-CN':
langBundle = import('../compiled-lang/zh-Hans-CN.json');
break;
case 'zh-Hant-HK':
langBundle = import('../compiled-lang/zh-Hant-HK.json');
break;
default:
break;
// Add more languages
}
}
if (!langBundle) {
return ['en', import('../compiled-lang/en.json')];
}
return [locale, langBundle];
}

const getInitialProps: typeof App.getInitialProps = async appContext => {
const {
ctx: {req},
} = appContext;
const locale = (req as any)?.locale || (window as any).LOCALE || 'en';
const requestedLocales: string | string[] =
(req as any)?.locale ||
(typeof navigator !== 'undefined' && navigator.languages) ||
// IE11
(typeof navigator !== 'undefined' && (navigator as any).userLanguage) ||
(typeof window !== 'undefined' && (window as any).LOCALE) ||
'en';

const [supportedLocale, messagePromise] = getMessages(requestedLocales);

const [appProps, messages] = await Promise.all([
polyfill(locale),
getMessages(locale),
polyfill(supportedLocale),
messagePromise,
App.getInitialProps(appContext),
]);

return {...(appProps as any), locale, messages};
return {...(appProps as any), locale: supportedLocale, messages};
};

MyApp.getInitialProps = getInitialProps;
Expand Down
24 changes: 16 additions & 8 deletions examples/with-react-intl/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,33 @@ class MyDocument extends Document<Props> {
static async getInitialProps(ctx: DocumentContext) {
const {req} = ctx;
const initialProps = await Document.getInitialProps(ctx);
const locale = (req as any).locale;
return {
...initialProps,
locale: (req as any).locale || 'en',
lang: ((req as any).locale || 'en').split('-')[0],
locale,
lang: locale ? locale.split('-')[0] : undefined,
nonce: (req as any).nonce,
};
}

render() {
let scriptEl;
if (this.props.locale) {
scriptEl = (
<script
nonce={this.props.nonce}
dangerouslySetInnerHTML={{
__html: `window.LOCALE="${this.props.locale}"`,
}}
></script>
);
}

return (
<Html lang={this.props.lang}>
<Head />
<body>
<script
nonce={this.props.nonce}
dangerouslySetInnerHTML={{
__html: `window.LOCALE="${this.props.locale}"`,
}}
></script>
{scriptEl}
<Main />
<NextScript />
</body>
Expand Down

0 comments on commit 67b67b2

Please sign in to comment.