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

Add next-intl example #21447

Merged
merged 13 commits into from
Apr 19, 2021
2 changes: 1 addition & 1 deletion docs/advanced-features/i18n-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ description: Next.js has built-in support for internationalized routing and lang

Next.js has built-in support for internationalized ([i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization#Naming)) routing since `v10.0.0`. You can provide a list of locales, the default locale, and domain-specific locales and Next.js will automatically handle the routing.

The i18n routing support is currently meant to complement existing i18n library solutions like `react-intl`, `react-i18next`, `lingui`, `rosetta`, and others by streamlining the routes and locale parsing.
The i18n routing support is currently meant to complement existing i18n library solutions like [`react-intl`](https://formatjs.io/docs/getting-started/installation), [`react-i18next`](https://react.i18next.com/), [`lingui`](https://lingui.js.org/), [`rosetta`](https://github.com/lukeed/rosetta), [`next-intl`](https://github.com/amannn/next-intl) and others by streamlining the routes and locale parsing.

## Getting started

Expand Down
34 changes: 34 additions & 0 deletions examples/with-i18n-next-intl/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel
29 changes: 29 additions & 0 deletions examples/with-i18n-next-intl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# next-intl example

This example uses [next-intl](https://github.com/amannn/next-intl), a minimal, but complete solution for managing internationalization in Next.js apps.
leerob marked this conversation as resolved.
Show resolved Hide resolved

## Features

- 🌟 I18n is an essential part of the user experience, therefore this library doesn't compromise on flexibility and never leaves you behind when you need to fine tune a translation. Messages use the proven [ICU syntax](https://formatjs.io/docs/core-concepts/icu-syntax) which covers interpolation, numbers, dates, times, plurals, ordinal pluralization, label selection based on enums and rich text.
- ⚔️ Based on battle-tested building blocks from [Format.JS](https://formatjs.io/) (used by `react-intl`), this library is a thin wrapper around high-quality, lower-level APIs for i18n.
- 💯 Built-in number and date formatting that is integrated with translations, e.g. allowing for the usage of global formats for a consistent look & feel of your app.
- 💡 A hooks-only API ensures that you can use the same API for `children` as well as for attributes which expect strings.
- 🚀 Integrates with both static as well as server side rendering.

## Deploy your own

Deploy the example using [Vercel](https://vercel.com):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-i18n-next-intl)

## How to use

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
npx create-next-app --example with-i18n-next-intl
# or
yarn create next-app --example with-i18n-next-intl
```

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)).
7 changes: 7 additions & 0 deletions examples/with-i18n-next-intl/components/Code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Code({ children }) {
return (
<code style={{ background: '#eee', padding: 4, borderRadius: 4 }}>
{children}
</code>
)
}
26 changes: 26 additions & 0 deletions examples/with-i18n-next-intl/components/Navigation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useTranslations } from 'next-intl'
import { useRouter } from 'next/dist/client/router'
import Link from 'next/link'

export default function Navigation() {
const t = useTranslations('Navigation')
leerob marked this conversation as resolved.
Show resolved Hide resolved

const { locale, locales, route } = useRouter()
const otherLocale = locales?.find((cur) => cur !== locale)

return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', gap: 10 }}>
<Link href="/">
<a>{t('index')}</a>
</Link>
<Link href="/about">
<a>{t('about')}</a>
</Link>
</div>
<Link href={route} locale={otherLocale}>
<a>{t('switchLocale', { locale: otherLocale })}</a>
</Link>
</div>
)
}
19 changes: 19 additions & 0 deletions examples/with-i18n-next-intl/components/PageLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Navigation from './Navigation'

export default function PageLayout({ children, title }) {
return (
<div
style={{
padding: 24,
fontFamily: 'system-ui, sans-serif',
lineHeight: 1.5,
}}
>
<Navigation />
<div style={{ maxWidth: 510 }}>
<h1>{title}</h1>
{children}
</div>
</div>
)
}
7 changes: 7 additions & 0 deletions examples/with-i18n-next-intl/messages/about/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"About": {
"title": "About",
"description": "Diese Seite verwendet Übersetzungen aus <code>./messages/about/{locale}.json</code> und <code>./messages/shared/{locale}.json</code>. Übersetzungen von der Startseite und anderen Sprachen werden nicht geladen.",
"lastUpdated": "Dieses Beispiel wurde {lastUpdatedRelative} aktualisiert ({lastUpdated, date, short})."
}
}
7 changes: 7 additions & 0 deletions examples/with-i18n-next-intl/messages/about/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"About": {
"title": "About",
"description": "This page uses messages from <code>./messages/about/{locale}.json</code> and <code>./messages/shared/{locale}.json</code>. Messages from the start page and other locales are not loaded.",
"lastUpdated": "This example was updated {lastUpdatedRelative} ({lastUpdated, date, short})."
}
}
6 changes: 6 additions & 0 deletions examples/with-i18n-next-intl/messages/index/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Index": {
"title": "Start",
"description": "Diese Seite verwendet Übersetzungen aus <code>./messages/index/{locale}.json</code> und <code>./messages/shared/{locale}.json</code>. Übersetzungen von der Über-Seite und anderen Sprachen werden nicht geladen."
}
}
6 changes: 6 additions & 0 deletions examples/with-i18n-next-intl/messages/index/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Index": {
"title": "Home",
"description": "This page uses messages from <code>./messages/index/{locale}.json</code> and <code>./messages/shared/{locale}.json</code>. Messages from the about page and other locales are not loaded."
}
}
7 changes: 7 additions & 0 deletions examples/with-i18n-next-intl/messages/shared/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"Navigation": {
"index": "Start",
"about": "Über",
"switchLocale": "Zu {locale, select, de {Deutsch} en {Englisch}} wechseln"
}
}
7 changes: 7 additions & 0 deletions examples/with-i18n-next-intl/messages/shared/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"Navigation": {
"index": "Home",
"about": "About",
"switchLocale": "Switch to {locale, select, de {German} en {English}}"
}
}
6 changes: 6 additions & 0 deletions examples/with-i18n-next-intl/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
i18n: {
locales: ['en', 'de'],
defaultLocale: 'en',
},
}
16 changes: 16 additions & 0 deletions examples/with-i18n-next-intl/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "with-i18n-next-intl",
"version": "1.0.0",
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "latest",
"next-intl": "^1.0.1",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"license": "MIT"
}
29 changes: 29 additions & 0 deletions examples/with-i18n-next-intl/pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { NextIntlProvider } from 'next-intl'

export default function App({ Component, messages, pageProps }) {
return (
<NextIntlProvider
// To achieve consistent date, time and number formatting
// across the app, you can define a set of global formats.
formats={{
dateTime: {
short: {
day: 'numeric',
month: 'short',
year: 'numeric',
},
},
}}
messages={pageProps.messages}
// Providing an explicit value for `now` ensures consistent formatting of
// relative values regardless of the server or client environment.
now={new Date(pageProps.now)}
// Also an explicit time zone is helpful to ensure dates render the
// same way on the client as on the server, which might be located
// in a different time zone.
timeZone="Austria/Vienna"
>
<Component {...pageProps} />
</NextIntlProvider>
)
}
40 changes: 40 additions & 0 deletions examples/with-i18n-next-intl/pages/about.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useIntl, useTranslations } from 'next-intl'
import { useRouter } from 'next/router'
import Code from '../components/Code'
import PageLayout from '../components/PageLayout'

export default function About() {
const t = useTranslations('About')
const { locale } = useRouter()
const intl = useIntl()
const lastUpdated = new Date(2021, 0, 26, 17, 4, 45)

return (
<PageLayout title={t('title')}>
<p>
{t('description', {
locale,
code: (children) => <Code>{children}</Code>,
})}
</p>
<p>
{t('lastUpdated', {
lastUpdated,
lastUpdatedRelative: intl.formatRelativeTime(lastUpdated),
})}
</p>
</PageLayout>
)
}

export function getStaticProps({ locale }) {
return {
props: {
messages: {
...require(`../messages/shared/${locale}.json`),
...require(`../messages/about/${locale}.json`),
},
now: new Date().getTime(),
},
}
}
31 changes: 31 additions & 0 deletions examples/with-i18n-next-intl/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useTranslations } from 'next-intl'
import { useRouter } from 'next/router'
import Code from '../components/Code'
import PageLayout from '../components/PageLayout'

export default function Index() {
const t = useTranslations('Index')
const { locale } = useRouter()

return (
<PageLayout title={t('title')}>
<p>
{t('description', {
locale,
code: (children) => <Code>{children}</Code>,
})}
</p>
</PageLayout>
)
}

export function getStaticProps({ locale }) {
return {
props: {
messages: {
...require(`../messages/shared/${locale}.json`),
...require(`../messages/index/${locale}.json`),
},
},
}
}