Skip to content

Commit 8f4b67a

Browse files
alacroixconico974
andauthored
Support i18n localeDetection with value false (#585)
* feat: support localeDetection = false * test: add unit tests for localizePath * Create giant-horses-sin.md --------- Co-authored-by: conico974 <[email protected]>
1 parent cf21175 commit 8f4b67a

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

.changeset/giant-horses-sin.md

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+
Support i18n localeDetection with value false

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ function getLocaleFromCookie(cookies: Record<string, string>) {
2121
}
2222

2323
function detectLocale(internalEvent: InternalEvent, i18n: i18nConfig): string {
24+
if (i18n.localeDetection === false) {
25+
return i18n.defaultLocale;
26+
}
27+
2428
const cookiesLocale = getLocaleFromCookie(internalEvent.cookies);
2529
const preferredLocale = acceptLanguage(
2630
internalEvent.headers["accept-language"],

packages/open-next/src/types/next-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export type Header = {
6464
export interface i18nConfig {
6565
locales: string[];
6666
defaultLocale: string;
67+
localeDetection?: false;
6768
}
6869
export interface NextConfig {
6970
basePath?: string;
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { NextConfig } from "@opennextjs/aws/adapters/config/index.js";
2+
import { localizePath } from "@opennextjs/aws/core/routing/i18n/index.js";
3+
import { convertFromQueryString } from "@opennextjs/aws/core/routing/util.js";
4+
import type { InternalEvent } from "@opennextjs/aws/types/open-next.js";
5+
import { vi } from "vitest";
6+
7+
vi.mock("@opennextjs/aws/adapters/config/index.js", () => {
8+
return {
9+
NextConfig: {
10+
i18n: {
11+
defaultLocale: "en",
12+
locales: ["en", "fr"],
13+
},
14+
},
15+
};
16+
});
17+
18+
vi.mock("@opennextjs/aws/core/routing/i18n/accept-header.js", () => ({
19+
acceptLanguage: (header: string, _?: string[]) => (header ? "fr" : undefined),
20+
}));
21+
22+
type PartialEvent = Partial<
23+
Omit<InternalEvent, "body" | "rawPath" | "query">
24+
> & { body?: string };
25+
26+
function createEvent(event: PartialEvent): InternalEvent {
27+
const [rawPath, qs] = (event.url ?? "/").split("?", 2);
28+
return {
29+
type: "core",
30+
method: event.method ?? "GET",
31+
rawPath,
32+
url: event.url ?? "/",
33+
body: Buffer.from(event.body ?? ""),
34+
headers: event.headers ?? {},
35+
query: convertFromQueryString(qs ?? ""),
36+
cookies: event.cookies ?? {},
37+
remoteAddress: event.remoteAddress ?? "::1",
38+
};
39+
}
40+
41+
beforeEach(() => {
42+
vi.resetAllMocks();
43+
});
44+
45+
describe("localizePath", () => {
46+
it("should return raw path if no i18n config is set", () => {
47+
const i18nSpy = vi
48+
.spyOn(NextConfig, "i18n", "get")
49+
.mockReturnValue(undefined);
50+
51+
const event = createEvent({
52+
url: "/foo",
53+
});
54+
55+
const result = localizePath(event);
56+
57+
expect(result).toEqual("/foo");
58+
59+
i18nSpy.mockRestore();
60+
});
61+
62+
it("should return default locale localized if localeDetection is set to false", () => {
63+
const i18nSpy = vi.spyOn(NextConfig, "i18n", "get").mockReturnValue({
64+
defaultLocale: "en",
65+
locales: ["en", "fr"],
66+
localeDetection: false,
67+
});
68+
69+
const event = createEvent({
70+
url: "/foo",
71+
headers: {
72+
"accept-language": "fr",
73+
},
74+
cookies: {
75+
NEXT_LOCALE: "fr",
76+
},
77+
});
78+
79+
const result = localizePath(event);
80+
81+
expect(result).toEqual("/en/foo");
82+
83+
i18nSpy.mockRestore();
84+
});
85+
86+
it("should return the same path if the path is already localized", () => {
87+
const event = createEvent({
88+
url: "/fr/foo",
89+
});
90+
91+
const result = localizePath(event);
92+
93+
expect(result).toEqual("/fr/foo");
94+
});
95+
96+
it("should get locale from cookies if NEXT_LOCALE cookie is set to a valid locale", () => {
97+
const event = createEvent({
98+
url: "/foo",
99+
cookies: {
100+
NEXT_LOCALE: "fr",
101+
},
102+
});
103+
104+
const result = localizePath(event);
105+
106+
expect(result).toEqual("/fr/foo");
107+
});
108+
109+
it("should fallback on default locale if NEXT_LOCALE cookie is set to an invalid locale", () => {
110+
const event = createEvent({
111+
url: "/foo",
112+
cookies: {
113+
NEXT_LOCALE: "pt",
114+
},
115+
});
116+
117+
const result = localizePath(event);
118+
119+
expect(result).toEqual("/en/foo");
120+
});
121+
122+
it("should use accept-language header if no cookie are present", () => {
123+
const event = createEvent({
124+
url: "/foo",
125+
headers: {
126+
"accept-language": "fr",
127+
},
128+
});
129+
130+
const result = localizePath(event);
131+
132+
expect(result).toEqual("/fr/foo");
133+
});
134+
135+
it("should fallback to default locale if no cookie or header are set", () => {
136+
const event = createEvent({
137+
url: "/foo",
138+
});
139+
140+
const result = localizePath(event);
141+
142+
expect(result).toEqual("/en/foo");
143+
});
144+
});

0 commit comments

Comments
 (0)