Commit 33101c05 by Alexander Makarov

Merge pull request #6562 from tanakahisateru/guide-ja-concept

Translation guide-ja/concept-*
parents 337326ff d3f6ce77
エイリアス
=======
ファイルパスや URL を表すのにエイリアスを使用すると、あなたはプロジェクト内で絶対パスや URL をハードコードする必要がなくなります。エイリアスは、通常のファイルパスや URL と区別するために、 `@` 文字で始まる必要があります。Yii はすでに利用可能な多くの事前定義エイリアスを持っています。
たとえば、 `@yii` というエイリアスは Yii フレームワークのインストールパスを表し、 `@web` は現在実行中の Web アプリケーションのベース URL を表します。
エイリアスの定義 <a name="defining-aliases"></a>
----------------
[[Yii::setAlias()]] を呼び出すことにより、ファイルパスまたは URL のエイリアスを定義することができます。
```php
// ファイルパスのエイリアス
Yii::setAlias('@foo', '/path/to/foo');
// URL のエイリアス
Yii::setAlias('@bar', 'http://www.example.com');
```
> 補足: エイリアスされているファイルパスやURLは、必ずしも実在するファイルまたはリソースを参照しない場合があります。
定義済みのエイリアスがあれば、スラッシュ `/` に続けて 1 つ以上のパスセグメントを追加することで([[Yii::setAlias()]]
の呼び出しを必要とせずに) 新しいエイリアスを導出することができます。 [[Yii::setAlias()]] を通じて定義されたエイリアスは
*ルートエイリアス* となり、それから派生したエイリアスは *派生エイリアス* になります。たとえば、 `@foo` がルートエイリアスなら、
`@foo/bar/file.php` は派生エイリアスです。
エイリアスを、他のエイリアス (ルートまたは派生のいずれか) を使用して定義することができます:
```php
Yii::setAlias('@foobar', '@foo/bar');
```
ルートエイリアスは通常、 [ブートストラップ](runtime-bootstrapping.md) 段階で定義されます。
たとえば、[エントリスクリプト](structure-entry-scripts.md)[[Yii::setAlias()]] を呼び出すことができます。
便宜上、 [アプリケーション](structure-applications.md) は、`aliases` という名前の書き込み可能なプロパティを提供しており、
それをアプリケーションの [構成情報](concept-configurations.md) で設定することが可能です。
```php
return [
// ...
'aliases' => [
'@foo' => '/path/to/foo',
'@bar' => 'http://www.example.com',
],
];
```
エイリアスの解決 <a name="resolving-aliases"></a>
-----------------
[[Yii::getAlias()]] を呼び出して、ルートエイリアスが表すファイルパスまたはURLを解決することができます。
同メソッドで、対応するファイルパスまたはURLに派生するエイリアスを解決することもできます。
```php
echo Yii::getAlias('@foo'); // /path/to/foo を表示
echo Yii::getAlias('@bar'); // http://www.example.com を表示
echo Yii::getAlias('@foo/bar/file.php'); // /path/to/foo/bar/file.php を表示
```
派生エイリアスによって表されるパスやURLは、派生エイリアス内のルートエイリアス部分を、対応するパス/URL
で置換して決定されます。
> 補足: [[Yii::getAlias()]] メソッドは、 結果のパスやURLが実在するファイルやリソースを参照しているかをチェックしません。
ルートエイリアス名にはスラッシュ `/` 文字を含むことができます。 [[Yii::getAlias()]] メソッドは、
エイリアスのどの部分がルートエイリアスであるかを賢く判別し、正確に対応するファイルパスやURLを決定します:
```php
Yii::setAlias('@foo', '/path/to/foo');
Yii::setAlias('@foo/bar', '/path2/bar');
Yii::getAlias('@foo/test/file.php'); // /path/to/foo/test/file.php を表示
Yii::getAlias('@foo/bar/file.php'); // /path2/bar/file.php を表示
```
もし `@foo/bar` がルートエイリアスとして定義されていなければ、最後のステートメントは `/path/to/foo/bar/file.php` を表示します。
エイリアスの使用 <a name="using-aliases"></a>
-------------
エイリアスは、それをパスやURLに変換するための [[Yii::getAlias()​]] の呼び出しがなくても、Yiiの多くの場所でみられます。
たとえば、 [[yii\caching\FileCache::cachePath]] ファイルパスとファイルパスを表すエイリアスの両方を受け入れることができ、
`@` プレフィックスによって、エイリアスとファイルパスを区別することができます。
```php
use yii\caching\FileCache;
$cache = new FileCache([
'cachePath' => '@runtime/cache',
]);
```
プロパティやメソッドのパラメータがエイリアスをサポートしているかどうかは、API ドキュメントに注意を払ってください。
事前定義されたエイリアス <a name="predefined-aliases"></a>
------------------
Yii では、一般的に使用されるフ​​ァイルのパスと URL を簡単に参照できるよう、エイリアスのセットが事前に定義されています:
- `@yii`, `BaseYii.php` ファイルがあるディレクトリ (フレームワークディレクトリとも呼ばれます)
- `@app`, 現在実行中のアプリケーションの [[yii\base\Application::basePath|ベースパス]]
- `@runtime`, 現在実行中のアプリケーションの [[yii\base\Application::runtimePath|ランタイムパス]] 。デフォルトは `@app/runtime`
- `@webroot`, 現在実行中の Web アプリケーションの Web ルートディレクトリ。エントリスクリプトを含むディレクトリをもとに決定されます。
- `@web`, 現在実行中の Web アプリケーションのベース URL。これは、 [[yii\web\Request::baseUrl]] と同じ値を持ちます。
- `@vendor`, [[yii\base\Application::vendorPath|Composerのベンダーディレクトリ]] 。デフォルトは `@app/vendor`
- `@bower`, [bower パッケージ](http://bower.io/) が含まれるルートディレクトリ。デフォルトは `@vendor/bower`
- `@npm`, [npm パッケージ](https://www.npmjs.org/) が含まれるルートディレクトリ。デフォルトは `@vendor/npm`
`@yii` エイリアスは [エントリスクリプト](structure-entry-scripts.md)`Yii.php` ファイルを読み込んだ時点で定義されます。
エイリアスの残りの部分は、アプリケーションのコンストラクタ内で、アプリケーションの [構成情報](concept-configurations.md) を適用するときに定義されます。
エクステンションのエイリアス <a name="extension-aliases"></a>
-----------------
Composer でインストールされる各 [エクステンション](structure-extensions.md) ごとに、エイリアスが自動的に定義されます。
各エイリアスは、その `composer.json` ファイルで宣言された、エクステンションのルート名前空間にちなんで名付けられており、
それらは、パ​​ッケージのルートディレクトリを表します。たとえば、あなたが `yiisoft/yii2-jui` エクステンションをインストールしたとすると、
自動的に `@yii/jui` というエイリアスができ、 [ブートストラップ](runtime-bootstrapping.md) 段階で、次のと同等のものとして定義されます:
```php
Yii::setAlias('@yii/jui', 'VendorPath/yiisoft/yii2-jui');
```
クラスのオートローディング
=================
Yiiは、必要となるすべてのクラスファイルを、特定してインクルードするにあたり、 [クラスのオートローディングメカニズム](http://www.php.net/manual/en/language.oop5.autoload.php)
を頼りにします。[PSR-4 標準](https://github.com/php-fig/fig-standards/blob/master/proposed/psr-4-autoloader/psr-4-autoloader.md) に準拠した、高性能なクラスのオートローダーを提供します。
このオートローダーは、あなたが `Yii.php` ファイルをインクルードするときにインストールされます。
> 補足: 説明を簡単にするため、このセクションではクラスのオートローディングについてのみお話しします。しかし、
ここに記述されている内容は、同様に、インタフェースとトレイトのオートロードにも適用されることに注意してください。
Yii オートローダーの使用 <a name="using-yii-autoloader"></a>
------------------------
Yii のクラスオートローダーを使用するには、自分のクラスを作成して名前を付けるとき、次の2つの単純なルールに従わなければなりません:
* 各クラスは名前空間の下になければなりません (例 `foo\bar\MyClass`)
* 各クラスは次のアルゴリズムで決定される個別のファイルに保存されなければなりません:
```php
// $className は先頭にバックスラッシュを持つ完全修飾名
$classFile = Yii::getAlias('@' . str_replace('\\', '/', $className) . '.php');
```
たとえば、クラス名と名前空間が `foo\bar\MyClass` であれば、対応するクラスファイルのパスの [エイリアス](concept-aliases.md) は、
`@foo/bar/MyClass.php` になります。このエイリアスがファイルパスになるようにするには、`@foo` または `@foo/bar`
のどちらかが、 [ルートエイリアス](concept-aliases.md#defining-aliases) でなければなりません。
[Basic Application Template](start-basic.md) を使用している場合、最上位の名前空間 `app` の下にクラスを置くことができ、
そうすると、新しいエイリアスを定義しなくても、Yii によってそれらをオートロードできるようになります。これは `@app`
[事前定義されたエイリアス](concept-aliases.md#predefined-aliases) であるためで、`app\components\MyClass` のようなクラス名を
今説明したアルゴリズムに従って、クラスファイル `AppBasePath/components/MyClass.php` だと解決できるのです。
[Advanced Application Template](tutorial-advanced-app.md) では、各階層にそれ自身のルートエイリアスを持っています。たとえば、
フロントエンド層はルートエイリアス `@frontend` を持ち、バックエンド層は `@backend` です。その結果、名前空間 `frontend` の下に
フロントエンドクラスを置き、バックエンドクラスを `backend` の下に置けます。これで、これらのクラスは Yii のオートローダーによって
オートロードできるようになります。
クラスマップ <a name="class-map"></a>
---------
Yii のクラスオートローダーは、 *クラスマップ* 機能をサポートしており、クラス名を対応するクラスファイルのパスにマップできます。
オートローダーがクラスをロードしているとき、クラスがマップに見つかるかどうかを最初にチェックします。もしあれば、対応する
ファイルのパスは、それ以上チェックされることなく、直接インクルードされます。これでクラスのオートローディングを非常に高速化できます。
実際に、すべての Yii のコアクラスは、この方法でオートロードされています。
次の方法で、 `Yii::$classMap` に格納されるクラスマップにクラスを追加できます:
```php
Yii::$classMap['foo\bar\MyClass'] = 'path/to/MyClass.php';
```
クラスファイルのパスを指定するのに、 [エイリアス](concept-aliases.md) を使うことができます。クラスが使用される前にマップが準備できるように、
[ブートストラップ](runtime-bootstrapping.md) プロセス内でクラスマップを設定する必要があります。
他のオートローダーの使用 <a name="using-other-autoloaders"></a>
-----------------------
Yii はパッケージ依存関係マネージャとして Composer を包含しているので、Composer のオートローダーもインストールすることをお勧めします。
あなたが独自のオートローダーを持つサードパーティライブラリを使用している場合、それらもインストールする必要があります。
Yii オートローダーを他のオートローダーと一緒に使うときは、他のすべてのオートローダーがインストールされた *後で*`Yii.php`
ファイルをインクルードする必要があります。これで Yii のオートローダーが、任意クラスのオートローディング要求に応答する最初のものになります。
たとえば、次のコードは [Basic Application Template](start-basic.md)[エントリスクリプト](structure-entry-scripts.md) から抜粋したものです。
最初の行は、Composer のオートローダーをインストールしており、二行目は Yii のオートローダーをインストールしています。
```php
require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
```
あなたは Yii のオートローダーを使わず、Composer のオートローダーだけを単独で使用することもできます。しかし、そうすることによって、
あなたのクラスのオートローディングのパフォーマンスは低下し、クラスをオートロード可能にするために Composer が設定したルールに従わなければならなくなります。
> Info: Yiiのオートローダーを使用したくない場合は、 `Yii.php` ファイルの独自のバージョンを作成し、
それを [エントリスクリプト](structure-entry-scripts.md) でインクルードする必要があります。
エクステンションクラスのオートロード <a name="autoloading-extension-classes"></a>
-----------------------------
Yii のオートローダーは、 [エクステンション](structure-extensions.md) クラスのオートロードが可能です。唯一の要件は、
エクステンションがその `composer.json` ファイルに正しく `autoload` セクションを指定していることです。
`autoload` 指定方法の詳細については [Composer のドキュメント](https://getcomposer.org/doc/04-schema.md#autoload) 参照してください。
Yii のオートローダーを使用しない場合でも、まだ Composer のオートローダーはエクステンションクラスをオートロード可能です。
コンポーネント
==========
コンポーネントは、Yiiアプリケーションの主要な構成ブロックです。コンポーネントは [[yii\base\Component]] 、
またはその派生クラスのインスタンスです。コンポーネントが他のクラスに提供する主な機能は次の 3 つです:
* [プロパティ](concept-properties.md)
* [イベント](concept-events.md)
* [ビヘイビア](concept-behaviors.md)
個々にでも、組み合わせでも、これらの機能は Yii のクラスのカスタマイズ性と使いやすさをとても高めてくれます。たとえば、[[yii\jui\DatePicker|日付選択]] を行うユーザインターフェース·コンポーネントは、
対話型の日付選択UIを生成するとき、ビューで次のように使用することができます:
```php
use yii\jui\DatePicker;
echo DatePicker::widget([
'language' => 'ja',
'name' => 'country',
'clientOptions' => [
'dateFormat' => 'yy-mm-dd',
],
]);
```
クラスが [[yii\base\Component]] を継承しているおかげで、ウィジェットのプロパティは簡単に記述できます。
コンポーネントは非常に強力ですが、 [イベント](concept-events.md)[ビヘイビア](concept-behaviors.md) をサポートするため、
余分にメモリとCPU時間を要し、通常のオブジェクトよりも少し重くなります。
あなたのコンポーネントがこれら2つの機能を必要としない場合、[[yii\base\Component]] の代わりに、 [[yii\base\Object]] からコンポーネントクラスを派生することを検討してもよいでしょう。
そうすることで、あなたのコンポーネントは、 [プロパティ](concept-properties.md) のサポートが維持されたまま、通常のPHPオブジェクトのように効率的になります。
[[yii\base\Component]] または [[yii\base\Object]] からクラスを派生するときは、次の規約に従うことが推奨されます:
- コンストラクタをオーバーライドする場合は、コンストラクタの *最後の* パラメータとして `$config` パラメータを指定し、親のコンストラクタにこのパラメータを渡すこと。
- 自分がオーバーライドしたコンストラクタの *最後で* 、必ず親クラスのコンストラクタを呼び出すこと。
- [[yii\base\Object::init()]] メソッドをオーバーライドする場合は、自分の `init` メソッドの *最初に* 、必ず `init` の親実装を呼び出すようにすること。
例:
```php
namespace yii\components\MyClass;
use yii\base\Object;
class MyClass extends Object
{
public $prop1;
public $prop2;
public function __construct($param1, $param2, $config = [])
{
// ... 構成前の初期化
parent::__construct($config);
}
public function init()
{
parent::init();
// ... 構成後の初期化
}
}
```
このガイドラインに従うことで、あなたのコンポーネントは生成時に [コンフィグ可能](concept-configurations.md) になります。例:
```php
$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);
// とする代わりに
$component = \Yii::createObject([
'class' => MyClass::className(),
'prop1' => 3,
'prop2' => 4,
], [1, 2]);
```
> 補足: [[Yii::createObject()]] を呼び出すアプローチは複雑に見えますが、より強力です。というのも、それが [依存性注入コンテナ](concept-di-container.md) 上に実装されているからです。
[[yii\base\Object]] クラスには、次のオブジェクトライフサイクルが適用されます:
1. コンストラクタ内の事前初期化。ここでデフォルトのプロパティ値を設定することができます。
2. `$config` によるオブジェクトの構成。構成情報は、コンストラクタ内で設定されたデフォルト値を上書きすることがあります。
3. [[yii\base\Object::init()|init()]] 内の事後初期化。サニティ・チェックやプロパティの正規化を行いたいときは、このメソッドをオーバーライドします。
4. オブジェクトのメソッド呼び出し。
最初の 3 つのステップは、すべてのオブジェクトのコンストラクタ内で発生します。これは、あなたがクラスインスタンス (つまり、オブジェクト) を得たときには、
すでにそのオブジェクトが適切な、信頼性の高い状態に初期化されていることを意味します。
プロパティ
==========
PHPでは、クラスのメンバ変数は *プロパティ* とも呼ばれます。これらの変数は、クラス定義の一部で、クラスのインスタンスの状態を表すために
(すなわち、クラスのあるインスタンスを別のものと区別するために) 使用されます。現実によく、特別な方法でこのプロパティの読み書きを扱いたい
場合があります。たとえば、`label` プロパティに割り当てられる文字列が常にトリミングされるようにしたい、など。その仕事を成し遂げるために、
あなたは次のようなコードを使ってきたのではありませんか:
```php
$object->label = trim($label);
```
上記のコードの欠点は、 `label` プロパティを設定するすべてのコードで、`trim()` を呼び出す必要があるということです。もし将来的に、
`label` プロパティに、最初の文字を大文字にしなければならない、といった新たな要件が発生したら、 `label` に値を代入するすべてのコードを変更しなければなりません。コー​​ドの繰り返しはバグを誘発するので、できれば避けたいところです。
この問題を解決するために、Yii は *getter* メソッドと *setter* メソッドをベースにしたプロパティ定義をサポートする、 [[yii\base\Object]] 基底クラスを提供します。
クラスがその機能を必要とするなら、 [[yii\base\Object]] またはその子クラスを継承しましょう。
> 補足: Yiiのフレームワークのほぼすべてのコアクラスは、 [[yii\base\Object]] またはその子クラスを継承しています。
これは、コアクラスに getter または setter があれば、それをプロパティのように使用できることを意味します。
getter メソッドは、名前が `get` で始まるメソッドで、setter メソッドは、`set` で始まるメソッドです。
`get` または `set` プレフィクスの後の名前で、プロパティ名を定義します。次のコードに示すように、たとえば、`getLabel()` という getter と `setLabel()` という setter は、
`label` という名前のプロパティを定義します:
```php
namespace app\components;
use yii\base\Object;
class Foo extends Object
{
private $_label;
public function getLabel()
{
return $this->_label;
}
public function setLabel($value)
{
$this->_label = trim($value);
}
}
```
(詳しく言うと、getter および setter メソッドは、この場合には、内部的に `_label` と名付けられた private 属性を参照する `label` プロパティを作っています。)
getter と setter によって定義されたプロパティは、クラスのメンバ変数のように使用することができます。主な違いは、
それらのプロパティが読み取りアクセスされるときは、対応する getter ソッドが呼び出されることであり、プロパティに値が割り当てられるときには、
対応する setter メソッドが呼び出されるということです。例:
```php
// $label = $object->getLabel(); と同じ
$label = $object->label;
// $object->setLabel('abc'); と同じ
$object->label = 'abc';
```
setter なしの getter で定義されたプロパティは、 *読み取り専用* です。そのようなプロパティに値を代入しようとすると、
[[yii\base\InvalidCallException|InvalidCallException]] が発生します。同様に、getter なしの setter で定義されたプロパティは、
*書き込み専用* で、そのようなプロパティを読み取りしようとしても、例外が発生します。書き込み専用のプロパティを持つのは一般的ではありませんが。
getter と ​​setter で定義されたプロパティには、いくつかの特別なルールと制限があります:
* この種のプロパティでは、名前の *大文字と小文字を区別しません* 。たとえば、 `$object->label``$object->Label` は同じです。
これは、PHPのメソッド名が大文字と小文字を区別しないためです。
* この種のプロパティの名前と、クラスのメンバ変数の名前とが同じである場合、後者が優先されます。
たとえば、上記の `Foo` クラスがもしメンバ変数 `label` を持っているとすると、`$object->label = 'abc'`
という代入は *メンバ変数の* `label` に作用することになり、その行で `setLabel()` setter メソッドは呼び出されなくなります。
* これらのプロパティは可視性をサポートしていません。プロパティが public、protected、private であるかどうかで、
getter または setter メソッドの定義に違いは生じません。
* プロパティは、 *静的でない* getter および setter でしか定義できません。静的メソッドは同じようには扱われません。
このガイドの冒頭で説明した問題に戻ると、 `label` に値が代入されているあらゆる箇所で `trim()` を呼ぶのではなく、もう `setLabel()` という setter の内部だけで `trim()` を呼べば済むのです。
さらに、新しい要求でラベルの先頭を大文字にする必要が発生しても、他のいっさいのコードに触れることなく、すぐに `setLabel()` メソッドを変更することができます。一箇所の変更は、すべての `label` への代入に普遍的に作用します。
サービスロケータ
===============
サービスロケータは、アプリケーションが必要とする可能性のある各種のサービス (またはコンポーネント) を提供する方法を知っているオブジェクトです。
サービスロケータ内では、各コンポーネントは単一のインスタンスとして存在し、IDによって一意に識別されます。
あなたは、このIDを使用してサービスロケータからコンポーネントを取得できます。
Yii では、サービスロケータは単純に [[yii\di\ServiceLocator]] のインスタンス、または子クラスのインスタンスです。
Yii の中で最も一般的に使用されるサービスロケータは、 *アプリケーション* オブジェクトで、 `\Yii::$app`
を通じてアクセスできます。それが提供するサービスは、 *アプリケーションコンポーネント* と呼ばれ、それは `request`
`response``urlManager` のようなコンポーネントです。あなたはサービスロケータによって提供される機能を通じて、
簡単に、これらのコンポーネントを構成、あるいは独自の実装に置き換え、といったことができます。
アプリケーションオブジェクトの他に、各モジュールオブジェクトもまたサービスロケータです。
サービスロケータを使用する最初のステップは、コンポーネントを登録することです。コンポーネントは、 [[yii\di\ServiceLocator::set()]]
を通じて登録することができます。次のコードは、コンポーネントを登録するさまざまな方法を示しています。
```php
use yii\di\ServiceLocator;
use yii\caching\FileCache;
$locator = new ServiceLocator;
// コンポーネントの作成に使われるクラス名を使用して "cache" を登録
$locator->set('cache', 'yii\caching\ApcCache');
// コンポーネントの作成に使われる構成情報配列を使用して "db" を登録
$locator->set('db', [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=demo',
'username' => 'root',
'password' => '',
]);
// コンポーネントを構築する匿名関数を使って "search" を登録
$locator->set('search', function () {
return new app\components\SolrService;
});
// コンポーネントを使って "pageCache" を登録
$locator->set('pageCache', new FileCache);
```
いったんコンポーネントが登録されたら、次の 2 つの方法のいずれかで、その ID を使ってそれにアクセスすることができます:
```php
$cache = $locator->get('cache');
// または代わりに
$cache = $locator->cache;
```
以上のように、 [[yii\di\ServiceLocator]] はコンポーネント ID を使用したプロパティのように、コンポーネントにアクセスすることができます。
あなたが最初にコンポーネントにアクセスしたとき、 [[yii\di\ServiceLocator]] はコンポーネントの登録情報を使用してコンポーネントの新しいインスタンスを作成し、
それを返します。後でそのコンポーネントが再度アクセスされた場合、サービスロケータは同じインスタンスを返します。
[[yii\di\ServiceLocator::has()]] を使って、コンポーネント ID がすでに登録されているかをチェックできます。
無効なIDで [[yii\di\ServiceLocator::get()]] を呼び出した場合、例外がスローされます。
サービスロケータは多くの場合、 [構成情報](concept-configurations.md) で作成されるため、
[[yii\di\ServiceLocator::setComponents()|components]] という名前の書き込み可能プロパティが提供されています。
これで一度に複数のコンポーネントを設定して登録することができます。次のコードはアプリケーションを構成する構成情報配列を示しており、
"db" と "cache" と "search" コンポーネントの登録もしています:
```php
return [
// ...
'components' => [
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=demo',
'username' => 'root',
'password' => '',
],
'cache' => 'yii\caching\ApcCache',
'search' => function () {
return new app\components\SolrService;
},
],
];
```
...@@ -39,6 +39,7 @@ Japanese ...@@ -39,6 +39,7 @@ Japanese
- Nobuo Kihara 木原伸夫, [@softark](https://github.com/softark), softark@gmail.com - Nobuo Kihara 木原伸夫, [@softark](https://github.com/softark), softark@gmail.com
- Tomoki Morita, [@jamband](https://github.com/jamband), tmsongbooks215@gmail.com - Tomoki Morita, [@jamband](https://github.com/jamband), tmsongbooks215@gmail.com
- Hisateru Tanaka, [@tanakahisateru](https://github.com/tanakahisateru), tanakahisateru@gmail.com
Russian Russian
------- -------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment