Skip to content

Add optional secondary textdomain support to mki18n#8

Open
cartpauj wants to merge 1 commit intomasterfrom
feature/multi-textdomain
Open

Add optional secondary textdomain support to mki18n#8
cartpauj wants to merge 1 commit intomasterfrom
feature/multi-textdomain

Conversation

@cartpauj
Copy link
Copy Markdown
Contributor

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.

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>
@cartpauj cartpauj requested a review from allycspf April 25, 2026 06:20
@cartpauj cartpauj self-assigned this Apr 25, 2026
@cartpauj
Copy link
Copy Markdown
Contributor Author

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.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread mki18n
@cartpauj
Copy link
Copy Markdown
Contributor Author

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');

Copy link
Copy Markdown
Contributor

@allycspf allycspf left a comment

Choose a reason for hiding this comment

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

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).

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