Skip to content

Latest commit

 

History

History
373 lines (259 loc) · 10.4 KB

README.md

File metadata and controls

373 lines (259 loc) · 10.4 KB

next-intl

NPM version Downloads Build Status Coverage Status Dependency status Dev Dependency status

Library to integrate react-intl with Next.js, making it easy to manage the current locale based on configurable policies.

Installation

$ npm install --save @moxy/next-intl react-intl

All the polyfilling will be taken care by this library automatically, so that you don't need to worry about react-intl runtime requirements.

ℹ️ If you are running Node.js < 13.1.0, you must also install full-icu and start node with --icu-data-dir=node_modules/full-icu.

Setup

1 Add the plugin to your next.config.js

const withNextIntl = require('@moxy/next-intl/plugin');

module.exports = withNextIntl()({ ...nextConfig });

This plugin will make some modifications to your webpack config to circuvent a few issues related to JSDOM, which is a runtime dependency of react-intl for the server.

2. Create a root folder named intl with the following structure:

intl/
  index.js
  messages/
    en-US.json

The index.js file should have the following contents:

import { cookiePolicy, acceptLanguagePolicy, defaultPolicy } from '@moxy/next-intl';

export default {
    locales: [
        {
            id: 'en-US',
            name: 'English',
            loadMessages: async () => {
                const module = await import(/* webpackChunkName: "intl-messages/en-US" */ './messages/en-US.json');

                return module.default;
            },
        },
    ],
    policies: [
        cookiePolicy(),
        acceptLanguagePolicy(),
        defaultPolicy('en-US'),
    ],
};

The messages/en-US.json file contains the messages for the en-US locale:

{
    "hello": "Hello World"
}

3. Include <NextIntlScript> in pages/_document.js:

import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { NextIntlScript } from '@moxy/next-intl';

export default class MyDocument extends Document {
    render() {
        const { assetPrefix } = this.props.__NEXT_DATA__;

        return (
            <Html>
                <Head />
                <body>
                    <Main />
                    <NextIntlScript assetPrefix={ assetPrefix } />
                    <NextScript />
                </body>
            </Html>
        );
    }
}

4. Wrap your app with withNextIntlSetup in pages/_app.js:

import React from 'react';
import App from 'next/app';
import { withNextIntlSetup } from '@moxy/next-intl';
import nextIntlConfig from '../intl';

export default withNextIntlSetup(nextIntlConfig)(App);

Here's an example if you have a custom app:

import React from 'react';
import App from 'next/app';
import { withNextIntlSetup } from '@moxy/next-intl';
import nextIntlConfig from '../intl';
import Layout from '../components/layout';

class MyApp extends App {
    render() {
        const { Component, pageProps } = this.props;

        return (
            <Layout>
                <Component { ...pageProps } />
            </Layout>
        );
    }
}

export default withNextIntlSetup(nextIntlConfig)(MyApp);

5. Ready!

You may now use react-intl as you normally would. Moreover, you will receive the current locale in your pages' getInitialProps static function.

import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';

export default class Homepage extends Component {
    static getInitialProps({ locale }) {
        // You may do something with `locale`, such as
        // fetching localized information from a CMS
    }

    render() {
        return (
            <main>
                <FormattedMessage id="hello" />
            </main>
        );
    }
}

API

<NextIntlScript>

<NextIntlScript> is a React component responsible for conditionally loading Intl polyfills and locale data if necessary.

Please check the setup guide to know how to set it up.

Available props:

assexPrefix

Type: string

The configured assetPrefix if any.

withNextIntlSetup(config)(App)

An higher-order React component that wraps App, setting up getInitialProps and <NextIntlProvider> automatically.

Please check the setup guide to know how to set it up.

config

Type: object

ℹ️ You may pass any of the supported react-intl's <IntlProvider> props as well, except for locale and messages.

locales

Type: Array

The list of supported locales. Each locale is an object with the following shape:

{
    id: 'en-US',
    name: 'English',
    loadMessages: async () => {
        // Usually you would use an `import()` statement here,
        // but you may load messages from some API such as a CMS.
    },
}
policies

Type: Array

The list of policies ordered by preference.

App

Type: Component

The App component that will be wrapped.

<NextIntlProvider>

A React component that sets up react-intl's <IntlProvider> and automatically manages the current locale based on the configured locales and policies.

⚠️ Please note that you should use withNextIntlSetup rather than setting up the provider yourself.

The provider value is an object with the following shape:

const nextIntl = {
    // The current locale object
    locale,
    // The array of supported locales
    locales,
    // A function to change the locale
    // Receives the locale id and returns a promise
    changeLocale,
    // The react-intl's intl object
    intl,
};

<NextIntlConsumer>

A React component that gives you access to the <NextIntlProvider> value.

This may be useful to render a language selection dropdown:

import { NextIntlConsumer } from '@moxy/next-intl';

const LanguageSelect = () => (
    <NextIntlConsumer>
        { ({ locales, locale, changeLocale }) => (
            <select
                value={ locale.id }
                onChange={ (event) => changeLocale(event.target.value) }>
                { locales.map(({ id, name }) => (
                    <option key={ id } value={ id }>{ name }</option>
                )) }
            </select>
        ) }
    </NextIntlConsumer>
);

export default LanguageSelect;

The changeLocale(localeId) function returns a promise, giving you the ability to render a loading while the switch is happening and display an error message if the switch failed.

useNextIntl()

The hook version of <NextIntlConsumer>.

Again, this may be useful to render a language selection dropdown:

import { useCallback } from 'react';
import { useNextIntl } from '@moxy/next-intl';

const LanguageSelect = () => {
    const { locales, locale, changeLocale } = useNextIntl();

    const handleHange = useCallback(
        (event) => changeLocale(event.target.value),
        [changeLocale],
    );

    return (
        <select value={ locale.id } onChange={ handleHange }>
            { locales.map(({ id, name }) => (
                <option key={ id } value={ id }>{ name }</option>
            )) }
        </select>
    );
};

export default LanguageSelect;

withNextIntl(Component)

The higher order component version of <NextIntlConsumer>, injecting the <NextIntlProvider> value as the nextIntl prop.

import { useCallback } from 'react';
import { useNextIntl } from '@moxy/next-intl';

const LanguageSelect = ({ nextIntl }) => {
    const { locales, locale, changeLocale } = nextIntl;

    // ...
};

export default LanguageSelect;

Policies

cookiePolicy(options?)

A policy that saves the locale preference in a cookie and then matches against the Cookie request header or document.cookie.

options

Type: object

ℹ️ Any options from the universal-cookie set method are available as well.

name

Type: string
Default: locale

The cookie name.

acceptLanguagePolicy()

A policy that uses the browser's language by matching against the Accept-Language request header or navigator.languages.

defaultPolicy(localeId)

A policy the simply returns localeId to serve as the fallback locale.

localeId

Type: string

The locale id to use as the default locale.

Custom policies

You may want to create custom policies for certain use-cases. One common use-case is to have a policy that matches against the locale saved in the account preferences of authenticated users.

A policy is a simple object that must have a match method and optionally a watch, act and save methods. Please check out the built-in policies to know how to implement one.

Tests

$ npm t
$ npm t -- --watch  # To run watch mode

License

Released under the MIT License.