Skip to content

feat: add full i18n support and ship .pot translation template (v3.15.0)#103

Merged
kunalnagar merged 2 commits into
masterfrom
feat/i18n-translation-support
Apr 25, 2026
Merged

feat: add full i18n support and ship .pot translation template (v3.15.0)#103
kunalnagar merged 2 commits into
masterfrom
feat/i18n-translation-support

Conversation

@kunalnagar

Copy link
Copy Markdown
Owner

Summary

  • Wraps all ~55 user-facing strings in i18n functions across 7 PHP files (views + admin classes), using the correct escaping function per output context
  • Ships languages/custom-404-pro.pot (78 msgid entries) generated via wp i18n make-pot — this is the template community translators copy to create .po files for their locale
  • Adds a validate-translations CI job that runs msgfmt --check-format and a grep-based XSS scan on any contributed .po files (no-ops cleanly until a translation PR lands)
  • Adds composer makepot script for regenerating the template after string changes
  • Adds a Contributing Translations section to README.md documenting the workflow for contributors

Security

i18n introduces a new attack surface — a translator could embed HTML/JS in a translated string. All output contexts are guarded:

Context Function used
Echoed HTML text esc_html_e() / esc_html( sprintf( __() ) )
HTML attribute values esc_attr__() / esc_attr_e()
Descriptions with <b> tags wp_kses_post( __() )
WP_List_Table column headers esc_html__() — WP does not escape these
WP_List_Table bulk action labels esc_html__() — WP does not escape these
Admin menu / page titles esc_html__() — WP doesn't guarantee escaping
Email body (wp_mail) __() raw — not output to browser
Messages via URL query param __() raw — custom_404_pro_notices() applies esc_html() at display time

The CI validate-translations job adds a second layer: msgfmt --check-format blocks format specifier mismatches (prevents runtime sprintf() errors) and a grep scan blocks <script, javascript:, onerror=, <iframe, and eval( in msgstr lines.

Test plan

  • PHPCS passes (composer lint) — 15/15 files clean
  • PHPUnit passes (composer test) — 36/36 tests pass
  • .pot file contains 78 msgid entries covering all wrapped strings
  • validate-translations CI job no-ops cleanly (no .po files yet)
  • Integration tests pass (composer test:integration)

- Wrap all ~55 user-facing strings in i18n functions across 7 PHP files
- Use esc_html__() for WP_List_Table columns/bulk actions (not escaped by WP core)
- Use esc_html__() for menu titles (WordPress doesn't guarantee escaping)
- Use wp_kses_post(__()) for description text containing <b> formatting
- Use _n() for proper plural form in prune log row count message
- Ship languages/custom-404-pro.pot (78 msgid entries) generated via wp i18n make-pot
- Add composer makepot script for regenerating the template
- Add validate-translations CI job: msgfmt --check-format + XSS pattern grep on msgstr lines
- Add Contributing Translations section to README.md
- Bump version to 3.15.0
…elines

Lead with translate.wordpress.org as the primary channel (web UI,
no setup required, translations auto-packaged at 90% completion).
Frame the GitHub PR path as the secondary route for developers who
want to contribute .po/.mo files directly.

Add `npx wp-env start` as a required prerequisite before `composer makepot`
so contributors aren't hit with a cryptic Docker connection error.
@kunalnagar kunalnagar force-pushed the feat/i18n-translation-support branch from e703e53 to d6c238d Compare April 25, 2026 07:23
@kunalnagar kunalnagar merged commit 1258f37 into master Apr 25, 2026
11 checks passed
@kunalnagar kunalnagar deleted the feat/i18n-translation-support branch April 25, 2026 07:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant