alexivanou/russian-text-bundle 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

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

License: MIT Tests

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:

  1. Resolution — resolving all service arguments. Fewer services = faster resolution.
  2. Dump — writing the compiled container to disk (only on first request after cache clear).
  3. 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.pluralizer is NOT registered
  • Autowiring alias PluralizerInterface::class is NOT registered
  • Any attempt to inject PluralizerInterface throws a clear ServiceNotFoundException, 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
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-08

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固