Symfony Component Advent Calendar 2023の2日目の記事です。
いろいろ翻訳、"Translation"
Translationは、翻訳を司るコンポーネントです。
インストール
composer require symfony/translation
翻訳
翻訳を行うには、元の文と翻訳文をセットにした翻訳用のリソースを読み込ませます。
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;
$translator = new Translator('ja');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [
'Hello World!' => 'こんにちは、世界!',
"This is an apple." => 'これはりんごです。',
], 'ja');
echo $translator->trans('Hello World!'); // こんにちは、世界!
Symfonyの場合
Symfonyの場合は、翻訳用のリソースをtranslations
ディレクトリに用意し設定を行うことで、自動的にリソースを読み込めます。
また、TranslatorInterface
をDIすることで、Translator
オブジェクトを自動注入できます。
framework:
default_locale: ja # デフォルトで翻訳する言語
translator:
default_path: '%kernel.project_dir%/translations' # 翻訳ファイルの置き場所
"Hello world!": "こんにちは、世界"
"This is an apple.": "これはりんごです。"
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Contracts\Translation\TranslatorInterface;
class TranslateCommand extends Command
{
public function __construct(private readonly TranslatorInterface $translator)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$message = 'Hello world!';
$translated = $this->translator->trans($message);
$io->info($translated); // こんにちは、世界!
return Command::SUCCESS;
}
}
メッセージIDの指定
元の文章ではなく、メッセージIDを指定することで翻訳文を入手することも可能です。
"Hello world!": "こんにちは、世界"
apple: "これはりんごです。" # ID指定
$translator->trans('apple'); // これはりんごです。
Twig上での利用
Twigではフィルター機能とタグで翻訳することが可能です。
{% set message = 'Helllo world!' %}
{{ message|trans }}
{% trans %}Hello world!{% endtrans %}
{* どちらも、「こんにちは、世界!」 *}
ICUメッセージフォーマット
たとえば英語だと、りんごは単数形だとapple
、複数形だとapples
です。また、男性に対してはhe
、女性に対してはshe
といったように、翻訳したい箇所で、別の単語に切り替えたい時があります。そのような場合、 ICU MessageFormatというものを利用して翻訳することができます。
num_of_apples: >-
{apples, plural,
=0 {りんごはないです。}
=1 {りんごはひとつです。}
=2 {りんごはふたつです。}
=3 {りんごはみっつです。}
=4 {りんごはよっつです。}
=5 {りんごはいつつです。}
=6 {りんごはむっつです。}
=7 {りんごはななつです。}
=8 {りんごはやっつです。}
=9 {りんごはここのつです。}
other {りんごは#個です。}
}
intivation: >-
{gender, select,
female {{name}さんに招待状をお送りしました。彼女の招待コードは{code}です}
male {{name}さんに招待状をお送りしました。彼の招待コードは{code}です}
other {{name}さんに招待状をお送りしました。{name}さんの招待コードは{code}です}
}
for ($i = 0; $i <= 10; $i ++) {
echo $translator->trans('num_of_apples', ['apples' => $i]);
}
/*
りんごはないです。
りんごはひとつです。
りんごはふたつです。
りんごはみっつです。
りんごはよっつです。
りんごはいつつです。
りんごはむっつです。
りんごはななつです。
りんごはやっつです。
りんごはここのつです。
りんごは10個です。
*/
echo $translator->trans(
'invitation',
['gender' => 'male', 'name' => 'すみだ', 'code' => '12345']
);
// すみださんに招待状をお送りしました。彼の招待コードは12345です。
外部プロバイダーの利用
現在はCrowdinなど、外部の翻訳サービスも充実しています。そんな外部サービスを利用して翻訳ファイルを生成することも可能です。現在利用できるのは、Crowdin
Loco
Lokalise
で、それぞれのプロバイダーを追加インストールします。
インストール&設定
composer require symfony/crowdin-translation-provider
framework:
translator:
providers:
crowdin:
dsn: '%env(CROWDIN_DSN)%'
locales: ['en', 'ja']
外部サービスから翻訳ファイル生成
bin/console translation:pull
外部サービスに翻訳ファイルをアップロード
bin/console translation:push
Crowdinにアップロードした結果
たとえば本番デプロイ後に外部サービスから翻訳ファイルを生成するようにしておけば、コードと翻訳ファイルを分けて扱うことが可能になります。
まとめ
今回はTranslation
を紹介しました。日本向けのサービスであればあまり活躍の機会はありませんが、グローバルなサイトを作る時には必須な機能になると思うので、要チェックなコンポーネントになります。使い方も楽ちんです。