Skip to content

Commit

Permalink
feat: provide a way to override an existing language
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Cherng <jfcherng@gmail.com>
  • Loading branch information
jfcherng committed Jan 18, 2023
1 parent 775a043 commit bfb89b6
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 88 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ $rendererOptions = [
'detailLevel' => 'line',
// renderer language: eng, cht, chs, jpn, ...
// or an array which has the same keys with a language file
// check the "Custom Language" section in the readme for more advanced usage
'language' => 'eng',
// show line numbers in HTML renderers
'lineNumbers' => true,
Expand Down Expand Up @@ -598,6 +599,26 @@ If you don't need those detailed diff, consider using the `JsonText` renderer.

</details>

## Custom Language

### Override an Existing Language

If you just want to override some translations of an existing language...

```php
$rendererOptions = [
'language' => [
// use English as the base language
'eng',
// your custom overrides
[
// use "Diff" as the new value of the "differences" key
'differences' => 'Diff',
],
// maybe more overrides if you somehow need them...
],
]
```

## Acknowledgment

Expand Down
1 change: 1 addition & 0 deletions example/demo_base.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'detailLevel' => 'line',
// renderer language: eng, cht, chs, jpn, ...
// or an array which has the same keys with a language file
// check the "Custom Language" section in the readme for more advanced usage
'language' => 'eng',
// show line numbers in HTML renderers
'lineNumbers' => true,
Expand Down
1 change: 1 addition & 0 deletions src/Renderer/AbstractRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ abstract class AbstractRenderer implements RendererInterface
'detailLevel' => 'line',
// renderer language: eng, cht, chs, jpn, ...
// or an array which has the same keys with a language file
// check the "Custom Language" section in the readme for more advanced usage
'language' => 'eng',
// show line numbers in HTML renderers
'lineNumbers' => true,
Expand Down
18 changes: 18 additions & 0 deletions src/Utility/Arr.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,22 @@ public static function getPartialByIndex(array $array, int $start = 0, ?int $end
// make the length non-negative
return \array_slice($array, $start, max(0, $end - $start));
}

/**
* Determines whether the array is associative.
*
* @param array $arr the array
*
* @return bool `true` if the array is associative, `false` otherwise
*/
public static function isAssociative($arr): bool
{
foreach ($arr as $key => $value) {
if (\is_string($key)) {
return true;
}
}

return false;
}
}
104 changes: 48 additions & 56 deletions src/Utility/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,52 @@ final class Language
/**
* The constructor.
*
* @param string|string[] $target the language string or translations dict
* @param array<int,string|string[]>|string|string[] $target the language ID or translations dict
*/
public function __construct($target = 'eng')
{
$this->setLanguageOrTranslations($target);
$this->load($target);
}

/**
* Set up this class.
* Gets the language.
*
* @param string|string[] $target the language string or translations array
*
* @throws \InvalidArgumentException
* @return string the language
*/
public function setLanguageOrTranslations($target): self
public function getLanguage(): string
{
if (\is_string($target)) {
$this->setUpWithLanguage($target);

return $this;
}

if (\is_array($target)) {
$this->setUpWithTranslations($target);

return $this;
}
return $this->language;
}

throw new \InvalidArgumentException('$target must be the type of string|string[]');
/**
* Gets the translations.
*
* @return array the translations
*/
public function getTranslations(): array
{
return $this->translations;
}

/**
* Get the language.
* Loads the target language.
*
* @return string the language
* @param array<int,string|string[]>|string|string[] $target the language ID or translations dict
*/
public function getLanguage(): string
public function load($target): void
{
return $this->language;
$this->translations = $this->resolve($target);
$this->language = \is_string($target) ? $target : '_custom_';
}

/**
* Get the translations.
* Translates the text.
*
* @return array the translations
* @param string $text the text
*/
public function getTranslations(): array
public function translate(string $text): string
{
return $this->translations;
return $this->translations[$text] ?? "![{$text}]";
}

/**
Expand All @@ -81,7 +78,7 @@ public function getTranslations(): array
*
* @return string[]
*/
public static function getTranslationsByLanguage(string $language): array
private static function getTranslationsByLanguage(string $language): array
{
$filePath = __DIR__ . "/../languages/{$language}.json";
$file = new \SplFileObject($filePath, 'r');
Expand All @@ -97,39 +94,34 @@ public static function getTranslationsByLanguage(string $language): array
}

/**
* Translation the text.
* Resolves the target language.
*
* @param string $text the text
*/
public function translate(string $text): string
{
return $this->translations[$text] ?? "![{$text}]";
}

/**
* Set up this class by language name.
* @param array<int,string|string[]>|string|string[] $target the language ID or translations array
*
* @param string $language the language name
*/
private function setUpWithLanguage(string $language): self
{
return $this->setUpWithTranslations(
self::getTranslationsByLanguage($language),
$language,
);
}

/**
* Set up this class by translations.
* @throws \InvalidArgumentException
*
* @param string[] $translations the translations dict
* @param string $language the language name
* @return string[] the resolved translations
*/
private function setUpWithTranslations(array $translations, string $language = '_custom_'): self
private function resolve($target): array
{
$this->language = $language;
$this->translations = array_map('strval', $translations);
if (\is_string($target)) {
return self::getTranslationsByLanguage($target);
}

if (\is_array($target)) {
// $target is an associative array
if (Arr::isAssociative($target)) {
return $target;
}

// $target is a list of "key-value pairs or language ID"
return array_reduce(
$target,
fn ($carry, $translation) => array_merge($carry, $this->resolve($translation)),
[],
);
}

return $this;
throw new \InvalidArgumentException('$target is not in valid form');
}
}
52 changes: 20 additions & 32 deletions tests/Utility/LanguageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,46 +24,34 @@ final class LanguageTest extends TestCase
*/
protected function setUp(): void
{
$this->languageObj = new Language('eng');
$this->languageObj = new Language();
}

/**
* Test the Language::setLanguageOrTranslations.
* Test the Language::load.
*
* @covers \Jfcherng\Diff\Utility\Language::setLanguageOrTranslations
* @covers \Jfcherng\Diff\Utility\Language::load
*/
public function testSetLanguageOrTranslations(): void
public function testLoad(): void
{
$this->languageObj->setLanguageOrTranslations('eng');
static::assertArrayHasKey(
'differences',
$this->languageObj->getTranslations(),
);
$this->languageObj->load('eng');
static::assertArrayHasKey('differences', $this->languageObj->getTranslations());

$this->languageObj->setLanguageOrTranslations(['hahaha' => '哈哈哈']);
static::assertArrayHasKey(
'hahaha',
$this->languageObj->getTranslations(),
);
$this->languageObj->load(['hahaha' => '哈哈哈']);
static::assertArrayHasKey('hahaha', $this->languageObj->getTranslations());

$this->expectException(\InvalidArgumentException::class);
$this->languageObj->setLanguageOrTranslations(5);
}
$this->languageObj->load([
'eng',
['hahaha_1' => '哈哈哈_1', 'hahaha_2' => '哈哈哈_2'],
['hahaha_1' => '哈哈哈_999'],
]);
$translations = $this->languageObj->getTranslations();
static::assertSame('Differences', $translations['differences']);
static::assertSame('哈哈哈_999', $translations['hahaha_1']);
static::assertSame('哈哈哈_2', $translations['hahaha_2']);

/**
* Test the Language::getTranslationsByLanguage.
*
* @covers \Jfcherng\Diff\Utility\Language::getTranslationsByLanguage
*/
public function testGetTranslationsByLanguage(): void
{
static::assertArrayHasKey(
'differences',
$this->languageObj->getTranslationsByLanguage('eng'),
);

$this->expectException(\RuntimeException::class);
$this->languageObj->getTranslationsByLanguage('a_non_existing_language');
$this->expectException(\InvalidArgumentException::class);
$this->languageObj->load(5);
}

/**
Expand All @@ -80,7 +68,7 @@ public function testTranslate(): void

static::assertStringMatchesFormat(
'![%s]',
$this->languageObj->translate('a_non_existing_translation'),
$this->languageObj->translate('a_non_existing_key'),
);
}
}

0 comments on commit bfb89b6

Please sign in to comment.