From 5526072dba59602d4eb11a2695fa925dc975ecde Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 15 Sep 2011 08:19:52 +0200 Subject: [PATCH] [Translation] added support for more than one fallback locale --- CHANGELOG-2.1.md | 1 + .../Component/Translation/Translator.php | 60 ++++++++++++------- .../Component/Translation/TranslatorTest.php | 49 +++++++++++++++ 3 files changed, 87 insertions(+), 23 deletions(-) diff --git a/CHANGELOG-2.1.md b/CHANGELOG-2.1.md index c148a68020e1..a3de5011b88f 100644 --- a/CHANGELOG-2.1.md +++ b/CHANGELOG-2.1.md @@ -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 diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php index 65e3c5dbb577..61702e970ad0 100644 --- a/src/Symfony/Component/Translation/Translator.php +++ b/src/Symfony/Component/Translation/Translator.php @@ -24,7 +24,7 @@ class Translator implements TranslatorInterface { protected $catalogues; protected $locale; - private $fallbackLocale; + private $fallbackLocales; private $loaders; private $resources; private $selector; @@ -44,6 +44,7 @@ public function __construct($locale, MessageSelector $selector) $this->loaders = array(); $this->resources = array(); $this->catalogues = array(); + $this->fallbackLocales = array(); } /** @@ -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); } /** @@ -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); @@ -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; } } diff --git a/tests/Symfony/Tests/Component/Translation/TranslatorTest.php b/tests/Symfony/Tests/Component/Translation/TranslatorTest.php index 5ecdc1109998..bd7c8fbdda7e 100644 --- a/tests/Symfony/Tests/Component/Translation/TranslatorTest.php +++ b/tests/Symfony/Tests/Component/Translation/TranslatorTest.php @@ -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()); @@ -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 */ @@ -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