Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f02ef88

Browse files
piehorinokai
andauthored
fix: edge-middleware i18n matching (#2555)
* fix: edge-middleware i18n matching * test: update edge-runtime test * test: do not localize data requests --------- Co-authored-by: Rob Stanford <me@robstanford.com>
1 parent 4ee9ef0 commit f02ef88

File tree

6 files changed

+58
-12
lines changed

6 files changed

+58
-12
lines changed

‎edge-runtime/lib/next-request.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Context } from '@netlify/edge-functions'
22

33
import {
44
addBasePath,
5+
addLocale,
56
addTrailingSlash,
67
normalizeDataUrl,
78
normalizeLocalePath,
@@ -73,6 +74,33 @@ const normalizeRequestURL = (
7374
}
7475
}
7576

77+
export const localizeRequest = (
78+
url: URL,
79+
nextConfig?: {
80+
basePath?: string
81+
i18n?: I18NConfig | null
82+
},
83+
): { localizedUrl: URL; locale?: string } => {
84+
const localizedUrl = new URL(url)
85+
localizedUrl.pathname = removeBasePath(localizedUrl.pathname, nextConfig?.basePath)
86+
87+
// Detect the locale from the URL
88+
const { detectedLocale } = normalizeLocalePath(localizedUrl.pathname, nextConfig?.i18n?.locales)
89+
90+
// Add the locale to the URL if not already present
91+
localizedUrl.pathname = addLocale(
92+
localizedUrl.pathname,
93+
detectedLocale ?? nextConfig?.i18n?.defaultLocale,
94+
)
95+
96+
localizedUrl.pathname = addBasePath(localizedUrl.pathname, nextConfig?.basePath)
97+
98+
return {
99+
localizedUrl,
100+
locale: detectedLocale,
101+
}
102+
}
103+
76104
export const buildNextRequest = (
77105
request: Request,
78106
context: Context,

‎edge-runtime/lib/util.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ export const addBasePath = (path: string, basePath?: string) => {
2929
return path
3030
}
3131

32+
// add locale prefix if not present, allowing for locale fallbacks
33+
export const addLocale = (path: string, locale?: string) => {
34+
if (
35+
locale &&
36+
path.toLowerCase() !== `/${locale.toLowerCase()}` &&
37+
!path.toLowerCase().startsWith(`/${locale.toLowerCase()}/`) &&
38+
!path.startsWith(`/api/`) &&
39+
!path.startsWith(`/_next/`)
40+
) {
41+
return `/${locale}${path}`
42+
}
43+
return path
44+
}
45+
3246
// https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/i18n/normalize-locale-path.ts
3347

3448
export interface PathLocale {

‎edge-runtime/middleware.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import nextConfig from './next.config.json' with { type: 'json' }
55

66
import { InternalHeaders } from './lib/headers.ts'
77
import { logger, LogLevel } from './lib/logging.ts'
8-
import { buildNextRequest, RequestData } from './lib/next-request.ts'
8+
import { buildNextRequest, localizeRequest,RequestData } from './lib/next-request.ts'
99
import { buildResponse, FetchEventResult } from './lib/response.ts'
1010
import {
1111
getMiddlewareRouteMatcher,
@@ -31,25 +31,29 @@ export async function handleMiddleware(
3131
context: Context,
3232
nextHandler: NextHandler,
3333
) {
34-
const nextRequest = buildNextRequest(request, context, nextConfig)
3534
const url = new URL(request.url)
35+
3636
const reqLogger = logger
3737
.withLogLevel(
3838
request.headers.has(InternalHeaders.NFDebugLogging) ? LogLevel.Debug : LogLevel.Log,
3939
)
4040
.withFields({ url_path: url.pathname })
4141
.withRequestID(request.headers.get(InternalHeaders.NFRequestID))
4242

43+
const { localizedUrl } = localizeRequest(url, nextConfig)
4344
// While we have already checked the path when mapping to the edge function,
4445
// Next.js supports extra rules that we need to check here too, because we
4546
// might be running an edge function for a path we should not. If we find
4647
// that's the case, short-circuit the execution.
47-
if (!matchesMiddleware(url.pathname, request, searchParamsToUrlQuery(url.searchParams))) {
48+
if (
49+
!matchesMiddleware(localizedUrl.pathname, request, searchParamsToUrlQuery(url.searchParams))
50+
) {
4851
reqLogger.debug('Aborting middleware due to runtime rules')
4952

5053
return
5154
}
5255

56+
const nextRequest = buildNextRequest(request, context, nextConfig)
5357
try {
5458
const result = await nextHandler({ request: nextRequest })
5559
const response = await buildResponse({

‎src/build/functions/edge.ts‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,7 @@ const writeHandlerFile = async (ctx: PluginContext, { matchers, name }: NextDefi
6565

6666
// Writing a file with the matchers that should trigger this function. We'll
6767
// read this file from the function at runtime.
68-
await writeFile(
69-
join(handlerRuntimeDirectory, 'matchers.json'),
70-
JSON.stringify(augmentMatchers(matchers, ctx)),
71-
)
68+
await writeFile(join(handlerRuntimeDirectory, 'matchers.json'), JSON.stringify(matchers))
7269

7370
// The config is needed by the edge function to match and normalize URLs. To
7471
// avoid shipping and parsing a large file at runtime, let's strip it down to

‎tests/fixtures/middleware-conditions/middleware.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const config = {
1919
source: '/hello',
2020
},
2121
{
22-
source: '/nl-NL/about',
22+
source: '/nl/about',
2323
locale: false,
2424
},
2525
],

‎tests/integration/edge-handler.test.ts‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,26 +261,29 @@ describe("aborts middleware execution when the matcher conditions don't match th
261261

262262
ctx.cleanup?.push(() => origin.stop())
263263

264-
for (const path of ['/hello', '/en/hello', '/nl-NL/hello', '/nl-NL/about']) {
264+
for (const path of ['/hello', '/en/hello', '/nl/hello', '/nl/about']) {
265265
const response = await invokeEdgeFunction(ctx, {
266266
functions: ['___netlify-edge-handler-middleware'],
267267
origin,
268268
url: path,
269269
})
270-
expect(response.headers.has('x-hello-from-middleware-res'), `does match ${path}`).toBeTruthy()
270+
expect(
271+
response.headers.has('x-hello-from-middleware-res'),
272+
`should match ${path}`,
273+
).toBeTruthy()
271274
expect(await response.text()).toBe('Hello from origin!')
272275
expect(response.status).toBe(200)
273276
}
274277

275-
for (const path of ['/hello/invalid', '/about', '/en/about']) {
278+
for (const path of ['/invalid/hello','/hello/invalid', '/about', '/en/about']) {
276279
const response = await invokeEdgeFunction(ctx, {
277280
functions: ['___netlify-edge-handler-middleware'],
278281
origin,
279282
url: path,
280283
})
281284
expect(
282285
response.headers.has('x-hello-from-middleware-res'),
283-
`does not match ${path}`,
286+
`should not match ${path}`,
284287
).toBeFalsy()
285288
expect(await response.text()).toBe('Hello from origin!')
286289
expect(response.status).toBe(200)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /