Skip to content

Commit

Permalink
Add redirect from '/en' to '/' (#149)
Browse files Browse the repository at this point in the history
  • Loading branch information
Faulik committed May 7, 2020
1 parent bcf3d5e commit f116c12
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 14 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,7 @@ Then, when we build the app, this **/pages** structure is going to be automatica
│   └── nested
│   └── index.js
├── en
│   ├── about.js
│   ├── index.js
│   └── nested
│   └── index.js
│   ├── [...path].js
├── es
│   ├── about.js
│   ├── index.js
Expand All @@ -106,6 +103,8 @@ Then, when we build the app, this **/pages** structure is going to be automatica
└── index.js
```

**Note**: `/en/[...path].js` is a redirect from `/en/some-route` to `/some-route`

Each page and its components can consume the translations with the `useTranslation` hook.

```js
Expand Down Expand Up @@ -208,7 +207,7 @@ In order to use each translation in the project, use the _translation id_ compos
| `defaultLanguage` | ISO of the default locale ("en" as default). | `string|function` | `"en"` |
| `allLanguages` | An array with all the languages to use in the project. | `Array<string>` | `[]` |
| `ignoreRoutes` | An array with all the routes to ignore in the middleware. This config property only effects using the `i18nMiddleware`, SO MAYBE YOU'LL NEVER NEED THIS. | `Array<string>` | `['/_next/', '/static/', '/favicon.ico', '/manifest.json', '/robots.txt']` |
| `redirectToDefaultLang` | When it's set to `true`, the route `/some-page` redirects to `/en/some-path` (if `en` is the default language). When it's set to `false`, entering to `/some-path` renders the page with the default language but without redirecting. | `boolean` | `false` |
| `redirectToDefaultLang` | When it's set to `true`, the route `/some-page` redirects to `/en/some-path` (if `en` is the default language). When it's set to `false`, entering to `/some-path` renders the page with the default language and the route `/en/some-path` redirects to `/some-page`. | `boolean` | `false` |
| `currentPagesDir` | A string with the directory where you have the pages code. This is needed for the "build step". | `string` | `"pages_"` |
| `finalPagesDir` | A string with the directory that is going to be used to build the pages. Only "pages" and "src/pages" are possible. This is needed for the "build step". | `string` | `"pages"` |
| `localesPath` | A string with the directory of JSONs locales. This is needed for the "build step". | `string` | `"locales"` |
Expand Down Expand Up @@ -767,6 +766,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
4 changes: 4 additions & 0 deletions __tests__/builder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,16 @@ describe('builder', () => {

test('Should inject lang to getStaticProps', () => {
const deflt = fs.readFileSync('examples/static-site/pages/index.js')
const en = fs.readFileSync('examples/static-site/pages/en/[...path].js')
const es = fs.readFileSync('examples/static-site/pages/es/index.js')
const ca = fs.readFileSync('examples/static-site/pages/ca/index.js')

expect(deflt.toString()).toContain(
`export const getStaticProps = ctx => _rest.getStaticProps({ ...ctx, lang: 'en' })`
)
expect(en.toString()).toContain(
`router.replace(\`\${router.query.path.slice(1).join('/')}\`)`
)
expect(es.toString()).toContain(
`export const getStaticProps = ctx => _rest.getStaticProps({ ...ctx, lang: 'es' })`
)
Expand Down
16 changes: 11 additions & 5 deletions __tests__/i18nMiddleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function loadServerWithMiddleware(config, port) {
return app
.prepare()
.then(() =>
server.listen(port, err => {
server.listen(port, (err) => {
if (err) throw err
})
)
Expand Down Expand Up @@ -56,7 +56,7 @@ describe('i18nMiddleware', () => {
;[
['/_next/chunk.js', 200, null],
['/ca/test', 200, 'ca'],
['/en/test', 200, 'en'],
['/en/test', 301, null, '/test'],
['/es/test', 200, 'es'],
['/favicon.ico', 200, null],
['/robots.txt', 200, null],
Expand All @@ -68,11 +68,14 @@ describe('i18nMiddleware', () => {
['/es/static/images/logo.svg', 301, null, '/static/images/logo.svg'],
['/test', 200, defaultLanguage],
].forEach(([path, status, lang, redirect]) => {
test(`${path} -> ${status}${redirect ? ` to ${redirect}` : ''}`, done => {
test(`${path} -> ${status}${
redirect ? ` to ${redirect}` : ''
}`, (done) => {
request(server1)
.get(path)
.set('Host', 'www.test.com')
.expect(status, (err, res) => {
.end((err, res) => {
expect(res.status).toBe(status)
if (lang) expect(res.text).toBe(lang)
done()
})
Expand All @@ -97,11 +100,14 @@ describe('i18nMiddleware', () => {
['/test', 301, null, `/${defaultLanguage}/test`],
[`/${defaultLanguage}/test`, 200, defaultLanguage],
].forEach(([path, status, lang, redirect]) => {
test(`${path} -> ${status}${redirect ? ` to ${redirect}` : ''}`, done => {
test(`${path} -> ${status}${
redirect ? ` to ${redirect}` : ''
}`, (done) => {
request(server2)
.get(path)
.set('Host', 'www.test.com')
.expect(status, (err, res) => {
expect(res.status).toBe(status)
if (lang) expect(res.text).toBe(lang)
done()
})
Expand Down
21 changes: 20 additions & 1 deletion cli/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,18 @@ async function createPagesDir() {
rimraf(finalPagesDir)
fs.mkdirSync(finalPagesDir)

getLangs().forEach(async (lang) => {
allLanguages.forEach(async (lang) => {
fs.mkdirSync(`${finalPagesDir}/${lang}`)
})

if (redirectToDefaultLang) {
fs.writeFileSync(`${finalPagesDir}/[...path].js`, getCatchAllTemplate())
fs.writeFileSync(`${finalPagesDir}/index.js`, getIndexRedirectTemplate())
} else {
fs.writeFileSync(
`${finalPagesDir}/${defaultLanguage}/[...path].js`,
getDefaultLanguageIndexRedirectTemplate()
)
}

if (logBuild) {
Expand Down Expand Up @@ -260,6 +265,20 @@ export * from './${defaultLanguage}/index'
`
}

function getDefaultLanguageIndexRedirectTemplate() {
return `import Error from 'next/error';
import { useRouter } from 'next/router';
export default function DefaultLanguageCatchAll() {
const router = useRouter()
if (Array.isArray(router.query.path)) {
router.replace(\`\${router.query.path.slice(1).join('/')}\`)
}
return null
}
`
}

function getCatchAllTemplate() {
return `import Error from 'next/error';
import { useRouter } from 'next/router';
Expand Down
16 changes: 13 additions & 3 deletions src/i18nMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export default function i18nMiddleware(config = {}) {
} = config

return (req, res, next) => {
const ignore = ignoreRoutes.some(r => req.url.startsWith(r))
const startsWithLang = allLanguages.some(l => req.url.startsWith(`/${l}`))
const ignore = ignoreRoutes.some((r) => req.url.startsWith(r))
const startsWithLang = allLanguages.some((l) => req.url.startsWith(`/${l}`))

/**
* Don't translate ignoreRoutes
Expand Down Expand Up @@ -43,8 +43,18 @@ export default function i18nMiddleware(config = {}) {
// Remove lang subpath to allow next.js to render the same page
req.url = req.url.replace(`/${lang}`, '') || '/'

// Redirect to root url from default language
if (!redirectToDefaultLang) {
const defaultLanguage = getDefaultLang(req, config) || 'en'

if (lang === defaultLanguage) {
res.redirect(301, req.url)
return
}
}

// Don't translate ignoreRoutes and redirect without lang
const [redirect] = ignoreRoutes.filter(r => req.url.startsWith(r))
const [redirect] = ignoreRoutes.filter((r) => req.url.startsWith(r))
if (redirect) {
res.redirect(301, redirect)
return
Expand Down

0 comments on commit f116c12

Please sign in to comment.