# General Translation Knowledge Base: Переключатель языка: лучшие практики UX, SEO и доступности
URL: https://generaltranslation.com/ru/kb/language-switcher.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Называйте каждый язык на нём самом, например Deutsch или 日本語, и не используйте флаги — они обозначают страны. Каждый вариант должен вести на ту же страницу на выбранном языке через индексируемую ссылку. Запоминайте выбор в cookie-файле и никогда не перенаправляйте автоматически с локализованного URL.
Category: Интернационализация
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

Больше всего переключатель языка нужен тому, кто не может прочитать страницу, на которую попал. Поэтому все рекомендации ниже подчиняются одному правилу: элемент управления должен быть понятен человеку, который не владеет основным языком вашего сайта.

## Каким должен быть хороший переключатель языка?

### 1. Называйте каждый язык на нём самом

Японский читатель на англоязычной странице ищет глазами 日本語, а не «Japanese», а немецкий — «Deutsch». В [рекомендациях W3C по языковым меню](https://www.w3.org/International/questions/qa-navigation-select) советуется писать «français», а не «French». Такие самоназвания называются эндонимами, и [`Intl.DisplayNames`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames) в JavaScript получает их по коду локали:

```js
const locales = ['en', 'fr', 'de', 'ja', 'ar', 'pt-BR', 'zh-Hant'];

for (const code of locales) {
  // Название каждого языка на нём самом (эндоним)
  const own = new Intl.DisplayNames([code], { type: 'language' }).of(code);
  // То же название на английском — для сравнения
  const english = new Intl.DisplayNames(['en'], { type: 'language' }).of(code);
  console.log(`${code}: ${own} (${english})`);
}
```

Результат в Node.js 24 (ICU 78.3, CLDR 48.0):

```text
en: English (English)
fr: français (French)
de: Deutsch (German)
ja: 日本語 (Japanese)
ar: العربية (Arabic)
pt-BR: português (Brasil) (Brazilian Portuguese)
zh-Hant: 繁體中文 (Traditional Chinese)
```

Во французском и португальском названия языков в середине предложения пишутся со строчной буквы, поэтому CLDR возвращает «français». В меню каждое название стоит отдельно, поэтому начинайте его с заглавной буквы. Регион указывайте, только если предлагаете два варианта одного языка, например «Português (Brasil)» рядом с «Português (Portugal)».

### 2. Не используйте флаги для обозначения языков

В [рекомендациях W3C по объявлению языка](https://www.w3.org/TR/i18n-html-tech-lang/) прямо сказано: «Не используйте значки флагов для обозначения языков». [Обоснование](https://www.w3.org/International/questions/qa-link-lang) таково: «Флаги обозначают страны, а не языки». На одном языке говорят во многих странах (на испанском — в Испании, Мексике и Аргентине), в одной стране может быть несколько официальных языков (например, в Швейцарии), а флаг может вызывать национальные ассоциации, неприятные другим носителям того же языка. Флаг уместен в селекторе страны или магазина, где выбирают регион; дополните его названиями доступных там языков.

### 3. Разместите его там, куда смотрят пользователи, и сделайте узнаваемым

W3C [рекомендует](https://www.w3.org/International/questions/qa-navigation-select) размещать элемент управления «вверху страницы или рядом с верхом», а поскольку текстовая подпись зависит от языка, добавлять рядом узнаваемое изображение. Общепризнанного символа пока нет, хотя, по наблюдению W3C, значок глобуса становится всё популярнее. Значок глобуса рядом с названием текущего языка понятен читателям на любом языке. Многие сайты дублируют переключатель в подвале как вторую точку входа, но подвал не должен быть единственной.

### 4. Переключайте на ту же страницу, а не на главную

Если человек читает страницу с ценами на английском и переключается на французский, он ожидает увидеть французскую версию той же страницы. Если перебросить его на французскую главную, он потеряет место, где был, и ему придётся заново искать нужный раздел — причём на языке, который он, возможно, только теперь может прочитать. Каждый вариант в переключателе должен вести на переведённую версию текущего URL, а если у страницы ещё нет перевода, — на ближайшую переведённую родительскую страницу с соответствующим пояснением.

### 5. Используйте индексируемые ссылки с `hreflang` и `lang`

Как правило, Google может [перейти по ссылке, только если это элемент `<a>` с атрибутом `href`](https://developers.google.com/search/docs/crawling-indexing/links-crawlable), а многоязычным сайтам рекомендует [добавлять гиперссылки на другие языковые версии](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites), чтобы пользователи могли выбрать нужную одним кликом. `<select>`, обработчик изменения которого задаёт `window.location`, ссылкой не является, поэтому поисковым роботам просто не по чему переходить.

Добавьте к каждой ссылке два атрибута. Атрибут `hreflang` у `<a>` [подсказывает язык страницы, на которую ведёт ссылка](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/a); встроенного поведения в браузере у него нет, и он не имеет отношения к аннотациям `<link rel="alternate" hreflang>`, которые считывают поисковые системы (см. [Что такое hreflang](/kb/what-is-hreflang)). Атрибут `lang` указывает язык текста ссылки, чтобы программа чтения с экрана произносила «Deutsch» по правилам немецкого языка. В критерии WCAG [Language of Parts](https://www.w3.org/WAI/WCAG22/Understanding/language-of-parts.html) в качестве примера приводятся ссылки на языки, названные на самих этих языках. Там же оговаривается ограничение: если вспомогательная технология озвучивает ссылку только по её доступному имени (например, в списке ссылок страницы), произношение не переключается.

### 6. Запоминайте явный выбор пользователя

Когда пользователь выбрал язык, сохраните его в cookie-файле или в учётной записи и используйте при последующих посещениях: чтобы определять, куда перенаправить пользователя с нейтрального корневого адреса, например `/`, и чтобы показывать текущий выбор в переключателе. Не позволяйте cookie-файлу переопределять локализованный URL, который открыл пользователь. W3C [рекомендует](https://www.w3.org/International/questions/qa-accept-lang-locales) «сохранять результаты в базе данных или cookie-файле для последующих посещений», а [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language) указывает, что сервер «никогда не должен переопределять явный выбор языка пользователем».

### 7. Не переключайте язык автоматически при каждом посещении

Перенаправление по языку браузера при каждом запросе ставит в тупик путешественников, пользователей общих компьютеров и всех, кто открыл ссылку на конкретном языке. Используйте языковые настройки браузера только для того, чтобы показать ненавязчивое предложение, которое можно закрыть («Diese Seite gibt es auch auf Deutsch»), и оставьте решение за пользователем. В статье [Маршрутизация по локалям и SEO](/kb/locale-routing-seo) объясняется, какой сигнал должен иметь приоритет, и описан единственный случай, когда перенаправление безопасно.

### 8. Обеспечьте доступность с клавиатуры и для программ чтения с экрана

Список обычных ссылок или нативный `<select>` изначально поддерживают управление с клавиатуры и вспомогательные технологии. Если вы создаёте раскрывающийся список, следуйте [паттерну disclosure](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) из WAI-ARIA: `<button>` показывает и скрывает список, клавиши Enter и Space открывают и закрывают его, а `aria-expanded` сообщает о его состоянии. Задайте элементу управления доступное имя на языке страницы (`aria-label="Sprache"` на немецкой странице) и отметьте текущий язык атрибутом `aria-current="page"`.

## Как создать переключатель языка?

### Обычный HTML

В этом меню применены все восемь практик: эндонимы, значок глобуса вместо флагов, ссылки на ту же страницу, `hreflang` и `lang` у каждой ссылки, кнопка раскрытия и cookie-файл для сохранения явного выбора.

```html title="On /de/pricing"
<nav aria-label="Sprache">
  <button type="button" aria-expanded="false" aria-controls="language-menu">
    <svg aria-hidden="true" width="16" height="16"><!-- иконка глобуса --></svg>
    Deutsch
  </button>
  <ul id="language-menu" hidden>
    <li><a href="/en/pricing" hreflang="en" lang="en">English</a></li>
    <li><a href="/fr/pricing" hreflang="fr" lang="fr">Français</a></li>
    <li><a href="/de/pricing" hreflang="de" lang="de" aria-current="page">Deutsch</a></li>
    <li><a href="/ja/pricing" hreflang="ja" lang="ja">日本語</a></li>
  </ul>
</nav>
```

```js title="switcher.js"
const button = document.querySelector('[aria-controls="language-menu"]');
const menu = document.getElementById('language-menu');

// Открываем и закрываем меню (Enter и Space активируют <button> штатно)
button.addEventListener('click', () => {
  const open = button.getAttribute('aria-expanded') === 'true';
  button.setAttribute('aria-expanded', String(!open));
  menu.hidden = open;
});

// Escape закрывает меню и возвращает фокус на его кнопку.
button.closest('nav').addEventListener('keydown', (event) => {
  if (event.key === 'Escape' && !menu.hidden) {
    menu.hidden = true;
    button.setAttribute('aria-expanded', 'false');
    button.focus();
  }
});

// Запоминаем явный выбор пользователя для следующих посещений
menu.addEventListener('click', (event) => {
  const link = event.target.closest('a[hreflang]');
  if (link) {
    document.cookie = `locale=${link.hreflang}; path=/; max-age=31536000; samesite=lax`;
  }
});
```

### React

Этот компонент использует обычные ссылки. Передайте в `currentHref` текущий путь, строку запроса и фрагмент, а также текущую локаль. Компонент предполагает, что у каждой локали есть префикс в URL, каждая страница существует во всех перечисленных локалях, а слаги совпадают во всех языках. Если слаги переводятся или некоторые страницы отсутствуют, не заменяйте префикс, а определяйте каждый целевой адрес на основе данных маршрутов или CMS.

```jsx title="LanguageSwitcher.jsx"
const LOCALES = ['en', 'fr', 'de', 'ja'];

// Меняем локаль, сохраняя путь, завершающий слеш, строку запроса и фрагмент.
function hrefForLocale(currentHref, locale) {
  const url = new URL(currentHref, 'https://example.invalid');
  const trailingSlash = url.pathname.endsWith('/');
  const segments = url.pathname.split('/').filter(Boolean);
  if (LOCALES.includes(segments[0])) segments.shift();
  url.pathname = `/${[locale, ...segments].join('/')}${trailingSlash ? '/' : ''}`;
  return `${url.pathname}${url.search}${url.hash}`;
}

// Название языка на нём самом, с заглавной буквы для меню
function endonym(locale) {
  const name = new Intl.DisplayNames([locale], { type: 'language' }).of(locale);
  return name.charAt(0).toLocaleUpperCase(locale) + name.slice(1);
}

function rememberChoice(locale) {
  document.cookie = `locale=${locale}; path=/; max-age=31536000; samesite=lax`;
}

export function LanguageSwitcher({ currentHref, currentLocale, label }) {
  return (
    <nav aria-label={label}>
      <ul>
        {LOCALES.map((locale) => (
          <li key={locale}>
            <a
              href={hrefForLocale(currentHref, locale)}
              hrefLang={locale}
              lang={locale}
              aria-current={locale === currentLocale ? 'page' : undefined}
              onClick={() => rememberChoice(locale)}
            >
              {endonym(locale)}
            </a>
          </li>
        ))}
      </ul>
    </nav>
  );
}
```

При `currentHref="/fr/pricing/?plan=pro#faq"`, `currentLocale="fr"` и `label="Langue"` ссылка на немецкую версию имеет вид `/de/pricing/?plan=pro#faq`, а ссылка на французскую версию помечается атрибутом `aria-current="page"`. Сохраняйте параметры строки запроса и фрагменты, только если на целевой странице они имеют тот же смысл. Если идентификаторы якорей переводятся, подставляйте и соответствующий фрагмент.

### С General Translation

Для публичных страниц Next.js компонент [`<Link>`](/docs/react/nextjs/link) от GT формирует locale-aware ссылки. При `prefixDefaultLocale: true` эти ссылки ведут на `/en/pricing` и `/de/pricing`. В этом примере предполагается, что страницы с ценами на английском и немецком уже опубликованы. Передайте текущую локаль и локализованную подпись навигационной ссылки:

```tsx
import Link from 'gt-next/link';

export function PricingLanguages({ currentLocale, label }: { currentLocale: string; label: string }) {
  return (
    <nav aria-label={label}>
      <Link href="/pricing" locale="en" hrefLang="en" lang="en" aria-current={currentLocale === 'en' ? 'page' : undefined}>English</Link>
      {' · '}
      <Link href="/pricing" locale="de" hrefLang="de" lang="de" aria-current={currentLocale === 'de' ? 'page' : undefined}>Deutsch</Link>
    </nav>
  );
}
```

Локализуйте подпись навигации для текущей страницы. Если нужен компактный нативный элемент управления, например внутри приложения для авторизованных пользователей, используйте селектор локали:

[`<LocaleSelector>`](/docs/react/reference/components/locale-selector), доступный в `gt-react`, `gt-next` и экспериментальном `gt-tanstack-start`, выполняет рендеринг нативного `<select>` со списком настроенных локалей, подписывает каждую из них на её собственном языке и сохраняет выбор в cookie-файле. Стандартные атрибуты `<select>` пробрасываются дальше, поэтому можно передать `aria-label`, переведённый с помощью [`useGT`](/docs/react/reference/hooks/use-gt):

```tsx
'use client';
import { LocaleSelector, useGT } from 'gt-next';

export function Header() {
  const gt = useGT();
  return <LocaleSelector aria-label={gt('Language')} />;
}
```

Для публичных страниц используйте приведённые выше ссылки, чтобы навигация была доступна поисковым роботам, и объявите альтернативные версии через hreflang в `<head>` страницы, как показано в статье [многоязычное SEO в Next.js](/kb/multilingual-nextjs-seo). Если нужен собственный дизайн, воспользуйтесь [`useLocaleSelector`](/docs/react/reference/hooks/use-locale-selector): он возвращает активную локаль, список локалей и `setLocale`. Во Vue создайте переключатель с помощью [`useLocale`](/docs/vue/reference/composables/use-locale) и [`useSetLocale`](/docs/vue/reference/composables/use-set-locale), следуя [руководству по локалям во Vue](/docs/vue/guides/managing-locales#switcher).

## Основные выводы

* Подписывайте варианты эндонимами, например Français и 日本語. Их генерирует `Intl.DisplayNames`, причём для французского и португальского — со строчной буквы.
* Используйте значок глобуса, а не флаги. W3C прямо указывает: «Не используйте значки флагов для обозначения языков».
* Давайте ссылки на ту же страницу на каждом языке с помощью обычных ссылок `<a href>` с атрибутами `hreflang` и `lang`: так поисковые роботы смогут переходить по ним, а программы чтения с экрана — правильно произносить каждое название.
* Сохраняйте явный выбор пользователя и никогда не перенаправляйте с локализованного URL, ориентируясь на язык браузера.
* Компонент [`<LocaleSelector>`](/docs/react/reference/components/locale-selector) от General Translation рендерит нативный `<select>` с названиями языков на них самих и сохраняет выбор в cookie-файле. Для поисковых роботов добавьте обычные ссылки.

## Related questions

- [Как определить язык браузера пользователя?](https://generaltranslation.com/ru/kb/detect-user-language.mdx): Прочитайте упорядоченный список языков браузера (navigator.languages в JavaScript или заголовок Accept-Language на сервере с сортировкой по q-значению) и сопоставьте его с поддерживаемыми локалями, предусмотрев резервный вариант. Safari и Chrome в режиме инкогнито сообщают только один язык, а геолокация по IP указывает на местоположение, а не на язык.
- [Языковые перенаправления и SEO: URL, cookie-файл или Accept-Language?](https://generaltranslation.com/ru/kb/locale-routing-seo.mdx): Язык каждой страницы, которую вы хотите видеть в индексе, должен определять URL, ведь Googlebot не отправляет заголовок Accept-Language и сканирует сайты в основном с американских адресов. Сохранённый в cookie-файле выбор, а за ним Accept-Language используйте только для того, чтобы решить, куда перенаправлять с нейтрального корневого пути вроде /, причём временным перенаправлением 302 или 307.
- [Что такое hreflang и как его настроить?](https://generaltranslation.com/ru/kb/what-is-hreflang.mdx): Hreflang сообщает Google, какие URL являются языковыми или региональными версиями одной и той же страницы, чтобы в результатах поиска показывалась нужная. Добавьте его с помощью HTML-тегов link, HTTP-заголовков Link или карты сайта, сделайте все ссылки взаимными и используйте корректные коды, например en-GB, а не en-UK.
- [Как сделать сайт многоязычным?](https://generaltranslation.com/ru/kb/make-website-multilingual.mdx): Выберите, где будут храниться переводы — в прокси, плагине CMS, конструкторе сайтов или в вашем собственном коде, — а затем выделите каждому языку собственный URL, как рекомендует Google. Переводите страницы целиком, включая заголовки и метаописания, связывайте языковые версии с помощью hreflang и предлагайте переключатель языка вместо автоматического перенаправления посетителей.

## Previous and next questions

- Previous question: [Как сделать сайт многоязычным?](https://generaltranslation.com/ru/kb/make-website-multilingual.mdx)
- Next question: [Подкаталог, поддомен или ccTLD для многоязычного SEO](https://generaltranslation.com/ru/kb/multilingual-url-structure.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/ru/kb/sitemap.md) for every article.
