Skip to content

Add npm run extract:i18n script for PHP + TypeScript string extraction#161

Merged
AllTerrainDeveloper merged 1 commit into
trunkfrom
add-extract-i18n-script
May 11, 2026
Merged

Add npm run extract:i18n script for PHP + TypeScript string extraction#161
AllTerrainDeveloper merged 1 commit into
trunkfrom
add-extract-i18n-script

Conversation

@epeicher
Copy link
Copy Markdown
Collaborator

@epeicher epeicher commented May 11, 2026

Summary

  • Adds bin/extract-i18n.sh (npm run extract:i18n), a counterpart to the existing bin/build-i18n.sh. It regenerates languages/desktop-mode.pot from PHP and TypeScript sources, then msgmerges the refreshed POT into every per-locale .po.
  • Adds npm run i18n, a convenience alias that chains extract:i18n + build:i18n.
  • Wires npm run i18n into bin/release.sh as the first step of every release so the version-bump commit always carries an up-to-date .pot, .po, and JSON bundles. New --skip-i18n flag for hotfix releases that should not include translation-file churn.

Why two extractors

wp-cli 2.12.0's JsCodeExtractor is hardcoded to ['extensions' => ['js', 'jsx']] (see vendor/wp-cli/i18n-command/src/MakePotCommand.php:672). It physically cannot parse .ts / .tsx, with or without --include='src/*.ts'. That is why the existing pre-PR .pot was 6 months stale on TS strings.

The script therefore:

  1. Runs @automattic/wp-babel-makepot (a Babel-based CLI, new devDependency) against src/**/*.{js,jsx,ts,tsx} to produce a JS-only POT in a temp dir.
  2. Runs wp i18n make-pot --skip-js --merge=<js-pot> to extract PHP strings and merge in the JS POT.
  3. Runs msgmerge --update --backup=none against every existing desktop-mode-<locale>.po so translators do not lose work.

This mirrors the approach used in Automattic/studio's Fastlane generate_pot_file lane.

Refreshed translation files

File Before After
.pot unique msgids 396 1074
.pot PHP refs 388 388
.pot TS source-line refs 276 1024
es_ES.po translated msgstrs 121 169
es_ES.po fuzzy entries 0 100 (msgmerge flagged, need translator review)
es_ES.po obsolete entries 0 2

The fuzzy entries are msgmerge's normal behavior when source line numbers shift across hundreds of files. They are flagged for translator review, not lost.

Trade-offs of the release-time refresh

  • Every release commit will carry some languages/ churn even if no strings changed (timestamps, occasional line-number renumbering).
  • New strings added during a release cycle ship untranslated in that release. They become translated in the next release after translators update the .po. This is the standard WordPress translation lifecycle.
  • bin/release.sh now also needs wp (wp-cli phar) and msgmerge (gettext) on the release machine, on top of the existing gh + git + npm.

--skip-i18n is the escape hatch for hotfixes where this churn is not wanted.

Test plan

  • npm run extract:i18n runs cleanly and produces the expected POT/PO diff
  • npm run build:i18n regenerates the per-handle JSON bundles
  • npm run i18n chains both stages
  • bin/release.sh --help prints the new usage
  • bin/release.sh 0.9.0 --skip-i18n (dry inspect, do not actually tag) skips the i18n step
  • bin/release.sh 0.9.0 (dry inspect) runs npm run i18n before bump-version.sh
  • Verify a localized string actually translates in the browser after running the new pipeline against a fresh translation in the .po
Open WordPress Playground Preview

Add bin/extract-i18n.sh (npm run extract:i18n) which scans PHP and
TypeScript sources for translatable strings, regenerates
languages/desktop-mode.pot, and msgmerges the refreshed POT into
every per-locale .po. Pairs with the existing build:i18n step that
compiles .po into per-handle JSON bundles.

Why two extractors: wp-cli 2.12.0's JS extractor is hardcoded to
['extensions' => ['js', 'jsx']] and physically cannot parse
.ts/.tsx, with or without --include. The script uses Automattic's
Babel-based @automattic/wp-babel-makepot for the JS/TS half and
wp i18n make-pot --skip-js --merge for the PHP half, mirroring the
approach used in Automattic/studio's generate_pot_file Fastlane
lane.

Also adds npm run i18n (alias: extract + build) and wires it into
bin/release.sh as the first step of every release, so the
version-bump commit always carries an up-to-date .pot, .po, and
JSON bundles. Pass --skip-i18n to release.sh for hotfix releases
that should not include translation-file churn.

Refresh languages/desktop-mode.pot and languages/desktop-mode-
es_ES.po against current source: 1074 unique msgids (388 PHP +
1024 TS source-line refs) vs the previous 396, with the additional
strings being TypeScript callers of __(), _x(), etc. that the
prior extractor never saw. msgmerge preserved all existing
translations and flagged 100 entries as fuzzy for translator
review.
@github-actions
Copy link
Copy Markdown

✅ WordPress Plugin Check Report

✅ Status: Passed

📊 Report

All checks passed! No errors or warnings found.


🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

Copy link
Copy Markdown
Collaborator

@AllTerrainDeveloper AllTerrainDeveloper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is amazing, thank you for taking care of this!

@AllTerrainDeveloper AllTerrainDeveloper merged commit 05448a6 into trunk May 11, 2026
6 checks passed
@AllTerrainDeveloper AllTerrainDeveloper deleted the add-extract-i18n-script branch May 11, 2026 22:49
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.

2 participants