Как сообщить Google о других языках на вашем сайте Django

Если у вас есть общедоступный сайт Django на нескольких языках, вы, вероятно, захотите сообщить об этом Google и другим поисковым системам.

Linguistic map of the world (<a href="https://en.wikipedia.org/wiki/Linguistic_map">source</a>)
Linguistic map of the world (source)

Многоязычный сайт Django

Django имеет очень обширную структуру для обслуживания сайтов на нескольких языках. Ознакомимся с базовыми настройками, необходимыми для добавления дополнительных языков на сайт Django.

Активируйте фреймворк i18n в settings.py:

# settings.py

USE_I18N = True

Определите поддерживаемые языки:

# settings.py

from django.utils.translation import gettext_lazy as _

LANGUAGES = [
    ('en', _('English')),
    ('ru', _('Russian')),
]

Установите язык по умолчанию:

# settings.py

LANGUAGE_CODE = 'en'

ДобавьтеLocaleMiddleware:

# settings.py

MIDDLEWARE = [
    # ...
    'django.middleware.locale.LocaleMiddleware',
    # ...
]

Используйте gettext, чтобы пометить тексты для перевода:

# app/views.py

from django.utils.translation import gettext_lazy as _
from django.http import HttpResponse

def about(request) -> HttpResponse:
    return HttpResponse(_('Hello!'))

Создайте файлы перевода:

$ python manage.py makemessages

Переведите текст:

msgid "Hello!"
msgstr "Привет!"

Скомпилируйте файлы перевода:

$ python manage.py compilemessages

Настройте представления на нескольких языках с помощью i18n_patterns:

# urls.py

from django.conf.urls.i18n import i18n_patterns
from django.conf.urls import url

from . import views


urlpatterns = i18n_patterns(
    url(r'^about$', views.about, name='about'),
)

Убедитесь, что это работает:

$ curl http://localhost:8000/en/about
Hello!

$ curl http://localhost:8000/ru/about
Привет!

Это всё!

Есть несколько дополнительных шагов, таких как добавление представления для переключения языка, но в целом ваш многоязычный сайт Django готов к работе!

Чтобы поисковые системы знали, что страница доступна на другом языке, вы можете использовать специальный тег ссылки:

<link rel="alternate" hreflang="en" href="https://example.com/en" />

Тег имеет следующие атрибуты:

  • hreflang: Код языка связанной страницы.
  • href: Ссылка на страницу на указанном языке.

Согласно рекомендациям Google и информации в Википедии, мы должны соблюдать следующие правила:

  1. Используйте абсолютные URL-адреса, включая схему.
  2. Ссылка должна быть действительной, а связанная страница должна быть на указанном языке.
  3. Перечислить все языки, включая текущий.
  4. Если язык X ссылается на язык Y, язык Y должен ссылаться обратно на язык X.

Чтобы реализовать следующее в Django, начните с перечисления доступных языков в шаблоне и установите код языка в атрибуте hreflang:

{% load i18n %}

{% get_available_languages as LANGUAGES %}
{% for language_code, language_name in LANGUAGES %}
<link
    rel="alternate"
    hreflang="{{ language_code }}"
    href="TODO" />
{% endfor %}

Следующим шагом будет добавление локализованных ссылок для каждого языка. Потребовалось немного покопаться, но оказалось, что в Django уже есть функция translate_url, которую мы можем использовать:

>>> from django import urls
>>> from django.utils import translation
>>> translation.activate('en')
>>> reverse('about')
'/en/about'
>>> urls.translate_url('/en/about', 'ru')
'/ru/about'

Функция translate_url принимает URL-адрес и язык и возвращает URL-адрес на этом языке. В приведенном выше примере мы активировали английский язык и получили URL-адрес с префиксом /en.

В правилах требуются абсолютные URL-адреса.

Убедитесь, что translate_url может обрабатывать и абсолютные URL:

>>> urls.translate_url('https://example.com/en/about', 'ru')
'https://example.com/ru/about'

Отлично! translate_url может «переводить» абсолютные URL-адреса.

Как насчет URL-адресов с параметрами запроса или хешем?

>>> urls.translate_url('https://example.com/en/about?utm_source=search#top', 'ru')
'https://example.com/ru/about?utm_source=search#top'

Круто, тоже заработало!

ПРИМЕЧАНИЕ. Не имеет особого смысла иметь URL-адрес страницы с параметрами запроса и хешами в таком месте, как тег ссылки (или канонический в этом отношении). Причина, по которой я упоминаю об этом, заключается в том, что это может быть полезно для ссылок на другие страницы.

Это практически все, что нам нужно. Но у translate_url есть некоторые ограничения, о которых стоит знать.

Перевести нелокализованный URL:

>>> urls.translate_url('/about', 'en')
'/about'

Если вы используете встроенный LocaleMiddleware и попытаетесь перейти к /about, Django перенаправит вас на страницу на текущем языке. translate_url не может сделать то же самое.

Как насчет перевода URL-адреса на язык, который не является текущим языком?

>>> translation.activate('en')
>>> urls.translate_url('/ru/about', 'en')
'/ru/about'

Нет, этого тоже нельзя.

Если вы посмотрите на реализацию translate_url, это ограничение станет понятным:

# django/urls/base.py

def translate_url(url, lang_code):
    """
    Given a URL (absolute or relative), try to get its translated version in
    the `lang_code` language (either by i18n_patterns or by translated regex).
    Return the original URL if no translated version is found.
    """
    parsed = urlsplit(url)
    try:
        match = resolve(parsed.path)
    except Resolver404:
        pass
    else:
        to_be_reversed = "%s:%s" % (match.namespace, match.url_name) if match.namespace else match.url_name
        with override(lang_code):
            try:
                url = reverse(to_be_reversed, args=match.args, kwargs=match.kwargs)
            except NoReverseMatch:
                pass
            else:
                url = urlunsplit((parsed.scheme, parsed.netloc, url, parsed.query, parsed.fragment))
    return url

Django сначала пытается разрешить URL-путь. Это способ Django проверить правильность URL-адреса. Только если URL-адрес действителен, он разбивается на части и переворачивается на желаемом языке.

Шаблонный тэг translate_url

Теперь, когда мы знаем, как «переводить» URL-адреса на разные языки, нам нужно иметь возможность использовать это в шаблоне. Django предоставляет нам возможность определять собственные теги и фильтры шаблонов.

Давайте добавим собственный тег шаблона для translate_url:

# app/templatetags/urls.py

from typing import Optional, Any

from django import urls


register = template.Library()


@register.simple_tag(takes_context=True)
def translate_url(context: Dict[str, Any], language: Optional[str]) -> str:
    """Get the absolute URL of the current page for the specified language.

    Usage:
        {% translate_url 'en' %}
    """
    url = context['request'].build_absolute_uri()
    return urls.translate_url(url, language)

Наш тег шаблона translate_url принимает контекст. Это необходимо, если мы хотим предоставить абсолютный URL. Мы используем build_absolute_uri для получения абсолютного URL из запроса.

Тег также принимает код целевого языка для перевода URL-адреса и использует translate_url для создания переведенного URL-адреса.

С помощью нашего нового тега шаблона мы можем заполнить пробелы в предыдущей реализации:

{% load i18n urls %}

{% get_available_languages as LANGUAGES %}
{% for language_code, language_name in LANGUAGES %}
<link
    rel="alternate"
    hreflang="{{ language_code }}"
    href="{% translate_url language_code %}" />
{% endfor %}

Использование x-default для языка по умолчанию

В руководстве есть еще одна рекомендация:

Зарезервированное значение hreflang = "x-default" используется, когда никакой другой язык/регион не соответствует настройке браузера пользователя. Это значение не является обязательным, но рекомендуется, чтобы вы могли управлять страницей, когда языки не совпадают. Хорошее использование - настроить таргетинг на домашнюю страницу вашего сайта, где есть интерактивная карта, позволяющая пользователю выбрать свою страну.

Так что также неплохо добавить ссылку на какой-нибудь язык по умолчанию. Если, например, мы хотим сделать нашим языком по умолчанию английский, мы можем добавить следующее в приведенный выше фрагмент:

{% load i18n urls %}

{% get_available_languages as LANGUAGES %}
{% for language_code, language_name in LANGUAGES %}
<link
    rel="alternate"
    hreflang="{{ language_code }}"
    href="{% translate_url language_code %}" />
{% endfor %}
<link
    rel="alternate"
    hreflang="x-default"
    href="{% translate_url en %}" />

Когда мы настраиваем наш проект Django, мы уже определили язык по умолчанию. Вместо жесткого кодирования английского (или любого другого языка в этом отношении) мы хотим использовать LANGUAGE_CODE, определенный в settings.py.

Чтобы использовать значения из settings.py в шаблонах, мы можем использовать старый прием, который мы использовали в прошлом, чтобы визуально различать среды в админке Django. Это простой обработчик контекста, который предоставляет определенные значения из settings.py шаблонам через контекст запроса:

# app/context_processor.py

from typing import Dict, Any

from django.conf import settings

def from_settings(request) -> Dict[str, Any]:
    return {
        attr: getattr(settings, attr, None)
        for attr in (
            'LANGUAGE_CODE',
        )
    }

Чтобы зарегистрировать обработчик контекста, добавьте в settings.py следующее:

# settings.py

TEMPLATES = [{
    # ...
    'OPTIONS': {
        'context_processors': [
            #...
            'app.context_processors.from_settings',
        ],
        #...
    }
]}

Теперь, когда у нас есть доступ к LANGUAGE_CODE в шаблоне, мы действительно можем завершить наш фрагмент:

{% load i18n urls %}

{% get_available_languages as LANGUAGES %}
{% for language_code, language_name in LANGUAGES %}
<link
    rel="alternate"
    hreflang="{{ language_code }}"
    href="{% translate_url language_code %}" />
{% endfor %}
<link
    rel="alternate"
    hreflang="x-default"
    href="{% translate_url LANGUAGE_CODE %}" />

Отрисованная разметка для страницы about выглядит так:

<link
    rel="alternate"
    hreflang="en"
    href="https://example.com/en/about" />
<link
    rel="alternate"
    hreflang="ru"
    href="https://example.com/ru/about" />
<link
    rel="alternate"
    hreflang="x-default"
    href="https://example.com/en/about" />

Заключительные слова

Надеюсь, эта короткая статья помогла вам лучше понять, как поисковые системы могут определять разные языки на вашем сайте Django. В процессе вы, возможно, также подобрали некоторые маленькие уловки для управления локализованными URL-адресами в Django.

Оригинал: https://hakibenita.com/django-multi-language-site-hreflang

Вернуться на верх