How to Fix Function _load_textdomain_just_in_time was called incorrectly in WordPress 6.8

How to Fix Function _load_textdomain_just_in_time was called incorrectly in WordPress 6.8

If you upgraded to WordPress 6.8 and suddenly started seeing this notice:

Notice: Function _load_textdomain_just_in_time was called incorrectly…

you’re not alone.

Starting in WordPress 6.7 and continuing in 6.8, core became stricter about when translations are loaded and now surfaces a developer notice whenever a theme or plugin loads a text domain too early in the request lifecycle. (WordPress.org)

The good news: this is not a fatal error. It’s a warning that something in your code is running at the wrong time. In this guide, you’ll learn what the notice means and multiple safe ways to fix (or, if necessary, temporarily silence) it.

Notice: This issue is one we’ve personally encountered. You can explore similar cases and their solutions in our WordPress Bug Fixing category.


What the “Function _load_textdomain_just_in_time was called incorrectly” Notice Means

_load_textdomain_just_in_time() is part of WordPress’s internationalization (i18n) system. Its job is to load translation files only when a given text domain is actually used, instead of eagerly loading all .mo files. This “just in time” loading improves performance and memory usage. (WordPress Developer Resources)

The notice usually looks like this:

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the {your-domain} domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. (This message was added in version 6.7.0.)

In plain English:

  • A theme or plugin is causing translations for a text domain to load before WordPress is ready (i.e., before hooks like init are fired).
  • WordPress now warns you so you can move that code to the correct hook and avoid subtle bugs.

Common triggers:

  • Calling load_plugin_textdomain() or load_theme_textdomain() too early.
  • Using __(), _e(), etc. in code that runs before init (for example, in plugin bootstrap files or early hooks).
  • Calling load_textdomain() directly with a custom path.
  • Using APIs that translate strings too early, such as some get_plugin_data() usages with translation enabled.

Why You See This More in WordPress 6.7–6.8

WordPress 6.7 introduced several i18n changes:

  • Just-in-time loading is stricter about when translations are loaded.
  • A new developer notice was added for domains loaded too early, with the “added in version 6.7.0” message.
  • Themes and plugins that were “quietly wrong” for years now log notices as soon as you enable debugging.

When you upgrade to 6.8, the same checks continue, so any plugin or theme that hasn’t been updated will still trigger the warning.


Quick Overview of Fixes

In most cases, you’ll fix this by:

  • Loading plugin translations in plugins_loaded or init.
  • Loading theme translations in after_setup_theme (or init for some setups).
  • Ensuring text domains, file names, and paths match.
  • Avoiding direct load_textdomain() unless you really need it.
  • Optionally, using an MU plugin to mute this one deprecation notice while you clean things up.

Let’s walk through each method.


Method 1: Use WP_DEBUG to Find Exactly Where the Notice Comes From

Before you change anything, it’s worth confirming which plugin or theme is responsible.

Step 1: Enable debugging (preferably on a staging site)

Edit wp-config.php and make sure you’re not doing this on a live site unless you hide error output.

A safe debugging setup is: (WordPress Developer Resources)

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', true );

This will:

  • Turn on WordPress debugging.
  • Hide notices from visitors.
  • Log all notices and warnings to wp-content/debug.log.

Tip: Always debug on a staging or local site where possible, or at least keep WP_DEBUG_DISPLAY set to false in production.

Step 2: Trigger the notice

  • Visit both the front end and wp-admin while logged in.
  • Then open wp-content/debug.log and search for _load_textdomain_just_in_time.

The log line typically includes:

  • The text domain that’s being loaded too early.
  • A stack trace with the file and line responsible (e.g. wp-content/plugins/some-plugin/some-file.php).

Once you know who causes it, you can apply the relevant fix below.


Method 2: Load Plugin Translations on the Correct Hook

If the notice points to a plugin domain (e.g. woocommerce, my-plugin, titan-security, etc.), you likely need to move or wrap your load_plugin_textdomain() call.

Bad pattern (too early)

// my-plugin/my-plugin.php

load_plugin_textdomain(
    'my-textdomain',
    false,
    dirname( plugin_basename( __FILE__ ) ) . '/languages'
);

This runs as soon as the file is included, which is often earlier than WordPress expects.

Recommended pattern

// my-plugin/my-plugin.php

add_action( 'plugins_loaded', 'myplugin_load_textdomain' );

function myplugin_load_textdomain() {
    load_plugin_textdomain(
        'my-textdomain',
        false,
        dirname( plugin_basename( __FILE__ ) ) . '/languages'
    );
}

Steps:

  1. Open your plugin’s main PHP file (the one with the plugin header).
  2. Wrap your load_plugin_textdomain() call in a function.
  3. Hook that function to plugins_loaded (or init if you need the current user set before translations are used).
  4. Make sure your .mo files actually exist in either:
    • wp-content/languages/plugins/, or
    • wp-content/plugins/your-plugin/languages/.

Why it works:

  • plugins_loaded fires after all active plugins are included, but before most of WordPress starts outputting anything.
  • This gives WordPress time to set up the internal registry so _load_textdomain_just_in_time() can do its job without complaining.

Method 3: Load Theme Translations on the Correct Hook

For themes, the same idea applies, but the recommended hook is usually after_setup_theme.

Bad pattern

// functions.php

load_theme_textdomain( 'mytheme', get_template_directory() . '/languages' );

Recommended pattern

// functions.php

add_action( 'after_setup_theme', 'mytheme_load_textdomain' );

function mytheme_load_textdomain() {
    load_theme_textdomain(
        'mytheme',
        get_template_directory() . '/languages'
    );
}

Steps:

  1. Open your theme’s functions.php.
  2. Wrap load_theme_textdomain() in a function.
  3. Hook it into after_setup_theme (or init if you have a special setup).
  4. Ensure your .mo files live in:
    • wp-content/languages/themes/, or
    • wp-content/themes/your-theme/languages/.

For child themes, use load_child_theme_textdomain() on the same hook:

add_action( 'after_setup_theme', 'my_child_theme_setup' );

function my_child_theme_setup() {
    load_child_theme_textdomain(
        'parent-theme-slug',
        get_stylesheet_directory() . '/languages'
    );
}

Method 4: Check Text Domains, File Names, and Locations

Even if you load translations at the right time, mistakes in file names or domains can still cause warnings and missing translations.

1. File locations

  • Plugins
    • Recommended: wp-content/languages/plugins/your-plugin-slug-locale.mo
    • Fallback: wp-content/plugins/your-plugin/languages/your-plugin-slug-locale.mo
  • Themes
    • Recommended: wp-content/languages/themes/your-theme-slug-locale.mo
    • Fallback: wp-content/themes/your-theme/languages/locale.mo (no text domain prefix).

2. File names

Use the pattern:

textdomain-locale.mo

Examples:

  • myplugin-en_US.mo
  • mytheme-de_DE.mo

3. Text domain consistency

Whatever domain you use in load_plugin_textdomain() or load_theme_textdomain() must match the domain used in translation functions:

__( 'My string', 'myplugin' );  // OK
_e( 'My string', 'myplugin' );  // OK

Avoid:

__( 'My string', 'my-plugin' ); // mismatch
__( 'My string', 'MyPlugin' );  // mismatch

Mismatched domains mean WordPress can’t find the correct translation and may still log notices in debug mode.


Method 5: Avoid Direct load_textdomain() Calls

A common pattern in older codebases is:

load_textdomain(
    'myplugin',
    WP_PLUGIN_DIR . '/myplugin/languages/myplugin-en_US.mo'
);

This bypasses WordPress’s modern i18n system and is more likely to interact badly with the just-in-time loader, especially in 6.7+.

Instead, prefer the wrapper functions:

// Plugins
load_plugin_textdomain(
    'myplugin',
    false,
    dirname( plugin_basename( __FILE__ ) ) . '/languages'
);

// Themes
load_theme_textdomain(
    'mytheme',
    get_template_directory() . '/languages'
);

These:

  • Register the custom path with WordPress’s text domain registry.
  • Work with core’s automatic translation loading.
  • Reduce the chance of conflicting with _load_textdomain_just_in_time().

Method 6: (Advanced) Temporarily Silence the Notice via MU Plugin

If you’re dealing with a legacy or abandoned plugin that you can’t easily fix (for example, no access to its source code, or updates will overwrite your changes), you may choose to mute only this specific deprecation notice to keep logs and front-end output clean.

Important: this does not fix the underlying timing issue. It only hides the _load_textdomain_just_in_time “doing it wrong” notice so you can keep working without noisy logs. Similar approaches are already used in the community as a workaround. (iTomation)

Step 1: Define a toggle constant (optional but recommended)

In wp-config.php, above the “That’s all, stop editing!” line, add:

define( 'DISABLE_DEPRECATION_NOTICE_LOAD_TEXTDOMAIN', true );

This lets you turn the suppression on/off without removing the file.

Step 2: Create an MU plugin

Create the file:

wp-content/mu-plugins/silence-jit-i18n-notice.php

Add this code:

<?php
/**
 * Mute the '_load_textdomain_just_in_time' deprecation warning.
 */

if ( defined( 'DISABLE_DEPRECATION_NOTICE_LOAD_TEXTDOMAIN' ) && DISABLE_DEPRECATION_NOTICE_LOAD_TEXTDOMAIN ) {
    add_filter(
        'doing_it_wrong_trigger_error',
        fn( $doing_it_wrong, $function_name ) => $function_name !== '_load_textdomain_just_in_time' ? $doing_it_wrong : false,
        10,
        2
    );
}

What this does:

  • Hooks into the doing_it_wrong_trigger_error filter, which controls whether “doing it wrong” notices become PHP warnings.
  • If the offending function is _load_textdomain_just_in_time, it returns false, preventing the notice from being triggered.
  • Because it’s an MU plugin, it loads automatically on every request.

Again: use this as a temporary shield, not a permanent solution. You should still fix or replace the plugins/themes that load translations too early where possible.


Best Practices to Avoid Translation Errors in WordPress 6.7+ and 6.8

To future-proof your code against similar issues:

  1. Use the right hooks
    • Plugins: register translation paths early, but make sure real translation usage (or anything that triggers _load_textdomain_just_in_time) happens no earlier than init. In practice, plugins_loaded or init is a safe place for loading plugin text domains.
    • Themes: load text domains in after_setup_theme (classic/child themes) or rely on core’s automatic loading for properly configured block themes.
  2. Keep translations in the expected locations
    Use wp-content/languages/plugins/ and wp-content/languages/themes/ for production translations wherever possible.
  3. Use consistent text domains
    Match the text domain used in headers, load_*_textdomain() calls, and translation functions (__(), _e(), etc.).
  4. Don’t expose notices on live sites
    On production, keep: define( 'WP_DEBUG', true ); define( 'WP_DEBUG_DISPLAY', false ); define( 'WP_DEBUG_LOG', true ); so notices go to logs instead of visitors’ screens.
  5. Test updates in a staging environment
    When upgrading WordPress (especially major versions like 6.7 or 6.8), test your stack in a staging copy first and inspect debug.log for new notices.

Final Thoughts

The “Function _load_textdomain_just_in_time was called incorrectly” notice is WordPress nudging you toward better translation loading practices, not a sign that your site is broken.

If you:

  • Move translation loading into the proper hooks,
  • Ensure your text domains and file paths are correct,
  • Avoid direct load_textdomain() where possible, and
  • Optionally use an MU plugin to mute this one deprecation notice while you refactor legacy code,

you’ll eliminate the warning and be better aligned with the modern i18n system shipped in WordPress 6.7 and 6.8.

  • How To Use the WordPress Register Sidebar Function

    How To Use the WordPress Register Sidebar Function

    In this article, we’ll explore practical approaches for using the WordPress register sidebar function. We’ll also share some advanced techniques to help you get even more out of your sidebars! WordPress Register Sidebar – Single If you’d like to add a sidebar to your WordPress theme, the first step is to let WordPress know about…

  • How to Rename WordPress User Roles Safely

    How to Rename WordPress User Roles Safely

    On a recent client project, we ran into a surprisingly annoying UX bug: user roles with old names that no longer matched the business. The client runs a subscription-based WordPress site. At launch they had separate “Print” and “Digital” products, with roles like: Later, they introduced a combined “Bundle” subscription (print + digital). Marketing and…