{"id":978,"date":"2018-08-26T20:19:44","date_gmt":"2018-08-26T23:19:44","guid":{"rendered":"https:\/\/felipeelia.com.br\/?p=978"},"modified":"2019-02-15T18:39:09","modified_gmt":"2019-02-15T21:39:09","slug":"como-funciona-a-traducao-no-wordpress","status":"publish","type":"post","link":"https:\/\/felipeelia.com.br\/como-funciona-a-traducao-no-wordpress\/","title":{"rendered":"Como funciona a tradu\u00e7\u00e3o no WordPress"},"content":{"rendered":"\n
A inten\u00e7\u00e3o deste texto \u00e9 servir como introdu\u00e7\u00e3o \u00e0 tradu\u00e7\u00e3o no WordPress, facilitando o entendimento dos conceitos b\u00e1sicos envolvidos no processo. Nada substitui a documenta\u00e7\u00e3o oficial de internacionaliza\u00e7\u00e3o de temas<\/a> e plugins<\/a>. No momento a vers\u00e3o do WordPress \u00e9 a 4.9.8, ent\u00e3o se voc\u00ea est\u00e1 lendo isso no futuro e alguma coisa ficou desatualizada, voc\u00ea pode me avisar<\/a>.<\/p>\n\n\n\n Al\u00e9m de tradu\u00e7\u00e3o, voc\u00ea vai encontrar dois termos importantes neste texto: internacionaliza\u00e7\u00e3o<\/strong> e localiza\u00e7\u00e3o<\/strong>. Internacionaliza\u00e7\u00e3o \u00e9 o processo de produzir um material, no nosso caso temas, plugins e o pr\u00f3prio WordPress, de forma que ele possa ser traduzido para v\u00e1rios grupos diferentes de pessoas. Localiza\u00e7\u00e3o \u00e9 o processo de transformar um material internacionalizado em algo de f\u00e1cil entendimento para pessoas de uma determinada cultura.<\/p>\n\n\n\n\n\n\n Os dois conceitos s\u00e3o complementares: internacionaliza\u00e7\u00e3o \u00e9 um movimento “pra fora”<\/strong>, alcan\u00e7ando o maior n\u00famero poss\u00edvel de pessoas, e localiza\u00e7\u00e3o \u00e9 um movimento “pra dentro”<\/strong>, focando em um n\u00famero mais especializado de indiv\u00edduos.<\/p>\n\n\n\n Em outros materiais voc\u00ea vai encontrar o termo internacionaliza\u00e7\u00e3o como i18n<\/em><\/strong>, porque existem 18 letras entre o i e o n da palavra em ingl\u00eas internationalization<\/em>. Da mesma forma localiza\u00e7\u00e3o tamb\u00e9m aparece com l10n<\/em><\/strong>, vindo de localization<\/em>.<\/p>\n\n\n\n \u00c9 f\u00e1cil deduzir porque o processo de localiza\u00e7\u00e3o \u00e9 importante quando voc\u00ea tem um tema ou plugin em ingl\u00eas e precisa dele em portugu\u00eas. \u00c9 o processo de internacionaliza\u00e7\u00e3o que muitas vezes \u00e9 dif\u00edcil de ser levado em considera\u00e7\u00e3o. Deixo aqui, ent\u00e3o, duas coisas para voc\u00ea pensar:<\/p>\n\n\n\n O item 2 tamb\u00e9m est\u00e1 relacionado com a sua reputa\u00e7\u00e3o.<\/strong> Algu\u00e9m que pegar um c\u00f3digo legado seu pode pensar que voc\u00ea fez aquilo de maneira desleixada, ou pior, por n\u00e3o saber como isso funciona ou sequer que isso existia.<\/p>\n\n\n\n Se voc\u00ea pensou “eu n\u00e3o ligo pro que os outros pensam de mim”, eu vou respeitar isso. S\u00f3 prefiro n\u00e3o ter que manter um c\u00f3digo que voc\u00ea fez xD<\/p>\n\n\n\n Para o core<\/em> as strings sempre ser\u00e3o em ingl\u00eas, mas para projetos privados as strings originais, presentes no c\u00f3digo, podem estar em portugu\u00eas mesmo.<\/p>\n\n\n\n Se a gente fosse crian\u00e7a, tudo seria resolvido com um monte de ifs<\/em>. Se o idioma do site \u00e9 ingl\u00eas imprime Read More<\/em>, se for portugu\u00eas imprime Leia mais<\/em>. O problema \u00e9 que pode aparecer a necessidade e traduzir pra espanhol e esses ifs<\/em> ficariam (ainda mais) bizarros. Para n\u00e3o ter esse problema, o WordPress usa o gettext<\/a>.<\/p>\n\n\n\n O b\u00e1sico do gettext<\/em> que vamos precisar entender agora \u00e9 como os arquivos funcionam.<\/p>\n\n\n\n Os arquivos .po<\/em> (PO de Portable Object<\/em>) s\u00e3o os arquivos edit\u00e1veis. S\u00e3o arquivos de texto, com as strings de origem, normalmente em ingl\u00eas, e as de destino, no nosso caso em portugu\u00eas, sendo um arquivo .po<\/em> por idioma. Embora eles sejam arquivos de texto aberto \u00e9 altamente recomendado edit\u00e1-los com programas espec\u00edficos como o PoEdit<\/a> e o OmegaT<\/a>. A Sheila Gomes<\/a>, minha refer\u00eancia de tradu\u00e7\u00e3o aqui no Brasil, oferece bastante material sobre o OmegaT<\/a>.<\/p>\n\n\n\n Voc\u00ea tamb\u00e9m pode traduzir atrav\u00e9s da pr\u00f3pria interface do WordPress com um plugin chamado Loco Translate<\/a>. Falei sobre ele em um meetup h\u00e1 dois anos, pode ser que os slides<\/a> ajudem em alguma coisa.<\/p>\n\n\n\n Al\u00e9m de todas essas, existe ainda a melhor forma de traduzir, que \u00e9 atrav\u00e9s da ferramenta oficial<\/a>. Pretendo fazer um outro texto falando s\u00f3 sobre isso, mas o importante aqui \u00e9 que para traduzir dessa forma voc\u00ea deve seguir o nosso Guia de tradu\u00e7\u00e3o<\/a>.<\/p>\n\n\n\n No fim das contas, s\u00e3o esses os arquivos que contam. MO, de Machine Object<\/em>, s\u00e3o arquivos bin\u00e1rios gerados a partir dos arquivos .po<\/em> e que s\u00e3o lidos pelo servidor.<\/p>\n\n\n\n Embora sejam eles os arquivos que realmente importem, sempre mantenha o par .po\/.mo<\/em><\/strong>. S\u00e3o arquivos extremamente leves e ter o .po<\/em> \u00e0 m\u00e3o para gerar um novo .mo<\/em> pode salvar a sua vida.<\/p>\n\n\n\n Promete que nunca vai separar os dois arquivos, n\u00e9?<\/p>\n\n\n\n Menos famoso, mas n\u00e3o menos importante, esse formato \u00e9 o modelo para gerar novos arquivos .po<\/em>, ou seja, se um plugin ou tema tem um arquivo .pot<\/em>, voc\u00ea pode criar um .po<\/em> a partir dele, sem ter que analisar o c\u00f3digo atr\u00e1s de chamadas para as fun\u00e7\u00f5es de tradu\u00e7\u00e3o. Nos arquivos .pot<\/em> s\u00f3 existem as strings originais.<\/p>\n\n\n\n Como exemplo, vamos pensar dentro do loop, no tema, um link para a interna do post:<\/p>\n\n\n\n Para deix\u00e1-lo traduz\u00edvel, seria s\u00f3 usar:<\/p>\n\n\n\n Viu o O text domain<\/em>, ou o dom\u00ednio do texto, serve para agrupar as strings por tema ou plugin e separ\u00e1-los do core<\/em>, que usa Na pr\u00e1tica o seu dom\u00ednio pode at\u00e9 ser qualquer texto, mas sempre use o slug do seu tema ou plugin<\/strong>. Para temas e plugins hospedados nos reposit\u00f3rios do WordPress isso \u00e9 ainda mais fundamental: se o text domain<\/em> n\u00e3o for igual ao plugin, por exemplo, ele n\u00e3o ser\u00e1 traduz\u00edvel pelo GlotPress.<\/p>\n\n\n\n Como esse \u00e9 um par\u00e2metro aceito por todas as fun\u00e7\u00f5es de tradu\u00e7\u00e3o, voc\u00ea pode se sentir tentado a usar uma constante ou uma vari\u00e1vel. N\u00e3o fa\u00e7a isso. Use sempre o formato de texto, assim voc\u00ea aumenta a compatibilidade do seu c\u00f3digo com algumas ferramentas de an\u00e1lise. <\/p>\n\n\n\n No exemplo vimos a fun\u00e7\u00e3o Os nomes das fun\u00e7\u00f5es de tradu\u00e7\u00e3o do WordPress seguem alguns padr\u00f5es:<\/p>\n\n\n N\u00e3o confunda esse uso do underscore no nome da fun\u00e7\u00e3o com as fun\u00e7\u00f5es privadas, como a As fun\u00e7\u00f5es de tradu\u00e7\u00e3o que cont\u00e9m a letra O WooCommerce, como outro exemplo, usa Algumas strings tem conte\u00fado vari\u00e1vel e, por isso, precisamos usar placeholders<\/em>, isto \u00e9, vari\u00e1veis que reservar\u00e3o o espa\u00e7o da informa\u00e7\u00e3o que n\u00e3o temos no momento para que ela seja substitu\u00edda quando for a hora.<\/p>\n\n\n O WooCommerce tem um exemplo \u00fatil: Quando precisamos de mais de um placeholder, o certo \u00e9 numer\u00e1-los. Veja a string \u00c9 na substitui\u00e7\u00e3o desses placeholders pelas informa\u00e7\u00f5es reais que as fun\u00e7\u00f5es Nem sempre \u00e9 \u00f3bvio o que ser\u00e1 substitu\u00eddo em uma string e, para ajudar, \u00e9 poss\u00edvel colocar uma nota. Para isso basta colocar um coment\u00e1rio PHP na linha anterior \u00e0 chamada da fun\u00e7\u00e3o e come\u00e7ar seu texto com Se voc\u00ea programa usando o WordPress Coding Standards<\/a>, j\u00e1 deve ter notado que ele reclama quando n\u00e3o h\u00e1 coment\u00e1rios nesses casos.<\/p>\n\n\n\n Outro exemplo de quando \u00e9 preciso substituir placeholders s\u00e3o as fun\u00e7\u00f5es de plural. A fun\u00e7\u00e3o mais simples, Um detalhe importante aqui: se voc\u00ea est\u00e1 fazendo c\u00f3digo para o core<\/em> ou tem alguma inten\u00e7\u00e3o de alcan\u00e7ar o p\u00fablico russo ou de idiomas eslavos, n\u00e3o considere essa fun\u00e7\u00e3o para cen\u00e1rios de um ou muitos<\/em>. Deixei isso explicado em um coment\u00e1rio l\u00e1 na documenta\u00e7\u00e3o<\/a>.<\/p>\n\n\n\n A forma mais f\u00e1cil de criar um arquivo de modelo de tradu\u00e7\u00e3o do seu tema ou plugin \u00e9 atrav\u00e9s do wp-cli<\/a>. Na vers\u00e3o 2.0.1, a mais recente enquanto eu escrevo esse texto, \u00e9 s\u00f3 voc\u00ea entrar na pasta do seu plugin, por exemplo, e executar:<\/p>\n\n\n\n Como eu expliquei no come\u00e7o, a partir desse arquivo voc\u00ea pode gerar o arquivo .po<\/em> do locale<\/em> para o qual voc\u00ea pretende traduzir.<\/p>\n\n\n\n Por padr\u00e3o, o WordPress l\u00ea os arquivos .po<\/em> da pasta wp-content\/languages<\/a>, cuidando assim das coisas baixadas atrav\u00e9s do reposit\u00f3rio. Se voc\u00ea est\u00e1 fazendo um plugin ou um tema para um cliente, voc\u00ea precisar\u00e1 dizer ao WP onde ele deve buscar os arquivos com as tradu\u00e7\u00f5es.<\/p>\n\n\n\n Para plugins voc\u00ea deve associar ao hook Isso far\u00e1 o WordPress procurar pelo arquivo Para alguns plugins maiores, a fun\u00e7\u00e3o Para temas o esquema \u00e9 parecido, mas voc\u00ea deve associar ao hook Existe uma outra diferen\u00e7a muito importante<\/strong>: para temas, diferente do que acontece nos plugins, o nome do arquivo deve conter apenas o locale<\/em> e a extens\u00e3o. Ent\u00e3o, por exemplo, o arquivo seria Em algumas situa\u00e7\u00f5es o WordPress precisa das tradu\u00e7\u00f5es dos temas e plugins sem executar seus c\u00f3digos. Exemplos disso s\u00e3o as listas do reposit\u00f3rio e a lista de plugins no Painel, que exibe os nomes e descri\u00e7\u00f5es traduzidos mesmo dos plugins que est\u00e3o desativados.<\/p>\n\n\n\n Para resolver \u00e9 simples: tanto para temas quanto para plugins, voc\u00ea precisa definir no cabe\u00e7alho dois valores: Text Domain<\/em> e Domain Path<\/em>. Um exemplo:<\/p>\n\n\n\n A vinda do Gutenberg<\/a> est\u00e1 alterando a forma como a API do WordPress lida com tradu\u00e7\u00f5es, mas por enquanto tudo \u00e9 feito atrav\u00e9s de PHP. Por isso, a forma mais simples de traduzir um texto usado em um arquivo js \u00e9 usar a fun\u00e7\u00e3o wp_localize_script<\/a>.<\/p>\n\n\n Vamos pensar na string voc\u00ea pode associar a tradu\u00e7\u00e3o da string \u00e0 uma vari\u00e1vel logo depois de incluir o arquivo JavaScript. No PHP use<\/p>\n\n\n\n e no js use<\/p>\n\n\n\n \u00c9 um texto gigante, eu sei. Se achou algum erro n\u00e3o deixe de avisar<\/a> e se voc\u00ea gostou n\u00e3o se esque\u00e7a de comentar e de se cadastrar na newsletter.<\/p>\n","protected":false},"excerpt":{"rendered":" Tudo o que precisa saber sobre como funcionam as tradu\u00e7\u00f5es no WordPress.<\/p>\n","protected":false},"author":1,"featured_media":988,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[30],"tags":[53],"yoast_head":"\nPor
O b\u00e1sico do
Arquivos .po<\/em><\/h3>\n\n\n\n
Arquivos .mo<\/em><\/h3>\n\n\n\n
Arquivos .pot<\/em><\/h3>\n\n\n\n
Na pr\u00e1tica,
<a href=\"<?php the_permalink(); ?>\">Read More<\/a><\/code><\/pre>\n\n\n\n
<a href=\"<?php the_permalink(); ?>\"><?php _e( 'Read More', 'meu-tema' ); ?><\/a><\/code><\/pre>\n\n\n
meu-tema<\/code> ali? Esse \u00e9 o text domain<\/em>.<\/p>\n\n\n
Definindo o text domain<\/em><\/h3>\n\n\n
default<\/code> como dom\u00ednio.<\/p>\n\n\n
Fun\u00e7\u00f5es de tradu\u00e7\u00e3o no WordPress<\/h2>\n\n\n
_e()<\/code>, que imprime uma string traduzida. Ela faz par com a fun\u00e7\u00e3o
__()<\/code>, que n\u00e3o imprime, mas retorna uma string traduzida. Elas funcionam como as fun\u00e7\u00f5es
printf<\/code> e
sprintf<\/code>, nativas do PHP e que tamb\u00e9m ser\u00e3o \u00fateis daqui a pouco.<\/p>\n\n\n
\n
__()<\/code> e
esc_attr__()<\/code>;<\/li>\n
e<\/code> imprimem a string. Exemplos:
_e()<\/code> e
esc_attr_e()<\/code>;<\/li>\n
n<\/code> est\u00e3o relacionadas com plural. Exemplos:
_n()<\/code> e
_n_noop()<\/code>;<\/li>\n
x<\/code> permitem informar um contexto para desambigua\u00e7\u00e3o. Exemplos:
_x()<\/code> e
_ex()<\/code>.<\/li>\n<\/ul>\n
_wp_get_current_user()<\/code>. As fun\u00e7\u00f5es de tradu\u00e7\u00e3o que come\u00e7am com
_<\/code> tem nome realmente curtos, caso contr\u00e1rio ele \u00e9 colocado depois, como no grupo de fun\u00e7\u00f5es relacionadas \u00e0
esc_attr__()<\/code>.<\/p>\n\n\n
O que \u00e9 contexto<\/h3>\n\n\n
x<\/code> permitem que voc\u00ea fa\u00e7a a desambigua\u00e7\u00e3o de um termo ou express\u00e3o. O core<\/em>, por exemplo, usa
_x( 'Add New', 'post' )<\/code> e
_x( 'Add New', 'page' )<\/code>. Um vira
Adiconar novo<\/code>, o outro
Adicionar nova<\/code>. Note que nas chamadas \u00e0s fun\u00e7\u00f5es de tradu\u00e7\u00e3o feitas pelo core<\/em> n\u00e3o h\u00e1 nenhum text domain<\/em>.<\/p>\n
_x( 'product', 'default-slug', 'woocommerce' )<\/code>. Note que aqui j\u00e1 temos o dom\u00ednio como terceiro par\u00e2metro.<\/p>\n\n\n
Placeholders<\/em> e vari\u00e1veis<\/h3>\n\n\n\n
__( 'Order number: %s', 'woocommerce' )<\/code>. Aqui, como na hora de traduzir a gente ainda n\u00e3o sabe o n\u00famero do pedido, o
%s<\/code> serve s\u00f3 para guardar o lugar. Normalmente se usa o
%s<\/code> mesmo, mas outros formatos s\u00e3o descritos na documenta\u00e7\u00e3o da sprintf<\/a>.<\/p>\n
__( '%1$s created the group %2$s', 'buddypress')<\/code>, por exemplo. Dessa forma, numerando as marca\u00e7\u00f5es, \u00e9 poss\u00edvel traduzir como
O grupo %2$s foi criado pelo usu\u00e1rio %1$s<\/code>, usando o segundo valor antes do primeiro.<\/p>\n
sprintf<\/code> e
printf<\/code> ser\u00e3o \u00fateis. Neste \u00faltimo exemplo do BuddyPress, o c\u00f3digo completo \u00e9:<\/p>\n\n\n
$action = sprintf( __( '%1$s created the group %2$s', 'buddypress'), $user_link, $group_link );<\/code><\/pre>\n\n\n\n
Adicionando notas para os tradutores<\/h4>\n\n\n
translators:<\/code>, dessa forma:<\/p>\n\n\n
\/* translators: %s: Weight unit *\/\nsprintf( __( 'Weight (%s)', 'woocommerce' ), $weight_unit ) => 'weight',<\/code><\/pre>\n\n\n\n
Plurais<\/h3>\n\n\n
_n()<\/code>, aceita 4 par\u00e2metros: a string no singular, a string no plural, o n\u00famero que ser\u00e1 usado para decidir e o text domain<\/em>. Na documenta\u00e7\u00e3o existe um exemplo bem f\u00e1cil para entender:<\/p>\n\n\n
$rating = '3';\n\n$text = sprintf( _n( '%s star', '%s stars', $rating, 'wpdocs_textdomain' ), $rating );<\/code><\/pre>\n\n\n\n
Resum\u00e3o das fun\u00e7\u00f5es<\/h3>\n\n\n\n
B\u00e1sicas<\/h4>\n\n\n\n
Fun\u00e7\u00f5es com escape<\/h4>\n\n\n
\n
return esc_html( translate( $text, $domain ) )<\/code>;<\/li>\n
echo esc_html( translate( $text, $domain ) )<\/code>; <\/li>\n
Data e n\u00famero<\/h4>\n\n\n
\n
echo number_format_i18n( 15150.3333, 2 );<\/code> imprime
15.150,33<\/code>.<\/li>\n
date<\/code> nativa do PHP e um timestamp, retorna a data traduzida e, por padr\u00e3o, no fuso-hor\u00e1rio definido no Painel -> Geral. Para manter a consist\u00eancia do seu c\u00f3digo e nunca depender da configura\u00e7\u00e3o do servidor, eu recomendo que voc\u00ea sempre use
date_i18n()<\/code> ao inv\u00e9s de
date()<\/code>.<\/li>\n<\/ul>\n\n\n
Como criar o arquivo .pot<\/em> do seu plugin ou tema<\/h2>\n\n\n\n
wp i18n make-pot . languages\/slug-do-seu-plugin.pot<\/code><\/pre>\n\n\n\n
Carregando os
Plugins<\/h4>\n\n\n
plugins_loaded<\/code> uma chamada \u00e0 fun\u00e7\u00e3o load_plugin_textdomain<\/a>:<\/p>\n\n\n
function seu_plugin_load_plugin_textdomain() {\n load_plugin_textdomain( 'seu-text-domain-aqui', false, basename( dirname( __FILE__ ) ) . '\/languages\/' );\n}\nadd_action( 'plugins_loaded', 'seu_plugin_load_plugin_textdomain' );<\/code><\/pre>\n\n\n
{text-domain}-{locale}.mo<\/code> na pasta languages do seu plugin. No nosso exemplo, para portugu\u00eas do Brasil, o arquivo seria o
wp-content\/plugins\/teste\/languages\/teste-pt_BR.mo<\/code>.<\/p>\n
seu_plugin_load_plugin_textdomain()<\/code> poderia ser um pouco mais elaborada. O BuddyPress, por exemplo, oferece o filtro buddypress_locale_locations<\/a> na fun\u00e7\u00e3o bp_core_load_buddypress_textdomain<\/a> para que os desenvolvedores possam carregar tradu\u00e7\u00f5es personalizadas sem ter que alterar nem o plugin nem a tradu\u00e7\u00e3o presente no reposit\u00f3rio.<\/p>\n\n\n
Temas<\/h4>\n\n\n
after_setup_theme<\/code> uma chamada \u00e0 fun\u00e7\u00e3o load_theme_textdomain<\/a> ou load_child_theme_textdomain<\/a>, se estiver usando um tema descendente<\/a> (nome atual para os temas filhos):<\/p>\n\n\n
function seu_tema_load_theme_textdomain() {\n load_theme_textdomain( 'seu-text-domain-aqui', get_stylesheet_directory() . '\/languages' );\n}\nadd_action( 'after_setup_theme', 'seu_tema_load_theme_textdomain' );<\/code><\/pre>\n\n\n
wp-content\/themes\/teste\/languages\/pt_BR.mo<\/code>.<\/p>\n\n\n
Tradu\u00e7\u00f5es de temas e plugins desativados<\/h3>\n\n\n\n
\/*\n * Theme Name: Meu tema\n * Author: Felipe Elia\n * Text Domain: meu-tema\n * Domain Path: \/languages\n *\/<\/code><\/pre>\n\n\n\n
Textos em arquivos JavaScript<\/h2>\n\n\n\n
Loading...<\/code>, que pode substituir o conte\u00fado de alguma coisa durante uma chamada AJAX. Ao inv\u00e9s de usar<\/p>\n\n\n
$( '#minha-div' ).html( 'Loading...' );<\/code><\/pre>\n\n\n\n
wp_enqueue_script( 'script-do-tema', get_template_directory_uri() . '\/assets\/js\/scripts.js' );\nwp_localize_script( 'script-do-tema', 'tema_l10n',\n\tarray(\n\t\t'loading' => __( 'Loading...', 'meu-tema' ),\n\t)\n);<\/code><\/pre>\n\n\n\n
$( '#minha-div' ).html( tema_l10n.loading );<\/code><\/pre>\n\n\n\n
\n\n\n\n