Проблеми перекладу Polylang/WPML: чому мови змішуються і як виправити

Було корисно?

Ваша головна сторінка англійською. Ваше меню — французькою. Сторінка товару наполовину іспанською, бо якийсь віджет вирішив творити на свій розсуд. Це не «багатомовність» — це міксер контенту.

Коли Polylang або WPML «змішують мови», це рідко відбувається випадково. Це передбачувана помилка: неправильний мовний контекст, неправильні ID об’єктів, невірні ключі кешу або міграція, яка тихо залишила відносини перекладів у ямі. Розгляньмо це як інцидент: відтворити, виміряти, ізолювати і застосувати виправлення, що переживуть наступне оновлення плагіна.

Чим насправді є «змішування мов» (а чим — ні)

Люди описують багато різних багів як «WPML змішує мови» або «Polylang зламаний». Під капотом існує кілька різних класів проблем:

  • Помилки контексту: WordPress рендерить сторінку мовою A, але віджет або запит працює мовою B, бо не успадкував поточну мову або її перезаписали.
  • Помилки зв’язків: На сайті є переклади, але зв’язки між оригіналами та перекладами відсутні або неправильні. Тоді плагін не може знайти відповідник і використовує фолбек.
  • Проблеми з таксономіями/меню: Записи перекладені, але категорії/теги/меню не перекладені або неправильно призначені. Інтерфейс показує «правильні» сторінки, тоді як навігація витягує «що є».
  • Проблеми кешу: Ваш кеш зберігає HTML для мови A і віддає його для мови B, бо ключ кешу не залежить від мови, cookie, URL чи заголовка.
  • Проблеми з переписом/канонікою: URL-адреси резольвляться, але виявлення мови обирає неправильну мову або редиректи префіксів мови працюють непослідовно.
  • Проблеми зі строковим перекладом: Рядки теми, заголовки віджетів чи ярлики ACF беруться з невірного домену або невірної мовної таблиці.

Також: іноді проблема не в плагіні перекладу. Тема з «хитрими» кастомними запитами й хардкодними ID може створити ідеальний хаос навіть на одномовному сайті. Додайте багатомовність — і це перетворюється з «особливості» на «інцидент».

Операційна істина: мішаний мовний вивід зазвичай детермінований. Якщо ви можете відтворити його з тим же URL + тими ж куками + тим самим станом кешу, його можна виправити. Якщо не можна відтворити — часто це варіація кешу або гонка між кількома кешами.

Як WPML і Polylang зберігають мову та переклади

WPML: групи перекладів, мовні коди та «element IDs»

WPML зазвичай відслідковує відносини перекладів у таблиці, яка групує «елементи» (пости, терміни, іноді строки) у групи перекладів. Кожен елемент має мовний код і ID групи перекладу. Якщо це відображення неправильне або неповне, WPML мусить вгадувати. Вгадування — це те, як ви отримуєте англійські пости з німецькими категоріями і перемикач мови, який веде нікуди.

Polylang: таксономія мов + пост-мета + відносини

Polylang використовує таксономію мов (терміни, що представляють мови) і зберігає відносини між перекладеними постами. За концепцією це просто, але це успадковує звичні «гострі кути» WordPress: кешування термінів, об’єктний кеш і кастомні запити, які не поважають таксономії.

Чому обидва можуть змішувати мови, хоч «налаштування виглядають правильно»

Бо сторінка налаштувань — це лише контрольна площина. Площина даних — це ваша база даних, шари кешу, правила перепису і все, що робить тема та плагіни в запитах. У продакшені помилка зазвичай одна з:

  • Відсутні зв’язки перекладів (цілісність даних)
  • Неправильне визначення мови (маршрутизація)
  • Ключ кешу без мови (кешування)
  • Кастомний запит ігнорує фільтри мови (логіка застосунку)

Цікаві факти і історичний контекст (чому це складно)

  1. WordPress не народився багатомовним. Протягом років «багатомовність» означала «встановити окремі сайти» або «зламати його плагінами». Ядро досі припускає одну локаль на запит.
  2. WPML популяризував «групи перекладів». Це робоча абстракція, але вона вимагає послідовного відображення між типами постів, термінами та мета.
  3. Polylang зробив ставку на таксономії WordPress. Розумно: він повторно використовує нативні механіки термінів, але також отримує їхні проблеми з кешуванням термінів.
  4. Об’єктний кеш змінив правила гри. Redis/Memcached зробили WordPress швидким, а потім зробили багатомовність дивною, бо багато тем/плагінів не варіюють ключі кешу за мовою.
  5. WooCommerce ускладнив ситуацію. Товари — це не просто пости; це пости плюс варіації, атрибути й таблиці пошуку. Несумісність мови може привести до неправильних цін, а не тільки тексту.
  6. «Мова в URL» стала SEO-нормою. Префікси (/fr/), субдомени та параметрні мови мають різну поведінку кешу і перепису. Деякі простіші для аналізу; жоден варіант не безкоштовний.
  7. Строковий переклад — це друга система. Переклад вмісту постів і UI-рядків зберігаються й шукаються по-різному, тому часто «виправляють пости», а заголовки й віджети лишаються неправильними.
  8. Правила canonical і hreflang еволюціонували. Пошукові системи стали вимогливішими до маркування мови/регіону. Оновлення плагіна, яке «виправляє SEO», може зламати припущення маршрутизації і вивести приховані баги.
  9. Сучасні конструктори (Elementor, WPBakery) додають ще один шар. Вони зберігають контент у серіалізованих блоґах; плагіни перекладу інтегруються, але крайові випадки навколо глобальних віджетів і шаблонів досі часті.

Швидкий план діагностики

Коли мови змішуються, не починайте з випадкового переключення налаштувань. Дійте як SRE: визначте шар, який бреше.

Перше: доведіть, чи це кеш

  • Тимчасово відключіть кеш сторінок (або обійдіть його) і повторно протестуйте той самий URL у двох мовах.
  • Перевірте, чи HTML відрізняється, коли ви змінюєте cookie мови, префікс URL мови або заголовок Accept-Language.
  • Якщо зміни є при обході кешу: ваші кеші неправильно ключуються або не очищаються по мові.

Друге: перевірте виявлення мови і маршрутизацію

  • Підтвердіть, що плагін вважає поточний запит мовою A.
  • Перевірте, що permalink-и/правила перепису консистентні й не дублюються кількома плагінами.
  • Переконайтеся, що у вас не активні одночасно WPML і Polylang (так, люди так роблять).

Третє: перевірте відносини перекладів і переклад таксономій

  • Візьміть один ламаний пост. Підтвердіть, що його переклад існує і зв’язаний.
  • Перевірте категорії/теги/меню на наявність еквівалентних перекладів.
  • Перевірте, чи запити теми враховують мову і не хардкодять ID.

Четверте: шукайте «корисні» оптимізації

  • Drop-in об’єктного кешу (Redis), плагіни персистентного кешу, CDN повного кешування сторінок і фрагментне кешування.
  • Кеші на рівні теми (transients, опції з збереженим рендереним HTML, кеши шаблонів конструкторів сторінок).

Правило рішення: якщо той самий URL віддає різні мови без зміни URL/cookie/заголовка — це майже завжди контамінація кешу або спільні фрагменти.

Практичні завдання: команди, виводи, рішення (12+)

Ці завдання передбачають, що у вас є доступ до shell і WP-CLI. Якщо ні — ви все одно можете застосувати логіку через панель хостингу, але з більше кліками і менше впевненістю.

Завдання 1: Підтвердити, який плагін мультимовності активний (і що лише один)

cr0x@server:~$ wp plugin list --status=active
+-----------------------+--------+-----------+---------+
| name                  | status | update    | version |
+-----------------------+--------+-----------+---------+
| wpml-multilingual-cms | active | none      | 4.6.11  |
| wpml-string-translation | active | available | 3.2.7 |
| redis-cache           | active | none      | 2.5.4   |
+-----------------------+--------+-----------+---------+

Що означає вивід: WPML активний, плюс Redis об’єктний кеш. Polylang відсутній. Добре.

Рішення: Якщо ви бачите одночасно WPML і Polylang — зупиніться. Деактивуйте один перед дебагом. Два провайдери мовного контексту = хаос.

Завдання 2: Переконатися, що WP-CLI бачить URL сайту і середовище

cr0x@server:~$ wp option get siteurl
https://example.com

Що це означає: Ви працюєте з очікуваним сайтом (не застарілою staging-базою).

Рішення: Якщо URL неправильний — виправте це спочатку. Неправильні WP-CLI запуску призводять до «дебагу», який стає «втратою даних».

Завдання 3: Перевірити структуру посилань (шар перепису)

cr0x@server:~$ wp option get permalink_structure
/%category%/%postname%/

Що це означає: Увімкнені красиві permalink-и. Плагіни мови будуть підключатись до цього.

Рішення: Якщо permalink-и порожні або неконсистентні між середовищами — скиньте правила перепису після підтвердження мовних налаштувань.

Завдання 4: Безпечно скинути правила перепису

cr0x@server:~$ wp rewrite flush --hard
Success: Rewrite rules flushed.

Що це означає: Перегенеровано правила перепису в базі і .htaccess (якщо використовується).

Рішення: Якщо мовні URL давали 404 або редиректили неправильно — протестуйте знову. Якщо нічого не змінилося — маршрутизація не є першопричиною.

Завдання 5: Виявити, чи присутній кеш сторінок (заголовки)

cr0x@server:~$ curl -sI https://example.com/fr/ | egrep -i 'cache|vary|set-cookie|x-cache|cf-cache|age'
cache-control: max-age=600
vary: Accept-Encoding
x-cache: HIT
age: 534

Що це означає: Є кеш (x-cache: HIT) і він не варіюється за cookie мови або заголовком, окрім /fr/ самого по собі.

Рішення: Якщо ви використовуєте cookie-детекцію мови, а кеш не варіюється за cookie — ви будете віддавати неправильну мову. Виправляйте шар кешування перш ніж міняти налаштування WPML/Polylang.

Завдання 6: Порівняти ту саму сторінку в двох мовах і шукати ідентичні ключі кешу

cr0x@server:~$ curl -sI https://example.com/ | egrep -i 'x-cache|age|cache-control'
x-cache: HIT
age: 410

cr0x@server:~$ curl -sI https://example.com/fr/ | egrep -i 'x-cache|age|cache-control'
x-cache: HIT
age: 410

Що це означає: Ідентичний age вказує, що кеш може зливати варіанти (залежить від стеку, але це підозріло).

Рішення: Якщо бачите підозрілу ідентичну поведінку кешу між мовами — обійдіть кеш і перевірте контент. Потім виправляйте ключування кешу.

Завдання 7: Обійти кеш і протестувати коректність мови (джерело правди)

cr0x@server:~$ curl -sH 'Cache-Control: no-cache' https://example.com/fr/ | grep -i '<html' -m1

Що це означає: Джерело правильно рендерить французьку (принаймні атрибут lang у HTML).

Рішення: Якщо джерело правильне, а кеш помилковий — припиніть дебаг даних WPML/Polylang. Починайте з виправлення шару кешування.

Завдання 8: Перевірити, чи увімкнено об’єктний кеш (тихий саботажник)

cr0x@server:~$ wp redis status
Status: Connected
Client: PhpRedis (v5.3.7)
Database: 0
Prefix: wp_cache:

Що це означає: Персистентний об’єктний кеш увімкнено. Добре для швидкості; небезпечно, якщо код не варіюється за мовним контекстом.

Рішення: Якщо проблеми з’явилися «після увімкнення Redis», підозрюйте кешовані результати запитів або фрагменти без мовних ключів.

Завдання 9: Очистити об’єктний кеш, щоб подивитися, чи зникає баг

cr0x@server:~$ wp cache flush
Success: The cache was flushed.

Що це означає: Усі кешовані об’єкти/запити/фрагменти видалено.

Рішення: Якщо проблема зникає після скидання і повертається пізніше — у вас проблема з ключуванням кешу. Не плануйте щогодинні скидання і називайте це «виправлено».

Завдання 10: Перевірити налаштування WPML у базі (саніті-чек)

cr0x@server:~$ wp option get icl_sitepress_settings --format=json | head
{"icl_lang_sel_type":"dropdown","language_negotiation_type":1,"urls":{"directory_for_default_language":0}}

Що це означає: Тип погодження WPML 1 зазвичай вказує на «мова по директорії». Налаштування існують і читаються.

Рішення: Якщо тип погодження — «cookie» або «parameter», переконайтеся, що кожен шар кешу варіюється відповідно. Якщо це «directory», перевірте, щоб перепис і канонічні редиректи їх зберігали.

Завдання 11: Перевірити, чи пост має пов’язані переклади (WPML)

cr0x@server:~$ wp db query "SELECT element_id, element_type, trid, language_code, source_language_code FROM wp_icl_translations WHERE element_id=123;"
+------------+--------------+------+---------------+----------------------+
| element_id | element_type | trid | language_code | source_language_code |
+------------+--------------+------+---------------+----------------------+
| 123        | post_post    | 9012 | en            | NULL                 |
+------------+--------------+------+---------------+----------------------+

Що це означає: Публікація 123 існує англійською, група перекладу 9012. Але ми ще не бачимо її французького брата.

Рішення: Запитайте по trid. Якщо інших рядків немає — переклади не зв’язані (або не існують). Перемикач мови може віддавати неправильне значення.

Завдання 12: Знайти всі переклади в тій групі (WPML)

cr0x@server:~$ wp db query "SELECT element_id, language_code FROM wp_icl_translations WHERE trid=9012;"
+------------+---------------+
| element_id | language_code |
+------------+---------------+
| 123        | en            |
| 456        | fr            |
+------------+---------------+

Що це означає: Переклад існує (французький пост ID 456) і зв’язаний.

Рішення: Якщо французька сторінка все ще показує англійські фрагменти, ймовірно причина в кеші, запитах теми або неперекладених таксономіях/строках, а не в відсутності зв’язку перекладів.

Завдання 13: Підтвердити, що терміни таксономій перекладені і зв’язані (WPML-терміни)

cr0x@server:~$ wp db query "SELECT element_id, element_type, trid, language_code FROM wp_icl_translations WHERE element_type LIKE 'tax_%' AND element_id=77;"
+------------+---------------------+------+---------------+
| element_id | element_type        | trid | language_code |
+------------+---------------------+------+---------------+
| 77         | tax_category        | 3001 | en            |
+------------+---------------------+------+---------------+

Що це означає: Термін категорії 77 зареєстрований лише для англійської в мапінгу WPML.

Рішення: Якщо меню/категорії змішують мови — відсутні переклади термінів є головним підозрюваним. Додайте/прив’яжіть перекладені терміни або увімкніть переклад термінів для цієї таксономії.

Завдання 14: Визначити кастомний запит у темі/плагіні, який ігнорує мовні фільтри

cr0x@server:~$ wp eval 'global $wp_query; echo "lang=".(defined("ICL_LANGUAGE_CODE")?ICL_LANGUAGE_CODE:"none")."\n";'
lang=fr

Що це означає: Контекст WPML для цього запиту — французький (корисно при запуску в веб-контексті; в CLI залежить від bootstrap).

Рішення: Якщо WPML говорить «fr», але ваш віджет показує англійські пости — віджет, ймовірно, виконує запит, який обходить фільтри WPML/Polylang (наприклад, suppress_filters=true) або використовує прямий SQL.

Завдання 15: Знайти підозрілі патерни запитів у кодовій базі (suppress_filters)

cr0x@server:~$ grep -R --line-number "suppress_filters" /var/www/html/wp-content | head
/var/www/html/wp-content/themes/acme/functions.php:812:  'suppress_filters' => true,
/var/www/html/wp-content/plugins/acme-widgets/widget-latest.php:55: $q = new WP_Query(['suppress_filters'=>true,'post_type'=>'post']);

Що це означає: Код явно відключає фільтри. Хуки WPML/Polylang — це фільтри. Ось ваш курильний стовпчик.

Рішення: Видаліть suppress_filters або додайте явні мовні обмеження через API плагіна, або використовуйте їхні контекстні хелпери для прив’язки запитів до мови. Якщо suppress_filters потрібен заради продуктивності — доведеться вручну обробляти мовне скопування та ключі кешу.

Завдання 16: Перевірити, чи cron «корисно» регенерує кеш у неправильній мові

cr0x@server:~$ wp cron event list | egrep -i 'wpml|polylang|cache|preload' | head
wpml_cache_clear         2025-12-27 03:10:00 +0000
rocket_preload_cache     2025-12-27 03:15:00 +0000

Що це означає: Існує пре-лоадер кешу. Якщо він прогріває лише URL-адреси мови за замовчуванням, він може перезаписувати загальні записи кешу.

Рішення: Робіть пре-лоад мови-усвідомленим (прогрівайте всі варіанти URL для кожної мови) або виправте ключування кешу так, щоб варіанти не могли перезаписувати один одного.

Жарт №1: Багатомовний сайт без кешу, що враховує мову, — це як двомовна секретарка, яка відповідає на кожен дзвінок тією мовою, яку вона вивчила останньою.

Великі режими відмов: чому мови змішуються

1) Ключ кешу не включає мову (кеш повної сторінки)

Це найпоширеніша корінна причина в реальному житті, бо вона проявляється як «випадково». Насправді не випадково; переможцем стає та мова, яка першою прогріла кеш.

Типові тригери:

  • Переключення з «мова в URL» на «мова за cookie» без оновлення конфігурації кешу.
  • Додавання CDN або реверс-проксі, що ігнорує cookie або обрізує query-параметри.
  • Використання плагіна кешування, оптимізованого для одномовних сайтів.

Виправлення: варіюйте кеш за мовою. На практиці це означає: окремі URL для кожної мови (переважно), або включити cookie мови в ключ кешу, або обходити кеш коли присутній cookie мови.

2) Об’єктний кеш і фрагментні кеші без мовного скопування

Кеш повної сторінки не є єдиним винуватцем. Персистентний об’єктний кеш може віддавати кешовані результати запитів між мовами, якщо ключ кешу не включає мову. WPML/Polylang намагаються інтегруватись, але вони не можуть виправити кастомні транзієнти або кеши теми, які ви написали в 2017 і забули.

Де це трапляється:

  • Transients, що зберігають відрендерений HTML для віджетів
  • Опції теми, що зберігають «кешоване меню HTML»
  • Кастомні хелпери «get_posts_cached()»

Виправлення: включайте мовний код у кожен ключ транзієнту. Якщо не можете — видаліть оптимізацію і живіть далі.

3) Кастомні запити, що відключають фільтри

Якщо ви бачите suppress_filters=true, вважайте, що багатомовність зламана, допоки не доведено протилежне. WPML і Polylang покладаються на фільтри для корекції запитів.

Виправлення: приберіть suppress_filters або додайте явні мовні обмеження через API плагіна, або використовуйте їхні хелпери поточної мови для обмеження запитів.

4) Переклади таксономій відсутні або неправильно зв’язані

Саме так ви отримуєте французьку сторінку з англійськими категоріями або перемикач мови, що зберігає той самий slug категорії, але змінює мову сторінки. Термін має власне відображення перекладу. Якщо воно відсутнє, плагіни роблять фолбек.

Виправлення: перекладіть терміни, призначте правильні перекладені терміни перекладеним постам і переконайтеся, що меню збираються для кожної мови окремо.

5) Конфігурація меню і віджетів не прив’язана до мови

Меню — це конфігурація. На багатомовному сайті конфігурацію також потрібно скопувати під мову. WPML може синхронізувати меню; Polylang може призначати меню на мову. Але якщо у вас одне меню з хардкодними посиланнями — ви побудували машину змішування контенту.

Виправлення: розділіть меню по мовах або використовуйте підтримувані плагіном інструменти синхронізації; уникайте хардкодних абсолютних посилань на мову за замовчуванням.

6) Налаштування «директорії мови за замовчуванням» ускладнює каноніку

Класична конфігурація WPML — «директорія для мови за замовчуванням: вимкнено», тобто мова за замовчуванням живе в /, інші — у /fr/, /de/ тощо. Це працює, але збільшує шанс, що кеші і канонічні редиректи сплющать шляхи і неправильно визначать мову.

Виправлення: якщо можете собі дозволити — помістіть мову за замовчуванням у директорію також. Це операційно нудно, але найнадійніше для URL-дизайну.

7) Змішаний вміст всередині одного поста (людський робочий процес)

Іноді плагін у порядку, а проблема — редакційна: перекладачі копіюють блоки, конструктори сторінок повторно використовують глобальні шаблони, або хтось редагував неправильну мовну версію, бо панель адміністратора вводила в оману.

Виправлення: жорстко регламентуйте робочі процеси: ролі, права і явний пайплайн перекладу (навіть якщо це «завжди дублюй, потім перекладай, ніколи не редагуй оригінал»).

Поширені помилки: симптом → корінь → виправлення

1) Симптом: Головна іноді змінює мову без зміни URL

Корінь: кеш повної сторінки не варіюється за cookie або заголовком мови.

Виправлення: використайте мову в URL, або налаштуйте кеш варіюватися за cookie мови; очистіть кеш після зміни методу визначення мови.

2) Симптом: Меню показує елементи невірною мовою, але контент сторінки правильний

Корінь: меню призначене глобально, а не по мовах; або кешоване меню не має мовного ключа.

Виправлення: призначте меню по мовах (функція WPML/Polylang) і приберіть фрагментне кешування меню або додайте мову в ключ.

3) Симптом: Архів категорії показує пости різних мов

Корінь: кастомний запит використовує suppress_filters; відсутній переклад таксономії; або ввімкнено налаштування «показувати всі мови» для таксономії.

Виправлення: приберіть suppress_filters; перекладіть і зв’яжіть терміни; переконайтеся, що таксономія встановлена як «перекладна» і фільтрується поточною мовою.

4) Симптом: Перемикач мов веде деякі сторінки на головну

Корінь: відсутні зв’язки перекладу для цього типу контенту (пост/термін/товар), або переклад існує, але не зв’язаний у групі перекладів.

Виправлення: відбудуйте зв’язки перекладів (дублюйте і зв’яжіть правильно), запустіть інструменти «усунення неполадок» плагіна і перевірте мапінг у БД для уражених елементів.

5) Симптом: Кошик/оформлення замовлення WooCommerce показує змішані мовні рядки

Корінь: рядки походять із доменів теми/плагіна, не зареєстрованих для перекладу, або кешовані фрагменти (мінi-корзина) збережені без мовного ключа.

Виправлення: зареєструйте рядки правильно, очистіть кеші і переконайтеся, що фрагменти кошика варіюються за cookie/URL мови.

6) Симптом: Мова адміністратора при редагуванні «не та», через що редагують неправильний переклад

Корінь: плутанина між мовою профілю користувача, мовою сайту та налаштуваннями адмін-мови WPML; редактори використовують швидке редагування без перевірки мови.

Виправлення: уніфікуйте поведінку адміна по мовах, навчіть редакторів перевіряти індикатори мови і обмежте, хто може редагувати оригінали.

7) Симптом: Теги hreflang неправильні або відсутні періодично

Корінь: кешований head спільний для мов; або канонічний редирект обрізає директорію мови; або неконсистентні налаштування permalink-ів.

Виправлення: виправте ключування для фрагментів head; перевірте поведінку canonical; забезпечте консистентний формат URL для всіх мов.

8) Симптом: Після міграції переклади існують, але «не зв’язані»

Корінь: міграція/імпорт БД не зберегла таблиці плагіна або пост-мета; або синхронізація зі стенду на прод пропустила таблиці перекладів.

Виправлення: перезапустіть міграцію, включивши таблиці WPML/Polylang; уникайте часткових експортів; перевірте кількість рядків в таблицях перекладів до перенесення.

Жарт №2: «Ми просто скопіюємо базу даних і подивимось, що станеться» — це мультимовний еквівалент «я просто перезавантажу». Іноді допомагає, але це не стратегія.

Три корпоративні міні-історії з практики

Міні-історія 1: Інцидент через хибне припущення

У них був акуратний WordPress-стек: WPML, три мови, плагін кешування і CDN. Маркетинг скаржився, що французькі відвідувачі іноді бачили англійські заголовки. Інженери зробили класичне: звинуватили WPML і відкрили тикет у вендора плагіна.

Хибне припущення було простим: «мова визначається URL, отже кеш безпечний». Насправді мова за замовчуванням віддавалася на /, і сайт використовував cookie, щоб запам’ятати останньо вибрану мову для деяких користувачів. CDN ігнорував cookie за дизайном (заради продуктивності!) і кешував / як один об’єкт.

Це означало, що та мова, яка перша звернулась до / після очищення, «вигравала» кеш. Французи, що обирали французьку, прогрівали французький HTML на /, CDN зберігав його, і тепер англомовні користувачі отримували французьку, поки не відбудеться чергове очищення. Інцидент був періодичним, дратівливим і повністю детермінованим.

Виправлення не було налаштуванням WPML. Вони змінили погодження мови на директорійний для всіх мов, включаючи мову за замовчуванням (тому англійська стала /en/), оновили редиректи, очистили кеши — і проблема зникла. Також додали синтетичну перевірку, яка фетчить домашню сторінку кожної мови і перевіряє, що <html lang> відповідає. Нудно, але ефективно.

Міні-історія 2: Оптимізація, що обернулась проти

Продуктова команда хотіла швидші сторінки категорій. Хтось додав «поліпшення продуктивності»: кешувати відрендерений віджет «Топ товарів» у транзієнті на 30 хвилин, бо він робив важкий запит. Гарна ідея для одномовного сайту.

На цьому сайті товари були перекладені по мовах, а ціни/доступність відрізнялись по ринку. Кешований HTML віджета зберігався під ключем типу top_products_widget без включення мови. Тож перший запит, що прогрів кеш (часто англійський, через внутрішніх користувачів), визначав, що бачитимуть всі наступні 30 хвилин.

Стало гірше: віджет включав посилання на товари в тій мові, що прогріла кеш. Тому французькі сторінки категорій показували англійські назви товарів, англійські URL та інколи англійське форматування валюти. Не катастрофа, але підривало довіру і збільшувало звернення в підтримку. Команда пробувала «очищати кеш при публікації», що лише підсилювало флап.

Врешті-решт виправили так: видалили кешований HTML і натомість кешують ID товарів per language (і per category), а під час запиту рендерять правильні назви/URL за мовою. Це більше навантаження на CPU, але відновило коректність. У production правильність — це також продуктивність. Користувачі не купують те, чого не розуміють.

Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію

Інша компанія працювала на Polylang з WooCommerce і Redis. Їх уже підпалювали раніше, тому вони вчинили дуже нецікаво: кожен реліз супроводжувався чек-листом мультимовної перевірки. Спочатку не автоматизовано; просто дисципліна.

Чек-лист включав: відкрити головну, одну товарну сторінку, один архів категорії, кошик і оформлення замовлення в кожній мові; перевірити меню; перевірити форматування валюти і локалі; перевірити посилання перемикача мови; перевірити наявність hreflang. У них також було правило: якщо додаєш кешування — документуй ключ і що його варіює.

Одного дня розробник оновив тему, яка додала опцію «глобальний кеш заголовка». Вона покращила Time To First Byte і зробила панелі приємнішими. Smoke-тести спіймали, що заголовок показує неправильну мову на не-стандартних мовах одразу після деплою.

Їм вистачило відкотитись за кілька хвилин, а потім повторно задеплоїти із відключеним кешем заголовка, поки не виправили його під мову. Ніякої драматичної історії, жодного великого впливу на клієнтів, ніяких «загадкових» тикетів. Нудна практика врятувала ситуацію.

Чек-листи / покроковий план

Покроковий план виправлення мішаного мовного виводу (розумний порядок)

  1. Виберіть один відтворюваний URL, що показує мішану мову. Запишіть його. Не ганяйтесь за десятьма багами одразу.
  2. Обійдіть кеш повної сторінки і порівняйте вивід. Якщо обхід вирішує проблему — ви в області кешу.
  3. Скиньте об’єктний кеш і протестуйте знову. Якщо це тимчасово допомагає — у вас об’єктний кеш або фрагментний кеш без мовного ключа.
  4. Підтвердіть метод погодження мови (директорія/субдомен vs cookie vs параметр). Збіжте його зі стратегією кешування.
  5. Аудит кастомних запитів на suppress_filters, прямі SQL і хардкодні ID.
  6. Підтвердіть зв’язки перекладів для проблемного контенту: пости й терміни.
  7. Перевірте меню по мовах і переконайтеся, що тема рендерить правильне меню залежно від мови.
  8. Перевірте домени строкових перекладів для теми і плагінів; переконайтеся, що потрібні рядки є в кожній мові.
  9. Очищайте кеш у правильному порядку: CDN/реверс-проксі → плагін кешу сторінок → об’єктний кеш → кеш браузера.
  10. Додайте моніторинг: простий скрипт, що фетчит ключові сторінки по мовах і перевіряє мовні маркери.

Операційний чек-лист: перед зміною налаштувань

  • Резервна копія бази даних (включно з таблицями WPML/Polylang) і wp-content.
  • Занотуйте поточний тип погодження мови і стратегію URL.
  • Перелічіть усі шари кешу: браузер, CDN, реверс-проксі, плагін кешу сторінок, об’єктний кеш.
  • Переконайтеся, що стенд повторює проблему.

Операційний чек-лист: після застосування виправлення

  • Тест шляхів для анонімного користувача (саме тут застосовується більшість кешів).
  • Тест шляхів для залогіненого редактора (інші cookie, інша поведінка кешу).
  • Перевірте посилання перемикача мови для щонайменше 5 випадкових сторінок, не лише головної.
  • Перевірте архіви таксономій і результати пошуку (там кастомні запити люблять ховатися).
  • Переконайтеся, що hreflang і canonical відповідають фактичним URL-ам.

FAQ

1) Чому сайт змішує мови тільки «іноді»?

Тому що кеші не «іноді» варіюються за мовою; вони варіюються за тим, що містить ключ кешу. Якщо мова не входить у ключ, виграє та мова, що прогріла кеш останньою до його витіснення.

2) Чи завжди краща мова в URL, ніж мова за cookie?

Операційно — так. URL робить кешування, дебаг і SEO більш детермінованими. Визначення мови за cookie може працювати, але кожен шар кешу повинен варіюватися за цим cookie, і багато хто цього не робить, якщо ви цього не заставите.

3) Контент сторінки правильний, але віджети — неправильно. Чому?

Віджети часто виконують окремі запити і можуть використовувати кешовані фрагменти. Якщо ті запити відключають фільтри або використовують транзієнти без мовних ключів — віджет може відходити від мови сторінки.

4) Чи може Redis об’єктний кеш викликати змішування мов?

Так, побічно. Redis не проблема сам по собі; він зберігає те, що просить ваш код. Якщо тема/плагін кешує «останні пости» без мови в ключі, Redis масштабно віддаватиме невірну мову.

5) Чому перемикач мови іноді відсилає деякі сторінки на головну?

Зазвичай це відсутні зв’язки перекладу (переклад існує, але не підключений) або переклад взагалі відсутній. Перемикач не може вигадавити ціль, тож робить фолбек на безпечну сторінку.

6) Після міграції переклади виглядають дубльованими або невзаємозвʼязаними. Що сталося?

Часткові експорт/імпорт бази даних часто пропускають таблиці плагінів або переписують ID постів так, що посилання груп перекладів ламаються. Міграції повинні включати таблиці плагіна і зберігати ID або коректно зіставляти їх після імпорту.

7) Як зрозуміти, це переклад таксономії чи поста?

Якщо основний контент правильний, але категорії/теги, хлібні крихти і меню — неправильні, підозрюйте переклад таксономії. Якщо тіло поста неправильне або посилки перемикача — підозрюйте звʼязки постів або маршрутизацію.

8) Чи «виправити» це вимкненням кешів назавжди?

Ні. Це не виправлення; це відкладений інцидент продуктивності. Зробіть кеші мовно-усвідомленими і очищуйте їх коректно. Збережіть швидкість і коректність.

9) Ми використовуємо конструктор сторінок. Чи змінює це щось?

Додає більше кешів і більше місць для зберігання глобальних шаблонів. Багато проблем зі змішаними мовами походять від глобальних віджетів/шаблонів, що повторно використовуються між мовами без мовно-усвідомлених варіантів.

10) Яке найшвидше довгострокове рішення, якщо застрягли?

Перейдіть на мову в URL для всіх мов (включно з мовою за замовчуванням), потім очистіть всі кеші і збережіть permalink-и. Це зменшить ступені свободи, що спричиняють змішування.

Цитата про надійність, варта зберегти

«Сподівання — не стратегія.» — часто приписують операційній культурі (перефразована ідея)

Висновок: наступні кроки, що працюють

Якщо робити лише дві речі, зробіть ці: зробіть мову явною в URL і зробіть кожен шар кешу залежним від мови. Більшість інцидентів «змішування мов» зникають, коли маршрутизація і кешування перестають імпровізувати.

Далі виконайте менш захопливу роботу: перевірте suppress_filters, приберіть транзієнти без мови, перекладіть і зв’яжіть таксономії, і додайте легкий моніторинг, що перевіряє ключові сторінки по мовах після кожного деплою. Багатомовний WordPress може бути стабільним. Він просто не стає стабільним випадково.

← Попередня
MySQL проти MariaDB на VPS: mysqldump проти фізичних резервних копій
Наступна →
Оновлення вкрали мої FPS: міф, реальність і нудна правда

Залишити коментар