クライアントがテキストを変更する必要があるため、ファイルベースの翻訳はうまくいきません。
そこで、このインターフェイスを実装してデータベースからデータを取得し、結果を APC キャッシュにキャッシュすることを考えています。これは良い解決策ですか?
クライアントがテキストを変更する必要があるため、ファイルベースの翻訳はうまくいきません。
そこで、このインターフェイスを実装してデータベースからデータを取得し、結果を APC キャッシュにキャッシュすることを考えています。これは良い解決策ですか?
これはあなたが探しているものかもしれません:
Symfony 2 でデータベースを翻訳プロバイダーとして使用する
序章
この記事では、Symfony 2 でデータベースを翻訳ストレージとして使用する方法について説明します。データベースを使用して翻訳を提供することは、Symfony 2 では非常に簡単ですが、残念ながら Symfony 2 の Web サイトでは実際には説明されていません。
言語エンティティの作成
最初に、言語管理用のデータベース エンティティを作成する必要があります。私の場合、3 つのエンティティを作成しました。Language エンティティには、使用可能なすべての言語 (フランス語、英語、ドイツ語など) が含まれています。
2 番目のエンティティの名前はLanguageTokenです。利用可能なすべての言語トークンを表します。トークンエンティティは、xliff ファイルのソース タグを表します。利用可能なすべての翻訳可能なテキストはトークンです。たとえば、home_pageをトークンとして使用すると、フランス語ではページ プリンシパル、英語ではホームページとして翻訳されます。
最後のエンティティはLanguageTranslationエンティティです。特定の言語でのトークンの翻訳が含まれています。以下の例では、Page プリンシパルは、言語frenchおよびトークンhome_pageのLanguageTranslationエンティティです。
これは非常に非効率的ですが、翻訳は Symfony 2 によってファイルにキャッシュされ、最終的には Symfony 2 の最初の実行時に一度だけ使用されます (Symfony 2 のキャッシュ ファイルを削除した場合を除く)。
Languageエンティティのコードは、次の場所に表示されます。
/** * @ORM\Entity(repositoryClass="YourApp\YourBundle\Repository\LanguageRepository") */ class Language { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue */ private $id; /** @ORM\column(type="string", length=200) */ private $locale; /** @ORM\column(type="string", length=200) */ private $name; public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getLocale() { return $this->locale; } public function setLocale($locale) { $this->locale = $locale; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } }
LanguageTokenエンティティのコードは、次の場所に表示されます。
/** * @ORM\Entity(repositoryClass="YourApp\YourBundle\Repository\LanguageTokenRepository") */ class LanguageToken { /** * @ORM\Id @ORM\Column(type="integer") * @ORM\GeneratedValue */ private $id; /** @ORM\column(type="string", length=200, unique=true) */ private $token; public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getToken() { return $this->token; } public function setToken($token) { $this->token = $token; } }
そして、LanguageTranslationエンティティのコードは次の場所に表示されます。
/** * @ORM\Entity(repositoryClass="YourApp\YourBundle\Repository\LanguageTranslationRepository") */ class LanguageTranslation { /** * @ORM\Id @ORM\Column(type="integer") * @ORM\GeneratedValue */ private $id; /** @ORM\column(type="string", length=200) */ private $catalogue; /** @ORM\column(type="text") */ private $translation; /** * @ORM\ManyToOne(targetEntity="YourApp\YourBundle\Entity\Language", fetch="EAGER") */ private $language; /** * @ORM\ManyToOne(targetEntity="YourApp\YourBundle\Entity\LanguageToken", fetch="EAGER") */ private $languageToken; public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getCatalogue() { return $this->catalogue; } public function setCatalogue($catalogue) { $this->catalogue = $catalogue; } public function getTranslation() { return $this->translation; } public function setTranslation($translation) { $this->translation = $translation; } public function getLanguage() { return $this->language; } public function setLanguage($language) { $this->language = $language; } public function getLanguageToken() { return $this->languageToken; } public function setLanguageToken($languageToken) { $this->languageToken = $languageToken; } }
LoaderInterface の実装
2 番目のステップは、 Symfony\Component\Translation\Loader\LoaderInterfaceを実装するクラスを作成することです。対応するクラスを次に示します。
class DBLoader implements LoaderInterface{ private $transaltionRepository; private $languageRepository; /** * @param EntityManager $entityManager */ public function __construct(EntityManager $entityManager){ $this->transaltionRepository = $entityManager->getRepository("AppCommonBundle:LanguageTranslation"); $this->languageRepository = $entityManager->getRepository("AppCommonBundle:Language"); } function load($resource, $locale, $domain = 'messages'){ //Load on the db for the specified local $language = $this->languageRepository->getLanguage($locale); $translations = $this->transaltionRepository->getTranslations($language, $domain); $catalogue = new MessageCatalogue($locale); /**@var $translation Frtrains\CommonbBundle\Entity\LanguageTranslation */ foreach($translations as $translation){ $catalogue->set($translation->getLanguageToken()->getToken(), $translation->getTranslation(), $domain); } return $catalogue; } }
DBLoaderクラスには、 LanguageTranslationRepository ( translationRepositoryメンバー)からのすべての翻訳が必要です。translationRepositoryオブジェクトのgetTranslations($language, $domain)メソッドは、次の場所に表示されます。
class LanguageTranslationRepository extends EntityRepository { /** * Return all translations for specified token * @param type $token * @param type $domain */ public function getTranslations($language, $catalogue = "messages"){ $query = $this->getEntityManager()->createQuery("SELECT t FROM AppCommonBundle:LanguageTranslation t WHERE t.language = :language AND t.catalogue = :catalogue"); $query->setParameter("language", $language); $query->setParameter("catalogue", $catalogue); return $query->getResult(); } ... }
DBLoaderクラスは、 Symfony によってサービスとして作成され、コンストラクター引数としてEntityManagerを受け取ります。loadメソッドのすべての引数を使用すると、翻訳ローダー インターフェイスの動作方法をカスタマイズできます。
DBLoader で Symfony サービスを作成する
3 番目のステップは、以前に作成したクラスを使用してサービスを作成することです。config.yml ファイルに追加するコードは次のとおりです。
services: translation.loader.db: class: MyApp\CommonBundle\Services\DBLoader arguments: [@doctrine.orm.entity_manager] tags: - { name: translation.loader, alias: db}
transation.loaderタグは、この翻訳ローダーをdbエイリアスに使用するように Symfony に指示します。
偽の翻訳ファイルを作成する
最後のステップは、翻訳ごとにapp/Resources/translations/messages.xx.dbファイルを作成することです (xx = en、fr、de、…)。
DBLoaderをデフォルトの翻訳ローダーとして使用するように Symfony に通知する方法が見つかりませんでした。私が見つけた唯一の簡単なハックは、app/Resources/translations/messages.en.dbファイルを作成することです。db拡張子は、サービス宣言で使用される db エイリアスに対応します。Web サイトで利用可能なすべての言語に対応するファイルが作成されます(フランス語の場合はmessages.fr.db 、ドイツ語の場合はmessages.de.dbなど) 。
Symfony がmessages.xx.dbファイルを見つけると、translation.loader.dbをロードしてこの未知の拡張子を管理し、DBLoaderがデータベース コンテンツを使用して翻訳を提供します。
また、データベースの変更時に翻訳キャッシュを適切に消去する方法も見つかりませんでした (Symfony に強制的に再作成させるには、キャッシュを消去する必要があります)。私が実際に使用するコードは次のとおりです。
/** * Remove language in every cache directories */ private function clearLanguageCache(){ $cacheDir = __DIR__ . "/../../../../app/cache"; $finder = new \Symfony\Component\Finder\Finder(); //TODO quick hack... $finder->in(array($cacheDir . "/dev/translations", $cacheDir . "/prod/translations"))->files(); foreach($finder as $file){ unlink($file->getRealpath()); } }
この解決策は最も美しいものではありません (より良い解決策が見つかったら、この投稿を更新します) が、機能しています ^^
社交的になり、共有しましょう!
Doctrine 2のTranslatable 動作拡張を見てみましょう。StofDoctrineExtensionsBundleはそれを Symfony と統合します。
PDO 接続を使用して、このローダー + リソースを確認することをお勧めします: https://gist.github.com/3315472
その後、memcache、apc、..を間に追加するなど、キャッシュ対応にするだけで済みます。その場合は、Translator 自体のファイル キャッシュを無効にすることができます。