A pattern for maintaining type-safe, consistent translations in Norwegian apps and packages.
Install
mkdir -p .claude/skills/sif-intl && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15222" && unzip -o skill.zip -d .claude/skills/sif-intl && rm skill.zipInstalls to .claude/skills/sif-intl
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Mønster for typesikker i18n (nb/nn) i apper og pakker — implementering, parametersjekk og meningssjekk.Key capabilities
- →Add or change texts in an application or package
- →Create new `i18n/nb.ts` or `i18n/nn.ts` files
- →Verify that `{param}` variables are identical in Norwegian Bokmål (nb) and Nynorsk (nn)
- →Assess if the meaning of nb and nn texts are equivalent
- →Extract texts from components into localization files
- →Aggregate translations correctly in `i18n/index.tsx`
How it works
The skill outlines a pattern for typesafe internationalization in Norwegian, ensuring consistent parameters and semantic equivalence between Bokmål and Nynorsk translations through file structure and verification steps.
Inputs & outputs
When to use sif-intl
- →Adding new translated strings
- →Verifying translation parameters
- →Extracting text into localization files
About this skill
sif-intl
Bruk når
- Du skal legge til eller endre tekster i en app eller pakke.
- Du skal opprette ny
i18n/nb.tselleri18n/nn.ts. - Du trenger å verifisere at
{param}-variabler er like i nb og nn. - Du skal vurdere om meningen i nb- og nn-tekster er ekvivalente.
- Du arbeider med
AppText,useAppIntl,useSifXxxIntl,applicationIntlMessages. - Du skal trekke ut tekster fra en komponent eller sett med komponenter
Leveranse
- Typesikre
nb.tsognn.tsmed korrekte nøkler og parametere - Verifiserte parametere (identiske
{param}i nb og nn) - Meningsekvivalente tekster på begge målformer
- Korrekt aggregering i
i18n/index.tsx
Arkitektur
Mønsteret er delt i to kontekster:
Pakker (f.eks. @sif/soknad-ui)
src/
i18n/
index.tsx ← samler nb, nn; eksporterer hooks, komponent og messages
nb.ts ← (alternativt aggregert via side-spesifikke filer)
pages/
my-page/
i18n/
nb.ts ← kilde for meldinger i denne siden/komponenten
nn.ts ← typed mot nb-objektet, garanterer full dekning
Eksempel nb.ts (pakke, side-spesifikk):
export const stepPageMessages_nb = {
'@sifSoknadUi.stepFooter.fortsettSenere.trigger.label': 'Lagre og fortsett senere',
'@sifSoknadUi.stepFooter.slettSøknad.dialog.text.1':
'Informasjonen du har fylt ut blir slettet, og du kommer tilbake til velkomstsiden.',
};
Eksempel nn.ts (pakke):
import { stepPageMessages_nb } from './nb';
export const stepPageMessages_nn: Record<keyof typeof stepPageMessages_nb, string> = {
'@sifSoknadUi.stepFooter.fortsettSenere.trigger.label': 'Lagre og hald fram seinare',
'@sifSoknadUi.stepFooter.slettSøknad.dialog.text.1':
'Informasjonen du har fylt ut vert sletta, og du kjem tilbake til velkomstsida.',
};
i18n/index.tsx (pakke):
import { typedIntlHelper } from '@navikt/sif-common-utils';
import { FormattedMessage, useIntl } from 'react-intl';
import { stepPageMessages_nb } from '../pages/step-page/i18n/nb';
import { stepPageMessages_nn } from '../pages/step-page/i18n/nn';
const nb = {
...stepPageMessages_nb,
};
const nn: Record<keyof typeof nb, string> = {
...stepPageMessages_nn,
};
type SifSoknadUiMessageKeys = keyof typeof nb;
export const useSifSoknadUiIntl = () => {
const intl = useIntl();
return typedIntlHelper<SifSoknadUiMessageKeys>(intl);
};
interface SifSoknadUiTextProps {
id: SifSoknadUiMessageKeys;
values?: any;
}
export const SifSoknadUiText = (props: SifSoknadUiTextProps) => {
return <FormattedMessage {...props} />;
};
export const sifSoknadUiMessages = {
nb,
nn,
};
Apper (f.eks. aktivitetspenger-soknad)
src/app/
i18n/
index.tsx ← samler lib-meldinger + app-meldinger; eksporterer AppText, useAppIntl
nb/
appMessages.ts ← aggregerer steg- og domain-meldinger
nn/
appMessages.ts ← Record<keyof typeof appMessages_nb, string>
pages/
velkommen/
i18n/
nb.ts ← side-spesifikke nb-tekster
nn.ts ← Record<keyof typeof velkommenPageMessages_nb, string>
steps/
barn/
i18n/
nb.ts ← steg-spesifikke nb-tekster
nn.ts ← Record<keyof typeof barnStegMessages_nb, string>
Eksempel nb.ts (steg, med param og HTML-tag):
export const barnStegMessages_nb = {
'barnSteg.tittel': 'Barn',
'barnSteg.spørsmål.harBarn': 'Stemmer opplysningen om {antallBarn, plural, one {barnet} other {barna}}?',
'barnSteg.opplysninger.info.text':
'Du må være registrert som forelder ... <Lenke>kontakt med Skatteetaten</Lenke>.',
};
Eksempel nn.ts (steg):
import { barnStegMessages_nb } from './nb';
export const barnStegMessages_nn: Record<keyof typeof barnStegMessages_nb, string> = {
'barnSteg.tittel': 'Barn',
'barnSteg.spørsmål.harBarn': 'Stemmer opplysninga om {antallBarn, plural, one {barnet} other {barna}}?',
'barnSteg.opplysninger.info.text': 'Du må vere registrert som forelder ... <Lenke>kontakt Skatteetaten</Lenke>.',
};
i18n/nb/appMessages.ts:
import { barnStegMessages_nb } from '../../steps/barn/i18n/nb';
export const appMessages_nb = {
...barnStegMessages_nb,
'application.title': 'Søknad om aktivitetspenger',
};
i18n/nn/appMessages.ts:
import { barnStegMessages_nn } from '../../steps/barn/i18n/nn';
import { appMessages_nb } from '../nb/appMessages';
export const appMessages_nn: Record<keyof typeof appMessages_nb, string> = {
...barnStegMessages_nn,
'application.title': 'Søknad om aktivitetspengar',
};
i18n/index.tsx (app):
import { typedIntlHelper } from '@navikt/sif-common-utils';
import { sifSoknadUiMessages } from '@sif/soknad-ui/i18n';
import { FormattedMessage, useIntl } from 'react-intl';
import { velkommenPageMessages_nb } from '../pages/velkommen/i18n/nb';
import { velkommenPageMessages_nn } from '../pages/velkommen/i18n/nn';
import { appMessages_nb } from './nb/appMessages';
import { appMessages_nn } from './nn/appMessages';
const libMessages = {
nb: { ...sifSoknadUiMessages.nb },
nn: { ...sifSoknadUiMessages.nn },
};
const nb = {
...libMessages.nb,
...appMessages_nb,
...velkommenPageMessages_nb,
};
const nn: Record<keyof typeof nb, string> = {
...libMessages.nn,
...appMessages_nn,
...velkommenPageMessages_nn,
};
export type AppMessageKeys = keyof typeof nb;
export const useAppIntl = () => {
const intl = useIntl();
return typedIntlHelper<AppMessageKeys>(intl);
};
export type AppIntlShape = ReturnType<typeof useAppIntl>;
interface AppTextProps {
id: AppMessageKeys;
values?: any;
}
export const AppText = (props: AppTextProps) => {
return <FormattedMessage {...props} />;
};
export const applicationIntlMessages = {
nb,
nn,
};
Regler og konvensjoner
Regler for copy og tekstendringer
- AI skal ikke finne på, skrive om eller forbedre brukervendte tekster på egen hånd.
- Når oppgaven er å flytte, trekke ut eller strukturere tekster, skal tekstinnholdet beholdes uendret.
- Nye tekster skal bare legges inn når teksten er eksplisitt oppgitt av bruker, finnes i eksisterende kildefil, eller kommer fra etablert copy.
- Hvis nødvendig tekstgrunnlag mangler, stopp og be om teksten i stedet for å dikte den.
- Ved migrering eller refaktorering er hovedregelen at eksisterende tekster skal være identiske før og etter endringen.
- Storybook-tekster, demo-tekster og labels i
preview.jseller stories er også brukervendte tekster og skal holde korrekt norsk rettskrivning. - Ikke erstatt norske tegn med ASCII-varianter i tekster. Bruk
æ,øogånår teksten er norsk, med mindre teksten eksplisitt kommer fra en kilde som allerede mangler disse tegnene.
Nøkkelstruktur
| Kontekst | Prefiks-konvensjon | Eksempel |
|---|---|---|
| Pakke | @pkgName. | @sifSoknadUi.stepFooter.slettSøknad.label |
| Side (app) | page.pageName. | page.velkommen.guide.tittel |
| Steg (app) | stepName. | barnSteg.tittel |
| Sub-komponent innen et steg | componentName. | tilsynsordningSøknadsperiode.leggTilEndring |
| App-nivå | fritt | application.title |
Grunnpattern for sub-komponenter: Bruk komponentnavnet (camelCase) som prefiks — ikke domenebetegnelsen for innholdet. Eksempel: nøkler i TilsynsordningSøknadsperiode.tsx bruker prefikset tilsynsordningSøknadsperiode..
nn.ts-typing
nn skal alltid types mot nb med Record<keyof typeof ..._nb, string>. Dette sikrer:
- TS-feil ved manglende nøkler i nn
- TS-feil ved nøkler i nn som ikke finnes i nb
- Full dekning garantert av typesystemet
ICU-parametre er kontrakter
Parametre i meldingsstrenger som {navn}, {dato}, {count} og custom tags er en del av kontrakten mellom meldingen og komponenten som sender values.
- Parameternavn skal være identiske i
nb.ts,nn.tsog i komponentkoden som sendervalues. - Parameternavn skal aldri oversettes. Oversett teksten rundt, men behold selve parameternavnet uendret.
- Ved migrering, portering eller refaktorering skal eksisterende parameternavn beholdes eksakt som i kilden.
- Før du avslutter en i18n-endring, gjør en eksplisitt parametersjekk: sammenlign alle
{param}og custom tags mellomnb,nnog kallstedet.
Dette er en blokkerende regel. Mismatch i parameternavn gir ødelagt interpolering i UI og er ikke akseptabelt.
Manglende nynorsk-oversettelse
Hvis nynorsk ikke finnes eller ikke er levert av en oversetter, spread nb uten å oversette selv:
import { myMessages_nb } from './nb';
export const myMessages_nn: Record<keyof typeof myMessages_nb, string> = {
...myMessages_nb,
};
Lag nynorsk-tekster kun når brukeren eksplisitt ber om det. Ellers skal nynorsk komme fra et menneske eller en offisiell oversetter.
Nynorsk pronomen
I nynorsk tekst skal me alltid brukes i staden for vi som subjektspronomen i første person fleirtal.
✅ «slik at me kan handsame saka di»
❌ «slik at vi kan handsame saka di»
Sjekk alle nn.ts-filer for vi (med mellomrom etter) og erstatt med me .
Bruk av komponent vs. hook
Velg basert på om teksten er en JSX-node (children) eller en string-prop:
| Kontekst | Bruk | Eksempel |
|---|---|---|
| JSX text node (children) |
Content truncated.
When not to use it
- →When working with languages other than Norwegian Bokmål (nb) and Nynorsk (nn)
- →When not needing typesafe internationalization
- →When not working with `AppText`, `useAppIntl`, `useSifXxxIntl`, or `applicationIntlMessages`
Limitations
- →The pattern is designed for Norwegian Bokmål (nb) and Nynorsk (nn) only
- →The parameter check focuses on `{param}` variables, ICU-plural forms, and HTML-tags
- →The meaning check is a semantic assessment, not an automated verification
How it compares
This pattern specifically addresses typesafety and semantic consistency for Norwegian dialects, providing a structured approach beyond basic internationalization setups.
Compared to similar skills
sif-intl side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| sif-intl (this skill) | 0 | 2mo | No flags | Intermediate |
| i18n | 5 | 2mo | No flags | Intermediate |
| review-frontend | 0 | 3mo | Review | Intermediate |
| remotion-solar-tts | 0 | 3mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by navikt
View all by navikt →You might also like
i18n
lobehub
Internationalization guide using react-i18next. Use when adding translations, creating i18n keys, or working with localized text in React components (.tsx files). Triggers on translation tasks, locale management, or i18n implementation.
review-frontend
OpenAEV-Platform
>-
remotion-solar-tts
wustites
Build or update narrated multilingual Remotion videos that combine Three.js solar-system visuals with Edge TTS voiceover. Use when Codex needs to scaffold, modify, validate, or render English, Chinese, Japanese, or Korean Remotion compositions with a Sun-to-planets narration timeline, generated MP3
React + Ant Design Frontend
daithang-organization
Complete guide for the SE104_VLEAGUE React frontend — pages, services, components, auth flow, i18n, and patterns
add-locale
yhk1038
Add a new UI language/locale to the claude-code-gui webview i18n system, or extend/verify an existing one. Walks a contributor (or agent) through creating the per-namespace translation JSON files, registering the locale, and verifying parity — consistently with how the shipped locales are structured
harden
Aixbox
Improve interface resilience through better error handling, i18n support, text overflow handling, and edge case management. Makes interfaces robust and production-ready. Use when the user asks to harden, make production-ready, handle edge cases, add error states, or fix overflow and i18n issues.