Plugin WordPress : i18n complet + Release sur le SVN de .org
Quand l'utiliser
- Plugin fraîchement approuvé sur WP.org qui doit être prêt pour translate.wordpress.org.
- Les chaînes traduisibles (
__(),_e(),wp.i18n.__(), etc.) sont codées en dur en espagnol et bloquent les autres locales (fr, de, pt, ja, etc.) dans GlotPress. - Il faut ajouter une traduction initiale ou un
.potpour la première fois. - Toute release qui met à jour la version et publie sur le SVN de plugins.svn.wordpress.org.
Prérequis
| Composant | Comment vérifier | S'il manque |
|---|---|---|
| TortoiseSVN (svn.exe) | Test-Path "C:\Program Files\TortoiseSVN\bin\svn.exe" | Installer depuis https://tortoisesvn.net/downloads.html (cocher "command line tools") |
| PHP 8.x dans le PATH | php --version depuis un dossier non-UNC | Herd legacy suffit ; ou PHP standalone |
| wp-cli.phar | Télécharger depuis https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar | Invoke-WebRequest -Uri ... -OutFile wp-cli.phar |
| Accès SVN au plugin | Email d'approbation de [email protected] avec l'URL SVN, le nom d'utilisateur et le mot de passe | Demander à l'équipe si manquant |
Workflow complet
- Vérifier l'environnement et le plugin
- Checkout du SVN du plugin
- Localiser les chaînes traduisibles dans le code (PHP + JS)
- Réécrire les chaînes en anglais en conservant la sémantique technique
- Régénérer le
.potavecwp i18n make-pot - Générer
.po+.mode la traduction initiale (ex : espagnol neutre) - Mettre à jour la version synchronisée (header
Version, constante, readme stable tag, CHANGELOG) - Régénérer un build propre en excluant les fichiers de développement (
.distignore) - Commit sur le trunk + créer un tag dans SVN
- (Optionnel) Synchroniser le dossier local en préservant les fichiers Git
1. Vérifier l'environnement et le plugin
# SVN sur Windows (souvent pas dans PATH ; utiliser le chemin complet ou ajouter)
$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"
svn --version
# PHP
php --version
Avant de toucher au code, lire le plugin :
- Plugin header (Name, Version, Text Domain, Domain Path)
readme.txt(Stable tag, Changelog)CHANGELOG.md(s'il existe)- Liste des fichiers PHP/JS dans
includes/etassets/
2. Checkout du SVN
$svnUser = '<wp-org-user>' # nom d'utilisateur WP.org (sensible à la casse)
$svnPass = 'svn_XXXXX...' # depuis "Account & Security" dans profiles.wordpress.org
$slug = 'mon-plugin' # le slug du 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
SVN contient déjà trunk/, tags/, assets/. Si vous venez de publier la première release, tags/ sera vide et trunk/ aussi (tout juste créé avec le trunk initial).
3. Localiser les chaînes traduisibles
Identifiez TOUS les appels aux fonctions i18n. Utilisez grep avec ces motifs :
# 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 chaînes codées en dur sans fonction)
Select-String -Path 'assets/js/*.js' -Pattern 'wp\.i18n|__\('
Select-String -Path 'assets/js/*.js' -Pattern '"[^"]{6,80}"' # détecte les chaînes codées en dur
Anti-motif courant : chaînes en espagnol dans du HTML concaténé en JS (ex : html += '<option>Toutes</option>';). Les réécrire en anglais est valable ; utiliser wp.i18n.__() est mieux si vous voulez qu'elles soient traduisibles au moment de l'exécution.
4. Réécrire les chaînes en anglais
Règles strictes :
- Traduisez UNIQUEMENT le premier argument des fonctions i18n. Ne touchez PAS au text-domain (deuxième argument).
- Ne touchez à rien en dehors d'un appel i18n.
- NE modifiez PAS le format PHP (espaces, guillemets, parenthèses).
- Les termes techniques comme
ExampleStore,WooCommerce,API,Correlation ID,Premium,JSON,HTTP,URL,CLPrestent en anglais (ce sont déjà des noms propres / acronymes). - PRÉSERVEZ les placeholders de sprintf/printf (
%s,%d,%1$s, etc.) exactement identiques. - Si une chaîne est déjà en anglais, ne la touchez PAS.
Table de traduction recommandée (ajustez au contexte) :
| Espagnol | Anglais |
|---|---|
| "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 {verbe}" | "Could not {verbe}" |
| "Debes ingresar X" | "You must enter X" |
| "Cotización API recibida" | "API quote received" |
| "Tarifa por defecto (CLP)" | "Default rate (CLP)" |
Pour les gros fichiers, déléguez à un agent avec des instructions précises et la table de traductions.
5. Régénérer le .pot avec wp-cli
# Depuis le dossier du plugin (miroir local)
$wpCli = 'C:\Temp\opencode\wp-cli.phar'
php $wpCli i18n make-pot . "languages/mon-plugin.pot" --domain="mon-plugin"
Le .pot doit être dans /languages/ avec le nom <text-domain>.pot.
6. Créer la traduction .po + .mo
Décision clé : quel locale ? Pour que le plugin soit traduit automatiquement pour tous les hispanophones sans variantes régionales, utilisez <text-domain>-es.po (racine es). WordPress fait un fallback en cascade : es_CL → es → anglais.
Stratégie recommandée :
es.po/.mo(racine) → sert es_CL, es_ES, es_MX, es_AR, es_PE, etc.- (Optionnel plus tard)
es_CL.po/.mospécifique pour le Chili avec des expressions locales.
Générer le .po : utilisez un script PHP qui lit le .pot et applique un tableau de traductions EN→ES. Utilisez mb_chr(0x00f3) pour les caractères accentués (PHP sur Windows a des problèmes de codepage avec les littéraux UTF-8).
// build-es.php
declare(encoding='UTF-8');
$pot = file_get_contents('languages/mon-plugin.pot');
$po_es = 'languages/mon-plugin-es.po';
// Précharger `mb_chr` pour les caractères accentués
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: Mon Plugin 1.0.0\\n\"\n"
. "\"Language-Team: Español (neutro) <>\"\n"
. "\"Language: es\\n\"\n"
. "\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n";
// Parser le .pot, remplir msgstr avec $t, écrire le .po
// (logique complète : voir wp-plugin-i18n-release/build-es.php)
Compilation du .mo :
php $wpCli i18n make-mo languages/
Ceci crée automatiquement languages/<text-domain>-es.mo.
7. Mettre à jour la version synchronisée
Mettez à jour quatre fichiers :
// 1. my-plugin.php (header)
* Version: 1.1.0
// 2. my-plugin.php (constante)
define('PLUGIN_VERSION', '1.1.0');
// 3. readme.txt
Stable tag: 1.1.0
/* + nouvelle entrée dans == Changelog == */
// 4. CHANGELOG.md
## [1.1.0] - 2026-09-14
### Changed
- ...
8. Régénérer un build propre (exclure les fichiers de dev)
Le .distignore du plugin définit ce qu'il faut exclure. S'il n'existe pas, utilisez cette liste standard :
$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')
# Nettoyer la destination, tout copier, supprimer les exclus
$src = 'chemin/miroir/plugin'
$dist = 'chemin/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 sur le trunk + créer un tag
$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"
# Remplacer le trunk par le build propre
$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 : chaînes source en anglais, réécriture i18n complète, traduction initiale en espagnol (es)"
# Créer un tag (copie SVN distante)
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"
Après le tag distant, forcer une mise à jour locale (les nouveaux tags ne se matérialisent pas complètement dans la copie de travail) :
svn cleanup
svn update --depth infinity --force --non-interactive --trust-server-cert `
--username $svnUser --password $svnPass "$trunk/../"
10. (Optionnel) Synchroniser le dossier local
Préserver les fichiers qui NE sont PAS dans l'export SVN (.git/, .gitignore, tests/, bin/, etc.) :
$local = 'C:\Users\...\wp-content\plugins\mon-plugin'
$exportDir = 'C:\Users\...\export-1.1.0'
# Exporter le tag fraîchement créé
svn export "https://plugins.svn.wordpress.org/$slug/tags/1.1.0" $exportDir
# Supprimer UNIQUEMENT les fichiers de la release du dossier local, garder le reste
$releaseNames = (Get-ChildItem $exportDir -Force).Name
foreach ($name in $releaseNames) {
$localPath = Join-Path $local $name
if (Test-Path $localPath) { Remove-Item $localPath -Recurse -Force }
}
# Copier l'export sur le dossier local
Copy-Item -Path "$exportDir\*" -Destination $local -Recurse -Force
11. Vérification finale
# Versions en local
Write-Host "header : " (Get-Content "$local\mon-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()
# Vérifier le SVN distant
svn ls --recursive "https://plugins.svn.wordpress.org/$slug/tags/1.1.0/"
Conventions critiques de WordPress.org
| Concept | Règle |
|---|---|
| Text Domain | Doit être identique au slug du plugin (ex : mon-plugin). Ne changez jamais le slug sans re-soumettre le plugin. |
| Domain Path | /languages/ pour que WP.org trouve le .pot |
| Plugin Name header | Ne pas inclure de marques déposées de tiers au début (la revue WP.org rejette) |
| readme.txt | WP.org parse ce fichier (pas README.md). Le Stable tag doit correspondre à la version. |
| SVN layout | /trunk/ (développement), /tags/X.Y.Z/ (releases), /assets/ (bannières, icône, captures d'écran) |
| SVN comme système de publication | "Seulement pousser des versions prêtes à l'emploi". Pas de commits de développement fréquents sur le trunk. |
| Fallback des traductions | WP cherche en cascade : text-domain-locale.mo → text-domain.mo → text-domain-en_US.mo (noyau). La racine de locale es dessert TOUTES les variantes es_XX. |
Gotchas et erreurs courantes
PowerShell et chemins UNC
PHP et de nombreux binaires échouent si le CWD est un chemin UNC (\\serveur\partage). Avant d'exécuter des commandes :
Set-Location C:\Users\<utilisateur> # ou tout autre dossier local
php $wpCli ... # maintenant ça fonctionne
Encodage des caractères accentués en PHP (Windows)
PHP sur Windows a des problèmes avec les littéraux UTF-8 dans les scripts. Solution : utilisez mb_chr(0x00f3) pour chaque caractère accentué :
"Obtener pa" . mb_chr(0x00ed) . "s" // "Obtener país"
PowerShell peut aussi afficher ? au lieu de caractères accentués avec Get-Content. Vérifiez les octets du fichier, pas le rendu :
$bytes = [System.IO.File]::ReadAllBytes('fichier.po')
# Chercher 0xC3 0xB3 (UTF-8 de 'ó')
La copie de travail SVN semble vide après un nouveau tag
SVN déduplique les fichiers entre le trunk et le tag (même "blob"). Après svn copy distant, les fichiers apparaissent sur le serveur mais la copie de travail locale a des placeholders. Solution : svn cleanup + svn update --depth infinity --force.
svn.exe n'est pas dans le PATH
TortoiseSVN s'installe dans C:\Program Files\TortoiseSVN\bin\ mais n'ajoute pas svn.exe au PATH par défaut. Solution temporaire :
$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"
Ou ajouter de manière permanente au PATH de l'utilisateur.
Le plugin utilise des chaînes en espagnol dans le header
Les chaînes du header du plugin (Plugin Name, Description, etc.) NE sont PAS traduisibles. Mais si wp i18n les inclut dans le .pot, laissez-les avec msgstr "" (vide). WP.org et GlotPress savent qu'elles ne sont pas traduites.
Ne pas inclure de .po/.mo pour les locales gérées par GlotPress
Une fois que GlotPress a des traductions pour votre plugin (via la communauté), WP.org fournit automatiquement les .mo. Vos .po/.mo initiaux sont uniquement le seed initial. Ne luttez pas contre GlotPress.
Fichiers modifiés par la release (checklist)
-
my-plugin.php— header Version + PLUGIN_VERSION -
readme.txt— Stable tag + entrée dans Changelog -
CHANGELOG.md— nouvelle entrée -
languages/<text-domain>.pot— régénéré -
languages/<text-domain>-es.po— nouvelle traduction initiale -
languages/<text-domain>-es.mo— compilé
Note : L'outil utilise PowerShell et des scripts PHP pour fonctionner sous Windows, mais les concepts (réécriture i18n, builds propres, SVN) sont universels.