1
0
Fork 0
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:
Jan Mühlemann 2017-07-16 22:19:45 +02:00 committed by Tim Neutkens
parent ed733dc14d
commit e7d91bf692
14 changed files with 301 additions and 0 deletions

View 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)

View 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

View 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>
)
}

View 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

View 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."
}

View file

@ -0,0 +1,6 @@
{
"welcome": "Willkommen zu next.js",
"link": {
"gotoPage2": "Zur Seite 2"
}
}

View file

@ -0,0 +1,6 @@
{
"welcomePage2": "Dies ist die 2te Seite",
"link": {
"gotoPage1": "Zur Seite 1"
}
}

View 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."
}

View file

@ -0,0 +1,6 @@
{
"welcome": "welcome to next.js",
"link": {
"gotoPage2": "Go to page 2"
}
}

View file

@ -0,0 +1,6 @@
{
"welcomePage2": "this is page 2",
"link": {
"gotoPage1": "Back to page 1"
}
}

View 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"
}
}

View 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

View 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

View 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')
})
})
})