JavaScriptを有効にしてください

【EC-CUBE4】DoctrineExtensionsでEC-CUBEのDBを多言語化する

EC-CUBE4ではフロント画面やメール文はデフォルトでyamlファイルでの多言語化が用意されており
(英語だけほかの言語が欲しいなら自前で用意)
.envのECCUBE_LOCALEの値を切り替えることで言語を切り替えることが可能です。

でも、DBの商品テーブル(dtb_product)とかは多言語対応の類が用意されていないので、
多言語で用意したEC-CUBEから1つのDBを参照しようとすると1つの言語でしか表示できません。
かといってそれぞれの言語ずつDBを用意すると受注データがとっ散らかって管理が大変です。

EC-CUBEの基盤になっているSymfonyにはDoctrineExtensionsというライブラリがあります。
参考: Github
今回はこれを使って商品名とかの多言語化を進めました。

但し書き

  • EC-CUBE4.0.3、PHP7.2、mysql8で確認
  • 外部ライブラリの導入+src/Eccube/Entity配下の修正なので、アップデートするときにさし戻ったり、障害がでたりするかもしれません。
  • 手順漏れがあるかも。
  • 正直EC-CUBE/Symfonyでまともに開発したのがこれが初めて(仕事はLaravelが中心でEC-CUBEは3系でプラグインのコード少し見たくらい)なのでもっといい方法があるかも。

インストール

手順: Github
(EC-CUBE4ってSymfony3.4ですけどこれでもできました)

composer require gedmo/doctrine-extensions

EC-CUBEでDoctrineExtensionsを使えるようにする

app/config/eccube/services.yamlのservicesの後ろにこれを追加

# Put parameters here that don't need to change on each machine where the app is deployed 
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
...(中略)...
services:
...(中略)...
Gedmo\Translatable\TranslatableListener:
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ '@annotation_reader' ] ]
- [ setDefaultLocale, [ 'ja' ] ]
- [ setTranslationFallback, [ false ] ]

app/config/eccube/packages/doctrine.yamlのormにもこれを追加

parameters: 
...(中略)...
doctrine:
dbal:
...(中略)...
orm:
...(中略)...
mappings:
translatable:
type: annotation
alias: Gedmo
prefix: Gedmo\Translatable\Entity
# make sure vendor library location is correct
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"

リクエストが来た時に.envのEC_LOCALEの言語で処理したいのでイベントリスナーを追加

<?php 
namespace Customize\EventListener;
use Gedmo\Translatable\TranslatableListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Eccube\Common\EccubeConfig;
class DoctrineExtensionListener implements EventSubscriberInterface
{
/**
* @var TranslatableListener
*/
private $translatableListener;
/**
* @var bool
*/
private $container;
/**
* @var bool
*/
private $eccubeConfig;
public function __construct(
TranslatableListener $translatableListener,
EccubeConfig $eccubeConfig
) {
$this->translatableListener = $translatableListener;
$this->eccubeConfig = $eccubeConfig;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => 'onKernelRequest'
];
}

public function onKernelRequest(GetResponseEvent $event): void
{
//.envのeccube_localeを設定する
$this->translatableListener->setTranslatableLocale($this->eccubeConfig['eccube_locale']);
$this->translatableListener->setPersistDefaultLocaleTranslation(true);
$this->translatableListener->setTranslationFallback(true);
}
}

これらを設定後下記のコマンドを実行します

bin/console eccube:generate:proxies
bin/console doctrine:schema:update --dump-sql --force
bin/console doctrine:mapping:info

これでext_translationsというテーブルができれいればOKです。

多言語設定法

各Entityの多言語化したいカラムにTranslatableアノテーションを追加します。
例えばProductだと商品名を多言語化したいわけですので

src/Eccube/Entity/Procut.php

namespace Eccube\Entity; 
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo; // 追加

if (!class_exists('\Eccube\Entity\Product')) {
/**
* Product
*
* @ORM\Table(name="dtb_product")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
* @ORM\HasLifecycleCallbacks()
* @ORM\Entity(repositoryClass="Eccube\Repository\ProductRepository")
*/
class Product extends \Eccube\Entity\AbstractEntity
{
...(中略)...
/**
* @var string
*
* @Gedmo\Translatable // <=Translatableアノテーション追加
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;

として

bin/console eccube:generate:proxies 
bin/console doctrine:schema:update --dump-sql --force
bin/console doctrine:mapping:info

を実行します。

動作検証

最初に日本語版(ECCUBE_LOCALE=ja)で商品を登録すると

mysql> select * from dtb_product order by id desc limit 1\G 
*************************** 1. row ***************************
id: 13
creator_id: 1
product_status_id: 2
name: 日本語名
note: NULL
description_list: NULL
description_detail: NULL
search_word: NULL
free_area: NULL
create_date: 2020-06-26 09:46:54
update_date: 2020-06-26 09:46:55
discriminator_type: product
1 row in set (0.00 sec)


mysql> select * from ext_translations\G
*************************** 42. row ***************************
id: 42
locale: ja
object_class: Eccube\Entity\Product
field: name
foreign_key: 13
content: 日本語名
42 rows in set (0.01 sec)

と日本語名がext_translationsに登録されているのがわかります。
フロントもちゃんと日本語名が出ていればOKです。

次に英語版(ECCUBE_LOCALE=en)で商品編集から同じ商品を開き
名前を編集します
(この設定だとフォールバックして日本語の商品名がフォームに出てるかも)

DBを見てみると

mysql> select * from dtb_product\G
*************************** 11. row ***************************
id: 13
creator_id: 1
product_status_id: 2
name: 日本語名
note: NULL
description_list: NULL
description_detail: NULL
search_word: NULL
free_area: NULL
create_date: 2020-06-26 09:46:54
update_date: 2020-06-26 09:51:17
discriminator_type: product
11 rows in set (0.00 sec)
mysql> select * from ext_translations\G
*************************** 42. row ***************************
id: 42
locale: ja
object_class: Eccube\Entity\Product
field: name
foreign_key: 13
content: 日本語名
*************************** 43. row ***************************
id: 43
locale: en
object_class: Eccube\Entity\Product
field: name
foreign_key: 13
content: English Name
43 rows in set (0.00 sec)
mysql>

ext_translationsに日本語版と英語版2つ設定されているのがわかります。

フロント側でも英語版なら英語名、日本語版なら日本語名が出ていればOKです。

追記(20200702)

TwitterやGithubのissueEC-CUBEの中の方とかから反響を頂いていてちょっとびっくりしました。
https://github.com/EC-CUBE/ec-cube/issues/4600

こちらはあくまでテーブルの項目の多言語化の方法までの紹介となります。

CSVのアップロード機能等に関しては考慮されていないのでご注意ください。