25 setembro 2010

Como traduzir temas e plugins?

Feed
Assine nosso Feed ou receba os artigos por email

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:
&lt;?php
  if(!empty($_POST['act'])) {
  ?&gt;&lt;div class="updated fade"&gt;&lt;p&gt;&lt;?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!";
  }
  ?&gt;&lt;/p&gt;&lt;/div&gt;&lt;?php
?&gt;
&lt;h2&gt;My things&lt;/h2&gt;
&lt;div class="wrap"&gt;
  &lt;form action="&lt;?php print $PHP_SELF; ?&gt;" method="post"&gt;
  &lt;label&gt;Your cool things:
  &lt;select name="mythings"&gt;
  &lt;?php foreach($coolthings as $things) { ?&gt;
  &lt;option value="&lt;?php print sanitize_title($things['thing_title']); ?&gt;"&gt;&lt;?php print $things['thing_title']; ?&gt;&lt;/option&gt;
  &lt;?php } ?&gt;
  &lt;/select&gt;
  &lt;/label&gt;
  &lt;p class="butts"&gt;
  &lt;input type="button" class="button auto"&lt;?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?&gt; 
value="Edit" onclick=" this.form.act.value = 'edit'; this.form.submit();" /&gt;
  &lt;input class="auto button delete"&lt;?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?&gt; 
value="&lt;?php _e("Edit", "mythings"); ?&gt;"type="button" value="Delete" onclick="if(!confirm('Do you really want to delete this thing?')) 
return; this.form.act.value = 'remove'; this.form.submit();" /&gt;
  &lt;/p&gt;
  &lt;input type="hidden" name="act" /&gt;
  &lt;/form&gt;
&lt;/div&gt;
Veja abaixo o mesmo código, já com o suporte à internacionalização:
&lt;?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']);
    ?&gt;
    &lt;div class="updated fade"&gt;
    &lt;p&gt;&lt;?php print $msg; ?&gt;&lt;/p&gt;
    &lt;/div&gt;
    &lt;?php
  }
?&gt;
 
&lt;?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
?&gt;
 
&lt;h2&gt;&lt;?php _e("My things", "mythings"); ?&gt;&lt;/h2&gt;
&lt;div class="wrap"&gt;
  &lt;form action="&lt;?php print $PHP_SELF; ?&gt;" method="post"&gt;
  &lt;label&gt;&lt;?php _e("Your cool things:", "mythings"); ?&gt;
  &lt;select name="mythings"&gt;
  &lt;?php foreach($coolthings as $things) { ?&gt;
  &lt;option value="&lt;?php print sanitize_title($things['thing_title']); ?&gt;"&gt;&lt;?php print $things['thing_title']; ?&gt;&lt;/option&gt;
  &lt;?php } ?&gt;
  &lt;/select&gt;
  &lt;/label&gt;
  &lt;p class="butts"&gt;
  &lt;input type="button" class="button auto"&lt;?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?&gt; 
value="&lt;?php _e("Edit", "mythings"); ?&gt;" onclick=" this.form.act.value = 'edit'; this.form.submit();" /&gt;
  &lt;input class="auto button delete"&lt;?php if(count($coolthings) == 0) print ' disabled="disabled"'; ?&gt; 
type="button" value="&lt;?php _e("Delete", "mythings"); ?&gt;" onclick="if(!confirm('&lt;?php _e("Do you really want to delete this thing?", "mythings"); ?&gt;')) return; this.form.act.value = 'remove'; this.form.submit();" /&gt;
  &lt;/p&gt;
  &lt;input type="hidden" name="act" /&gt;
  &lt;/form&gt;
&lt;/div&gt;
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 &lt;seu@email.com&gt;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!

Seja o primeiro a comentar

Postar um comentário


Web Statistics

  ©BLOG Dicas e Tutoriais | Truques, Templates, Gadgets e muito mas. - Todos os direitos reservados.

Template by Dicas e Tutorias Blog. | Topo  

BlogBlogs.Com.Br