diff --git a/src/Locale/Locale.php b/src/Locale/Locale.php index fc8ecce..0e610a2 100644 --- a/src/Locale/Locale.php +++ b/src/Locale/Locale.php @@ -25,6 +25,14 @@ class Locale */ public $default; + /** + * Fallback locale. Used when specific or default locale is missing translation. + * Should always be set to locale that includes all translations. + * + * @var string|null + */ + public $fallback = null; + /** * Get list of configured languages * @@ -54,7 +62,7 @@ public static function setLanguageFromArray(string $name, array $translations): */ public static function setLanguageFromJSON(string $name, string $path): void { - if (! file_exists($path)) { + if (! file_exists($path) && self::$exceptions) { throw new Exception('Translation file not found.'); } @@ -65,13 +73,31 @@ public static function setLanguageFromJSON(string $name, string $path): void public function __construct(string $default) { - if (! \array_key_exists($default, self::$language)) { + if (! \array_key_exists($default, self::$language) && self::$exceptions) { throw new Exception('Locale not found'); } $this->default = $default; } + /** + * Change fallback Locale + * + * @param $name + * + * @throws Exception + */ + public function setFallback(string $name): self + { + if (! \array_key_exists($name, self::$language) && self::$exceptions) { + throw new Exception('Locale not found'); + } + + $this->fallback = $name; + + return $this; + } + /** * Change Default Locale * @@ -81,7 +107,7 @@ public function __construct(string $default) */ public function setDefault(string $name): self { - if (! \array_key_exists($name, self::$language)) { + if (! \array_key_exists($name, self::$language) && self::$exceptions) { throw new Exception('Locale not found'); } @@ -101,17 +127,22 @@ public function setDefault(string $name): self */ public function getText(string $key, array $placeholders = []) { - $default = '{{'.$key.'}}'; + $defaultExists = \array_key_exists($key, self::$language[$this->default]); + $fallbackExists = \array_key_exists($key, self::$language[$this->fallback ?? ''] ?? []); - if (! \array_key_exists($key, self::$language[$this->default])) { - if (self::$exceptions) { - throw new Exception('Key named "'.$key.'" not found'); - } + $translation = '{{'.$key.'}}'; - return $default; + if ($fallbackExists) { + $translation = self::$language[$this->fallback ?? ''][$key]; } - $translation = self::$language[$this->default][$key]; + if ($defaultExists) { + $translation = self::$language[$this->default][$key]; + } + + if (! $defaultExists && ! $fallbackExists && self::$exceptions) { + throw new Exception('Key named "'.$key.'" not found'); + } foreach ($placeholders as $placeholderKey => $placeholderValue) { $translation = str_replace('{{'.$placeholderKey.'}}', (string) $placeholderValue, $translation); diff --git a/tests/Locale/LocaleTest.php b/tests/Locale/LocaleTest.php index 3b0b0d2..57709f0 100755 --- a/tests/Locale/LocaleTest.php +++ b/tests/Locale/LocaleTest.php @@ -17,16 +17,17 @@ public function setUp(): void { Locale::$exceptions = false; // Disable exceptions - $this->assertCount(0, Locale::getLanguages()); - - Locale::setLanguageFromArray('en-US', ['hello' => 'Hello', 'world' => 'World', 'helloPlaceholder' => 'Hello {{name}} {{surname}}!', 'numericPlaceholder' => 'We have {{usersAmount}} users registered.', 'multiplePlaceholders' => 'Lets repeat: {{word}}, {{word}}, {{word}}']); // Set English - - $this->assertCount(1, Locale::getLanguages()); + // Set English + Locale::setLanguageFromArray('en-US', [ + 'hello' => 'Hello', + 'world' => 'World', + 'helloPlaceholder' => 'Hello {{name}} {{surname}}!', + 'numericPlaceholder' => 'We have {{usersAmount}} users registered.', + 'multiplePlaceholders' => 'Lets repeat: {{word}}, {{word}}, {{word}}', + ]); Locale::setLanguageFromArray('he-IL', ['hello' => 'שלום']); // Set Hebrew - $this->assertCount(2, Locale::getLanguages()); - Locale::setLanguageFromJSON('hi-IN', realpath(__DIR__.'/../hi-IN.json') ?: ''); // Set Hindi $this->assertCount(3, Locale::getLanguages()); @@ -96,4 +97,27 @@ public function testTexts(): void $this->fail('No exception was thrown'); } + + public function testFallback(): void + { + $locale = new Locale('he-IL'); + + $this->assertEquals('שלום', $locale->getText('hello')); + $this->assertEquals('{{world}}', $locale->getText('world')); + $this->assertEquals('{{missing}}', $locale->getText('missing')); + + $locale->setFallback('en-US'); + + $this->assertEquals('שלום', $locale->getText('hello')); + $this->assertEquals('World', $locale->getText('world')); + $this->assertEquals('{{missing}}', $locale->getText('missing')); + + Locale::$exceptions = true; + try { + $locale->getText('missing'); + $this->fail('Failed to throw exception when translation is missing'); + } catch (Exception $e) { + $this->assertInstanceOf(Exception::class, $e); + } + } }