mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-15 10:43:55 -04:00
Closes https://github.com/FreshRSS/FreshRSS/issues/8508 Changes proposed in this pull request: - Use an integer for `Feed::error` everywhere (follow up to #8646) - Extract `Entry::machineReadableDate()` into function for use in HTML templates - Add `timeago()` function that converts a unix timestamp into a "4 weeks ago" string - Show the last successful feed update, and the last erroneous update How to test the feature manually: 1. Update a feed 2. Modify the feed URL in the database and set it to a non-existing URL 3. Update the feed again 4. Open the "Manage feed" and see the expanded error message: > Blast! This feed has encountered a problem. If this situation persists, please verify that it is still reachable. > Last successful update 3 hours ago, last erroneous update 1 hour ago. You can hover the relative dates to see the timestamp. * Make Feed::error an int everywhere Related: https://github.com/FreshRSS/FreshRSS/pull/8646 * Extract timestamptomachinedate() .. for later usage in the feed error time display. * Show time since when a feed has problems We add our own "timeago" function that converts a unix timestamp into a "4 weeks ago" string. Resolves: https://github.com/FreshRSS/FreshRSS/issues/8508 * Add new translation keys * i18n fr, en-US * Minor XHTML preference * Slightly shorter rewrite, also hopefully easier to read * Rewrite to allow (simple) plural I also moved some functions around for hopefully a more generic and better structure. I made some changes for the sake of speed (e.g. second-based logic instead of datetime intervals). Note: I used automatic translation as I was worried it would be too complicated to explain to translators... I proofread the few languages I have some familiarity with. * Add reference to CLDR * Slightly more compact syntax * Always show last update, fix case of unknown error date * Remove forgotten span * No need for multi-lines anymore * Fix error date thresshold * plurals forms * Extract gettext formula conversion script to cli * Simplify a bit * Escort excess parentheses to the door * Simplify * Avoid being too clever in localization * Fix German * Fix plural TODO parsing * Ignore en-US translation * make fix-all * git update-index --chmod=+x cli/compile.plurals.php * Heredoc indent PHP 7.3+ * compileAll: Continue on error * PHP strict comparisons * Light logical simplification * Cache plural_message_families * Avoid case of empty value * A bit of documentation --------- Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr> Co-authored-by: Frans de Jonge <frans@clevercast.com> Co-authored-by: Frans de Jonge <fransdejonge@gmail.com>
873 lines
25 KiB
PHP
873 lines
25 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once dirname(__DIR__, 3) . '/cli/i18n/I18nData.php';
|
|
require_once dirname(__DIR__, 3) . '/cli/i18n/I18nValue.php';
|
|
|
|
final class I18nDataTest extends \PHPUnit\Framework\TestCase {
|
|
/** @var array<string,array<string,array<string,I18nValue>>> */
|
|
private array $referenceData;
|
|
/** @var I18nValue&PHPUnit\Framework\MockObject\MockObject */
|
|
private $value;
|
|
|
|
#[\Override]
|
|
public function setUp(): void {
|
|
$this->value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$this->referenceData = [
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
public function testMoveKey(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
$value = $data->getData()['en']['file2.php']['file2.l1.l2.k2'];
|
|
self::assertTrue($data->isKnown('file2.l1.l2.k2'));
|
|
self::assertFalse($data->isKnown('file2.l1.nkl2'));
|
|
$data->moveKey('file2.l1.l2.k2', 'file2.l1.nkl2');
|
|
self::assertFalse($data->isKnown('file2.l1.l2.k2'));
|
|
self::assertTrue($data->isKnown('file2.l1.nkl2'));
|
|
}
|
|
|
|
public function testConstructWhenReferenceOnly(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
self::assertSame($this->referenceData, $data->getData());
|
|
}
|
|
|
|
public function testConstructorWhenLanguageIsMissingFile(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
self::assertEquals([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testConstructorWhenLanguageIsMissingKeys(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
self::assertEquals([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testConstructorWhenLanguageHasExtraKeys(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
'file1.l1.l2.k3' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
'file2.l1.l2.k3' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
self::assertEquals([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testConstructorKeepsLocalePluralVariants(): void {
|
|
$rawData = [
|
|
'en' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => $this->value,
|
|
'gen.interval.day.1' => $this->value,
|
|
],
|
|
],
|
|
'ru' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => $this->value,
|
|
'gen.interval.day.1' => $this->value,
|
|
'gen.interval.day.2' => $this->value,
|
|
],
|
|
],
|
|
];
|
|
|
|
$data = new I18nData($rawData);
|
|
|
|
self::assertArrayHasKey('gen.interval.day.2', $data->getLanguage('ru')['gen.php']);
|
|
self::assertArrayNotHasKey('gen.interval.day.2', $data->getReferenceLanguage()['gen.php']);
|
|
}
|
|
|
|
public function testConstructorPrefillsMissingLocalePluralVariantsFromEnglishPlural(): void {
|
|
$rawData = [
|
|
'en' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => new I18nValue('%d day ago'),
|
|
'gen.interval.day.1' => new I18nValue('%d days ago'),
|
|
'gen.interval.hour.0' => new I18nValue('%d hour ago'),
|
|
'gen.interval.hour.1' => new I18nValue('%d hours ago'),
|
|
],
|
|
],
|
|
'ru' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => new I18nValue('%d день назад'),
|
|
'gen.interval.day.1' => new I18nValue('%d дня назад'),
|
|
'gen.interval.day.2' => new I18nValue('%d дней назад'),
|
|
],
|
|
],
|
|
];
|
|
|
|
$data = new I18nData($rawData);
|
|
$ruTranslations = $data->getLanguage('ru')['gen.php'];
|
|
|
|
self::assertSame('%d hour ago', $ruTranslations['gen.interval.hour.0']->getValue());
|
|
self::assertSame('%d hours ago', $ruTranslations['gen.interval.hour.1']->getValue());
|
|
self::assertSame('%d hours ago', $ruTranslations['gen.interval.hour.2']->getValue());
|
|
self::assertTrue($ruTranslations['gen.interval.hour.0']->isTodo());
|
|
self::assertTrue($ruTranslations['gen.interval.hour.1']->isTodo());
|
|
self::assertTrue($ruTranslations['gen.interval.hour.2']->isTodo());
|
|
}
|
|
|
|
public function testConstructorMarksHigherLocalePluralVariantsAsTodoWhenEqualToEnglishPlural(): void {
|
|
$rawData = [
|
|
'en' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => new I18nValue('%d day ago'),
|
|
'gen.interval.day.1' => new I18nValue('%d days ago'),
|
|
],
|
|
],
|
|
'ru' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => new I18nValue('%d день назад'),
|
|
'gen.interval.day.1' => new I18nValue('%d дня назад'),
|
|
'gen.interval.day.2' => new I18nValue('%d days ago'),
|
|
],
|
|
],
|
|
];
|
|
|
|
$data = new I18nData($rawData);
|
|
$ruTranslations = $data->getLanguage('ru')['gen.php'];
|
|
|
|
self::assertFalse($ruTranslations['gen.interval.day.0']->isTodo());
|
|
self::assertFalse($ruTranslations['gen.interval.day.1']->isTodo());
|
|
self::assertTrue($ruTranslations['gen.interval.day.2']->isTodo());
|
|
}
|
|
|
|
public function testConstructorSkipsEnglishPluralVariantsNotUsedByOneFormLanguage(): void {
|
|
$rawData = [
|
|
'en' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => new I18nValue('%d day ago'),
|
|
'gen.interval.day.1' => new I18nValue('%d days ago'),
|
|
],
|
|
],
|
|
'id' => [
|
|
'gen.php' => [
|
|
'gen.interval.day.0' => new I18nValue('%d hari yang lalu'),
|
|
],
|
|
],
|
|
];
|
|
|
|
$data = new I18nData($rawData);
|
|
$idTranslations = $data->getLanguage('id')['gen.php'];
|
|
|
|
self::assertArrayHasKey('gen.interval.day.0', $idTranslations);
|
|
self::assertArrayNotHasKey('gen.interval.day.1', $idTranslations);
|
|
self::assertFalse($idTranslations['gen.interval.day.0']->isTodo());
|
|
}
|
|
|
|
public function testConstructorWhenValueIsIdenticalAndIsMarkedAsIgnore(): void {
|
|
$value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$value->expects(self::exactly(2))
|
|
->method('isIgnore')
|
|
->willReturn(true);
|
|
$value->expects(self::never())
|
|
->method('markAsTodo');
|
|
$value->expects(self::exactly(3))
|
|
->method('equal')
|
|
->with($value)
|
|
->willReturn(true);
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $value,
|
|
],
|
|
],
|
|
]);
|
|
$rawData['en']['file2.php']['file2.l1.l2.k1'] = $value;
|
|
new I18nData($rawData);
|
|
}
|
|
|
|
public function testConstructorWhenValueIsIdenticalAndIsNotMarkedAsIgnore(): void {
|
|
$value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$value->expects(self::exactly(2))
|
|
->method('isIgnore')
|
|
->willReturn(false);
|
|
$value->expects(self::exactly(2))
|
|
->method('markAsTodo');
|
|
$value->expects(self::exactly(2))
|
|
->method('equal')
|
|
->with($value)
|
|
->willReturn(true);
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $value,
|
|
],
|
|
],
|
|
]);
|
|
$rawData['en']['file2.php']['file2.l1.l2.k1'] = $value;
|
|
new I18nData($rawData);
|
|
}
|
|
|
|
public function testConstructorWhenValueIsDifferentAndIsMarkedAsToDo(): void {
|
|
$value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$value->expects(self::once())
|
|
->method('isTodo')
|
|
->willReturn(true);
|
|
$value->expects(self::once())
|
|
->method('markAsDirty');
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $value,
|
|
],
|
|
],
|
|
]);
|
|
new I18nData($rawData);
|
|
}
|
|
|
|
public function testConstructorWhenValueIsDifferentAndIsNotMarkedAsTodo(): void {
|
|
$value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$value->expects(self::once())
|
|
->method('isTodo')
|
|
->willReturn(false);
|
|
$value->expects(self::never())
|
|
->method('markAsDirty');
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $value,
|
|
],
|
|
],
|
|
]);
|
|
new I18nData($rawData);
|
|
}
|
|
|
|
public function testGetAvailableLanguagesWhenTheyAreSorted(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
'nl' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
self::assertSame([
|
|
'en',
|
|
'fr',
|
|
'nl',
|
|
], $data->getAvailableLanguages());
|
|
}
|
|
|
|
public function testGetAvailableLanguagesWhenTheyAreNotSorted(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'nl' => [],
|
|
'fr' => [],
|
|
'de' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
self::assertSame([
|
|
'de',
|
|
'en',
|
|
'fr',
|
|
'nl',
|
|
], $data->getAvailableLanguages());
|
|
}
|
|
|
|
public function testAddLanguageWhenLanguageExists(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected language already exists.');
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addLanguage('en');
|
|
}
|
|
|
|
public function testAddLanguageWhenNoReferenceProvided(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addLanguage('fr');
|
|
self::assertSame([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testAddLanguageWhenUnknownReferenceProvided(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addLanguage('fr', 'unknown');
|
|
self::assertSame([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testAddLanguageWhenKnownReferenceProvided(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addLanguage('fr', 'en');
|
|
self::assertSame([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testIsKnownWhenKeyExists(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
self::assertTrue($data->isKnown('file2.l1.l2.k2'));
|
|
}
|
|
|
|
public function testIsKnownWhenKeyDoesNotExist(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
self::assertFalse($data->isKnown('file2.l1.l2.k3'));
|
|
}
|
|
|
|
public function testAddKeyWhenKeyExists(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected key already exists.');
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addKey('file2.l1.l2.k1', 'value');
|
|
}
|
|
|
|
public function testAddKeyWhenParentKeyExists(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
]);
|
|
|
|
$data = new I18nData($rawData);
|
|
self::assertTrue($data->isKnown('file2.l1.l2.k1'));
|
|
self::assertFalse($data->isKnown('file2.l1.l2.k1._'));
|
|
self::assertFalse($data->isKnown('file2.l1.l2.k1.sk1'));
|
|
$data->addKey('file2.l1.l2.k1.sk1', 'value');
|
|
self::assertFalse($data->isKnown('file2.l1.l2.k1'));
|
|
self::assertTrue($data->isKnown('file2.l1.l2.k1._'));
|
|
self::assertTrue($data->isKnown('file2.l1.l2.k1.sk1'));
|
|
}
|
|
|
|
public function testAddKeyWhenKeyIsParent(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
]);
|
|
|
|
$data = new I18nData($rawData);
|
|
self::assertFalse($data->isKnown('file1.l1.l2._'));
|
|
self::assertTrue($data->isKnown('file1.l1.l2.k1'));
|
|
self::assertTrue($data->isKnown('file1.l1.l2.k2'));
|
|
$data->addKey('file1.l1.l2', 'value');
|
|
self::assertTrue($data->isKnown('file1.l1.l2._'));
|
|
self::assertTrue($data->isKnown('file1.l1.l2.k1'));
|
|
self::assertTrue($data->isKnown('file1.l1.l2.k2'));
|
|
}
|
|
|
|
public function testAddKey(): void {
|
|
$getTargetedValue = static fn(I18nData $data, string $language) => $data->getData()[$language]['file2.php']['file2.l1.l2.k3'];
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
]);
|
|
|
|
$data = new I18nData($rawData);
|
|
self::assertFalse($data->isKnown('file2.l1.l2.k3'));
|
|
$data->addKey('file2.l1.l2.k3', 'value');
|
|
self::assertTrue($data->isKnown('file2.l1.l2.k3'));
|
|
|
|
$enValue = $getTargetedValue($data, 'en');
|
|
$frValue = $getTargetedValue($data, 'fr');
|
|
self::assertInstanceOf(I18nValue::class, $enValue);
|
|
self::assertSame('value', $enValue->getValue());
|
|
self::assertTrue($enValue->isTodo());
|
|
self::assertSame($frValue, $enValue);
|
|
}
|
|
|
|
public function testAddFileWhenNotPhpFile(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected file name is not supported.');
|
|
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addFile('file2');
|
|
}
|
|
|
|
public function testAddFileWhenAlreadyExists(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected file exists already.');
|
|
|
|
$data = new I18nData($this->referenceData);
|
|
self::assertTrue($data->exists('file2.php'));
|
|
$data->addFile('file2.php');
|
|
}
|
|
|
|
public function testAddFileWhenNotExists(): void {
|
|
$data = new I18nData($this->referenceData);
|
|
self::assertFalse($data->exists('newfile.php'));
|
|
$data->addFile('newfile.php');
|
|
self::assertTrue($data->exists('newfile.php'));
|
|
}
|
|
|
|
public function testAddValueWhenLanguageDoesNotExist(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected language does not exist.');
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addValue('file2.l1.l2.k2', 'new value', 'fr');
|
|
}
|
|
|
|
public function testAddValueWhenKeyDoesNotExist(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected key does not exist for the selected language.');
|
|
$data = new I18nData($this->referenceData);
|
|
$data->addValue('unknown key', 'new value', 'en');
|
|
}
|
|
|
|
public function testAddValueWhenLanguageIsReferenceAndValueInOtherLanguageHasNotChange(): void {
|
|
$getTargetedValue = static fn(I18nData $data, string $language) => $data->getData()[$language]['file2.php']['file2.l1.l2.k2'];
|
|
|
|
$this->value->expects(self::atLeast(2))
|
|
->method('equal')
|
|
->with($this->value)
|
|
->willReturn(true);
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
$beforeEnValue = $getTargetedValue($data, 'en');
|
|
$beforeFrValue = $getTargetedValue($data, 'fr');
|
|
$data->addValue('file2.l1.l2.k2', 'new value', 'en');
|
|
$afterEnValue = $getTargetedValue($data, 'en');
|
|
$afterFrValue = $getTargetedValue($data, 'fr');
|
|
|
|
self::assertEquals($this->value, $beforeEnValue);
|
|
self::assertEquals($this->value, $beforeFrValue);
|
|
self::assertInstanceOf(I18nValue::class, $afterEnValue);
|
|
self::assertSame('new value', $afterEnValue->getValue());
|
|
self::assertInstanceOf(I18nValue::class, $afterFrValue);
|
|
self::assertSame('new value', $afterFrValue->getValue());
|
|
}
|
|
|
|
public function testAddValueWhenLanguageIsReferenceAndValueInOtherLanguageHasChange(): void {
|
|
$getTargetedValue = static fn(I18nData $data, string $language) => $data->getData()[$language]['file2.php']['file2.l1.l2.k2'];
|
|
|
|
$this->value->expects(self::any())
|
|
->method('equal')
|
|
->with($this->value)
|
|
->willReturn(true);
|
|
|
|
$value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file2.php' => [
|
|
'file2.l1.l2.k2' => $value,
|
|
]
|
|
],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
$beforeEnValue = $getTargetedValue($data, 'en');
|
|
$beforeFrValue = $getTargetedValue($data, 'fr');
|
|
$data->addValue('file2.l1.l2.k2', 'new value', 'en');
|
|
$afterEnValue = $getTargetedValue($data, 'en');
|
|
$afterFrValue = $getTargetedValue($data, 'fr');
|
|
|
|
self::assertEquals($this->value, $beforeEnValue);
|
|
self::assertEquals($value, $beforeFrValue);
|
|
self::assertInstanceOf(I18nValue::class, $afterEnValue);
|
|
self::assertSame('new value', $afterEnValue->getValue());
|
|
self::assertEquals($value, $afterFrValue);
|
|
}
|
|
|
|
public function testAddValueWhenLanguageIsNotReference(): void {
|
|
$getTargetedValue = static fn(I18nData $data, string $language) => $data->getData()[$language]['file2.php']['file2.l1.l2.k2'];
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
$beforeEnValue = $getTargetedValue($data, 'en');
|
|
$beforeFrValue = $getTargetedValue($data, 'fr');
|
|
$data->addValue('file2.l1.l2.k2', 'new value', 'fr');
|
|
$afterEnValue = $getTargetedValue($data, 'en');
|
|
$afterFrValue = $getTargetedValue($data, 'fr');
|
|
|
|
self::assertEquals($this->value, $beforeEnValue);
|
|
self::assertEquals($this->value, $beforeFrValue);
|
|
self::assertEquals($this->value, $afterEnValue);
|
|
self::assertInstanceOf(I18nValue::class, $afterFrValue);
|
|
self::assertSame('new value', $afterFrValue->getValue());
|
|
}
|
|
|
|
public function testRemoveKeyWhenKeyDoesNotExist(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected key does not exist.');
|
|
$data = new I18nData($this->referenceData);
|
|
$data->removeKey('Unknown key');
|
|
}
|
|
|
|
public function testRemoveKeyWhenKeyHasNoEmptySibling(): void {
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('The selected key does not exist.');
|
|
$data = new I18nData($this->referenceData);
|
|
$data->removeKey('file1.l1.l2');
|
|
}
|
|
|
|
public function testRemoveKeyWhenKeyIsEmptySibling(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
$data->removeKey('file2.l1.l2');
|
|
self::assertEquals([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2._' => $this->value,
|
|
'file3.l1.l2.k1' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testRemoveKeyWhenKeyIsTheOnlyChild(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
$data->removeKey('file3.l1.l2.k1');
|
|
self::assertEquals([
|
|
'en' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2' => $this->value,
|
|
],
|
|
],
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $this->value,
|
|
'file1.l1.l2.k2' => $this->value,
|
|
],
|
|
'file2.php' => [
|
|
'file2.l1.l2._' => $this->value,
|
|
'file2.l1.l2.k1' => $this->value,
|
|
'file2.l1.l2.k2' => $this->value,
|
|
],
|
|
'file3.php' => [
|
|
'file3.l1.l2' => $this->value,
|
|
],
|
|
],
|
|
], $data->getData());
|
|
}
|
|
|
|
public function testIgnore(): void {
|
|
$value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$value->expects(self::once())
|
|
->method('unmarkAsIgnore');
|
|
$value->expects(self::exactly(2))
|
|
->method('markAsIgnore');
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $value,
|
|
],
|
|
],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
$data->ignore('file1.l1.l2.k1', 'fr');
|
|
$data->ignore('file1.l1.l2.k1', 'fr', true);
|
|
$data->ignore('file1.l1.l2.k1', 'fr', false);
|
|
}
|
|
|
|
public function testIgnoreUnmodified(): void {
|
|
$value = $this->getMockBuilder(I18nValue::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$value->expects(self::once())
|
|
->method('unmarkAsIgnore');
|
|
$value->expects(self::exactly(2))
|
|
->method('markAsIgnore');
|
|
|
|
$this->value->expects(self::atLeast(2))
|
|
->method('equal')
|
|
->with($value)
|
|
->willReturn(true);
|
|
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [
|
|
'file1.php' => [
|
|
'file1.l1.l2.k1' => $value,
|
|
],
|
|
],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
$data->ignore_unmodified('fr');
|
|
$data->ignore_unmodified('fr', true);
|
|
$data->ignore_unmodified('fr', false);
|
|
}
|
|
|
|
public function testGetLanguage(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
'nl' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
self::assertSame($this->referenceData['en'], $data->getLanguage('en'));
|
|
}
|
|
|
|
public function testGetReferenceLanguage(): void {
|
|
$rawData = array_merge($this->referenceData, [
|
|
'fr' => [],
|
|
'nl' => [],
|
|
]);
|
|
$data = new I18nData($rawData);
|
|
self::assertSame($this->referenceData['en'], $data->getReferenceLanguage());
|
|
}
|
|
}
|