Como traduzir temas e plugins?

Quem lida com o WordPress provavelmente já traduziu um tema, simplesmente buscando por strings nos arquivos e substituindo pelo equivalente em português. Isso é simples (até certo ponto) e eficaz, mas o WP oferece um sistema de tradução nativo, baseado no GNU Gettext. Verdade que muitos desenvolvedores não dão a menor bola para isso, mas alguns sim! Um brinde a esses!

Nas próximas linhas vamos tentar mostrar como “internacionalizar” um tema ou plugin – usarei daqui em diante o termo genérico “addon” para ambos.

O modo grosseiro

O primeiro caso (citado acima) não requer muito conhecimento, apenas paciência. Onde achar uma frase traduzível, traduz e salva. Se eu fosse você, não apagaria as frases originais nesse caso. Mas isso só se aplica a temas e plugins que foram desenvolvidos sem essa preocupação – nos que usam as funções de tradução, o trabalho é bem mais fácil.

Se o addon que deseja traduzir tem suporte a tradução…

… as strings serão tratadas com as funções _e() e __(). Ou seja, o que você esperava que fosse assim:

<h2>Plugin title</h2>

aparece assim no código:

<h2><?php _e("Plugin title", "text_domain"); ?></h2>

Aqui “text_domain" é uma string definida pelo desenvolvedor para identificar o plugin no sistema de tradução e é definida pelas funções load_theme_textdomain() e load_plugin_textdomain() que associam o textdomain ao arquivo .mo, que é gerado a partir do catálogo PO.

Deve haver um ou mais arquivos com extensão .PO incluídos no pacote ou, de preferência, um arquivo .POT – que nada mais é que um .PO apenas com as strings originais, sem tradução, para servir de base para criação dos arquivos .PO. Para editar esses catálogos (PO ou POT), você pode usar o poEdit que é grátis e simples de usar.

Nesse caso, você não deve modificar os arquivos do addon – a tradução acontecerá apenas no catálogo, que deverá ser salvo seguindo o padrão definido pelo WP:

textdomain + ‘-‘ + código da linguagem + ‘.po’ -> no nosso exemplo seria assim -> text_domain-pt_BR.po

Ao finalizar a tradução, salve o catálogo. Neste momento o poEdit gera automaticamente um arquivo de mesmo nome com extensão .MO – esse é que será usado pelo WordPress, o .PO é apenas para nós, humanos, conseguirmos ler e editar as strings.

Se o arquivo text_domain-pt_BR.mo estiver presente no diretório informado ao declarar o textdomain, basta configurar a constante WP_LANG (em wp-config.php) para “pt_BR” e pronto, se tudo deu certo, o sistema aparecerá em português.

Mas você pode querer internacionalizar um addon que não oferece suporte à tradução…

Nesse caso teremos que fazer o que o desenvolvedor não fez, editando o código – portanto só é recomendado para quem tem alguma intimidade com o PHP.

A primeira coisa é declarar o textdomain e informar a localização dos arquivos .MO, usando uma das funções citada acima. A linha abaixo pode ser inserida em qualquer parte do arquivo principal do plugin, ou do arquivo functions.php, se estamos traduzindo um tema

// para plugins:
load_plugin_textdomain("mytextdomain", dirname(__FILE__)."/languages");
// para temas:
load_theme_textdomain("mytextdomain", dirname(__FILE__)."/languages");

Aqui vamos criar o textdomain “mytextdomain” e salvar nossos arquivos no diretório languages, sob a raiz do addon. Agora é que começa a parte chata. Teremos que editar os arquivos, um por um em busca das strings que precisam ser traduzidas. Vamos então entender a diferença entre as funções que iremos usar:

  • <?php _e('string', 'textdomain'); ?>
    a função _e() imprime a string traduzida na tela
  • <?php $str = __('string', 'textdomain'); ?>
    a função __() retorna a string traduzida sem imprimir

Vamos ver um trecho de código a ser traduzido, como exemplo:

<?php
  if(!empty($_POST['act'])) {
  ?><div class="updated fade"><p><?php
  if($_POST['act'] = 'remove')
  print deleteThing($_POST['mythings']) // função imaginária...
        ? "Your '{$_POST['mythings']}' was successfully removed."
        : "Your '{$_POST['mythings']}' could not be removed!";
  if($_POST['act'] = 'edit')
  print editThing($_POST['mythings'])// função imaginária...
        ? "Your '{$_POST['mythings']}' was successfully edited."
        : "Your '{$_POST['mythings']}' could not be edited!";
  }
  ?></p></div><?php
?>
<h2>My things</h2>
<div class="wrap">
  <form action="<?php print $PHP_SELF; ?>" method="post">
  <label>Your cool things:
  <select name="mythings">
  <?php foreach($coolthings as $things) { ?>
  <option value="<?php print sanitize_title($things['thing_title']); ?>"><?php print $things['thing_title']; ?></option>
  <?php } ?>
  </select>
  </label>
  <p class="butts">
  <input type="button" class="button auto"<?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?>
value="Edit" onclick=" this.form.act.value = 'edit'; this.form.submit();" />
  <input class="auto button delete"<?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?>
value="<?php _e("Edit", "mythings"); ?>"type="button" value="Delete" onclick="if(!confirm('Do you really want to delete this thing?'))
return; this.form.act.value = 'remove'; this.form.submit();" />
  </p>
  <input type="hidden" name="act" />
  </form>
</div>

Veja abaixo o mesmo código, já com o suporte à internacionalização:

<?php
// essa linha deve aparecer apenas uma vez, em qualquer lugar - fora
// de funções (ou não, se você sabe quando executá-la)
load_plugin_text_domain("mythings", dirname(__FILE__).'/languages');

// começamos usando __(), pois o valor está sendo atribuído à variável $msg, não impresso
  if(!empty($_POST['act'])) {
    if($_POST['act'] = 'remove')
    $msg = deleteThing($_POST['mythings']) // função imaginária...
          ? sprintf(__("Your '%s' was successfully removed.", "mythings"), $_POST['mythings'])
          : sprintf(__("Your '%s' could not be removed!", "mythings"), $_POST['mythings']);
    if($_POST['act'] = 'edit')
    $msg = editThing($_POST['mythings'])// função imaginária...
          ? sprintf(__("Your '%s' was successfully edited.", "mythings"), $_POST['mythings'])
          : sprintf(__("Your '%s' could not be edited!", "mythings"), $_POST['mythings']);
    ?>
    <div class="updated fade">
    <p><?php print $msg; ?></p>
    </div>
    <?php
  }
?>

<?php
// para os valores que estavam escritos diretamente na parte HTML do documento,
// usamos a função _e(), que imprime a tradução, sem precisar de echos ou prints
?>

<h2><?php _e("My things", "mythings"); ?></h2>
<div class="wrap">
  <form action="<?php print $PHP_SELF; ?>" method="post">
  <label><?php _e("Your cool things:", "mythings"); ?>
  <select name="mythings">
  <?php foreach($coolthings as $things) { ?>
  <option value="<?php print sanitize_title($things['thing_title']); ?>"><?php print $things['thing_title']; ?></option>
  <?php } ?>
  </select>
  </label>
  <p class="butts">
  <input type="button" class="button auto"<?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?>
value="<?php _e("Edit", "mythings"); ?>" onclick=" this.form.act.value = 'edit'; this.form.submit();" />
  <input class="auto button delete"<?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?>
type="button" value="<?php _e("Delete", "mythings"); ?>" onclick="if(!confirm('<?php _e("Do you really want to delete this thing?", "mythings"); ?>')) return; this.form.act.value = 'remove'; this.form.submit();" />
  </p>
  <input type="hidden" name="act" />
  </form>
</div>

Repare no uso da função sprintf() para trabalhar com strings que são mescladas com variáveis PHP. Isso simplifica a string evitando confusões na hora de traduzir – pode ser que outras pessoas tentem fazer isso, inclusive gente que não conhece PHP.

Não esqueça coisas que podem parecer detalhes, como rótulos dos botões, texto de alerts e conffirms Javascript, etc.

Bem, se não havia suporte… não existe nenhum arquivo .POT ou .PO…

Você pode usar o já citado poEdit para criá-lo ou copiar o texto abaixo para um arquivo de texto:

msgid ""
msgstr ""
"Project-Id-Version: n"
"Report-Msgid-Bugs-To: n"
"POT-Creation-Date: n"
"PO-Revision-Date: n"
"Last-Translator: seu nome <[email protected]>n"
"Language-Team: n"
"MIME-Version: 1.0n"
"Content-Type: text/plain; charset=UTF-8n"
"Content-Transfer-Encoding: 8bitn"
"X-Poedit-KeywordsList: __;_en"
"X-Poedit-Basepath: .n"
"X-Poedit-SearchPath-0: .n"

Salve este arquivo como mythings-pt_BR.po na pasta-mãe do plugin. A extensão .PO já associa o arquivo ao poEdit. Abra com um duplo clique. Clique então em “Atualizar catálogo” e as strings que estiverem dentro de chamadas às funções de tradução irão aparecer como num passe de mágica!

Clique em Ok e inicie sua tradução. Ao terminar, salve o arquivo e saia. Você verá que o arquivo mythings-pt_BR.mo foi gerado. Coloque este arquivo no diretório que associamos ao nosso textdomain (./languages). Pronto!

Posts Similares

  • Elimine ou altere o "\author\" da página de autores no WordPress

    Por padrão o WordPress cria uma URL para cada autor, seguindo a estrutura /author/nome-do-usuario. Alguns podem querer modificar esta palavra author, pois se pensarmos em sites em português a expressão não se encaixa muito bem. Teremos também aqueles que, de acordo com o projeto desenvolvido, poderão querer apagar esta palavra e deixar o site mostrando…

  • Como inserir uma chamada em meu Feed Rss?

    Se você desejar inserir uma chamada ou anúncio para aparecer apenas no RSS, no final de cada post, insira o código abaixo em seu arquivo functions.php:   function insertFootNote($content) { if(!is_feed() && !is_home()) { $content.= “<h4>Gostou deste artigo?</h4>”; $content.= “<p>Inscreva-se em nosso <a href=’#’>feed RSS</a></p>”; } return $content; } add_filter (‘the_content’, ‘insertFootNote’); Tal técnica é…

  • 7 verificações para fazer antes de publicar seu site/blog em WordPress

    Durante esses dias, eu fiz alguns testes em vários blogs no meu trabalho (+/- uns 100). Encontrei erros absurdos e ao mesmo tempo muito bobos.  Por isso me auto-convidei para escrever aqui no Tudo Para WordPress e compartilhar essa experiência com vocês. Esse post é apenas um “checklist” de coisas que você pode testar e…

  • Segurança: Alterando o prefixo do Banco de Dados do WordPress

    Uma das coisas incríveis do WordPress é que ele é um sistema de publicação dinâmica, que utiliza um banco de dados para armazenar informações de seu site: posts, opções e configurações de plugins e temas – todos estes dados são armazenados no banco de dados do seu site. É como se fosse cérebro da sua…

  • Veja todas as novas funções do WordPress 3.8

    A cada nova versão do WordPress, novas funções interessantes vão surgindo. Vamos ver TODAS as novas funções do WordPress? A explicação de cada uma está em inglês, mas aos poucos vamos traduzindo para vocês 😉 get_theme_update_available Retrieve the update link if there is a theme update available. Will return a link if there is an…

  • Migração do WordPress: Movendo o WordPress com o PhpMyAdmin

    O WordPress tem um sistema bastante funcional de exportação/importação, mas quando usamos esse método para transportar nosso WordPress para uma nova hospedagem, por exemplo, importamos todos os posts e páginas com seus respectivos comentários, mas todo o resto (como plugins e configurações) é perdido. Entretanto, se temos o PhpMyAdmin instalado em ambos os servidores envolvidos…

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

13 Comentários

  1. Para aprender, tentei usar um tema (Default) do WordPress que, com certeza, está preparado para tradução (internacionalização). Entretanto, no Poedit, quando ciclo em “Atualizar Catálogo”, usando o arquivo publicado no final do post, apenas a opção “Atualizar catálogo pelo arquivo pot” está habilitada. A outra opção, “Atualizar a partir do código fonte” não fica disponível. Assim, o “passe de mágica” não acontece.Fiz algo errado? O que é necessário para criar um arquivo pot? Baixei o GetTex, mas não achei nenhum arquivo executável que me permitisse instalar o programa. Ficaria grato pela gentileza de um esclarecimento.

  2. Armando,
    não entendo por que não… fiquei na dúvida e refiz o processo, salvando o arquivo a partir do texto publicado aqui. Abri no poEdit e lá estava a opção habilitada! – Estou usando a versão 1.3.6.
    A propósito, “atualizar pelo arquivo POT” não lê as strings do código fonte, mas de um arquivo POT indicado…
    Se achar algo a respeito, publico aqui.

    1. Ótimo o texto, mas também só consegui fazer depois das dicas do Armando nos comentários. Como seu texto está bem posicionado no Google, você já poderia incluir as dicas dele direto no seu texto (citando o nome dele, claro), para que seja ainda mais fácil do visitante encontrar a informação que deseja 🙂

  3. Para abrir a opção “Atualizar catálogo a partir do código fonte” no Poedit, há uma série de detalhes que precisam ser seguidos. Encontrei-os no Codex do WordPress. No caso, refere-se à tradução de plugins, mas funciona do mesmo jeito com temas. Os passos são os seguintes:
    1. Start POEdit
    2. Click File -> New Catalog
    3. Enter a project name (probably your plugin’s file name)
    4. Click the Paths tab at the top
    5. Click the New Item icon (second one, looks like a little square)
    6. Enter the path to the directory containing your plugin file (“.” tells POEdit to scan the directory that you will save the file to), press enter
    7. Click the Keywords tab at the top
    8. Click the New Item icon
    9. Enter __ (that’s underscore underscore), press enter
    10. Click the New Item icon
    11. Enter _e (that’s underscore e), press enter
    12. Click Okay
    13. Choose a name for your .po file (probably your plugin’s base filename)

    Seguindo essa sequência, não tem erro. Mas quero deixar claro que, de qualquer forma, seu post me foi muito útil. Obrigado.

  4. Muito bom este post, havia procurado em vários lugares como alterar o texto " Nenhum Comentário" para apenas "Comentários" já havia editado e salvo o arquivo de tradução do tema diversas vezes e não funcionava. Com a dica do PoEdit foi batata.
    Vlw

  5. Muito obrigado por compartilhar estas dicas, nem acredito que eu traduzia themes procurando pelas strings em cada arquivo e parseando todas as palavras com acento. Levava horas para traduzir um theme e agora acabo de traduzir um em 15 minutos

    1. Não Renato, basta usar utf-8. Abra as configurações do catálogo e defina ‘Tabela de caracteres’ e ‘Conjunto de caracteres no codigo fonte’

  6. Legal. Consegui traduzir um tema premium que veio sem suporte para arquivos .mo .po e pedia para comprar o plugin wpml para fazer a tradução. Boa dica.