Skip to content

Commit 301984f

Browse files
authored
Merge pull request #17 from utopia-php/feat-fallback-translations
Feat fallback locale
2 parents 2a817a2 + 8c4801c commit 301984f

File tree

2 files changed

+72
-17
lines changed

2 files changed

+72
-17
lines changed

src/Locale/Locale.php

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ class Locale
2525
*/
2626
public $default;
2727

28+
/**
29+
* Fallback locale. Used when specific or default locale is missing translation.
30+
* Should always be set to locale that includes all translations.
31+
*
32+
* @var string|null
33+
*/
34+
public $fallback = null;
35+
2836
/**
2937
* Get list of configured languages
3038
*
@@ -54,7 +62,7 @@ public static function setLanguageFromArray(string $name, array $translations):
5462
*/
5563
public static function setLanguageFromJSON(string $name, string $path): void
5664
{
57-
if (! file_exists($path)) {
65+
if (! file_exists($path) && self::$exceptions) {
5866
throw new Exception('Translation file not found.');
5967
}
6068

@@ -65,13 +73,31 @@ public static function setLanguageFromJSON(string $name, string $path): void
6573

6674
public function __construct(string $default)
6775
{
68-
if (! \array_key_exists($default, self::$language)) {
76+
if (! \array_key_exists($default, self::$language) && self::$exceptions) {
6977
throw new Exception('Locale not found');
7078
}
7179

7280
$this->default = $default;
7381
}
7482

83+
/**
84+
* Change fallback Locale
85+
*
86+
* @param $name
87+
*
88+
* @throws Exception
89+
*/
90+
public function setFallback(string $name): self
91+
{
92+
if (! \array_key_exists($name, self::$language) && self::$exceptions) {
93+
throw new Exception('Locale not found');
94+
}
95+
96+
$this->fallback = $name;
97+
98+
return $this;
99+
}
100+
75101
/**
76102
* Change Default Locale
77103
*
@@ -81,7 +107,7 @@ public function __construct(string $default)
81107
*/
82108
public function setDefault(string $name): self
83109
{
84-
if (! \array_key_exists($name, self::$language)) {
110+
if (! \array_key_exists($name, self::$language) && self::$exceptions) {
85111
throw new Exception('Locale not found');
86112
}
87113

@@ -101,17 +127,22 @@ public function setDefault(string $name): self
101127
*/
102128
public function getText(string $key, array $placeholders = [])
103129
{
104-
$default = '{{'.$key.'}}';
130+
$defaultExists = \array_key_exists($key, self::$language[$this->default]);
131+
$fallbackExists = \array_key_exists($key, self::$language[$this->fallback ?? ''] ?? []);
105132

106-
if (! \array_key_exists($key, self::$language[$this->default])) {
107-
if (self::$exceptions) {
108-
throw new Exception('Key named "'.$key.'" not found');
109-
}
133+
$translation = '{{'.$key.'}}';
110134

111-
return $default;
135+
if ($fallbackExists) {
136+
$translation = self::$language[$this->fallback ?? ''][$key];
112137
}
113138

114-
$translation = self::$language[$this->default][$key];
139+
if ($defaultExists) {
140+
$translation = self::$language[$this->default][$key];
141+
}
142+
143+
if (! $defaultExists && ! $fallbackExists && self::$exceptions) {
144+
throw new Exception('Key named "'.$key.'" not found');
145+
}
115146

116147
foreach ($placeholders as $placeholderKey => $placeholderValue) {
117148
$translation = str_replace('{{'.$placeholderKey.'}}', (string) $placeholderValue, $translation);

tests/Locale/LocaleTest.php

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ public function setUp(): void
1717
{
1818
Locale::$exceptions = false; // Disable exceptions
1919

20-
$this->assertCount(0, Locale::getLanguages());
21-
22-
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
23-
24-
$this->assertCount(1, Locale::getLanguages());
20+
// Set English
21+
Locale::setLanguageFromArray('en-US', [
22+
'hello' => 'Hello',
23+
'world' => 'World',
24+
'helloPlaceholder' => 'Hello {{name}} {{surname}}!',
25+
'numericPlaceholder' => 'We have {{usersAmount}} users registered.',
26+
'multiplePlaceholders' => 'Lets repeat: {{word}}, {{word}}, {{word}}',
27+
]);
2528

2629
Locale::setLanguageFromArray('he-IL', ['hello' => 'שלום']); // Set Hebrew
2730

28-
$this->assertCount(2, Locale::getLanguages());
29-
3031
Locale::setLanguageFromJSON('hi-IN', realpath(__DIR__.'/../hi-IN.json') ?: ''); // Set Hindi
3132

3233
$this->assertCount(3, Locale::getLanguages());
@@ -96,4 +97,27 @@ public function testTexts(): void
9697

9798
$this->fail('No exception was thrown');
9899
}
100+
101+
public function testFallback(): void
102+
{
103+
$locale = new Locale('he-IL');
104+
105+
$this->assertEquals('שלום', $locale->getText('hello'));
106+
$this->assertEquals('{{world}}', $locale->getText('world'));
107+
$this->assertEquals('{{missing}}', $locale->getText('missing'));
108+
109+
$locale->setFallback('en-US');
110+
111+
$this->assertEquals('שלום', $locale->getText('hello'));
112+
$this->assertEquals('World', $locale->getText('world'));
113+
$this->assertEquals('{{missing}}', $locale->getText('missing'));
114+
115+
Locale::$exceptions = true;
116+
try {
117+
$locale->getText('missing');
118+
$this->fail('Failed to throw exception when translation is missing');
119+
} catch (Exception $e) {
120+
$this->assertInstanceOf(Exception::class, $e);
121+
}
122+
}
99123
}

0 commit comments

Comments
 (0)