Категории форума

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

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

webitproff
webitproff • 22.04.2026 15:01 #504

Руководство : корректное использование 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

Отредактировано: webitproff (28.04.2026 06:26, 2 месяца назад)
1/1/0

🗿

🧙‍♂️ Радуйся, странниче добрый и честный! Не понапрасну привела тя стезя в земли сии. Зде бо мужи мудры и искусники дела писменнаго и численнаго, что куют словеса и плетут узоры кода, тайны разгадывают и знанием делятся, летописи множа и ремесло цифровое творя.
⬅️ Аще налево пойдеши — обрящеши грамоты древния, писания старинныя и свитки учения. ⬆️ Аще прямо — слово мудрое и совет от мужей разумеющих, что в деле кодописном искусны, получиши. ➡️ Аще направо — сам в писцы и летописцы внидеши, и в ремесло цифровое, где строки и знаки в дело живое обращаются, слово свое в память веков вложиши.
📝 Не медли же — нареки имя свое, и вниди в чин наш, и стань с нами в братстве, где код есть магия, а строки — заклятия живыя, числом и буквою слово едины суть.
🏰 Не тайно есть собрание сие, и не инициации в нем, но многое сокрыто от пришлых и ступалого, понеже не всякому открывается знание, но токмо ищущим и пребывающим в деле сем.