Forum categories

Использование hreflang, IETF-кодов и атрибута lang

Руководство : корректное использование hreflang, IETF-кодов и атрибута lang для мультиязычного сайта на Cotonti (i18n) с геопривязкой к одной стране


webitproff
webitproff • 2026-04-22 15:01 #1

Руководство : корректное использование hreflang, IETF-кодов и атрибута lang для мультиязычного сайта на Cotonti (i18n) с геопривязкой к одной стране

 

Полная статья по проблеме и ее решению

 

1. Введение: в чем реальная проблема

Большинство разработчиков воспринимают мультиязычность в Cotonti как простую задачу:

  • Есть языки (ua, ru, en, pl)
  • Есть URL с префиксом /en/, /ru/
  • Есть переводы

Но с точки зрения SEO и стандартов HTML этого недостаточно.

Проблема возникает на стыке трех вещей:

  1. URL языка (например: /en/)
  2. hreflang (например: en-AE)
  3. HTML lang (например: lang="en-AE")

И если эти три уровня не согласованы — поисковые системы начинают:

  • Путать языковые версии
  • Игнорировать hreflang
  • Считать страницы дублями
  • Неправильно определять региональную релевантность

2. Главная идея: язык ≠ регион

2.1 Ключевая концепция

Согласно стандарту RFC 5646 (IETF), код языка имеет формат:

язык-РЕГИОН

Примеры:

  • en-US — английский (США)
  • en-GB — английский (Великобритания)
  • ru-RU — русский (Россия)

2.2 Наш кейс: одна страна, разные языки

В вашем проекте ситуация иная:

Все пользователи находятся в одной стране — ОАЭ, но говорят на разных языках:

  • Русский
  • Английский
  • Украинский
  • Французский

Правильная модель:

ru-AE
en-AE
ua-AE
fr-AE

Важно: это не просто "красиво" — это критично для SEO.


3. Как это выглядит в реальном HTML

3.1 Пример для страницы

<link rel="alternate" hreflang="en-AE" href="https://cotonti.local/en/article-123">
<link rel="alternate" hreflang="ru-AE" href="https://cotonti.local/ru/article-123">
<link rel="alternate" hreflang="fr-AE" href="https://cotonti.local/fr/article-123">
<link rel="alternate" hreflang="x-default" href="https://cotonti.local/article-123">

3.2 HTML тег

<html lang="ru-AE">

4. Почему нельзя использовать просто "ru", "en"

Короткие коды типа ru — это:

  • НЕ географические
  • НЕ точные
  • НЕ оптимальные для SEO

Поисковик не понимает:

  • Это русский в России?
  • Или русский в ОАЭ?
  • Или русская версия глобального сайта?

В итоге — теряется региональный таргетинг.


5. Как решена проблема в новом коде

5.1 Введена карта IETF

$i18n_ietf_map = [
    'ua' => 'ua-AE',
    'ru' => 'ru-AE',
    'en' => 'en-AE',
    'pl' => 'pl-AE'
];

5.2 Что это дает

  • URL остаются короткими: /en/
  • SEO получает полный код: en-AE
  • HTML валиден
  • hreflang корректен

6. Логика генерации hreflang (разбор)

6.1 Получение переводов

$translated_locales = cot_i18n_list_page_locales($id);

Возвращает:

['ua', 'ru', 'en']

6.2 Преобразование в IETF

$hreflang = isset($i18n_ietf_map[$lc]) 
    ? $i18n_ietf_map[$lc] 
    : $lc;

6.3 Генерация URL

$url = $main_url . '/' . cot_url('page', $params, '', false, false);

6.4 Финальный тег

<link rel="alternate" hreflang="ru-AE" href="https://cotonti.local/ru/article">

7. Почему x-default всегда без языка

При omitmain = 1:

https://cotonti.local/article-123

Это:

  • каноническая версия
  • fallback для всех языков
  • точка входа

8. HTML lang: зачем он вообще нужен

<html lang="ru-AE"> используется:

  • браузерами
  • скринридерами
  • поисковыми системами

Без него:

  • падает accessibility
  • ухудшается SEO

9. Как работает новый блок установки lang

$current_short_locale = $i18n_locale ?: Cot::$cfg['defaultlang'];

$html_lang = isset($i18n_ietf_map[$current_short_locale]) 
    ? $i18n_ietf_map[$current_short_locale]
    : $current_short_locale;

$t->assign('HTML_LANG', $html_lang);

В шаблоне:

<html lang="{HTML_LANG}">

10. URL vs hreflang: важное различие

ЭлементНазначение
/en/Навигация
hreflang="en-AE"SEO
lang="en-AE"HTML стандарт

11. Почему это критично для SEO

Google использует hreflang чтобы:

  • не показывать RU пользователю EN страницу
  • не индексировать дубли
  • правильно ранжировать регионально

Без региона:

  • теряется локальная выдача
  • страницы конкурируют сами с собой

12. Частые ошибки

12.1 Использование только языка

hreflang="ru"

❌ Плохо

12.2 Несоответствие lang и hreflang

lang="ru-AE"
hreflang="ru-RU"

❌ Критическая ошибка

12.3 Неправильный x-default

hreflang="x-default" href="/ru/page"

❌ Нельзя


13. Текущий пример — правильный

Пример:

<link rel="alternate" hreflang="ru-AE" href="https://cotonti.local/ru/...">
<link rel="alternate" hreflang="x-default" href="https://cotonti.local/...">

Это:

  • соответствует стандартам
  • учитывает географию
  • не создает дублей

14. Влияние .htaccess

RewriteRule ^(en|ru|de|pl|ua)/(.*) $2?l=$1

Это:

  • преобразует URL → параметр
  • не влияет на hreflang напрямую
  • но влияет на структуру ссылок

15. Архитектурная схема

Пользователь → URL (/en/) → i18n → cot_url()
             ↓
        hreflang (en-AE)
             ↓
        HTML lang="en-AE"

16. Масштабирование

Добавление нового языка:

  1. Добавить в locales
  2. Добавить в $i18n_ietf_map

И всё — система сама подхватит.


17. Итог

Вы реализовали систему, которая:

  • Разделяет язык и регион
  • Соответствует RFC 5646
  • Корректно работает с SEO
  • Автоматически масштабируется

Это уже уровень production-grade архитектуры.


18. Заключение

Главный вывод:

URL — это не hreflang.

hreflang — это не lang.

Но все три должны работать вместе.

Именно это реализовано в вашем коде.


Документ основан на практической реализации и тестировании на проекте https://cotonti.local

This post was edited by webitproff (2026-04-28 06:26, 2 months ago)
1/1/0

🗿

🧙‍♂️ Well met, good traveler! Fate has guided your steps to our humble realm. Here, wise sages and masters of the coding craft ⚒️📜 gather to unravel the mysteries of the code ✨⚙️, share their knowledge, and add new chapters to our chronicles.
⬅️ To the left lie ancient scrolls 📚 filled with forgotten wisdom. ⬆️ Straight ahead, friendly folk offer counsel 💬 to those in need. ➡️ To the right, you may become a chronicler ✍️ yourself, sharing your craft and leaving words of wisdom for those yet to come.
📝 Delay no longer—tell us the name by which you are known, complete the rite of registration 🛡️, and join our fellowship!
🏰 Our brotherhood holds no secret initiation, yet many halls remain closed to wandering guests, for some knowledge 💎 is reserved for those who choose to walk this path beside us.