Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/open-next/src/core/routing/i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ function getLocaleFromCookie(cookies: Record<string, string>) {
}

function detectLocale(internalEvent: InternalEvent, i18n: i18nConfig): string {
if (i18n.localeDetection === false) {
return i18n.defaultLocale;
}

const cookiesLocale = getLocaleFromCookie(internalEvent.cookies);
const preferredLocale = acceptLanguage(
internalEvent.headers["accept-language"],
Expand Down
1 change: 1 addition & 0 deletions packages/open-next/src/types/next-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export type Header = {
export interface i18nConfig {
locales: string[];
defaultLocale: string;
localeDetection?: false;
}
export interface NextConfig {
basePath?: string;
Expand Down
144 changes: 144 additions & 0 deletions packages/tests-unit/tests/core/routing/i18n.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { NextConfig } from "@opennextjs/aws/adapters/config/index.js";
import { localizePath } from "@opennextjs/aws/core/routing/i18n/index.js";
import { convertFromQueryString } from "@opennextjs/aws/core/routing/util.js";
import type { InternalEvent } from "@opennextjs/aws/types/open-next.js";
import { vi } from "vitest";

vi.mock("@opennextjs/aws/adapters/config/index.js", () => {
return {
NextConfig: {
i18n: {
defaultLocale: "en",
locales: ["en", "fr"],
},
},
};
});

vi.mock("@opennextjs/aws/core/routing/i18n/accept-header.js", () => ({
acceptLanguage: (header: string, _?: string[]) => (header ? "fr" : undefined),
}));

type PartialEvent = Partial<
Omit<InternalEvent, "body" | "rawPath" | "query">
> & { body?: string };

function createEvent(event: PartialEvent): InternalEvent {
const [rawPath, qs] = (event.url ?? "/").split("?", 2);
return {
type: "core",
method: event.method ?? "GET",
rawPath,
url: event.url ?? "/",
body: Buffer.from(event.body ?? ""),
headers: event.headers ?? {},
query: convertFromQueryString(qs ?? ""),
cookies: event.cookies ?? {},
remoteAddress: event.remoteAddress ?? "::1",
};
}

beforeEach(() => {
vi.resetAllMocks();
});

describe("localizePath", () => {
it("should return raw path if no i18n config is set", () => {
const i18nSpy = vi
.spyOn(NextConfig, "i18n", "get")
.mockReturnValue(undefined);

const event = createEvent({
url: "/foo",
});

const result = localizePath(event);

expect(result).toEqual("/foo");

i18nSpy.mockRestore();
});

it("should return default locale localized if localeDetection is set to false", () => {
const i18nSpy = vi.spyOn(NextConfig, "i18n", "get").mockReturnValue({
defaultLocale: "en",
locales: ["en", "fr"],
localeDetection: false,
});

const event = createEvent({
url: "/foo",
headers: {
"accept-language": "fr",
},
cookies: {
NEXT_LOCALE: "fr",
},
});

const result = localizePath(event);

expect(result).toEqual("/en/foo");

i18nSpy.mockRestore();
});

it("should return the same path if the path is already localized", () => {
const event = createEvent({
url: "/fr/foo",
});

const result = localizePath(event);

expect(result).toEqual("/fr/foo");
});

it("should get locale from cookies if NEXT_LOCALE cookie is set to a valid locale", () => {
const event = createEvent({
url: "/foo",
cookies: {
NEXT_LOCALE: "fr",
},
});

const result = localizePath(event);

expect(result).toEqual("/fr/foo");
});

it("should fallback on default locale if NEXT_LOCALE cookie is set to an invalid locale", () => {
const event = createEvent({
url: "/foo",
cookies: {
NEXT_LOCALE: "pt",
},
});

const result = localizePath(event);

expect(result).toEqual("/en/foo");
});

it("should use accept-language header if no cookie are present", () => {
const event = createEvent({
url: "/foo",
headers: {
"accept-language": "fr",
},
});

const result = localizePath(event);

expect(result).toEqual("/fr/foo");
});

it("should fallback to default locale if no cookie or header are set", () => {
const event = createEvent({
url: "/foo",
});

const result = localizePath(event);

expect(result).toEqual("/en/foo");
});
});