¿Cómo implementar la internacionalización y localización en aplicaciones web?
En el entorno digital actual, proporcionar una experiencia de uso accesible para una audiencia global es crucial. La internacionalización y localización permiten adaptar la interfaz y el contenido de una aplicación para satisfacer las necesidades de los usuarios en diferentes idiomas y regiones. Imagina la complejidad que esto puede suponer si además deseas ofrecer distintas monedas, por ejemplo, dólares, euros o pesos de diferentes países. A continuación, exploraremos cómo puedes abordar este desafío utilizando la API de The MovieDB.
¿Qué es la internacionalización en aplicaciones?
Internacionalización (I18N) se refiere al diseño y al desarrollo de software en el que se separan los elementos lingüísticos y culturales del núcleo de la aplicación. Esto permite que el software se adapte fácilmente a diferentes idiomas y regiones sin necesitar cambios en el código base. La localización, por otro lado, es el proceso de adaptar completamente una aplicación para una región o lenguaje específico. Para beginer, seleccionando por defecto el idioma del navegador de tus usuarios es un comienzo. Con JavaScript, puedes identificar este idioma usando:
const userLanguage =navigator.language||navigator.userLanguage;console.log(`El idioma por defecto es: ${userLanguage}`);
¿Cómo utilizar The MovieDB para seleccionar idiomas?
The MovieDB es una API que nos permite acceder a una serie de opciones para trabajar con diferentes idiomas. Utilizando query parameters, podemos hacer solicitudes para que la API nos devuelva la información en el idioma deseado. Ejemplo de solicitud en JavaScript para obtener datos en portugués de Brasil:
const api_key ='tu_api_key';const language ='pt-BR';const url =`https://api.themoviedb.org/3/movie/550?api_key=${api_key}&language=${language}`;fetch(url).then(response=> response.json()).then(data=>console.log(data)).catch(error=>console.error('Error:', error));
Este código te muestra cómo hacer una petición para mostrar los datos de las películas en un idioma en particular utilizando los códigos de idioma ISO 639-1.
¿Cómo gestionar la selección de idiomas en una interfaz?
Incorporar un botón o menú de selección para que el usuario elija su idioma preferido es un mecanismo común. Los idiomas pueden ser almacenados como objetos en JavaScript, y dependiendo de la selección del usuario, podemos ajustar los textos dinámicamente.
const idiomas ={'es':'Español','en':'Inglés','fr':'Francés'};// Código para cambiar textos según el idioma seleccionadofunctioncambiarIdioma(idiomaSeleccionado){// Aquí se implementa la lógica para actualizar la página con el idioma seleccionadoconsole.log(`El idioma seleccionado es: ${idiomas[idiomaSeleccionado]}`);}
Es beneficioso que los textos no sean "hardcoded" y se utilice loading dinámico para poder cargarlos según el idioma que requiera el usuario.
¿Cómo determinar el idioma por defecto?
El idioma por defecto puede determinarse usando la configuración del navegador del usuario, a través de Navigator.language. A menos que el usuario elija un idioma específico en la interfaz, el idioma del navegador debería utilizarse como la opción predeterminada.
Android/iOS: Usa system language settings para aplicaciones móviles.
Web Browsers: navigator.language para detectar automáticamente.
Al implementar esta lógica, asegúrate de tener en cuenta la opción de recoger la elección del usuario, posiblemente usando localStorage, para recordar la selección durante visitas futuras.
Consejos para mejorar la internacionalización y localización
Revise y Pruebe: Asegúrate de que cada idioma y localización funcione adecuadamente.
Actualizaciones constantes: La API de tus textos y datos debe ser revisada regularmente para incorporar nuevos idiomas o ajustes culturales.
Escucha a tus usuarios: Agrupa comentarios y sugiere mejores prácticas basándote en el feedback de usuarios que usan idiomas o configuraciones regionales distintas.
Implementar internacionalización no solo mejora la accesibilidad, sino que también enriquece la experiencia de los usuarios, fomentando una mayor interacción y satisfacción. ¡Manos a la obra y no dudes en compartir tus logros y fracasos en la comunidad para seguir aprendiendo juntos!
Yo lo que hice fue, agregarle a axios el parametro language
const api = axios.create({baseURL:"https://api.themoviedb.org/3/",params:{"api_key":API_KEY,"language":navigator.language||"es-ES"}})
Deploy del proyecto
Después de dos días buscando porqué diablos no podía acceder al valor de navigator.language, me di cuenta que la función que tengo para ver el cambio de hash en la URL se llama así "navigator" y sobre escribe el window.navigator de la API.
Si al poner el consola navigator.language te sale undefindes es por esto.
Saludos.
!Mind blown
Me salvaste 🫶 no sabia por q siempre q llamaba navigator.language salía undefined
Cada vez me gusta más React, se vuelve mucho más sencillo todo, y puedes reutilizar código, y componentes.
En mi caso, para los lenguajes solamente agregué un select para escoger Inglés, Español, oy Francés, también para el idioma predeterminado, estoy utilizando el Inglés, y si el usuario da permisos a nuestra app para acceder a sus datos, entonces utilizo el del usuario.
Proyecto
Repo
Demo
El error del final no sé muy bien por qué paso 😅
se ve genial, felicidades
Tremendo proyecto, felicitaciones!
Idioma del navegador por defecto:
let lang =navigator.languages[1];
Array de idiomas:
const countries =[{name:"usa",language:"en-US",flag:'🇺🇸',},...
languageOptions.addEventListener("change",(event)=>{ lang = event.target.value;homePage();});
Agregar a cada llamado de la API:
params:{language: lang,},
noto un problema, cuando usas esa función varias veces los lenguajes se repiten, alguna solución?
para solucionar lo de los lenguajes que se repiten y de manera sencilla, agregue languageOptions.innerHTML = ''; al principio de la función getLanguages
una variable donde se cargara el idioma por defecto
let lang ='en';
Y la funcion que realizara el cambio con un evento click
language.addEventListener('click',()=>{ lang = language.value;homePage();})
Que forma tan simple y efectiva, gracias por compartirla 😁
esa función en que archivo la creaste?
La solución a esto sería primero comprobar si el navegador soporta el navigator.languages, si no, entonces debesmos prepararnos para asignar uno pordefecto, por ejemplo el español, y allí asignarlo a la api e ir implementándolo en todas las funciones!
Cree este objeto
const langs =[{lang:'es',captions:{trends:'Tendencias',category:'Categorias',likedTitle:'Peliculas Favoritas',trendMore:'Ver Más',footerCaption:'Hecho con amor en Platzi por @juandc',selectLang:'Seleccione Idioma',search:'Buscar',}},{lang:'en',captions:{trends:'Trends',category:'Categories',likedTitle:'Favorite Movies',trendMore:'More',footerCaption:'Made with love in Platzi by @juandc',selectLang:'Select a language',search:'Search',}},{lang:'pt',captions:{trends:'Tendências',category:'Categorias',likedTitle:'Filmes Favoritos',trendMore:'Ver mais',footerCaption:'Feito com amor em Platzi por @juandc',selectLang:'Selecione um idioma',search:'Procurar',}},{lang:'gr',captions:{trends:'Tendenzen',category:'Kategorien',likedTitle:'Lieblingsfilme',trendMore:'Mehr sehen',footerCaption:'Mit Liebe gemacht in Platzi von @juandc',selectLang:'Wähle eine Sprache',search:'Suche',}},{lang:'ar',captions:{trends:'اتجاهات',category:'فئات',likedTitle:'الأفلام المفضلة',trendMore:'المزيد',footerCaption:'صنع بكل حب في Platzi بواسطةjuandc',selectLang:'اختر لغة',search:'بحث',}},{lang:'fr',captions:{trends:'Les tendances',category:'Catégories',likedTitle:'voir plus',trendMore:'Mehr sehen',footerCaption:'Fait avec amour à Platzi par @juandc',selectLang:'Sélectionnez une langue',search:'Chercher',}},]
Luego en main.js
const lang =document.getElementById('optionLanguages')changeLanguage(navigator.language.substring(0,2))window.addEventListener('languagechange',()=>{changeLanguage(lang.value=navigator.language.substring(0,2))navigatorApp()});functionchangeLanguage(lang){//buscar lenguaje y asignarlo a un objeto captions = langs.find(e=> e.lang=== lang)if(captions ==undefined){ captions = langs.find(e=> e.lang==='es') optionLanguages.value='es'}else{ optionLanguages.value= lang
}}functionoptLang(lang){changeLanguage(lang)navigatorApp()}
En cada llamado al api se pasa la variable de Lang
Cree un select con los idiomas del primer objeto creado. Cuando el idioma se cambia por los settings del explorador, el evento se encarga de cambiar el idioma, buscar los titulos y refrescar la seccion que se esta mostrando.
Muy buena idea la de almacenar en un objeto los textos por defecto de la aplicación. Cómo sugerencia creo que sería bueno sustituir el uso de objetos literales por instancias de una clase llamada Languages, esto ayudaría a escribir más fácilmente el código y hacerlo escalable para agregar luego nuevos idiomas
Estoy haciendo algo parecido y no logro hacer que cuando recargo la pagina en mi selector aparezca el idioma que ya seleccione antes. Siempre aparece el primero del HTML cuando la recargo. Pudiste solucionar eso?
Cree esta constante y la pase como un parámetro junto a la api_key así: 'language': leng
window.navigator.language da una respuesta algo así como 'es-419'. El .split es para obtener lo que esta antes del guion. Mi pregunta es, ¿para que es el otro numero???
Quizas tiene las regiones en números, lo normal es ver resultados así: 'es-VE', que seria español de Venezuela, o 'es-MX' español México etc..
Al inicio del main.js agregue una constante language y esa constante la paso en el axios por parametros.
const language =window.navigator.language;const api = axios.create({baseURL:'https://api.themoviedb.org/3/',headers:{'Content-Type':'application/json;charset=utf-8',},params:{'api_key':API_KEY,'language': language
},});```
Bueno basicamente englobe el titulo de la pagina con el selector de idioma donde inserte 4 lenguajes populares como prueba:
Diseñe dos nuevas funciones donde en la primera alojo el valor del idioma en un dataset de las imagenes para que al ser clickeadas guarden ese valor en localStorage recordando asi las preferencias del usuario en futuras visitas. Mientras que en la segunda modifico los titulos y demas contenido de texto que hemos hardcodeado en la pagina para que se adepten al idioma seleccionado
functionassignLang(container){const lenguajes =Array.from(container.querySelectorAll('img')); lenguajes.forEach((lenguaje)=>{ lenguaje.addEventListener('click',()=>{localStorage.setItem('language', lenguaje.getAttribute('data-lang'))translator(localStorage.getItem('language'));location.reload();});});}functiontranslator(langParam){if(langParam =="es-AR"){ movieDetailDescription.textContent="No hay descripción disponible para la película actual. Echa un vistazo y puede que encuentres otra película que te guste.";}if(langParam =="fr-FR"){ searchFormInput.setAttribute("placeholder","Recherche d'un film"); trendingPreviewTitle.innerText="Tendances"; trendingBtn.innerText="Voir plus"; categoriesPreviewTitle.innerText="Catégories"; relatedMoviesTitle.innerText="Films similaires"; likedTitle.innerText="Films préférés"; footerTitle.innerHTML="Réalisé par @Dmaledicte"; movieDetailDescription.textContent="Aucune description n'est disponible pour le film actuel. Jetez un coup d'œil et vous trouverez peut-être un autre film qui pourrait vous plaire";}if(langParam =='pt-BR'){ searchFormInput.setAttribute("placeholder","Pesquisar um filme"); trendingPreviewTitle.innerText="Tendências"; trendingBtn.innerText="Ver mais"; categoriesPreviewTitle.innerText="Categorias"; relatedMoviesTitle.innerText="Filmes semelhantes"; likedTitle.innerText="Filmes favoritos"; footerTitle.innerHTML="Direção de @Dmaledicte"; movieDetailDescription.textContent="Nenhuma descrição está disponível para o filme atual. Dê uma olhada e você pode encontrar outro filme que você pode gostar";}if(langParam =='en-EN'){ searchFormInput.setAttribute("placeholder","Look for a movie"); trendingPreviewTitle.innerText="Trends"; trendingBtn.innerText="See more"; categoriesPreviewTitle.innerText="Categories"; relatedMoviesTitle.innerText="Similar movies"; likedTitle.innerText="Favourite movies"; footerTitle.innerHTML="Made by @Dmaledicte"; movieDetailDescription.textContent="There are no available descriptions for the current. Take a look around and you might find something you would like";}}
Luego en Axios inserto un nuevo Param que en caso de que no exista ninguna preferencia almacenada en localStorage utilice el lenguaje del navegador del usuario:
Detalles a tener en cuenta
-Es necesario establecer un refresh de pagina al seleccionar un nuevo idioma para que Axios tome correctamente el nuevo valor de language.
-Setear una ejecucion de translator en nuestro navigator para que se traduzca correctamente el contenido.
Viendo si ver o no la siguiente clase por lo que dijo el prof de LLORAR SANGRE...
Hola, todo bien solucionado, no sé si de la mejor forma pero funcionó, solo me quedó un bug, al añadir una peli a favoritos se me queda agregada con el idioma que tenía en el momento de agregarla, ya luego no se actualiza, lo seguiré viendo a ver si se soluciona.
De lo demás creé un array con los principales idiomas, e incluí el selector en el html y de forma dinámica con JS, fuí agregando cada idioma.
Luego creé una variable lang, con valor del navigator.language y creé un evento para observar cuándo se cambiaba una option del selector y se va cambiando el valor de lang, y llamo ahí dentro getTrendingMoviesPreview (), getCategoriesMoviesPreview (), getLikedMovies(), para que se actualicen.
El parámetro 'language', se lo tuve que pasar a cada función, porque no me funcionaba pasándola directamente al llamado de la api:
// variable langlet lang =navigator.language;
// array de lenguajesconst languages =[{"iso_639_1":"en","english_name":"English","name":"English"},{"iso_639_1":"fr","english_name":"French","name":"français"},{"iso_639_1":"de","english_name":"German","name":"Deutsche"},{"iso_639_1":"it","english_name":"Italian","name":"Italiano"},{"iso_639_1":"zh","english_name":"Chinese","name":"漢語"},{"iso_639_1":"es","english_name":"Spanish","name":"Español"},]
// función para crear el selector, y crear el evento de cambio de lenguaje:functioncreateLanguages(){ selectContainLanguaje.innerText='' languages.forEach(language=>{let optionLanguage =document.createElement('option'); optionLanguage.name= language.english_name; optionLanguage.innerText= language.name; optionLanguage.value= language.iso_639_1; selectContainLanguaje.appendChild(optionLanguage)}) selectContainLanguaje.addEventListener('change',(e)=>{console.log(e.target) lang = e.target.valueconsole.log(lang)getTrendingMoviesPreview()getCategoriesMoviesPreview()getLikedMovies()})}
// a cada función le pasé el parámetro de lenguajeasyncfunctiongetTrendingMoviesPreview(){const{data}=awaitapi('trending/movie/day',{params:{'language': lang,}})const movies = data.results;createMovies(movies, trendingMoviesPreviewList,{lazyLoad:true,cleanPage:true});}
Al inicio del archivo main.js, creé una variable language cuyo valor es navigator.language
let language =navigator.language;
Luego, dentro de la constante api donde llamamos a axios, agrego dentro de params un nuevo parámetro que tenga el valor de la variable creada antes.
const api = axios.create({baseURL:'https://api.themoviedb.org/3/',headers:{'Content-Type':'application/json;charset=utf-8',},params:{'api_key':API_KEY,'language': language,},});
Cambio el idioma del navegador y efectiamente muestra la información de las películas en el idioma correspondiente.
Creaste un selector para cada idioma? puedes aniadir tu codigo donde manda llamar el api como una guia. se agradece campeon
saludos
Al agregar idiomas como el español, hay que tener en cuenta que debemos usar **decodeURI **en varios textos, que por tener por ejemplo acentos, están codificados.
Hola Fernando. En qué parte del codigo hay que usar ese decodeURI y como funciona?
Yo resolví lo del idioma de este modo:
//Dataconst lang=localStorage.language;lan.value= lang !==''? lang :'en';if(lan.value=="en"){ trendingPreviewTitle.innerHTML='Trends'}lan.addEventListener('change',()=>{localStorage.setItem('language', lan.value);location.reload();})const api = axios.create({baseURL:'https://api.themoviedb.org/3',headers:{'Content-Type':'application/json; charset=utf-8',},params:{'api_key':API_KEY,'language': lan.value,},});
En el archivo navigation.js , agregue un switch para traducir los titulos y botones al ejecutarse la funcion homePage() :
nada no pude completar el reto por más que lo intente lo logre hacer despues de un tiempo pero no me cargaban los nombre de html de los idiomas y abandone el reto :-(
Si se realiza un proyecto sin usar API KEY sino que es como que nosotros mismos nos encargaramos de todo. La traducción a cada idioma, la haríamos nosotros con alguna técnica específica o tendríamos cada información en diferente idioma???
En ese caso podríamos traducir todo lo que sea pura UI (títulos, párrafos, placeholders), pero no lo que venga de la API. En ese caso sería la misma vuelta: escuchar el idioma seleccionado del navegador y dependiendo de eso renderizar los textos de un idioma u otro.
Muchas gracias profe!
No estoy pudiendo hacer el post del params language con axios. Podría configurarlo con fetch pero ya que el proyecto se hizo en axios quisiera poder hacerlo con esa herramienta. Si alguien pudo agradecería que me lo diga, es lo que me falta para terminar. Mientras tanto voy a seguir intentando.
Muy bien la actitud de seguir intentando hasta encontrar la solución. :green_heart:
Si quieres, pásame tu repo o muéstrame tu código para ayudarte a identificar qué podría estar causando problemas.
Ya pude, solo que tuve que agregar el parametro a cada llamado de la api, yo quería hacerlo una vez sola pero para hacer un post hay que indicar la url y esta es diferente por cada solicitud.
Primero lo hicé con Get Languages pero la mayoría de los idiomas devolvía null, luego probé tomando los primary_translations y devolviendolos como languages a travez del form, pero también había algunos que devolvían null. Por lo que decidí hacer un array manual con los valores de algunos de los idiomas.
yo soy de latam y quiero poner español-latinoamericano que seria es-419 pero al parecer el navigator.language no acepta los numeros y si lo intento hacer un string no me anda
¿Podrías mostrarme por fa tu código para ayudarte a identificar qué pasó? Definitivamente es-419 es el code correcto para español de latam.
me quedé en la selección de idioma con navigator.language, pero no pude hacer que al dar click en mi selector de idioma se cambie si me dan alguna ayuda se les agradece
mi código es este:
este langSelector es una imagen con una bandera, aquí puede poner la imagen y poner el idioma por defecto de acuerdo a la selección del navegador