UX и проектирование интерфейсов Текст в интерфейсе: формулировки, сообщения об ошибках, тон
0%

Текст в интерфейсе: формулировки, сообщения об ошибках, тон

Текст в интерфейсе: формулировки, сообщения об ошибках, тон

Откройте любой экран своего продукта и мысленно удалите с него весь текст. Останутся прямоугольники, иконки и отступы — и ни одного способа понять, что здесь происходит. Интерфейс на 80–90% состоит из слов: заголовки, подписи полей, названия кнопок, подсказки, ошибки, пустые экраны, письма и пуши. Всё остальное — способ эти слова расположить.

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

Эта статья — про то, как проектировать текст, а не «писать красиво»: где в интерфейсе текст живёт и на какой вопрос отвечает каждый кусок; почему конкретные формы бесят и как это чинится; из чего состоит нормальное сообщение об ошибке; как устроен тон и где заканчиваются данные и начинается вкусовщина; как считать «файлы» и «файла» и почему интерфейс разъезжается при переводе; как передавать строки в разработку так, чтобы они не превращались в скриншот в задаче. Предполагается, что вы уже прошли пользовательские сценарии, информационную архитектуру и состояния интерфейса: без сценария непонятно, какой вопрос у человека в голове, без IA — каким словарём называть сущности, без карты состояний — сколько вообще строк надо написать.

Первый принцип: текст отвечает на вопрос, который у человека уже есть

Никто не читает интерфейс. Люди сканируют его в поисках ответа на конкретный вопрос, который у них возник секунду назад. Классические измерения Nielsen Norman Group показывают: в среднем на веб-странице человек успевает прочитать порядка 20–28% слов, а взгляд движется не по строкам, а рваными паттернами — так называемым F-образным паттерном и его вариациями. Подробности в статье «How Little Do Users Read?».

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

  1. Где я и что это за место?
  2. Что от меня хотят прямо сейчас?
  3. Что будет, если я нажму вот это?
  4. Почему у меня не получилось и что делать?
  5. Всё получилось? Точно?
  6. Как отсюда уйти, ничего не сломав?

Хороший интерфейсный текст — это отображение «вопрос → слот на экране». Плохой — набор фраз, которые дизайнер посчитал уместными.

Обратите внимание на нижнюю ветку. Текст живёт не только внутри экрана: письмо «подтвердите почту», push «платёж не прошёл» и ответ API {"error": "invalid_state"}, который однажды всплывёт в интерфейсе, — часть той же системы. Если их пишут разные люди без общего словаря, продукт разговаривает несколькими голосами одновременно.

Карта слотов: где именно живёт текст

Прежде чем спорить о формулировках, полезно договориться о слотах. Слот — это позиция в интерфейсе с фиксированной ролью: у неё есть свой вопрос, свои ограничения по длине и свои правила поведения.

Карта слотов микрокопии на экране оплаты: тост, заголовок, подпись поля, плейсхолдер, ошибка, подсказка, кнопка, сноска и пустое состояние — и вопрос пользователя за каждым слотом

Слот Вопрос пользователя Типичная длина Частая ошибка
Заголовок экрана «Где я?» 1–4 слова Не совпадает с названием пункта меню, откуда пришли
Подзаголовок «Что от меня требуется?» 1 предложение Пересказывает заголовок другими словами
Подпись поля «Что сюда вводить?» 1–3 слова Заменена плейсхолдером и исчезает при вводе
Плейсхолдер «В каком формате?» пример значения Несёт смысл, который пропадает при первом символе
Подсказка под полем «Зачем вам это и что будет дальше?» 1 предложение Показывается только после ошибки
Кнопка «Что произойдёт, если нажму?» 1–3 слова, глагол «ОК», «Продолжить», «Отправить» без объекта
Ошибка «Что не так и как починить?» 1–2 предложения Название технического исключения
Пустое состояние «Почему пусто и что делать?» заголовок + строка + действие Просто «Нет данных»
Подтверждение и успех «Это необратимо? Всё получилось?» вопрос + последствие «Вы уверены?» без последствий; тост исчезает раньше, чем прочитан

Дальше — три подробных разбора: кнопка, форма, ошибка. Это три места, где текст чаще всего решает судьбу сценария.

Разбор №1: почему пользователь не нашёл кнопку

Ситуация из реального теста B2B-панели. Задача: «выгрузите отчёт за прошлый месяц, чтобы отправить бухгалтеру». Шесть человек из восьми не справились за отведённые три минуты. Кнопка была на экране, в правом верхнем углу, ничем не закрыта. Называлась «Экспорт». Разберём по слоям — каждый слой требует своего лечения.

Слой 1. Словарь системы вместо словаря пользователя. Люди искали глазами слова «скачать», «выгрузить», «Excel». «Экспорт» — слово из мира разработчика: так называется операция в коде. Проверка простая: в интервью и в тестах записывайте дословно, какими словами человек описывает задачу до того, как увидит интерфейс. Если в записи пять раз «выгрузить в эксель» и ноль раз «экспорт» — вопрос закрыт. Это прямое продолжение работы с именованием в информационной архитектуре и данных из исследований.

Слой 2. Иконка вместо слова. В том же интерфейсе рядом стояли восемь иконок в тулбаре, и «Экспорт» была одной из них — стрелка вниз. Иконка без подписи распознаётся надёжно только у нескольких десятков символов, которые люди видели тысячи раз: дом, лупа, корзина, крестик. Всё остальное — угадайка. Тултип не спасает: он появляется через 500–700 мс наведения, а сканирование идёт быстрее. Лечение: подпись рядом с иконкой, а не вместо неё. Если места нет — значит, в тулбаре слишком много действий, и это проблема приоритизации, а не текста.

Слой 3. Кнопка описывает механику, а не результат. Даже переименовав в «Скачать», мы не закрываем вопрос «а в каком виде?». «Скачать CSV» отвечает сразу. Хорошая формула названия кнопки: глагол + объект (+ ключевой параметр, если он снимает страх).

Слой 4. Кнопка не снимает страх. На предпоследнем шаге оплаты кнопка «Продолжить» вызывает заминку: спишут деньги прямо сейчас или нет? Человек ищет глазами дополнительные подтверждения, иногда уходит проверять корзину и не возвращается. Разведите шаги словами: на предпоследнем — «Перейти к оплате», на последнем — «Оплатить 1 490 руб.». Второе название одновременно сообщает сумму, необратимость и то, что дальше шага не будет.

Было Стало Что именно починили
Экспорт Скачать CSV словарь пользователя + результат вместо операции
ОК Удалить проект понятно без чтения модалки, какой из вариантов какой
Отправить Отправить заявку понятно, что именно уходит и кому
Продолжить Оплатить 1 490 руб. снята неопределённость по деньгам и необратимости
Сохранить / Применить рядом Сохранить черновик / Опубликовать два слова-синонима заменены на два разных результата
Готово Закрыть без сохранения честно названо последствие

Отдельный случай — кнопки в диалогах. «Вы уверены? [Да] [Нет]» заставляет перечитать вопрос, чтобы понять, что означает «Да». Пара «[Удалить 12 файлов] [Отмена]» читается без вопроса вообще. Это же требование прописано в WCAG как критерий 2.5.3 Label in Name: видимая надпись должна входить в программное имя элемента, иначе голосовое управление ломается — человек говорит «нажми Удалить», а система знает кнопку как «confirm_dialog_primary».

Разбор №2: почему форма неудобна

«Неудобная форма» — самая частая и самая бесполезная формулировка в отзывах. Разбираем на конкретных механизмах.

Плейсхолдер вместо подписи — самый популярный способ убить форму дизайном. Подпись внутри поля выглядит аккуратно в макете и разрушается в реальности: как только человек начинает печатать, подпись исчезает. Дальше цепочка: невозможно проверить заполненное перед отправкой; при ошибке непонятно, какое из полей какое; серый текст на белом почти всегда не проходит по контрасту; скринридеры читают плейсхолдер непредсказуемо; автозаполнение перекрывает его совсем. Лечение: подпись всегда видна над полем, плейсхолдер — только пример формата (+7 912 345-67-89), причём такой, чтобы его нельзя было спутать с уже введённым значением. Разбор — «Placeholders in Form Fields Are Harmful» у NN/g, механика доступности — в статье «Доступные формы».

Подсказка появляется только после ошибки. Классика: требования к паролю показываются красным после того, как пользователь придумал пароль и нажал «Зарегистрироваться». Это буквально «сначала сделай неправильно, потом мы объясним правила». То же с лимитом на размер файла: «файл слишком большой» после сорока секунд загрузки на мобильном интернете. Правило: ограничение показывается до ввода, а не после — «От 8 символов, минимум одна цифра» под полем пароля; «PDF или JPG, до 5 МБ» рядом с кнопкой выбора файла. Ошибка тогда становится редким событием, а не основным каналом коммуникации.

Непонятно, зачем это спрашивают. Поле «Кодовое слово» в заявке на карту стабильно даёт всплеск отказов: человек не знает, что это, боится придумать что-то не то и уходит «спросить». Подсказка «Понадобится, если вы позвоните в банк: оператор спросит его вместо паспортных данных» снимает вопрос целиком. Чем чувствительнее данные, тем обязательнее объяснение: телефон, ИНН, дата рождения, доступ к контактам — всё это требует одной строки «зачем».

Двойное отрицание в чекбоксах. «☐ Не присылать мне уведомления об акциях» — чтобы отказаться, надо поставить галочку в отрицании; человек читает три раза. Формулируйте чекбокс как положительное утверждение о том, что произойдёт при включении: «☑ Присылать письма о статусе заказа». И разделяйте сущности: транзакционные письма и маркетинг — разные чекбоксы, потому что это разные решения.

Звёздочки и обязательность. «Обязательные поля отмечены *» в форме, где 11 полей из 12 обязательны, — шум. Инвертируйте: помечайте словом «необязательно» те немногие, что необязательны. Звёздочка сама по себе не сообщает обязательность ни скринридеру, ни человеку, который её раньше не встречал.

Маски ввода, которые дерутся с пользователем. Маска +7 (___) ___-__-__ ломается, когда номер вставляют из буфера как 8 916 1234567 или +79161234567. Правильное поведение — нормализовать ввод на своей стороне и написать под полем «можно вставить в любом формате». Текст здесь не украшение: он сообщает о снятом ограничении, которое иначе невидимо.

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

Два важных момента, которые видны только на такой схеме.

Момент проверки. Валидация «на каждый символ» превращает ввод почты в поток красного текста: пока человек не дописал @example.com, всё «неверно». Baymard Institute в исследованиях форм показывает, что польза инлайн-валидации целиком зависит от момента срабатывания: проверка после потери фокуса помогает, преждевременная — раздражает и повышает отказ (разбор). Практическое правило: первый раз проверяем по blur; после того как ошибка показана, для этого поля переходим на проверку по каждому вводу, чтобы человек видел, как ошибка исчезает.

Состояние «заблокировано». Дизайнеры почти всегда рисуют серое поле и забывают текст. А у пользователя ровно один вопрос: почему. «Изменить нельзя после отправки декларации» — одна строка, которая закрывает поток обращений в поддержку.

Разбор №3: сообщения об ошибках

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

Анатомия сообщения об ошибке: бесполезное сообщение с кодом исключения и корректное из четырёх частей — что случилось, почему, что делать, данные для поддержки

Классический канон — Error Message Guidelines NN/g: сообщение должно быть заметным, написанным человеческим языком, вежливым, точным и содержать конструктивный совет. Добавим к этому две вещи, которых в каноне нет, а в продакшене они критичны: судьба введённых данных и идентификатор для поддержки.

Было Стало Почему
Произошла ошибка. Попробуйте позже Файл 12 МБ, а можно до 5 МБ. Сожмите или выберите другой названа причина и действие; лимит указан числом
Неверный формат данных Дата в формате ДД.ММ.ГГГГ, например 05.03.1990 правило показано на примере, а не описано термином
Ошибка 403 У вас нет прав на публикацию. Попросите доступ у администратора проекта — Анны Ким человеку названо, к кому идти
Не удалось сохранить Не сохранили черновик: пропала связь. Текст остался на устройстве, нажмите «Повторить» сообщена судьба данных, есть кнопка действия
Что-то пошло не так Не смогли построить отчёт. Мы уже чиним, обычно занимает 10–15 минут. Код для поддержки: RPT-8841 честно про статус, дан код, названо ожидание
Пароль не соответствует требованиям Добавьте цифру — остальное в пароле в порядке сказано ровно то, что осталось сделать

Несколько правил, которые стоит зафиксировать в гайде команды. Не обвиняйте: «Вы ввели неверный номер» → «Этот номер не подходит: в нём 15 цифр вместо 16». Разница не в вежливости, а в фокусе — во втором варианте описан факт, а не оценка человека.

Не врите про причину. «Попробуйте позже» при ошибке прав доступа — ложь: позже ничего не изменится. Пользователь потратит полчаса на повторные попытки, потом придёт в поддержку злым. Разные причины — разные тексты, даже если экран один.

Не показывайте код вместо сообщения, но и не выбрасывайте его. Код нужен поддержке: по нему находят конкретный запрос в логах. Значит, это должен быть correlation id, а не номер исключения, и его надо давать копируемым — в конце сообщения, мелким шрифтом.

Сообщайте, что случилось с данными. Самый страшный вопрос при ошибке — «я потерял то, что писал сорок минут?». Одна строка «черновик сохранён» стоит больше, чем весь остальной текст. И наоборот: не показывайте ошибку там, где её можно не показывать — автоматически обрежьте пробелы в номере карты, а не ругайтесь на них. Каждая ошибка, удалённая из системы, лучше самой хорошо написанной.

Где показывать сообщение — отдельное проектное решение, зависящее от масштаба поломки и от того, может ли пользователь продолжать работу.

Два узла этой схемы — про доступность, и они не случайно попали в схему выбора места. Куда переводится фокус и объявляется ли сообщение — это решение дизайнера, а не разработчика: именно вы знаете, в каком порядке человек должен узнать о проблеме. Механика — в доступности на этапе дизайна и подробно в статье «Доступные формы».

Отдельно про безопасность. «Неверный логин или пароль» — намеренно размытая формулировка: точное «такого пользователя нет» позволяет перебором собрать базу существующих аккаунтов. Это честный конфликт между юзабилити и защитой, и решается он не текстом, а конструкцией: после нескольких неудачных попыток предложите восстановление пароля прямо в сообщении. Смежная тема — в треке security.

Тон: как он устроен и где начинается вкусовщина

Тон часто обсуждают как черту бренда: «мы дружелюбные», «мы серьёзные». Это полуправда. У бренда есть голос — он постоянен; тон — это модуляция голоса под ситуацию, и он обязан меняться. NN/g предлагает раскладывать тон по четырём измерениям: смешной ↔ серьёзный, формальный ↔ разговорный, почтительный ↔ дерзкий, восторженный ↔ сдержанный. Полезность не в самой классификации, а в том, что по этим осям можно договориться с командой без слов «как-то не так звучит». Главное правило: чем выше цена ошибки и стресс пользователя, тем ближе к сухому и точному краю шкалы. Шутка на пустом экране «Здесь пока пусто» уместна; та же шутка при отказе платежа читается как издевательство.

Ещё несколько закономерностей, которые не про вкус. «Мы» и «вы». Используйте «мы», когда ответственность на системе («Мы не смогли связаться с банком»), и «вы» — когда действие принадлежит пользователю («Вы сможете отменить в течение часа»). Ошибка — писать «вы» там, где виновата система, и «мы» там, где решает пользователь. Второе особенно неприятно в согласиях: «мы обработаем ваши данные» вместо «вы разрешаете обработку» перекладывает субъектность.

Императив в кнопках, инфинитив в заголовках. «Оплатить», «Скачать» — на кнопках работает лучше всего, потому что кнопка отвечает на вопрос «что произойдёт». Заголовки лучше именные: «Оплата заказа», а не «Оплатите заказ» — заголовок называет место, а не командует.

Сокращения и термины. Термин допустим, если он у пользователя в голове раньше, чем в вашем интерфейсе. «Эквайринг» в панели для мерчантов — нормально. То же слово в приложении для покупателей — нет.

А вот честный список того, что является вкусовщиной и не должно занимать время команды дольше пятнадцати минут: «вы» с прописной или строчной; точка в конце подсказки; «Войти» против «Вход»; заглавные буквы в кнопках; тире или двоеточие в заголовке. Это вопрос стиля бренда. Правильный способ закрыть их — один раз зафиксировать в контент-гайде и больше не обсуждать; вечно спорить о них на ревью — способ не заниматься реальными проблемами.

Что доказано, а что стиль

Дисциплина богата советами, которые повторяют как заповеди. Полезно понимать, за какими из них стоят исследования, за какими — только здравый смысл, а какие — просто мода.

Утверждение Статус Основание
Плейсхолдер вместо подписи вредит сильные данные эксперименты NN/g и Baymard, требования WCAG к подписям
Ошибка рядом с полем работает лучше сводки наверху сильные данные исследования форм Baymard; сводка нужна дополнительно, не вместо
Преждевременная инлайн-валидация раздражает сильные данные тесты форм Baymard, повторяемый эффект
Люди не читают, а сканируют сильные данные eye-tracking NN/g, десятки исследований
Кнопка-глагол понятнее «ОК» умеренные данные тесты диалогов; частично закреплено в WCAG 2.5.3
Объяснение «зачем нужны данные» снижает отказы умеренные данные эксперименты в чекаутах, эффект зависит от чувствительности поля
Юмор в ошибках повышает лояльность нет данных, есть риск часто вредит при высоком стрессе
«Чем короче, тем лучше» миф Джинни Редиш: важна не длина, а информационная плотность и структура
«Вы» против «ты», точка в конце подсказки стиль позиционирование бренда, а не юзабилити; ноль измеримого влияния
Заглавные буквы в кнопках стиль, лёгкий минус сплошной верхний регистр читается чуть медленнее, эффект мал

Отсюда практическое следствие: спорные вопросы стиля решает контент-гайд волевым решением, а вопросы из верхних строк таблицы решают данные — и если кто-то в команде хочет их оспорить, это юзабилити-тест, а не совещание.

Отдельно про русскоязычную традицию «инфостиля». Приёмы вроде вычёркивания оценочных прилагательных и штампов действительно чистят текст. Но доведённое до догмы «сокращай» ломает интерфейс: из сообщения об ошибке первым делом вылетает объяснение причины, из подсказки — ответ на вопрос «зачем», и остаётся телеграфный стиль, в котором нет ни одной опоры. Сокращайте до тех пор, пока текст отвечает на вопрос пользователя, и ни словом дальше.

Числа, даты, единицы и множественное число

Место, где интерфейсный текст пересекается с кодом и где чаще всего проступает «программистский» акцент. Плюрализация: в русском три формы плюс форма для дробных: 1 файл, 2 файла, 5 файлов, 1,5 файла. Категории CLDR для русского — one, few, many, other; полная таблица правил — в спецификации CLDR. Никогда не собирайте строку конкатенацией — используйте ICU MessageFormat или встроенный Intl.PluralRules.

// Правила выбора формы отдаёт платформа, а не наши if-ы по последней цифре
const pr = new Intl.PluralRules("ru-RU");
// other — сюда попадают дробные: «1,5 файла»
const FILE_FORMS: Record<Intl.LDMLPluralRule, string> = {
  zero: "файлов", one: "файл", two: "файла",
  few: "файла", many: "файлов", other: "файла",
};

export function filesLabel(count: number): string {
  const form = FILE_FORMS[pr.select(count)];
  // Пробел-разделитель разрядов — тоже часть локали, не наша забота
  return `${new Intl.NumberFormat("ru-RU").format(count)} ${form}`;
}

filesLabel(1); // «1 файл»   filesLabel(3);    // «3 файла»
filesLabel(17); // «17 файлов»  filesLabel(1021); // «1 021 файл»

В ресурсном файле та же строка живёт как шаблон ICU — тогда переводчик на другой язык сам решает, сколько форм ему нужно:

{
  "files.selected": "{count, plural, one {Выбран # файл} few {Выбрано # файла} many {Выбрано # файлов} other {Выбрано # файла}}"
}

Даты. Абсолютная дата нужна там, где человек будет сверяться с документом или календарём: «12 июля 2026». Относительная («2 часа назад») хороша в лентах и уведомлениях, но вредна в юридически значимых местах — в истории платежей нужна точная отметка. Компромисс — относительная дата в тексте и точная в подсказке при наведении, но помните: наведения нет на телефоне, значит, точная дата должна быть доступна и по тапу.

Единицы и деньги. Пишите единицу рядом с числом и в том же формате, что на чеке: «1 490 руб.», «5 МБ», «10–15 минут». Не смешивайте «Мб» и «МБ» — это разные величины, и в интерфейсах хранилищ это регулярно приводит к жалобам.

Числа в кнопках. «Удалить 12 файлов» безопаснее, чем «Удалить»: человек видит масштаб операции. Но подставлять число надо через плюрализацию, а не «Удалить 12 файл(ов)» — скобки в интерфейсе выглядят как расписка в том, что до текста никому не было дела.

Локализация: почему макет разъезжается

Даже если продукт одноязычный, правила локализации полезны — они защищают от длинных реальных данных. Текст меняет длину при переводе непредсказуемо: W3C приводит ориентировочные коэффициенты роста — короткие строки до 10 символов при переводе с английского могут вырасти в 2–3 раза, длинные абзацы — на 10–30% (Text size in translation). Русский относительно английского обычно длиннее на 15–30%, немецкий даёт рекордные составные слова. Кнопка «Save» превращается в «Сохранить», «Speichern», «Sauvegarder» — и вёрстка с фиксированной шириной ломается. Три практических следствия для дизайнера.

  1. Проверяйте макет на самой длинной реалистичной строке, а не на «Иван Иванов». Возьмите из базы настоящее длинное название компании, настоящий email на 40 символов, настоящий адрес.
  2. Никогда не склеивайте предложение из кусков. Такой код выглядит невинно и неперводим:
// ПЛОХО: порядок слов и согласование зашиты в конкатенацию
const msg = "Вы " + (isDeleted ? "удалили" : "изменили") + " " + count + " " + entity;

// ХОРОШО: полноценные ключи под каждый смысл
// events.deleted = "{count, plural, one {Удалён # проект} few {Удалено # проекта} many {Удалено # проектов} other {Удалено # проекта}}"
// events.updated = "{count, plural, one {Изменён # проект} few {Изменено # проекта} many {Изменено # проектов} other {Изменено # проекта}}"
const msg2 = t(isDeleted ? "events.deleted" : "events.updated", { count });
  1. Задайте правило поведения при переполнении для каждого текстового слота: перенос на вторую строку, усечение с многоточием (и обязательно полный текст в тултипе и в accessible name), уменьшение кегля. Это часть спецификации компонента в дизайн-системе, а не импровизация разработчика.

Как текст живёт в команде

Текст в макете — черновик. Настоящий текст живёт в репозитории, в ресурсных файлах, и меняется чаще, чем макет. Отсюда практика: у каждой строки есть ключ, владелец и контекст.

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

Формат передачи строк — обычная таблица, но с обязательными колонками:

# strings/checkout.yaml — источник истины, ревьюится в PR как код
checkout.card.label:
  ru: "Номер карты"
  context: "Подпись поля ввода на экране оплаты"
  max_length: 24        # шире не влезет в мобильную колонку 320px
  owner: "design"
checkout.card.hint:
  ru: "Спишем {amount} один раз. Карту не сохраняем."
  context: "Подсказка под полем, видна всегда, не только при ошибке"
  vars: { amount: "сумма заказа с валютой, уже отформатирована" }
  owner: "product"
checkout.card.error.length:
  ru: "Нужно 16 цифр, сейчас {actual}"
  context: "Показывается по blur; повторно — на каждый ввод"
  vars: { actual: "число введённых цифр" }
  owner: "design"
  a11y: "Объявляется через role=alert, фокус остаётся в поле"

Такой файл можно проверять автоматически. Простой линтер ловит большую часть регрессий ещё до ревью:

"""Линтер интерфейсных строк: длина, запрещённые формулировки, потерянные переменные."""
import re, sys, yaml

BANNED = {
    "что-то пошло не так": "нет причины и нет действия",
    "произошла ошибка": "нет причины и нет действия",
    "вы ввели неверно": "обвинение пользователя",
    "нажмите здесь": "неинформативный текст ссылки, WCAG 2.4.4",
    "попробуйте позже": "часто ложь: позже ничего не изменится",
}
VAR = re.compile(r"\{(\w+)\}")


def check(path: str) -> list[str]:
    data = yaml.safe_load(open(path, encoding="utf-8"))
    problems: list[str] = []
    for key, item in data.items():
        text, limit = item["ru"], item.get("max_length")
        for phrase, why in BANNED.items():
            if phrase in text.lower():
                problems.append(f"{key}: запрещённая формулировка «{phrase}» — {why}")
        if limit and len(text) > limit:
            problems.append(f"{key}: {len(text)} символов при лимите {limit}")
        declared, used = set(item.get("vars", {})), set(VAR.findall(text))
        if declared != used:  # переменная без описания или описание без переменной
            problems.append(f"{key}: несовпадение переменных {sorted(used ^ declared)}")
        if not item.get("context"):
            problems.append(f"{key}: нет контекста — переводчик не поймёт, что это")
    return problems


if __name__ == "__main__":
    found = [p for path in sys.argv[1:] for p in check(path)]
    print("\n".join(found) or "Строки в порядке")
    sys.exit(1 if found else 0)

Сложность здесь линейная — O(n) по числу строк и O(1) дополнительной памяти на строку. Смысл не в алгоритме, а в том, что проверка встроена в CI: тексты перестают деградировать между релизами. Как встраивать такое в пайплайн — в треке devops и в статье про тестирование.

Передача в разработку и почему «пиксель в пиксель» — плохая цель

Тексты — самая наглядная иллюстрация того, почему требование «свёрстано должно быть точь-в-точь как в макете» вредит продукту. Макет показывает один сэмпл системы: одно значение переменной, одну длину имени, один язык, один размер шрифта, одну ширину экрана. Реальность — это множество состояний: имя на 42 символа, сумма в семь разрядов, перевод на немецкий, пользовательский зум 200%, узкий экран 320px, системный крупный шрифт на телефоне. Требовать пиксельного совпадения с одним сэмплом — значит требовать, чтобы все остальные случаи сломались.

Правильная цель передачи — совпадение правил, а не координат:

  • иерархия (что главнее, что второстепенно) сохранена;
  • отступы взяты из токенов системы, а не измерены линейкой;
  • поведение при переполнении задано явно для каждого слота;
  • состояния компонентов реализованы все, а не только «нормальное»;
  • порядок фокуса и порядок чтения соответствуют смыслу.

Отсюда обязанности дизайнера в передаче — не «отдать красивый макет», а отдать спецификацию поведения:

Что передаём Зачем разработчику
Ключи строк, а не картинки с текстом чтобы копировать без опечаток и класть в ресурсный файл
Все состояния каждого текстового слота чтобы не выдумывать текст загрузки и ошибки самому
Правило переполнения для каждого слота чтобы длинное имя не ломало карточку
Лимиты длины с обоснованием чтобы понимать, откуда взялась цифра, а не спорить
Переменные и их формат чтобы «12.07.2026» и «12 июля» не перепутались
Пометки по доступности что объявлять, куда переводить фокус
Реальные данные для проверки чтобы собрать экран на боевых значениях, а не на «Lorem»

И симметрично — обязанности разработчика: не молчать, когда строка не влезает, а возвращать вопрос; не сокращать текст самостоятельно; не заменять сообщение об ошибке на e.message. Самый дешёвый ритуал, который снимает 80% споров, — совместный просмотр собранного экрана на реальных данных до релиза, минут на пятнадцать. Подробно о процессе — в передаче в разработку и карьере, а о том, как это выглядит со стороны фронтенда, — в статье «Формы и валидация».

Доступность текста на этапе дизайна

Полностью тема раскрыта в треке accessibility — обзор, семантика, скринридеры — а в следующей статье трека мы разберём её как этап проектирования. Здесь достаточно того, что относится именно к формулировкам.

Текст ссылки должен работать вырванным из контекста. Скринридер умеет зачитывать список всех ссылок страницы; список из восьми «Подробнее» бесполезен. Пишите «Подробнее об условиях доставки» — критерий 2.4.4 Link Purpose.

Видимая надпись входит в программное имя. Если на кнопке написано «Отправить», её aria-label не может быть «Отправить заявку на кредит» — голосовое управление не найдёт кнопку по видимому названию (критерий 2.5.3). И ошибка не может передаваться только цветом или иконкой: нужен текст (критерий 1.4.1) и, где возможно, подсказка, как исправить (критерий 3.3.3 Error Suggestion).

Статусы должны объявляться. Тост «Сохранено», который просто появился, для скринридера не существует. В макете рядом с тостом должна стоять пометка: «объявляется вежливо» или «объявляется немедленно». Это ваша ответственность как проектировщика: только вы знаете, важно ли прерывать чтение.

Простой язык — это доступность. GOV.UK ориентируется на понятность текста для читателя примерно девятилетнего уровня чтения и открыто публикует руководство по стилю со списком слов, которых стоит избегать. Это не про «упрощение для глупых»: под нагрузкой, в спешке и на маленьком экране сложный синтаксис не читает никто.

Как проверять текст

Текст проверяется дешевле, чем макет, и почти всегда — быстрее.

Тест на предсказание. Показываете экран, закрываете всё кроме кнопки и спрашиваете: «что произойдёт, если нажать?». Если из пяти человек трое ответили по-разному — название плохое, независимо от того, насколько оно вам нравится. Рядом стоит пятисекундный тест для заголовков и пустых экранов: показать на 5 секунд, спросить «что это за экран и что тут можно сделать».

Highlighter test. Даёте распечатку экрана и два маркера: зелёным отметить то, что помогает, розовым — то, что смущает или лишнее. Работает без модератора и мгновенно показывает, какие абзацы никто не читает.

Cloze-тест на понятность. Из текста удаляется каждое пятое слово, человек восстанавливает пропуски. Доля правильных восстановлений — прокси понятности; методика описана у NN/g. Нужна редко, но незаменима для юридических и медицинских текстов.

A/B-тест формулировок — мощный, но применим только там, где хватает трафика. На экране, который видят 200 человек в месяц, разница между «Скачать» и «Экспорт» никогда не наберёт статистической значимости; там работает юзабилити-тест на пяти пользователях. Подробнее — в юзабилити-тестировании, метриках UX и в статье про эксперименты трека продакт-менеджмента.

Метрики, по которым видно плохой текст:

  • доля срабатываний валидации на конкретном поле (высокая — правило не объяснено заранее);
  • отвал на конкретном шаге воронки при том, что технически всё работает;
  • обращения в поддержку с формулировками «не понял», «не нашёл», «а это точно»;
  • доля нажатий «Отмена» в подтверждающих диалогах — если она велика, диалог пугает не тем;
  • время до первого клика на экране (косвенный признак того, что человек читает и не находит).

Типичные ошибки

  1. Текст пишут в последний день. Как следствие — в макете «Lorem ipsum», а в проде — то, что придумал разработчик в спешке.
  2. Кнопка называется механикой («Отправить», «Применить»), а не результатом.
  3. Подпись поля спрятана в плейсхолдер ради «чистоты» макета.
  4. Требования показываются только после ошибки.
  5. Сообщение об ошибке не говорит, что делать, и не говорит, что стало с данными.
  6. Код исключения показан вместо объяснения, а идентификатор для поддержки — не показан вовсе; «Что-то пошло не так» стоит на всех случаях жизни, включая нехватку прав.
  7. Юмор в местах с высокой ценой ошибки.
  8. Один и тот же объект называется разными словами на разных экранах (в списке «заявка», в письме «обращение», в поддержке «тикет»).
  9. Строки склеиваются конкатенацией — перевод и плюрализация ломаются; макет проверен на коротком имени, а в проде имена длинные.
  10. Тост с важной информацией исчезает через две секунды.
  11. Правовые формулировки переписаны дизайнером «чтобы звучало проще» — и потеряли юридический смысл.
  12. Спор о заглавной букве в «Вы» съел ревью, на котором можно было разобрать сообщения об ошибках.

Мини-итог

  • Интерфейс — это в основном текст; текст проектируют, а не «дописывают».
  • Каждый слот отвечает на один конкретный вопрос пользователя; слот без вопроса — лишний.
  • Кнопка называется результатом, а не операцией; словарь берётся у пользователя, а не из кода.
  • Ограничения показываются до ввода; валидация срабатывает после потери фокуса, а потом — на каждый символ.
  • Сообщение об ошибке состоит из четырёх частей: что случилось, почему, что делать, идентификатор для поддержки. Плюс судьба данных.
  • Тон — функция ситуации, а не только бренда: чем выше стресс, тем суше формулировка.
  • Есть вещи, доказанные исследованиями (плейсхолдеры, момент валидации, расположение ошибок), и есть чистая вкусовщина — её закрывают гайдом, а не спорами.
  • Числа, даты и множественное число форматирует платформа, а не наши if-ы; строки не склеиваются из кусков.
  • Передача в разработку — это ключи, состояния, лимиты и правила переполнения. «Пиксель в пиксель» — плохая цель, потому что макет показывает один сэмпл, а продукт живёт во множестве.

Источники

Что дальше

Мы всё время упирались в то, что текст должен дойти до человека — быть видимым, объявленным, доступным с клавиатуры и понятным при увеличении. Следующая статья превращает это в проектную дисциплину: как учитывать доступность на этапе дизайна, а не в конце.

Доступность на этапе дизайна: контраст, размеры, состояния, порядок

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов