15 KiB
Del 20 – Internationalisering: audit, målarkitektur & migreringsplan
Styrande spec: den uppladdade i18n-specifikationen (*_Internationalisering_Claude.md). Detta dokument är
den audit + plan som i18n-spec §34 kräver före implementation, samt kvitto på vad som
redan är genomfört. Status: ✅ klart · 🔨 genomfört i denna leverans · 📋 planerat (fas angiven).
1. Nulägesrapport (audit av kodbasen)
Redan korrekt (byggt språkneutralt från start)
| Område | i18n-spec | Läge |
|---|---|---|
| Interna enums (måltid, kök, diet, plats, svårighet, notistyp, plan, creator-nivå …) | §4, §16 | ✅ Engelska maskinidentifierare överallt (dinner, fridge, vegetarian) |
| Canonical ingredient-IDs | §12 | ✅ Engelska slugs (chicken_breast); aliases[] finns; nameSv/nameEn-kolumner |
| Ingrediensdensitet | §10 | ✅ density_g_per_ml + grams_per_piece per ingrediens; generell volym↔massa-konvertering vägrar utan densitet (returnerar null) |
| Nutrition canonical per 100 g + provenance | §19 | ✅ (kJ + natrium/salt-profiler 📋 fas i7) |
| Allergener som canonical IDs, deterministiska | §18 | ✅ (marknadsregler/visningsprofiler 📋 fas i7) |
| Högtider datadrivna med market-kod + datumregler | §17 | ✅ season_events(market, date_rule, lead_days …) – uppfyller market_events-syftet |
Datum: timestamps UTC (timestamptz), bäst före som lokalt kalenderdatum (date) |
§21 | ✅ |
AI: canonical separerat från displaytext i kontrakten (canonicalIngredientId + detectedName) |
§22, §30 | ✅ |
Minne: strukturerad value + key, inte fritextsanning |
§23 | ✅ delvis (summarySv finns som cache – renderas om vid språkbyte 📋 fas i6) |
| Recept: struktur (ingrediens-IDs, mängder, tider, temperaturer, DNA) skild från text | §13 | ✅ struktur; översättningstabeller 📋 fas i5 |
| Varumärket som konfiguration | §28 | 🔨 brand.config.json – enda platsen; kod svep-verifierad |
| Store-produkt-IDs språkneutrala | §25 | 🔨 household_monthly m.fl., härledda ur brand-config |
Avvikelser som åtgärdas i DENNA leverans (🔨)
- Enhetskoder var svenska (
msk,tsk,krm,dl,st,portion) – brott mot §11. Åtgärd: språkneutrala koderGRAM, KILOGRAM, MILLILITER, DECILITER, LITER, TEASPOON, TABLESPOON, CUP_US, FLUID_OUNCE_US, OUNCE, POUND, COUNT, PORTION, PINCH, SLICE, CLOVE, CAN, PACKAGEi enum/DB/motorer/seed/API/app. Dokumenterad standard (§10): TEASPOON=5 ml, TABLESPOON=15 ml (metrisk), CUP_US=236,59 ml, FL_OZ_US=29,57 ml, OUNCE=28,35 g, POUND=453,59 g, PINCH≈0,5 ml.krmi svenska recept = 1 ml → lagras som MILLILITER; svensk visning ("krm", "msk") sker via översättningslagret. - UserLocalePreferences saknades (§6): ny tabell
user_locale_preferences(languageTag BCP 47, regionCode ISO 3166-1, timeZone IANA, measurementSystem METRIC/US_CUSTOMARY/MIXED, temperatureUnit, currencyCode ISO 4217, firstDayOfWeek, use24HourTime) +GET/PATCH /v1/me/locale-preferences; språk ≠ region ≠ enheter. - AAMOS-anrop saknade full lokaliseringskontext (§22): kuvertets metadata har nu
localeContext(languageTag, regionCode, timeZone, measurementSystem, temperatureUnit, currencyCode) och skickas från API/worker. - Hårdkodade UI-texter i mobilappen utanför i18n-modulen (§7): options-/etikett-
kartor (onboarding, samtycken, måltidstyper, butiksavdelningar, enhetsetiketter,
Alert-rubriker) flyttade till översättningsnycklar;
t()injicerar{brand}. - Notiser sparade endast färdig text (§26):
notificationshar nutemplate_key,variables,locale; renderad text behålls som cache och renderas om vid utskick. - Namnet i koden (§28): all kod avvarumärkad – paketscope
@app/*, UI via{brand}, tjänste-/könamn ur brand-slug, neutrala standardvärden i config.
Status M1–M10
| Fas | Status | Verifiering |
|---|---|---|
| M1 | ✅ Genomförd | Alla pengakolumner *_minor (integer) + households.currency_code; E2E: budget i minor + valuta, receptfilter på minor-kostnad |
| M2 | ✅ Genomförd | ingredient_translations (101 en-rader seedade ur nameEn) + unit_translations (36 rader sv+en) + /v1/i18n/units; en-sök träffar översättningar |
| M3 | ✅ Genomförd | recipe_translations + recipe_step_translations; TRANSLATE_RECIPE-jobb → deterministisk verifiering (stegantal, bevarade tal, konfidens) → admin publicerar; E2E: en-användare får översatt titel/steg/ingrediensnamn med sv-fallback |
| M4 | ✅ Genomförd | i18next med Intl-plural (_one/_other), locales/{sv,en}/common.json (175 nycklar, paritetstestad), språkbyte utan omstart (versionsbaserad re-mount), språkval i profilen synkat mot locale-preferenser |
| M5 | ✅ Genomförd | displayQuantity i shared-types (METRIC/US_CUSTOMARY/MIXED, kvartsavrundning för cups/tsp, ≈-märkning); mobilens formatQuantity konverterar per användarens måttsystem; 15 tester |
| M6 | ✅ Genomförd | nutrition_display_profiles + allergen_market_rules seedade (EU/SE/GB 14, US 9, CA 12) + /v1/i18n/nutrition-profile med EU-fallback; kJ- och natrium↔salt-hjälpare med tester |
| M7 | ✅ Genomförd | unaccent + pg_trgm + trigramindex (skapas idempotent i migrate, master-varianten i create-database.sql); sök: "creme"→Crème fraiche, "jordgubar"→Jordgubbar |
| M8 | 📋 Kvar (ops) | Store-metadata, skärmbilder och prissättning per marknad görs i butikskonsolerna inför lansering – produkt-ID:n redan varumärkes- och språkneutrala |
| M9 | 📋 Kvar (beslut D-024) | Adminpanelen förblir svensk tills internationell ops finns |
| M10 | ✅ Genomförd | buildMemoryOverview(items, languageTag): rubriker per språk, deterministisk summary-rendering ur strukturerad value med sv-fallback; nya minnen får lokaliserad summary via localeContext |
Ursprunglig plan (behålls som referens)
| # | Åtgärd | i18n-spec | Fas | Kommentar |
|---|---|---|---|---|
| M1 | Pengar i minor units + valuta: alla *_sek-kolumner → *_minor int + currency_code (hushållsnivå), Money-typ i API |
§20 | i5 | Typ + hjälpare levererade i shared-types/money.ts; kolumnbyte görs som nästa migration MEDAN databasen är produktionstom – ingen datamigrering behövs |
| M2 | ingredient_translations + flytt av nameSv/nameEn → rader; unit_translations i DB (visning finns nu i app-lagret) |
§11–12 | i3 | Canonical-IDs är redan rätt → ren additiv migration |
| M3 | recipe_translations + recipe_step_translations; AI-utkast med status DRAFT_AI→…→PUBLISHED + automatiska verifieringar (samma IDs/mängder/tider/allergener) |
§13–14 | i5 | Receptstrukturen är redan språkoberoende |
| M4 | i18next + ICU-plural i mobilen (ersätter interims-t()), namespaces /locales/{lng}/{ns}.json, expo-localization för förslag, språkbyte utan omstart, CI-vakt mot hårdkodad text |
§5, §7–8, §31 | i4 | Nyckelstrukturen är redan namespace-formad (home.*, scan.* …) → mekanisk flytt. Motivering i beslutslogg D-023 |
| M5 | Måttvisningstjänst: canonical→display per measurementSystem (ml→cup/tsp, g→oz/lb, °C→°F) med förlustfri lagring (canonical + original + källa + confidence) | §9–10, §30 | i5 | Konverteringstabellen finns; visningsvalet styrs av locale-prefs |
| M6 | nutrition_display_profiles, allergen_market_rules, kJ + natrium |
§18–19 | i7 | |
| M7 | Sök: unaccent + pg_trgm + alias/synonymtabeller per locale | §24 | i5 | ingredient_aliases-fältet finns redan |
| M8 | Lokaliserad store-metadata, screenshots, prissättning per marknad | §25 | i8 | Produkt-IDs redan neutrala |
| M9 | Adminpanelen till nycklar | §7 | i8 | Beslut: internt verktyg får vara svenskt tills internationell ops finns (D-024) |
| M10 | AAMOS-minne: rendera summaries ur strukturerad value vid läsning i valt språk | §23 | i6 | value är redan strukturerad |
2. Berörda filer (denna leverans)
packages/shared-types (enums/units/brand/money/locale) · packages/validation
(unit-schema, locale-prefs-schema) · packages/database (unit-enum, user_locale_preferences,
notifications-kolumner, seed) · motorerna (enhetsberäkning via kodtabellen) ·
packages/ai-contracts (localeContext) · apps/api (locale-prefs-routes, kontext till
AAMOS, neutrala defaults) · apps/worker (notismallar, kontext) · apps/mobile
(unit-etiketter, nya nycklar, brand-injektion) · alla package.json (scope @app/*).
3. Migreringsplan & rollback
Pre-launch utan produktionsdata ⇒ enum-/kolumnbyten görs i basmigrationen (0000 regenererad; loggat i Del 7). Efter lansering gäller i stället spec §29-mönstret: nya kolumner → mapping → dubbelskrivning → verifiering → borttag efter kontroll; varje migration med backup (deploy.sh vägrar utan dump) och nedskriven rollback. Rollback för denna leverans: git-tag före sveper + regenererbar databas.
4. Testplan (§31)
🔨 Nu: enhetstester för alla nya enhetskoder inkl. cup/oz/lb-konvertering och "vägra-utan-densitet"; locale-prefs-API; oförändrade motorresultat (61 befintliga tester gröna efter kodbytet). 📋 i4–i8: pseudolokal-test (nycklar aldrig synliga), plural-ICU, formatteringstester (decimal/tusental/datum/valuta) för sv-SE, en-US, en-GB, de-DE, fi-FI, snapshot av receptöversättningsverifieringen, CI-grep-vakt mot hårdkodade strängar.
5. Exit criteria-läge (§33)
Uppfyllda redan: språkneutrala enums, canonical ingredienser, datadrivna högtider, UTC/lokaldatum, AAMOS-kontext, enhetsbyte utan dataförlust (canonical lagring), nytt språk = nya rader (ingen schemaändring), nytt land = data (market-rader). Återstår före internationell lansering: M1–M8 ovan – uppskattat 2–3 sprintar, kan ske parallellt med svensk beta eftersom allt är additivt.