Buenas!, recién un compañero expuso un problema que estaba teniendo con una traducción en el checkout de Magento, algo tan sencillo como esto <div class="step-title" translate="'Shipping Address:'" data-role="title" />, a simple vista parece que está todo bien.. un <div /> con su clase, su atributo data-role y su traducción con el binding, pero.. ¿qué ocurre con esto?

Si esa misma línea la ponemos en cualquier .html veremos el siguiente error: Captura de pantalla 2021-03-13 a las 16.51.51.png

Esto es debido a los : que hemos añadido al final de la traducción, pero ahora bien, esto solo pasa si utilizamos el binding translate, si en su defecto usamos i18n no ocurrirá. Veamos la diferencia y por qué pasa esto.

La consola nos avisa, nos dice que hay un problema al generar el binding, si bien vemos el i18n vemos que tiene lo siguiente:

app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/i18n.js

renderer
    .addNode('translate', {
        binding: 'i18n'
    })
    .addAttribute('translate', {
        binding: 'i18n'
    });

El mismo i18n está llamando al JS definido renderer que haga un addAttribute de translate y que este atributo sea un binding de i18n para que tanto el atributo como el binding hagan lo mismo y realicen una traducción de lo que le pasemos. (Un atributo o un nodo, ambos válidos).

Ahora nos iríamos al siguiente fichero:

app/code/Magento/Ui/view/base/web/js/lib/knockout/template/renderer.js

Este fichero se encargará de hacer lo que he comentado antes, pero, nos iremos a una función en concreto:

/**
 * Wraps provided string in curly braces if it's necessary.
 *
 * @param {String} args - String to be wrapped.
 * @returns {String} Wrapped string.
 */
wrapArgs: function (args) {
    if (~args.indexOf('\\:')) {
        args = args.replace(colonReg, ':');
    } else if (~args.indexOf(':') && !~args.indexOf('}')) {
        args = '{' + args + '}';
    }

    return args;
}

Esta es la parte que nos está generando el error, si hacemos debug con Chrome veamos que es lo que llega y que hace.

Llega a la función attribute con los siguientes datos: Captura de pantalla 2021-03-13 a las 17.16.43.png

Como vemos, nos llega el string a traducir y el atributo translate con el binding a "provocar", cuando este llama a la función wrapArgs, vemos que, a diferencia de por ejemplo este: Captura de pantalla 2021-03-13 a las 17.20.11.png

Nuestra traducción con los : nos devuelve la traducción de esta forma: Captura de pantalla 2021-03-13 a las 17.18.56.png

Esto, ¿qué provoca?, cuando la traducción llega al createBindingsStringEvaluator para crear el binding vemos los siguiente: Captura de pantalla 2021-03-13 a las 17.35.07.png

El rewrittenBindings devuelve el i18n pero el return de la función es un objeto, esto es lo que provoca el error de Knockout. Si vemos un ejemplo "correcto": Captura de pantalla 2021-03-13 a las 17.36.08.png

Aquí el return de la función sería lo esperado, un string el cual luego convertir.

Este caso es muy difícil que se de en un Magento2 mientras desarrollamos, y es más, si vemos que esto nos falla lo más normal es utilizar i18n pero quería aprovechar esto para explicar el funcionamiento del renderer para ver como magento aprovecha un atributo para generar un binding de KO.

Gracias, por leer el post.