feat(i18n): add language switcher

This commit is contained in:
2026-06-11 16:16:06 +02:00
parent 18a192a069
commit c3cf4182ad
11 changed files with 319 additions and 2 deletions
+50
View File
@@ -0,0 +1,50 @@
import { beforeEach, describe, expect, it, vi } from "vitest"
import { LOCALE_COOKIE_MAX_AGE_SECONDS } from "@/i18n/locales"
const headersMocks = vi.hoisted(() => ({
cookieSet: vi.fn(),
cookies: vi.fn(),
}))
vi.mock("next/headers", () => ({
cookies: headersMocks.cookies,
}))
import { setLocaleAction } from "@/actions/i18n.actions"
describe("setLocaleAction", () => {
beforeEach(() => {
headersMocks.cookieSet.mockReset()
headersMocks.cookies.mockReset()
headersMocks.cookies.mockResolvedValue({
set: headersMocks.cookieSet,
})
})
it("writes a validated supported locale to the locale cookie", async () => {
const result = await setLocaleAction("es")
expect(result).toEqual({ success: true, locale: "es" })
expect(headersMocks.cookies).toHaveBeenCalledOnce()
expect(headersMocks.cookieSet).toHaveBeenCalledWith(
"stock-manager-locale",
"es",
{
path: "/",
sameSite: "lax",
maxAge: LOCALE_COOKIE_MAX_AGE_SECONDS,
httpOnly: true,
secure: false,
},
)
})
it("rejects unsupported locales without writing a cookie", async () => {
const result = await setLocaleAction("fr")
expect(result).toEqual({ success: false, error: "UNSUPPORTED_LOCALE" })
expect(headersMocks.cookies).not.toHaveBeenCalled()
expect(headersMocks.cookieSet).not.toHaveBeenCalled()
})
})
+18
View File
@@ -26,6 +26,24 @@ describe("i18n dictionaries", () => {
})
})
it("provides localized language switcher copy for English and Spanish", () => {
expect(getDictionary("en").common.languageSwitcher).toEqual({
label: "Language",
options: {
en: "English",
es: "Spanish",
},
})
expect(getDictionary("es").common.languageSwitcher).toEqual({
label: "Idioma",
options: {
en: "Inglés",
es: "Español",
},
})
})
it("keeps dashboard home dictionary keys aligned across locales", () => {
expect(getDictionary("en").dashboardHome).toEqual({
heading: "Dashboard",
+2
View File
@@ -4,6 +4,7 @@ import {
DEFAULT_LOCALE_ENV_VAR,
FALLBACK_LOCALE,
isLocale,
LOCALE_COOKIE_MAX_AGE_SECONDS,
LOCALE_COOKIE_NAME,
resolveDefaultLocale,
resolveLocale,
@@ -16,6 +17,7 @@ describe("i18n locales", () => {
expect(FALLBACK_LOCALE).toBe("en")
expect(DEFAULT_LOCALE_ENV_VAR).toBe("STOCK_MANAGER_DEFAULT_LOCALE")
expect(LOCALE_COOKIE_NAME).toBe("stock-manager-locale")
expect(LOCALE_COOKIE_MAX_AGE_SECONDS).toBe(60 * 60 * 24 * 365)
})
it("accepts only exact supported locale codes", () => {