Oversette Stimulus Apps Med I18Neste

I min tidligere artikkel dekket jeg Stimulus-et beskjedent JavaScript-rammeverk laget av Basecamp. I dag snakker jeg om internasjonalisering av en Stimulus-applikasjon, siden rammen ikke gir noen I18n-verktøy ut av boksen. Internationalisering er et viktig skritt, spesielt når appen din brukes av folk fra hele verden, så en grunnleggende forståelse av hvordan du gjør det, kan egentlig komme til nytte.

Selvfølgelig er det opp til deg å bestemme hvilken internasjonaliseringsløsning som skal implementeres, det være seg jQuery.I18n, Polyglot eller noen andre. I denne opplæringen vil jeg gjerne vise deg et populært I18n-rammeverk, kalt I18next, som har mange kule funksjoner, og gir mange ekstra tredjeparts plugins for å forenkle utviklingsprosessen enda lenger. Selv med alle disse funksjonene, er I18next ikke et komplekst verktøy, og du trenger ikke å studere mye dokumentasjon for å komme i gang.

I denne artikkelen lærer du hvordan du aktiverer I18n-støtte i Stimulus-applikasjoner ved hjelp av I18next-biblioteket. Spesielt snakker vi om:

  • I18neste konfigurasjon
  • oversettelsesfiler og laster dem asynkront
  • utfører oversettelser og oversetter hele siden på en gang
  • jobber med flertall og kjønnsinformasjon
  • bytter mellom lokaliteter og vedvarer den valgte lokalen i GET-parameteren
  • Innstilling av lokalitet basert på brukerens preferanser

Kildekoden er tilgjengelig i opplæringen GitHub repo.

Bootstrapping en Stimulus App

For å komme i gang, la oss klone Stimulus Starter-prosjektet og installere alle avhengighetene ved hjelp av Garn pakkebehandling:

git klon https://github.com/stimulusjs/stimulus-starter.git cd stimulus-starter garn installere

Vi skal bygge et enkelt webprogram som laster opp informasjon om registrerte brukere. For hver bruker viser vi hans / hennes innlogging og antall bilder han eller hun har lastet opp så langt (det spiller ingen rolle hva disse bildene er). 

Vi skal også presentere en språkbryter øverst på siden. Når et språk er valgt, bør grensesnittet oversettes med en gang uten sidelastning. Dessuten bør nettadressen vedlegges med en ?locale GET-parameteren angir hvilken lokal som nå benyttes. Selvfølgelig, hvis siden er lastet med denne parameteren som allerede er oppgitt, bør riktig språk settes automatisk.

Ok, la oss fortsette å gjengi brukerne våre. Legg til følgende linje med kode til offentlig / index.html fil:

Her bruker vi brukere kontrolleren og gi en nettadresse som du kan laste brukerne fra. I en virkelig applikasjon, ville vi trolig ha et server-side script som henter brukere fra databasen og svarer med JSON. For denne opplæringen, la oss ganske enkelt plassere alle nødvendige data i offentlig / api / brukere / index.json fil:

["login": "johndoe", "photos_count": "male", "login": "annsmith", "photos_count": "20", "gender" "] 

Opprett nå en ny src / kontrollere / users_controller.js fil:

importer Controller fra "stimulus" eksport standard klasse utvider Controller connect () this.loadUsers ()

Så snart kontrolleren er koblet til DOM, laster vi asynkront våre brukere ved hjelp av loadUsers () metode:

 loadUsers () hent (this.data.get ("url")) .then (response => response.text ()) .then (json => this.renderUsers (json))

Denne metoden sender en henting forespørsel til den oppgitte nettadressen, tar tak i svaret, og til slutt gir brukerne:

 renderUsers (users) let content = "JSON.parse (users) .forEach ((user) => content + = '
Logg inn: $ user.login
Har lastet opp $ user.photos_count bilde (r)

') this.element.innerHTML = innhold

renderUsers (), Parser på nytt JSON, konstruerer en ny streng med alt innholdet, og viser sist dette innholdet på siden (this.element kommer til å returnere den faktiske DOM-noden som kontrolleren er koblet til, som er div i vårt tilfelle).

I18next

Nå skal vi fortsette å integrere I18next i vår app. Legg til to biblioteker til vårt prosjekt: I18next seg selv og et plugin for å aktivere asynkron lasting av oversettelsesfiler fra baksiden:

garn legg til i18next i18next-xhr-backend

Vi skal lagre alle I18next-relaterte ting i en egen src / i18n / config.js fil, så opprett den nå:

importer i18next fra 'i18next' import I18nXHR fra 'i18next-xhr-backend' const i18n = i18next.use (I18nXHR) .init (fallbackLng: 'en', hviteliste: ['en', 'ru'], forhåndsbelastning: 'en', 'ru'], ns: 'users', defaultNS: 'users', fallbackNS: false, debug: true, backend: loadPath: '/ i18n / lng / ns. json ',, funksjon (feil, t) hvis (feil) returnerer console.error (err)); eksporter i18n som i18n

La oss gå fra topp til bunn for å forstå hva som skjer her:

  • bruke (I18nXHR) gjør det mulig for i18next-xhr-backend-plugin.
  • fallbackLng forteller det å bruke engelsk som tilbakebetalingsspråk.
  • hviteliste tillater kun engelsk og russisk språk å bli satt. Selvfølgelig kan du velge andre språk.
  • forspenning instruerer oversettelsesfiler som skal forhåndslastes fra serveren, i stedet for å laste dem når det tilsvarende språket er valgt.
  • ns betyr "namespace" og aksepterer enten en streng eller en matrise. I dette eksemplet har vi bare ett navneområde, men for større applikasjoner kan du introdusere andre navneområder, som adminkurven, profil, etc. For hvert navneområde skal en egen oversettelsesfil opprettes.
  • defaultNS settene brukere for å være standard navneplass.
  • fallbackNS deaktiverer namespace-fallback.
  • debug Tillater feilsøkingsinformasjon å vises i nettleserens konsoll. Spesielt står det hvilke oversettelsesfiler som er lastet, hvilket språk er valgt, etc. Du vil sannsynligvis vil deaktivere denne innstillingen før du distribuerer programmet til produksjon.
  • baksiden gir konfigurasjon for I18nXHR-pluginet og angir hvor du skal laste oversettelser fra. Legg merke til at banen skal inneholde tittelens tittel, mens filen skal navngis etter navneområdet og ha .json forlengelse
  • funksjon (feil, t) er tilbakeringingen til å kjøre når I18next er klar (eller når en feil ble oppdratt).

Deretter la vi lage oversettelsesfiler. Oversetter for russisk språk bør plasseres i offentlig / i18n / ru / users.json fil:

"login": "Логин"

Logg Inn Her er oversettelsesnøkkelen, mens Логин er verdien som skal vises.

Engelske oversettelser, i sin tur, bør gå til offentlig / i18n / no / users.json fil:

"logg inn": "Logg inn"

For å være sikker på at I18next fungerer, kan du legge til følgende linje kode til tilbakeringingen inne i i18n / config.js fil:

// config går her ... funksjon (feil, t) hvis (err) returner console.error (err) console.log (i18n.t ('logg inn'))

Her bruker vi en metode som kalles t det betyr "oversette". Denne metoden aksepterer en oversettelsesnøkkel og returnerer den tilsvarende verdien.

Vi kan imidlertid ha mange deler av brukergrensesnittet som må oversettes, og gjør det ved å benytte t Metoden ville være ganske kjedelig. I stedet foreslår jeg at du bruker et annet plugin som heter loc-i18next, som lar deg oversette flere elementer samtidig.

Oversetter i én gang

Installer loc-i18next-plugin:

garn legg til loc-i18next

Importer den øverst på src / i18n / config.js fil:

importere locI18next fra 'loc-i18next'

Gi nå konfigurasjonen for selve plugin:

// andre config const loci18n = locI18next.init (i18n, selectorAttr: 'data-i18n', alternativerAttr: 'data-i18n-alternativer', useOptionsAttr: true); eksporter loci18n som loci18n, i18n som i18n

Det er et par ting å merke seg her:

  • locI18next.init (i18n) lager en ny forekomst av pluginet basert på den tidligere definerte forekomsten av I18next.
  • selectorAttr angir hvilket attributt som skal brukes til å oppdage elementer som krever lokalisering. I utgangspunktet vil loc-i18next søke etter slike elementer og bruke verdien av data-i18n Tilordne som oversettelsessnøkkel.
  • optionsAttr Angir hvilket attributt som inneholder flere oversettelsesalternativer.
  • useOptionsAttr instruerer pluginet for å bruke tilleggsalternativene.

Brukerne våre lastes asynkront, så vi må vente til denne operasjonen er ferdig og bare utføre lokalisering etter det. For nå, la oss bare stille en tidtaker som skal vente i to sekunder før du ringer til lokalisere () metode-det er en midlertidig hack, selvfølgelig.

 importer loci18n fra '... / i18n / config' // annen kode ... loadUsers () hent (this.data.get ("url")) .then (response => response.text ()) .then (json => this.renderUsers (json) setTimeout (() => // <--- this.localize() , '2000') ) 

Kode på lokalisere () metode selv:

 lokaliser () loci18n ('. users')

Som du ser, trenger vi bare å sende en velger til loc-i18next-plugin. Alle elementer inne (som har data-i18n attributtsett) vil bli lokalisert automatisk.

Nå juster du renderUsers metode. For nå, la oss bare oversette "Logg inn" -ordet:

 renderUsers (users) let content = "JSON.parse (users) .forEach ((user) => content + = '
ID: $ user.id
: $ user.login
Har lastet opp $ user.photos_count bilde (r)

') this.element.innerHTML = innhold

Hyggelig! Oppdater siden, vent i to sekunder, og kontroller at "Innloggingsord" vises for hver bruker.

Pluraler og kjønn

Vi har lokalisert en del av grensesnittet, som er veldig kult. Likevel har hver bruker to flere felt: antall opplastede bilder og kjønn. Siden vi ikke kan forutsi hvor mange bilder hver bruker skal ha, skal "foto" -ordet pluraliseres riktig på grunnlag av det gitte antall. For å gjøre dette, krever vi en data-i18n-alternativer attributt konfigurert tidligere. Å gi tellingen, data-i18n-alternativer bør tilordnes med følgende objekt: "count": YOUR_COUNT.

Kjønnsinformasjon bør også tas i betraktning. Ordet "lastet opp" på engelsk kan brukes på både mann og kvinne, men på russisk blir det enten "загрузил" eller "загрузила", så vi trenger data-i18n-alternativer igjen, som har "kontekst": "GENDER" som en verdi. Vær oppmerksom på at du kan bruke denne konteksten til å oppnå andre oppgaver, ikke bare for å gi kjønnsinformasjon.

 renderUsers (users) let content = "JSON.parse (users) .forEach ((user) => content + = '
: $ user.login

') this.element.innerHTML = innhold

Oppdater nå de engelske oversettelsene:

"login": "Login", "uploaded": "Har lastet opp", "bilder": "ett bilde", "photos_plural": "count photos"

Ingenting komplisert her. Siden for engelsk bryr vi oss ikke om kjønnsinformasjonen (som er konteksten), oversettelsessnøkkelen bør være ganske enkelt lastet opp. For å gi riktig pluraliserte oversettelser bruker vi bilder og photos_plural nøkler. De telle del er interpolering og vil bli erstattet med det faktiske nummeret.

For det russiske språket er tingene mer komplekse:

"login": "Логин", "uploaded_male": "Загрузил уже", "uploaded_female": "Загрузила уже", "photos_0": "одну фотографию", "photos_1": "count фотографии" photos_2 ":" count фотографий " 

Først av alt, merk at vi har begge uploaded_male og uploaded_female nøkler for to mulige sammenhenger. Deretter er pluraliseringsregler også mer komplekse på russisk enn på engelsk, så vi må ikke gi to, men tre mulige fraser. I18next støtter mange språk ut av boksen, og dette lille verktøyet kan hjelpe deg å forstå hvilke pluraliseringstaster som skal spesifiseres for et gitt språk.

Bytte lokal

Vi er ferdige med å oversette applikasjonen vår, men brukerne skal kunne bytte mellom lokalene. Derfor legger du til en ny «språkbryter» -komponent i offentlig / index.html fil:

    Gjør den tilsvarende kontrolleren inne i src / kontrollere / languages_controller.js fil:

    Import Controller fra "stimulus" import i18n, loci18n fra '... / i18n / config' eksport standard klasse utvider Controller initialize () let languages ​​= [title: 'engelsk', kode: 'en', title: 'Русский', kode: 'ru'] this.element.innerHTML = languages.map ((lang) => return '
  • $ lang.title
  • ' ).bli med(")

    Her bruker vi initialize () tilbakeringing for å vise en liste over støttede språk. Hver li har en data-handling attributt som spesifiserer hvilken metode (switchLanguage, i dette tilfellet) skal utløses når elementet klikkes på.

    Legg nå til switchLanguage () metode:

     switchLanguage (e) this.currentLang = e.target.getAttribute ("data-lang")

    Det tar rett og slett målet for arrangementet og tar tak i verdien av data-lang Egenskap.

    Jeg vil også legge til en getter og setter for currentLang Egenskap:

     få currentLang () return this.data.get ("currentLang") sett currentLang (lang) hvis (i18n.language! == lang) i18n.changeLanguage (lang) hvis (this.currentLang! == lang ) this.data.set ("currentLang", lang) loci18n ('body') this.highlightCurrentLang ()

    Getteren er veldig enkel - vi henter verdien av det brukte språket og returnerer det.

    Setteren er mer kompleks. Først av alt bruker vi skifte språk metode hvis det gjeldende språket ikke er lik det valgte. Vi lagrer også den nylig valgte lokalen under data-strøm-lang attributt (som nås i getteren), lokalisere kroppen til HTML-siden ved hjelp av loc-i18next-pluginet, og til slutt markere den gjeldende locale.

    La oss kode på highlightCurrentLang ():

     highlightCurrentLang () this.switcherTargets.forEach ((el, i) => el.classList.toggle ("current", this.currentLang === el.getAttribute ("data-lang")))

    Her er det iterating over en rekke lokale brytere og sammenligne verdiene av deres data-lang attributter til verdien av den for tiden brukte locale. Hvis verdiene passer, er bryteren tilordnet en nåværende CSS klasse, ellers er denne klassen fjernet.

    For å gjøre this.switcherTargets konstruere arbeid, må vi definere Stimulus mål på følgende måte:

    statiske mål = ["switcher"]

    Legg også til data-target attributter med verdier av switcher for lis:

     initialiser () // ... this.element.innerHTML = languages.map ((lang) => return '
  • $ lang.title
  • ' ).bli med(") //…

    En annen viktig ting å vurdere er at oversettelsesfiler kan ta litt tid å laste, og vi må vente på at denne operasjonen skal fullføres før du lar lokalet bli slått på. Derfor, la oss dra nytte av lastet Ring tilbake:

     initialiser () i18n.on ('lastet', (lastet) => // <--- let languages = [ title: 'English', code: 'en', title: 'Русский', code: 'ru' ] this.element.innerHTML = languages.map((lang) =>  komme tilbake '
  • $ lang.title
  • '). bli med (") this.currentLang = i18n.language)

    Til slutt, ikke glem å fjerne setTimeout fra loadUsers () metode:

     loadUsers () fetch (this.data.get ("url")) .then (response => response.text ()) .then (json => this.renderUsers (json) this.localize ())

    Vedvarende språk i nettadressen

    Etter at lokalen er slått, vil jeg gjerne legge til en ?lang GET parameter til URL-adressen som inneholder koden til det valgte språket. Legge til en GET param uten å laste siden kan enkelt gjøres ved hjelp av History API:

     sett currentLang (lang) if (i18n.language! == lang) i18n.changeLanguage (lang) window.history.pushState (null, null, '? lang = $ lang') // <---  if(this.currentLang !== lang)  this.data.set("currentLang", lang) loci18n('body') this.highlightCurrentLang()  

    Oppdage lokalitet

    Den siste tingen vi skal implementere i dag, er muligheten til å angi lokaliteten basert på brukerens preferanser. Et plugin som heter LanguageDetector kan hjelpe oss med å løse denne oppgaven. Legg til en ny Garn pakke:

    garn legg til i18next-leser-languagedetector

    Importere LanguageDetector inne i i18n / config.js fil:

    importer LngDetector fra 'i18next-browser-languagedetector'

    Nå juster konfigurasjonen:

    const i18n = i18next.use (I18nXHR) .use (LngDetector) .init (// <--- // other options go here… detection:  order: ['querystring', 'navigator', 'htmlTag'], lookupQuerystring: 'lang',  , function(err, t)  if (err) return console.error(err) );

    De rekkefølge alternativ lister alle teknikker (sortert etter deres betydning) at pluginet bør prøve for å "gjette" den foretrukne lokaliteten:

    • søkestreng betyr å sjekke en GET param som inneholder lokalens kode.
    • lookupQuerystring Setter navnet på GET-parameteren for å bruke, som er lang i vårt tilfelle.
    • navigator betyr å få lokaldata fra brukerens forespørsel.
    • htmlTag innebærer å hente den foretrukne lokaliteten fra lang attributten til html stikkord.

    Konklusjon

    I denne artikkelen har vi tatt en titt på I18next-en populær løsning for å oversette JavaScript-programmer med letthet. Du har lært hvordan du integrerer I18next med Stimulus-rammen, konfigurer den og laster oversettelsesfiler på asynkron måte. Du har også sett hvordan du bytter mellom lokaliteter og angir standardspråket basert på brukerens preferanser.

    I18next har noen ekstra konfigurasjonsalternativer og mange plugins, så sørg for å bla gjennom den offisielle dokumentasjonen for å lære mer. Vær også oppmerksom på at Stimulus ikke tvinger deg til å bruke en bestemt lokaliseringsløsning, så du kan også prøve å bruke noe som jQuery.I18n eller Polyglot. 

    Det var alt for i dag! Takk for at du leser sammen, og til neste gang.