Skip to content

Commit

Permalink
add new nextjs sample
Browse files Browse the repository at this point in the history
  • Loading branch information
jamuhl committed Jul 13, 2018
1 parent 00b0e69 commit 02bffe8
Show file tree
Hide file tree
Showing 19 changed files with 4,791 additions and 0 deletions.
82 changes: 82 additions & 0 deletions example/nextjs_withAppJS/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-react-i18next)

# Internationalization with [react-i18next](https://github.com/i18next/react-i18next).

## How to use

### Using `create-next-app`

Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example:

```bash
npx create-next-app --example with-react-i18next with-react-i18next-app
# or
yarn create next-app --example with-react-i18next with-react-i18next-app
```

### Download manually

Download the example [or clone the repo](https://github.com/zeit/next.js):

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

Install it and run:

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

Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download))

```bash
now
```

### Testing the app

auto detecting user language: [http://localhost:3000](http://localhost:3000)

german: [http://localhost:3000/?lng=de](http://localhost:3000/?lng=de)

english: [http://localhost:3000/?lng=en](http://localhost:3000/?lng=en)

## The idea behind the example

This example app shows how to integrate [react-i18next](https://github.com/i18next/react-i18next) with [Next](https://github.com/zeit/next.js).

**Plus:**

* Routing and separating translations into multiple files (lazy load them on client routing)
* Child components (pure or using translation hoc)

### Features of this example app

* Server-side language negotiation
* Full control and usage of i18next on express server using [i18next-express-middleware](https://github.com/i18next/i18next-express-middleware) which asserts no async request collisions resulting in wrong language renderings
* Support for save missing features to get untranslated keys automatically created `locales/{lng}/{namespace}.missing.json` -> never miss to translate a key
* Proper pass down on translations via initialProps
* Taking advantage of multiple translation files including lazy loading on client (no need to load all translations upfront)
* Use express to also serve translations for clientside
* In contrast to react-intl the translations are visible both during development and in production

### learn more

* [next.js](https://github.com/zeit/next.js)
* [react-i18next repository](https://github.com/i18next/react-i18next)
* [react-i18next documentation](https://react.i18next.com)

**Translation features:**

* [i18next repository](https://github.com/i18next/i18next)
* [i18next documentation](https://www.i18next.com)

**Translation management:**

* [locize](http://locize.com)
12 changes: 12 additions & 0 deletions example/nextjs_withAppJS/components/ComponentWithTrans.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import { Trans } from 'react-i18next'

export default function ComponentWithTrans () {
return (
<p>
<Trans i18nKey='common:transComponent'>
Alternatively, you can use <code>Trans</code> component.
</Trans>
</p>
)
}
10 changes: 10 additions & 0 deletions example/nextjs_withAppJS/components/ExtendedComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'
import { translate } from 'react-i18next'

function MyComponent ({ t }) {
return <p>{t('extendedComponent')}</p>
}

const Extended = translate('common')(MyComponent)

export default Extended
5 changes: 5 additions & 0 deletions example/nextjs_withAppJS/components/PureComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react'

export default function PureComponent ({ t }) {
return <p>{t('common:pureComponent')}</p>
}
60 changes: 60 additions & 0 deletions example/nextjs_withAppJS/i18n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const i18n = require('i18next')
const XHR = require('i18next-xhr-backend')
const LanguageDetector = require('i18next-browser-languagedetector')

const options = {
fallbackLng: 'en',
load: 'languageOnly', // we only provide en, de -> no region specific locals like en-US, de-DE

// have a common namespace used around the full app
ns: ['common'],
defaultNS: 'common',

debug: false, //process.env.NODE_ENV !== 'production',
saveMissing: true,

interpolation: {
escapeValue: false, // not needed for react!!
formatSeparator: ',',
format: (value, format, lng) => {
if (format === 'uppercase') return value.toUpperCase()
return value
}
}
}


// for browser use xhr backend to load translations and browser lng detector
if (process.browser) {
i18n
.use(XHR)
// .use(Cache)
.use(LanguageDetector)
}

// initialize if not already initialized
if (!i18n.isInitialized) i18n.init(options)

// a simple helper to getInitialProps passed on loaded i18n data
i18n.getInitialProps = (req, namespaces) => {
if (!namespaces) namespaces = i18n.options.defaultNS
if (typeof namespaces === 'string') namespaces = [namespaces]

req.i18n.toJSON = () => null // do not serialize i18next instance and send to client

const initialI18nStore = {}
req.i18n.languages.forEach((l) => {
initialI18nStore[l] = {}
namespaces.forEach((ns) => {
initialI18nStore[l][ns] = (req.i18n.services.resourceStore.data[l] || {})[ns] || {}
})
})

return {
i18n: req.i18n, // use the instance on req - fixed language on request (avoid issues in race conditions with lngs of different users)
initialI18nStore,
initialLanguage: req.i18n.language
}
}

module.exports = i18n;
25 changes: 25 additions & 0 deletions example/nextjs_withAppJS/lib/withI18next.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { translate, loadNamespaces } from 'react-i18next'
import i18n from '../i18n'

export const withI18next = (namespaces = ['common']) => ComposedComponent => {
const Extended = translate(namespaces, { i18n, wait: process.browser })(
ComposedComponent
)

Extended.getInitialProps = async (ctx) => {
const composedInitialProps = ComposedComponent.getInitialProps
? await ComposedComponent.getInitialProps(ctx)
: {}

const i18nInitialProps = ctx.req
? i18n.getInitialProps(ctx.req, namespaces)
: {};

return {
...composedInitialProps,
...i18nInitialProps
}
}

return Extended
}
6 changes: 6 additions & 0 deletions example/nextjs_withAppJS/locales/de/common.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"integrates_react-i18next": "Dieses Beispiel integriert react-i18next für einfache Übersetzung.",
"pureComponent": "Entweder t Funktion an Komponente via props weiterreichen.",
"extendedComponent": "Oder die Komponente erneut mit dem translate hoc erweiteren.",
"transComponent": "Sonst können Sie auch die Komponente <1>Trans</1> verwenden."
}
9 changes: 9 additions & 0 deletions example/nextjs_withAppJS/locales/de/home.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"welcome": "Willkommen zu next.js",
"sample_test": "test words for de",
"sample_button": "fire in the wind for de",
"link": {
"gotoPage2": "Zur Seite 2",
"gotoPage3": "Zur Seite 3 (no hoc)"
}
}
6 changes: 6 additions & 0 deletions example/nextjs_withAppJS/locales/de/page2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"welcomePage2": "Dies ist die 2te Seite",
"link": {
"gotoPage1": "Zur Seite 1"
}
}
6 changes: 6 additions & 0 deletions example/nextjs_withAppJS/locales/en/common.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"integrates_react-i18next": "This example integrates react-i18next for simple internationalization.",
"pureComponent": "You can either pass t function to child components.",
"extendedComponent": "Or wrap your component using the translate hoc provided by react-i18next.",
"transComponent": "Alternatively, you can use <1>Trans</1> component."
}
9 changes: 9 additions & 0 deletions example/nextjs_withAppJS/locales/en/home.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"welcome": "welcome to next.js",
"sample_test": "test words for en",
"sample_button": "fire in the wind for en",
"link": {
"gotoPage2": "Go to page 2",
"gotoPage3": "Go to page 3 (no hoc)"
}
}
6 changes: 6 additions & 0 deletions example/nextjs_withAppJS/locales/en/page2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"welcomePage2": "this is page 2",
"link": {
"gotoPage1": "Back to page 1"
}
}
25 changes: 25 additions & 0 deletions example/nextjs_withAppJS/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "react-i18next-nextjs-example",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
},
"author": "",
"license": "MIT",
"dependencies": {
"express": "4.16.3",
"i18next": "11.3.6",
"i18next-browser-languagedetector": "2.2.0",
"i18next-express-middleware": "1.2.0",
"i18next-node-fs-backend": "1.2.1",
"i18next-xhr-backend": "1.5.1",
"next": "^6.1.1",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-i18next": "7.8.1"
}
}
24 changes: 24 additions & 0 deletions example/nextjs_withAppJS/pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import App, { Container } from "next/app";
import { I18n as I18nR } from "react-i18next";
import i18n from "../i18n";

export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
console.warn(pageProps)
return (
<Container>
<I18nR ns="common" i18n={(pageProps && pageProps.i18n) || i18n } wait>
{
(t) => (
<div>
<h1>{t('common:integrates_react-i18next')}</h1>
<Component {...pageProps} />
</div>
)
}
</I18nR>
</Container>
);
}
}
37 changes: 37 additions & 0 deletions example/nextjs_withAppJS/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'
import Link from 'next/link'

import PureComponent from '../components/PureComponent'
import ExtendedComponent from '../components/ExtendedComponent'
import ComponentWithTrans from '../components/ComponentWithTrans'
import { withI18next } from '../lib/withI18next'

const TestContent = withI18next(['home', 'common'])(({ t, initialI18nStore }) => (
<div>
<h1>{t('welcome')}</h1>
<p>{t('common:integrates_react-i18next')}</p>
<p>{t('sample_test')}</p>
<div>
<button>{t('sample_button')}</button>
</div>
<PureComponent t={t} />
<ExtendedComponent />
<ComponentWithTrans />
<Link href='/page2'>
<a>{t('link.gotoPage2')}</a>
</Link>
<br />
<Link href='/page3'>
<a>{t('link.gotoPage3')}</a>
</Link>
</div>
))


const Test = () => {
return(
<TestContent/>
)
}

export default Test;
22 changes: 22 additions & 0 deletions example/nextjs_withAppJS/pages/page2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react'
import Link from 'next/link'

import PureComponent from '../components/PureComponent'
import ExtendedComponent from '../components/ExtendedComponent'
import ComponentWithTrans from '../components/ComponentWithTrans'
import { withI18next } from '../lib/withI18next'

const Page2 = ({ t }) => (
<div>
<h1>{t('welcomePage2')}</h1>
<p>{t('common:integrates_react-i18next')}</p>
<PureComponent t={t} />
<ExtendedComponent />
<ComponentWithTrans />
<Link href='/'>
<a>{t('link.gotoPage1')}</a>
</Link>
</div>
)

export default withI18next(['page2', 'common'])(Page2)
14 changes: 14 additions & 0 deletions example/nextjs_withAppJS/pages/page3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// a page not using i18next - no hoc - not t function
import React from 'react';
import Link from 'next/link'

export default () => {
return (
<div>
<h1>Hello Page 3</h1>
<Link href='/'>
<a>back</a>
</Link>
</div>
)
};
Loading

0 comments on commit 02bffe8

Please sign in to comment.