未验证 提交 54b70425 编写于 作者: J JJ Kasper 提交者: GitHub

Ensure i18n with basePath works properly (#19083)

上级 41866a13
......@@ -85,7 +85,7 @@ export function createEntrypoints(
assetPrefix: config.assetPrefix,
generateEtags: config.generateEtags,
poweredByHeader: config.poweredByHeader,
canonicalBase: config.canonicalBase,
canonicalBase: config.amp.canonicalBase,
basePath: config.basePath,
runtimeConfig: hasRuntimeConfig
? JSON.stringify({
......
......@@ -8,6 +8,7 @@ import {
BUILD_MANIFEST,
REACT_LOADABLE_MANIFEST,
ROUTES_MANIFEST,
TEMPORARY_REDIRECT_STATUS,
} from '../../../next-server/lib/constants'
import { isDynamicRoute } from '../../../next-server/lib/router/utils'
import { __ApiPreviewProps } from '../../../next-server/server/api-utils'
......@@ -361,11 +362,11 @@ const nextServerlessLoader: loader.Loader = function () {
localeDomainRedirect
? localeDomainRedirect
: shouldStripDefaultLocale
? '/'
: \`/\${detectedLocale}\`,
? "${basePath}" || '/'
: \`${basePath}/\${detectedLocale}\`,
})
)
res.statusCode = 307
res.statusCode = ${TEMPORARY_REDIRECT_STATUS}
res.end()
return
}
......
......@@ -475,7 +475,8 @@ export default class Router implements BaseRouter {
this.changeState(
'replaceState',
formatWithValidation({ pathname: addBasePath(pathname), query }),
getURL()
getURL(),
{ locale }
)
}
......@@ -621,7 +622,7 @@ export default class Router implements BaseRouter {
normalizeLocalePath,
} = require('../i18n/normalize-locale-path') as typeof import('../i18n/normalize-locale-path')
const parsedAs = parseRelativeUrl(as)
const parsedAs = parseRelativeUrl(hasBasePath(as) ? delBasePath(as) : as)
const localePathResult = normalizeLocalePath(
parsedAs.pathname,
......@@ -630,7 +631,7 @@ export default class Router implements BaseRouter {
if (localePathResult.detectedLocale) {
this.locale = localePathResult.detectedLocale
url = localePathResult.pathname
url = addBasePath(localePathResult.pathname)
}
}
......@@ -646,8 +647,13 @@ export default class Router implements BaseRouter {
this.abortComponentLoad(this._inFlightRoute)
}
as = addLocale(as, options.locale, this.defaultLocale)
as = addBasePath(
addLocale(
hasBasePath(as) ? delBasePath(as) : as,
options.locale,
this.defaultLocale
)
)
const cleanedAs = delLocale(
hasBasePath(as) ? delBasePath(as) : as,
this.locale
......@@ -848,12 +854,7 @@ export default class Router implements BaseRouter {
}
Router.events.emit('beforeHistoryChange', as)
this.changeState(
method,
url,
addLocale(as, options.locale, this.defaultLocale),
options
)
this.changeState(method, url, as, options)
if (process.env.NODE_ENV !== 'production') {
const appComp: any = this.components['/_app'].Component
......@@ -1230,7 +1231,7 @@ export default class Router implements BaseRouter {
const pages = await this.pageLoader.getPageList()
parsed = this._resolveHref(parsed, pages) as typeof parsed
parsed = this._resolveHref(parsed, pages, false) as typeof parsed
if (parsed.pathname !== pathname) {
pathname = parsed.pathname
......
......@@ -315,9 +315,11 @@ export default class Server {
req.url = req.url!.replace(basePath, '') || '/'
}
if (i18n && !parsedUrl.pathname?.startsWith('/_next')) {
if (i18n && !req.url?.startsWith('/_next')) {
// get pathname from URL with basePath stripped for locale detection
const { pathname, ...parsed } = parseUrl(req.url || '/')
let { pathname, ...parsed } = parseUrl(req.url || '/')
pathname = pathname || '/'
let defaultLocale = i18n.defaultLocale
let detectedLocale = detectLocaleCookie(req, i18n.locales)
let acceptPreferredLocale =
......@@ -348,13 +350,13 @@ export default class Server {
pathname: localePathResult.pathname,
})
;(req as any).__nextStrippedLocale = true
parsedUrl.pathname = localePathResult.pathname
parsedUrl.pathname = `${basePath || ''}${localePathResult.pathname}`
}
// If a detected locale is a domain specific locale and we aren't already
// on that domain and path prefix redirect to it to prevent duplicate
// content from multiple domains
if (detectedDomain && parsedUrl.pathname === '/') {
if (detectedDomain && pathname === '/') {
const localeToCheck = acceptPreferredLocale
// const localeToCheck = localePathResult.detectedLocale
// ? detectedLocale
......@@ -429,8 +431,8 @@ export default class Server {
pathname: localeDomainRedirect
? localeDomainRedirect
: shouldStripDefaultLocale
? '/'
: `/${detectedLocale}`,
? basePath || `/`
: `${basePath || ''}/${detectedLocale}`,
})
)
res.statusCode = 307
......@@ -710,7 +712,7 @@ export default class Server {
type: redirectRoute.type,
match: redirectRoute.match,
statusCode: redirectRoute.statusCode,
name: `Redirect route`,
name: `Redirect route ${redirectRoute.source}`,
fn: async (req, res, params, parsedUrl) => {
const { parsedDestination } = prepareDestination(
redirectRoute.destination,
......@@ -750,7 +752,7 @@ export default class Server {
...rewriteRoute,
check: true,
type: rewriteRoute.type,
name: `Rewrite route`,
name: `Rewrite route ${rewriteRoute.source}`,
match: rewriteRoute.match,
fn: async (req, res, params, parsedUrl) => {
const { newUrl, parsedDestination } = prepareDestination(
......
......@@ -183,9 +183,17 @@ export default class Router {
(req as any).__nextStrippedLocale &&
parsedUrl.query.__nextLocale
) {
if (keepBasePath) {
currentPathname = replaceBasePath(this.basePath, currentPathname!)
}
currentPathname = `/${parsedUrl.query.__nextLocale}${
currentPathname === '/' ? '' : currentPathname
}`
if (keepBasePath) {
currentPathname = `${this.basePath}${currentPathname}`
}
}
const newParams = testRoute.match(currentPathname)
......
......@@ -101,7 +101,7 @@ describe('Build Output', () => {
expect(parseFloat(err404Size) - 3.6).toBeLessThanOrEqual(0)
expect(err404Size.endsWith('kB')).toBe(true)
expect(parseFloat(err404FirstLoad) - 64.9).toBeLessThanOrEqual(0)
expect(parseFloat(err404FirstLoad) - 65).toBeLessThanOrEqual(0)
expect(err404FirstLoad.endsWith('kB')).toBe(true)
expect(parseFloat(sharedByAll) - 61.5).toBeLessThanOrEqual(0)
......
module.exports = {
// target: 'experimental-serverless-trace',
// basePath: '/docs',
i18n: {
// localeDetection: false,
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
defaultLocale: 'en-US',
domains: [
{
// used for testing, this should not be needed in most cases
// as production domains should always use https
http: true,
domain: 'example.be',
defaultLocale: 'nl-BE',
locales: ['nl', 'nl-NL', 'nl-BE'],
},
{
http: true,
domain: 'example.fr',
defaultLocale: 'fr',
locales: ['fr-BE'],
},
],
},
async redirects() {
return [
{
source: '/en-US/redirect',
destination: '/somewhere-else',
permanent: false,
},
{
source: '/nl/redirect',
destination: '/somewhere-else',
permanent: false,
},
{
source: '/redirect',
destination: '/somewhere-else',
permanent: false,
},
]
},
async rewrites() {
return [
{
source: '/en-US/rewrite',
destination: '/another',
},
{
source: '/nl/rewrite',
destination: '/another',
},
{
source: '/rewrite',
destination: '/another',
},
]
},
async headers() {
return [
{
source: '/en-US/add-header',
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
source: '/nl/add-header',
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
source: '/add-header',
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
]
},
}
export default function NotFound(props) {
return (
<>
<h1 id="not-found">This page could not be found | 404</h1>
<p id="props">{JSON.stringify(props)}</p>
</>
)
}
export const getStaticProps = ({ locale, locales, defaultLocale }) => {
return {
props: {
locale,
locales,
defaultLocale,
is404: true,
},
}
}
if (typeof window !== 'undefined') {
window.caughtWarns = []
const origWarn = window.console.warn
const origError = window.console.error
window.console.warn = function (...args) {
window.caughtWarns.push(args.join(' '))
origWarn(...args)
}
window.console.error = function (...args) {
window.caughtWarns.push(args.join(' '))
origError(...args)
}
}
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
import { useAmp } from 'next/amp'
import { useRouter } from 'next/router'
export const config = {
amp: true,
}
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="another">another page</p>
<p id="is-amp">{useAmp() ? 'yes' : 'no'}</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
</>
)
}
export const getServerSideProps = ({ locale, locales }) => {
return {
props: {
locale,
locales,
},
}
}
import { useAmp } from 'next/amp'
import { useRouter } from 'next/router'
export const config = {
amp: 'hybrid',
}
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="another">another page</p>
<p id="is-amp">{useAmp() ? 'yes' : 'no'}</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
</>
)
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="another">another page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getServerSideProps = ({ locale, locales, defaultLocale }) => {
return {
props: {
locale,
locales,
defaultLocale,
},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="auto-export">auto-export page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
</>
)
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="dynamic">dynamic page</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another">
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/gsp">
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first">
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello">
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first">
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp">
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first">
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
)
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="frank">frank page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another">
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/gsp">
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first">
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello">
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first">
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp">
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first">
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ locale, locales, defaultLocale }) => {
return {
props: {
locale,
locales,
defaultLocale,
},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
if (router.isFallback) return 'Loading...'
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ params, locale, locales, defaultLocale }) => {
// ensure getStaticProps isn't called without params
if (!params || !params.slug) {
throw new Error(`missing params ${JSON.stringify(params)}`)
}
if (params && params.slug === 'mixed-not-found-redirect') {
return {
notFound: true,
redirect: {
destination: '/another',
permanent: false,
},
}
}
return {
props: {
params,
locale,
locales,
defaultLocale,
},
}
}
export const getStaticPaths = ({ locales, defaultLocale }) => {
// make sure locales were provided correctly
if (!locales || locales.length !== 7) {
throw new Error(
'locales missing in getStaticPaths!! got: ' + JSON.stringify(locales)
)
}
if (!defaultLocale) {
throw new Error('missing defaultLocale in getStaticPaths')
}
return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map((slug) => ({
params: { slug },
})),
fallback: true,
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ locale, locales, defaultLocale }) => {
return {
props: {
locale,
locales,
defaultLocale,
},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
if (router.isFallback) return 'Loading...'
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ params, locale, locales, defaultLocale }) => {
// ensure getStaticProps isn't called without params
if (!params || !params.slug) {
throw new Error(`missing params ${JSON.stringify(params)}`)
}
return {
props: {
params,
locale,
locales,
defaultLocale,
},
}
}
export const getStaticPaths = () => {
return {
paths: [
{ params: { slug: 'first' } },
'/gsp/no-fallback/second',
{ params: { slug: 'first' }, locale: 'en-US' },
'/nl-NL/gsp/no-fallback/second',
],
fallback: false,
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getServerSideProps = ({
params,
locale,
locales,
defaultLocale,
}) => {
return {
props: {
params,
locale,
locales,
defaultLocale,
},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="gssp">gssp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getServerSideProps = ({ locale, locales, defaultLocale }) => {
return {
props: {
locale,
locales,
defaultLocale,
},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="index">index page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another">
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/dynamic/first">
<a id="to-dynamic">to /dynamic/first</a>
</Link>
<br />
<Link href="/gsp">
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first">
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello">
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first">
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp">
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first">
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ locale, locales, defaultLocale }) => {
return {
props: {
locale,
locales,
defaultLocale,
},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
const { nextLocale } = router.query
return (
<>
<p id="links">links page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-default-locale">{router.defaultLocale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/another" locale={nextLocale}>
<a id="to-another">to /another</a>
</Link>
<br />
<Link href="/gsp" locale={nextLocale}>
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href="/gsp/fallback/first" locale={nextLocale}>
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href="/gsp/fallback/hello" locale={nextLocale}>
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href="/gsp/no-fallback/first" locale={nextLocale}>
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href="/gssp" locale={nextLocale}>
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href="/gssp/first" locale={nextLocale}>
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
)
}
// make SSR page so we have query values immediately
export const getServerSideProps = () => {
return {
props: {},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
const { nextLocale } = router.query
return (
<>
<p id="links">links page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href={`/${nextLocale}/another`} locale={false}>
<a id="to-another">to /another</a>
</Link>
<br />
<Link href={`/${nextLocale}/gsp`} locale={false}>
<a id="to-gsp">to /gsp</a>
</Link>
<br />
<Link href={`/${nextLocale}/gsp/fallback/first`} locale={false}>
<a id="to-fallback-first">to /gsp/fallback/first</a>
</Link>
<br />
<Link href={`/${nextLocale}/gsp/fallback/hello`} locale={false}>
<a id="to-fallback-hello">to /gsp/fallback/hello</a>
</Link>
<br />
<Link href={`/${nextLocale}/gsp/no-fallback/first`} locale={false}>
<a id="to-no-fallback-first">to /gsp/no-fallback/first</a>
</Link>
<br />
<Link href={`/${nextLocale}/gssp`} locale={false}>
<a id="to-gssp">to /gssp</a>
</Link>
<br />
<Link href={`/${nextLocale}/gssp/first`} locale={false}>
<a id="to-gssp-slug">to /gssp/first</a>
</Link>
<br />
</>
)
}
// make SSR page so we have query values immediately
export const getServerSideProps = () => {
return {
props: {},
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ params, locale, locales }) => {
// ensure getStaticProps isn't called without params
if (!params || !params.slug) {
throw new Error(`missing params ${JSON.stringify(params)}`)
}
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
}
}
return {
props: {
params,
locale,
locales,
},
}
}
export const getStaticPaths = () => {
return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map((slug) => ({
params: { slug },
})),
fallback: 'blocking',
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
if (router.isFallback) return 'Loading...'
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ params, locale, locales }) => {
// ensure getStaticProps isn't called without params
if (!params || !params.slug) {
throw new Error(`missing params ${JSON.stringify(params)}`)
}
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
}
}
return {
props: {
params,
locale,
locales,
},
}
}
export const getStaticPaths = () => {
return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map((slug) => ({
params: { slug },
})),
fallback: true,
}
}
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Page(props) {
const router = useRouter()
return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}
export const getStaticProps = ({ locale, locales }) => {
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
}
}
return {
props: {
locale,
locales,
},
}
}
import http from 'http'
import fs from 'fs-extra'
import { join } from 'path'
import cheerio from 'cheerio'
import { runTests, locales } from '../../i18n-support/test/shared'
import {
nextBuild,
nextStart,
findPort,
killApp,
getPageFileFromPagesManifest,
fetchViaHTTP,
File,
launchApp,
} from 'next-test-utils'
const appDir = join(__dirname, '../')
const nextConfig = new File(join(appDir, 'next.config.js'))
const ctx = {
basePath: '/docs',
appDir,
}
describe('i18n Support', () => {
describe('dev mode', () => {
const curCtx = {
...ctx,
isDev: true,
}
beforeAll(async () => {
nextConfig.replace('// basePath', 'basePath')
await fs.remove(join(appDir, '.next'))
curCtx.appPort = await findPort()
curCtx.app = await launchApp(appDir, curCtx.appPort)
})
afterAll(async () => {
nextConfig.restore()
await killApp(curCtx.app)
})
runTests(curCtx)
})
describe('production mode', () => {
beforeAll(async () => {
nextConfig.replace('// basePath', 'basePath')
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
ctx.appPort = await findPort()
ctx.app = await nextStart(appDir, ctx.appPort)
ctx.buildPagesDir = join(appDir, '.next/server/pages')
ctx.buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(async () => {
nextConfig.restore()
await killApp(ctx.app)
})
runTests(ctx)
})
describe('serverless mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
nextConfig.replace('// target', 'target')
nextConfig.replace('// basePath', 'basePath')
await nextBuild(appDir)
ctx.appPort = await findPort()
ctx.app = await nextStart(appDir, ctx.appPort)
ctx.buildPagesDir = join(appDir, '.next/serverless/pages')
ctx.buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(async () => {
nextConfig.restore()
await killApp(ctx.app)
})
it('should have correct props for blocking notFound', async () => {
const serverFile = getPageFileFromPagesManifest(
appDir,
'/not-found/blocking-fallback/[slug]'
)
const appPort = await findPort()
const mod = require(join(appDir, '.next/serverless', serverFile))
const server = http.createServer(async (req, res) => {
try {
await mod.render(req, res)
} catch (err) {
res.statusCode = 500
res.end('internal err')
}
})
await new Promise((resolve, reject) => {
server.listen(appPort, (err) => (err ? reject(err) : resolve()))
})
console.log('listening on', appPort)
const res = await fetchViaHTTP(
appPort,
`${ctx.basePath}/nl/not-found/blocking-fallback/first`
)
server.close()
expect(res.status).toBe(404)
const $ = cheerio.load(await res.text())
const props = JSON.parse($('#props').text())
expect($('#not-found').text().length > 0).toBe(true)
expect(props).toEqual({
is404: true,
locale: 'nl',
locales,
defaultLocale: 'en-US',
})
})
runTests(ctx)
})
describe('with localeDetection disabled', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
nextConfig.replace('// basePath', 'basePath')
nextConfig.replace('// localeDetection', 'localeDetection')
await nextBuild(appDir)
ctx.appPort = await findPort()
ctx.app = await nextStart(appDir, ctx.appPort)
})
afterAll(async () => {
nextConfig.restore()
await killApp(ctx.app)
})
it('should have localeDetection in routes-manifest', async () => {
const routesManifest = await fs.readJSON(
join(appDir, '.next/routes-manifest.json')
)
expect(routesManifest.i18n).toEqual({
localeDetection: false,
locales: ['en-US', 'nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en'],
defaultLocale: 'en-US',
domains: [
{
http: true,
domain: 'example.be',
defaultLocale: 'nl-BE',
locales: ['nl', 'nl-NL', 'nl-BE'],
},
{
http: true,
domain: 'example.fr',
defaultLocale: 'fr',
locales: ['fr-BE'],
},
],
})
})
it('should not detect locale from accept-language', async () => {
const res = await fetchViaHTTP(
ctx.appPort,
`${ctx.basePath || '/'}`,
{},
{
redirect: 'manual',
headers: {
'accept-language': 'fr',
},
}
)
expect(res.status).toBe(200)
const $ = cheerio.load(await res.text())
expect($('html').attr('lang')).toBe('en-US')
expect($('#router-locale').text()).toBe('en-US')
expect(JSON.parse($('#router-locales').text())).toEqual(locales)
expect($('#router-pathname').text()).toBe('/')
expect($('#router-as-path').text()).toBe('/')
})
it('should set locale from detected path', async () => {
for (const locale of locales) {
const res = await fetchViaHTTP(
ctx.appPort,
`${ctx.basePath}/${locale}`,
{},
{
redirect: 'manual',
headers: {
'accept-language': 'en-US,en;q=0.9',
},
}
)
expect(res.status).toBe(200)
const $ = cheerio.load(await res.text())
expect($('html').attr('lang')).toBe(locale)
expect($('#router-locale').text()).toBe(locale)
expect(JSON.parse($('#router-locales').text())).toEqual(locales)
expect($('#router-pathname').text()).toBe('/')
expect($('#router-as-path').text()).toBe('/')
}
})
})
})
module.exports = {
// target: 'experimental-serverless-trace',
// basePath: '/docs',
i18n: {
// localeDetection: false,
locales: ['nl-NL', 'nl-BE', 'nl', 'fr-BE', 'fr', 'en-US', 'en'],
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册