Имена и их склонение

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

Типичные сценарии:

  • «Письмо отправлено Ивану» (дательный падеж)
  • «Профиль Ивана обновлён» (родительный падеж)
  • «Поздравляем Ивана» (винительный падеж)

При интернационализации через FormatJS базовые механизмы ICU MessageFormat не предоставляют встроенного склонения имён, поскольку ICU ориентирован на правила чисел, множественности и выбора (plural/sel ect), но не на морфологию имён.


ICU MessageFormat и ограничения морфологии

FormatJS использует ICU MessageFormat как основу для шаблонов:

import { IntlMessageFormat } fr om 'intl-messageformat';

const msg = new IntlMessageFormat(
  'Письмо отправлено {name}',
  'ru'
);

msg.format({ name: 'Иван' });

Результат всегда будет одинаковым вне зависимости от падежа:

Письмо отправлено Иван

ICU поддерживает конструкции:

  • plural
  • select
  • selectordinal
  • number, date, time

Однако отсутствует механизм:

  • склонения имён по падежам
  • морфологической трансформации существительных
  • грамматической генерации по языковым правилам

Архитектурные подходы к склонению имён в FormatJS

Подход 1. Хранение готовых форм имени

Самый прямолинейный способ — хранить все падежные формы имени заранее:

const user = {
  name: {
    nominative: 'Иван',
    genitive: 'Ивана',
    dative: 'Ивану',
    accusative: 'Ивана',
    instrumental: 'Иваном',
    prepositional: 'Иване'
  }
};

Использование в сообщениях:

const msg = new IntlMessageFormat(
  'Профиль {nameGenitive} обновлён',
  'ru'
);

msg.format({
  nameGenitive: user.name.genitive
});

Особенности подхода:

  • высокая точность
  • отсутствие runtime-логики
  • рост объёма данных пользователя
  • необходимость генерации на backend

Подход 2. Грамматические функции в values

FormatJS поддерживает передачу функций через values, что позволяет внедрять пользовательскую логику форматирования.

const cases = {
  genitive: (name) => inflect(name, 'genitive')
};

const msg = new IntlMessageFormat(
  'Профиль {name}',
  'ru'
);

msg.format({
  name: cases.genitive('Иван')
});

Здесь inflect — внешняя функция морфологического анализа.

Этот подход часто комбинируется с библиотеками:

  • pymorphy2 (через backend)
  • ru-inflect
  • custom NLP API

Подход 3. Централизованный морфологический сервис

В крупных системах склонение имён выносится на уровень сервиса:

async function getInflectedName(name, caseForm) {
  const response = await fetch('/api/inflect', {
    method: 'POST',
    body: JSON.stringify({ name, case: caseForm })
  });

  const data = await response.json();
  return data.result;
}

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

const msg = new IntlMessageFormat(
  'Письмо отправлено {name}',
  'ru'
);

const formatted = await msg.formatAsync({
  name: await getInflectedName('Иван', 'dative')
});

Преимущества:

  • единая языковая логика
  • возможность расширения на все языки
  • централизованное обновление правил

Недостатки:

  • задержка сети
  • необходимость кеширования
  • сложность офлайн-режима

Проблема неоднозначности имён

Имя пользователя может быть:

  • нераспознаваемым (никнеймы)
  • не склоняемым (латиница, бренды)
  • многосоставным («Jean-Luc», «de la Cruz»)

Поэтому система склонения должна учитывать классификацию:

const nameMeta = {
  value: 'Jean-Luc',
  type: 'foreign', // native | foreign | nickname | brand
  language: 'fr'
};

Правила применения:

  • foreign → не склонять
  • nickname → не склонять
  • native → применять морфологию
  • brand → фиксированная форма

Комбинация ICU Select и внешней морфологии

ICU MessageFormat позволяет выбирать шаблон:

const msg = new IntlMessageFormat(
  `{gender, select,
    male {Профиль обновлён пользователем {name}}
    female {Профиль обновлён пользователем {name}}
    other {Профиль обновлён пользователем {name}}
  }`,
  'ru'
);

Склонение имени остаётся внешним:

msg.format({
  gender: 'male',
  name: inflect('Иван', 'instrumental')
});

Таким образом разделяются задачи:

  • ICU отвечает за грамматику предложения
  • внешняя логика отвечает за морфологию слова

Использование rich-text форматирования для имён

FormatJS поддерживает rich-text значения:

const msg = new IntlMessageFormat(
  'Добро пожаловать, <b>{name}</b>',
  'ru',
  null,
  { formatters: {} }
);

Подстановка:

msg.format({
  name: (chunks) => `<span class="name">${chunks}</span>`
});

Это не решает склонение, но позволяет:

  • визуально выделять имя
  • отделять форматирование от логики
  • комбинировать с внешними трансформациями

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

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

Стратегия:

const cache = new Map();

function inflectCached(name, form) {
  const key = `${name}:${form}`;

  if (cache.has(key)) {
    return cache.get(key);
  }

  const result = inflect(name, form);
  cache.set(key, result);

  return result;
}

Особенно важно при массовых списках пользователей:

  • уведомления
  • ленты активности
  • отчёты

Смешанные языки и fallback-логика

При неизвестном языке или отсутствии поддержки склонения:

function safeInflect(user, form) {
  if (user.locale !== 'ru') {
    return user.name;
  }

  if (!isInflectable(user.name)) {
    return user.name;
  }

  return inflect(user.name, form);
}

Это предотвращает:

  • искажение иностранных имён
  • некорректные формы
  • ошибки NLP-моделей

Интеграция с форматированием сообщений FormatJS

В типичной архитектуре FormatJS слой сообщений отделён:

const messages = {
  profileUpdated: 'Профиль {name} обновлён',
  messageSent: 'Сообщение отправлено {name}'
};

Обёртка:

function formatMessage(id, values) {
  const msg = new IntlMessageFormat(messages[id], 'ru');
  return msg.format(values);
}

Интеграция с морфологией:

formatMessage('messageSent', {
  name: inflect('Иван', 'dative')
});

Масштабируемые стратегии для крупных систем

В больших i18n-системах обычно применяется комбинация:

  • ICU MessageFormat для структуры предложения
  • backend-сервис для морфологии
  • кеширование на уровне клиента
  • классификация имён по типам
  • fallback на неизменяемую форму

Архитектурная схема:

  • UI (FormatJS)
  • i18n layer (IntlMessageFormat)
  • morphology service
  • user profile service
  • cache layer (memory / Redis / IndexedDB)

Ограничения и практические последствия

Даже при использовании FormatJS остаются фундаментальные ограничения:

  • невозможность универсального алгоритма склонения имён
  • языковая неоднозначность (особенно в славянских языках)
  • отсутствие стандартов в ICU для именной морфологии
  • зависимость от внешних библиотек или сервисов

В результате система интернационализации перестаёт быть только задачей шаблонов и превращается в гибрид лингвистики и программной архитектуры.