Skip to content

Commit 3a99872

Browse files
committed
feat: provide a way to override an existing language
Signed-off-by: Jack Cherng <[email protected]>
1 parent 8ab946c commit 3a99872

File tree

7 files changed

+112
-89
lines changed

7 files changed

+112
-89
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ $rendererOptions = [
8888
'detailLevel' => 'line',
8989
// renderer language: eng, cht, chs, jpn, ...
9090
// or an array which has the same keys with a language file
91+
// check the "Custom Language" section in the readme for more advanced usage
9192
'language' => 'eng',
9293
// show line numbers in HTML renderers
9394
'lineNumbers' => true,
@@ -598,6 +599,26 @@ If you don't need those detailed diff, consider using the `JsonText` renderer.
598599

599600
</details>
600601

602+
## Custom Language
603+
604+
### Override an Existing Language
605+
606+
If you just want to override some translations of an existing language...
607+
608+
```php
609+
$rendererOptions = [
610+
'language' => [
611+
// use English as the base language
612+
'eng',
613+
// your custom overrides
614+
[
615+
// use "Diff" as the new value of the "differences" key
616+
'differences' => 'Diff',
617+
],
618+
// maybe more overrides if you somehow need them...
619+
],
620+
]
621+
```
601622

602623
## Acknowledgment
603624

example/demo_base.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
'detailLevel' => 'line',
2929
// renderer language: eng, cht, chs, jpn, ...
3030
// or an array which has the same keys with a language file
31+
// check the "Custom Language" section in the readme for more advanced usage
3132
'language' => 'eng',
3233
// show line numbers in HTML renderers
3334
'lineNumbers' => true,

example/demo_web.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
$newString,
6060
'Inline',
6161
$diffOptions,
62-
['detailLevel' => 'line'] + $rendererOptions,
62+
['detailLevel' => 'line'] + $rendererOptions
6363
);
6464

6565
echo $inlineResult;

src/Renderer/AbstractRenderer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ abstract class AbstractRenderer implements RendererInterface
5757
'detailLevel' => 'line',
5858
// renderer language: eng, cht, chs, jpn, ...
5959
// or an array which has the same keys with a language file
60+
// check the "Custom Language" section in the readme for more advanced usage
6061
'language' => 'eng',
6162
// show line numbers in HTML renderers
6263
'lineNumbers' => true,

src/Utility/Arr.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,22 @@ public static function getPartialByIndex(array $array, int $start = 0, ?int $end
4444
// make the length non-negative
4545
return \array_slice($array, $start, \max(0, $end - $start));
4646
}
47+
48+
/**
49+
* Determines whether the array is associative.
50+
*
51+
* @param array $arr the array
52+
*
53+
* @return bool `true` if the array is associative, `false` otherwise
54+
*/
55+
public static function isAssociative($arr): bool
56+
{
57+
foreach ($arr as $key => $value) {
58+
if (\is_string($key)) {
59+
return true;
60+
}
61+
}
62+
63+
return false;
64+
}
4765
}

src/Utility/Language.php

Lines changed: 50 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -19,55 +19,52 @@ final class Language
1919
/**
2020
* The constructor.
2121
*
22-
* @param string|string[] $target the language string or translations dict
22+
* @param array<int,string|string[]>|string|string[] $target the language ID or translations dict
2323
*/
2424
public function __construct($target = 'eng')
2525
{
26-
$this->setLanguageOrTranslations($target);
26+
$this->load($target);
2727
}
2828

2929
/**
30-
* Set up this class.
30+
* Gets the language.
3131
*
32-
* @param string|string[] $target the language string or translations array
33-
*
34-
* @throws \InvalidArgumentException
32+
* @return string the language
3533
*/
36-
public function setLanguageOrTranslations($target): self
34+
public function getLanguage(): string
3735
{
38-
if (\is_string($target)) {
39-
$this->setUpWithLanguage($target);
40-
41-
return $this;
42-
}
43-
44-
if (\is_array($target)) {
45-
$this->setUpWithTranslations($target);
46-
47-
return $this;
48-
}
36+
return $this->language;
37+
}
4938

50-
throw new \InvalidArgumentException('$target must be the type of string|string[]');
39+
/**
40+
* Gets the translations.
41+
*
42+
* @return array the translations
43+
*/
44+
public function getTranslations(): array
45+
{
46+
return $this->translations;
5147
}
5248

5349
/**
54-
* Get the language.
50+
* Loads the target language.
5551
*
56-
* @return string the language
52+
* @param array<int,string|string[]>|string|string[] $target the language ID or translations dict
5753
*/
58-
public function getLanguage(): string
54+
public function load($target): void
5955
{
60-
return $this->language;
56+
$this->translations = $this->resolve($target);
57+
$this->language = \is_string($target) ? $target : '_custom_';
6158
}
6259

6360
/**
64-
* Get the translations.
61+
* Translates the text.
6562
*
66-
* @return array the translations
63+
* @param string $text the text
6764
*/
68-
public function getTranslations(): array
65+
public function translate(string $text): string
6966
{
70-
return $this->translations;
67+
return $this->translations[$text] ?? "![{$text}]";
7168
}
7269

7370
/**
@@ -81,7 +78,7 @@ public function getTranslations(): array
8178
*
8279
* @return string[]
8380
*/
84-
public static function getTranslationsByLanguage(string $language): array
81+
private static function getTranslationsByLanguage(string $language): array
8582
{
8683
$filePath = __DIR__ . "/../languages/{$language}.json";
8784
$file = new \SplFileObject($filePath, 'r');
@@ -99,39 +96,36 @@ public static function getTranslationsByLanguage(string $language): array
9996
}
10097

10198
/**
102-
* Translation the text.
99+
* Resolves the target language.
103100
*
104-
* @param string $text the text
105-
*/
106-
public function translate(string $text): string
107-
{
108-
return $this->translations[$text] ?? "![{$text}]";
109-
}
110-
111-
/**
112-
* Set up this class by language name.
101+
* @param array<int,string|string[]>|string|string[] $target the language ID or translations array
113102
*
114-
* @param string $language the language name
115-
*/
116-
private function setUpWithLanguage(string $language): self
117-
{
118-
return $this->setUpWithTranslations(
119-
self::getTranslationsByLanguage($language),
120-
$language
121-
);
122-
}
123-
124-
/**
125-
* Set up this class by translations.
103+
* @throws \InvalidArgumentException
126104
*
127-
* @param string[] $translations the translations dict
128-
* @param string $language the language name
105+
* @return string[] the resolved translations
129106
*/
130-
private function setUpWithTranslations(array $translations, string $language = '_custom_'): self
107+
private function resolve($target): array
131108
{
132-
$this->language = $language;
133-
$this->translations = \array_map('strval', $translations);
109+
if (\is_string($target)) {
110+
return self::getTranslationsByLanguage($target);
111+
}
112+
113+
if (\is_array($target)) {
114+
// $target is an associative array
115+
if (Arr::isAssociative($target)) {
116+
return $target;
117+
}
118+
119+
// $target is a list of "key-value pairs or language ID"
120+
return \array_reduce(
121+
$target,
122+
function ($carry, $translation) {
123+
return \array_merge($carry, $this->resolve($translation));
124+
},
125+
[]
126+
);
127+
}
134128

135-
return $this;
129+
throw new \InvalidArgumentException('$target is not in valid form');
136130
}
137131
}

tests/Utility/LanguageTest.php

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,46 +26,34 @@ final class LanguageTest extends TestCase
2626
*/
2727
protected function setUp(): void
2828
{
29-
$this->languageObj = new Language('eng');
29+
$this->languageObj = new Language();
3030
}
3131

3232
/**
33-
* Test the Language::setLanguageOrTranslations.
33+
* Test the Language::load.
3434
*
35-
* @covers \Jfcherng\Diff\Utility\Language::setLanguageOrTranslations
35+
* @covers \Jfcherng\Diff\Utility\Language::load
3636
*/
37-
public function testSetLanguageOrTranslations(): void
37+
public function testLoad(): void
3838
{
39-
$this->languageObj->setLanguageOrTranslations('eng');
40-
static::assertArrayHasKey(
41-
'differences',
42-
$this->languageObj->getTranslations()
43-
);
39+
$this->languageObj->load('eng');
40+
static::assertArrayHasKey('differences', $this->languageObj->getTranslations());
4441

45-
$this->languageObj->setLanguageOrTranslations(['hahaha' => '哈哈哈']);
46-
static::assertArrayHasKey(
47-
'hahaha',
48-
$this->languageObj->getTranslations()
49-
);
42+
$this->languageObj->load(['hahaha' => '哈哈哈']);
43+
static::assertArrayHasKey('hahaha', $this->languageObj->getTranslations());
5044

51-
$this->expectException(\InvalidArgumentException::class);
52-
$this->languageObj->setLanguageOrTranslations(5);
53-
}
45+
$this->languageObj->load([
46+
'eng',
47+
['hahaha_1' => '哈哈哈_1', 'hahaha_2' => '哈哈哈_2'],
48+
['hahaha_1' => '哈哈哈_999'],
49+
]);
50+
$translations = $this->languageObj->getTranslations();
51+
static::assertSame('Differences', $translations['differences']);
52+
static::assertSame('哈哈哈_999', $translations['hahaha_1']);
53+
static::assertSame('哈哈哈_2', $translations['hahaha_2']);
5454

55-
/**
56-
* Test the Language::getTranslationsByLanguage.
57-
*
58-
* @covers \Jfcherng\Diff\Utility\Language::getTranslationsByLanguage
59-
*/
60-
public function testGetTranslationsByLanguage(): void
61-
{
62-
static::assertArrayHasKey(
63-
'differences',
64-
$this->languageObj->getTranslationsByLanguage('eng')
65-
);
66-
67-
$this->expectException(\RuntimeException::class);
68-
$this->languageObj->getTranslationsByLanguage('a_non_existing_language');
55+
$this->expectException(\InvalidArgumentException::class);
56+
$this->languageObj->load(5);
6957
}
7058

7159
/**
@@ -82,7 +70,7 @@ public function testTranslate(): void
8270

8371
static::assertStringMatchesFormat(
8472
'![%s]',
85-
$this->languageObj->translate('a_non_existing_translation')
73+
$this->languageObj->translate('a_non_existing_key')
8674
);
8775
}
8876
}

0 commit comments

Comments
 (0)