Bilde av PUS-logo

Typesikre ledetekster i React

Av Steffen Hageland 15 Aug 2018

Ved utvikling av applikasjoner her på NAV treffer man ofte på behov for internasjonalisering (i18n) og støtte for redigerbare ledetekster i koden. Dette behovet er løst på flere ulike måter i forskjellige applikasjoner. I denne bloggposten vil jeg ta for meg hvordan vi har valgt å løse dette dette i FO. Og hvordan vi kan få typesikkerhet på tekstene våre og dermed garantere at vi ikke bruker tekster som mangler.

Bakgrunn

I FO prosjektet bruker vi React som rammeverk for frontendapplikasjonene våre. For å ta i bruk ledetekster i applikasjoner bruker vi verktøyet react-intl. Historisk hadde vi ledetekstene i et eget repository og det var lagt til rette for at tekstene kunne deployes ut i miljø som et eget artifakt som kun inneholdt tekstene. Dette betydde at vi hadde mulighet for å kontinerlig deploye tekstene til produksjon selv om applikasonene våre skulle være med i en hovedleveranse.

Ved overgang til kontinuerlige leveranser var dette ikke lenger et behov, og for å forenkle oppsettet var det derfør ønskelig at tekstene lå nærmere applikasjonene. Det var og ønskelig å videreføre dagens oppsett med en tekstfil per ledetekst for å gjøre overgangen for redaktørene mindre.

Vi har i stor grad gått over til å skrive frontenden i TypeScript, og vi hadde et ønske om å dra fordel av dette også for ledetekster - og sørge for at vi hadde typesikkerhet også for ledetekster.

Nytt oppsett med ledetekster i applikasjonen

For å ta i bruk det nye oppsettet startet vi ved å flytte tekstene direkte over i applikasjonene. Dette betydde at vi hadde hadde en tekstfil for hver ledetekst på formatet {nøkkel}_{locale}.txt (f.eks. hovedside-overskrift_nb.txt) hvor innholdet var selve teksten. Vi lagde også et lite script som vi kunne kjøre som en del av et byggesteg for frontende som fant alle tekstfilene og genererte et javaScript object på formatet react-intl ønsket.

Vi endte nå opp med et objekt som kunne importeres direkte inn i react-intl. Redaktørene kunne gjøre endringer på innholdet på siden ved å oppdatere ledetekstfilene - og endringene ble bygget inn i applikasjonen og ble en del av neste release til produksjon.

Når man ønsker å bruke en ledetekst tar man bare i bruk komponenten FormattedMessage fra react-intl og gir den IDen på teksten man ønsker. Siden vi allerede har tatt i bruk TypeScript på prosjektet er det ønskelig at man får kompileringsfeil om man prøver å bruke tekster som ikke eksisterer. Løsningen på dette ble å la verktøyet som genererer tekst-bundelen (objektet med alle tekstene som man ga til react-intl) også generere en oppslagsfil. Denne oppslagsfilen kan man da bruke for å hente ut IDen for en bestemt tekst.

const texts = {
    "frontpageExampleText": "frontpage-example-text",
    "frontpageExampleTitle": "frontpage-example-title"
};

export default texts;

Objektene som ble generert ble på formatet som du ser over. Og når man nå ønsket å ta i bruk en tekst i applikasjonen importerte man dette objektet og hentet ut IDen derfra. Hvis man da prøvde å bruke et felt som ikke eksisterer vil typescript gi en kompileringsfeil.

import React from 'react';
import { FormattedMessage } from 'react-intl';

import texts from './intl.ts'

const MyFrontpage = () => (
    <section>
        <h1><FormattedMessage id={texts.frontpageExampleTitle} /></h1>
        <p><FormattedMessage id={texts.frontpageExampleText} /></p>
    </section>
);

Vi er ganske fornøyd med dette oppsettet og har derfor valgt å lage et verktøy som er publisert på NPM som gjør denne jobben. Om dette er noe som kan være nyttig for dere kan dere ta en titt på react-intl-bundler. Pakken er open-sourcet og vi håper å kunne jobbe videre på den som en open-source prosjekt. Så vi er veldig åpne for innspill og tilbakemeldinger på den.

Alternativ med tekster som JSON

Å ha en tekstfil per ledetekst fører fort til at det blir veldig mange filer i prosjektet. Og dette fører fort til at det kan føles litt rotete, spesielt om tekstene for de ulike komponentene ligger side om side med komponentene de bruker i. Vi har derfor gjort noen vurdringer på alternative oppsett.

Et forslag vi har er å gruppere tekster som er relaterte sammen. F.eks. om man har flere tekster innad i en modul eller komponent. Dette kan f.eks. gjøres gjennom å bruke et enkelt JSON-objekt hvor nøkkelen er ID på teksten og verdien er selve innholdet. Og med noen små utvidelser burde det være mulig å få react-intl-bundler til å støtte dette formatet. Men her er vi veldig åpne for innspill om man har gode tanker.

Redigering av ledetekster

I dag er det ofte slik at avstanden mellom teamene og redaktørene som får ansvaret for å redigere ledetekstene er ganske stor. Og redaktørene har gjerne ansvar for tekster og innhold i mange ulike applikasjoner. Dette medfører at man har et behov for standardisering for å gjøre det mulig for redaktørene og utføre sine arbeidsoppgaver på en effektiv måte. Dette begrenset mulighetene for teamet selv og finne gode løsninger for innhold og ledetekster.

Min anbefaling er derfor at man prøver å jobbe for at teamet selv tar eierskap til tekstene i applikasjonen. Man kan gjerne ha et godt samarbeid med redaktører og ved behov gjerne også gjøre redaktøren til en naturlig del av teamet. På den måten kan man sammen som team jobbe for å finne gode løsninger. I tillegg vil dette naturlig føre til at man jobber mer kontinuerlig med tekstene i applikasjonen.