19declare(strict_types=1);
28use PHPUnit\Framework\TestCase;
39 private const string MODULE =
'rtest';
46 protected function setUp(): void
52 if (!defined(
'ILIAS_ABSOLUTE_PATH')) {
53 define(
'ILIAS_ABSOLUTE_PATH', realpath(__DIR__ .
'/../../../../../'));
56 $this->fixture_directory = __DIR__ .
'/tmp-remarks-sync-fixtures-' . bin2hex(random_bytes(4));
57 mkdir($this->fixture_directory, 0775,
true);
58 $this->client_data_dir = sys_get_temp_dir() .
'/ilias_mlfs_remarks_test_' . bin2hex(random_bytes(4));
59 mkdir($this->client_data_dir, 0775,
true);
63 'components/ILIAS/Language/tests/ComponentTranslation/' . basename($this->fixture_directory) .
'/'
76 private function shippedPo(
string $lang_key =
'de'): string
78 return $this->fixture_directory .
'/' . self::MODULE .
'_' . $lang_key .
'.po';
93 return $this->
overlayPo()->find(
null, $identifier);
100 private function seedShipped(array $entries,
string $header =
'nplurals=2; plural=(n != 1);'): void
103 $catalog->setHeader(
'Content-Type',
'text/plain; charset=UTF-8');
104 $catalog->setHeader(
'Plural-Forms', $header);
105 foreach ($entries as $identifier => $value) {
107 if (is_array($value)) {
108 $entry->setPlural($identifier .
'_plural', $value);
110 $entry->translate($value);
112 $catalog->add($entry);
121 private function sync(array $entries, ?array $remarks =
null): void
123 MigratedLanguageFileSync::sync(
129 $this->client_data_dir,
145 $this->
sync([
'greeting' =>
'Hallo'], [
'greeting' =>
'Bitte prüfen']);
148 $this->assertNotNull($entry,
'the remark-only entry must be in the overlay .po');
149 $this->assertSame(
'', $entry->getTranslation(),
'empty msgstr - the value itself is unchanged');
150 $this->assertSame(
'Bitte prüfen', LocalChangeComments::getRemark($entry));
161 $this->
sync([
'greeting' =>
'Hallo'], [
'greeting' =>
'Bitte prüfen']);
163 $translations = MigratedLanguageFileSync::loadModuleTranslations($this->manager,
'de', self::MODULE, $this->client_data_dir);
164 $this->assertSame(
'Hallo', $translations[
'greeting'][
'value'] ??
null);
165 $this->assertFalse($translations[
'greeting'][
'local_change']);
167 $local_changes = MigratedLanguageFileSync::loadLocalChanges($this->manager,
'de', self::MODULE, $this->client_data_dir);
168 $this->assertArrayNotHasKey(
'greeting', $local_changes,
'a remark-only entry is not a local change of the value');
174 $this->
sync([
'greeting' =>
'Hallo'], [
'greeting' =>
'Bitte prüfen']);
176 $remarks = MigratedLanguageFileSync::loadRemarks($this->manager,
'de', self::MODULE, $this->client_data_dir);
178 $this->assertSame([
'greeting' =>
'Bitte prüfen'], $remarks);
185 $this->assertSame([], MigratedLanguageFileSync::loadRemarks($this->manager,
'de', self::MODULE, $this->client_data_dir));
195 $this->
sync([
'greeting' =>
'Hallo'], [
'greeting' =>
'Bitte prüfen']);
197 $this->
sync([
'greeting' =>
'Servus']);
200 $this->assertNotNull($entry);
201 $this->assertSame(
'Servus', $entry->getTranslation());
202 $this->assertSame(
'Bitte prüfen', LocalChangeComments::getRemark($entry),
'the remark must survive a remarks-less sync()');
212 $this->
sync([
'greeting' =>
'Hallo'], [
'greeting' =>
'Bitte prüfen']);
213 $this->assertNotNull($this->
overlayEntry(
'greeting'),
'precondition');
215 $this->
sync([
'greeting' =>
'Hallo'], []);
217 $this->assertFalse(is_file($this->
overlayBase() .
'.po'),
'nothing left to keep - the whole overlay is removed');
227 $this->
sync([
'greeting' =>
'Hallo'], [
'greeting' =>
'Bitte prüfen']);
229 $this->
sync([
'greeting' =>
'Servus'], [
'greeting' =>
'Bitte prüfen']);
232 $this->assertNotNull($entry);
233 $this->assertSame(
'Servus', $entry->getTranslation());
234 $this->assertSame(
'Bitte prüfen', LocalChangeComments::getRemark($entry));
244 $this->
seedShipped([
'item' => [
'Eintrag',
'Einträge']]);
247 [
'item [0]' =>
'Eintrag',
'item [1]' =>
'Einträge'],
248 [
'item' =>
'Bitte prüfen']
252 $this->assertNotNull($entry);
253 $this->assertTrue($entry->isPlural());
254 $this->assertSame([
'',
''], $entry->getPluralTranslations(),
'remark only - both forms stay empty');
255 $this->assertSame(
'Bitte prüfen', LocalChangeComments::getRemark($entry));
265 $this->
seedShipped([
'greeting' =>
'Hallo',
'farewell' =>
'Tschüss']);
266 $this->
sync([
'greeting' =>
'Hallo',
'farewell' =>
'Tschüss'], [
'greeting' =>
'Alte Bemerkung']);
268 MigratedLanguageFileSync::setRemarks(
272 [
'greeting' =>
'Neue Bemerkung',
'farewell' =>
'Auch geprüft'],
273 $this->client_data_dir
276 $remarks = MigratedLanguageFileSync::loadRemarks($this->manager,
'de', self::MODULE, $this->client_data_dir);
279 $this->assertSame([
'farewell' =>
'Auch geprüft',
'greeting' =>
'Neue Bemerkung'], $remarks);
285 $this->
sync([
'greeting' =>
'Hallo'], [
'greeting' =>
'Alte Bemerkung']);
287 MigratedLanguageFileSync::setRemarks($this->manager,
'de', self::MODULE, [
'greeting' =>
null], $this->client_data_dir);
289 $this->assertSame([], MigratedLanguageFileSync::loadRemarks($this->manager,
'de', self::MODULE, $this->client_data_dir));
299 $this->
seedShipped([
'greeting' =>
'Hallo',
'farewell' =>
'Tschüss']);
303 $previous_error_log = ini_set(
'error_log', sys_get_temp_dir() .
'/ilias_mlfs_remarks_test_error.log');
306 [
'greeting' =>
'Hallo',
'farewell' =>
'Tschüss'],
307 [
'greeting' =>
"Ung\xFCltig",
'farewell' =>
'Gültige Bemerkung']
310 ini_set(
'error_log', (
string) $previous_error_log);
313 $remarks = MigratedLanguageFileSync::loadRemarks($this->manager,
'de', self::MODULE, $this->client_data_dir);
314 $this->assertArrayNotHasKey(
'greeting', $remarks);
315 $this->assertSame(
'Gültige Bemerkung', $remarks[
'farewell'] ??
null);
328 $this->expectException(RuntimeException::class);
329 $this->expectExceptionMessageMatches(
'/would not be valid UTF-8/');
330 $this->
sync([
'greeting' =>
"Ung\xFCltig"]);
340 $catalog->setHeader(
'Plural-Forms',
'nplurals=2; plural=(n != 1);');
342 $fuzzy_singular->translate(
'Noch nicht übersetzt');
343 $fuzzy_singular->addFlag(
'fuzzy');
344 $catalog->add($fuzzy_singular);
346 $clean_singular->translate(
'Übersetzt');
347 $catalog->add($clean_singular);
349 $fuzzy_plural->setPlural(
'items', [
'Eintrag',
'Einträge']);
350 $fuzzy_plural->addFlag(
'fuzzy');
351 $catalog->add($fuzzy_plural);
354 $fuzzy = MigratedLanguageFileSync::loadShippedFuzzyIdentifiers($this->
shippedPo(), self::MODULE);
357 [
'fuzzy_one' =>
true,
'item [0]' =>
true,
'item [1]' =>
true],
366 $this->assertSame([], MigratedLanguageFileSync::loadShippedFuzzyIdentifiers($this->
shippedPo(), self::MODULE));
trait RestoresProcessState
Whatever a test does to the process (locale, LANGUAGE/LC_*, LOCPATH, the static state of the runtime ...
An in-memory gettext catalog (header block plus messages) and the only place of the component that re...
One message of a TranslationCatalog (a msgctxt/msgid/msgstr block of a .po file).
Reads the shipped .po of a migrated module and maintains its per-installation overlay: the ....
static directory(string $prefix, string $path, bool $local=false)
static readMo(string $file)
static writePo(string $file, TranslationCatalog $catalog)
static removeDirectory(string $directory)
static overlayMo(string $base_path)
The compiled catalog of the revision current names; a non-existing path if there is none.
static readPo(string $file)
static overlayBase(string $client_data_dir, LanguageFileDirectory $directory, string $lang_key)
The overlay of $directory's module for $lang_key without the .po extension.
static resetRuntime(?string $artifact_directory=null)
Forgets the static state of the runtime read path and points build and runtime at $artifact_directory...
A directory of language files with a path and a prefix for them.