This filter exists to solve one specific problem: script translations silently breaking when the script's src is served from a different host than your WordPress install — most commonly a CDN subdomain (e.g. https://cdn.example.com/my-plugin/build/index.js instead of https://example.com/wp-content/plugins/my-plugin/build/index.js).
Looking at <code>_load_script_textdomain_from_src()</code> (which fires this filter), <code>$relative</code> is computed by comparing the script's host/path against <code>content_url()</code>, <code>plugins_url()</code>, and <code>site_url()</code> in turn. If none of those hosts match — which happens whenever assets are proxied or served from a CDN with its own domain — $relative is left as false, and translations for that script silently fail to load (no error, the script just renders untranslated).
This filter runs right before that failure would happen, letting you supply the correct relative path yourself:
[php]
add_filter( 'load_script_textdomain_relative_path', function ( $relative, $src, $is_module ) {
// Only handle scripts actually served from our CDN.
if ( ! str_starts_with( $src, 'https://cdn.example.com/my-plugin/' ) ) {
return $relative;
}
// Rebuild the relative path as if it were served from the plugin's
// real location (relative to WP_LANG_DIR/plugins/my-plugin/).
return str_replace( 'https://cdn.example.com/my-plugin/', '', $src );
}, 10, 3 );
[/php]
A few things worth knowing:
- <code>$relative</code> must end up being a string for translations to load at all — returning false (or anything non-string) means "give up", which is also useful if you want to explicitly disable translation lookup for specific external scripts instead of triggering warnings.
- The <code>$is_module</code> parameter (added in WP 7.0) lets you apply different rewriting logic for script modules (registered via <code>wp_register_script_module()</code>) vs classic scripts, since both share this same resolution function.
- The final filename is built as <code>md5($relative) + '.json'</code> — so <code>$relative</code> doesn't need to be a real filesystem path, it just needs to be a STABLE, predictable string that you use consistently when naming your .json translation files (via <code>wp i18n make-json</code>).
Filters the relative path of scripts used for finding translation files.
Parameters$relativestring|falseThe relative path of the script. False if it could not be determined.
$srcstringThe full source URL of the script.
$is_moduleboolWhether the source belongs to a script module (true) or a classic script (false).
$relative = apply_filters( 'load_script_textdomain_relative_path', $relative, $src, $is_module );
View all references View on Trac View on GitHub
| Used by | Description |
|---|---|
_load_script_textdomain_from_src()wp-includes/l10n.php | Resolves and loads the translation JSON file for a given script or script module source URL. |
| Version | Description |
|---|---|
| 7.0.0 | The $is_module parameter was added. |
| 5.0.2 | Introduced. |
This filter exists to solve one specific problem: script translations silently breaking when the script’s src is served from a different host than your WordPress install — most commonly a CDN subdomain (e.g. https://cdn.example.com/my-plugin/build/index.js instead of https://example.com/wp-content/plugins/my-plugin/build/index.js).
Looking at _load_script_textdomain_from_src() (which fires this filter), $relative is computed by comparing the script’s host/path against content_url(), plugins_url(), and site_url() in turn. If none of those hosts match — which happens whenever assets are proxied or served from a CDN with its own domain — $relative is left as false, and translations for that script silently fail to load (no error, the script just renders untranslated).
This filter runs right before that failure would happen, letting you supply the correct relative path yourself:
add_filter( 'load_script_textdomain_relative_path', function ( $relative, $src, $is_module ) {
// Only handle scripts actually served from our CDN.
if ( ! str_starts_with( $src, 'https://cdn.example.com/my-plugin/' ) ) {
return $relative;
}
// Rebuild the relative path as if it were served from the plugin's
// real location (relative to WP_LANG_DIR/plugins/my-plugin/).
return str_replace( 'https://cdn.example.com/my-plugin/', '', $src );
}, 10, 3 );
A few things worth knowing:
– $relative must end up being a string for translations to load at all — returning false (or anything non-string) means “give up”, which is also useful if you want to explicitly disable translation lookup for specific external scripts instead of triggering warnings.
– The $is_module parameter (added in WP 7.0) lets you apply different rewriting logic for script modules (registered via wp_register_script_module()) vs classic scripts, since both share this same resolution function.
– The final filename is built as md5($relative) + '.json' — so $relative doesn’t need to be a real filesystem path, it just needs to be a STABLE, predictable string that you use consistently when naming your .json translation files (via wp i18n make-json).
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Comment on load_script_module_textdomain() by Rodrigo Vieira Eufrasio da Silva | 0 | 6.98 | 14-08-2026 |
| 2 | Comment on wp_insert_post by Rodrigo Vieira Eufrasio da Silva | 0 | 13.12 | 14-08-2026 |
| 3 | Comment on WP_Icons_Registry by Rodrigo Vieira Eufrasio da Silva | 0 | 9.38 | 14-08-2026 |
| 4 | Comment on wp_deregister_script() by Rodrigo Vieira Eufrasio da Silva | 0 | 29.53 | 14-08-2026 |
| 5 | Reply To: WPS Hide Login Has Completely Broken My WordPress 7.0 | 2 | 3 | 02-07-2026 |
| 6 | Comment on login_head by vee | 0 | 9.55 | 15-08-2026 |
| 7 | Comment on WP_REST_Request::get_param() by Rodrigo Vieira Eufrasio da Silva | 0 | 12.64 | 14-08-2026 |
| 8 | Comment on resolve_pattern_blocks() by Rodrigo Vieira Eufrasio da Silva | 0 | 10.65 | 14-08-2026 |
| 9 | PTE Request for Silvaitamar Duplicate… | 0 | 5 | 06-07-2026 |
| 10 | Hi, I’m the plugin author…. | 0 | 5 | 05-07-2026 |