Add npm run extract:i18n script for PHP + TypeScript string extraction#161
Merged
Conversation
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.
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
AllTerrainDeveloper
approved these changes
May 11, 2026
Collaborator
AllTerrainDeveloper
left a comment
There was a problem hiding this comment.
This is amazing, thank you for taking care of this!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
bin/extract-i18n.sh(npm run extract:i18n), a counterpart to the existingbin/build-i18n.sh. It regenerateslanguages/desktop-mode.potfrom PHP and TypeScript sources, thenmsgmerges the refreshed POT into every per-locale.po.npm run i18n, a convenience alias that chainsextract:i18n+build:i18n.npm run i18nintobin/release.shas the first step of every release so the version-bump commit always carries an up-to-date.pot,.po, and JSON bundles. New--skip-i18nflag for hotfix releases that should not include translation-file churn.Why two extractors
wp-cli 2.12.0's
JsCodeExtractoris hardcoded to['extensions' => ['js', 'jsx']](seevendor/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.potwas 6 months stale on TS strings.The script therefore:
@automattic/wp-babel-makepot(a Babel-based CLI, new devDependency) againstsrc/**/*.{js,jsx,ts,tsx}to produce a JS-only POT in a temp dir.wp i18n make-pot --skip-js --merge=<js-pot>to extract PHP strings and merge in the JS POT.msgmerge --update --backup=noneagainst every existingdesktop-mode-<locale>.poso translators do not lose work.This mirrors the approach used in Automattic/studio's Fastlane
generate_pot_filelane.Refreshed translation files
.potunique msgids.potPHP refs.potTS source-line refses_ES.potranslated msgstrses_ES.pofuzzy entrieses_ES.poobsolete entriesThe 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
languages/churn even if no strings changed (timestamps, occasional line-number renumbering)..po. This is the standard WordPress translation lifecycle.bin/release.shnow also needswp(wp-cli phar) andmsgmerge(gettext) on the release machine, on top of the existinggh+git+npm.--skip-i18nis the escape hatch for hotfixes where this churn is not wanted.Test plan
npm run extract:i18nruns cleanly and produces the expected POT/PO diffnpm run build:i18nregenerates the per-handle JSON bundlesnpm run i18nchains both stagesbin/release.sh --helpprints the new usagebin/release.sh 0.9.0 --skip-i18n(dry inspect, do not actually tag) skips the i18n stepbin/release.sh 0.9.0(dry inspect) runsnpm run i18nbeforebump-version.sh.po