The minimalistic localization solution for Next.js, powered by Rosetta
β¨
Features
- Supports all rendering modes: (Static) | β (SSG) | Ξ» (Server).
- Less than 1000 bytes β including dependencies!
- Pluralization support
- No build step, No enforced conventions.
Installation & Setup
yarn add next-localization
See Demo
for full example and locale setup.
Basic Usage
Your _app.js
.
import { I18nProvider } from 'next-localization';
export default function MyApp({ Component, pageProps }) {
return (
<I18nProvider lngDict={{ hello: 'world', welcome: 'Welcome, {{username}}!' }} locale={'en'}>
<Component {...pageProps} />
</I18nProvider>
);
}
Any functional component.
import { useI18n } from 'next-localization';
const HomePage = () => {
const i18n = useI18n();
// Change locale
i18n.locale('de'); // if dict was already loaded
i18n.locale('de', DE); // set and load dict at the same time
// Get current locale
i18n.locale(); // de
// Checkout https://github.com/lukeed/rosetta for full interpolation support
return <p>{i18n.t('welcome', { username })}</p>;
};
getStaticProps
Usage with You can use Next.js's static APIs to feed your _app.js
's lngDict
:
import { I18nProvider } from 'next-localization';
export default function MyApp({ Component, pageProps }) {
return (
<I18nProvider lngDict={pageProps.lngDict} locale={pageProps.lng}>
<Component {...pageProps} />
</I18nProvider>
);
}
Each page should define getStaticProps
and getStaticPaths
:
// pages/[lng]/index.js
export const getStaticProps = async ({ params }) => {
// You could also fetch from external API at build time
// this example loads locales from disk once for each language defined in `params.lng`
const { default: lngDict = {} } = await import(`../../locales/${params.lng}.json`);
return {
props: {
lng: params?.lng,
lngDict
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every second
revalidate: 1
};
};
export const getStaticPaths = async () => {
// You could also fetch from external API at build time
const languages = ['en', 'de', 'fr'];
return {
paths: languages.map((lng) => ({ params: { lng } })),
fallback: false
};
};
The same steps works with getServerSideProps
.
Redirect to default language
Next.js +9.5 is shipped with a rewrite engine. This will create a permanent redirect from /
to /en
when en
should be your default language. This features requires to run Next.js in server mode. This won't work if you just export your site.
module.exports = {
redirects() {
return [
{
source: '/',
destination: '/en',
permanent: true
}
];
}
};
Internationalization
We rely on the native platform api Intl
.
Language helper
If you need to detect the browser language based on your available app languages you can use the getPreferredLanguage
utility function.
import { getPreferredLanguage } from 'next-localization';
const appLanguages = ['en', 'de-DE']; // all available app languages
getPreferredLanguage(appLanguages); // returns the best match based on navigator.languages
// e.g { full: 'en-US', language: 'en', region: 'EN' }
Pluralization
We provide a small pluralization i18n.plural
utility function. The implementation uses Intl.PluralRules
.
import { I18nProvider, useI18n } from 'next-localization';
function Root() {
return (
<I18nProvider
lngDict={{
warning: 'WARNING: {{birds}}',
birds: {
other: 'birds',
one: 'bird',
two: 'two birds',
few: 'some birds'
}
}}
locale={'en'}>
<Child />
</I18nProvider>
);
}
function Child() {
const i18n = useI18n();
const t = i18n.plural('en-US');
return <p>{t('warning', { birds: 2 })}</p>; // WARNING: two birds
}
Datetime, Numbers
Use DateTimeFormat
, NumberFormat
directly or rely on an external library. The integration will look very similiar.
import { I18nProvider } from 'next-localization';
function Root() {
return (
<I18nProvider
lngDict={{
copyright: 'Copyright: {{date}}'
}}
locale={'en'}>
<Child />
</I18nProvider>
);
}
function Child() {
const date = new Intl.DateTimeFormat('en-US').format(new Date());
return <p>{t('copyright', { date })}</p>; // Copyright: 8/30/2020
}
Usage on the server
The same api as the useI18n
react hook.
import { I18n } from 'next-localization';
const i18n = I18n();
i18n.locale('en', { hello: 'world', welcome: 'Welcome, {{username}}!' });
// Get current locale
i18n.locale(); // de
// translate
i18n.t('welcome', { username });
Performance considerations
Don't forget that a locale change will rerender all components under the I18nProvider
provider. It's safe to create multiple providers with different language dictionaries. This can be useful for lazy-loading scenarios. For all other cases, you can still use React.memo
, useMemo
in your components.
Other considerations
Depending on your application next-localization
might not be sufficient to internationalize your application. You still need to consider:
- Detect user language on server.
- Localize your app links
<Link />
based on the user language. - Format times and numbers.
- Declare html
lang
attributes for SEO and a11y.
With some effort those points are very easy to solve and you can still base on a very lightweight localization strategy.