feat(i18n): translate hardcoded strings across print, public recipe, and recipe management UI
Wires dozens of components and server pages to next-intl instead of hardcoded English text, and adds a lightweight getMessages/formatMessage helper for server components (print pages, public recipe page, cook metadata) since next-intl/server isn't wired into this app's routing. Backfills missing en/fr message keys that existing code already referenced but fr.json lacked.
This commit is contained in:
@@ -2,16 +2,10 @@ import { headers } from "next/headers";
|
||||
import { auth } from "@/lib/auth/server";
|
||||
import { db, mealPlans, eq, and } from "@epicure/db";
|
||||
import { PrintTrigger } from "@/components/recipe/print-trigger";
|
||||
import { getMessages, formatMessage } from "@/lib/i18n/server";
|
||||
|
||||
const DAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] as const;
|
||||
const DAY_LABELS: Record<string, string> = {
|
||||
mon: "Monday", tue: "Tuesday", wed: "Wednesday", thu: "Thursday",
|
||||
fri: "Friday", sat: "Saturday", sun: "Sunday",
|
||||
};
|
||||
const MEAL_ORDER = ["breakfast", "lunch", "dinner", "snack"] as const;
|
||||
const MEAL_LABELS: Record<string, string> = {
|
||||
breakfast: "Breakfast", lunch: "Lunch", dinner: "Dinner", snack: "Snack",
|
||||
};
|
||||
|
||||
function getMonday(dateStr?: string): Date {
|
||||
const d = dateStr ? new Date(dateStr) : new Date();
|
||||
@@ -31,6 +25,8 @@ export default async function MealPlanPrintPage({
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
if (!session) return null;
|
||||
|
||||
const m = getMessages((session.user as { locale?: string }).locale);
|
||||
|
||||
const monday = getMonday(week);
|
||||
const weekStart = monday.toISOString().slice(0, 10);
|
||||
const sunday = new Date(monday);
|
||||
@@ -82,22 +78,22 @@ export default async function MealPlanPrintPage({
|
||||
|
||||
<PrintTrigger />
|
||||
|
||||
<h1>Meal Plan</h1>
|
||||
<h1>{m.mealPlan.title}</h1>
|
||||
<p className="subtitle">{label}</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: "100px" }}>Day</th>
|
||||
{MEAL_ORDER.map((m) => (
|
||||
<th key={m}>{MEAL_LABELS[m]}</th>
|
||||
<th style={{ width: "100px" }}>{m.print.day}</th>
|
||||
{MEAL_ORDER.map((meal) => (
|
||||
<th key={meal}>{m.mealPlan.meals[meal]}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{DAYS.map((day) => (
|
||||
<tr key={day}>
|
||||
<td style={{ fontWeight: 600, color: "#555" }}>{DAY_LABELS[day]}</td>
|
||||
<td style={{ fontWeight: 600, color: "#555" }}>{m.print.days[day]}</td>
|
||||
{MEAL_ORDER.map((mealType) => {
|
||||
const entry = entries.find((e) => e.day === day && e.mealType === mealType);
|
||||
return (
|
||||
@@ -106,7 +102,7 @@ export default async function MealPlanPrintPage({
|
||||
<>
|
||||
<span className="recipe-title">{entry.recipe?.title ?? entry.note ?? "—"}</span>
|
||||
{entry.servings && (
|
||||
<span className="servings"> · {entry.servings} srv</span>
|
||||
<span className="servings">{formatMessage(m.print.srv, { count: entry.servings })}</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
@@ -120,7 +116,7 @@ export default async function MealPlanPrintPage({
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<footer>Printed from Epicure</footer>
|
||||
<footer>{m.print.footer}</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user