feat(i18n): add locale dictionaries and pilot surfaces

This commit is contained in:
2026-06-11 04:55:47 +02:00
parent 2c6d6bffcd
commit ac3dfe69cd
15 changed files with 354 additions and 19 deletions
+86
View File
@@ -0,0 +1,86 @@
import { describe, expect, it } from "vitest"
import { dictionaries, getDictionary } from "@/i18n/dictionaries"
import { SUPPORTED_LOCALES } from "@/i18n/locales"
describe("i18n dictionaries", () => {
it("provides dictionaries for every supported locale and no extra locales", () => {
expect(Object.keys(dictionaries).sort()).toEqual(
[...SUPPORTED_LOCALES].sort(),
)
})
it("returns localized login copy for English and Spanish", () => {
expect(getDictionary("en").login).toEqual({
title: "Sign In",
usernameLabel: "Username",
passwordLabel: "Password",
submitLabel: "Sign In",
})
expect(getDictionary("es").login).toEqual({
title: "Iniciar sesión",
usernameLabel: "Usuario",
passwordLabel: "Contraseña",
submitLabel: "Iniciar sesión",
})
})
it("keeps dashboard home dictionary keys aligned across locales", () => {
expect(getDictionary("en").dashboardHome).toEqual({
heading: "Dashboard",
cards: {
items: {
title: "Total Items",
countLabel: "Total",
},
assets: {
title: "Total Assets",
countLabel: "Total",
},
recipients: {
title: "Total Recipients",
countLabel: "Total",
},
},
})
expect(getDictionary("es").dashboardHome).toEqual({
heading: "Panel de control",
cards: {
items: {
title: "Total de artículos",
countLabel: "Total",
},
assets: {
title: "Total de activos",
countLabel: "Total",
},
recipients: {
title: "Total de destinatarios",
countLabel: "Total",
},
},
})
})
it("has exact structural parity between English and Spanish dictionaries", () => {
expect(extractKeyPaths(getDictionary("es"))).toEqual(
extractKeyPaths(getDictionary("en")),
)
})
})
function extractKeyPaths(value: unknown, prefix = ""): string[] {
if (!isPlainObject(value)) return [prefix]
return Object.keys(value)
.sort()
.flatMap((key) =>
extractKeyPaths(value[key], prefix ? `${prefix}.${key}` : key),
)
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
}
+48
View File
@@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest"
import {
DEFAULT_LOCALE_ENV_VAR,
FALLBACK_LOCALE,
isLocale,
LOCALE_COOKIE_NAME,
resolveDefaultLocale,
resolveLocale,
SUPPORTED_LOCALES,
} from "@/i18n/locales"
describe("i18n locales", () => {
it("defines exactly English and Spanish with an env-configured default", () => {
expect(SUPPORTED_LOCALES).toEqual(["en", "es"])
expect(FALLBACK_LOCALE).toBe("en")
expect(DEFAULT_LOCALE_ENV_VAR).toBe("STOCK_MANAGER_DEFAULT_LOCALE")
expect(LOCALE_COOKIE_NAME).toBe("stock-manager-locale")
})
it("accepts only exact supported locale codes", () => {
expect(isLocale("en")).toBe(true)
expect(isLocale("es")).toBe(true)
for (const value of ["", "ES", "es-MX", "fr", undefined, null, 1]) {
expect(isLocale(value)).toBe(false)
}
})
it("resolves valid locale cookie values and falls back to the configured default for invalid values", () => {
expect(resolveLocale("es", "en")).toBe("es")
expect(resolveLocale("en", "es")).toBe("en")
for (const value of ["", "ES", "es-MX", "fr", undefined, null, 1]) {
expect(resolveLocale(value, "es")).toBe("es")
expect(resolveLocale(value, "en")).toBe("en")
}
})
it("resolves the configured default locale from env-like values with a safe English fallback", () => {
expect(resolveDefaultLocale("es")).toBe("es")
expect(resolveDefaultLocale("en")).toBe("en")
for (const value of ["", "ES", "es-MX", "fr", undefined, null, 1]) {
expect(resolveDefaultLocale(value)).toBe("en")
}
})
})