feat(wordpress): full reconciliation of WP plugin .pot → wordpress namespace#59
Conversation
…mespace
Closes the gap between the WP plugin's PHP-side translatable strings
and this repo's wordpress namespace bookkeeping. Prior state: 46 keys
under wordpress.* covering Phase 2 admin + widget surfaces only. Many
post-Phase-2 sprints (SWE-447, SWE-448, SWE-453, SWE-453, SWE-488,
SWE-499, SWE-504, SWE-507, SWE-510, SWE-511) added strings to the WP
plugin's .pot without a matching record here.
This commit syncs the plugin's `languages/loyaltydog.pot` (post-
regeneration on `loyaltydog/loyaltydog-wordpress-ecommerce@PR #128`)
against the existing wordpress namespace and adds 570 new keys grouped
by source-file area:
admin.activation admin.connection admin.credentials
admin.dashboard admin.diagnostics admin.encryptionKeyNotice
admin.help admin.menu admin.notices
admin.program admin.settings admin.settingsSync
admin.subscriptionLapse admin.systemInfo admin.testConnection
admin.trialNotices admin.widget admin.wizard
compliance earning emails
frontend.account frontend.checkout frontend.checkoutService
frontend.customerExport frontend.elementor frontend.points
frontend.redeem misc plugin
privacy program rateLimiter
webhooks.stripe
Naming algorithm: take the first 4-6 words of each msgid, drop
placeholder tokens (%s/%d), camelCase, dedupe with numeric suffix on
collision. Scoping: `wordpress.<area>.<camelKey>`. Non-deterministic
to revisit — keys are mechanically generated, not curated. Translators
can refine paths in a follow-up; the bookkeeping role is the priority.
Conflict checks performed:
- Path collisions with existing wordpress.* paths in this repo
(across common.json, errors.json, wordpress.json): 3 found.
* 2 skipped as semantic dupes (case / placeholder syntax only).
* 1 renamed with `Wp` suffix to disambiguate.
- Value overlaps with non-wordpress namespaces: 52 found, ALLOWED
per the established pattern (each integration namespace is self-
contained for translators; e.g. `wordpress.admin.widget.save`
coexists with `common.actions.save`).
All 8 hyphen-form locale dirs (en-US, en-GB, es-ES, es-MX, fr, it,
pt-BR, pt-PT) seeded with English values. Crowdin will translate on
next sync. en-US is source of truth.
Final leaf count per locale wordpress.json: 616 (was 46).
Source ref: loyaltydog-wordpress-ecommerce PR #128 (.pot regeneration
post-sprint; merged 2026-05-10).
|
CodeAnt AI is reviewing your PR. Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
Greptile SummaryThis PR reconciles the WordPress plugin's
Confidence Score: 5/5Safe to merge — all 8 locale files are valid JSON, no existing keys were overwritten, and the change is bookkeeping-only (the WP plugin reads its own .po/.mo files at runtime). The change is additive JSON data with no runtime coupling to the JS application layer. All prior blocking findings have been fully resolved. Two residual concerns (unconverted single-brace tokens and missing namespaces in the PR description) are documentation-level rather than functional defects in a bookkeeping-only change. No files require special attention for merge safety. The en-US/wordpress.json misc and admin.menu sections contain {name}-style tokens worth cleaning up in a follow-up if these keys are ever consumed by the JS i18n layer.
|
| Filename | Overview |
|---|---|
| packages/i18n/locales/en-US/wordpress.json | Source-of-truth locale: 570+ keys added across all wordpress.* sub-namespaces; prior-review issues resolved; residual single-brace {name}/{value} tokens in misc and admin.menu not converted to mustache; five PR-described namespaces absent from actual file. |
| packages/i18n/locales/en-GB/wordpress.json | Seeded with English source values identical to en-US; inherits all the same key-set. Carries same residual {name}-style tokens. |
| packages/i18n/locales/es-ES/wordpress.json | Previously translated keys retained in Spanish; 570 new keys seeded with English values awaiting Crowdin translation. |
| packages/i18n/locales/es-MX/wordpress.json | Existing translations preserved, new keys seeded with English values pending Crowdin sync. |
| packages/i18n/locales/fr/wordpress.json | French locale seeded with English for new keys; existing translations preserved; no structural issues. |
| packages/i18n/locales/it/wordpress.json | Italian locale seeded with English for new keys; existing translations preserved; no structural issues. |
| packages/i18n/locales/pt-BR/wordpress.json | Brazilian Portuguese locale seeded with English for new keys; no structural differences from other non-English locales. |
| packages/i18n/locales/pt-PT/wordpress.json | European Portuguese locale seeded with English for new keys; no structural differences from other non-English locales. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
POT["loyaltydog.pot\n(PHP plugin source)"]
GEN["Key generator\n(camelCase + dedup)"]
POT --> GEN
GEN -->|"printf tokens\n%s → {{value}}\n%d → {{count}}"| CONVERT["Placeholder Conversion"]
GEN -->|"single-brace tokens\n{name}, {value}\nnot converted"| WARN["⚠️ Residual {name} tokens"]
CONVERT --> ENFILE["en-US/wordpress.json\n(source of truth, 616 keys)"]
WARN --> ENFILE
ENFILE -->|"seeded with EN values"| OTHER["7 non-EN locales"]
OTHER -->|"next sync"| CROWDIN["Crowdin (translates non-EN)"]
ENFILE -->|"bookkeeping only"| PLATFORM["Localization Platform"]
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/i18n/locales/en-US/wordpress.json:544-546
**Single-brace `{name}` placeholder tokens left unconverted**
The printf conversion work (`%s` → `{{value}}`, etc.) did not cover the PHP custom-template tokens in `misc.nameFree`, `misc.nameValueCurrency`, and `misc.nameValueCurrency2`, which use `{name}`, `{value}`, and `{currency}` with single braces. Several `admin.menu.*` strings carry the same pattern (`{friendlyNameToBeAdded}`, `{friendlyName}`, `{name}`, `{value}`, `{currency}` at lines ~339–345). i18next's default interpolation uses `{{var}}` delimiters, so any JS consumer calling `t('wordpress.misc.nameFree', { name: '...' })` will receive the literal token `{name} (FREE)` unexpanded. The previous printf P1 was addressed, but this second class of non-mustache tokens was not included in the conversion table.
### Issue 2 of 2
packages/i18n/locales/en-US/wordpress.json:1-5
**PR description lists five namespaces absent from all locale files**
The "What's in the diff" section of the PR description explicitly names `compliance`, `earning`, `rateLimiter`, `webhooks.stripe`, and a top-level `wordpress.program` namespace as part of the 570 new keys. None of these namespaces exist in any of the 8 locale files — confirmed by grepping the final `en-US/wordpress.json`. Unlike `frontend.elementor`, which the description explicitly notes produced 0 new keys, these five are listed without caveat. A future reconciliation pass or automated key-coverage report will flag a gap between the PR's stated scope and the actual files.
Reviews (4): Last reviewed commit: "fix(wordpress): remove stray top-level a..." | Re-trigger Greptile
|
CodeAnt AI finished reviewing your PR. |
Greptile P1: every new key carrying a runtime value used PHP printf
tokens (%s, %d, %1\$s, %2\$s) instead of i18next's {{var}} mustache
syntax. Pre-existing keys in this namespace consistently use mustache
(e.g. "Last checked: {{date}}", "HTTP {{code}} from API"). A future
JS consumer pulling one of the new keys via t() would have received
the literal placeholder unexpanded.
The new keys came from the WP plugin's .pot, which is consumed by
PHP sprintf — so the printf form was correct for the WP runtime but
wrong for the platform-wide convention. The localization-platform
record is purely bookkeeping (per the WP-plugin/localization-platform
split memo: WP keeps its own .pot for runtime), so converting here
doesn't affect WP plugin behaviour at all and makes the namespace
internally consistent.
Conversion mapping:
- Positional: %1\$s -> {{first}}, %2\$s -> {{second}}, ... (ordinal)
%1\$d -> {{firstNumber}}, etc.
- Sequential: %s -> {{value}} / {{value2}} / ... (left-to-right)
%d -> {{count}} / {{count2}} / ...
%f -> {{number}} / {{number2}} / ...
37 leaves converted per locale (× 8 locales = 296 total). Zero
printf-style placeholders remain.
Examples:
"Program: %s" -> "Program: {{value}}"
"HTTP %d from API" -> "HTTP {{count}} from API"
"PHP %1\$s or newer is required..." -> "PHP {{first}} or newer is required..."
|
Greptile review addressed in P1 — Printf-style tokens won't interpolate in JS i18n layer
This doesn't affect the WP plugin runtime — the plugin reads its own P2 — |
Greptile P1 #1 — Missing plural forms for trial countdown --------------------------------------------------------- The WP plugin's .pot uses _n() for these two strings, producing both singular (msgid) and plural (msgid_plural) forms. The first commit on this PR only captured the singular, which would render "Trial: 3 day remaining" for any count > 1 in a JS consumer. JS consumers use i18next's _one / _other sibling-key convention. Renamed the affected keys to use _one suffix and added the _other companion: wordpress.admin.trialNotices.trialDayRemaining -> trialDayRemaining_one = "Trial: {{count}} day remaining" -> trialDayRemaining_other = "Trial: {{count}} days remaining" wordpress.admin.trialNotices.yourLoyaltydogTrialEndsInDay -> yourLoyaltydogTrialEndsInDay_one = "Your LoyaltyDog trial ends in {{count}} day." -> yourLoyaltydogTrialEndsInDay_other = "Your LoyaltyDog trial ends in {{count}} days." Greptile P1 #2 — Contradictory PHP version requirement ------------------------------------------------------- The platform record reproduced an actual contradiction in the WP plugin source: diagnostic-service.php said "requires 7.4+" while loyaltydog.php said "requires PHP 8.0 or newer", and the real floor (per readme.txt + plugin header) is PHP 8.2. Fixed at the source in loyaltydog/loyaltydog-wordpress-ecommerce#129; mirrored here so the platform record matches what the .pot now says: wordpress.admin.diagnostics.checkPhpVersionRequires74 -> checkPhpVersionRequires82 = "Check PHP version (requires 8.2+)" wordpress.plugin.loyaltydogRequiresPhp80Or -> loyaltydogRequiresPhp82OrNewer = "LoyaltyDog requires PHP 8.2 or newer." Note the key NAMES also changed (the version was baked into the mechanically-generated camelCase). Crowdin will pick up the new keys on next sync; old keys are removed from this branch's en-US source so stale translations don't carry forward. All 8 hyphen-form locales updated identically.
|
Both P1 findings addressed in P1 #1 — Missing plural forms for trial countdown
P1 #2 — Contradictory PHP version requirement Fixed at the source in loyaltydog/loyaltydog-wordpress-ecommerce#129 (and Old keys ( Branch HEAD: |
The pluralization commit (dc68286) introduced an empty \"admin\": { \"trialNotices\": {} } sibling at the root of every locale's wordpress.json, contradicting the PR's own test plan (\"No new top-level keys outside wordpress.*\"). Root cause: the migration script had two walks per pluralization spec — the first walked from `data` directly and used setdefault() for ['admin', 'trialNotices'] before being superseded by a correct walk from `data['wordpress']`. The first walk's setdefault left the empty stub behind. The intended content lives at the correct path (wordpress.admin.trialNotices.{trialDayRemaining_one,_other,...}) and is unaffected. Removed the stray empty \"admin\" key from all 8 locales after verifying it contained only empty sub-objects (no content loss). Final top-level shape per file: { \"wordpress\": ... } only.
|
P1 fixed in Stray top-level Verified pre-deletion that the stray block contained only empty sub-objects (no content loss). All 8 locale files now have |
Summary
Closes the gap between the WordPress plugin's PHP-side translatable strings (
languages/loyaltydog.pot) and this repo'swordpressnamespace bookkeeping. Was 46 keys → now 616 keys across 8 locales.Why now
The plugin's
.potwas regenerated as part ofloyaltydog-wordpress-ecommercePR #128 (SWE-502, merged 2026-05-10), which also landed strings introduced by the v2.x security + feature sprint:None of those were tracked in
wordpress.jsonuntil now.What's in the diff
570 new keys under
wordpress.*, grouped by source-file area:Note:
frontend.elementorwas a candidate namespace in my generator's source-file map but produced 0 new keys because every translatable string inclass-elementor-widget.phpwas already covered by a value-equal entry underwordpress.admin.widget.*(e.g.,"LoyaltyDog Widget"). The shipped namespace list isfrontend.blocksonly. (Resolves Greptile P2 on PR #59.)Naming algorithm: first 4-6 words of each msgid → camelCase → numeric-suffix dedupe. Scoping:
wordpress.<area>.<camelKey>. The keys are mechanically generated, not curated — translators can refine paths in a follow-up; the bookkeeping role is the priority.Placeholder syntax (Greptile P1 fix in commit
460f64d)The first commit copied PHP printf placeholders (
%s,%d,%1$s) verbatim from the .pot. Pre-existing keys in this namespace consistently use i18next mustache syntax ({{var}}), so a JS consumer would have seen the literal token unexpanded. Converted to mustache in the follow-up commit:%1$s{{first}}(positional → ordinal name)%2$s{{second}}%s(single){{value}}%s(multiple sequential){{value}},{{value2}}, ...%d{{count}}(or{{count2}}for sequential)%f{{number}}37 leaves converted per locale; zero printf-style placeholders remain. Doesn't affect the WP plugin runtime — the plugin reads its own
.pot/.po/.mofiles; the localization-platform record is bookkeeping only (per the WP/platform split memo).Conflict checks
wordpress.*(across common.json, errors.json, wordpress.json)%s↔{{date}}, case-only); 1 renamed withWpsuffixwordpress.admin.widget.save = "Save"coexists withcommon.actions.save = "Save")Locale coverage
All 8 hyphen-form locale dirs (
en-US,en-GB,es-ES,es-MX,fr,it,pt-BR,pt-PT) seeded with English values (placeholders in mustache syntax). Crowdin will translate on the next sync.en-USis source of truth.Test plan
wordpress.jsonfiles parse as valid JSON.wordpress.*.frontend.elementorremoved,frontend.blocksconfirmed).🤖 Generated with Claude Code