Traducir aplicaciones de estímulo con I18Siguiente

En mi artículo anterior cubrí Stimulus, un modesto marco de JavaScript creado por Basecamp. Hoy hablaré sobre la internacionalización de una aplicación de estímulo, ya que el marco no proporciona ninguna herramienta I18n lista para usar. La internacionalización es un paso importante, especialmente cuando su aplicación es utilizada por personas de todo el mundo, por lo que una comprensión básica de cómo hacerlo puede ser realmente útil..

Por supuesto, depende de usted decidir qué solución de internacionalización implementar, ya sea jQuery.I18n, Polyglot o alguna otra. En este tutorial me gustaría mostrarle un popular marco I18n llamado I18siguiente que tiene muchas características geniales y proporciona muchos complementos adicionales de terceros para simplificar aún más el proceso de desarrollo. Incluso con todas estas características, I18siguiente no es una herramienta compleja, y no necesitas estudiar mucha documentación para comenzar.

En este artículo, aprenderá cómo habilitar la compatibilidad con I18n en aplicaciones de estímulo con la ayuda de la biblioteca I18siguiente. En concreto, vamos a hablar de:

  • I18siguiente configuracion
  • Traducción de archivos y carga de forma asíncrona.
  • Realizando traducciones y traduciendo toda la página de una vez.
  • Trabajando con plurales e información de género.
  • cambiar entre las configuraciones regionales y mantener la configuración regional elegida en el parámetro GET
  • configuración regional basada en las preferencias del usuario

El código fuente está disponible en el tutorial de repositorio de GitHub..

Bootstrapping una aplicación de estímulo

Para comenzar, clonemos el proyecto Stimulus Starter e instalemos todas las dependencias usando el administrador de paquetes Yarn:

git clone https://github.com/stimulusjs/stimulus-starter.git cd hilo de inicio de estímulo instalar

Vamos a construir una aplicación web simple que carga información sobre los usuarios registrados. Para cada usuario, mostraremos su inicio de sesión y el número de fotos que ha subido hasta ahora (realmente no importa cuáles sean estas fotos). 

Además, vamos a presentar un selector de idioma en la parte superior de la página. Cuando se elige un idioma, la interfaz debe traducirse de inmediato sin recargar la página. Además, la URL debe adjuntarse con un ?lugar Parámetro GET que especifica qué configuración regional se está utilizando actualmente. Por supuesto, si la página está cargada con este parámetro ya provisto, el idioma apropiado debe configurarse automáticamente.

Bien, procedamos a renderizar a nuestros usuarios. Agregue la siguiente línea de código a la public / index.html expediente:

Aquí estamos usando el usuarios Controlador y proporcionando una URL desde la cual cargar a nuestros usuarios. En una aplicación del mundo real, es probable que tengamos un script del lado del servidor que extraiga a los usuarios de la base de datos y responda con JSON. Para este tutorial, sin embargo, simplemente coloquemos todos los datos necesarios en el public / api / users / index.json expediente:

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

Ahora crea un nuevo src / controllers / users_controller.js expediente:

importar Controlador de "estímulo" exportar clase predeterminada extiende Controlador conectar () this.loadUsers ()

Tan pronto como el controlador se conecta al DOM, estamos cargando asíncronamente a nuestros usuarios con la ayuda de loadUsers () método:

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

Este método envía una solicitud de recuperación a la URL dada, toma la respuesta y finalmente procesa a los usuarios:

 renderUsers (usuarios) let content = "JSON.parse (users) .forEach ((user) => content + = '
Login: $ user.login
Ha subido $ user.photos_count foto (s)

') this.element.innerHTML = content

renderUsers (), a su vez, analiza JSON, construye una nueva cadena con todo el contenido y, por último, muestra este contenido en la página (este.elemento devolverá el nodo DOM real al que está conectado el controlador, que es div en nuestro caso).

I18siguiente

Ahora vamos a proceder a integrar I18siguiente en nuestra aplicación. Agregue dos bibliotecas a nuestro proyecto: I18siguiente y un complemento para permitir la carga asíncrona de archivos de traducción desde el back-end:

hilo agregar i18siguiente i18siguiente-xhr-backend

Vamos a almacenar todas las cosas relacionadas con I18siguiente en una src / i18n / config.js archivo, así que créalo ahora:

importar i18siguiente desde 'i18siguiente' importar I18nXHR desde 'i18siguiente-xhr-backend' const i18n = i18siguiente.use (I18nXHR) .init (fallbackLng: 'en', lista blanca: ['en', 'ru'], preload: [ '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)); exportar i18n como i18n

Vayamos de arriba a abajo para entender lo que está pasando aquí:

  • utilizar (I18nXHR) habilita el complemento i18siguiente-xhr-backend.
  • fallbackLng le dice que use el inglés como idioma alternativo.
  • lista blanca permite que solo se establezcan los idiomas inglés y ruso. Por supuesto, puedes elegir cualquier otro idioma..
  • precarga ordena que los archivos de traducción se carguen previamente desde el servidor, en lugar de cargarlos cuando se selecciona el idioma correspondiente.
  • ns significa "espacio de nombres" y acepta una cadena o una matriz. En este ejemplo, solo tenemos un espacio de nombres, pero para aplicaciones más grandes puede introducir otros espacios de nombres, como administracióncarro, perfil, Para cada espacio de nombres, se debe crear un archivo de traducción separado..
  • defaultNS conjuntos usuarios para ser el espacio de nombres predeterminado.
  • fallbackNS desactiva el espacio de nombres.
  • depurar permite que la información de depuración se muestre en la consola del navegador. Específicamente, indica qué archivos de traducción se cargan, qué idioma se selecciona, etc. Es probable que desees deshabilitar esta configuración antes de implementar la aplicación en producción..
  • backend proporciona configuración para el complemento I18nXHR y especifica de dónde cargar las traducciones. Tenga en cuenta que la ruta debe contener el título de la configuración regional, mientras que el archivo debe tener el nombre del espacio de nombres y debe tener el nombre .json extensión
  • función (err, t) es la devolución de llamada que debe ejecutarse cuando I18siguiente está listo (o cuando se generó un error).

A continuación, vamos a crear archivos de traducción. Las traducciones de la lengua rusa se deben colocar en el public / i18n / ru / users.json expediente:

"login": "Логин"

iniciar sesión Aquí está la clave de traducción, mientras que Логин es el valor a mostrar.

Las traducciones al inglés, a su vez, deben ir al public / i18n / es / users.json expediente:

"login": "Login"

Para asegurarse de que I18siguiente funcione, puede agregar la siguiente línea de código a la devolución de llamada dentro de la i18n / config.js expediente:

// config va aquí ... función (err, t) si (err) devolver console.error (err) console.log (i18n.t ('login'))

Aquí, estamos usando un método llamado t que significa "traducir". Este método acepta una clave de traducción y devuelve el valor correspondiente..

Sin embargo, es posible que tengamos muchas partes de la interfaz de usuario que deban traducirse, y al hacerlo utilizando el t El método sería bastante tedioso. En su lugar, le sugiero que use otro complemento llamado loc-i18siguiente que le permite traducir varios elementos a la vez..

Traduciendo en One Go

Instala el complemento loc-i18siguiente:

hilo agregar loc-i18siguiente

Importarlo en la parte superior de la src / i18n / config.js expediente:

importar locI18siguiente desde 'loc-i18siguiente'

Ahora proporcione la configuración para el complemento en sí:

// otra configuración const consti18n = locI18next.init (i18n, selectorAttr: 'data-i18n', optionsAttr: 'data-i18n-options', useOptionsAttr: true); exportar loci18n como loci18n, i18n como i18n

Hay un par de cosas a tener en cuenta aquí:

  • locI18siguiente.init (i18n) crea una nueva instancia del complemento basado en la instancia previamente definida de I18siguiente.
  • selectorAttr especifica qué atributo usar para detectar elementos que requieren localización. Básicamente, loc-i18next buscará dichos elementos y usará el valor de data-i18n atributo como la clave de traducción.
  • opcionesAttr Especifica qué atributo contiene opciones de traducción adicionales..
  • useOptionsAttr le indica al plugin que use las opciones adicionales.

Nuestros usuarios se están cargando de forma asíncrona, por lo que tenemos que esperar hasta que se realice esta operación y solo realizar la localización después de eso. Por ahora, simplemente configuremos un temporizador que debe esperar dos segundos antes de llamar al localizar() método-eso es un hack temporal, por supuesto.

 importe loci18n desde '… / i18n / config' // otro código ... loadUsers () fetch (this.data.get ("url")) .then (response => response.text ()) .then (json => this.renderUsers (json) setTimeout (() => // <--- this.localize() , '2000') ) 

Codificar el localizar() método en sí:

 localize () loci18n ('. users')

Como puede ver, solo necesitamos pasar un selector al complemento loc-i18next. Todos los elementos en el interior (que tienen la data-i18n conjunto de atributos) se localizará automáticamente.

Ahora pellizque el usuarios de render método. Por ahora, solo traduzcamos la palabra "Iniciar sesión":

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

') this.element.innerHTML = content

¡Bonito! Vuelva a cargar la página, espere dos segundos y asegúrese de que aparezca la palabra "Iniciar sesión" para cada usuario.

Plurales y Género

Hemos localizado parte de la interfaz, lo cual es realmente genial. Aún así, cada usuario tiene dos campos más: el número de fotos cargadas y el género. Dado que no podemos predecir cuántas fotos tendrá cada usuario, la palabra "foto" debe estar pluralizada correctamente en función del conteo dado. Para hacer esto, necesitaremos un opciones de datos i18n atributo configurado previamente. Para proporcionar el recuento, opciones de datos i18n Debe asignarse con el siguiente objeto: "cuenta": YOUR_COUNT.

La información de género también debe ser tomada en consideración. La palabra "subido" en inglés se puede aplicar tanto a hombres como a mujeres, pero en ruso se convierte en "загрузил" o "загрузила", por lo que necesitamos opciones de datos i18n de nuevo, que tiene "contexto": "GÉNERO" como un valor. Tenga en cuenta, por cierto, que puede emplear este contexto para lograr otras tareas, no solo para proporcionar información de género.

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

') this.element.innerHTML = content

Ahora actualiza las traducciones al inglés:

"login": "Login", "uploaded": "Ha subido", "photos": "one photo", "photos_plural": "count photos"

Nada complejo aquí. Ya que para el inglés no nos importa la información de género (que es el contexto), la clave de traducción debe ser simple subido. Para proporcionar traducciones correctamente pluralizadas, estamos utilizando el fotos y fotos_plural llaves. los contar La parte es interpolación y se reemplazará con el número real..

En cuanto al idioma ruso, las cosas son más complejas:

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

En primer lugar, tenga en cuenta que tenemos ambos uploaded_male y uploaded_female Claves para dos contextos posibles. A continuación, las reglas de pluralización también son más complejas en ruso que en inglés, por lo que debemos proporcionar no dos, sino tres frases posibles. I18siguiente admite muchos idiomas de forma inmediata, y esta pequeña herramienta puede ayudarlo a comprender qué claves de pluralización se deben especificar para un idioma determinado..

Configuración regional

Hemos terminado con la traducción de nuestra aplicación, pero los usuarios deberían poder cambiar entre las configuraciones regionales. Por lo tanto, agregue un nuevo componente "conmutador de idioma" al public / index.html expediente:

    Craft el controlador correspondiente dentro de la src / controllers / languages_controller.js expediente:

    importar controlador desde "estímulo" importar i18n, loci18n desde '... / i18n / config' exportar clase predeterminada extiende el controlador initialize () let languages ​​= [title: 'English', código: 'en', título: 'Русский', código: 'ru'] this.element.innerHTML = languages.map ((lang) => return '
  • $ lang.title
  • ' ).unirse(")

    Aquí estamos usando el inicializar() devolución de llamada para mostrar una lista de idiomas admitidos. Cada li tiene un acción de datos atributo que especifica qué método (cambio de idioma, en este caso) debe activarse cuando se hace clic en el elemento.

    Ahora agregue el switchLanguage () método:

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

    Simplemente toma el objetivo del evento y toma el valor del lang de datos atributo.

    También me gustaría añadir un getter y setter para el currentLang atributo:

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

    El captador es muy simple: obtenemos el valor del idioma utilizado actualmente y lo devolvemos.

    El setter es más complejo. En primer lugar, utilizamos el cambiar idioma Método si el idioma establecido actualmente no es igual al seleccionado. Además, estamos almacenando el local recién seleccionado bajo el data-current-lang atributo (al que se accede en el getter), que localiza el cuerpo de la página HTML utilizando el complemento loc-i18next y, por último, resalta la configuración regional utilizada actualmente.

    Vamos a codificar el highlightCurrentLang ():

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

    Aquí estamos iterando sobre una matriz de localizadores y comparando los valores de sus lang de datos atributos al valor del local actualmente usado. Si los valores coinciden, al conmutador se le asigna un corriente Clase CSS, de lo contrario esta clase es eliminada.

    Para hacer el this.switcherTargets Construir el trabajo, necesitamos definir los objetivos de estímulo de la siguiente manera:

    objetivos estáticos = ["switcher"]

    Además, agregue objetivo de datos atributos con valores de conmutador Para el lis:

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

    Otra cosa importante a tener en cuenta es que los archivos de traducción pueden tardar un poco en cargarse, y debemos esperar a que se complete esta operación antes de permitir que se cambie la configuración regional. Por lo tanto, aprovechemos la cargado llamar de vuelta:

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

    Por último, no te olvides de eliminar setTimeout desde el loadUsers () método:

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

    Configuración regional persistente en la URL

    Después de cambiar la configuración regional, me gustaría agregar un ?lang GET parámetro a la URL que contiene el código del idioma elegido. La adición de un parámetro GET sin volver a cargar la página se puede hacer fácilmente con la ayuda de la API de historial:

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

    Detectando locale

    Lo último que vamos a implementar hoy es la capacidad de establecer la configuración regional según las preferencias del usuario. Un complemento llamado LanguageDetector puede ayudarnos a resolver esta tarea. Agregar un nuevo paquete de hilo:

    hilo agregar i18next-browser-languagedetector

    Importar LanguageDetector dentro de i18n / config.js expediente:

    importar LngDetector desde 'i18next-browser-languagedetector'

    Ahora ajusta la configuración:

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

    los orden Opción enumera todas las técnicas (ordenadas por su importancia) que el complemento debe probar para "adivinar" la configuración regional preferida:

    • cadena de consulta significa comprobar un parámetro GET que contiene el código del local.
    • lookupQuerystring establece el nombre del parámetro GET a usar, que es lang en nuestro caso.
    • navegador significa obtener datos locales de la solicitud del usuario.
    • etiqueta html consiste en buscar el lugar preferido de la lang atributo de la html etiqueta.

    Conclusión

    En este artículo hemos analizado I18siguiente, una solución popular para traducir aplicaciones JavaScript con facilidad. Ha aprendido cómo integrar I18siguiente con el marco de Stimulus, configurarlo y cargar archivos de traducción de forma asíncrona. Además, ha visto cómo cambiar entre las configuraciones regionales y establecer el idioma predeterminado según las preferencias del usuario.

    I18siguiente tiene algunas opciones de configuración adicionales y muchos complementos, así que asegúrese de consultar su documentación oficial para obtener más información. También tenga en cuenta que Stimulus no le obliga a usar una solución de localización específica, por lo que también puede intentar usar algo como jQuery.I18n o Polyglot. 

    ¡Eso es todo por hoy! Gracias por seguir leyendo, y hasta la próxima..