Создание по-настоящему интернационального веб-приложения - задача не из простых. Особенно когда речь заходит о правильной настройке SEO для разных языковых версий. Сегодня мы разберём, как грамотно внедрить поддержку hreflang в приложение на Next.js с помощью getServerSideProps.

Почему это важно?

Представьте: вы создали отличное приложение, которое работает на нескольких языках. Но поисковики почему-то путаются в версиях, а пользователи из разных стран не всегда попадают на страницы на своём родном языке. Знакомая ситуация?

Атрибут hreflang как раз и решает эту проблему. Он говорит поисковым системам, какая версия страницы предназначена для какого языка или региона. Давайте разберёмся, как правильно его внедрить в Next.js-приложение.

Основы работы с hreflang

Начнём с простого примера. Допустим, у нас есть страница, доступная на трёх языках:

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

Эти строки говорят поисковикам: "Эй, у этой страницы есть версии на английском, испанском и русском языках, и вот где их найти!"

Реализация в Next.js

В Next.js мы можем добавить поддержку hreflang через getServerSideProps. Это позволит динамически генерировать правильные ссылки для каждой страницы. Вот как это можно сделать:


export async function getServerSideProps(context) {
  const { locale } = context;
  const supportedLocales = ['en', 'es', 'ru'];
  
  const hreflangLinks = supportedLocales.map(lang => ({
    rel: 'alternate',
    hreflang: lang,
    href: `https://example.com/${lang}${context.resolvedUrl}`
  }));

  return {
    props: {
      hreflangLinks
    }
  };
}

А теперь давайте добавим эти ссылки в head секцию нашего приложения:

jsx
import Head from 'next/head';

function Page({ hreflangLinks }) {
  return (
    <>
      <Head>
        {hreflangLinks.map(({ rel, hreflang, href }) => (
          <link key={hreflang} rel={rel} hreflang={hreflang} href={href} />
        ))}
      </Head>
      {/* Остальной контент страницы */}
    </>
  );
}

Продвинутые техники

Иногда нам нужно учитывать не только язык, но и регион. Например, разные версии английского для США и Великобритании:


const localeConfig = {
  'en-US': {
    hreflang: 'en-us',
    defaultForLanguage: true
  },
  'en-GB': {
    hreflang: 'en-gb'
  }
};

Также полезно добавить x-default для случаев, когда нет подходящей языковой версии:


const hreflangLinks = [
  ...supportedLocales.map(/* ... */),
  {
    rel: 'alternate',
    hreflang: 'x-default',
    href: `https://example.com/en${context.resolvedUrl}`
  }
];

Обработка ошибок и edge cases

При работе с мультиязычностью всегда найдутся особые случаи. Давайте рассмотрим типичные проблемы и их решения:

1. Отсутствующие переводы:

const checkTranslationExists = async (locale, path) => {
  // Проверяем наличие перевода
  return true; // Ваша логика проверки
};

const getValidHreflangLinks = async (locales, path) => {
  const links = [];
  for (const locale of locales) {
    if (await checkTranslationExists(locale, path)) {
      links.push({
        rel: 'alternate',
        hreflang: locale,
        href: `https://example.com/${locale}${path}`
      });
    }
  }
  return links;
};

2. Канонические URL:
jsx
<Head>
  <link 
    rel="canonical" 
    href={`https://example.com/${currentLocale}${currentPath}`} 
  />
  {/* hreflang links */}
</Head>

Тестирование реализации

Крайне важно проверить работу hreflang на реальных сценариях:


describe('Hreflang implementation', () => {
  it('generates correct links for all supported locales', async () => {
    const { props } = await getServerSideProps({
      locale: 'en',
      resolvedUrl: '/about'
    });
    
    expect(props.hreflangLinks).toContainEqual({
      rel: 'alternate',
      hreflang: 'en',
      href: 'https://example.com/en/about'
    });
  });
});

Оптимизация производительности

Генерация hreflang-ссылок может влиять на производительность, особенно при большом количестве языков. Вот несколько советов по оптимизации:

1. Кэширование результатов:

const cache = new Map();

const getCachedHreflangLinks = (key, generator) => {
  if (!cache.has(key)) {
    cache.set(key, generator());
  }
  return cache.get(key);
};

2. Параллельная обработка:

const generateHreflangLinks = async (locales, path) => {
  const linkPromises = locales.map(async locale => ({
    rel: 'alternate',
    hreflang: locale,
    href: await generateLocalizedUrl(locale, path)
  }));
  
  return Promise.all(linkPromises);
};

Заключение

Правильная реализация hreflang - это важный шаг к созданию качественного мультиязычного приложения. Используя getServerSideProps в Next.js, мы можем гибко и эффективно управлять языковыми версиями наших страниц.

Помните о тестировании реализации и мониторинге её работы в production. Регулярно проверяйте отчёты в Google Search Console и других инструментах, чтобы убедиться, что поисковые системы правильно понимают структуру ваших языковых версий.

А как вы решаете задачи мультиязычности в своих проектах? Возможно, у вас есть свои интересные находки и решения?