Skip to content

Commit

Permalink
Make sure default locale isn't prefixed on the client
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Oct 8, 2020
1 parent 2f4f40e commit 552fc6d
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 43 deletions.
Expand Up @@ -468,6 +468,7 @@ const nextServerlessLoader: loader.Loader = function () {
isDataReq: _nextData,
locale: detectedLocale,
locales: i18n.locales,
defaultLocale: i18n.defaultLocale,
},
options,
)
Expand Down
2 changes: 2 additions & 0 deletions packages/next/client/index.tsx
Expand Up @@ -65,6 +65,7 @@ const {
isFallback,
head: initialHeadData,
locales,
defaultLocale,
} = data

let { locale } = data
Expand Down Expand Up @@ -317,6 +318,7 @@ export default async (opts: { webpackHMR?: any } = {}) => {
render({ App, Component, styleSheets, props, err }),
locale,
locales,
defaultLocale,
})

// call init-client middleware
Expand Down
4 changes: 3 additions & 1 deletion packages/next/client/link.tsx
Expand Up @@ -332,7 +332,9 @@ function Link(props: React.PropsWithChildren<LinkProps>) {
// If child is an <a> tag and doesn't have a href attribute, or if the 'passHref' property is
// defined, we specify the current 'href', so that repetition is not needed by the user
if (props.passHref || (child.type === 'a' && !('href' in child.props))) {
childProps.href = addBasePath(addLocale(as, router && router.locale))
childProps.href = addBasePath(
addLocale(as, router && router.locale, router && router.defaultLocale)
)
}

return React.cloneElement(child, childProps)
Expand Down
29 changes: 25 additions & 4 deletions packages/next/client/page-loader.ts
Expand Up @@ -203,13 +203,23 @@ export default class PageLoader {
* @param {string} href the route href (file-system path)
* @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes
*/
getDataHref(href: string, asPath: string, ssg: boolean, locale?: string) {
getDataHref(
href: string,
asPath: string,
ssg: boolean,
locale?: string,
defaultLocale?: string
) {
const { pathname: hrefPathname, query, search } = parseRelativeUrl(href)
const { pathname: asPathname } = parseRelativeUrl(asPath)
const route = normalizeRoute(hrefPathname)

const getHrefForSlug = (path: string) => {
const dataRoute = addLocale(getAssetPathFromRoute(path, '.json'), locale)
const dataRoute = addLocale(
getAssetPathFromRoute(path, '.json'),
locale,
defaultLocale
)
return addBasePath(
`/_next/data/${this.buildId}${dataRoute}${ssg ? '' : search}`
)
Expand All @@ -229,15 +239,26 @@ export default class PageLoader {
* @param {string} href the route href (file-system path)
* @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes
*/
prefetchData(href: string, asPath: string) {
prefetchData(
href: string,
asPath: string,
locale?: string,
defaultLocale?: string
) {
const { pathname: hrefPathname } = parseRelativeUrl(href)
const route = normalizeRoute(hrefPathname)
return this.promisedSsgManifest!.then(
(s: ClientSsgManifest, _dataHref?: string) =>
// Check if the route requires a data file
s.has(route) &&
// Try to generate data href, noop when falsy
(_dataHref = this.getDataHref(href, asPath, true)) &&
(_dataHref = this.getDataHref(
href,
asPath,
true,
locale,
defaultLocale
)) &&
// noop when data has already been prefetched (dedupe)
!document.querySelector(
`link[rel="${relPrefetch}"][href^="${_dataHref}"]`
Expand Down
1 change: 1 addition & 0 deletions packages/next/client/router.ts
Expand Up @@ -39,6 +39,7 @@ const urlPropertyFields = [
'basePath',
'locale',
'locales',
'defaultLocale',
]
const routerEvents = [
'routeChangeStart',
Expand Down
7 changes: 5 additions & 2 deletions packages/next/export/index.ts
Expand Up @@ -283,6 +283,8 @@ export default async function exportApp(
}
}

const { i18n } = nextConfig.experimental

// Start the rendering process
const renderOpts = {
dir,
Expand All @@ -298,8 +300,9 @@ export default async function exportApp(
ampValidatorPath: nextConfig.experimental.amp?.validator || undefined,
ampSkipValidation: nextConfig.experimental.amp?.skipValidation || false,
ampOptimizerConfig: nextConfig.experimental.amp?.optimizer || undefined,
locales: nextConfig.experimental.i18n?.locales,
locale: nextConfig.experimental.i18n?.defaultLocale,
locales: i18n?.locales,
locale: i18n.defaultLocale,
defaultLocale: i18n.defaultLocale,
}

const { serverRuntimeConfig, publicRuntimeConfig } = nextConfig
Expand Down
32 changes: 26 additions & 6 deletions packages/next/next-server/lib/router/router.ts
Expand Up @@ -55,9 +55,13 @@ function addPathPrefix(path: string, prefix?: string) {
: path
}

export function addLocale(path: string, locale?: string) {
export function addLocale(
path: string,
locale?: string,
defaultLocale?: string
) {
if (process.env.__NEXT_i18n_SUPPORT) {
return locale && !path.startsWith('/' + locale)
return locale && locale !== defaultLocale && !path.startsWith('/' + locale)
? addPathPrefix(path, '/' + locale)
: path
}
Expand Down Expand Up @@ -246,6 +250,7 @@ export type BaseRouter = {
basePath: string
locale?: string
locales?: string[]
defaultLocale?: string
}

export type NextRouter = BaseRouter &
Expand Down Expand Up @@ -356,6 +361,7 @@ export default class Router implements BaseRouter {
_shallow?: boolean
locale?: string
locales?: string[]
defaultLocale?: string

static events: MittEmitter = mitt()

Expand All @@ -375,6 +381,7 @@ export default class Router implements BaseRouter {
isFallback,
locale,
locales,
defaultLocale,
}: {
subscription: Subscription
initialProps: any
Expand All @@ -387,6 +394,7 @@ export default class Router implements BaseRouter {
isFallback: boolean
locale?: string
locales?: string[]
defaultLocale?: string
}
) {
// represents the current component key
Expand Down Expand Up @@ -440,6 +448,7 @@ export default class Router implements BaseRouter {
if (process.env.__NEXT_i18n_SUPPORT) {
this.locale = locale
this.locales = locales
this.defaultLocale = defaultLocale
}

if (typeof window !== 'undefined') {
Expand Down Expand Up @@ -596,7 +605,7 @@ export default class Router implements BaseRouter {
this.abortComponentLoad(this._inFlightRoute)
}

as = addLocale(as, this.locale)
as = addLocale(as, this.locale, this.defaultLocale)
const cleanedAs = delLocale(
hasBasePath(as) ? delBasePath(as) : as,
this.locale
Expand Down Expand Up @@ -790,7 +799,12 @@ export default class Router implements BaseRouter {
}

Router.events.emit('beforeHistoryChange', as)
this.changeState(method, url, addLocale(as, this.locale), options)
this.changeState(
method,
url,
addLocale(as, this.locale, this.defaultLocale),
options
)

if (process.env.NODE_ENV !== 'production') {
const appComp: any = this.components['/_app'].Component
Expand Down Expand Up @@ -960,7 +974,8 @@ export default class Router implements BaseRouter {
formatWithValidation({ pathname, query }),
delBasePath(as),
__N_SSG,
this.locale
this.locale,
this.defaultLocale
)
}

Expand Down Expand Up @@ -1117,7 +1132,12 @@ export default class Router implements BaseRouter {

const route = removePathTrailingSlash(pathname)
await Promise.all([
this.pageLoader.prefetchData(url, asPath),
this.pageLoader.prefetchData(
url,
asPath,
this.locale,
this.defaultLocale
),
this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route),
])
}
Expand Down
1 change: 1 addition & 0 deletions packages/next/next-server/lib/utils.ts
Expand Up @@ -103,6 +103,7 @@ export type NEXT_DATA = {
head: HeadEntry[]
locale?: string
locales?: string[]
defaultLocale?: string
}

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/next/next-server/server/next-server.ts
Expand Up @@ -140,6 +140,7 @@ export default class Server {
optimizeImages: boolean
locale?: string
locales?: string[]
defaultLocale?: string
}
private compression?: Middleware
private onErrorMiddleware?: ({ err }: { err: Error }) => Promise<void>
Expand Down Expand Up @@ -193,6 +194,7 @@ export default class Server {
: null,
optimizeImages: this.nextConfig.experimental.optimizeImages,
locales: this.nextConfig.experimental.i18n?.locales,
defaultLocale: this.nextConfig.experimental.i18n?.defaultLocale,
}

// Only the `publicRuntimeConfig` key is exposed to the client side
Expand Down
11 changes: 9 additions & 2 deletions packages/next/next-server/server/render.tsx
Expand Up @@ -69,6 +69,7 @@ class ServerRouter implements NextRouter {
isFallback: boolean
locale?: string
locales?: string[]
defaultLocale?: string
// TODO: Remove in the next major version, as this would mean the user is adding event listeners in server-side `render` method
static events: MittEmitter = mitt()

Expand All @@ -79,7 +80,8 @@ class ServerRouter implements NextRouter {
{ isFallback }: { isFallback: boolean },
basePath: string,
locale?: string,
locales?: string[]
locales?: string[],
defaultLocale?: string
) {
this.route = pathname.replace(/\/$/, '') || '/'
this.pathname = pathname
Expand All @@ -89,6 +91,7 @@ class ServerRouter implements NextRouter {
this.basePath = basePath
this.locale = locale
this.locales = locales
this.defaultLocale = defaultLocale
}
push(): any {
noRouter()
Expand Down Expand Up @@ -164,6 +167,7 @@ export type RenderOptsPartial = {
resolvedAsPath?: string
locale?: string
locales?: string[]
defaultLocale?: string
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
Expand Down Expand Up @@ -203,6 +207,7 @@ function renderDocument(
devOnlyCacheBusterQueryString,
locale,
locales,
defaultLocale,
}: RenderOpts & {
props: any
docComponentsRendered: DocumentProps['docComponentsRendered']
Expand Down Expand Up @@ -251,6 +256,7 @@ function renderDocument(
appGip, // whether the _app has getInitialProps
locale,
locales,
defaultLocale,
head: React.Children.toArray(docProps.head || [])
.map((elem) => {
const { children } = elem?.props
Expand Down Expand Up @@ -517,7 +523,8 @@ export async function renderToHTML(
},
basePath,
renderOpts.locale,
renderOpts.locales
renderOpts.locales,
renderOpts.defaultLocale
)
const ctx = {
err,
Expand Down
2 changes: 1 addition & 1 deletion test/integration/i18n-support/next.config.js
Expand Up @@ -3,7 +3,7 @@ module.exports = {
experimental: {
i18n: {
locales: ['nl-NL', 'nl-BE', 'nl', 'en-US', 'en'],
defaultLocale: 'en',
defaultLocale: 'en-US',
},
},
}

0 comments on commit 552fc6d

Please sign in to comment.