Генерация файла sitemap.xml для мультиязычного сайта

Многие из нас делают мультиязычные сайты на контекстах. Контексты в MODx — вообще очень крутая штука, наверное, одна из самых крутых. На текущем большом мультиязычном проекте, SEO оптимизатор скинул мне статью в гугле и сказал, что нужно оптимизировать сайт под неё. Вопрос по теме задавали аж два года назад и ответов не последовало. Но народ вроде дал мотивацию тратить время на статью…



Поползли!

Что имеем?

1. Использовать будем Babel (для связи ресурсов и это ключевой момент) и pdoSitemap. Большой плюс в том, что в данной статье никакого программирования не будет. Так что, радуемся и пляшем. Все очень просто!

2. Сайт с контекстами, в моем случае два базовых контекста ru (web по-умолчанию) и кастомный en.

Шаг 1

В базовом контексте (ru), создаем ресурс sitemap.xml, с типом содержимого — XML. Именно в базовом контексте! Далее, в шаблоне ресурса (а если вы гуру MODx, то сразу в ресурсе) пишем обычный вызов сниппета pdoSitemap:

{'pdoSitemap' | snippet : [
'showHidden' => 1,
'resources' => -3,
'tplWrapper' => '@FILE chunks/sitemap/wrapper.tpl',
'tpl' => '@FILE chunks/sitemap/row.tpl'
]}

Убираем ресурсы которые не должны попасть в XML карту сайта ну и прописывайте нужные вам параметры. НО! Не указывайте в параметре contexts — ничего! Это важно! Обязательно создайте два кастомных чанка, будем редактировать их содержимое!

Шаг 2

Открываем чанк wrapper.tpl и пишем в нем:

<?xml version="1.0" encoding="{$_modx->config.modx_charset}"?>
<urlset xmlns="{$schema}"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
    {$output}
</urlset>

Что это? Это XML разметка, внутри которой мы будем помещать урлы. Google говорит использовать пространство имен xhtml в карте сайта. Так мы и сделали!

Шаг 3

Открываем чанк row.tpl и пишем в нем:

<url>
    <loc>{$url}</loc>
    <lastmod>{$date}</lastmod>
    <changefreq>{$update}</changefreq>
    <priority>{$priority}</priority>
    {'babelLinks' | snippet : [
    'resourceId' => $id,
    'showCurrent' => 1,
    'tpl' => '@FILE chunks/sitemap/xhtml.tpl'
    ]}
</url>
В атрибуте loc — мы указываем урл страницы текущего контекста (главного). Который мы генерим сниппетом pdoSitemap. lastmod, changefreq, priority — ноу комментс, гуглите если хотите узнать про эти атрибуты. А далее, с помощью прекрасного сниппета babelLinks (который идет в коробке компонента Babel), мы генерим те самые, заветные атрибуты <xhtml:link>!

Очень важно указать в параметре как идентификатор ресурса, так и значение 1/true, в параметре showCurrent. Этим самым — мы выводим тег <xhtml:link> для страницы активного контекста и для связанных страниц из других контекстов.

Шаг 4

Создаем чанк xhtml.tpl и пишем в нем:

<xhtml:link rel="alternate" hreflang="{$cultureKey}" href="{$url}"/>

Шаг 5

Чистим кэш, открываем файл sitemap.xml и ужасаемся, куда делась разметка? Не волнуйтесь, вся разметка на месте (xhtml мать его), просто откройте исходный код. Именно его и прочитают боты, можете проверить sitemap в валидаторах — все валидно и вуаля:



Итого, мы имеем валидный sitemap.xml файл для мультиязычных сайтов, который сгенерирован по рекомендациям Google. Ну а если у ресурса нет какой-либо версии языка, то там выведется та версия — которая есть.

Ни грамма кода, правки исходников и изобретения своего велосипеда!

Ну и я тестировал данную карту на сайте с 1.200 + ресурсами. Все ровно и быстро. Возможно, будут некие проблемки, если у вас ну ОООЧЕНЬ много ресурсов, т.к. мы используем сниппет в сниппете. И умные дядьки могут сказать нам, что мы говнокодеры. Возможно так оно и есть. Но лично меня данный вариант вполне устроит. Кстати! Если у вас реально очень много ресурсов, то sitemap нужно разбивать на несколько штук. Но это не отменяет того, что PHP код в сниппетах будет дергаться при построении урлов в сайтмапе.

В ином случае, все таки придется писать свой, уже оптимизированный сниппет, а это уже надо звать Артура Плагина…

Бонус

Ну и если вы почитали рекомендации гугла, то там требуется еще в теге head указать все возможные языковые варианты страницы. Для этого используется атрибут hreflang. Забацаем и его!

Шаг 1

Внутри тега head вызываем сниппет:

{'babelLinks' | snippet : [
'showCurrent' => 1,
'tpl' => '@FILE chunks/language/hreflang.tpl'
]}

Шаг 2

Создаем чанк hreflang.tpl и пишем в нём:

<link hreflang="{$cultureKey}" href="{$url}" rel="alternate"/>

Бонус внутри бонуса

Так свершилось судьбой, что у меня контексты на фронте переключаются через выпадающее меню. И его я тоже генерю сниппетом babelLinks, но если вдруг у вас нет перевода для определённого языка, babelLinks падла такая, выдаст страницу из настройки site_start…

Что я сделал? В каждом контексте я создал ресурс — «Нет перевода» и перевёл на нужные языки. В контекстах создал новую настройку no_translate и в ней указал идентификаторы этих ресурсов. Скопировал сниппет babelLinks и назвал его contextLinks, в нем, нашел строчку под порядковым номером 139 и заменил её на:

$url          = $context->makeUrl($context->getOption('no_translate'), $getRequest, 'full');

Теперь, если у определённого контекста нету перевода, там выводится страница «Нет перевода», дак еще и на нужном языке.

Ну не чудо ли, этот умирающий MODx?

Вот и всё! Кому хочется поблагодарить чеканной монетой — кнопка есть внизу или пишите мне в телегу — t.me/iWatchYouFromAfar. Ну или на моем сайте можете бахнуть червонца.

Готовое решение без вызова сниппета babelLinks.
Компонент SEO Tab умеет генерить аналогичный sitemap.xml без необходимости вызывать в нем сниппет babelLinks. Нужно лишь в контекстах задать настройки, включая cultureKey и включить системную настройку компонента с ключом stercseo.xmlsitemap.babel.add_alternate_links.

За инфо большое спасибо @Yurij Finiv
Люди которые мотивируют писать полезные статьи:
@Николай — 500р
PaRaDoX2012 — 222р
iWatchYouFromAfar
03 марта 2020, 22:28
modx.pro
15
4 322
+19
Поблагодарить автора Отправить деньги

Комментарии: 7

Aleksandr Huz
04 марта 2020, 10:36
+2
А такой вариант не подходит?
{'!StercSeoSiteMap' | snippet: [
    'contexts' => 'web,en'
]}

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

Для hreflang хорошее решение. Ранее я использовал свой сниппет, который включал и x-default
Кстати, в чанке есть незначительные ошибки — нет кавычек.
<link hreflang="{$cultureKey}" href="{$url}" rel="alternate"/>
    iWatchYouFromAfar
    04 марта 2020, 10:40
    0
    По поводу StercSeoSiteMap — без понятия, не работал с этим сниппетом. Если он генерит нужный сайтмап — отлично.

    Babel не выводит язык, если нет перевода. Посему я скопировал сниппет babelLinks, сделал страницу — «Нет перевода» и вывожу её.
      Yurij Finiv
      04 марта 2020, 16:02
      +2
      Пользуюсь этим отличный компонент
        iWatchYouFromAfar
        04 марта 2020, 16:07
        0
        Он генерит структуру XML для мультиязычного сайта?
          iWatchYouFromAfar
          05 марта 2020, 12:33
          0
          Да, компонент SEO Tab генерит мультиязычный sitemap без необходимости вызова в нем стороннего сниппета Babel. Дополню в статье.
      Павел Бигель
      06 марта 2020, 18:20
      +1
      Мне нравится что Паша пишет статьи «выжимки».
      Step-by-step гайд который и новичкам полезен, и людям поопытнее экономит время.
      Два лайка бы поставил
        Николай
        07 марта 2020, 13:10
        0
        Ему б еще подобреть к новичкам немного))))
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        7