ARIA-атрибуты для локализованного контента

Локализация пользовательского интерфейса затрагивает не только видимый текст, но и данные, предназначенные для вспомогательных технологий: экранных дикторов, брайлевских дисплеев, голосового управления и навигационных систем. Атрибуты WAI-ARIA позволяют описывать назначение элементов, состояния интерфейса и связи между компонентами. При интернационализации приложения ARIA-атрибуты становятся частью переводимого контента.

JavaScript API Intl помогает формировать локализованные строки, которые затем могут использоваться внутри ARIA-атрибутов:

  • aria-label
  • aria-description
  • aria-valuetext
  • aria-roledescription
  • aria-placeholder
  • aria-live
  • aria-busy
  • aria-current
  • aria-label для динамических элементов интерфейса

Неправильно локализованные ARIA-атрибуты приводят к тому, что визуально интерфейс переведён, а экранный диктор продолжает озвучивать текст на другом языке.


Язык документа и ARIA

Атрибут lang напрямую влияет на поведение screen reader. Если язык документа определён неверно, вспомогательные технологии будут использовать неправильные правила произношения.

<html lang="ru">

Для отдельных локализованных фрагментов язык может переопределяться:

<button aria-label="Download report" lang="en">
  Скачать отчёт
</button>

Экранный диктор переключит голосовой движок на английский только для содержимого кнопки.


Локализация aria-label

aria-label используется для задания текстового описания элемента без видимого контента.

Базовый пример

<button aria-label="Закрыть окно">
  ×
</button>

При локализации строки должны формироваться в соответствии с текущей локалью приложения.

Использование Intl API

const locale = 'ru';

const labels = {
  ru: {
    close: 'Закрыть окно'
  },
  en: {
    close: 'Close dialog'
  }
};

button.setAttribute(
  'aria-label',
  labels[locale].close
);

Формирование динамических ARIA-строк

Многие ARIA-атрибуты содержат динамические значения: даты, числа, валюты, проценты.

Локализованная дата в aria-label

const formatter = new Intl.DateTimeFormat('ru-RU', {
  dateStyle: 'long'
});

const date = formatter.format(new Date());

button.setAttribute(
  'aria-label',
  `Выбрана дата ${date}`
);

Результат:

Выбрана дата 27 мая 2026 г.

Для английской локали:

const formatter = new Intl.DateTimeFormat('en-US', {
  dateStyle: 'long'
});

Результат:

Selected date May 27, 2026

aria-valuetext и Intl.NumberFormat

Элементам диапазона (slider, progressbar, spinbutton) часто требуется человекочитаемое значение.

Пример без локализации

<div
  role="slider"
  aria-valuenow="75"
  aria-valuetext="75 percent"
></div>

Такой текст непригоден для многоязычного интерфейса.

Использование Intl.NumberFormat

const percentFormatter = new Intl.NumberFormat('ru-RU', {
  style: 'percent'
});

const value = 0.75;

slider.setAttribute(
  'aria-valuetext',
  percentFormatter.format(value)
);

Результат:

75 %

Для немецкой локали форматирование также изменится:

new Intl.NumberFormat('de-DE', {
  style: 'percent'
}).format(0.75);

Валюты внутри ARIA

ARIA-описания финансовых элементов должны учитывать локальные правила отображения валют.

Пример

const formatter = new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'KZT'
});

const amount = formatter.format(150000);

button.setAttribute(
  'aria-label',
  `Оплатить ${amount}`
);

Результат:

Оплатить 150 000,00 ₸

Intl.ListFormat для ARIA-описаний

При перечислении элементов необходимо учитывать языковые правила соединения списков.

Без Intl API

const text = items.join(', ');

Такой подход не учитывает правила конкретного языка.

Использование Intl.ListFormat

const formatter = new Intl.ListFormat('ru', {
  style: 'long',
  type: 'conjunction'
});

const result = formatter.format([
  'HTML',
  'CSS',
  'JavaScript'
]);

element.setAttribute(
  'aria-label',
  result
);

Результат:

HTML, CSS и JavaScript

Для английского языка:

HTML, CSS, and JavaScript

Intl.RelativeTimeFormat в live-region

Динамические уведомления часто озвучиваются через aria-live.

Пример

<div aria-live="polite" id="status"></div>
const formatter = new Intl.RelativeTimeFormat('ru', {
  numeric: 'auto'
});

status.textContent = formatter.format(-5, 'minute');

Результат:

5 минут назад

Экранный диктор озвучит строку в корректном формате.


Локализация aria-roledescription

aria-roledescription позволяет переопределять стандартное описание роли.

Пример

<div
  role="group"
  aria-roledescription="Карусель"
>
</div>

При интернационализации текст должен изменяться вместе с языком интерфейса.

const roleDescriptions = {
  ru: 'Карусель',
  en: 'Carousel',
  fr: 'Carrousel'
};

element.setAttribute(
  'aria-roledescription',
  roleDescriptions[locale]
);

Формирование сообщений для aria-live

Live-region особенно чувствительны к качеству локализации, поскольку сообщения озвучиваются автоматически.

Неправильный подход

status.textContent = `${count} files uploaded`;

Использование Intl.PluralRules

const pluralRules = new Intl.PluralRules('ru');

function getFilesText(count) {
  const category = pluralRules.select(count);

  const variants = {
    one: 'файл загружен',
    few: 'файла загружено',
    many: 'файлов загружено',
    other: 'файла загружено'
  };

  return `${count} ${variants[category]}`;
}

status.textContent = getFilesText(5);

Результат:

5 файлов загружено

Intl.DisplayNames для ARIA

API Intl.DisplayNames используется для локализованного отображения языков, регионов и валют.

Пример локализации списка языков

const languageNames = new Intl.DisplayNames(['ru'], {
  type: 'language'
});

const label = languageNames.of('en');

button.setAttribute(
  'aria-label',
  label
);

Результат:

английский

Локализация переключателей языка

Смена языка интерфейса должна учитывать доступность.

Пример

<button id="lang-switch"></button>
const currentLocale = 'ru';

const displayNames = new Intl.DisplayNames(
  [currentLocale],
  { type: 'language' }
);

const nextLanguage = displayNames.of('en');

button.textContent = nextLanguage;

button.setAttribute(
  'aria-label',
  `Переключить язык на ${nextLanguage}`
);

Использование Intl.Segmenter для screen reader

Некоторые языки требуют корректного разбиения текста на слова и предложения.

Пример

const segmenter = new Intl.Segmenter('ja', {
  granularity: 'word'
});

const segments = segmenter.segment('こんにちは世界');

Такой подход помогает при создании кастомных систем озвучивания и текстовой навигации.


Синхронизация видимого текста и ARIA

Одна из самых распространённых ошибок — перевод только видимого интерфейса.

Ошибка

<button aria-label="Close">
  Закрыть
</button>

Экранный диктор озвучит английский текст, несмотря на русский интерфейс.

Правильный подход

<button aria-label="Закрыть">
  Закрыть
</button>

Использование словарей локализации

ARIA-строки должны храниться рядом с обычными переводами.

Структура локализации

const translations = {
  ru: {
    buttons: {
      close: 'Закрыть',
      save: 'Сохранить'
    },
    aria: {
      closeDialog: 'Закрыть диалоговое окно',
      saveDocument: 'Сохранить документ'
    }
  }
};

Локализация состояний элементов

Состояния элементов также должны быть понятны screen reader.

Пример переключателя

<button
  role="switch"
  aria-checked="true"
  aria-label="Тёмная тема включена"
>
</button>

Динамическое обновление

function updateThemeState(enabled) {
  button.setAttribute(
    'aria-label',
    enabled
      ? 'Тёмная тема включена'
      : 'Тёмная тема выключена'
  );
}

Работа с числами в aria-description

aria-description дополняет основной label.

Пример

const formatter = new Intl.NumberFormat('ru-RU');

const value = formatter.format(1250000);

element.setAttribute(
  'aria-description',
  `Количество пользователей: ${value}`
);

Локализация таблиц и ARIA

Для таблиц часто используются:

  • aria-sort
  • aria-colindex
  • aria-rowindex
  • aria-label

Пример сортировки

<th
  aria-sort="ascending"
  aria-label="Сортировка по возрастанию"
>
  Цена
</th>

Доступность календарей

Календари являются одним из самых сложных компонентов локализации.

Формирование даты

const formatter = new Intl.DateTimeFormat('ru-RU', {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

const label = formatter.format(new Date());

cell.setAttribute(
  'aria-label',
  label
);

Результат:

среда, 27 мая 2026 г.

Локализация времени

Пример

const formatter = new Intl.DateTimeFormat('ru-RU', {
  timeStyle: 'short'
});

const time = formatter.format(new Date());

element.setAttribute(
  'aria-label',
  `Время ${time}`
);

ARIA и направление текста

Для RTL-языков необходимо корректно задавать направление текста.

Пример

<div dir="rtl" lang="ar">
  مرحبا
</div>

ARIA-атрибуты также должны содержать текст в соответствующей локали.

<button
  aria-label="إغلاق النافذة"
  dir="rtl"
>
</button>

Кэширование Intl-форматтеров

Создание экземпляров Intl внутри частых обновлений может ухудшать производительность.

Неэффективный код

function formatPrice(price) {
  return new Intl.NumberFormat(
    'ru-RU',
    {
      style: 'currency',
      currency: 'RUB'
    }
  ).format(price);
}

Оптимизированный вариант

const currencyFormatter =
  new Intl.NumberFormat('ru-RU', {
    style: 'currency',
    currency: 'RUB'
  });

function formatPrice(price) {
  return currencyFormatter.format(price);
}

Типичные ошибки локализации ARIA

Жёстко закодированные строки

element.setAttribute(
  'aria-label',
  'Delete'
);

Конкатенация строк

`${count} items selected`

Без учёта множественных форм такой код работает некорректно для многих языков.

Отсутствие перевода ARIA

Визуальный интерфейс локализован, а вспомогательные атрибуты — нет.

Использование сокращений

<button aria-label="Ред. профиля">

Screen reader могут озвучивать сокращения непредсказуемо.


Проверка локализованной доступности

При тестировании необходимо проверять:

  • язык озвучивания screen reader;
  • корректность aria-label;
  • локализацию дат;
  • локализацию чисел;
  • множественные формы;
  • RTL-поддержку;
  • корректность live-region;
  • согласованность видимого текста и ARIA;
  • переключение языка во время работы приложения.

Интеграция с i18n-библиотеками

Intl API обычно используется совместно с:

  • i18next
  • FormatJS
  • Lingui
  • Vue I18n
  • React Intl

Пример:

button.setAttribute(
  'aria-label',
  intl.formatMessage({
    id: 'dialog.close'
  })
);

Динамическая смена локали

При переключении языка необходимо обновлять не только текст DOM-узлов, но и ARIA-атрибуты.

Пример

function updateLocale(locale) {
  button.textContent =
    translations[locale].close;

  button.setAttribute(
    'aria-label',
    translations[locale].closeDialog
  );
}

SSR и локализованные ARIA

При серверном рендеринге ARIA-атрибуты должны генерироваться сразу в нужной локали.

Пример

<button aria-label="Сохранить документ">
  Сохранить
</button>

Это особенно важно для screen reader, которые начинают анализировать страницу до полной гидратации JavaScript.


Intl API и доступность сложных компонентов

Наиболее критичны локализованные ARIA-атрибуты в:

  • datepicker;
  • combobox;
  • treeview;
  • menu;
  • grid;
  • slider;
  • tabs;
  • dialog;
  • toast-уведомлениях;
  • live-region;
  • data grid.

В этих компонентах одновременно используются:

  • динамические состояния;
  • локализованные сообщения;
  • форматирование дат и чисел;
  • голосовые уведомления;
  • смена языка интерфейса;
  • управление клавиатурой.