Skip to content

Commit

Permalink
[Translation] added support for more than one fallback locale
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Sep 15, 2011
1 parent 3a4d1a6 commit 5526072
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-2.1.md
Expand Up @@ -38,6 +38,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c

### Translation

* added support for more than one fallback locale
* added support for translations in ResourceBundles
* added support for extracting translation messages from templates (Twig and PHP)
* added dumpers for translation catalogs
Expand Down
60 changes: 37 additions & 23 deletions src/Symfony/Component/Translation/Translator.php
Expand Up @@ -24,7 +24,7 @@ class Translator implements TranslatorInterface
{
protected $catalogues;
protected $locale;
private $fallbackLocale;
private $fallbackLocales;
private $loaders;
private $resources;
private $selector;
Expand All @@ -44,6 +44,7 @@ public function __construct($locale, MessageSelector $selector)
$this->loaders = array();
$this->resources = array();
$this->catalogues = array();
$this->fallbackLocales = array();
}

/**
Expand Down Expand Up @@ -95,18 +96,18 @@ public function getLocale()
}

/**
* Sets the fallback locale.
* Sets the fallback locale(s).
*
* @param string $locale The fallback locale
* @param string|array $locale The fallback locale(s)
*
* @api
*/
public function setFallbackLocale($locale)
public function setFallbackLocale($locales)
{
// needed as the fallback locale is used to fill-in non-yet translated messages
// needed as the fallback locales are linked to the already loaded catalogues
$this->catalogues = array();

$this->fallbackLocale = $locale;
$this->fallbackLocales = is_array($locales) ? $locales : array($locales);
}

/**
Expand Down Expand Up @@ -142,15 +143,28 @@ public function transChoice($id, $number, array $parameters = array(), $domain =
$this->loadCatalogue($locale);
}

if (!$this->catalogues[$locale]->defines((string) $id, $domain)) {
// we will use the fallback
$locale = $this->computeFallbackLocale($locale);
$id = (string) $id;

if (!$this->catalogues[$locale]->defines($id, $domain)) {
// we will use the locale fallback
foreach ($this->computeFallbackLocales($locale) as $fallback) {
if ($this->catalogues[$fallback]->defines($id, $domain)) {
$locale = $fallback;
break;
}
}
}

return strtr($this->selector->choose($this->catalogues[$locale]->get((string) $id, $domain), (int) $number, $locale), $parameters);
return strtr($this->selector->choose($this->catalogues[$locale]->get($id, $domain), (int) $number, $locale), $parameters);
}

protected function loadCatalogue($locale)
{
$this->doLoadCatalogue($locale);
$this->optimizeCatalogue($locale);
}

private function doLoadCatalogue($locale)
{
$this->catalogues[$locale] = new MessageCatalogue($locale);

Expand All @@ -162,29 +176,29 @@ protected function loadCatalogue($locale)
$this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2]));
}
}

$this->optimizeCatalogue($locale);
}

private function optimizeCatalogue($locale)
{
if (!$fallback = $this->computeFallbackLocale($locale)) {
return;
}
$current = $this->catalogues[$locale];
foreach ($this->computeFallbackLocales($locale) as $fallback) {
if (!isset($this->catalogues[$fallback])) {
$this->doLoadCatalogue($fallback);
}

if (!isset($this->catalogues[$fallback])) {
$this->loadCatalogue($fallback);
$current->addFallbackCatalogue($this->catalogues[$fallback]);
$current = $this->catalogues[$fallback];
}

$this->catalogues[$locale]->addFallbackCatalogue($this->catalogues[$fallback]);
}

private function computeFallbackLocale($locale)
private function computeFallbackLocales($locale)
{
$locales = $this->fallbackLocales;

if (strlen($locale) > 3) {
return substr($locale, 0, -strlen(strrchr($locale, '_')));
} else {
return $this->fallbackLocale;
array_unshift($locales, substr($locale, 0, -strlen(strrchr($locale, '_'))));
}

return $locales;
}
}
49 changes: 49 additions & 0 deletions tests/Symfony/Tests/Component/Translation/TranslatorTest.php
Expand Up @@ -41,6 +41,20 @@ public function testSetFallbackLocale()
$this->assertEquals('foobar', $translator->trans('bar'));
}

public function testSetFallbackLocaleMultiple()
{
$translator = new Translator('en', new MessageSelector());
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foo (en)'), 'en');
$translator->addResource('array', array('bar' => 'bar (fr)'), 'fr');

// force catalogue loading
$translator->trans('bar');

$translator->setFallbackLocale(array('fr_FR', 'fr'));
$this->assertEquals('bar (fr)', $translator->trans('bar'));
}

public function testTransWithFallbackLocale()
{
$translator = new Translator('fr_FR', new MessageSelector());
Expand All @@ -62,6 +76,19 @@ public function testTransWithFallbackLocaleBis()
$this->assertEquals('foobar', $translator->trans('bar'));
}

public function testTransWithFallbackLocaleTer()
{
$translator = new Translator('fr_FR', new MessageSelector());
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('foo' => 'foo (en_US)'), 'en_US');
$translator->addResource('array', array('bar' => 'bar (en)'), 'en');

$translator->setFallbackLocale(array('en_US', 'en'));

$this->assertEquals('foo (en_US)', $translator->trans('foo'));
$this->assertEquals('bar (en)', $translator->trans('bar'));
}

/**
* @expectedException RuntimeException
*/
Expand Down Expand Up @@ -173,6 +200,28 @@ public function testTransChoiceFallback()

$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}

public function testTransChoiceFallbackBis()
{
$translator = new Translator('ru', new MessageSelector());
$translator->setFallbackLocale(array('en_US', 'en'));
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', array('some_message2' => 'one thing|%count% things'), 'en_US');

$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}

/**
* @expectedException \InvalidArgumentException
*/
public function testTransChoiceFallbackWithNoTranslation()
{
$translator = new Translator('ru', new MessageSelector());
$translator->setFallbackLocale('en');
$translator->addLoader('array', new ArrayLoader());

$this->assertEquals('10 things', $translator->transChoice('some_message2', 10, array('%count%' => 10)));
}
}

class String
Expand Down

0 comments on commit 5526072

Please sign in to comment.