Ir para o conteúdo
AZ Tools

Inspetor de catálogos .mo do gettext

Um arquivo .mo é o que o msgfmt deixa ao compilar um .po, e é o arquivo que de fato vai para locale/<lang>/LC_MESSAGES/. Quando o .po se perde sobra um binário que ninguém consegue ler, embora a estrutura seja pequena: um número mágico, uma revisão, e as contagens e deslocamentos de duas tabelas de cadeias, mais uma tabela hash opcional que os leitores podem ignorar. Este inspetor percorre tudo isso e mostra o que existe lá dentro. O número mágico é 0x950412de e também a sua troca de bytes, 0xde120495: é assim que o arquivo declara em que ordem de bytes estão escritas as demais palavras, de modo que um catálogo compilado para uma máquina big-endian é lido aqui igual a um little-endian. As duas coisas que se escondem num .mo são bytes NUL embutidos, invisíveis num editor de texto e fáceis de errar: uma entrada com contexto guarda msgctxt, um byte 0x04 e depois o msgid numa única cadeia, e uma entrada plural guarda msgid e msgid_plural separados por um NUL, com as formas traduzidas unidas por NUL do outro lado. Aqui as duas são separadas, então o contexto ganha a sua própria coluna e as formas plurais aparecem numeradas. A entrada de metadados, aquela cujo msgid é vazio, é analisada em Project-Id-Version, Language, Content-Type, Plural-Forms e POT-Creation-Date, e todas as outras cadeias são decodificadas com a codificação que esse cabeçalho declara, e não com um palpite. Quando o cabeçalho falta ou mente, a página diz isso em vez de produzir texto ilegível em silêncio. A expressão de plural é analisada e avaliada para n = 0 a 20, então dá para ver qual forma cada contagem escolhe, e o número de formas de cada entrada é conferido contra nplurals: uma divergência é um defeito real que devolve a cadeia errada para algumas contagens. Também aponta entradas sem tradução, traduções idênticas ao msgid, um %s ou um {} que a tradução perdeu, uma tabela de originais fora de ordem (essa ordem é a que a busca binária dos leitores escritos em C usa) e uma tabela hash com menos posições do que entradas. Ele não reconstrói o .po e não julga a qualidade de uma tradução: apenas diz se o arquivo é sólido e coerente consigo mesmo.

Como usar

  1. Solte um arquivo .mo na caixa: o catálogo compilado de locale/<lang>/LC_MESSAGES/, ou qualquer coisa que o msgfmt tenha produzido.
  2. Leia primeiro a linha do cabeçalho: ordem de bytes, revisão, número de entradas e onde ficam de fato as duas tabelas de cadeias e a tabela hash.
  3. Confira a codificação na entrada de metadados. Todas as cadeias abaixo são decodificadas com ela, então um Content-Type errado é a primeira coisa a corrigir.
  4. Veja a tabela da regra de plural para n = 0 a 20 e compare o número de formas de cada entrada plural com nplurals.
  5. Passe os olhos pela lista de achados e use o filtro para chegar a um msgid, uma tradução ou um contexto na tabela de entradas.

Perguntas frequentes

Por que o número mágico aparece com dois valores diferentes?
Existe apenas um número mágico, 0x950412de, mas um .mo guarda cada palavra de 32 bits na ordem de bytes da máquina que o compilou, e o mágico é guardado do mesmo jeito. Um leitor carrega os quatro primeiros bytes das duas formas: se lidos como little-endian resultam em 0x950412de, o arquivo inteiro é little-endian; se resultam nisso lidos como big-endian, o arquivo inteiro é big-endian. Essa é toda a declaração de ordem de bytes, não há sinalizador em nenhum outro lugar, e é por isso que um catálogo compilado num equipamento big-endian continua funcionando num little-endian. Esta página mostra os quatro bytes crus ao lado da ordem que deduziu, para você conferir a dedução em vez de confiar nela.
O que é o byte 0x04 dentro de alguns msgid?
É como o msgctxt é guardado. O gettext não tem um campo separado para o contexto, então o msgfmt cola o contexto, um único byte 0x04 (o EOT do ASCII) e o msgid numa só cadeia, e o pgettext procura a cadeia combinada. Por isso duas entradas podem compartilhar o msgid "Open" e ainda assim serem entradas distintas: uma é "menu\x04Open" e a outra é "state\x04Open". Num editor ou num despejo ingênuo o separador é invisível e as duas parecem duplicadas. Este inspetor divide ali e dá ao contexto uma coluna própria, então as entradas que compartilham um msgid ficam lado a lado.
Como as formas plurais são guardadas e com o que nplurals precisa bater?
Do lado do original, uma entrada plural é msgid, um byte NUL e depois msgid_plural. Do lado da tradução são todas as formas unidas por bytes NUL. O cabeçalho decide quantas deveriam existir: Plural-Forms traz nplurals e uma expressão em n que devolve o índice da forma a usar. Se uma entrada tem menos formas do que nplurals promete, as contagens que selecionam o índice ausente não recebem nada e o gettext volta ao msgid ou ao msgid_plural sem tradução, apenas para alguns números, que é exatamente por que o defeito sobrevive aos testes. Esta página conta as formas de cada entrada plural e marca as que discordam do cabeçalho.
O que acontece quando o cabeçalho Content-Type falta ou está errado?
Nada no arquivo marca a própria codificação além desse cabeçalho, então o leitor precisa confiar nele. O gettext do Python recusa o catálogo de imediato: um charset=CHARSET não preenchido gera um erro de codificação desconhecida, e bytes inválidos na codificação declarada geram um erro de decodificação. Esta página é de propósito mais tolerante: recorre ao UTF-8, decodifica o que dá e diz qual das duas coisas aconteceu, porque em geral você abriu o arquivo justamente para descobrir por que um aplicativo mostrava lixo. Um catálogo sem entrada de metadados não tem codificação, idioma nem regra de plural; o gettext então trata as cadeias como ASCII e aplica a regra germânica n != 1.
A ordem das entradas importa?
Importa, e é o requisito que os geradores de .mo feitos à mão mais deixam passar. A especificação do gettext diz que as cadeias originais devem estar ordenadas por bytes, porque um leitor pode achar uma entrada com uma busca binária sobre a tabela de originais. A implementação em C faz exatamente isso quando não há uma tabela hash utilizável, então um catálogo fora de ordem falha em silêncio ao procurar algumas entradas, enquanto o Python, que monta um dicionário com o arquivo inteiro, lê tudo perfeitamente. É uma classe desagradável de defeito: funciona no seu script de teste e não no aplicativo. Esta página verifica a ordenação e informa quantos pares estão invertidos.
Para que serve a tabela hash e posso ignorá-la?
É um acelerador de busca opcional: o msgfmt escreve uma tabela hashpjw com cerca de um terço mais posições do que entradas e usa endereçamento aberto, de modo que um leitor consiga achar um msgid sem a busca binária. Leitores podem ignorá-la por completo, como o Python faz, e um catálogo com tamanho 0 é perfeitamente válido. O que não é válido é uma tabela sem mais posições do que entradas, já que o endereçamento aberto precisa de pelo menos uma livre; essa combinação indica que o arquivo foi escrito por algo que não entendia o formato, e por isso é relatado aqui. O tamanho e o deslocamento exibidos vêm direto das palavras do cabeçalho.

Ferramentas relacionadas