alexivanou/russian-text-bundle
Composer 安装命令:
composer require alexivanou/russian-text-bundle
包简介
Symfony bundle providing Russian text functions: pluralization, number spelling, name inflection, noun/adjective declension, money spelling, time intervals, and more. Wraps wapmorgan/morphos into Symfony DI services and Twig extensions.
README 文档
README
Symfony bundle providing Russian text functions: pluralization, number spelling, name inflection, noun/adjective declension, geographical name inflection, money spelling, time ago, transliteration, phone formatting, identifier validation, semantic name parsing, date formatting, and text utilities. Built on top of wapmorgan/morphos.
Also available in Russian.
Installation
composer require alexivanou/russian-text-bundle
Symfony Flex will auto-configure the bundle. If you don't use Flex, add to bundles.php:
AlexIvanou\RussianTextBundle\AlexIvanouRussianTextBundle::class => ['all' => true],
Configuration
Every feature can be toggled on or off. When a feature is disabled, its service definition and autowiring alias are removed from the container at compile time — they are never instantiated, never loaded, and never consume memory.
# config/packages/russian_text.yaml russian_text: # --- Core services (14 flags) --- enable_pluralizer: true enable_number_speller: true enable_money_speller: true enable_name_inflector: true enable_noun_decliner: true enable_adjective_decliner: true enable_geo_inflector: true enable_time_speller: true enable_transliterator: true enable_identifier_validator: true enable_phone_formatter: true enable_name_parser: true enable_russian_date_formatter: true enable_text_helper: true # --- Twig extensions (7 flags) --- enable_twig_plural: true enable_twig_numeral: true enable_twig_name: true enable_twig_declension: true enable_twig_time: true enable_twig_utility: true enable_twig_validation: true
All flags default to true.
Why this matters
Memory savings
Every service is a container object with its own definition, arguments, and tags. Even if a service is never invoked, its definition is loaded into memory during DI container compilation. Disabling unused services:
- Reduces compiled container size (files in
var/cache/{env}/Container*.php) - Lowers PHP memory consumption per request in production
- Removes autowiring aliases, speeding up dependency resolution
Performance
On each Symfony request the container goes through several compilation phases:
- Resolution — resolving all service arguments. Fewer services = faster resolution.
- Dump — writing the compiled container to disk (only on first request after cache clear).
- Autowiring — searching for a matching service by type. Aliases for disabled services are not registered → faster autowiring.
Real-world example
If you only use Pluralizer and TimeSpeller (no validators, phones, declensions), disabling 15+ flags removes ~20 class definitions + 12 autowiring aliases from the container. Difference in a typical Symfony project:
| Metric | All enabled | Minimal set |
|---|---|---|
| Lines of compiled container code | ~3000 | ~800 |
| Service definitions | ~40 | ~18 |
| Memory per request | ~2.8 MB | ~2.5 MB |
| Compilation time (debug=0) | ~120 ms | ~80 ms |
Values are approximate and depend on PHP version and project complexity.
How it works
Flags are honest: if enable_pluralizer: false, then:
- Service
alexivanou.russian_text.pluralizeris NOT registered - Autowiring alias
PluralizerInterface::classis NOT registered - Any attempt to inject
PluralizerInterfacethrows a clearServiceNotFoundException, not a silent null
This lets IDEs and static analyzers (PHPStan, Psalm) correctly track dependencies.
Services
| Service ID | Interface | Description |
|---|---|---|
alexivanou.russian_text.pluralizer |
PluralizerInterface |
Pluralization of nouns after numerals |
alexivanou.russian_text.number_speller |
NumberSpellerInterface |
Cardinal and ordinal numerals in words |
alexivanou.russian_text.money_speller |
MoneySpellerInterface |
Money amounts in words (RUB, USD, EUR, UAH, KZT, BYN) |
alexivanou.russian_text.name_inflector |
NameInflectorInterface |
Personal name inflection by cases + gender detection |
alexivanou.russian_text.noun_decliner |
NounDeclinerInterface |
Noun declension by cases + pluralization |
alexivanou.russian_text.adjective_decliner |
AdjectiveDeclinerInterface |
Adjective declension by cases, gender, number |
alexivanou.russian_text.geo_inflector |
GeographicalNameInflectorInterface |
Geographical name inflection by cases |
alexivanou.russian_text.time_speller |
TimeSpellerInterface |
Human-readable time intervals in Russian |
alexivanou.russian_text.transliterator |
TransliteratorInterface |
Cyrillic → Latin transliteration + slug |
alexivanou.russian_text.identifier_validator |
IdentifierValidatorInterface |
INN, SNILS, OGRN, SWIFT, IBAN, etc. validation |
alexivanou.russian_text.phone_formatter |
PhoneFormatterInterface |
Phone number formatting (30 countries) |
alexivanou.russian_text.name_parser |
NameParserInterface |
Semantic FIO parsing |
alexivanou.russian_text.russian_date_formatter |
RussianDateFormatterInterface |
Russian month/day names |
alexivanou.russian_text.text_helper |
TextHelperInterface |
ordinalSuffix, currencySymbol, truncate |
Twig Usage
Pluralization (pluralize filter)
{{ 'дом'|pluralize(5) }} {# домов #}
{{ 'машина'|pluralize(2) }} {# машины #}
{{ 'сообщение'|pluralize(10) }} {# сообщений #}
Number spelling (cardinal, ordinal filters)
{{ 123|cardinal }} {# сто двадцать три #}
{{ 21|ordinal }} {# двадцать первый #}
{{ 961|ordinal }} {# девятьсот шестьдесят первый #}
Money spelling (spell_money filter)
{{ 123.45|spell_money }} {# сто двадцать три рубля сорок пять копеек #}
{{ 100|spell_money('USD') }} {# сто долларов #}
{{ 123.45|spell_money('RUB', 'short') }} {# 123 рубля 45 копеек #}
{{ 123.45|spell_money('RUB', 'accounting') }} {# сто двадцать три рубля 45 копеек #}
Name inflection (inflect_name filter, name_cases, detect_gender functions)
{{ 'Иванов Иван'|inflect_name('творительный') }} {# Ивановым Иваном #}
{{ 'Иванов Иван Иванович'|inflect_name('дательный') }} {# Иванову Ивану Ивановичу #}
{% set cases = name_cases('Иванов Иван') %}
{{ cases.genitive }} {# Иванова Ивана #}
{{ detect_gender('Иванова Мария') }} {# w #}
Noun declension (noun_case, noun_plural filters)
{{ 'дом'|noun_case('родительный') }} {# дома #}
{{ 'машина'|noun_case('творительный') }} {# машиной #}
{{ 'дом'|noun_plural(5) }} {# домов #}
Adjective declension (adj_case filter)
{{ 'новый'|adj_case('родительный') }} {# нового #}
{{ 'новый'|adj_case('творительный') }} {# новым #}
{{ 'синий'|adj_case('родительный') }} {# синего #}
{{ 'синий'|adj_case('дательный', 'f') }} {# синей (feminine) #}
Geographical name inflection (geo_case filter)
{{ 'Москва'|geo_case('родительный') }} {# Москвы #}
{{ 'Париж'|geo_case('творительный') }} {# Парижем #}
{{ 'Саратов'|geo_case('родительный') }} {# Саратова #}
{{ 'Томск'|geo_case('дательный') }} {# Томску #}
{{ 'Сочи'|geo_case('родительный') }} {# Сочи (immutable) #}
Time ago (time_ago, distance_of_time filters)
{{ post.createdAt|time_ago }} {# 5 минут назад #}
{{ event.date|time_ago }} {# через 2 часа #}
{{ post.createdAt|distance_of_time }} {# 5 минут назад #}
Transliteration (translit, slug filters)
{{ 'Привет'|translit }} {# Privet #}
{{ 'Привет'|slug }} {# privet #}
{{ 'Привет, мир!'|slug }} {# privet-mir #}
Validation functions
{{ is_valid_inn('7707083893') }} {# true #}
{{ is_valid_swift('DEUTDEFF') }} {# true #}
{{ is_valid_iban('DE44500105175407324931') }} {# true #}
{{ is_valid_credit_card('4111111111111111') }} {# true #}
Phone formatting
{{ '+79031234567'|phone_format }} {# +7 (903) 123-45-67 #}
{{ phone_is_valid('+79031234567') }} {# true #}
{{ phone_detect_country('+375291234567') }} {# BY #}
Name parsing
{% set name = parse_name('Иванов Иван Петрович') %}
{{ name.surname }} {# Иванов #}
{{ name.firstName }} {# Иван #}
{{ name.patronymic }} {# Петрович #}
{{ name.gender }} {# m #}
{{ name_initials('Иванов Иван Петрович') }} {# Иванов И. П. #}
{{ name_initials('Иванов Иван Петрович', 'before') }} {# И. П. Иванов #}
Date formatting
{{ post.createdAt|russian_date('j F Y') }} {# 15 январь 2026 #}
{{ post.createdAt|russian_date_genitive('j F Y') }} {# 15 января 2026 #}
{{ russian_month(3) }} {# март #}
{{ russian_month(3, 'genitive') }} {# марта #}
{{ russian_day_of_week(post.createdAt) }} {# вторник #}
Text helpers
{{ 1|ordinal_suffix }} {# 1-й #}
{{ 1|ordinal_suffix('f') }} {# 1-я #}
{{ 'RUB'|currency_symbol }} {# ₽ #}
{{ 'Очень длинный текст'|truncate(10) }} {# Очень... #}
Case Names
All case-aware methods accept any of the following formats:
| Constant | Russian | Abbreviation | English |
|---|---|---|---|
nominative |
именительный | и | nominative |
genitive |
родительный | р | genitive |
dative |
дательный | д | dative |
accusative |
винительный | в | accusative |
ablative |
творительный | т | instrumental |
prepositional |
предложный | п | prepositional |
PHP Usage
use AlexIvanou\RussianTextBundle\Service\Pluralizer; use AlexIvanou\RussianTextBundle\Service\NumberSpeller; use AlexIvanou\RussianTextBundle\Service\NameInflector; use AlexIvanou\RussianTextBundle\Service\NounDecliner; use AlexIvanou\RussianTextBundle\Service\MoneySpeller; use AlexIvanou\RussianTextBundle\Service\TimeSpeller; $pluralizer = new Pluralizer(); echo $pluralizer->pluralize('дом', 5); // домов $numberSpeller = new NumberSpeller(); echo $numberSpeller->cardinal(123); // сто двадцать три $nameInflector = new NameInflector(); echo $nameInflector->inflect('Иванов Иван', 'творительный'); // Ивановым Иваном $nounDecliner = new NounDecliner(); echo $nounDecliner->decline('дом', 'родительный'); // дома $adjDecliner = new AdjectiveDecliner(); echo $adjDecliner->decline('новый', 'родительный'); // нового $geoInflector = new GeographicalNameInflector(); echo $geoInflector->inflect('Москва', 'родительный'); // Москвы $moneySpeller = new MoneySpeller(); echo $moneySpeller->spell(123.45, MoneySpeller::RUBLE); // сто двадцать три рубля сорок пять копеек $timeSpeller = new TimeSpeller(); echo $timeSpeller->timeAgo(new \DateTime('-5 minutes')); // 5 минут назад
Architecture
Services are split into separate classes (not one monolithic helper) so you can inject only what you need.
Each service implements a corresponding interface (PluralizerInterface, NumberSpellerInterface, etc.) and is registered with an autowiring alias. You can type-hint the interface in your constructors:
use AlexIvanou\RussianTextBundle\Service\PluralizerInterface; class MyService { public function __construct(PluralizerInterface $pluralizer) { // ... } }
Validator architecture
The IdentifierValidator uses tagged services. Each validation rule (INN, KPP, SWIFT, IBAN, etc.) is a separate class implementing ValidationRuleInterface tagged with russian_text.validator. To add a custom validator:
namespace App\Validator; use AlexIvanou\RussianTextBundle\Service\ValidationRuleInterface; class PassportUaValidator implements ValidationRuleInterface { public function getType() { return 'passport_ua'; } public function isValid($value) { /* ... */ } }
# config/services.yaml services: App\Validator\PassportUaValidator: tags: - { name: russian_text.validator }
The IdentifierValidator collects all tagged validators via a compiler pass and exposes them through the fluent API.
Twig extensions
Twig extensions are separated by domain:
- PluralExtension — pluralization (
pluralize) - NumeralExtension — numerals + money (
cardinal,ordinal,spell_money) - NameExtension — names (
inflect_name,name_cases,detect_gender) - DeclensionExtension — nouns (
noun_case,noun_plural), adjectives (adj_case), geographical names (geo_case) - TimeExtension — time (
time_ago,distance_of_time) - UtilityExtension — transliteration (
translit,slug), text helper (truncate,ordinal_suffix,currency_symbol), date (russian_date,russian_date_genitive,russian_month,russian_day_of_week) - ValidationExtension — phone format (
phone_format,phone_is_valid,phone_detect_country), identifier validation (is_valid_inn,is_valid_snils, etc.), name parsing (parse_name,name_initials)
This design lets you disable unused features via bundle configuration and keeps your DI container lean.
What's covered vs wapmorgan/morphos
| Feature | morphos | This bundle |
|---|---|---|
| Noun pluralization | ✅ Russian\Plurality |
✅ Pluralizer |
| Cardinal numerals | ✅ Russian\CardinalNumeral |
✅ NumberSpeller |
| Ordinal numerals | ✅ Russian\OrdinalNumeral |
✅ NumberSpeller |
| Noun declension | ✅ Russian\GeneralDeclension |
✅ NounDecliner |
| Name inflection | ✅ Russian\name() |
✅ NameInflector |
| Gender detection | ✅ Russian\detectGender() |
✅ NameInflector |
| Adjective declension | ❌ | ✅ AdjectiveDecliner (custom) |
| Geographical name inflection | ❌ | ✅ GeographicalNameInflector (custom) |
| Money spelling | ❌ | ✅ MoneySpeller (custom) |
| Time ago | ❌ | ✅ TimeSpeller (custom) |
Requirements
- PHP 7.2+
- Symfony 4.2+
- Twig 2.0+
- ext-mbstring
Testing
vendor/bin/phpunit
License
MIT
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-08