feat(i18n): server-genererad prosa -> nyckel+params (min dag, ombokning, scan-diff)
- Min-dag-noten: noteKey (estimateNote/computedNote) bredvid svensk note; klient t(noteKey).
- Ombokningsskäl (planning.ts): rescheduleReasonKey + params bredvid rescheduleReasonSv;
klient t(key, {title}). Workerns två andra ombokningstexter lämnade på svensk fallback.
- Scan-diff: reasonCode-enum (6 koder) bredvid reasonSv; klient t(scanDiff.reason.<code>).
- 8 nya nycklar i alla 12 språk. Ingen beslutslogik rörd; alla Sv-fält kvar som fallback.
- Kvar som egna spår: recept-säkerhetsmeddelanden + substitutions-data (översättningstabell).
- typecheck grönt (20/20), vakt grön, inventory-engine 53/53 tester.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -184,9 +184,12 @@ export async function mealRoutes(app: FastifyInstance) {
|
||||
title: m.recipeId ? (titleMap.get(m.recipeId) ?? m.titleSv) : m.titleSv,
|
||||
})),
|
||||
summary,
|
||||
// Svenska strängen behålls som fallback; noteKey låter klienten översätta
|
||||
// (samma två fall som förr – noten visas alltid, exakt som tidigare).
|
||||
note: hasEstimates
|
||||
? "Dagen innehåller uppskattade värden från foto – justera gärna vid behov."
|
||||
: "Värdena är beräknade ur recept och livsmedelsdata och visas som uppskattningar.",
|
||||
noteKey: hasEstimates ? "myday.estimateNote" : "myday.computedNote",
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,14 @@ import { loadLocalePreferences } from "../lib/localeContext.js";
|
||||
* (GENERATE_WEEK_PLAN) som väger lager, utgångsdatum, matlådor, budget,
|
||||
* variation och mål – med deterministisk kärna och AAMOS som rådgivare.
|
||||
*/
|
||||
/**
|
||||
* Kanonisk svensk motivering vid dynamisk omplanering (spec §25). Samma funktion
|
||||
* används både när planposten skrivs och för att härleda den översättbara i18n-
|
||||
* nyckeln vid läsning (så att klienten kan visa den på användarens språk).
|
||||
*/
|
||||
export const rescheduleMovedReasonSv = (title: string): string =>
|
||||
`${title} flyttades hit eftersom råvarorna bör användas först.`;
|
||||
|
||||
export async function planningRoutes(app: FastifyInstance) {
|
||||
const auth = { preHandler: [app.authenticate] };
|
||||
|
||||
@@ -54,10 +62,20 @@ export async function planningRoutes(app: FastifyInstance) {
|
||||
);
|
||||
const result = plansWithEntries.map(({ plan, entries }) => ({
|
||||
...plan,
|
||||
entries: entries.map((e) => ({
|
||||
...e,
|
||||
title: e.recipeId ? (titleMap.get(e.recipeId) ?? e.titleSv) : e.titleSv,
|
||||
})),
|
||||
entries: entries.map((e) => {
|
||||
const title = e.recipeId ? (titleMap.get(e.recipeId) ?? e.titleSv) : e.titleSv;
|
||||
// Behåll rescheduleReasonSv (fallback) men lägg till översättbar nyckel +
|
||||
// parametrar för just omplanerings-meningen (andra motiveringar, t.ex.
|
||||
// matlåde-noteringar från workern, faller kvar på svenska).
|
||||
const structured =
|
||||
e.rescheduleReasonSv && e.rescheduleReasonSv === rescheduleMovedReasonSv(e.titleSv)
|
||||
? {
|
||||
rescheduleReasonKey: "plan.rescheduleReason",
|
||||
rescheduleReasonParams: { title },
|
||||
}
|
||||
: {};
|
||||
return { ...e, title, ...structured };
|
||||
}),
|
||||
}));
|
||||
return { plans: result };
|
||||
});
|
||||
@@ -167,7 +185,7 @@ export async function planningRoutes(app: FastifyInstance) {
|
||||
recipeId: entry.recipeId,
|
||||
titleSv: entry.titleSv,
|
||||
status: "moved",
|
||||
rescheduleReasonSv: `${entry.titleSv} flyttades hit eftersom råvarorna bör användas först.`,
|
||||
rescheduleReasonSv: rescheduleMovedReasonSv(entry.titleSv),
|
||||
})
|
||||
.where(eq(schema.weekPlanEntries.id, later[0].id));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user