Skip to content

Commit fd7125a

Browse files
committed
refactor: Use rewrites instead of custom server (#689)
1 parent 9eb6add commit fd7125a

24 files changed

+2046
-2048
lines changed

.circleci/config.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ commands:
33
build-and-test:
44
steps:
55
- checkout
6+
- run:
7+
name: Install rsync
8+
command: sudo apt install rsync
69
- restore_cache:
710
name: Restore cache (main)
811
keys:
@@ -38,19 +41,19 @@ commands:
3841
command: yarn test
3942

4043
jobs:
41-
node-v8:
44+
node-v10:
4245
docker:
43-
- image: circleci/node:8-browsers
46+
- image: circleci/node:10-browsers
4447
steps:
4548
- build-and-test
46-
node-v10:
49+
node-v12:
4750
docker:
48-
- image: circleci/node:10-browsers
51+
- image: circleci/node:12-browsers
4952
steps:
5053
- build-and-test
5154

5255
workflows:
5356
node-multi-build:
5457
jobs:
55-
- node-v8
5658
- node-v10
59+
- node-v12

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ out
1818

1919
# Testing
2020
coverage/
21+
.e2e
2122

2223
# npm
2324
package-lock.json

README.md

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,8 @@ It's recommended to export this `NextI18Next` instance from a single file in you
7474
After creating and exporting your `NextI18Next` instance, you need to take the following steps to get things working:
7575

7676
1. Create an `_app.js` file inside your `pages` directory, and wrap it with the `NextI18Next.appWithTranslation` higher order component (HOC). You can see this approach in the [examples/simple/pages/_app.js](./examples/simple/pages/_app.js).
77-
Your app component must either extend `App` if it's a class component or define a `getInitialProps` if it's a function component [(explanation here)](https://github.com/isaachinman/next-i18next/issues/615#issuecomment-575578375).
78-
2. Create a `server.js` file inside your root directory, initialise an [express](https://www.npmjs.com/package/express) server, and use the `nextI18NextMiddleware` middleware with your `nextI18Next` instance passed in. You can see this approach in the [examples/simple/server.js](./examples/simple/server.js).
79-
3. Update the scripts in `package.json` to:
80-
```
81-
{
82-
"scripts": {
83-
"dev": "node server.js",
84-
"build": "next build",
85-
"start": "NODE_ENV=production node server.js"
86-
}
87-
}
88-
```
89-
For more info, see [the NextJs section on custom servers](https://github.com/zeit/next.js#custom-server-and-routing).
77+
Your app component must either extend `App` if it's a class component or define a `getInitialProps` if it's a functional component [(explanation here)](https://github.com/isaachinman/next-i18next/issues/615#issuecomment-575578375).
78+
2. Create a `next.config.js` file inside your root directory if you want to use locale subpaths. You can see this approach in the [examples/simple/next.config.js](./examples/simple/next.config.js).
9079

9180
Note: You can pass `shallowRender: true` into config options to avoid triggering getInitialProps when `changeLanguage` method is invoked.
9281

@@ -138,6 +127,8 @@ new NextI18Next({
138127
})
139128
```
140129

130+
The `localeSubpaths` object must also be passed into `next.config.js`, via the `nextI18NextRewrites` util, which you can import from `next-i18next/rewrites`.
131+
141132
The `localeSubpaths` option is a key/value mapping, where keys are the locale itself (case sensitive) and values are the subpath without slashes.
142133

143134
Now, all your page routes will be duplicated across all your locale subpaths. Here's an example:
@@ -158,7 +149,7 @@ myapp.com/german
158149
myapp.com/eng
159150
```
160151

161-
When using the localeSubpaths option, our middleware may redirect without calling any subsequent middleware. Therefore, if there are any critical middleware that must run before this redirect, ensure that you place it before the `nextI18NextMiddleware` middleware.
152+
When using the localeSubpaths option, our middleware will redirect as needed in the wrapped `getInitialProps` one level above your `_app`, so none of your code will be called.
162153

163154
The main "gotcha" with locale subpaths is routing. We want to be able to route to "naked" routes, and not have to worry about the locale subpath part of the route:
164155

@@ -183,8 +174,7 @@ const SomeLink = () => (
183174
)
184175
```
185176

186-
We can also navigate imperatively with locale subpaths by importing `Router` from your `NextI18Next` instance.
187-
The exported Router shares the same API as the native Next Router. The push, replace, and prefetch functions will automatically prepend locale subpaths.
177+
We can also navigate imperatively with locale subpaths by importing `Router` from your `NextI18Next` instance. The exported Router shares the same API as the native Next Router. The push, replace, and prefetch functions will automatically prepend locale subpaths.
188178

189179
```jsx
190180
import React from 'react'
@@ -259,7 +249,6 @@ _This table contains options which are specific to next-i18next. All other [i18n
259249
## Notes
260250

261251
- [`next export` will result in a _client-side only_ React application.](https://github.com/isaachinman/next-i18next/issues/10)
262-
- [Serverless (e.g. Now 2.0) is not currently supported](https://github.com/isaachinman/next-i18next/issues/274).
263252
- [To add a `lang` attribute to your top-level html DOM node, you must create a `_document.js` file.](https://github.com/isaachinman/next-i18next/issues/20#issuecomment-443461652)
264253
- [Localising `next/head` requires special consideration due to NextJs internals](https://github.com/isaachinman/next-i18next/issues/251#issuecomment-479421852).
265254

__tests__/middlewares/next-i18next-middleware.test.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,18 @@ describe('next-18next middleware', () => {
2626
i18n: testI18NextConfig
2727
}
2828
res = {
29-
redirect: jest.fn(),
30-
header: jest.fn()
29+
end: jest.fn(),
30+
writeHead: jest.fn(),
31+
setHeader: jest.fn()
3132
}
3233
next = jest.fn()
3334
})
3435

3536
afterEach(() => {
3637
i18nextMiddleware.handle.mockClear()
3738

38-
res.redirect.mockReset()
39-
res.header.mockReset()
39+
res.writeHead.mockReset()
40+
res.setHeader.mockReset()
4041
})
4142

4243
const callAllMiddleware = () => {
@@ -76,7 +77,7 @@ describe('next-18next middleware', () => {
7677
expect(req.i18n).toBeDefined()
7778
})
7879

79-
it('adds lng to query parameters and removes from url for i18next processing', () => {
80+
it('adds lng to query parameters', () => {
8081
const language = 'de'
8182
const subpath = 'german'
8283
req = {
@@ -95,11 +96,7 @@ describe('next-18next middleware', () => {
9596

9697
callAllMiddleware()
9798

98-
expect(req.url).toBe('/page1')
99-
expect(req.query).toEqual({
100-
lng: language,
101-
subpath,
102-
})
99+
expect(req.url).toBe('/german/page1')
103100

104101
expect(next).toBeCalledTimes(1)
105102
})
@@ -128,10 +125,10 @@ describe('next-18next middleware', () => {
128125
expect(req.url).toBe('/page1')
129126
expect(req.query).toEqual({})
130127

131-
expect(res.redirect).toHaveBeenCalledWith(302, '/german/page1')
132-
expect(res.header).toHaveBeenNthCalledWith(1, 'Cache-Control', 'private, no-cache, no-store, must-revalidate')
133-
expect(res.header).toHaveBeenNthCalledWith(2, 'Expires', '-1')
134-
expect(res.header).toHaveBeenNthCalledWith(3, 'Pragma', 'no-cache')
128+
expect(res.writeHead).toHaveBeenCalledWith(302, { Location: '/german/page1' })
129+
expect(res.setHeader).toHaveBeenNthCalledWith(1, 'Cache-Control', 'private, no-cache, no-store, must-revalidate')
130+
expect(res.setHeader).toHaveBeenNthCalledWith(2, 'Expires', '-1')
131+
expect(res.setHeader).toHaveBeenNthCalledWith(3, 'Pragma', 'no-cache')
135132
expect(next).toBeCalledTimes(0)
136133
})
137134

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { nextI18NextRewrites } = require('next-i18next/rewrites')
2+
3+
const localeSubpaths = {
4+
en: 'en',
5+
de: 'de',
6+
}
7+
8+
module.exports = {
9+
publicRuntimeConfig: {
10+
localeSubpaths,
11+
},
12+
experimental: {
13+
async rewrites() {
14+
return [
15+
...nextI18NextRewrites(localeSubpaths)
16+
]
17+
}
18+
}
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { nextI18NextRewrites } = require('next-i18next/rewrites')
2+
3+
const localeSubpaths = {
4+
de: 'de',
5+
}
6+
7+
module.exports = {
8+
publicRuntimeConfig: {
9+
localeSubpaths,
10+
},
11+
experimental: {
12+
async rewrites() {
13+
return [
14+
...nextI18NextRewrites(localeSubpaths)
15+
]
16+
}
17+
}
18+
}

examples/simple/i18n.js

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,7 @@
1-
/*
2-
Do not copy/paste this file. It is used internally
3-
to manage end-to-end test suites.
4-
*/
5-
61
const NextI18Next = require('next-i18next').default
72
const { localeSubpaths } = require('next/config').default().publicRuntimeConfig
83

9-
const localeSubpathVariations = {
10-
none: {},
11-
foreign: {
12-
de: 'de',
13-
},
14-
all: {
15-
en: 'en',
16-
de: 'de',
17-
},
18-
}
19-
204
module.exports = new NextI18Next({
215
otherLanguages: ['de'],
22-
localeSubpaths: localeSubpathVariations[localeSubpaths],
6+
localeSubpaths,
237
})

examples/simple/index.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

examples/simple/next.config.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1+
const { nextI18NextRewrites } = require('next-i18next/rewrites')
2+
3+
const localeSubpaths = {}
4+
15
module.exports = {
26
publicRuntimeConfig: {
3-
localeSubpaths: typeof process.env.LOCALE_SUBPATHS === 'string'
4-
? process.env.LOCALE_SUBPATHS
5-
: 'none',
7+
localeSubpaths,
68
},
9+
experimental: {
10+
async rewrites() {
11+
return [
12+
...nextI18NextRewrites(localeSubpaths)
13+
]
14+
}
15+
}
716
}

examples/simple/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
"node": ">=8"
88
},
99
"scripts": {
10-
"dev": "node index.js",
10+
"dev": "next",
1111
"build": "next build",
12-
"start": "NODE_ENV=production node index.js"
12+
"start": "next start -p ${PORT:=3000}"
1313
},
1414
"devDependencies": {},
1515
"dependencies": {
1616
"express": "^4.16.4",
1717
"i18next": "^14.0.1",
18-
"next": "^9.1.6",
18+
"next": "^9.3.5",
1919
"next-i18next": "link:../../"
2020
}
21-
}
21+
}

examples/simple/server.js

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)