mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
adds with react-i18next example (#2558)
* adds with react-i18next example * lint example code * remove unneeded .babelrc
This commit is contained in:
parent
ed733dc14d
commit
e7d91bf692
51
examples/with-react-i18next/README.md
Normal file
51
examples/with-react-i18next/README.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Getting started
|
||||
|
||||
Example with [react-i18next](https://github.com/i18next/react-i18next).
|
||||
|
||||
```bash
|
||||
# npm install
|
||||
# npm run dev
|
||||
```
|
||||
|
||||
**open:**
|
||||
|
||||
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)
|
14
examples/with-react-i18next/components/ExtendedComponent.js
Normal file
14
examples/with-react-i18next/components/ExtendedComponent.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import React from 'react'
|
||||
import { translate } from 'react-i18next'
|
||||
|
||||
function MyComponennt ({ t }) {
|
||||
return (
|
||||
<div>
|
||||
{t('extendedComponent')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Extended = translate('common')(MyComponennt)
|
||||
|
||||
export default Extended
|
10
examples/with-react-i18next/components/PureComponent.js
Normal file
10
examples/with-react-i18next/components/PureComponent.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import React from 'react'
|
||||
|
||||
// pure component just getting t function by props
|
||||
export default function PureComponent ({ t }) {
|
||||
return (
|
||||
<div>
|
||||
{t('common:pureComponent')}
|
||||
</div>
|
||||
)
|
||||
}
|
59
examples/with-react-i18next/i18n.js
Normal file
59
examples/with-react-i18next/i18n.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
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: true,
|
||||
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.defautlNS
|
||||
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
|
5
examples/with-react-i18next/locales/de/common.json
Normal file
5
examples/with-react-i18next/locales/de/common.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"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."
|
||||
}
|
6
examples/with-react-i18next/locales/de/home.json
Normal file
6
examples/with-react-i18next/locales/de/home.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"welcome": "Willkommen zu next.js",
|
||||
"link": {
|
||||
"gotoPage2": "Zur Seite 2"
|
||||
}
|
||||
}
|
6
examples/with-react-i18next/locales/de/page2.json
Normal file
6
examples/with-react-i18next/locales/de/page2.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"welcomePage2": "Dies ist die 2te Seite",
|
||||
"link": {
|
||||
"gotoPage1": "Zur Seite 1"
|
||||
}
|
||||
}
|
5
examples/with-react-i18next/locales/en/common.json
Normal file
5
examples/with-react-i18next/locales/en/common.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"integrates_react-i18next": "this sample 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."
|
||||
}
|
6
examples/with-react-i18next/locales/en/home.json
Normal file
6
examples/with-react-i18next/locales/en/home.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"welcome": "welcome to next.js",
|
||||
"link": {
|
||||
"gotoPage2": "Go to page 2"
|
||||
}
|
||||
}
|
6
examples/with-react-i18next/locales/en/page2.json
Normal file
6
examples/with-react-i18next/locales/en/page2.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"welcomePage2": "this is page 2",
|
||||
"link": {
|
||||
"gotoPage1": "Back to page 1"
|
||||
}
|
||||
}
|
25
examples/with-react-i18next/package.json
Normal file
25
examples/with-react-i18next/package.json
Normal file
|
@ -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.15.3",
|
||||
"i18next": "8.4.2",
|
||||
"i18next-browser-languagedetector": "2.0.0",
|
||||
"i18next-express-middleware": "1.0.5",
|
||||
"i18next-node-fs-backend": "1.0.0",
|
||||
"i18next-xhr-backend": "1.4.2",
|
||||
"next": "2.4.4",
|
||||
"react": "15.6.1",
|
||||
"react-dom": "15.6.1",
|
||||
"react-i18next": "4.6.3"
|
||||
}
|
||||
}
|
30
examples/with-react-i18next/pages/index.js
Normal file
30
examples/with-react-i18next/pages/index.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { translate } from 'react-i18next'
|
||||
import i18n from '../i18n'
|
||||
|
||||
import PureComponent from '../components/PureComponent'
|
||||
import ExtendedComponent from '../components/ExtendedComponent'
|
||||
|
||||
function Home ({ t, initialI18nStore }) {
|
||||
return (
|
||||
<div>
|
||||
{t('welcome')}
|
||||
<p>{t('common:integrates_react-i18next')}</p>
|
||||
<PureComponent t={t} />
|
||||
<ExtendedComponent />
|
||||
<Link href='/page2'><a>{t('link.gotoPage2')}</a></Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Extended = translate(['home', 'common'], { i18n, wait: process.browser })(Home)
|
||||
|
||||
// Passing down initial translations
|
||||
// use req.i18n instance on serverside to avoid overlapping requests set the language wrong
|
||||
Extended.getInitialProps = async ({ req }) => {
|
||||
if (req && !process.browser) return i18n.getInitialProps(req, ['home', 'common'])
|
||||
return {}
|
||||
}
|
||||
|
||||
export default Extended
|
30
examples/with-react-i18next/pages/page2.js
Normal file
30
examples/with-react-i18next/pages/page2.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { translate } from 'react-i18next'
|
||||
import i18n from '../i18n'
|
||||
|
||||
import PureComponent from '../components/PureComponent'
|
||||
import ExtendedComponent from '../components/ExtendedComponent'
|
||||
|
||||
function Page2 ({ t, initialI18nStore }) {
|
||||
return (
|
||||
<div>
|
||||
{t('welcomePage2')}
|
||||
<p>{t('common:integrates_react-i18next')}</p>
|
||||
<PureComponent t={t} />
|
||||
<ExtendedComponent />
|
||||
<Link href='/'><a>{t('link.gotoPage1')}</a></Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Extended = translate(['page2', 'common'], { i18n, wait: process.browser })(Page2)
|
||||
|
||||
// Passing down initial translations
|
||||
// use req.i18n instance on serverside to avoid overlapping requests set the language wrong
|
||||
Extended.getInitialProps = async ({ req }) => {
|
||||
if (req && !process.browser) return i18n.getInitialProps(req, ['page2', 'common'])
|
||||
return {}
|
||||
}
|
||||
|
||||
export default Extended
|
48
examples/with-react-i18next/server.js
Normal file
48
examples/with-react-i18next/server.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
const express = require('express')
|
||||
const path = require('path')
|
||||
const next = require('next')
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
const app = next({ dev })
|
||||
const handle = app.getRequestHandler()
|
||||
|
||||
const i18nextMiddleware = require('i18next-express-middleware')
|
||||
const Backend = require('i18next-node-fs-backend')
|
||||
const i18n = require('./i18n')
|
||||
|
||||
// init i18next with serverside settings
|
||||
// using i18next-express-middleware
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(i18nextMiddleware.LanguageDetector)
|
||||
.init({
|
||||
preload: ['en', 'de'], // preload all langages
|
||||
ns: ['common', 'home', 'page2'], // need to preload all the namespaces
|
||||
backend: {
|
||||
loadPath: path.join(__dirname, '/locales/{{lng}}/{{ns}}.json'),
|
||||
addPath: path.join(__dirname, '/locales/{{lng}}/{{ns}}.missing.json')
|
||||
}
|
||||
}, () => {
|
||||
// loaded translations we can bootstrap our routes
|
||||
app.prepare()
|
||||
.then(() => {
|
||||
const server = express()
|
||||
|
||||
// enable middleware for i18next
|
||||
server.use(i18nextMiddleware.handle(i18n))
|
||||
|
||||
// serve locales for client
|
||||
server.use('/locales', express.static(path.join(__dirname, '/locales')))
|
||||
|
||||
// missing keys
|
||||
server.post('/locales/add/:lng/:ns', i18nextMiddleware.missingKeyHandler(i18n))
|
||||
|
||||
// use next.js
|
||||
server.get('*', (req, res) => handle(req, res))
|
||||
|
||||
server.listen(3000, (err) => {
|
||||
if (err) throw err
|
||||
console.log('> Ready on http://localhost:3000')
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue