1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00

Update with react 18next to latest (#5017)

* update with-react-i18next supporting new _app.js

* update readme to not encourage cloning of repo
This commit is contained in:
Jan Mühlemann 2018-08-24 09:49:10 +02:00 committed by Tim Neutkens
parent bb13e941e5
commit e11d08ae45
11 changed files with 105 additions and 54 deletions

View file

@ -53,30 +53,30 @@ This example app shows how to integrate [react-i18next](https://github.com/i18ne
**Plus:**
* Routing and separating translations into multiple files (lazy load them on client routing)
* Child components (pure or using translation hoc)
- 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
- 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)
- [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)
- [i18next repository](https://github.com/i18next/i18next)
- [i18next documentation](https://www.i18next.com)
**Translation management:**
* [locize](http://locize.com)
- [locize](http://locize.com)

View file

@ -1,4 +1,4 @@
const i18next = require('i18next')
const i18n = require('i18next')
const XHR = require('i18next-xhr-backend')
const LanguageDetector = require('i18next-browser-languagedetector')
@ -10,7 +10,7 @@ const options = {
ns: ['common'],
defaultNS: 'common',
debug: process.env.NODE_ENV !== 'production',
debug: false, // process.env.NODE_ENV !== 'production',
saveMissing: true,
interpolation: {
@ -23,22 +23,20 @@ const options = {
}
}
const i18nInstance = i18next
// for browser use xhr backend to load translations and browser lng detector
if (process.browser) {
i18nInstance
i18n
.use(XHR)
// .use(Cache)
.use(LanguageDetector)
}
// initialize if not already initialized
if (!i18nInstance.isInitialized) i18nInstance.init(options)
if (!i18n.isInitialized) i18n.init(options)
// a simple helper to getInitialProps passed on loaded i18n data
const getInitialProps = (req, namespaces) => {
if (!namespaces) namespaces = i18nInstance.options.defaultNS
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
@ -58,8 +56,4 @@ const getInitialProps = (req, namespaces) => {
}
}
module.exports = {
getInitialProps,
i18nInstance,
I18n: i18next.default
}
module.exports = i18n

View file

@ -1,8 +1,8 @@
import { translate, loadNamespaces } from 'react-i18next'
import { getInitialProps, I18n } from '../i18n'
import { translate } from 'react-i18next'
import i18n from '../i18n'
export const withI18next = (namespaces = ['common']) => ComposedComponent => {
const Extended = translate(namespaces, { i18n: I18n, wait: process.browser })(
const Extended = translate(namespaces, { i18n, wait: process.browser })(
ComposedComponent
)
@ -12,11 +12,8 @@ export const withI18next = (namespaces = ['common']) => ComposedComponent => {
: {}
const i18nInitialProps = ctx.req
? getInitialProps(ctx.req, namespaces)
: await loadNamespaces({
components: [{ props: { namespaces } }],
i18n: I18n
})
? i18n.getInitialProps(ctx.req, namespaces)
: {}
return {
...composedInitialProps,

View file

@ -1,6 +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"
"gotoPage2": "Zur Seite 2",
"gotoPage3": "Zur Seite 3 (no hoc)"
}
}

View file

@ -1,6 +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"
"gotoPage2": "Go to page 2",
"gotoPage3": "Go to page 3 (no hoc)"
}
}

View file

@ -11,15 +11,15 @@
"author": "",
"license": "MIT",
"dependencies": {
"express": "4.16.2",
"i18next": "10.4.1",
"i18next-browser-languagedetector": "2.1.0",
"i18next-express-middleware": "1.0.10",
"i18next-node-fs-backend": "1.0.0",
"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": "5.0.0",
"react": "16.2.0",
"react-dom": "16.2.0",
"react-i18next": "7.3.6"
"next": "^6.1.1",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-i18next": "7.8.1"
}
}

View file

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

View file

@ -6,15 +6,29 @@ import ExtendedComponent from '../components/ExtendedComponent'
import ComponentWithTrans from '../components/ComponentWithTrans'
import { withI18next } from '../lib/withI18next'
export default withI18next(['home', 'common'])(({ t, initialI18nStore }) => (
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

View file

@ -6,7 +6,7 @@ import ExtendedComponent from '../components/ExtendedComponent'
import ComponentWithTrans from '../components/ComponentWithTrans'
import { withI18next } from '../lib/withI18next'
export default withI18next(['page2', 'common'])(({ t, initialI18nStore }) => (
const Page2 = ({ t }) => (
<div>
<h1>{t('welcomePage2')}</h1>
<p>{t('common:integrates_react-i18next')}</p>
@ -17,4 +17,6 @@ export default withI18next(['page2', 'common'])(({ t, initialI18nStore }) => (
<a>{t('link.gotoPage1')}</a>
</Link>
</div>
))
)
export default withI18next(['page2', 'common'])(Page2)

View file

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

View file

@ -8,11 +8,11 @@ const handle = app.getRequestHandler()
const i18nextMiddleware = require('i18next-express-middleware')
const Backend = require('i18next-node-fs-backend')
const { i18nInstance } = require('./i18n')
const i18n = require('./i18n')
// init i18next with serverside settings
// using i18next-express-middleware
i18nInstance
i18n
.use(Backend)
.use(i18nextMiddleware.LanguageDetector)
.init({
@ -30,13 +30,13 @@ i18nInstance
const server = express()
// enable middleware for i18next
server.use(i18nextMiddleware.handle(i18nInstance))
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(i18nInstance))
server.post('/locales/add/:lng/:ns', i18nextMiddleware.missingKeyHandler(i18n))
// use next.js
server.get('*', (req, res) => handle(req, res))