WordPressプラグイン:完全i18n + .org SVNへのリリース
使用するタイミング
- WordPress.orgで承認されたばかりで、translate.wordpress.orgに対応する必要があるプラグイン。
- 翻訳可能な文字列(
__()、_e()、wp.i18n.__()など)がスペイン語でハードコードされており、GlotPressでの他言語(fr、de、pt、jaなど)への翻訳を妨げている場合。 - 初めて初期翻訳や.potを追加する必要がある場合。
- バージョンを上げてplugins.svn.wordpress.orgのSVNにリリースする場合。
前提条件
| コンポーネント | 確認方法 | 不足している場合 |
|---|---|---|
| TortoiseSVN (svn.exe) | Test-Path "C:\Program Files\TortoiseSVN\bin\svn.exe" | https://tortoisesvn.net/downloads.html からインストール("command line tools"にチェック) |
| PHP 8.x (PATHに含まれていること) | UNC以外のディレクトリで php --version | Herd legacyでも可。またはスタンドアロンPHP。 |
| wp-cli.phar | https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar からダウンロード | Invoke-WebRequest -Uri ... -OutFile wp-cli.phar |
| プラグインへのSVNアクセス | [email protected]からの承認メール(SVN URL、ユーザー名、パスワードを含む) | 不足している場合はチームに依頼 |
完全なワークフロー
- 環境とプラグインを確認
- プラグインのSVNをチェックアウト
- コード内の翻訳可能な文字列を特定(PHP + JS)
- 文字列を英語に書き換え(技術的なセマンティクスを維持)
.potを再生成(wp i18n make-pot)- 初期翻訳の
.po+.moを生成(例:中性スペイン語) - 同期バージョンを更新(ヘッダー
Version、定数、readme stable tag、CHANGELOG) - 開発ファイルを除外したクリーンビルドを再生成(
.distignore) - trunkにコミット + SVNにタグを作成
- (オプション)Gitファイルを保持したままローカルディレクトリを同期
1. 環境とプラグインを確認
# WindowsのSVN(通常PATHに含まれていない。フルパスを使用するか追加する)
$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"
svn --version
# PHP
php --version
コードに触れる前に、プラグインを読む:
- プラグインヘッダー(Name、Version、Text Domain、Domain Path)
readme.txt(Stable tag、Changelog)CHANGELOG.md(存在する場合)includes/とassets/のPHP/JSファイル一覧
2. SVNのチェックアウト
$svnUser = '<wp-org-user>' # WP.orgのユーザー名(大文字小文字区別)
$svnPass = 'svn_XXXXX...' # profiles.wordpress.orgの「Account & Security」から取得
$slug = 'mi-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には既に trunk/、tags/、assets/ が含まれています。初めてのリリースをアップロードしたばかりの場合、tags/ は空で trunk/ も空(初期trunkで作成されたばかり)です。
3. 翻訳可能な文字列を特定
i18n関数の呼び出しをすべて特定します。以下のパターンで grep を使用します。
# PHP
Select-String -Path 'includes/*.php' -Pattern '__\(|_e\(|_x\(|esc_html__\(|esc_attr__\(|esc_html_e\(|esc_attr_e\(|_n\(|_nx\('
# JS(wp.i18n または関数なしのハードコードされた文字列)
Select-String -Path 'assets/js/*.js' -Pattern 'wp\.i18n|__\('
Select-String -Path 'assets/js/*.js' -Pattern '"[^"]{6,80}"' # ハードコードされた文字列を検出
よくあるアンチパターン:JS内で連結されたHTML内のスペイン語の文字列(例:html += '<option>Todas</option>';)。直接英語に書き換えることも有効ですが、ランタイムで翻訳可能にするには wp.i18n.__() を使用する方が適切です。
4. 文字列を英語に書き換え
厳格なルール:
- i18n関数の最初の引数のみ翻訳します。text-domain(2番目の引数)には触れないでください。
- i18n呼び出し以外には触れないでください。
- PHPの形式(スペース、引用符、括弧)は変更しないでください。
ExampleStore、WooCommerce、API、Correlation ID、Premium、JSON、HTTP、URL、CLPなどの技術用語は英語のまま維持します(すでに固有名詞/頭字語です)。- sprintf/printfのプレースホルダー(
%s、%d、%1$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 {動詞}" | "Could not {動詞}" |
| "Debes ingresar X" | "You must enter X" |
| "Cotización API recibida" | "API quote received" |
| "Tarifa por defecto (CLP)" | "Default rate (CLP)" |
大規模なファイルの場合は、エージェントに正確な指示と翻訳表を委任します。
5. wp-cliで.potを再生成
# プラグインディレクトリから(ローカルミラー)
$wpCli = 'C:\Temp\opencode\wp-cli.phar'
php $wpCli i18n make-pot . "languages/mi-plugin.pot" --domain="mi-plugin"
.pot は /languages/ に <text-domain>.pot という名前で配置する必要があります。
6. 翻訳.po + .moを作成
重要な決定:どのロケールを使用するか?地域バリエーションなしでプラグインをすべてのスペイン語話者に自動翻訳させるには、<text-domain>-es.po(ルート es)を使用します。WordPressはカスケードフォールバックを行います:es_CL → es → 英語。
推奨戦略:
es.po/.mo(ルート)→ es_CL、es_ES、es_MX、es_AR、es_PEなどに対応。- (後日オプション)チリ限定の
es_CL.po/.mo(地域の表現用)。
.poを生成:.pot を読み取り、EN→ESの翻訳配列を適用するPHPスクリプトを使用します。アクセント付き文字には mb_chr(0x00f3) を使用します(WindowsのPHPはUTF-8リテラルでコードページの問題が発生します)。
// build-es.php
declare(encoding='UTF-8');
$pot = file_get_contents('languages/mi-plugin.pot');
$po_es = 'languages/mi-plugin-es.po';
// アクセント付き文字にmb_chrをプリロード
function A() { return mb_chr(0x00e1); } // á
function E() { return mb_chr(0x00e9); } // é
function O() { return mb_chr(0x00f3); } // ó
// など
$t = [
"Configure API URL and token" => "Configura la URL de API y el token",
// ...
];
$header = "msgid \"\"\nmsgstr \"\"\n"
. "\"Project-Id-Version: Mi 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";
// .potをパースし、$tでmsgstrを入力、.poを書き込む
// (完全なロジック:wp-plugin-i18n-release/build-es.phpを参照)
.moをコンパイル:
php $wpCli i18n make-mo languages/
これにより、自動的に languages/<text-domain>-es.mo が作成されます。
7. 同期バージョンを更新
4つのファイルを更新します:
// 1. my-plugin.php (ヘッダー)
* Version: 1.1.0
// 2. my-plugin.php (定数)
define('PLUGIN_VERSION', '1.1.0');
// 3. readme.txt
Stable tag: 1.1.0
/* + == Changelog == に新しいエントリ */
// 4. CHANGELOG.md
## [1.1.0] - 2026-09-14
### Changed
- ...
8. クリーンビルドを再生成(開発ファイルを除外)
プラグインの .distignore で除外するものが定義されています。存在しない場合は、次の標準リストを使用します。
$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')
# 出力先をクリーンアップし、全てコピー、除外ファイルを削除
$src = 'ruta/espejo/plugin'
$dist = 'ruta/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. trunkにコミット + タグを作成
$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"
# trunkをクリーンビルドで置き換え
$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"
# タグを作成 (リモートsvn copy)
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"
リモートタグの後、ローカル更新を強制します(新しいタグはワーキングコピーに完全には反映されません)。
svn cleanup
svn update --depth infinity --force --non-interactive --trust-server-cert `
--username $svnUser --password $svnPass "$trunk/../"
10. (オプション)ローカルディレクトリを同期
SVNエクスポートにないファイル(.git/、.gitignore、tests/、bin/など)を保持します。
$local = 'C:\Users\...\wp-content\plugins\mi-plugin'
$exportDir = 'C:\Users\...\export-1.1.0'
# 新しく作成されたタグをエクスポート
svn export "https://plugins.svn.wordpress.org/$slug/tags/1.1.0" $exportDir
# ローカルからリリースファイルのみを削除し、その他は保持
$releaseNames = (Get-ChildItem $exportDir -Force).Name
foreach ($name in $releaseNames) {
$localPath = Join-Path $local $name
if (Test-Path $localPath) { Remove-Item $localPath -Recurse -Force }
}
# エクスポートをローカルにコピー
Copy-Item -Path "$exportDir\*" -Destination $local -Recurse -Force
11. 最終確認
# ローカルのバージョン確認
Write-Host "header: " (Get-Content "$local\mi-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()
# リモートSVNの確認
svn ls --recursive "https://plugins.svn.wordpress.org/$slug/tags/1.1.0/"
WordPress.orgの重要ルール
| 概念 | ルール |
|---|---|
| Text Domain | プラグインのスラッグと同一でなければなりません(例:mi-plugin)。スラッグを変更するにはプラグインの再承認が必要です。絶対に変更しないでください。 |
| Domain Path | WordPress.orgが.potを見つけるためには /languages/ を指定します。 |
| Plugin Name ヘッダー | 先頭にサードパーティの商標を含めないでください(WP.orgのレビューで拒否されます)。 |
| readme.txt | WP.orgはこのファイルをパースします(README.mdではありません)。Stable tag はバージョンと一致している必要があります。 |
| SVNレイアウト | /trunk/(開発)、/tags/X.Y.Z/(リリース)、/assets/(バナー、アイコン、スクリーンショット) |
| SVNはリリースシステム | "ready-to-useバージョンのみをプッシュ"。trunkへの頻繁な開発コミットは避けてください。 |
| 翻訳のフォールバック | WPはカスケード検索:text-domain-locale.mo → text-domain.mo → text-domain-en_US.mo(コア)。ルートロケール es はすべての es_XX バリアントに対応します。 |
よくある問題とエラー(Gotchas)
PowerShellとUNCパス
CWDがUNCパス(\\server\share)の場合、PHPや多くのバイナリが失敗します。コマンドを実行する前に:
Set-Location C:\Users\<ユーザー名> # または任意のローカルディレクトリ
php $wpCli ... # これで動作
Windows PHPでのアクセント付き文字のエンコーディング
WindowsのPHPはスクリプト内のUTF-8リテラルで問題が発生します。解決策:各アクセント付き文字に mb_chr(0x00f3) を使用します。
"Obtener pa" . mb_chr(0x00ed) . "s" // "Obtener país"
PowerShellはまた、Get-Content でアクセント付き文字の代わりに ? を表示する場合があります。ファイルのバイトを確認し、レンダリングを確認しないでください。
$bytes = [System.IO.File]::ReadAllBytes('archivo.po')
# 0xC3 0xB3 ('ó'のUTF-8) を探す
新しいタグの後、SVNワーキングコピーが空に見える
SVNはtrunkとタグ間でファイルを重複排除します(同じ「blob」)。リモートで svn copy を実行すると、ファイルはサーバー上に表示されますが、ローカルのワーキングコピーにはプレースホルダーがあります。解決策:svn cleanup + svn update --depth infinity --force。
svn.exeがPATHにない
TortoiseSVNは C:\Program Files\TortoiseSVN\bin\ にインストールされますが、デフォルトで svn.exe をPATHに追加しません。一時的な解決策:
$env:Path = "$env:ProgramFiles\TortoiseSVN\bin;$env:Path"
または、ユーザーのPATHに永続的に追加します。
プラグインがヘッダーにスペイン語の文字列を使用している
プラグインヘッダーの文字列(Plugin Name、Descriptionなど)は翻訳されません。しかし、wp i18nがそれらを.potに含めた場合は、msgstr ""(空)のままにしておきます。WP.orgとGlotPressはこれらが翻訳されないことを認識しています。
GlotPressが管理するロケールの.po/.moを含めない
GlotPressが(コミュニティを通じて)プラグインの翻訳を持つようになると、WP.orgが自動的に.moを提供します。初期の.po/.moは初期シードのみです。GlotPressと競合しないでください。
リリースによって変更されるファイル(チェックリスト)
-
my-plugin.php— ヘッダー Version + PLUGIN_VERSION -
readme.txt— Stable tag + Changelogのエントリ -
CHANGELOG.md— 新しいエントリ -
languages/<text-domain>.pot— 再生成 -
languages/<text-domain>-es.po— 生成 -
languages/<text-domain>-es.mo— コンパイル -
.distignore— 必要に応じて確認/更新