Skip to content
Closed
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e23d4f2
I18N: Add translation support for script modules.
manzoorwanijk Apr 10, 2026
7057417
Tests: Add end-to-end coverage for script module translation printing.
manzoorwanijk Apr 10, 2026
40bbbf2
Script Loader: Restore original position of print_enqueued_script_mod…
manzoorwanijk Apr 11, 2026
e23c08a
Docs: Update `@since` tags to 7.0.0 for script module translation APIs.
manzoorwanijk Apr 11, 2026
1a0d9ca
Simplify by using null coalescing operator
manzoorwanijk Apr 11, 2026
05c6016
Use ES6 and PHP 7.4 syntax
manzoorwanijk Apr 11, 2026
de8090f
Make PHPCS happy
manzoorwanijk Apr 11, 2026
44a1f56
Revert the change to heredoc indentation
manzoorwanijk Apr 11, 2026
eb78a18
Tests: Align null return type for get_registered_src().
manzoorwanijk Apr 11, 2026
cee8f0c
Apply suggestions from code review
manzoorwanijk Apr 13, 2026
c3380cc
I18N: Extract shared helper for loading script translation files.
manzoorwanijk Apr 13, 2026
2be0c57
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 13, 2026
28cfe1c
Use type-hints
manzoorwanijk Apr 13, 2026
1354cb0
I18N: Reuse load_script_textdomain_relative_path filter for script mo…
manzoorwanijk Apr 13, 2026
918490c
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 13, 2026
38dd31a
Rename get_registered_src() to get_registered()
westonruter Apr 14, 2026
7aad84d
Use get_echo() in tests
westonruter Apr 15, 2026
a146cf8
Add covers for WP_Script_Modules::set_translations()
westonruter Apr 15, 2026
555f309
Add assertions for new is_module arg for load_script_textdomain_relat…
westonruter Apr 15, 2026
d360841
Use HTML Tag Processor for inspecting output
westonruter Apr 15, 2026
db4106a
Fix variable name to use locale instead of local
westonruter Apr 15, 2026
da7e67a
Move load_script_module_textdomain() to immediately follow load_scrip…
westonruter Apr 15, 2026
7d734d9
Add covers for load_script_module_textdomain()
westonruter Apr 15, 2026
33b7f3b
Merge branch 'trunk' into add/script-module-translations
westonruter Apr 15, 2026
cfd6f50
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 16, 2026
c374a89
Merge branch 'trunk' into add/script-module-translations
westonruter Apr 18, 2026
1e9d62f
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 20, 2026
1eaaa51
I18N: Auto-detect script module translations.
manzoorwanijk Apr 21, 2026
63e70d4
Tests: Update assertion for auto-detection in dependencies test
manzoorwanijk Apr 21, 2026
07fbf49
Merge remote-tracking branch 'upstream/trunk' into add/script-module-…
manzoorwanijk Apr 21, 2026
f8bd260
Script Modules: Merge translation overrides into \$registered.
manzoorwanijk Apr 22, 2026
8c14a86
I18N: Namespace script module translation inline script IDs.
manzoorwanijk Apr 22, 2026
e9345df
Script Modules: Drop redundant translations_ prefix from nested path …
manzoorwanijk Apr 22, 2026
d3f1691
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 22, 2026
fc8c957
Script Modules: Hoist locale-data JS heredoc out of the translations …
manzoorwanijk Apr 22, 2026
e112a15
Script Modules: Print wp-i18n just-in-time before translation inline …
manzoorwanijk Apr 22, 2026
e5b32e7
Script Modules: Simplify translation override lookup with null-coales…
manzoorwanijk Apr 22, 2026
f4d4e69
Script Modules: Guard setLocaleData call against missing wp.i18n inst…
manzoorwanijk Apr 22, 2026
37a6b84
Add safety flags to JSON encode
manzoorwanijk Apr 22, 2026
0cd71d3
Reduce string literal duplication
manzoorwanijk Apr 22, 2026
5b592e7
Script Modules: Add failure messages to multi-assertion translation t…
manzoorwanijk Apr 23, 2026
3a0a32d
Script Modules: Force-print wp-i18n before translation inline scripts.
manzoorwanijk Apr 23, 2026
11c37c2
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 23, 2026
374a234
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 24, 2026
89a25ea
Script Modules: Flatten translation override fields onto registered e…
manzoorwanijk Apr 27, 2026
3d99f00
Merge branch 'trunk' into add/script-module-translations
manzoorwanijk Apr 27, 2026
0185911
Fix phpcs assignment alignment sniff
westonruter Apr 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions src/wp-includes/class-wp-script-modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@
* Core class used to register script modules.
*
* @since 6.5.0
*
* @phpstan-type ScriptModule array{
* src: string,
* version: string|false|null,
* dependencies: array<int, array{ id: string, import: 'static'|'dynamic' }>,
* in_footer: bool,
* fetchpriority: 'auto'|'low'|'high',
Comment thread
westonruter marked this conversation as resolved.
* translations?: array{ textdomain: string, path: string },
* }
*/
class WP_Script_Modules {
/**
* Holds the registered script modules, keyed by script module identifier.
*
* @since 6.5.0
* @var array<string, array<string, mixed>>
* @phpstan-var array<string, ScriptModule>
*/
private $registered = array();

Expand Down Expand Up @@ -328,6 +338,88 @@ public function deregister( string $id ) {
unset( $this->registered[ $id ] );
}

/**
* Overrides the text domain and path used to load translations for a script module.
*
* This is only needed for modules whose text domain differs from 'default'
* or whose translation files live outside the standard locations, for
* example plugin modules that register their own text domain. Translations
* for modules that use the default domain are loaded automatically by
* {@see WP_Script_Modules::print_script_module_translations()}.
*
* @since 7.0.0
*
* @param string $id The identifier of the script module.
* @param string $domain Optional. Text domain. Default 'default'.
* @param string $path Optional. The full file path to the directory containing translation files.
* @return bool True if the text domain was registered, false if the module is not registered.
*/
public function set_translations( string $id, string $domain = 'default', string $path = '' ): bool {
if ( ! isset( $this->registered[ $id ] ) ) {
Comment thread
manzoorwanijk marked this conversation as resolved.
return false;
}

$this->registered[ $id ]['translations'] = array(
'textdomain' => $domain,
'path' => $path,
);

return true;
}

/**
* Prints translations for all enqueued script modules.
*
* Outputs inline `<script>` tags that call `wp.i18n.setLocaleData()` with
* the translated strings for each script module. This must run before
* the script modules execute.
*
* Auto-detects the text domain and translation path for each module from
* its source URL. Modules whose text domain or path differs from the
* defaults can opt into a specific domain/path via
* {@see WP_Script_Modules::set_translations()}.
*
* @since 7.0.0
*/
public function print_script_module_translations(): void {
// Collect all module IDs that will be on the page (enqueued + their dependencies).
$module_ids = $this->get_sorted_dependencies( $this->queue );

foreach ( $module_ids as $id ) {
if ( isset( $this->registered[ $id ]['translations'] ) ) {
$domain = $this->registered[ $id ]['translations']['textdomain'];
$path = $this->registered[ $id ]['translations']['path'];
} else {
$domain = 'default';
$path = '';
}
Comment thread
manzoorwanijk marked this conversation as resolved.
Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
if ( isset( $this->registered[ $id ]['translations'] ) ) {
$domain = $this->registered[ $id ]['translations']['textdomain'];
$path = $this->registered[ $id ]['translations']['path'];
} else {
$domain = 'default';
$path = '';
}
$translation_settings = $this->registered[ $id ]['translations'] ?? null;
$domain = $translation_settings['textdomain'] ?? 'default';
$path = $translation_settings['path'] ?? '';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Or rather:

Suggested change
if ( isset( $this->registered[ $id ]['translations'] ) ) {
$domain = $this->registered[ $id ]['translations']['textdomain'];
$path = $this->registered[ $id ]['translations']['path'];
} else {
$domain = 'default';
$path = '';
}
$domain = $this->registered[ $id ]['translations']['textdomain'] ?? 'default';
$path = $this->registered[ $id ]['translations']['path'] ?? '';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The $domain and $path variables could even be left unassigned, because load_script_module_textdomain will default them to the same values anyway.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@jsnajdr good point on the defaults matching, but $domain is reused right below in wp_json_encode( $domain ) to pass the textdomain into the wp.i18n.setLocaleData() call, so it has to stay as a local. Kept $path assigned too for symmetry.


$json_translations = load_script_module_textdomain( $id, $domain, $path );

if ( ! $json_translations ) {
continue;
}

$set_locale_data_js_function = <<<JS
( domain, translations ) => {
const localeData = translations.locale_data[ domain ] || translations.locale_data.messages;
localeData[""].domain = domain;
wp.i18n.setLocaleData( localeData, domain );
}
JS;
Comment thread
manzoorwanijk marked this conversation as resolved.
Outdated

$output = sprintf(
'( %s )( %s, %s );',
$set_locale_data_js_function,
wp_json_encode( $domain ),
Comment thread
manzoorwanijk marked this conversation as resolved.
Outdated
$json_translations
);
$source_url = rawurlencode( "wp-script-module-translation-data-{$id}" );
$output .= "\n//# sourceURL={$source_url}";
wp_print_inline_script_tag( $output, array( 'id' => "wp-script-module-translation-data-{$id}" ) );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's possible at this point that the wp-i18n script hasn't actually been printed, right? To guard against that, should this first do something like:

Suggested change
wp_print_inline_script_tag( $output, array( 'id' => "wp-script-module-translation-data-{$id}" ) );
if ( ! wp_script_is( 'wp-i18n', 'done' ) ) {
wp_scripts()->do_items( array( 'wp-i18n' ) );
}
wp_print_inline_script_tag( $output, array( 'id' => "wp-script-module-translation-data-{$id}" ) );

With that, then the test_print_script_module_translations_outputs_set_locale_data test (and other tests) will need to be updated to account for this new script being added first.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's possible at this point that the wp-i18n script hasn't actually been printed, right?

The PR tries to prevent this by making sure that the print_script_module_translations handler is registered with priority 11/21 and runs only after _wp_footer_scripts has printed the wp-i18n script. Is that insufficient or unreliable?

Maybe we should throw an error or print a warning when wp-i18n is not done. Instead of calling do_items at an unexpected place, outside the "natural" order.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wp-i18n script is not always enqueued, is it? I think it is preferable to print the wp-i18n script just-in-time rather than to assume it is present or else to short-circuit if it wasn't printed. I don't see any issue with printing a script in this way, as WP_Scripts will keep track of it being printed to avoid re-printing it later.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Priority ordering alone isn't sufficient: it only guarantees we run after wp_print_footer_scripts, which prints enqueued classic scripts. If nothing in the page tree happened to enqueue wp-i18n, the ordering buys us nothing. Script modules don't declare classic-script dependencies, so there's no mechanism that enqueues wp-i18n just because a module calls __().

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wp-i18n script is not always enqueued, is it?

This gets interesting 🙂 The original version of print_script_module_translations printed the translations only when i) someone called set_translations explicitly and ii) the script module has wp-i18n in dependencies.

In that original version wp-i18n was always enqueued because it was a declared dependency. But the current version of print_script_module_translations removed both checks, it prints translations for all modules for which a JSON translation file exists. That can lead to wp-i18n not being enqueued yet.

But should we really call wp_scripts()->do_items( array( 'wp-i18n' ) ) when processing script modules? There are environments that are module-only and don't want to use the "legacy" scripts. Like Interactivity API on frontend. Then, when one of such modules has a translation file (it's generated automatically by GlotPress, you don't have much control over that), the module-only environment will get "contaminated" by a legacy script, and you can't opt out from that.

I see two solutions that are less intrusive:

  • add back the check for wp-i18n dependency. If you want a localized script module, it needs to have the dependencies in order.
  • make the wp.i18n.setLocaleData in the inline script fail-safe. Check the presence of wp.i18n before calling, and warn if its' missing.

FYI @sirreal who does a lot of work in the Interactivity API and script module area and might have some insights.

Copy link
Copy Markdown
Member

@westonruter westonruter Apr 22, 2026

Choose a reason for hiding this comment

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

I'm not sure I fully agree. The reality is that the script module here does have a dependency on the wp-i18n classic script, right? We don't want there to be this dependency, but this dependency currently exists for it to work. For the translations to work correctly, as I understand, a developer currently has to do:

wp_enqueue_script( 'wp-i18n' );
wp_enqueue_script_module( 'foo' );
wp_set_script_module_translations( 'foo', 'foo' );

If they forget to enqueue wp-i18n then the result at the moment is that the translations don't appear, correct? This seems like a worse outcome for users, and it's an easy mistake to make when a developer is only working in English.

Also, this seems to have the effect of cementing the dependency in code. If we are able to improve script module i18n to eliminate the need for the wp-i18n script in a future release, then any such just-in-time printing of the i18n script would be to our advantage because we wouldn't have legacy wp-i18n classic scripts being enqueued still in module-only environments.

Disclaimer: I'm not an expert on how i18n works.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think that makes. The dependency is real, and fail-safe just turns a debuggable missing-script into a silent "English on every translated site." The contamination concern mostly dissolves once you trace it: no __() calls → no chunks → no inline script → no force-print. For the guard to fire at all, the module genuinely needs wp.i18n. And keeping the coupling inside Core (rather than pushing wp_enqueue_script( 'wp-i18n' ) out to every plugin author) is easier to walk back later if a module-native i18n runtime lands.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The reality is that the script module here does have a dependency on the wp-i18n classic script, right?

That brings up a more basic question: when there are __() calls in a script module, where does the __ function come from? Ideally, it would be imported also from a native script module:

import { __ } from '@wordpress/i18n';

But we don't have that native i18n module yet. The current usage of script modules in the @wordpress/route framework (used by the new Connectors and Font Library pages) therefore makes a compromise: it uses a hybrid approach where a script module depends on the wp-i18n classic script and gets the __ function from the window.wp.i18n global variable.

What I'd like to point out is that the print_script_module_translations function we are implementing here further hardcodes this compromise with the classic wp-i18n script. We probably don't have any other option: even the wp.i18n.setLocaleData call in the generated translation script assumes the wp-i18n script. One day there will be an ESM variant:

<script type="module" id="wp-script-module-translations-foo">
  import { setLocaleData } from '@wordpress/i18n';
  setLocaleData( { fooTranslations } );
</script>

So, OK, let's auto-enqueue the wp-i18n script and whatever else helps us being more ergonomic and fail-safe. But with the awareness that it's a temporary compromise because of an incomplete ESM framework in WordPress. One day we'll have to revisit this.

Also, this seems to have the effect of cementing the dependency in code. If we are able to improve script module i18n to eliminate the need for the wp-i18n script in a future release, then any such just-in-time printing of the i18n script would be to our advantage

Yes, this is a great point. If we auto-enqueue the classic script in Core, we can change that later, again in Core. Instead of having a big number of published plugins that do the same on their own, and updating very slowly or not at all. I'm convinced 🙂

script modules can't declare classic-script deps through the current register API

This is another sign how temporary and improvised the classic script dependencies really are. wp-build detects during build which import is a module and which is a script (looking at package.json data) and then writes the .asset.php file with dependencies and module_dependencies data. But during registration, only module_dependencies are looked at. The classic script dependencies are ignored, it's just assumed that all the needed scripts are already enqueued.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Glad we converged 🙂 Reverting to the do_items( 'wp-i18n' ) guard and dropping the JS window.wp?.i18n?.setLocaleData check - with the force-print in place it's dead code. Agreed the whole compromise is temporary; a future ESM @wordpress/i18n will retire this code path cleanly, and keeping the coupling inside Core is what makes that retirement tractable.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated in 3a0a32d

Comment thread
manzoorwanijk marked this conversation as resolved.
Outdated
}
}

/**
* Adds the hooks to print the import map, enqueued script modules and script
* module preloads.
Expand Down Expand Up @@ -359,6 +451,15 @@ public function add_hooks() {
add_action( 'admin_print_footer_scripts', array( $this, 'print_enqueued_script_modules' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'print_script_module_preloads' ) );

/*
* Print translations after classic scripts like wp-i18n are loaded (at
* priority 10 via _wp_footer_scripts), but before the script modules
* execute. Script modules with type="module" are deferred by default,
* so inline translation scripts at priority 11 will execute before them.
*/
add_action( 'wp_footer', array( $this, 'print_script_module_translations' ), 21 );
add_action( 'admin_print_footer_scripts', array( $this, 'print_script_module_translations' ), 11 );

add_action( 'wp_footer', array( $this, 'print_script_module_data' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'print_script_module_data' ) );
add_action( 'wp_footer', array( $this, 'print_a11y_script_module_html' ), 20 );
Expand Down Expand Up @@ -631,6 +732,7 @@ private function get_import_map(): array {
* @since 6.5.0
*
* @return array<string, array<string, mixed>> Script modules marked for enqueue, keyed by script module identifier.
* @phpstan-return array<string, ScriptModule>
*/
private function get_marked_for_enqueue(): array {
return wp_array_slice_assoc(
Expand All @@ -652,6 +754,7 @@ private function get_marked_for_enqueue(): array {
* @param string[] $import_types Optional. Import types of dependencies to retrieve: 'static', 'dynamic', or both.
* Default is both.
* @return array<string, array<string, mixed>> List of dependencies, keyed by script module identifier.
* @phpstan-return array<string, ScriptModule>
*/
private function get_dependencies( array $ids, array $import_types = array( 'static', 'dynamic' ) ): array {
$all_dependencies = array();
Expand Down Expand Up @@ -840,6 +943,19 @@ private function sort_item_dependencies( string $id, array $import_types, array
return true;
}

/**
* Gets the data for a registered script module.
*
* @since 7.0.0
*
* @param string $id The script module identifier.
* @return array|null The script module data, or null if not registered.
* @phpstan-return ScriptModule|null
*/
public function get_registered( string $id ): ?array {
return $this->registered[ $id ] ?? null;
}

/**
* Gets the versioned URL for a script module src.
*
Expand Down
80 changes: 66 additions & 14 deletions src/wp-includes/l10n.php
Original file line number Diff line number Diff line change
Expand Up @@ -1134,24 +1134,80 @@ function load_child_theme_textdomain( $domain, $path = false ) {
*
* @see WP_Scripts::set_translations()
*
* @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
*
* @param string $handle Name of the script to register a translation domain to.
* @param string $domain Optional. Text domain. Default 'default'.
* @param string $path Optional. The full file path to the directory containing translation files.
* @return string|false The translated strings in JSON encoding on success,
* false if the script textdomain could not be loaded.
*/
function load_script_textdomain( $handle, $domain = 'default', $path = '' ) {
/** @var WP_Textdomain_Registry $wp_textdomain_registry */
global $wp_textdomain_registry;

$wp_scripts = wp_scripts();

if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
return false;
}

$src = $wp_scripts->registered[ $handle ]->src;

if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $wp_scripts->content_url && str_starts_with( $src, $wp_scripts->content_url ) ) ) {
$src = $wp_scripts->base_url . $src;
}

return _load_script_textdomain_from_src( $handle, $src, $domain, $path, false );
}

/**
* Loads the translation data for a given script module ID and text domain.
*
* Works like {@see load_script_textdomain()} but for script modules registered
* via {@see wp_register_script_module()}.
*
* @since 7.0.0
*
Comment thread
manzoorwanijk marked this conversation as resolved.
* @param string $id The script module identifier.
* @param string $domain Optional. Text domain. Default 'default'.
* @param string $path Optional. The full file path to the directory containing translation files.
* @return string|false The JSON-encoded translated strings for the given script module and text domain.
* False if there are none.
*/
function load_script_module_textdomain( string $id, string $domain = 'default', string $path = '' ) {
$module = wp_script_modules()->get_registered( $id );
if ( null === $module ) {
return false;
}
$src = $module['src'];

// Ensure src is an absolute URL for path resolution.
if ( ! preg_match( '|^(https?:)?//|', $src ) ) {
$src = site_url( $src );
}

return _load_script_textdomain_from_src( $id, $src, $domain, $path, true );
}

/**
* Resolves and loads the translation JSON file for a given script or script module source URL.
*
* This is a shared implementation used by {@see load_script_textdomain()} and
* {@see load_script_module_textdomain()} to avoid duplicating the path
* resolution and file lookup logic.
*
* @since 7.0.0
* @access private
*
* @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry.
*
* @param string $handle Name of the script or script module identifier to register a translation domain to.
* @param string $src Absolute source URL of the script or script module.
* @param string $domain Text domain.
* @param string $path The full file path to the directory containing translation files,
* or an empty string to use the default path from the text domain registry.
* @param bool $is_module Whether the source belongs to a script module (true) or a classic script (false).
* @return string|false The JSON-encoded translated strings on success, false otherwise.
*/
function _load_script_textdomain_from_src( string $handle, string $src, string $domain, string $path, bool $is_module ) {
global $wp_textdomain_registry;

$locale = determine_locale();

if ( ! $path ) {
Expand All @@ -1172,12 +1228,6 @@ function load_script_textdomain( $handle, $domain = 'default', $path = '' ) {
}
}

$src = $wp_scripts->registered[ $handle ]->src;

if ( ! preg_match( '|^(https?:)?//|', $src ) && ! ( $wp_scripts->content_url && str_starts_with( $src, $wp_scripts->content_url ) ) ) {
$src = $wp_scripts->base_url . $src;
}

$relative = false;
$languages_path = WP_LANG_DIR;

Expand Down Expand Up @@ -1245,11 +1295,13 @@ function load_script_textdomain( $handle, $domain = 'default', $path = '' ) {
* Filters the relative path of scripts used for finding translation files.
*
* @since 5.0.2
* @since 7.0.0 The `$is_module` parameter was added.
*
* @param string|false $relative The relative path of the script. False if it could not be determined.
* @param string $src The full source URL of the script.
* @param string|false $relative The relative path of the script. False if it could not be determined.
* @param string $src The full source URL of the script.
* @param bool $is_module Whether the source belongs to a script module (true) or a classic script (false).
*/
$relative = apply_filters( 'load_script_textdomain_relative_path', $relative, $src );
$relative = apply_filters( 'load_script_textdomain_relative_path', $relative, $src, $is_module );

// If the source is not from WP.
if ( false === $relative ) {
Expand Down
21 changes: 21 additions & 0 deletions src/wp-includes/script-modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,27 @@ function wp_deregister_script_module( string $id ) {
wp_script_modules()->deregister( $id );
}

/**
* Overrides the text domain and path used to load translations for a script module.
*
* Translations for script modules are loaded automatically from the default
* text domain and language directory. Use this function only when a module's
* text domain differs from `'default'` or when translation files live outside
* the standard location, for example plugin modules using their own text domain.
*
* @since 7.0.0
*
* @see WP_Script_Modules::set_translations()
*
* @param string $id The identifier of the script module.
* @param string $domain Optional. Text domain. Default 'default'.
* @param string $path Optional. The full file path to the directory containing translation files.
* @return bool True if the text domain was registered, false if the module is not registered.
*/
function wp_set_script_module_translations( string $id, string $domain = 'default', string $path = '' ): bool {
return wp_script_modules()->set_translations( $id, $domain, $path );
}

/**
* Registers all the default WordPress Script Modules.
*
Expand Down
Loading
Loading