feat(i18n): add initial Russian localization for documentation and site config

- Added full Russian translations for all main documentation sections () into .
- Sections translated include: key features, installation, getting started, all core concepts, advanced features, API reference, FAQ, links, additional modules, contributing, and license.
- Updated  to ensure language switching is available and Russian locale is active.
- Each Russian file preserves the structure and formatting of the original Markdown, with machine-aided draft translation for immediate use.
- Lays the groundwork for UI language switching (en/ru) and enables further manual translation refinement and review.
This commit is contained in:
Sergey Penkovsky
2025-08-14 15:46:53 +03:00
parent 248bf4c8c5
commit 9fee26c524
20 changed files with 682 additions and 10 deletions

View File

@@ -112,16 +112,8 @@ const config: Config = {
title: 'Community',
items: [
{
label: 'Stack Overflow',
href: 'https://stackoverflow.com/questions/tagged/docusaurus',
},
{
label: 'Discord',
href: 'https://discordapp.com/invite/docusaurus',
},
{
label: 'X',
href: 'https://x.com/docusaurus',
label: 'Telegram',
href: 'https://t.me/+22IVT0vqXBg1NDdi',
},
],
},

View File

@@ -0,0 +1,14 @@
---
sidebar_position: 9
---
# Дополнительные модули
CherryPick предоставляет набор официальных доп. модулей для расширенных и специфичных сценариев:
| Имя модуля | Описание |
|--------------------------|--------------------------------------------------------------|
| [**cherrypick_annotations**](https://pub.dev/packages/cherrypick_annotations) | Dart-аннотации для лаконичного DI и генерации кода. |
| [**cherrypick_generator**](https://pub.dev/packages/cherrypick_generator) | Генератор кода для автосборки DI-привязок по аннотациям. |
| [**cherrypick_flutter**](https://pub.dev/packages/cherrypick_flutter) | Интеграция DI с Flutter: виджеты-провайдеры, injection. |
| [**talker_cherrypick_logger**](https://pub.dev/packages/talker_cherrypick_logger) | Продвинутый логгер событий DI CherryPick с интеграцией в [Talker](https://pub.dev/packages/talker). Позволяет визуализировать состояние, ошибки и автоматически отслеживать DI прямо в UI и консоли. |

View File

@@ -0,0 +1,71 @@
---
sidebar_position: 3
---
# Обнаружение циклических зависимостей
CherryPick может обнаруживать циклические зависимости в вашей DI-конфигурации, помогая избежать бесконечных циклов и сложных для отладки ошибок.
## Как использовать:
### 1. Включите обнаружение во время разработки
**Локально (в рамках одного скоупа):**
```dart
final scope = CherryPick.openSafeRootScope(); // Локальное обнаружение включено по умолчанию
// или для существующего скоупа:
scope.enableCycleDetection();
```
**Глобально (между скоупами):**
```dart
CherryPick.enableGlobalCrossScopeCycleDetection();
final rootScope = CherryPick.openGlobalSafeRootScope();
```
### 2. Пример ошибки
Если вы объявите взаимозависимые сервисы:
```dart
class A { A(B b); }
class B { B(A a); }
scope.installModules([
Module((bind) {
bind<A>().to((s) => A(s.resolve<B>()));
bind<B>().to((s) => B(s.resolve<A>()));
}),
]);
scope.resolve<A>(); // выбросит CircularDependencyException
```
### 3. Общая рекомендация
- **Включайте обнаружение** всегда в debug и тестовой среде для максимальной безопасности.
- **Отключайте обнаружение** в production после завершения тестирования, ради производительности.
```dart
import 'package:flutter/foundation.dart';
void main() {
if (kDebugMode) {
CherryPick.enableGlobalCycleDetection();
CherryPick.enableGlobalCrossScopeCycleDetection();
}
runApp(MyApp());
}
```
### 4. Отладка и обработка ошибок
При обнаружении будет выброшено исключение `CircularDependencyException` с цепочкой зависимостей:
```dart
try {
scope.resolve<MyService>();
} on CircularDependencyException catch (e) {
print('Цепочка зависимостей: ${e.dependencyChain}');
}
```
**Подробнее:** смотрите [cycle_detection.ru.md](doc/cycle_detection.ru.md)

View File

@@ -0,0 +1,45 @@
---
sidebar_position: 1
---
# Иерархические подскоупы
CherryPick поддерживает иерархическую структуру скоупов, что позволяет строить сложные и модульные графы зависимостей для профессиональных архитектур приложений. Каждый подскоуп наследует зависимости родителя и позволяет переопределять их локально.
## Основные моменты
- **Подскоупы** — дочерние скоупы, открываемые от любого существующего (в том числе root).
- Зависимости подскоупа перекрывают родительские при разрешении.
- Если зависимость не найдена в подскоупе, CherryPick ищет её выше по иерархии.
- Подскоупы могут иметь собственные модули, "жизненный цикл", Disposable-объекты.
- Можно делать вложенность любой глубины для фич, компонентов и т.д.
## Пример
```dart
final rootScope = CherryPick.openRootScope();
rootScope.installModules([AppModule()]);
// Открыть подскоуп для функции/страницы
final userFeatureScope = rootScope.openSubScope('userFeature');
userFeatureScope.installModules([UserFeatureModule()]);
// В userFeatureScope сперва ищет в своей области
final userService = userFeatureScope.resolve<UserService>();
// Если не нашлось — идёт в rootScope
final sharedService = userFeatureScope.resolve<SharedService>();
// Подскоупы можно вкладывать друг в друга сколь угодно глубоко
final dialogScope = userFeatureScope.openSubScope('dialog');
dialogScope.installModules([DialogModule()]);
final dialogManager = dialogScope.resolve<DialogManager>();
```
## Применение
- Модульная изоляция частей/экранов с собственными зависимостями
- Переопределение сервисов для конкретных сценариев/навигации
- Управление жизнью и освобождением ресурсов по группам
**Совет:** Всегда закрывайте подскоупы, когда они больше не нужны, чтобы освободить ресурсы.

View File

@@ -0,0 +1,62 @@
---
sidebar_position: 2
---
# Логирование
CherryPick позволяет логировать все события и ошибки DI с помощью расширяемого observer-механизма.
## Кастомные Observer'ы
Вы можете передавать свою реализацию `CherryPickObserver` в root- или любой подскоуп.
Это позволяет централизовать и настраивать логирование, направлять логи в консоль, файл, сторонние сервисы или системы как [Talker](https://pub.dev/packages/talker).
### Пример: вывод всех событий в консоль
```dart
import 'package:cherrypick/cherrypick.dart';
void main() {
// Встроенный PrintCherryPickObserver для консоли
final observer = PrintCherryPickObserver();
final scope = CherryPick.openRootScope(observer: observer);
// Все события и ошибки DI будут выведены!
}
```
### Пример: расширенное логирование через Talker
Для более гибкого логирования или UI-оверлеев можно использовать observer наподобие [talker_cherrypick_logger](../talker_cherrypick_logger):
```dart
import 'package:cherrypick/cherrypick.dart';
import 'package:talker/talker.dart';
import 'package:talker_cherrypick_logger/talker_cherrypick_logger.dart';
void main() {
final talker = Talker();
final observer = TalkerCherryPickObserver(talker);
CherryPick.openRootScope(observer: observer);
// Все события попадают в Talker!
}
```
## Поведение по умолчанию
- По умолчанию логирование "тихое" (SilentCherryPickObserver) для production — нет вывода без observer'а.
- Можно назначить observer для любого скоупа.
## Возможности Observer'а
- Регистрация зависимостей
- Получение/создание/удаление экземпляров
- Установка/удаление модулей
- Открытие/закрытие скоупов
- Кэш-хиты/мимо
- Обнаружение циклов
- Диагностика, предупреждения, ошибки
## Когда применять
- Подробное логирование в dev/test окружениях
- Передача логов в основную систему/аналитику
- Отладка и профилирование DI

View File

@@ -0,0 +1,10 @@
---
sidebar_position: 4
---
# Улучшения производительности
> **Примечание по производительности:**
> **Начиная с версии 3.0.0**, CherryPick использует Map-индексатор для поиска зависимостей. Это означает, что вызовы `resolve<T>()` и связанные методы работают за O(1) независимо от количества модулей/биндингов в скоупе. Ранее библиотека просматривала все модули/биндинги, что могло замедлять DI в крупных проектах.
>
> Эта оптимизация полностью внутренняя: интерфейс библиотеки и пользовательский код не изменились, но производительность заметно выросла на больших графах зависимостей.

View File

@@ -0,0 +1,7 @@
---
sidebar_position: 10
---
# Вклад в проект
Вкладывайтесь! Открывайте задачи или отправляйте pull request'ы на [GitHub](https://github.com/pese-git/cherrypick).

View File

@@ -0,0 +1,41 @@
---
sidebar_position: 1
---
# Привязка (Binding)
**Binding** — это конфигурация, которая определяет, как создавать или предоставлять конкретную зависимость. Binding поддерживает:
* Прямое присваивание экземпляра (`toInstance()`, `toInstanceAsync()`)
* Ленивые провайдеры (синхронные/асинхронные функции)
* Провайдеры с поддержкой динамических параметров
* Именованные экземпляры для получения по строковому ключу
* Необязательное управление жизненным циклом синглтона
## Пример
```dart
// Прямое создание экземпляра
Binding<String>().toInstance("Hello world");
// Асинхронное создание экземпляра
Binding<String>().toInstanceAsync(Future.value("Hello world"));
// Ленивое создание экземпляра через фабрику (sync)
Binding<String>().toProvide(() => "Hello world");
// Ленивое создание экземпляра через фабрику (async)
Binding<String>().toProvideAsync(() async => "Hello async world");
// Экземпляр с параметрами (sync)
Binding<String>().toProvideWithParams((params) => "Hello $params");
// Экземпляр с параметрами (async)
Binding<String>().toProvideAsyncWithParams((params) async => "Hello $params");
// Именованный экземпляр для получения по имени
Binding<String>().toProvide(() => "Hello world").withName("my_string");
// Синглтон (один экземпляр внутри скоупа)
Binding<String>().toProvide(() => "Hello world").singleton();
```

View File

@@ -0,0 +1,52 @@
---
sidebar_position: 4
---
# Disposable
CherryPick может автоматически очищать любые зависимости, реализующие интерфейс `Disposable`. Это упрощает управление ресурсами (контроллеры, потоки, сокеты, файлы и др.) — особенно при закрытии скоупа или приложения.
Если вы регистрируете объект, реализующий `Disposable`, как синглтон или через DI-контейнер, CherryPick вызовет его метод `dispose()` при закрытии или очистке скоупа.
## Основные моменты
- Поддерживаются синхронная и асинхронная очистка (dispose может возвращать `void` или `Future`).
- Все объекты `Disposable` из текущего скоупа и подскоупов будут удалены в правильном порядке.
- Предотвращает утечки ресурсов и обеспечивает корректную очистку.
- Не нужно вручную связывать очистку — просто реализуйте интерфейс.
## Минимальный синхронный пример
```dart
class CacheManager implements Disposable {
void dispose() {
cache.clear();
print('CacheManager удалён!');
}
}
final scope = CherryPick.openRootScope();
scope.installModules([
Module((bind) => bind<CacheManager>().toProvide(() => CacheManager()).singleton()),
]);
// ...спустя время
await CherryPick.closeRootScope(); // выведет: CacheManager удалён!
```
## Асинхронный пример
```dart
class MyServiceWithSocket implements Disposable {
@override
Future<void> dispose() async {
await socket.close();
print('Socket закрыт!');
}
}
scope.installModules([
Module((bind) => bind<MyServiceWithSocket>().toProvide(() => MyServiceWithSocket()).singleton()),
]);
await CherryPick.closeRootScope(); // дождётся завершения async очистки
```
**Совет:** Всегда вызывайте `await CherryPick.closeRootScope()` или `await scope.closeSubScope(key)` в вашем shutdown/teardown-коде для гарантированной очистки ресурсов.

View File

@@ -0,0 +1,19 @@
---
sidebar_position: 2
---
# Модуль
**Модуль** — это логическая точка сбора для привязок (bindings), предназначенная для группирования и инициализации связанных зависимостей. Реализуйте метод `builder`, чтобы определить, как зависимости будут связываться внутри скоупа.
## Пример
```dart
class AppModule extends Module {
@override
void builder(Scope currentScope) {
bind<ApiClient>().toInstance(ApiClientMock());
bind<String>().toProvide(() => "Hello world!");
}
}
```

View File

@@ -0,0 +1,31 @@
---
sidebar_position: 3
---
# Скоуп (Scope)
**Scope** управляет деревом модулей и экземпляров зависимостей. Скоупы могут быть вложенными (иерархия родитель-дочерний), обеспечивая модульную компоновку приложения и возможность переопределения зависимостей для отдельных контекстов.
Обычно вы работаете с корневым скоупом, но при необходимости можете создавать именованные подскоупы.
## Пример
```dart
// Открыть основной/корневой скоуп
final rootScope = CherryPick.openRootScope();
// Установить пользовательский модуль
rootScope.installModules([AppModule()]);
// Получить зависимость синхронно
final str = rootScope.resolve<String>();
// Получить зависимость асинхронно
final result = await rootScope.resolveAsync<String>();
// Рекомендуется: закрывать корневой скоуп и высвобождать все ресурсы
await CherryPick.closeRootScope();
// Либо вручную вызвать dispose на любом скоупе, которым вы управляете индивидуально
// await rootScope.dispose();
```

View File

@@ -0,0 +1,15 @@
---
sidebar_position: 4
---
# API разрешения зависимостей
- `resolve<T>()` — получает экземпляр зависимости или выбрасывает исключение, если не найдено.
- `resolveAsync<T>()` — асинхронный вариант для зависимостей с асинхронной инициализацией.
- `tryResolve<T>()` — возвращает `null`, если не найдено (синхронно).
- `tryResolveAsync<T>()` — возвращает `null` асинхронно, если не найдено.
Поддерживает:
- Синхронные и асинхронные зависимости
- Именованные зависимости
- Провайдеры с runtime-параметрами или без них

View File

@@ -0,0 +1,7 @@
---
sidebar_position: 8
---
# Ссылки на документацию
* Обнаружение циклических зависимостей [(En)](doc/cycle_detection.en.md)[(Ru)](doc/cycle_detection.ru.md)

View File

@@ -0,0 +1,124 @@
---
sidebar_position: 6
---
# Пример приложения
Ниже приведён полный пример с модулями, подскоупами, асинхронными провайдерами и разрешением зависимостей.
```dart
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:cherrypick/cherrypick.dart';
class AppModule extends Module {
@override
void builder(Scope currentScope) {
bind<ApiClient>().withName("apiClientMock").toInstance(ApiClientMock());
bind<ApiClient>().withName("apiClientImpl").toInstance(ApiClientImpl());
}
}
class FeatureModule extends Module {
final bool isMock;
FeatureModule({required this.isMock});
@override
void builder(Scope currentScope) {
// Асинхронный провайдер DataRepository с выбором зависимости по имени
bind<DataRepository>()
.withName("networkRepo")
.toProvideAsync(() async {
final client = await Future.delayed(
Duration(milliseconds: 100),
() => currentScope.resolve<ApiClient>(
named: isMock ? "apiClientMock" : "apiClientImpl",
),
);
return NetworkDataRepository(client);
})
.singleton();
// Вызов асинхронного провайдера для DataBloc
bind<DataBloc>().toProvideAsync(
() async {
final repo = await currentScope.resolveAsync<DataRepository>(
named: "networkRepo");
return DataBloc(repo);
},
);
}
}
void main() async {
final scope = CherryPick.openRootScope().installModules([AppModule()]);
final featureScope = scope.openSubScope("featureScope")
..installModules([FeatureModule(isMock: true)]);
final dataBloc = await featureScope.resolveAsync<DataBloc>();
dataBloc.data.listen(
(d) => print('Получены данные: $d'),
onError: (e) => print('Ошибка: $e'),
onDone: () => print('DONE'),
);
await dataBloc.fetchData();
}
class DataBloc {
final DataRepository _dataRepository;
Stream<String> get data => _dataController.stream;
final StreamController<String> _dataController = StreamController.broadcast();
DataBloc(this._dataRepository);
Future<void> fetchData() async {
try {
_dataController.sink.add(await _dataRepository.getData());
} catch (e) {
_dataController.sink.addError(e);
}
}
void dispose() {
_dataController.close();
}
}
abstract class DataRepository {
Future<String> getData();
}
class NetworkDataRepository implements DataRepository {
final ApiClient _apiClient;
final _token = 'token';
NetworkDataRepository(this._apiClient);
@override
Future<String> getData() async =>
await _apiClient.sendRequest(
url: 'www.google.com',
token: _token,
requestBody: {'type': 'data'},
);
}
abstract class ApiClient {
Future sendRequest({@required String? url, String? token, Map? requestBody});
}
class ApiClientMock implements ApiClient {
@override
Future sendRequest(
{@required String? url, String? token, Map? requestBody}) async {
return 'Local Data';
}
}
class ApiClientImpl implements ApiClient {
@override
Future sendRequest(
{@required String? url, String? token, Map? requestBody}) async {
return 'Network data';
}
}
```

View File

@@ -0,0 +1,10 @@
---
sidebar_position: 7
---
# Часто задаваемые вопросы
### В: Нужно ли использовать `await` с CherryPick.closeRootScope(), CherryPick.closeScope() или scope.dispose(), если у меня нет Disposable-сервисов?
**О:**
Да! Даже если ваши сервисы сейчас не реализуют `Disposable`, всегда используйте `await` при закрытии скоупов. Если вы позже добавите очистку ресурсов (реализовав dispose()), CherryPick всё обработает автоматически и ваш код останется без изменений. Это гарантирует надежное освобождение ресурсов для любого сценария.

View File

@@ -0,0 +1,27 @@
---
sidebar_position: 3
---
# Быстрый старт
Минимальный пример регистрации и получения зависимости:
```dart
import 'package:cherrypick/cherrypick.dart';
class AppModule extends Module {
@override
void builder(Scope currentScope) {
bind<ApiClient>().toInstance(ApiClientMock());
bind<String>().toProvide(() => "Hello, CherryPick!");
}
}
final rootScope = CherryPick.openRootScope();
rootScope.installModules([AppModule()]);
final greeting = rootScope.resolve<String>();
print(greeting); // напечатает: Hello, CherryPick!
await CherryPick.closeRootScope();
```

View File

@@ -0,0 +1,18 @@
---
sidebar_position: 2
---
# Установка
Добавьте в ваш `pubspec.yaml`:
```yaml
dependencies:
cherrypick: ^<latest_version>
```
Затем выполните команду:
```shell
dart pub get
```

View File

@@ -0,0 +1,16 @@
---
sidebar_position: 1
---
# Ключевые возможности
- Главный скоуп и именованные подскоупы
- Привязка и разрешение экземпляров по имени
- Асинхронные и синхронные провайдеры
- Провайдеры с поддержкой параметров времени выполнения
- Управление жизненным циклом синглтонов
- Модульная и иерархическая композиция
- Null-safe разрешение зависимостей (tryResolve/tryResolveAsync)
- Обнаружение циклических зависимостей (локально и глобально)
- Подробное логирование состояния и действий DI-контейнера
- Автоматическое освобождение ресурсов для всех зарегистрированных Disposable зависимостей

View File

@@ -0,0 +1,9 @@
---
sidebar_position: 11
---
# Лицензия
Проект распространяется под [лицензией Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
**Важно:** При отсутствии специальных договорённостей поставляется "КАК ЕСТЬ", без каких-либо гарантий. Подробности см. в самой лицензии.

View File

@@ -0,0 +1,102 @@
---
sidebar_position: 5
---
# Использование аннотаций и генерация кода
CherryPick предоставляет продвинутую эргономику и безопасный DI благодаря **аннотациям Dart** и генерации кода. Это позволяет избавить вас от рутины — просто аннотируйте классы, поля и модули, запускайте генератор и используйте полностью автосвязанный DI!
## Как это работает
1. **Аннотируйте** сервисы, провайдеры и поля с помощью `cherrypick_annotations`.
2. **Генерируйте** код с помощью `cherrypick_generator` и `build_runner`.
3. **Используйте** автосгенерированные модули и миксины для автоматического внедрения.
---
## Поддерживаемые аннотации
| Аннотация | Target | Описание |
|---------------------|---------------|--------------------------------------------------------------|
| `@injectable()` | класс | Включает автоподстановку полей (генерируется mixin) |
| `@inject()` | поле | Автоподстановка через DI (работает с @injectable) |
| `@module()` | класс | DI-модуль: методы — провайдеры и сервисы |
| `@provide` | метод | Регистрирует как DI-провайдер (можно с параметрами) |
| `@instance` | метод/класс | Регистрирует новый экземпляр (на каждый resolve, factory) |
| `@singleton` | метод/класс | Регистрация как синглтон (один экземпляр на скоуп) |
| `@named` | поле/параметр | Использование именованных экземпляров для внедрения/resolve |
| `@scope` | поле/параметр | Внедрение/resolve из другого (именованного) скоупа |
| `@params` | параметр | Добавляет user-defined параметры во время resolve |
---
## Пример Field Injection
```dart
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
@injectable()
class ProfilePage with _\$ProfilePage {
@inject()
late final AuthService auth;
@inject()
@scope('profile')
late final ProfileManager manager;
@inject()
@named('admin')
late final UserService adminUserService;
}
```
- После запуска build_runner миксин `_ProfilePage` будет сгенерирован для внедрения.
- Вызовите `myProfilePage.injectFields();` чтобы все зависимости были внедрены автоматически.
## Пример модуля/провайдера
```dart
@module()
abstract class AppModule {
@singleton
AuthService provideAuth(Api api) => AuthService(api);
@named('logging')
@provide
Future<Logger> provideLogger(@params Map<String, dynamic> args) async => ...;
}
```
---
## Шаги использования
1. Добавьте зависимости в `pubspec.yaml`.
2. Аннотируйте классы и модули.
3. Генерируйте код командой build_runner.
4. Регистрируйте модули и используйте автосвязь.
---
## Расширенные возможности
- Используйте `@named` для внедрения по ключу.
- Используйте `@scope` для внедрения из разных скоупов.
- Используйте `@params` для передачи runtime-параметров.
---
## Советы и FAQ
- После изменений в DI-коде запускайте build_runner заново.
- Не редактируйте `.g.dart` вручную.
- Ошибки некорректных аннотаций определяются автоматически.
---
## Ссылки
- [Подробнее про аннотации (en)](doc/annotations_en.md)
- [cherrypick_annotations/README.md](../cherrypick_annotations/README.md)
- [cherrypick_generator/README.md](../cherrypick_generator/README.md)
- Полный пример: [`examples/postly`](../examples/postly)