-
Resolve the slug and confirm i18n is wired. From the user's lib name derive the kebab slug (e.g. "charts" → sugar-charts, "pty" → candy-pty). Verify the source exists:
ls <slug>/lang/en.php <slug>/src/Lang.php
If <slug>/lang/en.php is missing, STOP — this is first-time wiring, not a locale add. Verify both files exist before proceeding.
-
Resolve the locale code against LOCALES.md. Map the requested language to a code from the "Recommended set" table (Japanese→ja, French→fr, Brazilian Portuguese→pt-br, Simplified Chinese→zh-cn). Confirm <slug>/lang/<code>.php does not already exist:
test -f <slug>/lang/<code>.php && echo EXISTS || echo OK
If it prints EXISTS, ask the user whether to overwrite. Uses the slug from Step 1.
-
Read the full <slug>/lang/en.php. Read <slug>/lang/en.php in its entirety. Capture: the header docblock, every 'key' => 'value' pair (including comment lines like // Canvas/Canvas.php that group keys in multi-section files such as sugar-charts/lang/fr.php), and every {placeholder} token inside each value. This is the template — the output must have identical structure. Uses the file from Step 1.
-
Write <slug>/lang/<code>.php matching this exact shape (from candy-pty/lang/en.php and sugar-charts/lang/fr.php):
<?php
declare(strict_types=1);
return [
'canvas.dim_nonneg' => '<translated prose keeping {tokens}>',
];
Rules for the body: same keys, same order, same grouping comments as <slug>/lang/en.php; translate only the right-hand string; keep every {placeholder} and any format/upstream identifiers (barWidth, TIOCSWINSZ, proc_open(), paths) as-is. Do NOT add, remove, or reorder keys. Verify your key list matches en.php before saving.
-
Verify key parity and placeholder parity with a one-off PHP check (adjust slug/code):
php -r '$s="<slug>"; $c="<code>";
$en=require "$s/lang/en.php"; $x=require "$s/lang/$c.php";
$mk=array_diff(array_keys($en),array_keys($x));
$ek=array_diff(array_keys($x),array_keys($en));
if($mk||$ek){echo "KEY MISMATCH missing=".implode(",",$mk)." extra=".implode(",",$ek)."\n";exit(1);}
foreach($en as $k=>$v){preg_match_all("/\{[a-z0-9_]+\}/i",$v,$a);preg_match_all("/\{[a-z0-9_]+\}/i",$x[$k],$b);sort($a[0]);sort($b[0]);
if($a[0]!=$b[0]){echo "PLACEHOLDER MISMATCH on $k: en=".implode(",",$a[0])." $c=".implode(",",$b[0])."\n";exit(1);}}
echo "OK: ".count($en)." keys, placeholders match\n";'
This must print OK: <n> keys, placeholders match. If it reports KEY or PLACEHOLDER MISMATCH, fix the locale file and re-run. Do not proceed until it passes. Uses the file from Step 4.
-
Run the library's test suite so LangCoverageTest (present in most libs, e.g. sugar-table/tests/LangCoverageTest.php) confirms nothing regressed:
cd <slug> && composer install --quiet && vendor/bin/phpunit
If phpunit fails only on stale deps, run composer update first (per project gotchas), then re-run. Must be green before shipping.
-
Ship it via the ship-as-you-go cadence on branch ai/<slug>-<code>-locale, author Joe Huss <detain@interserver.net>. Bundle 2-4 locales for the same lib into one PR when adding several. Title: <slug>: add <code> translation.