Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade react-intl workflow in example #16215

Merged
merged 7 commits into from
Aug 27, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions examples/with-react-intl/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"presets": ["next/babel"],
"plugins": [
["babel-plugin-react-intl", {
"ast": true,
"idInterpolationPattern": "[sha512:contenthash:base64:6]",
"extractFromFormatMessageCall": true
}]
]
}
1 change: 1 addition & 0 deletions examples/with-react-intl/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ yarn-error.log*

# vercel
.vercel
compiled-lang
3 changes: 3 additions & 0 deletions examples/with-react-intl/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.formatOnSave": true
}
49 changes: 6 additions & 43 deletions examples/with-react-intl/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# Example app with [React Intl][]

This example app shows how to integrate [React Intl][] with Next.
This example app shows how to integrate [React Intl][] with Next.js.

## How to use

### Using `create-next-app`

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:

```bash
Expand All @@ -14,39 +12,21 @@ npx create-next-app --example with-react-intl with-react-intl-app
yarn create next-app --example with-react-intl with-react-intl-app
```

### Download manually

Download the example:

```bash
curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-react-intl
cd with-react-intl
```

Install it and run:

```bash
npm install
npm run dev
# or
yarn
yarn dev
```

Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

### Features of this example app
## Features of this example app

- Server-side language negotiation
- React Intl locale data loading via `pages/_document.js` customization
- React Intl integration with [custom App](https://github.com/vercel/next.js#custom-app) component
- `<IntlProvider>` creation with `locale`, `messages` props
- Default message extraction via `babel-plugin-react-intl` integration
- Default message extraction via `@formatjs/cli` integration
- Pre-compile messages into AST with `babel-plugin-react-intl` for performance
- Translation management via build script and customized Next server

### Translation Management

This app stores translations and default strings in the `lang/` dir. This dir has `.messages/` subdir which is where React Intl's Babel plugin outputs the default messages it extracts from the source code. The default messages (`en.json` in this example app) is also generated by the build script. This file can then be sent to a translation service to perform localization for the other locales the app should support.
This app stores translations and default strings in the `lang/` dir. The default messages (`en.json` in this example app) is also generated by the build script. This file can then be sent to a translation service to perform localization for the other locales the app should support.

The translated messages files that exist at `lang/*.json` are only used during production, and are automatically provided to the `<IntlProvider>`. During development the `defaultMessage`s defined in the source code are used. To prepare the example app for localization and production run the build script and start the server in production mode:

Expand All @@ -57,21 +37,4 @@ $ npm start

You can then switch your browser's language preferences to French and refresh the page to see the UI update accordingly.

### FormattedHTMLMessage support (react-intl pre-v4)

Out of the box, this example does not support the use of the `FormattedHTMLMessage` component on the server due to `DOMParser` not being present in a Node environment.
This functionality is deprecated and has been removed as of react-intl 4.0
If you still want to enable this feature, you should install a `DOMParser` implementation (e.g. `xmldom` or `jsdom`) and enable the polyfill in `server.js`:

```js
// Polyfill Node with `DOMParser` required by formatjs.
// See: https://github.com/vercel/next.js/issues/10533
const { DOMParser } = require('xmldom')
global.DOMParser = DOMParser
```

[react intl]: https://github.com/yahoo/react-intl

### Transpile react-intl

According to [react-intl docs](https://github.com/formatjs/react-intl/blob/53f2c826c7b1e50ad37215ce46b5e1c6f5d142cc/docs/Getting-Started.md#esm-build), react-intl and its underlying libraries must be transpiled to support older browsers (eg IE11). This is done by [next-transpile-modules](https://www.npmjs.com/package/next-transpile-modules) in next.config.js.
[react intl]: https://formatjs.io
29 changes: 0 additions & 29 deletions examples/with-react-intl/components/Layout.js

This file was deleted.

28 changes: 28 additions & 0 deletions examples/with-react-intl/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react';
import {useIntl} from 'react-intl';
import Head from 'next/head';
import Nav from './Nav';

export default function Layout({title, children}) {
const intl = useIntl();

return (
<div>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
{title ||
intl.formatMessage({
defaultMessage: 'React Intl Next.js Example',
})}
</title>
</Head>

<header>
<Nav />
</header>

{children}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { FormattedMessage } from 'react-intl'
import Link from 'next/link'
import * as React from 'react';
import {FormattedMessage} from 'react-intl';
import Link from 'next/link';

export default function Nav() {
return (
<nav>
<li>
<Link href="/">
<a>
<FormattedMessage id="nav.home" defaultMessage="Home" />
<FormattedMessage defaultMessage="Home" />
</a>
</Link>
</li>
<li>
<Link href="/about">
<a>
<FormattedMessage id="nav.about" defaultMessage="About" />
<FormattedMessage defaultMessage="About" />
</a>
</Link>
</li>
Expand All @@ -29,5 +30,5 @@ export default function Nav() {
}
`}</style>
</nav>
)
);
}
10 changes: 5 additions & 5 deletions examples/with-react-intl/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "React Intl Next.js Example",
"nav.home": "Home",
"nav.about": "About",
"description": "An example app integrating React Intl with Next.js",
"greeting": "Hello, World!"
"11754": "An example app integrating React Intl with Next.js",
"65a8e": "Hello, World!",
"8cf04": "Home",
"8f7f4": "About",
"9c817": "React Intl Next.js Example"
lfades marked this conversation as resolved.
Show resolved Hide resolved
}
10 changes: 5 additions & 5 deletions examples/with-react-intl/lang/fr.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "React Intl Next.js Exemple",
"nav.home": "Accueil",
"nav.about": "À propos de nous",
"description": "Un exemple d'application intégrant React Intl avec Next.js",
"greeting": "Bonjour le monde!"
"11754": "Un exemple d'application intégrant React Intl avec Next.js",
"65a8e": "Bonjour le monde!",
"8cf04": "Accueil",
"8f7f4": "À propos de nous",
"9c817": "React Intl Next.js Exemple"
}
2 changes: 2 additions & 0 deletions examples/with-react-intl/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
10 changes: 0 additions & 10 deletions examples/with-react-intl/next.config.js

This file was deleted.

32 changes: 22 additions & 10 deletions examples/with-react-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,40 @@
"name": "with-react-intl",
"version": "1.0.0",
"scripts": {
"dev": "node --icu-data-dir=node_modules/full-icu server.js",
"build": "next build && npm run extract",
"extract": "node ./scripts/extract '{pages,components}/*.{js,ts,tsx}'",
"dev": "next dev",
"build": "next build && npm run extract:i18n && npm run compile:i18n",
"extract:i18n": "formatjs extract '{pages,components}/*.{js,ts,tsx}' --format simple --out-file lang/en.json",
"compile:i18n": "formatjs compile-folder --ast --format simple lang/ compiled-lang/",
"start": "NODE_ENV=production node --icu-data-dir=node_modules/full-icu server.js"
},
"dependencies": {
"@formatjs/cli": "1.1.12",
"@formatjs/intl-relativetimeformat": "^2.8.2",
"@formatjs/intl-utils": "^0.6.1",
"@formatjs/cli": "^2.7.3",
"@formatjs/intl-datetimeformat": "^2.4.3",
"@formatjs/intl-getcanonicallocales": "^1.3.2",
"@formatjs/intl-numberformat": "^5.4.1",
"@formatjs/intl-pluralrules": "^3.4.0",
"@formatjs/intl-relativetimeformat": "^7.1.1",
"accepts": "^1.3.7",
"babel-plugin-react-intl": "^8.1.1",
"full-icu": "^1.3.0",
"glob": "^7.1.4",
"intl": "^1.2.5",
"intl-locales-supported": "1.8.4",
"next": "latest",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-intl": "^3.1.12"
"react-intl": "^5.6.3"
},
"license": "ISC",
"devDependencies": {
"@types/accepts": "^1.3.5",
"cross-spawn": "7.0.3",
"next-transpile-modules": "^4.0.2"
"prettier": "2.0.5",
"typescript": "3.9.7"
},
"prettier": {
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": false,
"endOfLine": "lf",
"arrowParens": "avoid"
}
}
36 changes: 0 additions & 36 deletions examples/with-react-intl/pages/_app.js

This file was deleted.

30 changes: 30 additions & 0 deletions examples/with-react-intl/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react';
import {IntlProvider} from 'react-intl';
import {polyfill} from '../polyfills';
import App from 'next/app';

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

const getInitialProps: typeof App.getInitialProps = async appContext => {
const {
ctx: {req},
} = appContext;
const locale = (req as any)?.locale ?? 'en';
const messages = (req as any)?.messages ?? {};
const [appProps] = await Promise.all([
polyfill(locale),
App.getInitialProps(appContext),
]);

return {...appProps, locale, messages};
};

MyApp.getInitialProps = getInitialProps;

export default MyApp;
38 changes: 0 additions & 38 deletions examples/with-react-intl/pages/_document.js

This file was deleted.

Loading