问题描述
基于文件的翻译对我不起作用,因为客户需要更改文本.
File based translations don't work for me because clients need to change the texts.
所以我正在考虑实现这个接口 从数据库中获取数据并将结果缓存在 APC 缓存中.这是一个好的解决方案吗?
So I am thinking about implementing this interface to fetch data from the database and cache the results in an APC cache.Is this a good solution?
推荐答案
这可能就是您要找的:
这篇文章解释了如何在 Symfony 2 中使用数据库作为翻译存储.使用数据库提供翻译在 Symfony 2 中很容易做到,但不幸的是,实际上在 Symfony 2 网站中没有解释.
首先,我们必须为语言管理创建数据库实体.就我而言,我创建了三个实体:Language 实体包含所有可用的语言(如法语、英语、德语).
At first, we have to create database entities for language management. In my case, I’ve created three entities : the Language entity contain every available languages (like french, english, german).
第二个实体名为LanguageToken.它代表每个可用的语言标记.token 实体代表 xliff 文件的 source 标签.每个可用的可翻译文本都是一个标记.例如,我使用 home_page 作为标记,它在法语中被翻译为 Page principale,在英语中被翻译为 Home page.
The second entity is named LanguageToken. It represent every available language tokens. The token entity represent the source tag of the xliff files. Every translatable text available is a token. For example, I use home_page as a token and it’s translated as Page principale in french and as Home page in english.
最后一个实体是 LanguageTranslation 实体:它包含特定语言的标记翻译.在下面的示例中,Page principale 是语言 french 和标记 home_page 的 LanguageTranslation 实体.
The last entity is the LanguageTranslation entity : it contain the translation of a token in a specific language. In the example below, the Page principale is a LanguageTranslation entity for the language french and the token home_page.
效率很低,但是翻译被Symfony 2缓存在一个文件中,最后它只在Symfony 2第一次执行时使用了一次(除非你删除了Symfony 2的缓存文件).
It’s quite inefficient, but the translations are cached in a file by Symfony 2, finally it’s used only one time at Symfony 2 first execution (except if you delete Symfony 2’s cache files).
Language 实体的代码在这里可见:
The code of the Language entity is visible here :
/**
* @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 实体的代码在这里可见:
The code of the LanguageToken entity is visible here :
/**
* @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 实体的代码在这里可见:
And the LanguageTranslation entity’s code is visible here :
/**
* @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;
}
}
实现加载器接口
第二步是创建一个实现Symfony\Component\Translation\Loader\LoaderInterface的类.对应的类如下所示:
Implementing a LoaderInterface
The second step is to create a class implementing the Symfony\Component\Translation\Loader\LoaderInterface. The corresponding class is shown here :
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) 方法在这里可见:
The DBLoader class need to have every translations from the LanguageTranslationRepository (the translationRepository member). The getTranslations($language, $domain) method of the translationRepository object is visible here :
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 方法的所有参数都允许您自定义翻译加载器界面的工作方式.
The DBLoader class will be created by Symfony as a service, receiving an EntityManager as constructor argument. All arguments of the load method let you customize the way the translation loader interface work.
第三步是使用之前创建的类创建服务.添加到 config.yml 文件的代码在这里:
The third step is to create a service using the previously created class. The code to add to the config.yml file is here :
services:
translation.loader.db:
class: MyApp\CommonBundle\Services\DBLoader
arguments: [@doctrine.orm.entity_manager]
tags:
- { name: translation.loader, alias: db}
transation.loader 标签指示 Symfony 使用这个翻译加载器作为 db 别名.
The transation.loader tag indicate to Symfony to use this translation loader for the db alias.
最后一步是为每个翻译创建一个 app/Resources/translations/messages.xx.db 文件(xx = en, fr, de, ...).
The last step is to create an app/Resources/translations/messages.xx.db file for every translation (with xx = en, fr, de, …).
我没有找到通知 Symfony 使用 DBLoader 作为默认翻译加载器的方法.我发现的唯一快速技巧是创建一个 app/Resources/translations/messages.en.db 文件.db 扩展对应于服务声明中使用的 db 别名.为网站上可用的每种语言创建一个相应的文件,例如 messages.fr.db 法语或 messages.de.db 德语.
I didn’t found the way to notify Symfony to use DBLoader as default translation loader. The only quick hack I’ve found is to create a app/Resources/translations/messages.en.db file. The db extension correspond to the db alias used in the service declaration. A corresponding file is created for every language available on the website, like messages.fr.db for french or messages.de.db for german.
当Symfony 找到messages.xx.db 文件时,他加载translation.loader.db 来管理这个未知的扩展,然后加载DBLoader 使用数据库内容提供翻译.
When Symfony find the messages.xx.db file he load the translation.loader.db to manage this unknown extension and then the DBLoader use database content to provide translation.
我也没有找到在数据库修改时正确清理翻译缓存的方法(必须清理缓存以强制 Symfony 重新创建它).我实际使用的代码在这里可见:
I’ve also didn’t found the way to clean properly the translations cache on database modification (the cache have to be cleaned to force Symfony to recreate it). The code I actually use is visible here :
/**
* 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());
}
}
这个解决方案不是最漂亮的(如果我找到更好的解决方案,我会更新这篇文章)但它有效^^
This solution isn’t the pretiest one (I will update this post if I find better solution) but it’s working ^^
善于交际,分享!
这篇关于数据库中的翻译和 Symfony2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!