const estrategiasInterpolacion = { 'mail': (partes) => { return `${partes[1]}` }, 'link': (partes) => { return `${partes[2]}` }, 'linkblank': (partes) => { return `${partes[2]}` }, /** * Cuando se quiere interpolar un valor pero además que se traduzca. * t|/ruta/en/traduccion|clave. Con $ en la ruta se autoreferencia a la clave * @example Caso de traducción de caminos (con i18n-ejercicio="caminos") * {{t|$/nombre|ejercicio}} -> roads * @param {[string, string, string]} partes * @param {HTMLElement} target */ 't': (partes, target) => { const clave = partes[2] const valor = target.getAttribute('i18n-' + clave) const path = partes[1].replace('$', valor) const partesPath = path.split('/') let res = textos[idioma] partesPath.forEach((p) => res = res[p]) return res } } /** * @returns {string[]} Array strings. El contenido que estuviera dentro de cada * {{...}} * @example '{{a}} b {{c}}' -> ['a', 'c'] */ function extraerInterpolaciones(texto) { const interpolaciones = [] const re = /\{\{(.+?)\}\}/g; let m; while (m = re.exec(texto)) { interpolaciones.push(m[1]) } return interpolaciones } /** * Tipos de interpolación: * --- * 1. Generadores: pequeños programas con múltiples partes separadas por |, * actualmente solo usados para generar enlaces formateados. * @example {{link|enlace|texto}} -> texto. * @example {{mailto|a@b.c|texto}} -> texto * --- * 2. Interpolaciones normales: sustituyen la {{clave}} por un valor obtenido * de un atributo i18n-clave. * @example
* con ejemplo siendo "Hello {{a}}!" * Resultado:Hello World!
* @param {HTMLAreaElement} target Elemento HTML que contiene el i18n y * posibles atributos relacionados. * @param {string} interpolacion una clave de interpolación (sin las llaves {{}}) */ function interpolar(target, interpolacion) { const partes = interpolacion.split('|') const tipo = partes[0] //const attr = target.getAttribute(`i18n_${tipo}`) if (partes.length > 1) { return estrategiasInterpolacion[tipo](partes, target) } return target.getAttribute('i18n-' + interpolacion) } function traducir_pagina() { const targets = document.querySelectorAll('[i18n]') const targetAttrs = document.querySelectorAll('[i18nattr]') targets.forEach(t => traducir(t)) targetAttrs.forEach(t => traducirAtributos(t)) } /** * Obtiene las categorías del i18n. Si el target no tiene categorías, separadas * por |, busca la categoría en el i18n de sus ancestros superiores. * @param {HTMLElement} target * @param {string} i18nkey */ function getCategorias(target, i18nkey) { const categorias = i18nkey.split('|') if (categorias.length > 1) { return categorias } let padre = target while ( padre = padre.parentElement ) { const group = padre.getAttribute('i18ngroup') if (group) { categorias.unshift(...group.split('|')) break } const attr = padre.getAttribute('i18n') if (attr && attr.length > 0) { categorias.unshift(...attr.split('|')) } } return categorias } function getTraduccion(path) { try { let txt = textos[idioma] path.forEach(j => { if (txt == undefined) { return } txt = txt[j] }) if (typeof txt != 'string') { return } return txt } finally {} } /** * Un elemento puede tener un atributo i18n con siguientes formatos: * i18n="ruta|en|locales" * i18n="modificador:ruta|en|locales" * * Donde modificar puede ser: * · txtnode: solo reemplazará el texto, respetando el resto del html * * @param {HTMLElement} target */ function traducir(target) { let i18n = target.getAttribute('i18n') if (i18n == null) { return } let mod = 'normal' if (i18n.includes(':')) { const modYPath = i18n.split(':') mod = modYPath[0] i18n = modYPath[1] } if (i18n.length == 0) { i18n = target.classList.item(0) } const path = getCategorias(target, i18n) if (path[0].length == 0){ return } try { console.log('######## i18n #########'); console.log(path); let txt = getTraduccion(path) console.log(txt); console.log('#######################\n'); if (!txt) { return } const interpolaciones = extraerInterpolaciones(txt) interpolaciones.forEach(i => { const res = interpolar(target, i) txt = txt.replace('{{' + i + '}}', res) }) if (mod == 'txtnode') { target.childNodes.forEach(c => { if (c.nodeType == Node.TEXT_NODE && c.textContent.trim().length != 0) { c.textContent = ' ' + txt + ' ' } }) } else { target.innerHTML = txt } } finally {} } function traducirAtributos(target) { let attr = target.getAttribute('i18nattr') if (attr == null) { return } const atributo = attr.split(':')[0] const categorias = getCategorias(target, attr.split(':')[1]) const txt = getTraduccion(categorias) if (!txt) { return } target.setAttribute(atributo, txt) } function traducirOrdinal(numero) { const __ = textos[idioma] let simbolo = numero > 20 ? __.simbolosOrdinales["20"] : __.simbolosOrdinales[`${numero}`] return numero + simbolo } document.addEventListener('DOMContentLoaded', ()=>{ getIdioma() traducir_pagina() })