Add optional secondary textdomain support to mki18n#8
Add optional secondary textdomain support to mki18n#8
Conversation
Plugins that bundle a Pro add-on (or any second textdomain) inside the same repo can now define optional WP_SCRIPT_TEXTDOMAIN_2 plus matching LANGUAGES_DIR_2 / EXCLUDE_2 / INCLUDE_2 constants. When set, mki18n runs a second `wp i18n make-pot` pass scoped to its own subtree and filtered to its own domain. Single-domain projects are unaffected — every new constant is gated behind defined() and falls through to the original behavior. Also exposes WP_SCRIPT_LANGUAGES_DIR / EXCLUDE / INCLUDE for the primary domain so callers can avoid the legacy `i18n/` default and pass scoping flags from config rather than CLI. The auto-tag step (add-textdomain.php) is skipped when a secondary domain is configured: an untagged call cannot be unambiguously assigned to either domain, so two-domain plugins must tag every gettext call explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
This is to support being able to build lite and pro pot's in Pretty Links which uses two different text domains for lite and pro. |
There was a problem hiding this comment.
Code Review
This pull request refactors the mki18n script to support multiple textdomains and utilizes the standard wp i18n make-pot command. The documentation and sample configuration have been updated to reflect these changes, including new options for include/exclude paths. A security vulnerability was identified in the mki18n script where the $domain variable is concatenated into shell commands without escaping, which could allow for command injection.
|
Sample Config for Pretty Links/Pro <?php
if(!defined('STDIN')) { die("You're unauthorized to view this page."); }
// ===== Required =====
// Primary textdomain — Pretty Link Lite.
define('WP_SCRIPT_TEXTDOMAIN', 'pretty-link');
// ===== Primary domain (Lite) i18n scoping =====
// Lite POT: /languages/pretty-link.pot
define('WP_SCRIPT_LANGUAGES_DIR', 'languages');
// Lite excludes — paths relative to plugin root.
//
// This config runs in GitHub Actions on a fresh checkout, so .gitignore'd
// trees (addons/, easy-affiliate/, current-version/, tmp/, coverage/, .claude/)
// don't exist on disk and don't need to be listed. Only committed paths are
// listed below.
//
// pro — Pro subtree, scanned in the secondary pass below.
// vendor-prefixed — Strauss-prefixed third-party deps. NOT a wp-cli default
// exclude (unlike `vendor`), so must be listed.
// assets/js/build — committed webpack output. Strings are duplicates of
// src-js/ with shifted line numbers; including them just
// clutters refs.
// tests, docs, bin — no translatable user-facing strings.
//
// Already in wp-cli's hardcoded defaults (see `wp i18n make-pot --help`):
// node_modules, .git, .svn, .CVS, .hg, vendor, *.min.js
define(
'WP_SCRIPT_EXCLUDE',
'pro,vendor-prefixed,assets/js/build,tests,docs,bin'
);
// No include filter — scan the whole tree minus the excludes above.
// define('WP_SCRIPT_INCLUDE', '');
// ===== Secondary textdomain — Pretty Link Pro =====
// Pro lives at ./pro/ in the same repo. POT: /pro/languages/pretty-link-pro.pot
define('WP_SCRIPT_TEXTDOMAIN_2', 'pretty-link-pro');
define('WP_SCRIPT_LANGUAGES_DIR_2', 'pro/languages');
// Scope the Pro pass to the pro/ subtree.
//
// NOTE: this is a scan-scope optimization, NOT a correctness requirement.
// The pass already runs with --domain=pretty-link-pro, so any string tagged
// 'pretty-link' (or any other domain) is filtered out by wp-cli regardless
// of where it's scanned. Restricting to pro/ just (a) keeps the scan fast
// and (b) makes file refs in the POT all share a clean `pro/...` prefix.
define('WP_SCRIPT_INCLUDE_2', 'pro');
// Pro excludes — paths relative to plugin root (NOT relative to pro/).
//
// pro/assets/js/build — webpack output for Pro src-js (same reasoning as Lite).
//
// pro/ has no vendor/, no node_modules/, no nested addons — verified directly.
// Nothing else inside pro/ needs excluding.
define('WP_SCRIPT_EXCLUDE_2', 'pro/assets/js/build');
// ===== Mothership / deploy (placeholders — replace before shipping) =====
define('MY_PLUGIN_MAIN_FILE', 'pretty-link.php');
define('MOTHERSHIP_URL', 'https://mothership.caseproof.com');
define('MOTHERSHIP_API_KEY', 'REPLACE_ME_API_KEY');
define('MOTHERSHIP_PROJECT_ID', 'REPLACE_ME_PROJECT_ID'); |
allycspf
left a comment
There was a problem hiding this comment.
Tested on pretty-link and noticed the sample WP_SCRIPT_EXCLUDE value in the README + wp-script-config.sample.php excludes vendor-prefixed. For plugins using Strauss to ship in-house libraries (most Caseproof plugins do — ground-level-*, growth-tools, etc.), those libraries contain real user-facing translatable strings tagged with the plugin's textdomain. Excluding vendor-prefixed drops them from the .pot — saw a ~29-string drop on pretty-link.
Also flagging assets in the same example — now that wp i18n make-pot extracts JS, this exclude could drop translatable JS strings depending on project layout.
Code is fine; just suggesting the sample value drop vendor-prefixed (and reconsider assets).
Plugins that bundle a Pro add-on (or any second textdomain) inside the same repo can now define optional WP_SCRIPT_TEXTDOMAIN_2 plus matching LANGUAGES_DIR_2 / EXCLUDE_2 / INCLUDE_2 constants. When set, mki18n runs a second
wp i18n make-potpass scoped to its own subtree and filtered to its own domain.Single-domain projects are unaffected — every new constant is gated behind defined() and falls through to the original behavior.
Also exposes WP_SCRIPT_LANGUAGES_DIR / EXCLUDE / INCLUDE for the primary domain so callers can avoid the legacy
i18n/default and pass scoping flags from config rather than CLI.The auto-tag step (add-textdomain.php) is skipped when a secondary domain is configured: an untagged call cannot be unambiguously assigned to either domain, so two-domain plugins must tag every gettext call explicitly.