Saltar al contenido
AZ Tools

Inspector de catálogos .mo de gettext

Un archivo .mo es lo que deja msgfmt al compilar un .po, y es el archivo que realmente se distribuye en locale/<lang>/LC_MESSAGES/. Cuando se pierde el .po queda un binario que nadie puede leer, aunque su estructura es pequeña: un número mágico, una revisión, y los recuentos y desplazamientos de dos tablas de cadenas, más una tabla hash opcional que los lectores pueden ignorar. Este inspector recorre todo eso y muestra lo que hay dentro de verdad. El número mágico es 0x950412de y también su intercambio de bytes, 0xde120495: así declara el archivo en qué orden de bytes están escritas el resto de sus palabras, de modo que un catálogo compilado para una máquina big-endian se lee aquí igual que uno little-endian. Las dos cosas que se esconden en un .mo son bytes NUL incrustados, invisibles en un editor de texto y fáciles de equivocar: una entrada con contexto guarda msgctxt, un byte 0x04 y luego el msgid en una sola cadena, y una entrada plural guarda msgid y msgid_plural separados por un NUL, con las formas traducidas unidas por NUL al otro lado. Aquí se separan las dos, así que el contexto tiene su propia columna y las formas plurales aparecen numeradas. La entrada de metadatos, la que tiene el msgid vacío, se analiza en Project-Id-Version, Language, Content-Type, Plural-Forms y POT-Creation-Date, y todas las demás cadenas se decodifican con la codificación que declara esa cabecera, no con una suposición. Si la cabecera falta o miente, la página lo dice en vez de producir texto ilegible en silencio. La expresión de plural se analiza y se evalúa para n = 0 a 20, de forma que se ve qué forma elige cada cantidad, y el número de formas de cada entrada se compara con nplurals: una discrepancia es un error real que devuelve la cadena equivocada para algunas cantidades. También señala entradas sin traducir, traducciones idénticas a su msgid, un %s o un {} que la traducción perdió, una tabla de originales sin ordenar (ese orden es el que usa la búsqueda binaria de los lectores escritos en C) y una tabla hash con menos ranuras que entradas. No reconstruye el .po ni juzga la calidad de una traducción: solo dice si el archivo es correcto y coherente consigo mismo.

Cómo usar

  1. Suelta un archivo .mo en la caja: el catálogo compilado de locale/<lang>/LC_MESSAGES/, o cualquier cosa que haya producido msgfmt.
  2. Lee primero la fila de la cabecera: orden de bytes, revisión, número de entradas y dónde están realmente las dos tablas de cadenas y la tabla hash.
  3. Comprueba la codificación en la entrada de metadatos. Todas las cadenas de abajo se decodifican con ella, así que un Content-Type erróneo es lo primero que hay que arreglar.
  4. Mira la tabla de la regla de plural para n = 0 a 20 y compara el número de formas de cada entrada plural con nplurals.
  5. Repasa la lista de hallazgos y usa el filtro para saltar a un msgid, una traducción o un contexto en la tabla de entradas.

Preguntas frecuentes

¿Por qué el número mágico aparece con dos valores distintos?
Solo hay un número mágico, 0x950412de, pero un .mo guarda cada palabra de 32 bits en el orden de bytes de la máquina que lo compiló, y el mágico se guarda igual. Un lector carga los cuatro primeros bytes de las dos maneras: si leídos como little-endian dan 0x950412de, todo el archivo es little-endian; si dan ese valor leídos como big-endian, todo el archivo es big-endian. Esa es toda la declaración de orden de bytes, no hay ninguna bandera en otro sitio, y por eso un catálogo compilado en un equipo big-endian sigue funcionando en uno little-endian. Esta página muestra los cuatro bytes crudos junto al orden que dedujo, para que puedas comprobar la deducción en vez de fiarte de ella.
¿Qué es el byte 0x04 que aparece dentro de algunos msgid?
Es la forma de guardar msgctxt. gettext no tiene un campo aparte para el contexto, así que msgfmt pega el contexto, un único byte 0x04 (el EOT de ASCII) y el msgid en una sola cadena, y pgettext busca la cadena combinada. Por eso dos entradas pueden compartir el msgid "Open" y seguir siendo entradas distintas: una es "menu\x04Open" y la otra "state\x04Open". En un editor o en un volcado ingenuo el separador es invisible y las dos parecen duplicadas. Este inspector corta por ahí y da al contexto su propia columna, así las entradas que comparten msgid quedan juntas y se ve cuál recibirá cada llamada.
¿Cómo se guardan las formas plurales y con qué tiene que coincidir nplurals?
Del lado del original, una entrada plural es msgid, un byte NUL y luego msgid_plural. Del lado de la traducción son todas las formas unidas por bytes NUL. La cabecera decide cuántas debería haber: Plural-Forms lleva nplurals y una expresión en n que devuelve el índice de la forma a usar. Si una entrada tiene menos formas de las que promete nplurals, las cantidades que seleccionan el índice ausente no reciben nada y gettext vuelve al msgid o al msgid_plural sin traducir, solo para algunos números, que es justo por lo que el fallo sobrevive a las pruebas. Esta página cuenta las formas de cada entrada plural y marca las que no cuadran con la cabecera.
¿Qué pasa si falta la cabecera Content-Type o está mal?
Nada dentro del archivo indica su propia codificación salvo esa cabecera, así que el lector tiene que fiarse. El gettext de Python rechaza el catálogo directamente: un charset=CHARSET sin rellenar provoca un error de codificación desconocida, y los bytes que no son válidos en la codificación declarada provocan un error de decodificación. Esta página es a propósito más tolerante: vuelve a UTF-8, decodifica lo que puede y te dice cuál de las dos cosas ocurrió, porque normalmente abriste el archivo justo para averiguar por qué una aplicación mostraba basura. Un catálogo sin entrada de metadatos no tiene codificación, ni idioma, ni regla de plural; gettext trata entonces las cadenas como ASCII y aplica la regla germánica n != 1.
¿Importa el orden de las entradas?
Sí, y es el requisito que más se les escapa a los generadores de .mo escritos a mano. La especificación de gettext dice que las cadenas originales deben estar ordenadas por bytes, porque un lector puede encontrar una entrada con una búsqueda binaria sobre la tabla de originales. La implementación en C hace exactamente eso cuando no hay una tabla hash utilizable, así que un catálogo sin ordenar falla en silencio al buscar algunas entradas mientras que Python, que construye un diccionario con todo el archivo, lo lee sin problema. Es una clase desagradable de error: funciona en tu script de prueba y no en la aplicación. Esta página comprueba el orden y dice cuántos pares están invertidos.
¿Para qué sirve la tabla hash y puedo ignorarla?
Es un acelerador de búsqueda opcional: msgfmt escribe una tabla hashpjw con alrededor de un tercio más de ranuras que entradas y usa direccionamiento abierto, de modo que un lector puede encontrar un msgid sin la búsqueda binaria. Los lectores pueden ignorarla por completo, como hace Python, y un catálogo con tamaño 0 es perfectamente válido. Lo que no es válido es una tabla sin más ranuras que entradas, porque el direccionamiento abierto necesita que quede al menos una libre; esa combinación significa que el archivo lo escribió algo que no entendía el formato, y por eso se informa aquí. El tamaño y el desplazamiento que se muestran salen directamente de las palabras de la cabecera.

Herramientas relacionadas