Skip to content

Commit

Permalink
Pass locales to getStaticPaths for i18n (#18077)
Browse files Browse the repository at this point in the history
This makes sure the `locales` are passed to `getStaticPaths` and also disables the removing the default locale from the path when the default locale is the preferred header. It also updates tests to ensure the domain redirects are working as expected.  

x-ref: #17370
  • Loading branch information
ijjk committed Oct 21, 2020
1 parent c28d39e commit 2ab3445
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 69 deletions.
2 changes: 1 addition & 1 deletion packages/next/build/utils.ts
Expand Up @@ -544,7 +544,7 @@ export async function buildStaticPaths(
// Get the default list of allowed params.
const _validParamKeys = Object.keys(_routeMatcher(page))

const staticPathsResult = await getStaticPaths()
const staticPathsResult = await getStaticPaths({ locales })

const expectedReturnVal =
`Expected: { paths: [], fallback: boolean }\n` +
Expand Down
40 changes: 26 additions & 14 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Expand Up @@ -264,28 +264,40 @@ const nextServerlessLoader: loader.Loader = function () {
// check if the locale prefix matches a domain's defaultLocale
// and we're on a locale specific domain if so redirect to that domain
if (detectedDomain) {
const matchedDomain = detectDomainLocale(
i18n.domains,
undefined,
detectedLocale
)
// if (detectedDomain) {
// const matchedDomain = detectDomainLocale(
// i18n.domains,
// undefined,
// detectedLocale
// )
// if (matchedDomain) {
// localeDomainRedirect = \`http\${
// matchedDomain.http ? '' : 's'
// }://\${matchedDomain.domain}\`
// }
// }
} else if (detectedDomain) {
const matchedDomain = detectDomainLocale(
i18n.domains,
undefined,
acceptPreferredLocale
)
if (matchedDomain) {
localeDomainRedirect = \`http\${
matchedDomain.http ? '' : 's'
}://\${matchedDomain.domain}\`
}
if (matchedDomain && matchedDomain.domain !== detectedDomain.domain) {
localeDomainRedirect = \`http\${matchedDomain.http ? '' : 's'}://\${
matchedDomain.domain
}\`
}
}
const denormalizedPagePath = denormalizePagePath(parsedUrl.pathname || '/')
const detectedDefaultLocale =
!detectedLocale ||
detectedLocale.toLowerCase() === defaultLocale.toLowerCase()
const shouldStripDefaultLocale =
detectedDefaultLocale &&
denormalizedPagePath.toLowerCase() === \`/\${i18n.defaultLocale.toLowerCase()}\`
const shouldStripDefaultLocale = false
// detectedDefaultLocale &&
// denormalizedPagePath.toLowerCase() === \`/\${i18n.defaultLocale.toLowerCase()}\`
const shouldAddLocalePrefix =
!detectedDefaultLocale && denormalizedPagePath === '/'
Expand Down
42 changes: 27 additions & 15 deletions packages/next/next-server/server/next-server.ts
Expand Up @@ -345,29 +345,41 @@ export default class Server {

// check if the locale prefix matches a domain's defaultLocale
// and we're on a locale specific domain if so redirect to that domain
if (detectedDomain) {
const matchedDomain = detectDomainLocale(
i18n.domains,
undefined,
detectedLocale
)
// if (detectedDomain) {
// const matchedDomain = detectDomainLocale(
// i18n.domains,
// undefined,
// detectedLocale
// )

// if (matchedDomain) {
// localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${
// matchedDomain?.domain
// }`
// }
// }
} else if (detectedDomain) {
const matchedDomain = detectDomainLocale(
i18n.domains,
undefined,
acceptPreferredLocale
)

if (matchedDomain) {
localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${
matchedDomain?.domain
}`
}
if (matchedDomain && matchedDomain.domain !== detectedDomain.domain) {
localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${
matchedDomain.domain
}`
}
}

const denormalizedPagePath = denormalizePagePath(pathname || '/')
const detectedDefaultLocale =
!detectedLocale ||
detectedLocale.toLowerCase() === defaultLocale.toLowerCase()
const shouldStripDefaultLocale =
detectedDefaultLocale &&
denormalizedPagePath.toLowerCase() ===
`/${i18n.defaultLocale.toLowerCase()}`
const shouldStripDefaultLocale = false
// detectedDefaultLocale &&
// denormalizedPagePath.toLowerCase() ===
// `/${i18n.defaultLocale.toLowerCase()}`

const shouldAddLocalePrefix =
!detectedDefaultLocale && denormalizedPagePath === '/'
Expand Down
10 changes: 7 additions & 3 deletions packages/next/types/index.d.ts
Expand Up @@ -105,14 +105,18 @@ export type InferGetStaticPropsType<T> = T extends GetStaticProps<infer P, any>
? P
: never

export type GetStaticPathsContext = {
locales?: string[]
}

export type GetStaticPathsResult<P extends ParsedUrlQuery = ParsedUrlQuery> = {
paths: Array<string | { params: P; locale?: string }>
fallback: boolean | 'unstable_blocking'
}

export type GetStaticPaths<
P extends ParsedUrlQuery = ParsedUrlQuery
> = () => Promise<GetStaticPathsResult<P>>
export type GetStaticPaths<P extends ParsedUrlQuery = ParsedUrlQuery> = (
context: GetStaticPathsContext
) => Promise<GetStaticPathsResult<P>>

export type GetServerSidePropsContext<
Q extends ParsedUrlQuery = ParsedUrlQuery
Expand Down
9 changes: 8 additions & 1 deletion test/integration/i18n-support/pages/gsp/fallback/[slug].js
Expand Up @@ -33,7 +33,14 @@ export const getStaticProps = ({ params, locale, locales }) => {
}
}

export const getStaticPaths = () => {
export const getStaticPaths = ({ locales }) => {
// make sure locales were provided correctly
if (!locales || locales.length !== 7) {
throw new Error(
'locales missing in getStaticPaths!! got: ' + JSON.stringify(locales)
)
}

return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map((slug) => ({
Expand Down
71 changes: 36 additions & 35 deletions test/integration/i18n-support/test/index.test.js
Expand Up @@ -397,19 +397,19 @@ function runTests(isDev) {
expect(JSON.parse($2('#router-locales').text())).toEqual(locales)
})

it('should strip locale prefix for default locale with locale domains', async () => {
it('should not strip locale prefix for default locale with locale domains', async () => {
const res = await fetchViaHTTP(appPort, '/fr', undefined, {
headers: {
host: 'example.fr',
},
redirect: 'manual',
})

expect(res.status).toBe(307)
expect(res.status).toBe(200)

const result = url.parse(res.headers.get('location'), true)
expect(result.pathname).toBe('/')
expect(result.query).toEqual({})
// const result = url.parse(res.headers.get('location'), true)
// expect(result.pathname).toBe('/')
// expect(result.query).toEqual({})

const res2 = await fetchViaHTTP(appPort, '/nl-BE', undefined, {
headers: {
Expand All @@ -418,28 +418,28 @@ function runTests(isDev) {
redirect: 'manual',
})

expect(res2.status).toBe(307)
expect(res2.status).toBe(200)

const result2 = url.parse(res2.headers.get('location'), true)
expect(result2.pathname).toBe('/')
expect(result2.query).toEqual({})
// const result2 = url.parse(res2.headers.get('location'), true)
// expect(result2.pathname).toBe('/')
// expect(result2.query).toEqual({})
})

it('should set locale cookie when removing default locale and accept-lang doesnt match', async () => {
const res = await fetchViaHTTP(appPort, '/en-US', undefined, {
headers: {
'accept-language': 'nl',
},
redirect: 'manual',
})
// ('should set locale cookie when removing default locale and accept-lang doesnt match', async () => {
// const res = await fetchViaHTTP(appPort, '/en-US', undefined, {
// headers: {
// 'accept-language': 'nl',
// },
// redirect: 'manual',
// })

expect(res.status).toBe(307)
// expect(res.status).toBe(307)

const parsedUrl = url.parse(res.headers.get('location'), true)
expect(parsedUrl.pathname).toBe('/')
expect(parsedUrl.query).toEqual({})
expect(res.headers.get('set-cookie')).toContain('NEXT_LOCALE=en-US')
})
// const parsedUrl = url.parse(res.headers.get('location'), true)
// expect(parsedUrl.pathname).toBe('/')
// expect(parsedUrl.query).toEqual({})
// expect(res.headers.get('set-cookie')).toContain('NEXT_LOCALE=en-US')
// })

it('should not redirect to accept-lang preferred locale with locale cookie', async () => {
const res = await fetchViaHTTP(appPort, '/', undefined, {
Expand All @@ -465,18 +465,19 @@ function runTests(isDev) {
it('should redirect to correct locale domain', async () => {
const checks = [
// test domain, locale prefix, redirect result
['example.be', 'nl-BE', 'http://example.be/'],
// ['example.be', 'nl-BE', 'http://example.be/'],
['example.be', 'fr', 'http://example.fr/'],
['example.fr', 'nl-BE', 'http://example.be/'],
['example.fr', 'fr', 'http://example.fr/'],
// ['example.fr', 'fr', 'http://example.fr/'],
]

for (const check of checks) {
const [domain, localePath, location] = check
const [domain, locale, location] = check

const res = await fetchViaHTTP(appPort, `/${localePath}`, undefined, {
const res = await fetchViaHTTP(appPort, `/`, undefined, {
headers: {
host: domain,
'accept-language': locale,
},
redirect: 'manual',
})
Expand Down Expand Up @@ -705,20 +706,20 @@ function runTests(isDev) {
expect(await browser.eval('window.beforeNav')).toBe(1)
})

it('should remove un-necessary locale prefix for default locale', async () => {
it('should not remove locale prefix for default locale', async () => {
const res = await fetchViaHTTP(appPort, '/en-US', undefined, {
redirect: 'manual',
headers: {
'Accept-Language': 'en-US;q=0.9',
},
})

expect(res.status).toBe(307)
expect(res.status).toBe(200)

const parsedUrl = url.parse(res.headers.get('location'), true)
// const parsedUrl = url.parse(res.headers.get('location'), true)

expect(parsedUrl.pathname).toBe('/')
expect(parsedUrl.query).toEqual({})
// expect(parsedUrl.pathname).toBe('/')
// expect(parsedUrl.query).toEqual({})

// make sure locale is case-insensitive
const res2 = await fetchViaHTTP(appPort, '/eN-Us', undefined, {
Expand All @@ -728,12 +729,12 @@ function runTests(isDev) {
},
})

expect(res2.status).toBe(307)
expect(res2.status).toBe(200)

const parsedUrl2 = url.parse(res.headers.get('location'), true)
// const parsedUrl2 = url.parse(res.headers.get('location'), true)

expect(parsedUrl2.pathname).toBe('/')
expect(parsedUrl2.query).toEqual({})
// expect(parsedUrl2.pathname).toBe('/')
// expect(parsedUrl2.query).toEqual({})
})

it('should load getStaticProps page correctly SSR (default locale no prefix)', async () => {
Expand Down

0 comments on commit 2ab3445

Please sign in to comment.