feat: add i18n support (FR/EN) to backoffice with English as default

Implement full internationalization for the Next.js backoffice:
- i18n infrastructure: type-safe dictionaries (fr.ts/en.ts), cookie-based locale detection, React Context for client components, server-side translation helper
- Language selector in Settings page (General tab) with cookie + DB persistence
- All ~35 pages and components translated via t() / useTranslation()
- Default locale set to English, French available via settings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-18 19:39:01 +01:00
parent 055c376222
commit d4f87c4044
43 changed files with 2024 additions and 693 deletions

View File

@@ -1,4 +1,7 @@
"use client";
import { ReactNode } from "react";
import { useTranslation } from "../../../lib/i18n/context";
type BadgeVariant =
| "default"
@@ -70,19 +73,19 @@ const statusVariants: Record<string, BadgeVariant> = {
unread: "unread",
};
const statusLabels: Record<string, string> = {
extracting_pages: "Extraction des pages",
generating_thumbnails: "Miniatures",
};
interface StatusBadgeProps {
status: string;
className?: string;
}
export function StatusBadge({ status, className = "" }: StatusBadgeProps) {
const { t } = useTranslation();
const key = status.toLowerCase();
const variant = statusVariants[key] || "default";
const statusLabels: Record<string, string> = {
extracting_pages: t("statusBadge.extracting_pages"),
generating_thumbnails: t("statusBadge.generating_thumbnails"),
};
const label = statusLabels[key] ?? status;
return <Badge variant={variant} className={className}>{label}</Badge>;
}
@@ -95,22 +98,23 @@ const jobTypeVariants: Record<string, BadgeVariant> = {
thumbnail_regenerate: "warning",
};
const jobTypeLabels: Record<string, string> = {
rebuild: "Indexation",
full_rebuild: "Indexation complète",
thumbnail_rebuild: "Miniatures",
thumbnail_regenerate: "Régén. miniatures",
cbr_to_cbz: "CBR → CBZ",
};
interface JobTypeBadgeProps {
type: string;
className?: string;
}
export function JobTypeBadge({ type, className = "" }: JobTypeBadgeProps) {
const { t } = useTranslation();
const key = type.toLowerCase();
const variant = jobTypeVariants[key] || "default";
const jobTypeLabels: Record<string, string> = {
rebuild: t("jobType.rebuild"),
full_rebuild: t("jobType.full_rebuild"),
thumbnail_rebuild: t("jobType.thumbnail_rebuild"),
thumbnail_regenerate: t("jobType.thumbnail_regenerate"),
cbr_to_cbz: t("jobType.cbr_to_cbz"),
metadata_batch: t("jobType.metadata_batch"),
};
const label = jobTypeLabels[key] ?? type;
return <Badge variant={variant} className={className}>{label}</Badge>;
}