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:
parent
bb13e941e5
commit
e11d08ae45
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
24
examples/with-react-i18next/pages/_app.js
Normal file
24
examples/with-react-i18next/pages/_app.js
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
14
examples/with-react-i18next/pages/page3.js
Normal file
14
examples/with-react-i18next/pages/page3.js
Normal 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>
|
||||
)
|
||||
}
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue