Улучшения интернационализации в WordPress 6.7: переводим правильно и вовремя

В WordPress 6.7 заметно переработали интернационализацию: just-in-time загрузка переводов, новые хелперы, язык писем для администратора и более строгие предупреждения, если textdomain трогают слишком рано. Ниже – что изменилось на практике и как не словить лавину _doing_it_wrong после обновления.

Опора на официальный разбор: i18n improvements in 6.7 (Make/Core).

1. has_translation(): проверка без лишней нагрузки

Появилась функция has_translation(): можно узнать, есть ли перевод для строки, не обязательно «дёргая» тяжёлую логику там, где раньше приходилось угадывать через загрузку домена.

Сигнатура (упрощённо): has_translation( string $singular, string $textdomain = 'default', ?string $locale = null ): bool.

if ( has_translation( 'Settings saved.', 'my-text-domain' ) ) {
    echo esc_html__( 'Settings saved.', 'my-text-domain' );
} else {
    // fallback / другая ветка UI
}

Первый аргумент – singular-строка (как в __()), второй – textdomain. Не путайте с slug плагина.

2. Письма на языке администратора

С WP 4.7 у пользователей есть свой язык интерфейса. В 6.7 логика уведомлений умнее: если получатель – администратор сайта (в типичном сценарии связанный с admin_email / locale пользователя), письма могут уходить на языке админа, а не на языке фронта сайта.

Пример: сайт на испанском для посетителей, админ читает интерфейс по-английски – служебные письма ближе к языку админа. Для кастомных рассылок через wp_mail() всё равно проверяйте, какой locale активен в момент отправки (switch_to_locale / restore_previous_locale при необходимости).

wp_mail(
    get_option( 'admin_email' ),
    __( 'Notification', 'my-text-domain' ),
    __( 'Hello, this is a site notice.', 'my-text-domain' )
);

3. Откуда грузятся переводы тем и плагинов

Каноничное место для файлов с translate.wordpress.org и ручных .mo/.l10n.php – каталоги под WP_LANG_DIR:

wp-content/languages/plugins/{slug}-{locale}.mo
wp-content/languages/themes/{slug}-{locale}.mo
# плюс современные .l10n.php варианты в тех же деревьях

Пример:

wp-content/languages/themes/your-theme-ru_RU.mo

Явная загрузка по-прежнему возможна, но её лучше вызывать в правильный момент жизненного цикла:

add_action( 'after_setup_theme', function () {
    load_theme_textdomain( 'your-theme', get_template_directory() . '/languages' );
} );

// или централизованный каталог:
// load_theme_textdomain( 'your-theme', WP_LANG_DIR . '/themes' );

Для плагинов аналогично: load_plugin_textdomain() / JIT, без вызовов переводимых строк на верхнем уровне файла плагина при подключении.

4. Предупреждения о преждевременной загрузке

6.7 активнее сигналит, если перевод загружается «слишком рано» (до того, как i18n-стек готов). Типичный триггер – вызов __(), esc_html__(), get_plugin_data() с переводом прямо при include файла плагина.

Плохо (часто ловит notice в debug):

// top-level в main plugin file
define( 'MYPLUGIN_VERSION', get_plugin_data( __FILE__ )['Version'] );

Лучше отключить перевод в get_plugin_data или отложить чтение:

define(
    'MYPLUGIN_VERSION',
    get_plugin_data( __FILE__, false /* $translate */, false )['Version']
);

// строки интерфейса - на init / admin_init / after_setup_theme
add_action( 'init', function () {
    // load_plugin_textdomain( ... ); если всё ещё нужен явный вызов
} );

Общее правило: не вызывайте переводимые API и не читайте переведённые заголовки плагина до хуков вроде init / after_setup_theme.

5. Как поймать источник _doing_it_wrong

Включите WP_DEBUG / log на staging и повесьте трассировку:

add_action(
    'doing_it_wrong_run',
    static function ( $function_name ) {
        if ( '_load_textdomain_just_in_time' === $function_name ) {
            // error_log() / monolog / временный file_put_contents
            error_log( wp_debug_backtrace_summary() );
        }
    }
);

Удобно смотреть стек в Query Monitor (секции Doing It Wrong / Languages). Чаще всего виноват чужой плагин с top-level __() или тема, которая грузит textdomain в конструкторе при подключении functions.php слишком рано.

Чеклист перед/после 6.7

  • Нет переводимых вызовов на верхнем уровне main-файла плагина.
  • get_plugin_data( ..., false ), если нужна только версия/мета без перевода.
  • .mo/.l10n.php лежат в ожидаемых путях под languages/.
  • Staging с WP_DEBUG_LOG, прогон админки и cron.
  • Обновление «шумных» плагинов, если backtrace указывает на них.

Итог

i18n в WordPress 6.7 заточен под более позднюю и аккуратную загрузку переводов: быстрее типичные запросы, меньше сюрпризов с locale, зато строже к старому коду. Проверьте свои темы и плагины на premature translation loading, разложите языковые файлы по WP_LANG_DIR и держите debug включённым на staging после апдейта.

Источники и ссылки


Комментарии загружаются…