Экранные читалки (screen readers) — вспомогательные технологии, преобразующие содержимое интерфейса в голосовой вывод или текст для устройств Брайля. При разработке многоязычных приложений недостаточно просто перевести текст. Интерфейс должен корректно озвучиваться, учитывать локаль пользователя, особенности форматирования чисел, дат, валют и структуру документа.
Библиотека FormatJS используется для интернационализации JavaScript-приложений и тесно связана с доступностью. Неправильное использование API форматирования способно ухудшить восприятие интерфейса экранными читалками даже при наличии корректного перевода.
Экранная читалка анализирует:
Для пользователя критичны:
Пример проблемы:
<div>12/05/2026</div>
Без информации о локали и формате экранная читалка может интерпретировать дату неоднозначно:
FormatJS решает подобные проблемы через локализованное форматирование.
Основные пакеты экосистемы:
react-intlintl-messageformat@formatjs/intl@formatjs/cliКлючевая задача — предоставление семантически корректного локализованного текста.
Пример:
<IntlProvider locale="ru">
<FormattedDate
value={new Date()}
year="numeric"
month="long"
day="numeric"
/>
</IntlProvider>
Экранная читалка получит уже локализованную строку:
29 мая 2026 г.
Вместо:
05/29/2026
langЭкранные читалки используют атрибут lang для выбора
правил произношения.
Без него текст может озвучиваться неверным голосовым движком.
<html>
<html lang="ru">
Для переключения локали:
<IntlProvider locale="fr">
<App />
</IntlProvider>
и:
<html lang="fr">
Если приложение меняет язык динамически, атрибут должен обновляться синхронно.
Пример:
document.documentElement.lang = currentLocale;
Числа читаются экранными читалками согласно региональным правилам.
<FormattedNumber value={1000000.5} />
Русская локаль:
1 000 000,5
Американская:
1,000,000.5
Экранная читалка корректно распознает:
<span>{price} RUB</span>
Читалка может озвучить:
руб
эр ю би
или:
R U B
<FormattedNumber
value={price}
style="currency"
currency="RUB"
/>
Результат:
1 500,00 ₽
Озвучивание становится естественным.
Дата должна быть понятной в голосовом интерфейсе.
<div>2026-05-29</div>
Озвучивание может быть неудобным:
два ноль два шесть тире ноль пять тире два девять
FormattedDate<FormattedDate
value={date}
weekday="long"
year="numeric"
month="long"
day="numeric"
/>
Результат:
пятница, 29 мая 2026 г.
Временные значения особенно важны:
<FormattedTime
value={date}
hour="numeric"
minute="numeric"
/>
Локализованный вывод:
14:30
или:
2:30 PM
в зависимости от региона.
Экранная читалка использует региональные правила чтения времени.
aria-labelИногда визуальный текст недостаточен для доступности.
<button>
?
</button>
Экранная читалка может озвучить:
кнопка
или:
эмодзи лупа
aria-label<button
aria-label={intl.formatMessage({
id: 'search',
defaultMessage: 'Поиск'
})}
>
?
</button>
FormatJS часто используется именно внутри ARIA-атрибутов:
aria-labelaria-descriptionaria-valuetextaria-placeholderaria-roledescription<input
aria-label={intl.formatMessage({
id: 'emailField',
defaultMessage: 'Адрес электронной почты'
})}
/>
formatMessageОсновной механизм локализации:
intl.formatMessage()
Пример:
const message = intl.formatMessage({
id: 'profile.edit',
defaultMessage: 'Редактировать профиль'
});
Экранным читалкам нужен полный контекст.
<button>Подробнее</button>
При навигации по кнопкам пользователь услышит множество одинаковых элементов.
<button>
{intl.formatMessage({
id: 'article.more',
defaultMessage: 'Подробнее о статье'
})}
</button>
aria-liveScreen reader должен узнавать об изменениях интерфейса.
<div aria-live="polite">
{message}
</div>
const message = intl.formatMessage({
id: 'cart.added',
defaultMessage: 'Товар добавлен в корзину'
});
aria-livepoliteСообщение озвучивается после завершения текущей речи.
<div aria-live="polite">
Подходит для:
assertiveПрерывает текущую речь.
<div aria-live="assertive">
Используется осторожно:
FormatJS поддерживает безопасную подстановку параметров.
intl.formatMessage(
{
id: 'notifications',
defaultMessage:
'У вас {count} уведомлений'
},
{
count: 5
}
)
Экранная читалка озвучит фразу естественно.
Русский язык имеет сложные правила склонения.
`${count} сообщений`
Ошибки:
defaultMessage: `
{count, plural,
one {# сообщение}
few {# сообщения}
many {# сообщений}
}`
Это важно для корректного восприятия голосом.
Некоторые языки требуют гендерных вариантов.
defaultMessage: `
{gender, select,
male {Он вошёл}
female {Она вошла}
other {Вход выполнен}
}`
Экранная читалка получает грамматически корректную фразу.
Экранные читалки воспринимают списки иначе, чем визуальные пользователи.
<span>Apple, Banana, Orange</span>
FormattedList<FormattedList
type="conjunction"
value={['Apple', 'Banana', 'Orange']}
/>
Результат:
Apple, Banana и Orange
Читалка произносит список естественно.
FormatJS не заменяет правильную HTML-структуру.
<div onCl ick={submit}>
Отправить
</div>
<button>
{intl.formatMessage({
id: 'submit',
defaultMessage: 'Отправить'
})}
</button>
Пользователи screen reader часто перемещаются по заголовкам.
<div className="title">
Настройки
</div>
<h2>
{intl.formatMessage({
id: 'settings',
defaultMessage: 'Настройки'
})}
</h2>
Иногда нужен дополнительный контекст.
<button>
×
<span className="sr-only">
{intl.formatMessage({
id: 'closeDialog',
defaultMessage: 'Закрыть окно'
})}
</span>
</button>
CSS:
.sr-only {
position: absolute;
left: -9999px;
}
Аббревиатуры могут читаться неверно.
<span>JS</span>
Читалка может произнести:
джей эс
или:
жс
<abbr title="JavaScript">
JS
</abbr>
FormatJS умеет локализовать валюты.
<FormattedNumber
value={1999}
style="currency"
currency="KZT"
/>
Для Казахстана:
1 999 ₸
Экранная читалка корректно распознает тенге.
FormattedRelativeTimeОтносительное время удобнее воспринимается голосом.
<FormattedRelativeTime
value={-3}
unit="day"
/>
Результат:
3 дня назад
Вместо:
26 мая 2026
Ошибка:
<button aria-label="Search">
Поиск
</button>
Визуально — русский интерфейс, голосом — английский.
<img src="button.png" />
Screen reader не получает контент.
alt<img src="logo.png" />
Правильно:
<img
src="logo.png"
alt={intl.formatMessage({
id: 'companyLogo',
defaultMessage: 'Логотип компании'
})}
/>
Для арабского и иврита важны:
<html lang="ar" dir="rtl">
Часто используется:
navigator.language
Пример:
const locale = navigator.language;
Однако screen reader может использовать отдельные настройки языка, поэтому предпочтительно предоставлять ручной выбор локали.
Инструменты тестирования:
Автоматические проверки:
Проверяется:
defaultMessagedefaultMessage служит резервным текстом.
intl.formatMessage({
id: 'save',
defaultMessage: 'Сохранить'
})
Если перевод отсутствует, интерфейс не останется пустым.
Для screen reader это особенно важно: пустой aria-label
делает элемент практически недоступным.
<FormattedNumber
value={25}
style="unit"
unit="kilobyte"
/>
Результат:
25 килобайт
Вместо:
25 KB
<label htmlFor="email">
{intl.formatMessage({
id: 'email',
defaultMessage: 'Электронная почта'
})}
</label>
<input id="email" />
Ошибка должна:
<input
aria-describedby="email-error"
/>
<div
id="email-error"
aria-live="assertive"
>
{intl.formatMessage({
id: 'emailError',
defaultMessage:
'Введите корректный адрес электронной почты'
})}
</div>
Частые обновления DOM могут:
FormatJS помогает централизовать локализацию и уменьшить количество лишних ререндеров при правильной архитектуре.
useIntlХук:
const intl = useIntl();
Позволяет локализовать:
Пример:
const label = intl.formatMessage({
id: 'menu.open',
defaultMessage: 'Открыть меню'
});
<button>
ON
</button>
<button
aria-pressed={enabled}
>
{enabled
? intl.formatMessage({
id: 'enabled',
defaultMessage: 'Включено'
})
: intl.formatMessage({
id: 'disabled',
defaultMessage: 'Выключено'
})}
</button>
FormattedMessageБазовый компонент:
<FormattedMessage
id="welcome"
defaultMessage="Добро пожаловать"
/>
Подходит для большинства текстовых элементов интерфейса.
FormatJS поддерживает rich text formatting.
<FormattedMessage
id="terms"
defaultMessage="
Прочитайте <link>условия использования</link>
"
values={{
link: chunks => (
<a href="/terms">
{chunks}
</a>
)
}}
/>
Screen reader корректно воспринимает ссылку внутри предложения.
aria-label="Close"
Флаг не является языком.
При динамическом переключении интерфейса screen reader может не сообщить пользователю об изменениях.
Основные принципы:
lang.