国際化(I18N)とは、ソフトウェアアプリケーションを設計する際に、工学的変更なしにさまざまな言語や地域に適応できるようにするプロセスを指します。Webアプリケーションでは、潜在的なユーザーが世界中に存在する可能性があるため、特に重要です。Yiiは、メッセージ翻訳、ビュー翻訳、日付と数値のフォーマットをサポートする、包括的なI18N機能を提供しています。
ロケールとは、ユーザーの言語、国、ユーザーがユーザーインターフェースで表示したい特別なバリアントの設定を定義するパラメータのセットです。通常、言語IDと地域IDで構成されるIDで識別されます。
たとえば、ID `en-US` は「英語とアメリカ合衆国」のロケールを表します。
一貫性を保つために、Yiiアプリケーションで使用されるすべてのロケールIDは、ISO-639に従った2文字または3文字の小文字の言語コードである`ll`と、ISO-3166に従った2文字の国コードである`CC`の形式`ll-CC`に正規化される必要があります。ロケールの詳細については、ICUプロジェクトのドキュメントを参照してください。
Yiiでは、多くの場合、「言語」という用語を使用してロケールを指します。
Yiiアプリケーションは2種類の言語を使用します
いわゆるメッセージ翻訳サービスは、主にテキストメッセージをソース言語からターゲット言語に翻訳します。
アプリケーション言語は、「アプリケーション設定」で次のように設定できます
return [
// set target language to be Russian
'language' => 'ru-RU',
// set source language to be English
'sourceLanguage' => 'en-US',
......
];
デフォルトのソース言語はen-US
(米語)です。このデフォルト値は変更しないことをお勧めします。「英語から他の言語への翻訳」よりも「英語以外の言語から英語以外の言語への翻訳」ができる人を探す方がはるかに容易な場合が多いからです。
エンドユーザーの言語設定など、さまざまな要因に基づいてターゲット言語を動的に設定する必要があることがよくあります。アプリケーション設定で設定する代わりに、ターゲット言語を変更するには、次のステートメントを使用できます。
// change target language to Chinese
\Yii::$app->language = 'zh-CN';
ヒント:ソース言語がコードの異なる部分で異なる場合は、次のセクションで説明する、異なるメッセージソースに対してソース言語をオーバーライドできます。
メッセージ翻訳サービスは、テキストメッセージをある言語(通常はソース言語)から別の言語(通常はターゲット言語)に翻訳します。
このサービスは、元のメッセージと翻訳済みメッセージを保存するメッセージソースで、翻訳するメッセージを検索することで翻訳を行います。メッセージが見つかった場合は、対応する翻訳済みメッセージが返されます。そうでない場合は、元のメッセージが翻訳されずに返されます。
メッセージ翻訳サービスを使用するには、主に次の作業を行う必要があります。
Yii::t()メソッドは次のように使用できます。
echo \Yii::t('app', 'This is a string to translate!');
ここで、第2パラメーターは翻訳するテキストメッセージを表し、第1パラメーターはメッセージを分類するために使用されるカテゴリの名前を表します。
Yii::t()メソッドは、実際の翻訳作業を実行するために、i18n
アプリケーションコンポーネントのtranslate
メソッドを呼び出します。このコンポーネントは、次のようにアプリケーション設定で設定できます。
'components' => [
// ...
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
//'basePath' => '@app/messages',
//'sourceLanguage' => 'en-US',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
],
],
],
],
上記のコードでは、yii\i18n\PhpMessageSourceでサポートされているメッセージソースが設定されています。
*
記号を使用したカテゴリワイルドカード ¶パターンapp*
は、名前がapp
で始まるすべてのメッセージカテゴリがこのメッセージソースを使用して翻訳されることを示します。
yii\i18n\PhpMessageSourceクラスは、単純なPHP配列を使用してメッセージ翻訳を保存するPHPファイルを使用します。これらのファイルには、ソース言語
のメッセージからターゲット言語
への翻訳のマッピングが含まれています。
情報:これらのPHPファイルは、後でこの章で説明する
message
コマンドを使用して自動生成できます。
各PHPファイルは、単一のカテゴリのメッセージに対応します。デフォルトでは、ファイル名はカテゴリ名と同じである必要があります。app/messages/nl-NL/main.php
の例:
<?php
/**
* Translation map for nl-NL
*/
return [
'welcome' => 'welkom'
];
fileMapを設定して、カテゴリを異なる命名方法のPHPファイルにマッピングできます。
上記の例では、カテゴリapp/error
はPHPファイル@app/messages/ru-RU/error.php
にマッピングされています(ru-RU
がターゲット言語であると仮定します)。ただし、この設定がない場合、カテゴリは@app/messages/ru-RU/app/error.php
にマッピングされます。
メッセージをPHPファイルに保存することに加えて、次のメッセージソースを使用して、異なるストレージに翻訳済みメッセージを保存することもできます。
メッセージを翻訳するときに、いくつかのプレースホルダーを埋め込み、それらを動的なパラメーター値で置き換えることができます。特殊なプレースホルダー構文を使用して、パラメーター値をターゲット言語に合わせてフォーマットすることもできます。この小節では、メッセージをフォーマットするさまざまな方法について説明します。
翻訳するメッセージには、1つ以上のパラメーター(プレースホルダーとも呼ばれます)を埋め込むことができ、それらを指定された値で置き換えることができます。異なる値のセットを指定することで、翻訳済みメッセージを動的に変更できます。次の例では、メッセージ'Hello, {username}!'
のプレースホルダー{username}
は、それぞれ'Alexander'
と'Qiang'
に置き換えられます。
$username = 'Alexander';
// display a translated message with username being "Alexander"
echo \Yii::t('app', 'Hello, {username}!', [
'username' => $username,
]);
$username = 'Qiang';
// display a translated message with username being "Qiang"
echo \Yii::t('app', 'Hello, {username}!', [
'username' => $username,
]);
プレースホルダーを含むメッセージを翻訳する際には、プレースホルダーはそのままにしておく必要があります。これは、メッセージを翻訳するためにYii::t()
を呼び出すと、プレースホルダーが実際の値に置き換えられるためです。
単一のメッセージでは、名前付きプレースホルダーまたは位置プレースホルダーのいずれかを使用できますが、両方を使用することはできません。
前の例では、名前付きプレースホルダーの使用方法を示しています。つまり、各プレースホルダーは{name}
の形式で記述され、キーがプレースホルダー名(中括弧なし)、値が対応する値である連想配列を提供します。
位置プレースホルダーは、0から始まる整数シーケンスを名前として使用し、Yii::t()
の呼び出しにおけるそれらの位置に従って提供された値で置き換えられます。次の例では、位置プレースホルダー{0}
、{1}
、{2}
は、それぞれ$price
、$count
、$subtotal
の値で置き換えられます。
$price = 100;
$count = 2;
$subtotal = 200;
echo \Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', [$price, $count, $subtotal]);
位置パラメーターが1つの場合、配列にラップせずに値を指定できます。
echo \Yii::t('app', 'Price: {0}', $price);
ヒント:ほとんどの場合、名前付きプレースホルダーを使用する必要があります。これは、名前によって翻訳者が翻訳されるメッセージ全体をよりよく理解できるようになるためです。
メッセージのプレースホルダーに追加のフォーマットルールを指定して、パラメーター値をプレースホルダーに置き換える前に適切にフォーマットできます。次の例では、価格パラメーター値は数値として扱われ、通貨値としてフォーマットされます。
$price = 100;
echo \Yii::t('app', 'Price: {0,number,currency}', $price);
注記:パラメーターのフォーマットには、intl PHP拡張機能のインストールが必要です。
短縮形または完全形を使用して、フォーマットを含むプレースホルダーを指定できます。
short form: {name,type}
full form: {name,type,style}
注記:
{
、}
、'
、#
などの特殊文字を使用する必要がある場合は、'
で囲みます。
echo Yii::t('app', "Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}", ['count' => 3]);
完全な形式については、ICUドキュメントを参照してください。以下では、いくつかの一般的な使用方法を示します。
パラメーター値は数値として扱われます。例:
$sum = 42;
echo \Yii::t('app', 'Balance: {0,number}', $sum);
オプションのパラメータースタイルとして、integer
、currency
、またはpercent
を指定できます。
$sum = 42;
echo \Yii::t('app', 'Balance: {0,number,currency}', $sum);
数値をフォーマットするカスタムパターンを指定することもできます。例:
$sum = 42;
echo \Yii::t('app', 'Balance: {0,number,,000,000000}', $sum);
カスタムフォーマットで使用される文字は、「特殊パターン文字」セクションでICU APIリファレンスにあります。
値は常に翻訳先のロケールに従ってフォーマットされます。つまり、翻訳ロケールを変更せずに、小数点や千の区切り記号、通貨記号などを変更することはできません。これらのカスタマイズが必要な場合は、yii\i18n\Formatter::asDecimal()とyii\i18n\Formatter::asCurrency()を使用できます。
パラメーター値は日付としてフォーマットする必要があります。例:
echo \Yii::t('app', 'Today is {0,date}', time());
オプションのパラメータースタイルとして、short
、medium
、long
、またはfull
を指定できます。
echo \Yii::t('app', 'Today is {0,date,short}', time());
日付値をフォーマットするカスタムパターンを指定することもできます。
echo \Yii::t('app', 'Today is {0,date,yyyy-MM-dd}', time());
パラメーター値は時刻としてフォーマットする必要があります。例:
echo \Yii::t('app', 'It is {0,time}', time());
オプションのパラメータースタイルとして、short
、medium
、long
、またはfull
を指定できます。
echo \Yii::t('app', 'It is {0,time,short}', time());
時刻値をフォーマットするカスタムパターンを指定することもできます。
echo \Yii::t('app', 'It is {0,date,HH:mm}', time());
パラメーター値は数値として扱われ、スペルアウトとしてフォーマットする必要があります。例:
// may produce "42 is spelled as forty-two"
echo \Yii::t('app', '{n,number} is spelled as {n,spellout}', ['n' => 42]);
デフォルトでは、数値は基数としてスペルアウトされます。変更できます。
// may produce "I am forty-seventh agent"
echo \Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);
spellout,
の後と%
の前にスペースがあってはならないことに注意してください。
使用しているロケールで使用可能なオプションのリストについては、https://intl.rmcreative.ru/の「Numbering schemas, Spellout」を確認してください。
パラメーター値は数値として扱われ、序数名としてフォーマットする必要があります。例:
// may produce "You are the 42nd visitor here!"
echo \Yii::t('app', 'You are the {n,ordinal} visitor here!', ['n' => 42]);
序数は、スペイン語などの言語でより多くのフォーマット方法をサポートしています。
// may produce 471ª
echo \Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);
ordinal,
の後と%
の前にスペースがあってはならないことに注意してください。
使用しているロケールで使用可能なオプションのリストについては、https://intl.rmcreative.ru/の「Numbering schemas, Ordinal」を確認してください。
パラメーター値は秒数として扱われ、時間期間文字列としてフォーマットする必要があります。例:
// may produce "You are here for 47 sec. already!"
echo \Yii::t('app', 'You are here for {n,duration} already!', ['n' => 47]);
期間は、より多くのフォーマット方法をサポートしています。
// may produce 130:53:47
echo \Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);
duration,
の後と%
の前にスペースがあってはならないことに注意してください。
使用しているロケールで使用可能なオプションの一覧については、「番号付けスキーム、期間」をhttps://intl.rmcreative.ru/で確認してください。
異なる言語では、複数形の活用方法が異なります。Yiiは、非常に複雑なルールでもうまく機能する、異なる複数形でのメッセージ翻訳のための便利な方法を提供します。活用規則を直接扱う代わりに、特定の状況での活用語の翻訳のみを提供すれば十分です。例えば、
// When $n = 0, it may produce "There are no cats!"
// When $n = 1, it may produce "There is one cat!"
// When $n = 42, it may produce "There are 42 cats!"
echo \Yii::t('app', 'There {n,plural,=0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);
上記の複数形ルールの引数では、=
は明示的な値を意味します。そのため、=0
は正確にゼロ、=1
は正確に1を意味します。other
はその他の任意の値を表します。#
はターゲット言語に従ってフォーマットされたn
の値に置き換えられます。
複数形は、一部の言語では非常に複雑になる可能性があります。次のロシア語の例では、=1
は正確にn = 1
に一致する一方、one
は21
または101
に一致します。
Здесь {n,plural,=0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!
これらのother
、few
、many
、およびその他の特別な引数名は、言語によって異なります。特定のロケールで指定する必要がある引数については、「複数形ルール、基数」をhttps://intl.rmcreative.ru/で参照してください。または、unicode.orgのルールリファレンスを参照することもできます。
注記: 上記のロシア語の例は、アプリケーションのソース言語を
ru-RU
に設定し、ロシア語から翻訳する場合を除き、主に翻訳済みメッセージとして使用され、元のメッセージとしては使用されません。
Yii::t()
呼び出しで指定された元のメッセージの翻訳が見つからない場合、ソース言語の複数形ルールが元のメッセージに適用されます。
文字列が次のようになっている場合に備えて、offset
パラメーターがあります。
$likeCount = 2;
echo Yii::t('app', 'You {likeCount,plural,
offset: 1
=0{did not like this}
=1{liked this}
one{and one other person liked this}
other{and # others liked this}
}', [
'likeCount' => $likeCount
]);
// You and one other person liked this
selectordinal
のパラメーター型は、翻訳先のロケールの序数に関する言語ルールに基づいて文字列を選択することを目的としています。
$n = 3;
echo Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);
// For English it outputs:
// You are the 3rd visitor
// Translation
'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n,selectordinal,other{#-й}} посетитель',
// For Russian translation it outputs:
// Вы 3-й посетитель
形式は、複数形で使用されるものと非常に似ています。特定のロケールで指定する必要がある引数については、「複数形ルール、序数」をhttps://intl.rmcreative.ru/で参照してください。または、unicode.orgのルールリファレンスを参照することもできます。
select
パラメーター型を使用して、パラメーター値に基づいてフレーズを選択できます。例えば、
// It may produce "Snoopy is a dog and it loves Yii!"
echo \Yii::t('app', '{name} is a {gender} and {gender,select,female{she} male{he} other{it}} loves Yii!', [
'name' => 'Snoopy',
'gender' => 'dog',
]);
上記の式では、female
とmale
の両方が可能性のあるパラメーター値であり、other
はどちらにも一致しない値を処理します。可能性のあるパラメーター値ごとに、フレーズを指定し、波括弧で囲む必要があります。
設定されたカテゴリに一致しないカテゴリのフォールバックとして使用されるデフォルトメッセージソースを指定できます。ワイルドカードカテゴリ*
を設定することで、これを行うことができます。そのためには、アプリケーションの設定に次を追加します。
//configure i18n component
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource'
],
],
],
これで、各カテゴリを設定せずにカテゴリを使用できるようになりました。これは、Yii 1.1の動作に似ています。カテゴリのメッセージは、デフォルトの翻訳basePath
(@app/messages
)にあるファイルから読み込まれます。
echo Yii::t('not_specified_category', 'message from unspecified category');
メッセージは@app/messages/
から読み込まれます。
モジュールのメッセージを翻訳し、すべてのメッセージに単一の翻訳ファイルを使用しないようにするには、次のようにします。
<?php
namespace app\modules\users;
use Yii;
class Module extends \yii\base\Module
{
public $controllerNamespace = 'app\modules\users\controllers';
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
Yii::$app->i18n->translations['modules/users/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/modules/users/messages',
'fileMap' => [
'modules/users/validation' => 'validation.php',
'modules/users/form' => 'form.php',
...
],
];
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('modules/users/' . $category, $message, $params, $language);
}
}
上記の例では、ワイルドカードを使用して一致させ、必要なファイルごとにカテゴリをフィルタリングしています。fileMap
を使用する代わりに、カテゴリのマッピングを同じ名前のファイルにマッピングするという規則を使用することもできます。これで、Module::t('validation', 'your custom validation message')
またはModule::t('form', 'some form label')
を直接使用できます。
上記のモジュールに適用されるのと同じルールをウィジェットにも適用できます。例えば
<?php
namespace app\widgets\menu;
use yii\base\Widget;
use Yii;
class Menu extends Widget
{
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
$i18n = Yii::$app->i18n;
$i18n->translations['widgets/menu/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/widgets/menu/messages',
'fileMap' => [
'widgets/menu/messages' => 'messages.php',
],
];
}
public function run()
{
echo $this->render('index');
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('widgets/menu/' . $category, $message, $params, $language);
}
}
fileMap
を使用する代わりに、カテゴリのマッピングを同じ名前のファイルにマッピングするという規則を使用することもできます。これで、Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])
を直接使用できます。
注記: ウィジェットでは、コントローラーに適用されるのと同じルールで、i18nビューを使用することもできます。
Yiiには、検証エラーやその他の文字列に対するデフォルトの翻訳メッセージが付属しています。これらのメッセージはすべてカテゴリyii
に含まれています。アプリケーションのデフォルトのフレームワークメッセージの翻訳を修正したい場合があります。そのためには、次のようにi18n
アプリケーションコンポーネントを設定します。
'i18n' => [
'translations' => [
'yii' => [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/messages'
],
],
],
これで、調整済み翻訳を@app/messages/
に配置できます。
ソースに翻訳がなくても、Yiiは要求されたメッセージの内容を表示します。このような動作は、生のメッセージが有効な冗長テキストである場合に非常に便利です。しかし、場合によってはそれで十分ではありません。要求された翻訳がソースにない場合に、状況のカスタム処理を実行する必要がある場合があります。missingTranslationイベントyii\i18n\MessageSourceを使用することで、これを実現できます。
例えば、すべての翻訳されていないメッセージを目立つようにマークして、ページで簡単に検索できるようにしたい場合があります。まず、イベントハンドラーを設定する必要があります。これは、アプリケーション設定で行うことができます。
'components' => [
// ...
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
'on missingTranslation' => ['app\components\TranslationEventHandler', 'handleMissingTranslation']
],
],
],
],
次に、独自のイベントハンドラーを実装する必要があります。
<?php
namespace app\components;
use yii\i18n\MissingTranslationEvent;
class TranslationEventHandler
{
public static function handleMissingTranslation(MissingTranslationEvent $event)
{
$event->translatedMessage = "@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @";
}
}
yii\i18n\MissingTranslationEvent::$translatedMessageがイベントハンドラーによって設定されている場合、翻訳結果として表示されます。
注記: 各メッセージソースは、それぞれ個別に翻訳の欠落を処理します。複数のメッセージソースを使用していて、それらに同じ方法で翻訳の欠落を処理させたい場合は、各メッセージソースに該当するイベントハンドラーを割り当てる必要があります。
message
コマンドの使用 ¶翻訳は、phpファイル、.poファイル、またはデータベースに保存できます。追加のオプションについては、具体的なクラスを参照してください。
まず、設定ファイルを作成する必要があります。どこに保存するかを決定し、次のコマンドを実行します。
./yii message/config-template path/to/config.php
作成されたファイルを開き、必要に応じてパラメーターを調整します。特に次の点に注意してください。
languages
: アプリケーションを翻訳する必要がある言語を表す配列。messagePath
: メッセージファイルを保存するパス。設定で指定されたi18n
のbasePath
パラメーターと一致する必要があります。CLIを介して指定したオプションを使用して、設定ファイルを動的に生成するコマンド`./yii message/config`も使用できます。例えば、languages
とmessagePath
パラメーターを次のように設定できます。
./yii message/config --languages=de,ja --messagePath=messages path/to/config.php
使用可能なオプションの一覧を取得するには、次のコマンドを実行します。
./yii help message/config
設定ファイルの準備ができたら、次のコマンドでメッセージを抽出できます。
./yii message path/to/config.php
また、オプションを使用して、抽出のパラメーターを動的に変更することもできます。
その後、ファイル(ファイルベースの翻訳を選択した場合)がmessagePath
ディレクトリに作成されます。
個々のテキストメッセージを翻訳する代わりに、ビュースクリプト全体を翻訳したい場合があります。この目的を達成するには、ビューを翻訳し、ターゲット言語と同じ名前のサブディレクトリに保存します。例えば、ビュースクリプトviews/site/index.php
を翻訳し、ターゲット言語がru-RU
の場合、ビューを翻訳してviews/site/ru-RU/index.php
ファイルとして保存できます。これで、yii\base\View::renderFile()またはこのメソッドを呼び出すメソッド(例:yii\base\Controller::render())を使用してビューviews/site/index.php
をレンダリングすると、代わりに翻訳されたビューviews/site/ru-RU/index.php
がレンダリングされるようになります。
詳細については、「データのフォーマット」セクションを参照してください。
Yiiは、PHP intl拡張機能を使用して、yii\i18n\Formatterクラスの日付と数値のフォーマット、およびyii\i18n\MessageFormatterを使用したメッセージのフォーマットなど、I18N機能のほとんどを提供します。両方のクラスは、intl
拡張機能がインストールされていない場合のフォールバックメカニズムを提供します。ただし、フォールバック実装は英語のターゲット言語でのみ適切に機能します。そのため、I18Nが必要な場合は、intl
をインストールすることを強くお勧めします。
PHP intl拡張機能はICUライブラリに基づいており、すべての異なるロケールの日付と数値のフォーマットルールを提供します。ICUの異なるバージョンでは、日付と数値のフォーマット結果が異なる場合があります。すべての環境でWebサイトが同じ結果を生成することを保証するには、すべての環境で同じバージョンのintl
拡張機能(したがって同じバージョンのICU)をインストールすることをお勧めします。
PHPで使用されているICUのバージョンを確認するには、次のスクリプトを実行します。これにより、使用されているPHPとICUのバージョンが表示されます。
<?php
echo "PHP: " . PHP_VERSION . "\n";
echo "ICU: " . INTL_ICU_VERSION . "\n";
echo "ICU Data: " . INTL_ICU_DATA_VERSION . "\n";
ICU バージョン49以上のバージョンを使用することをお勧めします。これにより、このドキュメントで説明されているすべての機能を使用できます。たとえば、ICUバージョン49未満では、複数形ルールで#
プレースホルダーを使用できません。利用可能なICUバージョンの完全なリストについては、https://icu.unicode.org/downloadを参照してください。バージョン番号は4.8リリース後変更されています(例:ICU 4.8、ICU 49、ICU 50など)。
さらに、ICUライブラリに同梱されているタイムゾーンデータベースの情報は古くなっている可能性があります。タイムゾーンデータベースの更新の詳細については、ICUマニュアルを参照してください。出力フォーマットにはICUタイムゾーンデータベースが使用されますが、PHPで使用されるタイムゾーンデータベースも関連している場合があります。peclパッケージtimezonedb
の最新バージョンをインストールすることで、更新できます。
誤字脱字を発見した、またはこのページを改善する必要があると思われる場合は?
Githubで編集する !
コメントするにはサインアップするかログインしてください。