Impulse AI Docs
Intern dokumentasjon
Hopp til innhold

Server-side i18n

Server-side tekster (push-varsler, transaksjonelle e-poster) oversettes via et eget i18n-system som er frikoblet fra KMP/moko. Systemet er designet for global skalering: engelsk som base, nye språk legges til per marked basert på brukerdata.

apps/api/src/i18n/
├── index.ts # t(), tVar(), resolveLocale()
├── locales.ts # 16 locale-koder (ServerLocale type)
└── messages/
├── notifications.ts # Push-varsel strenger
└── emails.ts # Transaksjonell e-post (fremtidig)

Designprinsipp: Én kilde per streng, ett oppslag per bruk. Engelsk er alltid påkrevd (compile-time). Andre språk er valgfrie og faller tilbake til engelsk.

Språkstrategi

heading.anchorLabel
LagSystemSpråk i dagSkalering
App UI (iOS/Android)moko-resources (KMP)16 språkVia moko strings.xml
Server varsleri18n/ (dette systemet)Kun engelskPer marked, datadrevet
AI-outputAnthropic prompt directivesBrukerens valgte språkAutomatisk

Strategien er engelsk globalt fra dag 1. Nye språk legges til i meldingskatalogen når et marked beviser behov gjennom brukerdata. Aldri 16 språk samtidig.

resolveLocale(code?: string | null): ServerLocale

heading.anchorLabel

Normaliserer en rå språkkode til en gyldig ServerLocale:

InputOutputGrunn
'en''en'Direkte match
'no''no'Direkte match
'nb''no'Norsk variant
'nn''no'Norsk variant
null'en'Fallback
'xx''en'Ukjent kode

t(catalog, key, locale): string

heading.anchorLabel

Slår opp en oversatt streng. Faller tilbake til engelsk hvis locale mangler oversettelse.

import { t, resolveLocale } from '../../i18n/index.js'
import { NOTIFICATION_MESSAGES } from '../../i18n/messages/notifications.js'
const locale = resolveLocale(user.preferredLanguage)
const body = t(NOTIFICATION_MESSAGES, 'reminder.week2', locale)
// → "It's been two weeks. Impulse AI is here when you need it."

tVar(catalog, key, locale, vars): string

heading.anchorLabel

Som t(), men interpolerer {placeholder}-variabler:

const body = tVar(NOTIFICATION_MESSAGES, 'lifecycle.trial_expiring', locale, {
expiryDate: 'May 1',
})
// → "Your trial expires May 1. Upgrade to keep access."

Meldingskatalog

heading.anchorLabel

Alle strenger lever i typed catalogs under i18n/messages/. Hver entry krever en (compile-time) og kan ha valgfrie oversettelser:

export const NOTIFICATION_MESSAGES = {
'reminder.week1.a': {
en: 'You captured something important. How did it go?',
// de: 'Du hast etwas Wichtiges festgehalten. Wie lief es?', ← fremtidig
},
'lifecycle.trial_expiring': {
en: 'Your trial expires {expiryDate}. Upgrade to keep access.',
},
} as const satisfies MessageCatalog

MessageCatalog-typen sikrer at en alltid finnes:

type MessageCatalog = Record<string, Partial<Record<ServerLocale, string>> & { en: string }>

Legge til et nytt språk

heading.anchorLabel

To steg:

1. Legg til oversettelser i katalogen:

i18n/messages/notifications.ts
'reminder.week1.a': {
en: 'You captured something important. How did it go?',
de: 'Du hast etwas Wichtiges festgehalten. Wie ist es gelaufen?',
},

2. Ferdig. resolveLocale() håndterer allerede alle 16 locale-koder. t() faller tilbake til EN for nøkler som mangler oversettelse i det nye språket.

Ingen migrasjon, ingen KMP-endring, ingen deploy-avhengighet.

Legge til nye meldinger

heading.anchorLabel

Push-varsel:

i18n/messages/notifications.ts
'campaign.new_feature': {
en: 'Something new is here. Open the app to explore.',
},

E-post (fremtidig):

i18n/messages/emails.ts
'welcome.subject': {
en: 'Welcome to Impulse AI',
},
'welcome.body': {
en: 'Your transformation journey begins now, {name}.',
},

E-post body med HTML wrapping implementeres i en fremtidig apps/api/src/emails/-modul som kaller tVar() for subject/body og wrapper i en felles HTML-layout.

Notification Privacy Rule

heading.anchorLabel

Push-varsler er en offentlig overflate (låseskjerm, varslingssenteret, widgets). Aldri inkluder brukerinnhold i push-tekst:

  • Ingen impulse-titler eller beskrivelser
  • Ingen session-innhold eller refleksjoner
  • Ingen sjekk-inn-svar
  • Ingen AI-genererte analyse-sammendrag
  • Ingen personlige områdenavn

Varsler skal invitere uten å avsløre. Alle som ser brukerens låseskjerm skal ikke lære noe om deres indre prosess.

Forskjell fra moko (KMP)

heading.anchorLabel
Server i18nmoko (KMP)
KjørerAPI-server (Node.js)iOS/Android app
BruksområdePush-varsler, e-postApp-UI labels
EndringshastighetHøy (eksperimenterer med tone/timing)Lav (stabile UI-labels)
DeployAPI-deployApp Store release
KoblingFrikobletKMP compile required

Å koble server-varsler til moko ville bety at en tekstendring i en push-varsel krever KMP-kompilering og app-deploy. Det er feil avveining for tekster som itereres hyppig.