Руководство : корректное использование hreflang, IETF-кодов и атрибута lang для мультиязычного сайта на Cotonti (i18n) с геопривязкой к одной стране
Полная статья по проблеме и ее решению
1. Введение: в чем реальная проблема
Большинство разработчиков воспринимают мультиязычность в Cotonti как простую задачу:
- Есть языки (ua, ru, en, pl)
- Есть URL с префиксом /en/, /ru/
- Есть переводы
Но с точки зрения SEO и стандартов HTML этого недостаточно.
Проблема возникает на стыке трех вещей:
- URL языка (например:
/en/) - hreflang (например:
en-AE) - 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. Масштабирование
Добавление нового языка:
- Добавить в locales
- Добавить в $i18n_ietf_map
И всё — система сама подхватит.
17. Итог
Вы реализовали систему, которая:
- Разделяет язык и регион
- Соответствует RFC 5646
- Корректно работает с SEO
- Автоматически масштабируется
Это уже уровень production-grade архитектуры.
18. Заключение
Главный вывод:
URL — это не hreflang.
hreflang — это не lang.
Но все три должны работать вместе.
Именно это реализовано в вашем коде.
Документ основан на практической реализации и тестировании на проекте https://cotonti.local