Traduzione delle app di stimolo con I18Pross

Nel mio precedente articolo ho trattato Stimulus, un modesto framework JavaScript creato da Basecamp. Oggi parlerò dell'internazionalizzazione di un'applicazione Stimulus, dal momento che il framework non fornisce alcun strumento I18n immediato. L'internazionalizzazione è un passo importante, soprattutto quando la tua app viene utilizzata da persone di tutto il mondo, quindi una comprensione di base su come farlo potrebbe essere davvero utile.

Naturalmente, spetta a te decidere quale soluzione di internazionalizzazione implementare, sia jQuery.I18n, Polyglot, o qualche altro. In questo tutorial vorrei mostrarti un famoso framework I18n chiamato I18next che ha molte funzioni interessanti e fornisce molti plugin aggiuntivi di terze parti per semplificare ulteriormente il processo di sviluppo. Anche con tutte queste funzionalità, I18next non è uno strumento complesso e non è necessario studiare molta documentazione per iniziare.

In questo articolo, imparerai come abilitare il supporto I18n nelle applicazioni Stimulus con l'aiuto della libreria I18next. Nello specifico, parleremo di:

  • I18prossima configurazione
  • file di traduzione e caricandoli in modo asincrono
  • eseguire traduzioni e tradurre l'intera pagina in un colpo solo
  • lavorare con plurali e informazioni di genere
  • passare da una locale all'altra e persistere nella locale scelta nel parametro GET
  • impostazione locale in base alle preferenze dell'utente

Il codice sorgente è disponibile nel tutorial repo GitHub.

Avvio automatico di un'app di stimolo

Per iniziare, cloniamo il progetto Stimulus Starter e installiamo tutte le dipendenze usando il gestore di pacchetti Yarn:

git clone https://github.com/stimulusjs/stimulus-starter.git cd stimulus-starter filato installa

Creeremo una semplice applicazione web che carica informazioni sugli utenti registrati. Per ciascun utente, mostreremo il suo login e il numero di foto che ha caricato fino a quel momento (non importa cosa siano queste foto). 

Inoltre, presenteremo un selettore di lingua nella parte superiore della pagina. Quando viene scelta una lingua, l'interfaccia deve essere tradotta immediatamente senza ricaricare la pagina. Inoltre, l'URL dovrebbe essere aggiunto con a ?località GET parametro che specifica quale locale è attualmente utilizzata. Naturalmente, se la pagina viene caricata con questo parametro già fornito, la lingua corretta deve essere impostata automaticamente.

Ok, procediamo a rendere i nostri utenti. Aggiungi la seguente riga di codice al pubblico / index.html file:

Qui, stiamo usando il utenti controller e fornendo un URL da cui caricare i nostri utenti. In un'applicazione reale, probabilmente avremmo uno script sul lato server che recupera gli utenti dal database e risponde con JSON. Per questo tutorial, tuttavia, inseriamo semplicemente tutti i dati necessari nel / Api / utenti / index.json pubblico file:

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

Ora crea un nuovo src / controller / users_controller.js file:

import Controller dalla classe predefinita di esportazione "stimulus" estende Controller connect () this.loadUsers ()

Non appena il controller è collegato al DOM, stiamo caricando in modo asincrono i nostri utenti con l'aiuto di loadUsers () metodo:

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

Questo metodo invia una richiesta di recupero all'URL specificato, acquisisce la risposta e infine esegue il rendering degli utenti:

 renderUsers (users) let content = "JSON.parse (users) .forEach ((user) => content + = '
Accesso: $ user.login
Ha caricato $ user.photos_count foto

') this.element.innerHTML = contenuto

renderUsers (), a sua volta, analizza JSON, crea una nuova stringa con tutto il contenuto e infine visualizza questo contenuto nella pagina (this.element restituirà l'attuale nodo DOM a cui è connesso il controller, che è div nel nostro caso).

I18next

Ora procederemo all'integrazione di I18next nella nostra app. Aggiungi due librerie al nostro progetto: I18next stesso e un plugin per abilitare il caricamento asincrono dei file di traduzione dal back-end:

filato aggiungere i18next i18next-xhr-backend

Stiamo andando a memorizzare tutte le cose relative a I18next in un separato src / i18n / config.js file, quindi crealo ora:

importare i18next da 'i18next' import I18nXHR da 'i18next-xhr-backend' const i18n = i18next.use (I18nXHR) .init (fallbackLng: 'en', whitelist: ['en', 'ru'], precarico: [ 'en', 'ru'], ns: 'users', defaultNS: 'users', fallbackNS: false, debug: true, backend: loadPath: '/ i18n / lng / ns. json ',, function (err, t) if (err) return console.error (err)); esporta i18n come i18n

Andiamo dall'alto verso il basso per capire cosa sta succedendo qui:

  • utilizzare (I18nXHR) abilita il plugin i18next-xhr-backend.
  • fallbackLng dice di usare l'inglese come lingua di riserva.
  • lista bianca consente di impostare solo le lingue inglese e russa. Certo, puoi scegliere qualsiasi altra lingua.
  • precarico ordina che i file di traduzione vengano precaricati dal server, anziché caricarli quando viene selezionata la lingua corrispondente.
  • ns significa "spazio dei nomi" e accetta una stringa o un array. In questo esempio abbiamo un solo spazio dei nomi, ma per le applicazioni più grandi è possibile introdurre altri spazi dei nomi, come Admincarrello, profilo, ecc. Per ogni spazio dei nomi, dovrebbe essere creato un file di traduzione separato.
  • defaultNS imposta utenti essere lo spazio dei nomi predefinito.
  • fallbackNS disabilita il fallback del namespace.
  • mettere a punto consente di visualizzare le informazioni di debug nella console del browser. In particolare, indica quali file di traduzione sono caricati, quale lingua è selezionata, ecc. Probabilmente vorrai disabilitare questa impostazione prima di distribuire l'applicazione in produzione.
  • backend fornisce la configurazione per il plugin I18nXHR e specifica dove caricare le traduzioni. Si noti che il percorso deve contenere il titolo delle impostazioni internazionali, mentre il nome del file deve essere il seguente al namespace e avere il .jSON estensione
  • funzione (err, t) è il callback da eseguire quando I18next è pronto (o quando è stato sollevato un errore).

Quindi, costruiamo i file di traduzione. Le traduzioni per la lingua russa dovrebbero essere inserite nel / I18n / RU / users.json pubblico file:

"login": "Логин"

accesso ecco la chiave di traduzione, mentre Логин è il valore da visualizzare.

Le traduzioni in inglese, a loro volta, dovrebbero andare al pubblico / i18n / it / users.json file:

 "Entra Entra" 

Per assicurarsi che I18next funzioni, è possibile aggiungere la seguente riga di codice al callback all'interno di i18n / config.js file:

// config goes here ... function (err, t) if (err) return console.error (err) console.log (i18n.t ('login'))

Qui, stiamo usando un metodo chiamato t ciò significa "tradurre". Questo metodo accetta una chiave di traduzione e restituisce il valore corrispondente.

Tuttavia, potremmo avere molte parti dell'interfaccia utente che devono essere tradotte, e facendo ciò utilizzando il t il metodo sarebbe abbastanza noioso Invece, ti suggerisco di usare un altro plugin chiamato loc-i18next che ti permette di tradurre più elementi contemporaneamente.

Traduzione in One Go

Installa il plugin loc-i18next:

filato aggiungere loc-i18next

Importalo nella parte superiore del src / i18n / config.js file:

importare locI18next da 'loc-i18next'

Ora fornisci la configurazione per il plugin stesso:

// other config const loci18n = locI18next.init (i18n, selectorAttr: 'data-i18n', optionsAttr: 'data-i18n-options', useOptionsAttr: true); export loci18n as loci18n, i18n as i18n

Ci sono un paio di cose da notare qui:

  • locI18next.init (i18n) crea una nuova istanza del plugin in base all'istanza precedentemente definita di I18next.
  • selectorAttr specifica quale attributo utilizzare per rilevare elementi che richiedono la localizzazione. Fondamentalmente, loc-i18next sta cercando questi elementi e usa il valore di Dati-i18n attributo come chiave di traduzione.
  • optionsAttr specifica quale attributo contiene opzioni di traduzione aggiuntive.
  • useOptionsAttr indica al plug-in di utilizzare le opzioni aggiuntive.

I nostri utenti vengono caricati in modo asincrono, quindi dobbiamo aspettare fino a quando questa operazione non viene completata e solo dopo eseguire la localizzazione. Per ora, impostiamo semplicemente un timer che dovrebbe aspettare per due secondi prima di chiamare il localizzare() metodo: è un trucco temporaneo, ovviamente.

 import loci18n da '... / i18n / config' // altro codice ... loadUsers () fetch (this.data.get ("url")) .then (response => response.text ()) .then (json => this.renderUsers (json) setTimeout (() => // <--- this.localize() , '2000') ) 

Codice il localizzare() metodo stesso:

 localize () loci18n ('. users')

Come vedi, abbiamo solo bisogno di passare un selettore al plug-in i18next di loc. Tutti gli elementi all'interno (che hanno il Dati-i18n set di attributi) verrà localizzato automaticamente.

Ora modificare il renderUsers metodo. Per ora, traduciamo solo la parola "Login":

 renderUsers (users) let content = "JSON.parse (users) .forEach ((user) => content + = '
ID: $ user.id
: $ user.login
Ha caricato $ user.photos_count foto

') this.element.innerHTML = contenuto

Bello! Ricarica la pagina, attendi due secondi e assicurati che la parola "Login" appaia per ogni utente.

Plurali e Genere

Abbiamo localizzato parte dell'interfaccia, che è davvero interessante. Tuttavia, ogni utente ha altri due campi: il numero di foto e il sesso caricati. Dal momento che non possiamo prevedere quante foto ogni utente avrà, la parola "foto" dovrebbe essere pluralizzata correttamente in base al conteggio dato. Per fare questo, abbiamo bisogno di un -i18n-opzioni dati attributo configurato in precedenza. Per fornire il conteggio, -i18n-opzioni dati dovrebbe essere assegnato con il seguente oggetto: "count": YOUR_COUNT.

Anche le informazioni di genere dovrebbero essere prese in considerazione. La parola "caricato" in inglese può essere applicata sia a uomini che a donne, ma in russo diventa "загрузил" o "загрузила", quindi abbiamo bisogno -i18n-opzioni dati di nuovo, che ha "context": "GENDER" come valore Nota, a proposito, che puoi utilizzare questo contesto per raggiungere altri compiti, non solo per fornire informazioni di genere.

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

') this.element.innerHTML = contenuto

Ora aggiorna le traduzioni in inglese:

"login": "Login", "caricato": "Ha caricato", "foto": "una foto", "foto_plural": "count foto"

Niente di complesso qui. Dal momento che per l'inglese non ci interessa l'informazione di genere (che è il contesto), la chiave di traduzione dovrebbe essere semplicemente caricato. Per fornire traduzioni correttamente pluralizzate, stiamo usando il fotografie e photos_plural chiavi. Il contare parte è l'interpolazione e sarà sostituita con il numero effettivo.

Per quanto riguarda la lingua russa, le cose sono più complesse:

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

Prima di tutto, nota che abbiamo entrambi uploaded_male e uploaded_female chiavi per due possibili contesti. Inoltre, le regole di pluralizzazione sono anche più complesse in russo che in inglese, quindi non dobbiamo fornire due, ma tre possibili frasi. I18next supporta molte lingue fuori dalla scatola e questo piccolo strumento può aiutarti a capire quali tasti di pluralizzazione dovrebbero essere specificati per una determinata lingua.

Cambio delle impostazioni internazionali

Abbiamo finito con la traduzione della nostra applicazione, ma gli utenti dovrebbero essere in grado di passare da un locale all'altro. Pertanto, aggiungere un nuovo componente "selettore di lingua" al pubblico / index.html file:

    Crea il controller corrispondente all'interno del src / controller / languages_controller.js file:

    import Controller da "stimulus" import i18n, loci18n da '... / i18n / config' la classe di default di esportazione estende Controller initialize () let languages ​​= [title: 'English', code: 'en', title: 'Русский', codice: 'ru'] this.element.innerHTML = languages.map ((lang) => return '
  • $ lang.title
  • ' ).aderire(")

    Qui stiamo usando il inizializzare() callback per visualizzare un elenco di lingue supportate. Ogni Li ha un Dati-action attributo che specifica quale metodo (switchLanguage, in questo caso) deve essere attivato quando si fa clic sull'elemento.

    Ora aggiungi il switchLanguage () metodo:

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

    Prende semplicemente il bersaglio dell'evento e prende il valore del Dati-lang attributo.

    Vorrei anche aggiungere un getter e setter per il currentLang attributo:

     get currentLang () return this.data.get ("currentLang") imposta currentLang (lang) if (i18n.language! == lang) i18n.changeLanguage (lang) if (this.currentLang! == lang ) this.data.set ("currentLang", lang) loci18n ('body') this.highlightCurrentLang ()

    Il getter è molto semplice: recuperiamo il valore del linguaggio correntemente usato e lo restituiamo.

    Il setter è più complesso. Prima di tutto, usiamo il Cambia lingua metodo se la lingua attualmente impostata non è uguale a quella selezionata. Inoltre, stiamo memorizzando la nuova localizzazione selezionata sotto Dati-corrente-lang attributo (a cui si accede nel getter), localizzando il corpo della pagina HTML utilizzando il plug-in loc-i18next e infine evidenziando le impostazioni locali attualmente utilizzate.

    Cerchiamo di codificare il highlightCurrentLang ():

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

    Qui stiamo iterando su un array di switcher locali e confrontando i loro valori Dati-lang attribuisce al valore della locale attualmente utilizzata. Se i valori corrispondono, il commutatore viene assegnato con a attuale Classe CSS, altrimenti questa classe viene rimossa.

    Per rendere il this.switcherTargets costruire il lavoro, dobbiamo definire gli obiettivi dello stimolo nel seguente modo:

    target statici = ["switcher"]

    Inoltre, aggiungere data-obiettivo attributi con valori di interruttore per il LiS:

     initialize () // ... this.element.innerHTML = languages.map ((lang) => return '
  • $ lang.title
  • ' ).aderire(") //…

    Un'altra cosa importante da considerare è che i file di traduzione potrebbero richiedere del tempo per essere caricati, e dobbiamo attendere il completamento di questa operazione prima di consentire il cambio delle impostazioni internazionali. Pertanto, approfittiamo del caricato richiama:

     initialize () i18n.on ('loaded', (loaded) => // <--- let languages = [ title: 'English', code: 'en', title: 'Русский', code: 'ru' ] this.element.innerHTML = languages.map((lang) =>  ritorno '
  • $ lang.title
  • '). join (") this.currentLang = i18n.language)

    Infine, non dimenticare di rimuovere setTimeout dal loadUsers () metodo:

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

    Impostazioni internazionali persistenti nell'URL

    Dopo aver cambiato la lingua, vorrei aggiungere un ?Lang Ottieni parametro per l'URL contenente il codice della lingua scelta. L'aggiunta di un parametro GET senza ricaricare la pagina può essere facilmente eseguita con l'aiuto dell'API Cronologia:

     imposta 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()  

    Rilevazione delle impostazioni internazionali

    L'ultima cosa che implementeremo oggi è la possibilità di impostare le impostazioni internazionali in base alle preferenze dell'utente. Un plugin chiamato LanguageDetector può aiutarci a risolvere questo compito. Aggiungi un nuovo pacchetto di filati:

    filato aggiungere i18next-browser-languagedetector

    Importare LanguageDetector dentro il i18n / config.js file:

    importare LngDetector da 'i18next-browser-languagedetector'

    Ora modificare la configurazione:

    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) );

    Il ordine opzione elenca tutte le tecniche (ordinate in base alla loro importanza) che il plugin dovrebbe provare per "indovinare" le impostazioni locali preferite:

    • stringa della domanda significa controllare un parametro GET contenente il codice locale.
    • lookupQuerystring imposta il nome del parametro GET da usare, che è Lang nel nostro caso.
    • navigatore significa ottenere dati locali dalla richiesta dell'utente.
    • htmlTag implica il recupero delle impostazioni internazionali preferite dal Lang attributo del html etichetta.

    Conclusione

    In questo articolo abbiamo dato un'occhiata a I18next, una soluzione popolare per tradurre le applicazioni JavaScript con facilità. Hai imparato come integrare I18next con il framework Stimulus, configurarlo e caricare i file di traduzione in modo asincrono. Inoltre, hai visto come passare da un locale all'altro e impostare la lingua predefinita in base alle preferenze dell'utente.

    I18next ha alcune opzioni di configurazione aggiuntive e molti plugin, quindi assicurati di consultare la documentazione ufficiale per saperne di più. Nota inoltre che Stimulus non ti obbliga a utilizzare una soluzione di localizzazione specifica, quindi potresti provare a utilizzare qualcosa come jQuery.I18n o Polyglot. 

    È tutto per oggi! Grazie per la lettura, e fino alla prossima volta.