Skip to content

Commit e58b5f1

Browse files
committed
Ensure i18n support with AMP
1 parent b14331c commit e58b5f1

File tree

8 files changed

+133
-7
lines changed

8 files changed

+133
-7
lines changed

packages/next/build/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ export default async function build(
785785
const outputPath = `/${locale}${page === '/' ? '' : page}`
786786

787787
defaultMap[outputPath] = {
788-
page: defaultMap[page].page,
788+
page: defaultMap[page]?.page || page,
789789
query: { __nextLocale: locale },
790790
}
791791

packages/next/build/webpack/loaders/next-serverless-loader.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,18 @@ const nextServerlessLoader: loader.Loader = function () {
533533
534534
${handleBasePath}
535535
536+
// remove ?amp=1 from request URL if rendering for export
537+
if (fromExport && parsedUrl.query.amp) {
538+
const queryNoAmp = Object.assign({}, origQuery)
539+
delete queryNoAmp.amp
540+
541+
req.url = formatUrl({
542+
...parsedUrl,
543+
search: undefined,
544+
query: queryNoAmp
545+
})
546+
}
547+
536548
if (parsedUrl.pathname.match(/_next\\/data/)) {
537549
const {
538550
default: getrouteNoAssetPath,

packages/next/export/worker.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ interface RenderOpts {
7070
fontManifest?: FontManifest
7171
locales?: string[]
7272
locale?: string
73+
defaultLocale?: string
7374
}
7475

7576
type ComponentModule = ComponentType<{}> & {
@@ -99,8 +100,9 @@ export default async function exportPage({
99100
const { query: originalQuery = {} } = pathMap
100101
const { page } = pathMap
101102
const filePath = normalizePagePath(path)
102-
const ampPath = `${filePath}.amp`
103103
const isDynamic = isDynamicRoute(page)
104+
const ampPath = `${filePath}.amp`
105+
let renderAmpPath = ampPath
104106
let query = { ...originalQuery }
105107
let params: { [key: string]: string | string[] } | undefined
106108

@@ -114,6 +116,10 @@ export default async function exportPage({
114116
if (localePathResult.detectedLocale) {
115117
updatedPath = localePathResult.pathname
116118
locale = localePathResult.detectedLocale
119+
120+
if (locale === renderOpts.defaultLocale) {
121+
renderAmpPath = `${normalizePagePath(updatedPath)}.amp`
122+
}
117123
}
118124
}
119125

@@ -238,7 +244,7 @@ export default async function exportPage({
238244
res,
239245
'export',
240246
{
241-
ampPath,
247+
ampPath: renderAmpPath,
242248
/// @ts-ignore
243249
optimizeFonts,
244250
/// @ts-ignore
@@ -298,7 +304,7 @@ export default async function exportPage({
298304
curRenderOpts = {
299305
...components,
300306
...renderOpts,
301-
ampPath,
307+
ampPath: renderAmpPath,
302308
params,
303309
optimizeFonts,
304310
optimizeImages,
@@ -352,15 +358,16 @@ export default async function exportPage({
352358
if (serverless) {
353359
req.url += (req.url!.includes('?') ? '&' : '?') + 'amp=1'
354360
// @ts-ignore
355-
ampHtml = (await renderMethod(req, res, 'export', undefined, params))
356-
.html
361+
ampHtml = (
362+
await renderMethod(req, res, 'export', curRenderOpts, params)
363+
).html
357364
} else {
358365
ampHtml = await renderMethod(
359366
req,
360367
res,
361368
page,
362369
// @ts-ignore
363-
{ ...query, amp: 1 },
370+
{ ...query, amp: '1' },
364371
curRenderOpts
365372
)
366373
}

packages/next/next-server/server/next-server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ export default class Server {
335335
...parsed,
336336
pathname: localePathResult.pathname,
337337
})
338+
;(req as any).__nextStrippedLocale = true
338339
parsedUrl.pathname = localePathResult.pathname
339340

340341
// check if the locale prefix matches a domain's defaultLocale

packages/next/next-server/server/render.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,10 @@ export async function renderToHTML(
899899

900900
let html = renderDocument(Document, {
901901
...renderOpts,
902+
canonicalBase:
903+
!renderOpts.ampPath && (req as any).__nextStrippedLocale
904+
? `${renderOpts.canonicalBase || ''}/${renderOpts.locale}`
905+
: renderOpts.canonicalBase,
902906
docComponentsRendered,
903907
buildManifest: filteredBuildManifest,
904908
// Only enabled in production as development mode has features relying on HMR (style injection for example)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useAmp } from 'next/amp'
2+
import { useRouter } from 'next/router'
3+
4+
export const config = {
5+
amp: true,
6+
}
7+
8+
export default function Page(props) {
9+
const router = useRouter()
10+
11+
return (
12+
<>
13+
<p id="another">another page</p>
14+
<p id="is-amp">{useAmp() ? 'yes' : 'no'}</p>
15+
<p id="props">{JSON.stringify(props)}</p>
16+
<p id="router-locale">{router.locale}</p>
17+
<p id="router-locales">{JSON.stringify(router.locales)}</p>
18+
<p id="router-query">{JSON.stringify(router.query)}</p>
19+
<p id="router-pathname">{router.pathname}</p>
20+
<p id="router-as-path">{router.asPath}</p>
21+
</>
22+
)
23+
}
24+
25+
export const getServerSideProps = ({ locale, locales }) => {
26+
return {
27+
props: {
28+
locale,
29+
locales,
30+
},
31+
}
32+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useAmp } from 'next/amp'
2+
import { useRouter } from 'next/router'
3+
4+
export const config = {
5+
amp: 'hybrid',
6+
}
7+
8+
export default function Page(props) {
9+
const router = useRouter()
10+
11+
return (
12+
<>
13+
<p id="another">another page</p>
14+
<p id="is-amp">{useAmp() ? 'yes' : 'no'}</p>
15+
<p id="props">{JSON.stringify(props)}</p>
16+
<p id="router-locale">{router.locale}</p>
17+
<p id="router-locales">{JSON.stringify(router.locales)}</p>
18+
<p id="router-query">{JSON.stringify(router.query)}</p>
19+
<p id="router-pathname">{router.pathname}</p>
20+
<p id="router-as-path">{router.asPath}</p>
21+
</>
22+
)
23+
}

test/integration/i18n-support/test/index.test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,53 @@ function runTests(isDev) {
391391
await checkDomainLocales('fr', 'example.fr')
392392
})
393393

394+
it('should generate AMP pages with all locales', async () => {
395+
for (const locale of locales) {
396+
const localePath = locale !== 'en-US' ? `/${locale}` : ''
397+
const html = await renderViaHTTP(appPort, `${localePath}/amp/amp-hybrid`)
398+
const $ = cheerio.load(html)
399+
expect($('html').attr('lang')).toBe(locale)
400+
expect($('#is-amp').text()).toBe('no')
401+
expect($('#router-locale').text()).toBe(locale)
402+
expect(JSON.parse($('#router-locales').text())).toEqual(locales)
403+
expect($('#router-pathname').text()).toBe('/amp/amp-hybrid')
404+
expect($('#router-as-path').text()).toBe('/amp/amp-hybrid')
405+
expect(JSON.parse($('#router-query').text())).toEqual({})
406+
407+
const amphtmlPath = `${localePath}/amp/amp-hybrid${
408+
isDev ? '?amp=1' : '.amp'
409+
}`
410+
expect($('link[rel=amphtml]').attr('href')).toBe(amphtmlPath)
411+
412+
const html2 = await renderViaHTTP(appPort, amphtmlPath)
413+
const $2 = cheerio.load(html2)
414+
expect($2('html').attr('lang')).toBe(locale)
415+
expect($2('#is-amp').text()).toBe('yes')
416+
expect($2('#router-locale').text()).toBe(locale)
417+
expect(JSON.parse($2('#router-locales').text())).toEqual(locales)
418+
expect($2('#router-pathname').text()).toBe('/amp/amp-hybrid')
419+
expect($2('#router-as-path').text()).toBe('/amp/amp-hybrid')
420+
expect(JSON.parse($2('#router-query').text())).toEqual({ amp: '1' })
421+
expect($2('link[rel=amphtml]').attr('href')).toBeFalsy()
422+
}
423+
})
424+
425+
it('should work with AMP first page with all locales', async () => {
426+
for (const locale of locales) {
427+
const localePath = locale !== 'en-US' ? `/${locale}` : ''
428+
const html = await renderViaHTTP(appPort, `${localePath}/amp/amp-first`)
429+
const $ = cheerio.load(html)
430+
expect($('html').attr('lang')).toBe(locale)
431+
expect($('#is-amp').text()).toBe('yes')
432+
expect($('#router-locale').text()).toBe(locale)
433+
expect(JSON.parse($('#router-locales').text())).toEqual(locales)
434+
expect($('#router-pathname').text()).toBe('/amp/amp-first')
435+
expect($('#router-as-path').text()).toBe('/amp/amp-first')
436+
expect(JSON.parse($('#router-query').text())).toEqual({})
437+
expect($('link[rel=amphtml]').attr('href')).toBeFalsy()
438+
}
439+
})
440+
394441
it('should generate fallbacks with all locales', async () => {
395442
for (const locale of locales) {
396443
const html = await renderViaHTTP(

0 commit comments

Comments
 (0)