19declare(strict_types=1);
31use PHPUnit\Framework\Attributes\DataProvider;
32use PHPUnit\Framework\TestCase;
42 $this->
shipPo([
'farewell' =>
'Tschüss']);
43 $this->
seedOverlay([
'greeting' => [
'value' =>
'Hallo',
'original' =>
'Hallo']]);
44 $this->db_local_changes = [self::MODULE => [
'greeting' =>
'Hallo']];
46 $this->
manager()->insertLanguageForInstallation(
'de');
48 $this->assertArrayNotHasKey(
'greeting', $this->
lngModules()[self::MODULE]);
52 $this->assertFileDoesNotExist($this->
overlayBase() .
'.po',
'dropped from the overlay as well');
60 $this->
shipPo([
'farewell' =>
'Tschüss']);
63 'original' =>
'Hallo',
64 'local_change' =>
'2025-05-05T05:05:05Z',
66 $this->db_local_changes = [self::MODULE => [
'greeting' =>
'Servus']];
68 $this->
manager()->insertLanguageForInstallation(
'de');
70 $this->assertSame(
'Servus', $this->
lngModules()[self::MODULE][
'greeting']);
71 $this->assertSame([], $this->
droppedIdentifiers(self::MODULE),
'a real local change is never dropped');
72 $this->assertSame(
'Servus', $this->
overlayPo()->find(
null,
'greeting')->getTranslation());
81 $this->
shipPo([
'farewell' =>
'Tschüss']);
82 $this->
seedOverlay([
'farewell' => [
'value' =>
'Tschüss',
'original' =>
'Tschüss']]);
83 file_put_contents($this->
overlayBase() .
'.po',
"msgid \"kaputt\n");
84 $this->db_local_changes = [self::MODULE => [
'greeting' =>
'Hallo']];
86 $this->
manager()->insertLanguageForInstallation(
'de');
88 $this->assertArrayNotHasKey(self::MODULE, $this->
lngModules(),
'nothing of the module is written');
98 $this->
shipPo([
'farewell' =>
'Tschüss']);
99 $this->
seedOverlay([
'greeting' => [
'value' =>
'Hallo',
'original' =>
'Hallo']]);
100 $this->
writeLang(
'lang/customizing/ilias_de.lang.local', [
'pilot#:#greeting#:#Hallo']);
102 $this->
manager()->insertLanguageForInstallation(
'de');
104 $this->assertSame(
'Hallo', $this->
lngModules()[self::MODULE][
'greeting']);
105 $this->assertSame([
'value' =>
'Hallo',
'local_change' => self::NOW], $this->
lngData()[
'pilot|greeting']);
115 $this->
shipPo([
'farewell' =>
'Tschüss']);
117 'value' =>
'Angepasst',
118 'original' =>
'Hallo',
119 'local_change' =>
'2025-05-05T05:05:05Z',
121 $this->db_local_changes = [self::MODULE => [
'greeting' =>
'Angepasst']];
123 $this->
manager()->insertLanguageForInstallation(
'de');
126 $this->
lngModules()[self::MODULE][
'greeting'],
127 'sanity: kept in run 1 because L != O'
131 $this->
shipPo([
'greeting' =>
'Neu',
'farewell' =>
'Tschüss']);
132 $this->db_local_changes = [self::MODULE => [
'greeting' =>
'Hallo']];
134 $this->
manager()->insertLanguageForInstallation(
'de');
138 $this->
lngModules()[self::MODULE][
'greeting'],
139 'the overlay is the only source: the revert in lng_data is not read, its local change stays'
141 $this->assertSame(
'Angepasst', $this->
lngData()[
'pilot|greeting'][
'value']);
151 'A' => [
'value' =>
'A1',
'original' =>
'A1'],
152 'B' => [
'value' =>
'B1',
'original' =>
'B1'],
153 'C' => [
'value' =>
'C_custom',
'original' =>
'C1',
'local_change' =>
'2025-01-01T00:00:00Z'],
155 $po_before_the_gap = file_get_contents($this->
overlayBase() .
'.po');
158 $this->
writeLang(
'components/pilot/lang/ilias_de.lang', [
'A#:#A1',
'B#:#B1',
'C#:#C_custom']);
160 $this->
manager()->insertLanguageForInstallation(
'de');
163 [
'A' =>
'A1',
'B' =>
'B1',
'C' =>
'C_custom'],
165 'sanity: the .lang fallback is used while the .po is missing'
168 [
'value' =>
'C_custom',
'local_change' =>
null],
170 'the DB no longer sees C as a local change while the module is not migrated'
172 $this->assertSame($po_before_the_gap, file_get_contents($this->
overlayBase() .
'.po'),
'the overlay is untouched during the gap');
178 $this->
shipPo([
'A' =>
'A2',
'B' =>
'B1',
'C' =>
'C1']);
180 $this->
manager()->insertLanguageForInstallation(
'de');
183 [
'C' =>
'C_custom',
'A' =>
'A2',
'B' =>
'B1'],
186 $this->assertSame([
'value' =>
'A2',
'local_change' =>
null], $this->
lngData()[
'pilot|A'],
'L === preserved O -> S wins');
187 $this->assertSame([
'value' =>
'B1',
'local_change' =>
null], $this->
lngData()[
'pilot|B'],
'unchanged');
189 [
'value' =>
'C_custom',
'local_change' =>
'2025-01-01 00:00:00'],
191 'the overlay-only local change is taken over again - the known limitation above'
195 $this->assertNull($overlay->find(
null,
'A'));
196 $this->assertNull($overlay->find(
null,
'B'));
197 $this->assertSame(
'A2', $this->
effective()[
'A'][
'value']);
198 $this->assertSame(
'C_custom', $overlay->find(
null,
'C')->getTranslation());
199 $this->assertNotNull(LocalChangeComments::getLocalChange($overlay->find(
null,
'C')));
An in-memory gettext catalog (header block plus messages) and the only place of the component that re...
Reads the shipped .po of a migrated module and maintains its per-installation overlay: the ....
The module array just written to lng_modules could not be read back - almost always a wrong collation...
Write access to the language installation: installing, flushing and registering languages in the data...
testAKeyDroppedFromTheShippedPoWithACorruptOverlayLeavesItsModuleUnchanged()
A corrupt overlay must not be treated as L === O: no three-way decision is possible,...
testAKeyThatReturnsAfterBeingUnshippedKeepsTheLocalChangeOfTheOverlay()
MigratedLanguageFileSync::sync() must preserve the local change of an entry the .po currently lacks: ...
testAKeyDroppedFromTheShippedPoWithoutALocalChangeInTheOverlayIsRemoved()
A no-longer-shipped entry that is no local change in the overlay (its only source,...
testAWholeMissingShippedPoIsBridgedByThePreservedOverlayAcrossTwoUpdates()
The whole shipped .po missing and later back: meanwhile the module is not migrated and falls back to ...
testACustomizingValueForAKeyDroppedFromTheShippedPoStaysLocal()
A customizing value equal to the previously shipped one must not be dropped as an unchanged entry onc...
testAKeyDroppedFromTheShippedPoButWithARealLocalChangeIsKept()
A real local change must survive its key disappearing from the shipped .po.
Fixtures of the LanguageInstallationManager tests for modules migrated to PO/MO ("pilot").
droppedIdentifiers(string $module)
Identifiers of $module dropped via the per-module "DELETE FROM lng_data ... AND identifier IN (....
shipPo(array $entries, string $lang_key='de')
seedOverlay(array $entries)
effective()
Shipped .po plus overlay delta, as served (MigratedLanguageFileSync::loadModuleTranslations()) - the ...
manager(?\Closure $client_data_dir_resolver=null, ?\Closure $language_cache_invalidator=null, bool $with_resolver=true, bool $with_logger=true, ?LoggerInterface $logger=null)
writeLang(string $relative_file, array $lines)
static overlayMo(string $base_path)
The compiled catalog of the revision current names; a non-existing path if there is none.
A directory of language files with a path and a prefix for them.
Read-only access to the installed languages (database) and the available or valid language files (fil...