Skip to content

Commit f5c9a3d

Browse files
committed
[Translation] added message cache + doctrine cache.
1 parent 33ff623 commit f5c9a3d

File tree

14 files changed

+856
-71
lines changed

14 files changed

+856
-71
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Tests\Translation;
13+
14+
use Symfony\Bridge\Doctrine\Translation\DoctrineMessageCatalogue;
15+
use Doctrine\Common\Cache\ArrayCache;
16+
17+
class DoctrineMessageCatalogueTest extends \PHPUnit_Framework_TestCase
18+
{
19+
protected function setUp()
20+
{
21+
if (!interface_exists('Doctrine\Common\Cache\Cache')) {
22+
$this->markTestSkipped('The "Doctrine Cache" is not available');
23+
}
24+
}
25+
26+
public function testGetLocale()
27+
{
28+
$catalogue = $this->getCatalogue('en');
29+
30+
$this->assertEquals('en', $catalogue->getLocale());
31+
}
32+
33+
public function testGetDomains()
34+
{
35+
$catalogue = $this->getCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
36+
37+
$this->assertEquals(array('domain1', 'domain2'), $catalogue->getDomains());
38+
}
39+
40+
public function testAll()
41+
{
42+
if (!interface_exists('Doctrine\Common\Cache\MultiGetCache')) {
43+
$this->markTestSkipped('The "Doctrine MultiGetCache" is not available');
44+
}
45+
46+
$catalogue = $this->getCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
47+
48+
$this->assertEquals(array('foo' => 'foo'), $catalogue->all('domain1'));
49+
$this->assertEquals(array(), $catalogue->all('domain88'));
50+
$this->assertEquals(array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')), $catalogue->all());
51+
}
52+
53+
public function testHas()
54+
{
55+
$catalogue = $this->getCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
56+
57+
$this->assertTrue($catalogue->has('foo', 'domain1'));
58+
$this->assertFalse($catalogue->has('bar', 'domain1'));
59+
$this->assertFalse($catalogue->has('foo', 'domain88'));
60+
}
61+
62+
public function testGetSet()
63+
{
64+
$catalogue = $this->getCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
65+
66+
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
67+
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
68+
}
69+
70+
public function testAdd()
71+
{
72+
$catalogue = $this->getCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
73+
$catalogue->add(array('foo1' => 'foo1'), 'domain1');
74+
75+
$this->assertEquals('foo', $catalogue->get('foo', 'domain1'));
76+
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
77+
78+
$catalogue->add(array('foo' => 'bar'), 'domain1');
79+
$this->assertEquals('bar', $catalogue->get('foo', 'domain1'));
80+
$this->assertEquals('foo1', $catalogue->get('foo1', 'domain1'));
81+
82+
$catalogue->add(array('foo' => 'bar'), 'domain88');
83+
$this->assertEquals('bar', $catalogue->get('foo', 'domain88'));
84+
}
85+
86+
public function testReplace()
87+
{
88+
$catalogue = $this->getCatalogue('en', array('domain1' => array('foo' => 'foo'), 'domain2' => array('bar' => 'bar')));
89+
$catalogue->replace($messages = array('foo1' => 'foo1_trans'), 'domain1');
90+
91+
$this->assertEquals($messages['foo1'], $catalogue->get('foo1', 'domain1'));
92+
}
93+
94+
private function getCatalogue($locale, $messages = array())
95+
{
96+
$catalogue = new DoctrineMessageCatalogue($locale, new ArrayCache());
97+
foreach ($messages as $domain => $domainMessages) {
98+
$catalogue->add($domainMessages, $domain);
99+
}
100+
101+
return $catalogue;
102+
}
103+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Tests\Translation;
13+
14+
use Symfony\Component\Translation\Translator;
15+
use Symfony\Component\Translation\MessageCatalogue;
16+
use Symfony\Component\Translation\MessageSelector;
17+
use Symfony\Bridge\Doctrine\Translation\DoctrineMessageCache;
18+
use Doctrine\Common\Cache\ArrayCache;
19+
20+
class TranslatorDoctrineCacheTest extends \PHPUnit_Framework_TestCase
21+
{
22+
protected function setUp()
23+
{
24+
if (!interface_exists('Doctrine\Common\Cache\Cache')) {
25+
$this->markTestSkipped('The "Doctrine Cache" is not available');
26+
}
27+
}
28+
29+
public function testTrans()
30+
{
31+
$cache = new DoctrineMessageCache(new ArrayCache());
32+
33+
// prime the cache
34+
$translator = $this->getTranslator($this->getLoader(), $cache);
35+
$translator->setLocale('fr');
36+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
37+
38+
$this->assertEquals('foo (FR)', $translator->trans('foo'));
39+
$this->assertEquals('bar (EN)', $translator->trans('bar'));
40+
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
41+
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
42+
$this->assertEquals('no translation', $translator->trans('no translation'));
43+
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
44+
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
45+
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
46+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
47+
48+
// do it another time as the cache is primed now
49+
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
50+
$translator = $this->getTranslator($loader, $cache);
51+
$translator->setLocale('fr');
52+
$translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin'));
53+
54+
$this->assertEquals('foo (FR)', $translator->trans('foo'));
55+
$this->assertEquals('bar (EN)', $translator->trans('bar'));
56+
$this->assertEquals('foobar (ES)', $translator->trans('foobar'));
57+
$this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0));
58+
$this->assertEquals('no translation', $translator->trans('no translation'));
59+
$this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo'));
60+
$this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1));
61+
$this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz'));
62+
$this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax'));
63+
}
64+
65+
public function testRefreshCacheWhenResourcesChange()
66+
{
67+
// prime the cache
68+
$cache = new DoctrineMessageCache(new ArrayCache(), true);
69+
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
70+
$loader
71+
->method('load')
72+
->will($this->returnValue($this->getCatalogue('fr', array(
73+
'foo' => 'foo A',
74+
))))
75+
;
76+
77+
$translator = new Translator('fr', new MessageSelector(), $cache);
78+
$translator->setLocale('fr');
79+
$translator->addLoader('loader', $loader);
80+
$translator->addResource('loader', 'foo', 'fr');
81+
82+
$this->assertEquals('foo A', $translator->trans('foo'));
83+
84+
// add a new resource to refresh the cache
85+
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
86+
$loader
87+
->method('load')
88+
->will($this->returnValue($this->getCatalogue('fr', array(
89+
'foo' => 'foo B',
90+
))))
91+
;
92+
93+
$translator = new Translator('fr', new MessageSelector(), $cache);
94+
$translator->setLocale('fr');
95+
$translator->addLoader('loader', $loader);
96+
$translator->addResource('loader', 'bar', 'fr');
97+
98+
$this->assertEquals('foo B', $translator->trans('foo'));
99+
}
100+
101+
protected function getLoader()
102+
{
103+
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
104+
$loader
105+
->expects($this->at(0))
106+
->method('load')
107+
->will($this->returnValue($this->getCatalogue('fr', array(
108+
'foo' => 'foo (FR)',
109+
))))
110+
;
111+
$loader
112+
->expects($this->at(1))
113+
->method('load')
114+
->will($this->returnValue($this->getCatalogue('en', array(
115+
'foo' => 'foo (EN)',
116+
'bar' => 'bar (EN)',
117+
'choice' => '{0} choice 0 (EN)|{1} choice 1 (EN)|]1,Inf] choice inf (EN)',
118+
))))
119+
;
120+
$loader
121+
->expects($this->at(2))
122+
->method('load')
123+
->will($this->returnValue($this->getCatalogue('es', array(
124+
'foobar' => 'foobar (ES)',
125+
))))
126+
;
127+
$loader
128+
->expects($this->at(3))
129+
->method('load')
130+
->will($this->returnValue($this->getCatalogue('pt-PT', array(
131+
'foobarfoo' => 'foobarfoo (PT-PT)',
132+
))))
133+
;
134+
$loader
135+
->expects($this->at(4))
136+
->method('load')
137+
->will($this->returnValue($this->getCatalogue('pt_BR', array(
138+
'other choice' => '{0} other choice 0 (PT-BR)|{1} other choice 1 (PT-BR)|]1,Inf] other choice inf (PT-BR)',
139+
))))
140+
;
141+
$loader
142+
->expects($this->at(5))
143+
->method('load')
144+
->will($this->returnValue($this->getCatalogue('fr.UTF-8', array(
145+
'foobarbaz' => 'foobarbaz (fr.UTF-8)',
146+
))))
147+
;
148+
$loader
149+
->expects($this->at(6))
150+
->method('load')
151+
->will($this->returnValue($this->getCatalogue('sr@latin', array(
152+
'foobarbax' => 'foobarbax (sr@latin)',
153+
))))
154+
;
155+
156+
return $loader;
157+
}
158+
159+
protected function getCatalogue($locale, $messages)
160+
{
161+
$catalogue = new MessageCatalogue($locale);
162+
foreach ($messages as $key => $translation) {
163+
$catalogue->set($key, $translation);
164+
}
165+
166+
return $catalogue;
167+
}
168+
169+
public function getTranslator($loader, $cache)
170+
{
171+
$translator = new Translator('fr', new MessageSelector(), $cache);
172+
173+
$translator->addLoader('loader', $loader);
174+
$translator->addResource('loader', 'foo', 'fr');
175+
$translator->addResource('loader', 'foo', 'en');
176+
$translator->addResource('loader', 'foo', 'es');
177+
$translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese
178+
$translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese
179+
$translator->addResource('loader', 'foo', 'fr.UTF-8');
180+
$translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian
181+
182+
return $translator;
183+
}
184+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Translation;
13+
14+
use Doctrine\Common\Cache\Cache;
15+
use Symfony\Component\Translation\MessageCacheInterface;
16+
use Symfony\Component\Translation\MessageCatalogueInterface;
17+
18+
/**
19+
* @author Abdellatif Ait Boudad <a.aitboudad@gmail.com>
20+
*/
21+
class DoctrineMessageCache implements MessageCacheInterface
22+
{
23+
const CACHE_RESOURCE_HASH = 'resources_hash';
24+
const CATALOGUE_FALLBACK_LOCALE = 'fallback_locale';
25+
26+
/**
27+
* @var bool
28+
*/
29+
private $debug;
30+
31+
/**
32+
* @var Cache
33+
*/
34+
private $cache;
35+
36+
/**
37+
* @param Cache $cache
38+
* @param bool $debug
39+
*/
40+
public function __construct(Cache $cache, $debug = false)
41+
{
42+
$this->debug = $debug;
43+
$this->cache = $cache;
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
public function isFresh($locale, array $options = array())
50+
{
51+
$currentResourcesHash = isset($options['resources_hash']) ? $options['resources_hash'] : '';
52+
$resourcesHash = $this->cache->fetch($this->getResourceHashKey($locale));
53+
if (false === $resourcesHash || ($this->debug && $resourcesHash !== $currentResourcesHash)) {
54+
return false;
55+
}
56+
57+
return true;
58+
}
59+
60+
/**
61+
* {@inheritdoc}
62+
*/
63+
public function load($locale)
64+
{
65+
$messages = new DoctrineMessageCatalogue($locale, $this->cache);
66+
$catalogue = $messages;
67+
while ($fallbackLocale = $this->cache->fetch($this->getFallbackLocaleKey($catalogue->getLocale()))) {
68+
$fallback = new DoctrineMessageCatalogue($fallbackLocale, $this->cache);
69+
$catalogue->addFallbackCatalogue($fallback);
70+
$catalogue = $fallback;
71+
}
72+
73+
return $messages;
74+
}
75+
76+
/**
77+
* {@inheritdoc}
78+
*/
79+
public function dump(MessageCatalogueInterface $messages, array $options = array())
80+
{
81+
$resourcesHash = isset($options['resources_hash']) ? $options['resources_hash'] : '';
82+
while ($messages) {
83+
$catalogue = new DoctrineMessageCatalogue($messages->getLocale(), $this->cache);
84+
$catalogue->addCatalogue($messages);
85+
86+
$this->cache->save($this->getResourceHashKey($messages->getLocale()), $resourcesHash);
87+
if ($fallback = $messages->getFallbackCatalogue()) {
88+
$this->cache->save($this->getFallbackLocaleKey($messages->getLocale()), $fallback->getLocale());
89+
}
90+
91+
$messages = $messages->getFallbackCatalogue();
92+
}
93+
}
94+
95+
private function getResourceHashKey($locale)
96+
{
97+
return self::CACHE_RESOURCE_HASH.'_'.$locale;
98+
}
99+
100+
private function getFallbackLocaleKey($locale)
101+
{
102+
return self::CATALOGUE_FALLBACK_LOCALE.'_'.$locale;
103+
}
104+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy