Skip to content

Commit e49782a

Browse files
authored
fix: preserve query parameters during i18n redirects to a localized path (#1118)
* fix: preserve query parameters during i18n redirects to a localized path * handle trailingSlash * add changeset
1 parent 8bd6809 commit e49782a

3 files changed

Lines changed: 55 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/aws": patch
3+
---
4+
5+
Preserve query parameters during i18n redirects to a localized path

packages/open-next/src/core/routing/i18n/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { InternalEvent, InternalResult } from "types/open-next";
44

55
import { emptyReadableStream } from "utils/stream.js";
66
import { debug } from "../../../adapters/logger.js";
7-
import { constructNextUrl } from "../util.js";
7+
import { constructNextUrl, convertToQueryString } from "../util.js";
88
import { acceptLanguage } from "./accept-header";
99

1010
function isLocalizedPath(path: string): boolean {
@@ -164,11 +164,16 @@ export function handleLocaleRedirect(
164164
const defaultLocale = domainLocale?.defaultLocale ?? i18n.defaultLocale;
165165

166166
if (detectedLocale.toLowerCase() !== defaultLocale.toLowerCase()) {
167+
const nextUrl = constructNextUrl(
168+
internalEvent.url,
169+
`/${detectedLocale}${NextConfig.trailingSlash ? "/" : ""}`,
170+
);
171+
const queryString = convertToQueryString(internalEvent.query);
167172
return {
168173
type: "core",
169174
statusCode: 307,
170175
headers: {
171-
Location: constructNextUrl(internalEvent.url, `/${detectedLocale}`),
176+
Location: `${nextUrl}${queryString}`,
172177
},
173178
body: emptyReadableStream(),
174179
isBase64Encoded: false,

packages/tests-unit/tests/core/routing/i18n.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ vi.mock("@opennextjs/aws/adapters/config/index.js", () => {
1414
defaultLocale: "en",
1515
locales: ["en", "fr"],
1616
},
17+
trailingSlash: undefined,
1718
},
1819
};
1920
});
@@ -232,6 +233,48 @@ describe("handleLocaleRedirect", () => {
232233
expect(result).toBe(false);
233234
});
234235

236+
it("should redirect to the localized path with a query parameter", () => {
237+
const event = createEvent({
238+
url: "http://localhost?foo=bar",
239+
headers: {
240+
"accept-language": "fr",
241+
},
242+
});
243+
244+
const result = handleLocaleRedirect(event);
245+
246+
expect(result).toMatchObject({
247+
statusCode: 307,
248+
headers: {
249+
Location: "http://localhost/fr?foo=bar",
250+
},
251+
});
252+
});
253+
254+
it("should redirect to the localized path with a query parameter when trailingSlash is true", () => {
255+
const trailingSlashSpy = vi
256+
.spyOn(NextConfig, "trailingSlash", "get")
257+
.mockReturnValue(true);
258+
259+
const event = createEvent({
260+
url: "http://localhost?foo=bar",
261+
headers: {
262+
"accept-language": "fr",
263+
},
264+
});
265+
266+
const result = handleLocaleRedirect(event);
267+
268+
expect(result).toMatchObject({
269+
statusCode: 307,
270+
headers: {
271+
Location: "http://localhost/fr/?foo=bar",
272+
},
273+
});
274+
275+
trailingSlashSpy.mockRestore();
276+
});
277+
235278
describe("using domain", () => {
236279
it("should redirect to the preferred domain if the domain is different", () => {
237280
vi.spyOn(NextConfig, "i18n", "get").mockReturnValue({

0 commit comments

Comments
 (0)