JSPM

  • Created
  • Published
  • Downloads 331
  • Score
    100M100P100Q94792F
  • License MIT

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

Package Exports

  • @moxy/next-intl
  • @moxy/next-intl/es/polyfill.browser.js
  • @moxy/next-intl/es/react/NextIntlScript.browser.js
  • @moxy/next-intl/lib/polyfill.browser.js
  • @moxy/next-intl/lib/react/NextIntlScript.browser.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@moxy/next-intl) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

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 or use NODE_ICU_DATA=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() {
        return (
            <Html>
                <Head />
                <body>
                    <Main />
                    <NextIntlScript />
                    <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.

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 handleChange = useCallback(
        (event) => changeLocale(event.target.value),
        [changeLocale],
    );

    return (
        <select value={ locale.id } onChange={ handleChange }>
            { 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 { withNextIntl } from '@moxy/next-intl';

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

    // ...
};

export default withNextIntl(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.