wp-plugin-i18n-release

Prepara um plugin WordPress hospedado em plugins.svn.wordpress.org para internacionalização completa e publica o release no SVN. Abrange: reescrever strings hardcoded do espanhol para inglês (PHP e JS), gerar .pot com wp-cli, criar tradução inicial .po/.mo (ex: espanhol neutro), incrementar versão sincronizada, commitar no trunk e criar tag. Use quando o plugin acabou de ser aprovado no WP.org, quando as strings traduzíveis estão em espanhol e bloqueiam outras locales, ou quando deseja adicionar/atualizar traduções e enviar um novo release.

O que é wp-plugin-i18n-release?

Esta skill automatiza o processo de internacionalização (i18n) e publicação de plugins WordPress hospedados no repositório SVN oficial (plugins.svn.wordpress.org). Ela é ideal para situações onde um plugin aprovado no WP.org possui strings hardcoded em espanhol que precisam ser migradas para inglês, permitindo que outras comunidades de tradução (como francês, alemão, português) possam contribuir via GlotPress. A skill também cria o arquivo .pot com wp-cli, gera as traduções iniciais .po/.mo (por exemplo, para espanhol neutro), incrementa a versão do plugin, e realiza o commit no trunk e a criação de tag no SVN. Ela é compatível com ambientes Windows (PowerShell) que tenham TortoiseSVN, PHP e wp-cli, e inclui regras detalhadas para reescrita de strings, tratamento de codificação de caracteres, e boas práticas de release no SVN.

Funciona com~Claude Code~Codex CLI~Cursor✓OpenCode
npx skills add https://github.com/leoarayas/agent-skills/tree/main/wp-plugin-i18n-release

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação


name: wp-plugin-i18n-release description: Prepara um plugin WordPress hospedado em plugins.svn.wordpress.org para internacionalização completa e publica o release no SVN. Abrange: reescrever strings hardcoded do espanhol para inglês (PHP e JS), gerar .pot com wp-cli, criar tradução inicial .po/.mo (ex: espanhol neutro), incrementar versão sincronizada, commitar no trunk e criar tag. Use quando o plugin acabou de ser aprovado no WP.org, quando as strings traduzíveis estão em espanhol e bloqueiam outras locales, ou quando deseja adicionar/atualizar traduções e enviar um novo release.

Plugin WordPress: i18n completo + Release ao SVN do .org

Quando usar

  • Plugin recém-aprovado no WP.org que precisa estar pronto para translate.wordpress.org.
  • Strings traduzíveis (__(), _e(), wp.i18n.__(), etc.) estão hardcoded em espanhol e bloqueiam outras locales (fr, de, pt, ja, etc.) no GlotPress.
  • É necessário adicionar uma tradução inicial ou um .pot pela primeira vez.
  • Qualquer release que incrementa versão e é publicada no SVN de plugins.svn.wordpress.org.

Pré-requisitos

ComponenteComo verificarSe faltar
TortoiseSVN (svn.exe)Test-Path "C:\Program Files\TortoiseSVN\bin\svn.exe"Instalar de https://tortoisesvn.net/downloads.html (marcar "command line tools")
PHP 8.x no PATHphp --version a partir de um dir não-UNCHerd legacy está ok; ou PHP standalone
wp-cli.pharBaixar de https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.pharInvoke-WebRequest -Uri ... -OutFile wp-cli.phar
Acesso SVN ao pluginEmail de aprovação de [email protected] com URL SVN, username e passwordSolicitar à equipe se faltar

Fluxo de trabalho completo

  1. Verificar ambiente e plugin
  2. Fazer checkout do SVN do plugin
  3. Localizar strings traduzíveis no código (PHP + JS)
  4. Reescrever strings para inglês mantendo semântica técnica
  5. Regenerar .pot com wp i18n make-pot
  6. Gerar .po + .mo da tradução inicial (ex: espanhol neutro)
  7. Incrementar versão sincronizada (header Version, constante, stable tag no readme, CHANGELOG)
  8. Regenerar build limpo excluindo arquivos de desenvolvimento (.distignore)
  9. Commit ao trunk + criar tag no SVN
  10. (Opcional) Sincronizar o diretório local preservando arquivos do Git

1. Verificar ambiente e plugin

# SVN no Windows (geralmente não está no PATH; usar caminho completo ou adicionar)
$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"
svn --version

# PHP
php --version

Antes de tocar no código, ler o plugin:

  • Plugin header (Name, Version, Text Domain, Domain Path)
  • readme.txt (Stable tag, Changelog)
  • CHANGELOG.md (se existir)
  • Lista de arquivos PHP/JS em includes/ e assets/

2. Checkout do SVN

$svnUser = '<wp-org-user>'       # username WP.org (case-sensitive)
$svnPass = 'svn_XXXXX...'     # de "Account & Security" em profiles.wordpress.org
$slug    = 'meu-plugin'        # o slug do plugin
$checkout = "C:\Temp\opencode\$slug-svn"

svn checkout --non-interactive --trust-server-cert `
  --username $svnUser --password $svnPass `
  "https://plugins.svn.wordpress.org/$slug" $checkout

O SVN já contém trunk/, tags/, assets/. Se você acabou de enviar o primeiro release, tags/ estará vazio e trunk/ também (recém-criado com o trunk inicial).

3. Localizar strings traduzíveis

Identifique TODAS as chamadas para funções i18n. Use grep com estes padrões:

# PHP
Select-String -Path 'includes/*.php' -Pattern '__\(|_e\(|_x\(|esc_html__\(|esc_attr__\(|esc_html_e\(|esc_attr_e\(|_n\(|_nx\('

# JS (wp.i18n ou strings hardcoded sem função)
Select-String -Path 'assets/js/*.js' -Pattern 'wp\.i18n|__\('
Select-String -Path 'assets/js/*.js' -Pattern '"[^"]{6,80}"'   # detecta strings hardcoded

Anti-padrão comum: strings em espanhol dentro de HTML concatenado em JS (ex: html += '<option>Todas</option>';). Reescrever para inglês diretamente é válido; usar wp.i18n.__() é melhor se você quiser que sejam traduzíveis em tempo de execução.

4. Reescrever strings para inglês

Regras estritas:

  1. SOMENTE traduza o primeiro argumento das funções i18n. NÃO toque no text-domain (segundo argumento).
  2. NÃO toque em nada fora de uma chamada i18n.
  3. NÃO modifique o formato PHP (espaços, aspas, parênteses).
  4. Termos técnicos como ExampleStore, WooCommerce, API, Correlation ID, Premium, JSON, HTTP, URL, CLP permanecem em inglês (já são nomes próprios / siglas).
  5. PRESERVE placeholders de sprintf/printf (%s, %d, %1$s, etc.) exatamente como estão.
  6. Se uma string já está em inglês, NÃO a toque.

Tabela de tradução recomendada (ajuste ao contexto):

EspanholInglês
"Activo""Enabled"
"Título""Title"
"Configurar""Configure"
"Guardar""Save"
"Eliminar""Delete"
"Comuna""Commune"
"Región""Region"
"Selecciona una región""Select a region"
"Sin reglas guardadas""No saved rules"
"Configura la URL de API y el token""Configure API URL and token"
"Diagnóstico ejecutado correctamente""Diagnostic executed successfully"
"No se pudo {verbo}""Could not {verbo}"
"Debes ingresar X""You must enter X"
"Cotización API recibida""API quote received"
"Tarifa por defecto (CLP)""Default rate (CLP)"

Para arquivos grandes, delegue a um agente com instruções precisas e a tabela de traduções.

5. Regenerar .pot com wp-cli

# Do diretório do plugin (espelho local)
$wpCli = 'C:\Temp\opencode\wp-cli.phar'
php $wpCli i18n make-pot . "languages/meu-plugin.pot" --domain="meu-plugin"

O .pot deve estar em /languages/ com o nome <text-domain>.pot.

6. Criar tradução .po + .mo

Decisão chave: qual locale? Para que o plugin seja traduzido automaticamente para todos os falantes de espanhol sem variantes regionais, use <text-domain>-es.po (raiz es). O WordPress faz fallback em cascata: es_CL → es → inglês.

Estratégia recomendada:

  • es.po/.mo (raiz) → atende es_CL, es_ES, es_MX, es_AR, es_PE, etc.
  • (Opcional depois) es_CL.po/.mo específico para Chile com regionalismos.

Gerar o .po: use um script PHP que leia o .pot e aplique um array de traduções EN→ES. Use mb_chr(0x00f3) para caracteres acentuados (PHP no Windows tem problemas de codepage com literais UTF-8).

// build-es.php
declare(encoding='UTF-8');
$pot = file_get_contents('languages/meu-plugin.pot');
$po_es = 'languages/meu-plugin-es.po';

// Pré-carregar `mb_chr` para caracteres acentuados
function A() { return mb_chr(0x00e1); } // á
function E() { return mb_chr(0x00e9); } // é
function O() { return mb_chr(0x00f3); } // ó
// etc.

$t = [
    "Configure API URL and token" => "Configura la URL de API y el token",
    // ...
];

$header = "msgid \"\"\nmsgstr \"\"\n"
        . "\"Project-Id-Version: Meu Plugin 1.0.0\\n\"\n"
        . "\"Language-Team: Español (neutro) <>\\n\"\n"
        . "\"Language: es\\n\"\n"
        . "\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n";

// Parsear .pot, preencher msgstr com $t, escrever .po
// (lógica completa: ver wp-plugin-i18n-release/build-es.php)

Compilar .mo:

php $wpCli i18n make-mo languages/

Isso cria automaticamente languages/<text-domain>-es.mo.

7. Incrementar versão sincronizada

Atualize quatro arquivos:

// 1. meu-plugin.php (header)
 * Version: 1.1.0

// 2. meu-plugin.php (constante)
define('PLUGIN_VERSION', '1.1.0');

// 3. readme.txt
Stable tag: 1.1.0
/* + nova entrada em == Changelog == */

// 4. CHANGELOG.md
## [1.1.0] - 2026-09-14
### Changed
- ...

8. Regenerar build limpo (excluindo arquivos de desenvolvimento)

O .distignore do plugin define o que excluir. Se não existir, use esta lista padrão:

$exclude = @('.git','.github','.gitignore','.gitattributes','.distignore',
             '.idea','.DS_Store','.release-build','bin','node_modules',
             'tests','vendor/bin','phpcs.xml*','phpunit.xml*',
             '.phpunit.result.cache','*.lock','*.log','*.zip',
             'README.md','RELEASING.md','Makefile','.pr-body-*.md')

# Limpar destino, copiar tudo, remover excluídos
$src  = 'caminho/espelho/plugin'
$dist = 'caminho/build/plugin'
Copy-Item -Path "$src\*" -Destination $dist -Recurse -Force
foreach ($pat in $exclude) {
  Get-ChildItem -Path $dist -Recurse -Force -Filter $pat -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force
}

9. Commit ao trunk + criar tag

$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"

# Substituir trunk pelo build limpo
$trunk = "C:\Temp\opencode\$slug-svn\trunk"
Get-ChildItem -Path $trunk -Force | Where-Object { $_.Name -ne '.svn' } | Remove-Item -Recurse -Force
Copy-Item -Path "$dist\*" -Destination $trunk -Recurse -Force

# svn add + commit
Set-Location $trunk
svn add --force --depth infinity .

svn commit --non-interactive --trust-server-cert `
  --username $svnUser --password $svnPass `
  -m "Release 1.1.0: English source strings, full i18n rewrite, initial Spanish (es) translation"

# Criar tag (svn copy remoto)
svn copy --non-interactive --trust-server-cert `
  --username $svnUser --password $svnPass `
  -m "Tagging 1.1.0" `
  "https://plugins.svn.wordpress.org/$slug/trunk" `
  "https://plugins.svn.wordpress.org/$slug/tags/1.1.0"

Após tag remoto, forçar atualização local (as novas tags não são materializadas completamente na working copy):

svn cleanup
svn update --depth infinity --force --non-interactive --trust-server-cert `
  --username $svnUser --password $svnPass "$trunk/../"

10. (Opcional) Sincronizar diretório local

Preservar arquivos que NÃO estão no SVN export (.git/, .gitignore, tests/, bin/, etc.):

$local    = 'C:\Users\...\wp-content\plugins\meu-plugin'
$exportDir = 'C:\Users\...\export-1.1.0'

# Export da tag recém-criada
svn export "https://plugins.svn.wordpress.org/$slug/tags/1.1.0" $exportDir

# Remover APENAS os arquivos do release do local, manter o resto
$releaseNames = (Get-ChildItem $exportDir -Force).Name
foreach ($name in $releaseNames) {
  $localPath = Join-Path $local $name
  if (Test-Path $localPath) { Remove-Item $localPath -Recurse -Force }
}

# Copiar o export sobre o local
Copy-Item -Path "$exportDir\*" -Destination $local -Recurse -Force

11. Verificação final

# Versões no local
Write-Host "header: " (Get-Content "$local\meu-plugin.php" | Select-String -Pattern 'Version:' | Select-Object -First 1).ToString().Trim()
Write-Host "readme: " (Get-Content "$local\readme.txt" | Select-String -Pattern 'Stable tag:' | Select-Object -First 1).ToString().Trim()

# Verificar SVN remoto
svn ls --recursive "https://plugins.svn.wordpress.org/$slug/tags/1.1.0/"

Convenções críticas do WordPress.org

ConceitoRegra
Text DomainDeve ser idêntico ao slug do plugin (ex: meu-plugin). Nunca mude o slug sem re-aprovar o plugin.
Domain Path/languages/ para que o WP.org encontre o .pot
Plugin Name headerNão incluir marcas registradas de terceiros no início (revisão WP.org rejeita)
readme.txtWP.org interpreta este arquivo (não README.md). O Stable tag deve coincidir com a versão.
SVN layout/trunk/ (desenvolvimento), /tags/X.Y.Z/ (releases), /assets/ (banners, ícone, screenshots)
SVN como sistema de release"Apenas envie versões prontas para uso". Não faça commits de desenvolvimento frequentes no trunk.
Fallback de traduçõesWP busca em cascata: text-domain-locale.mo → text-domain.mo → text-domain-en_US.mo (núcleo). O locale raiz es atende TODAS as variantes es_XX.

Armadilhas e erros comuns

PowerShell e UNC paths

PHP e muitos binários falham se o CWD é um caminho UNC (\\server\share). Antes de executar comandos:

Set-Location C:\Users\<usuario>   # ou qualquer dir local
php $wpCli ...                # agora funciona

Codificação de caracteres acentuados no PHP (Windows)

PHP no Windows tem problemas com literais UTF-8 em scripts. Solução: usar mb_chr(0x00f3) para cada caractere acentuado:

"Obtener pa" . mb_chr(0x00ed) . "s"  // "Obtener país"

PowerShell também pode exibir ? em vez de caracteres acentuados ao fazer Get-Content. Verifique os bytes do arquivo, não a renderização:

$bytes = [System.IO.File]::ReadAllBytes('arquivo.po')
# Procurar 0xC3 0xB3 (UTF-8 de 'ó')

Working copy SVN parece vazia após uma nova tag

SVN deduplica arquivos entre trunk e tag (mesmo "blob"). Após svn copy remoto, os arquivos aparecem no servidor, mas a working copy local tem placeholders. Solução: svn cleanup + svn update --depth infinity --force.

svn.exe não está no PATH

TortoiseSVN é instalado em C:\Program Files\TortoiseSVN\bin\ mas não adiciona svn.exe ao PATH por padrão. Solução temporária:

$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"

Ou adicionar permanentemente ao PATH do usuário.

O plugin usa strings em espanhol no header

As strings do header do plugin (Plugin Name, Description, etc.) NÃO são traduzíveis. Mas se wp i18n as incluir no .pot, deixe-as com msgstr "" (vazio). WP.org e GlotPress sabem que não são traduzidas.

Não incluir .po/.mo para locales gerenciados pelo GlotPress

Uma vez que o GlotPress tenha traduções para seu plugin (através da comunidade), o WP.org fornece automaticamente os .mo. Seus .po/.mo iniciais são apenas a semente inicial. Não lute contra o GlotPress.


Arquivos modificados por release (checklist)

  • meu-plugin.php — header Version + PLUGIN_VERSION
  • readme.txt — Stable tag + entrada no Changelog
  • CHANGELOG.md — nova entrada
  • languages/<text-domain>.pot — regenerado
  • languages/<text-domain>-es.po — criado/atualizado
  • languages/<text-domain>-es.mo — compilado

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas