ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
ILIAS\Language\Setup\LanguageInstallationManager Class Reference

Write access to the language installation: installing, flushing and registering languages in the database. More...

+ Collaboration diagram for ILIAS\Language\Setup\LanguageInstallationManager:

Public Member Functions

 __construct (private readonly \ilDBInterface $db, private readonly LanguageFileDirectoryManager $language_file_directory_manager, private readonly string $absolute_path, private readonly InstalledLanguageRepository $repository, private readonly ?\Closure $now=null, private readonly TranslationCache $translation_cache=new NullTranslationCache(), private readonly ?\Closure $client_data_dir_resolver=null, private readonly ?LoggerInterface $logger=null,)
 
 getSkippedInvalidMarkupEntries ()
 Entries of the customizing/local files that the insertLanguageFor...() calls of this instance left out because of markup TranslationMarkupPolicy does not allow. More...
 
 getSkippedDuplicateEntries ()
 Entries the insertLanguageFor...() calls left out: their identifier exists in another module or spelling (language.md rule 2). More...
 
 getSkippedUnreadableOverlays ()
 The migrated modules left out of the insertLanguageFor...() calls because their overlay cannot be read. More...
 
 getOverlaysOfUnshippedModules ()
 Modules no longer shipped for the language that still have an overlay or a taken-over record. More...
 
 getUnrecordedTakenOverModules ()
 Modules whose taken-over record could not be written: they are taken over again by the next run. More...
 
 installLanguages (array $lang_keys, array $local_keys=[])
 Installs the given languages, flushes all other known ones. More...
 
 registerInstalledLanguage (string $lang_key, array $known_languages, array $local_language_keys)
 Registers or updates the object_data row of a (re-)inserted language ("installed" vs. More...
 
 flushLanguageForInstallation (string $lang_key)
 
 flushLanguageForUninstallation (string $lang_key)
 
 insertLanguageForInstallation (string $lang_key)
 Installs or refreshes a language from every directory the LanguageFileDirectoryManager knows (including customizing/local), merged on top of the recorded local changes. More...
 
 insertLanguageForRemovingLocalChanges (string $lang_key)
 Re-seeds a language purely from the shipped global/component files (.lang, shipped .po), without customizing/local files and local changes: the write path of "remove local changes". More...
 
 insertLanguageForApplyingLocalChanges (string $lang_key)
 Applies only the customizing/local file on top of an installed language, leaving the base data untouched: the write path of "Install local" (see InstallLanguage::perform()). More...
 
 findUnwritableOverlayDirectories (array $lang_keys, ?int $for_user_id=null)
 The overlay directories of the modules migrated for $lang_keys that cannot be written, see MigratedLanguageFileSync::findUnwritableOverlayDirectories(). More...
 

Private Member Functions

 logWarning (string $message)
 
 markupPolicy ()
 
 invalidMarkupOfCustomizingValue (string $value, ?string $current, ?string $shipped)
 The violations of a customizing $value, only if it changes something (see TranslationMarkupPolicy::findInvalidChangedValues()). More...
 
 duplicatesOfCustomizingLines (array $content, string $prefix, array $existing)
 
 clientDataDir ()
 
 utcTimestamp (?int $unix_timestamp=null)
 
 removeOverlays (string $lang_key)
 The overlay counterpart of flushLanguage($lang_key, "all") for a language that is no longer installed - see ilObjLanguage::uninstall(). More...
 
 flushLanguage (string $lang_key, string $mode="all")
 
 deleteLangData (string $lang_key, bool $keep_local_change)
 
 insertLanguage (string $lang_key, iterable $directories, array $lang_array, bool $merge_shipped_migrated_modules, bool $keep_local_changes, bool $delete_unchanged_migrated_rows=false)
 Writes the data of the given $directories/$lang_array seed; the callers above decide both. More...
 
 findOverlaysOfUnshippedModules (string $lang_key, ?string $client_data_dir, ?array $shipped_modules)
 
 writeLanguage (string $lang_key, iterable $directories, array $lang_array, array $shipped_migrated_modules, array $locked_modules, bool $keep_local_changes, bool $delete_unchanged_migrated_rows, ?string $client_data_dir, array $overlay_only_modules=[], array $skipped_modules=[])
 insertLanguage() once the overlays of $locked_modules are locked. More...
 
 resolveMigratedModule (array $local_entries, array $customized, array $shipped_entries, array $overlay, \Closure $write_row, \Closure $delete_row)
 The shipped/local decision for one migrated module: a three-way comparison per shipped entry between L = the local value (. More...
 
 isReadableOverlay (string $lang_key, string $module, ?string $client_data_dir)
 
 loadOverlay (string $lang_key, string $module, ?string $client_data_dir)
 The overlay state of a migrated module, or null. More...
 
 assertModulesCorrectlySaved (string $lang_key, array $modules)
 A wrong table collation silently corrupts the serialized module arrays, which would otherwise only surface as missing translations (mantis #20046/#19140). More...
 
 invalidateLanguageCache (string $lang_key)
 
 migratedModuleRemarks (string $lang_key, array $shipped_migrated_modules, array $customized_remarks, array $shipped_line_comments, ?string $client_data_dir, array $overlay_only_modules=[])
 The remarks of every migrated module (identifier => remark; a plural message under its identifier): lng_data, overridden by the overlay, overridden by the customizing file. More...
 
 migratedLocalChangeDates (string $lang_key, array $modules)
 lng_data.local_change of every locally changed entry of $modules in $lang_key (as written by this run), module => identifier => database timestamp - for the overlay, see MigratedLanguageFileSync::sync(). More...
 
 syncMigratedModules (string $lang_key, array $lang_array, ?string $client_data_dir, array $expected_overlays=[], array $remarks=[], array $local_change_dates=[])
 

Static Private Member Functions

static databaseRowsOfMigratedModule (array $rows, array $final_entries, array $shipped_entries, array $remarks=[])
 The lng_data rows of a migrated module from the rows buffered while its content was decided: the rows of a plural message's forms (and a plain row of its identifier) become one row of the identifier with the final default value (see MigratedLanguageFileSync::collapsePluralForms() and databaseValues()), the first remark and, only if that value differs from the shipped default, the first local_change. More...
 

Private Attributes

const string SEPARATOR = "#:#"
 
const string COMMENT_SEPARATOR = "###"
 
array $skipped_invalid_markup_entries = []
 
array $skipped_duplicate_entries = []
 
array $skipped_unreadable_overlays = []
 
array $overlays_of_unshipped_modules = []
 
array $unrecorded_taken_over_modules = []
 
TranslationMarkupPolicy $markup_policy = null
 

Detailed Description

Write access to the language installation: installing, flushing and registering languages in the database.

Reading lives in InstalledLanguageRepository.

Definition at line 40 of file LanguageInstallationManager.php.

Constructor & Destructor Documentation

◆ __construct()

ILIAS\Language\Setup\LanguageInstallationManager::__construct ( private readonly \ilDBInterface  $db,
private readonly LanguageFileDirectoryManager  $language_file_directory_manager,
private readonly string  $absolute_path,
private readonly InstalledLanguageRepository  $repository,
private readonly ?\Closure  $now = null,
private readonly TranslationCache  $translation_cache = new NullTranslationCache(),
private readonly ?\Closure  $client_data_dir_resolver = null,
private readonly ?LoggerInterface  $logger = null 
)
Parameters
(

Closure():\DateTimeImmutable)|null $now injectable clock (UTC now by default), for tests

Parameters
(

Closure():?string)|null $client_data_dir_resolver resolved on every call, as no client may exist at construction time; null = no overlay is maintained, the database is written regardless

Parameters
LoggerInterface | null$loggernull in the Setup: warnings go to the PHP error log

Definition at line 79 of file LanguageInstallationManager.php.

88 {
89 }

Member Function Documentation

◆ assertModulesCorrectlySaved()

ILIAS\Language\Setup\LanguageInstallationManager::assertModulesCorrectlySaved ( string  $lang_key,
array  $modules 
)
private

A wrong table collation silently corrupts the serialized module arrays, which would otherwise only surface as missing translations (mantis #20046/#19140).

Parameters
list<string>$modules

Definition at line 1216 of file LanguageInstallationManager.php.

1216 : void
1217 {
1218 $ilDB = $this->db;
1219 $result = $ilDB->query(sprintf(
1220 "SELECT module, lang_array FROM lng_modules WHERE lang_key = %s AND %s",
1221 $ilDB->quote($lang_key, "text"),
1222 $ilDB->in('module', $modules, false, 'text')
1223 ));
1224
1225 while ($row = $ilDB->fetchAssoc($result)) {
1226 if (!is_array(@unserialize((string) $row["lang_array"], ["allowed_classes" => false]))) {
1227 throw new LanguageDataNotSavedException((string) $row["module"], $lang_key);
1228 }
1229 }
1230 }

References $ilDB.

◆ clientDataDir()

ILIAS\Language\Setup\LanguageInstallationManager::clientDataDir ( )
private

Definition at line 208 of file LanguageInstallationManager.php.

208 : ?string
209 {
210 return $this->client_data_dir_resolver !== null ? ($this->client_data_dir_resolver)() : null;
211 }

◆ databaseRowsOfMigratedModule()

static ILIAS\Language\Setup\LanguageInstallationManager::databaseRowsOfMigratedModule ( array  $rows,
array  $final_entries,
array  $shipped_entries,
array  $remarks = [] 
)
staticprivate

The lng_data rows of a migrated module from the rows buffered while its content was decided: the rows of a plural message's forms (and a plain row of its identifier) become one row of the identifier with the final default value (see MigratedLanguageFileSync::collapsePluralForms() and databaseValues()), the first remark and, only if that value differs from the shipped default, the first local_change.

The row stands for the default value alone, so after a rollback it does not hold back updates of the legacy .lang files. Every other row is kept.

Parameters
array<string,array{0string, 1: ?string, 2: ?string}> $rows identifier => value, local_change, remarks
array<string,string>$final_entries the module's final identifier => value map
array<string,string>$shipped_entries
array<string,string>$remarks identifier => remark, see migratedModuleRemarks()
Returns
array<string, array{0: string, 1: ?string, 2: ?string}>

Definition at line 1037 of file LanguageInstallationManager.php.

1037 : array
1038 {
1039 $values = MigratedLanguageFileSync::collapsePluralForms($final_entries, $shipped_entries);
1040 $shipped_defaults = MigratedLanguageFileSync::collapsePluralForms($shipped_entries, $shipped_entries);
1041 $database_values = MigratedLanguageFileSync::databaseValues($values, $shipped_defaults);
1042 $result = [];
1043 foreach ($rows as $identifier => [$value, $local_change, $row_remarks]) {
1044 $identifier = (string) $identifier;
1045 $plural_identifier = MigratedLanguageFileSync::pluralMessageOf($identifier, $shipped_entries);
1046 if ($plural_identifier === null) {
1047 $result[$identifier] = [$value, $local_change, $remarks[$identifier] ?? $row_remarks];
1048 continue;
1049 }
1050 if (!isset($database_values[$plural_identifier])) {
1051 continue;
1052 }
1053 $previous = $result[$plural_identifier] ?? null;
1054 $is_local_change = (string) $values[$plural_identifier] !== (string) ($shipped_defaults[$plural_identifier] ?? '');
1055 $result[$plural_identifier] = [
1056 (string) $database_values[$plural_identifier],
1057 $is_local_change ? ($previous[1] ?? $local_change) : null,
1058 $remarks[$plural_identifier] ?? $previous[2] ?? $row_remarks,
1059 ];
1060 }
1061
1062 return $result;
1063 }
static pluralMessageOf(string $key, array $shipped_entries)
The identifier of the plural message of $shipped_entries (form keys included) $key belongs to,...
static collapsePluralForms(array $entries, array $shipped_entries)
$entries (see the class docblock) as the database holds them: the forms of every plural message of $s...
static databaseValues(array $entries, array $shipped_entries)
The values of $entries as written to lng_data/lng_modules (the database fallback of a migrated module...

References ILIAS\Scripts\PHPStan\$rows.

◆ deleteLangData()

ILIAS\Language\Setup\LanguageInstallationManager::deleteLangData ( string  $lang_key,
bool  $keep_local_change 
)
private

Definition at line 369 of file LanguageInstallationManager.php.

369 : void
370 {
372
373 if (!$keep_local_change) {
374 $ilDB->manipulate("DELETE FROM lng_data WHERE lang_key = " .
375 $ilDB->quote($lang_key, "text"));
376 } else {
377 $ilDB->manipulate("DELETE FROM lng_data WHERE lang_key = " .
378 $ilDB->quote($lang_key, "text") .
379 " AND local_change IS NULL");
380 }
381 }

References ILIAS\$db, and $ilDB.

◆ duplicatesOfCustomizingLines()

ILIAS\Language\Setup\LanguageInstallationManager::duplicatesOfCustomizingLines ( array  $content,
string  $prefix,
array  $existing 
)
private
Parameters
list<string>$content lines of a customizing file, without header
list<array<string,array<string,mixed>>>$existing module => identifier => … this run writes besides lng_data, which a flush before an installation has emptied for this language
Returns
array<string, list<string>> see IdentifierUniqueness::findDuplicatesAmong()

Definition at line 184 of file LanguageInstallationManager.php.

184 : array
185 {
186 $entries = [];
187 foreach ($content as $line) {
188 $separated = explode(self::SEPARATOR, trim($line));
189 if ($prefix !== '') {
190 array_unshift($separated, $prefix);
191 }
192 if (count($separated) >= 2) {
193 $entries[$separated[0] . self::SEPARATOR . $separated[1]] = ['module' => $separated[0], 'identifier' => $separated[1]];
194 }
195 }
196 $known = [];
197 foreach ($existing as $modules) {
198 foreach ($modules as $module => $identifiers) {
199 foreach (array_keys($identifiers) as $identifier) {
200 $known[] = ['module' => (string) $module, 'identifier' => (string) $identifier];
201 }
202 }
203 }
204
205 return (new IdentifierUniqueness($this->db))->findDuplicatesAmong($entries, $known);
206 }
if(!isset($arguments[0])) if(count( $arguments) > 3) if(! $remove_from_lang &&isset($arguments[2]) &&!is_dir($arguments[2])) $module

References $module.

◆ findOverlaysOfUnshippedModules()

ILIAS\Language\Setup\LanguageInstallationManager::findOverlaysOfUnshippedModules ( string  $lang_key,
?string  $client_data_dir,
?array  $shipped_modules 
)
private
Parameters
list<string>|null$shipped_modules null: looked up here

Definition at line 634 of file LanguageInstallationManager.php.

634 : void
635 {
636 if ($client_data_dir === null) {
637 return;
638 }
639 try {
640 $modules = array_diff(
641 array_unique([
643 ...MigratedLanguageFileSync::findModulesWithOverlay($client_data_dir, $lang_key),
644 ]),
645 $shipped_modules ?? array_map('strval', array_keys(MigratedLanguageFileSync::findShippedModuleFiles(
646 $this->language_file_directory_manager,
647 $this->absolute_path,
648 $lang_key
649 )))
650 );
651 } catch (\Throwable $t) {
652 $this->logWarning(sprintf('Could not look for overlays of modules not shipped for language "%s": %s', $lang_key, $t->getMessage()));
653 return;
654 }
655 if ($modules !== []) {
656 sort($modules, SORT_STRING);
657 $this->overlays_of_unshipped_modules[$lang_key] = $modules;
658 }
659 }
static findModulesWithOverlay(?string $client_data_dir, string $lang_key)
Modules with an overlay for $lang_key, contributed or not (see hasOverlay()).
static findShippedModuleFiles(LanguageFileDirectoryManager $language_file_directory_manager, string $ilias_absolute_path, string $lang_key)
Every module migrated for $lang_key (its shipped .po exists, parseable or not) with the path of that ...
static modulesWithLocalChangesTakenOver(?string $client_data_dir, string $lang_key)
The modules whose overlay is the only source of local changes (see markLocalChangesTakenOver()).

References ILIAS\UI\examples\Symbol\Glyph\Sort\sort().

+ Here is the call graph for this function:

◆ findUnwritableOverlayDirectories()

ILIAS\Language\Setup\LanguageInstallationManager::findUnwritableOverlayDirectories ( array  $lang_keys,
?int  $for_user_id = null 
)

The overlay directories of the modules migrated for $lang_keys that cannot be written, see MigratedLanguageFileSync::findUnwritableOverlayDirectories().

To be checked before installing, so a missing permission is reported up front instead of only logged per module.

Parameters
list<string>$lang_keys
int | null$for_user_idsee MigratedLanguageFileSync::findUnwritableOverlayDirectories()
Returns
list<string>

Definition at line 474 of file LanguageInstallationManager.php.

474 : array
475 {
477 $this->language_file_directory_manager,
478 $this->absolute_path,
479 $lang_keys,
480 $this->clientDataDir(),
481 $for_user_id
482 );
483 }
static findUnwritableOverlayDirectories(LanguageFileDirectoryManager $language_file_directory_manager, string $ilias_absolute_path, array $lang_keys, ?string $client_data_dir, ?int $for_user_id=null)
The directories the overlay of the modules migrated for any of $lang_keys would have to be written to...

References ILIAS\Language\ComponentTranslation\MigratedLanguageFileSync\findUnwritableOverlayDirectories().

+ Here is the call graph for this function:

◆ flushLanguage()

ILIAS\Language\Setup\LanguageInstallationManager::flushLanguage ( string  $lang_key,
string  $mode = "all" 
)
private
Parameters
string$modeeither "all" or "keep_local"

Definition at line 358 of file LanguageInstallationManager.php.

358 : void
359 {
360 $this->deleteLangData($lang_key, ($mode === "keep_local"));
361
362 if ($mode === "all") {
363 $this->db->manipulate("DELETE FROM lng_modules WHERE lang_key = " .
364 $this->db->quote($lang_key, "text"));
365 $this->invalidateLanguageCache($lang_key);
366 }
367 }
deleteLangData(string $lang_key, bool $keep_local_change)

Referenced by ILIAS\Language\Setup\LanguageInstallationManager\installLanguages().

+ Here is the caller graph for this function:

◆ flushLanguageForInstallation()

ILIAS\Language\Setup\LanguageInstallationManager::flushLanguageForInstallation ( string  $lang_key)

Definition at line 345 of file LanguageInstallationManager.php.

345 : void
346 {
347 $this->flushLanguage($lang_key, "keep_local");
348 }

◆ flushLanguageForUninstallation()

ILIAS\Language\Setup\LanguageInstallationManager::flushLanguageForUninstallation ( string  $lang_key)

Definition at line 350 of file LanguageInstallationManager.php.

350 : void
351 {
352 $this->flushLanguage($lang_key, "all");
353 }

◆ getOverlaysOfUnshippedModules()

ILIAS\Language\Setup\LanguageInstallationManager::getOverlaysOfUnshippedModules ( )

Modules no longer shipped for the language that still have an overlay or a taken-over record.

Only reported: once migrated again, they would win over lng_data changes made in between.

Returns
array<string, list<string>> language key => modules

Definition at line 143 of file LanguageInstallationManager.php.

References ILIAS\Language\Setup\LanguageInstallationManager\$overlays_of_unshipped_modules.

◆ getSkippedDuplicateEntries()

ILIAS\Language\Setup\LanguageInstallationManager::getSkippedDuplicateEntries ( )

Entries the insertLanguageFor...() calls left out: their identifier exists in another module or spelling (language.md rule 2).

Returns
array<string, array<string, list<string>>> language key => module#:#identifier => "<module>: <identifier>" it would duplicate

Definition at line 122 of file LanguageInstallationManager.php.

References ILIAS\Language\Setup\LanguageInstallationManager\$skipped_duplicate_entries.

◆ getSkippedInvalidMarkupEntries()

ILIAS\Language\Setup\LanguageInstallationManager::getSkippedInvalidMarkupEntries ( )

Entries of the customizing/local files that the insertLanguageFor...() calls of this instance left out because of markup TranslationMarkupPolicy does not allow.

Returns
array<string, array<string, list<string>>> language key => module#:#identifier => violations

Definition at line 110 of file LanguageInstallationManager.php.

References ILIAS\Language\Setup\LanguageInstallationManager\$skipped_invalid_markup_entries.

◆ getSkippedUnreadableOverlays()

ILIAS\Language\Setup\LanguageInstallationManager::getSkippedUnreadableOverlays ( )

The migrated modules left out of the insertLanguageFor...() calls because their overlay cannot be read.

Returns
array<string, list<string>> language key => modules

Definition at line 132 of file LanguageInstallationManager.php.

References ILIAS\Language\Setup\LanguageInstallationManager\$skipped_unreadable_overlays.

◆ getUnrecordedTakenOverModules()

ILIAS\Language\Setup\LanguageInstallationManager::getUnrecordedTakenOverModules ( )

Modules whose taken-over record could not be written: they are taken over again by the next run.

Returns
array<string, list<string>> language key => modules

Definition at line 153 of file LanguageInstallationManager.php.

References ILIAS\Language\Setup\LanguageInstallationManager\$unrecorded_taken_over_modules.

◆ insertLanguage()

ILIAS\Language\Setup\LanguageInstallationManager::insertLanguage ( string  $lang_key,
iterable  $directories,
array  $lang_array,
bool  $merge_shipped_migrated_modules,
bool  $keep_local_changes,
bool  $delete_unchanged_migrated_rows = false 
)
private

Writes the data of the given $directories/$lang_array seed; the callers above decide both.

Of a migrated module (see MigratedLanguageFileSync) whose shipped .po is merged ($merge_shipped_migrated_modules), the .lang lines of the global/component files are ignored and each shipped entry is reconciled via resolveMigratedModule(); customizing lines still apply as local changes.

Afterwards the stored module arrays are verified (collation, mantis #20046/#19140), the language cache is invalidated and each migrated overlay is synced to the final content: only the delta to the shipped .po is written, see MigratedLanguageFileSync::sync().

Concurrent writes: every migrated module that has an overlay is locked for the whole run (MigratedLanguageFileSync::withOverlayLock()). A module without overlay is not locked; if one appears meanwhile, its sync() is refused and the module reported (see $expected_overlay_exists of sync()).

Parameters
iterable<LanguageFileDirectory>$directories
array<string,array<string,string>>$lang_array module => identifier => value
bool$keep_local_changeswhether local changes recorded only in a migrated module's overlay count as local changes (false for "remove local changes")
bool$delete_unchanged_migrated_rowsdelete the not locally changed lng_data rows of migrated modules that are no longer written from the shipped .po, for callers that did not flush first
Returns
list<string> the migrated modules whose overlay could not be written; the database write is not undone for them

Definition at line 509 of file LanguageInstallationManager.php.

516 : array {
517 // Reported per call (see getSkippedInvalidMarkupEntries()): only what this run left out
518 unset(
519 $this->skipped_invalid_markup_entries[$lang_key],
520 $this->skipped_duplicate_entries[$lang_key],
521 $this->skipped_unreadable_overlays[$lang_key],
522 $this->overlays_of_unshipped_modules[$lang_key],
523 $this->unrecorded_taken_over_modules[$lang_key]
524 );
525 $client_data_dir = $this->clientDataDir();
526 $modules_with_local_changes = array_map('strval', array_keys(array_filter($lang_array)));
527 $shipped_migrated_modules = $merge_shipped_migrated_modules
529 $this->language_file_directory_manager,
530 $this->absolute_path,
531 $lang_key
532 )
533 : [];
535 $lang_key,
536 $client_data_dir,
537 $merge_shipped_migrated_modules ? array_map('strval', array_keys($shipped_migrated_modules)) : null
538 );
539
540 $locked_modules = [];
541 foreach (array_keys($shipped_migrated_modules) as $module) {
543 $this->language_file_directory_manager,
544 $lang_key,
545 (string) $module,
546 $client_data_dir
547 )) {
548 $locked_modules[] = (string) $module;
549 }
550 }
551 // locks are always acquired in this global order (module names), not in getDirectories() order,
552 // which differs between entry points: other orders would deadlock (flock() has no timeout)
553 sort($locked_modules, SORT_STRING);
554
555 // lng_data changes are taken over once; with an overlay, or once taken over, it is their only source.
556 // A module with an unreadable overlay is left out completely.
557 $taken_over = MigratedLanguageFileSync::modulesWithLocalChangesTakenOver($client_data_dir, $lang_key);
558 $overlay_only_modules = [];
559 $skipped_modules = [];
560 foreach (array_keys($shipped_migrated_modules) as $module) {
561 $module = (string) $module;
562 $has_overlay = in_array($module, $locked_modules, true);
563 if ($has_overlay && !$this->isReadableOverlay($lang_key, $module, $client_data_dir)) {
564 $skipped_modules[] = $module;
565 unset($lang_array[$module]);
566 } elseif ($has_overlay || in_array($module, $taken_over, true)) {
567 $overlay_only_modules[] = $module;
568 if (isset($lang_array[$module])) {
569 $lang_array[$module] = [];
570 }
571 }
572 }
573 if ($skipped_modules !== []) {
574 $this->skipped_unreadable_overlays[$lang_key] = $skipped_modules;
575 $this->logWarning(sprintf(
576 'The overlay of the modules %s of language "%s" cannot be read: they are left unchanged, their local changes are only served again once the overlay is repaired; until then their rollback copy in lng_data/lng_modules is incomplete.',
577 implode(', ', $skipped_modules),
578 $lang_key
579 ));
580 $shipped_migrated_modules = array_diff_key($shipped_migrated_modules, array_flip($skipped_modules));
581 }
582
583 $write = fn(): array => $this->writeLanguage(
584 $lang_key,
585 $directories,
586 $lang_array,
587 $shipped_migrated_modules,
588 $locked_modules,
589 $keep_local_changes,
590 $delete_unchanged_migrated_rows,
591 $client_data_dir,
592 $overlay_only_modules,
593 $skipped_modules
594 );
595 // Built inside out, so the outermost closure acquires the first lock of $locked_modules
596 foreach (array_reverse($locked_modules) as $module) {
597 $locked = $write;
598 $write = fn(): array => MigratedLanguageFileSync::withOverlayLock(
599 $this->language_file_directory_manager,
600 $lang_key,
601 $module,
602 $client_data_dir,
603 $locked,
604 $this->absolute_path
605 );
606 }
607
608 $failed_modules = $write();
609 // not recorded, so taken over again next run: overlay not written, or an entry left out
610 $not_taken_over = $failed_modules;
611 foreach (array_keys(($this->skipped_invalid_markup_entries[$lang_key] ?? []) + ($this->skipped_duplicate_entries[$lang_key] ?? [])) as $key) {
612 $not_taken_over[] = explode(self::SEPARATOR, (string) $key, 2)[0];
613 }
614 if ($client_data_dir !== null && $shipped_migrated_modules !== []) {
615 $taken_over_now = array_values(array_diff(array_map('strval', array_keys($shipped_migrated_modules)), $not_taken_over));
616 try {
617 MigratedLanguageFileSync::markLocalChangesTakenOver($client_data_dir, $lang_key, $taken_over_now);
618 } catch (\Throwable $t) {
619 // taken over again next run; only modules with lng_data local changes are reported
620 $unrecorded = array_values(array_intersect(array_diff($taken_over_now, $taken_over), $modules_with_local_changes));
621 if ($unrecorded !== []) {
622 $this->unrecorded_taken_over_modules[$lang_key] = $unrecorded;
623 }
624 $this->logWarning(sprintf('Could not record the taken over local changes of language "%s": %s', $lang_key, $t->getMessage()));
625 }
626 }
627
628 return $failed_modules;
629 }
static loadShippedModules(LanguageFileDirectoryManager $language_file_directory_manager, string $ilias_absolute_path, string $lang_key)
The shipped values of every module migrated for $lang_key.
static hasOverlay(LanguageFileDirectoryManager $language_file_directory_manager, string $lang_key, string $module, ?string $client_data_dir)
Whether $module/$lang_key has an overlay (anything at the place of its .po or current),...
static markLocalChangesTakenOver(string $client_data_dir, string $lang_key, array $modules)
Records that the lng_data local changes of $modules are in their overlays, so lng_data is not read ag...
isReadableOverlay(string $lang_key, string $module, ?string $client_data_dir)
writeLanguage(string $lang_key, iterable $directories, array $lang_array, array $shipped_migrated_modules, array $locked_modules, bool $keep_local_changes, bool $delete_unchanged_migrated_rows, ?string $client_data_dir, array $overlay_only_modules=[], array $skipped_modules=[])
insertLanguage() once the overlays of $locked_modules are locked.
findOverlaysOfUnshippedModules(string $lang_key, ?string $client_data_dir, ?array $shipped_modules)

◆ insertLanguageForApplyingLocalChanges()

ILIAS\Language\Setup\LanguageInstallationManager::insertLanguageForApplyingLocalChanges ( string  $lang_key)

Applies only the customizing/local file on top of an installed language, leaving the base data untouched: the write path of "Install local" (see InstallLanguage::perform()).

The caller flushes nothing.

The seed must hold every stored entry, not only the local changes: insertLanguage() rebuilds the lng_modules row of each module from the seed plus the given directories, so a narrower seed would silently drop base entries the customizing file does not override.

Migrated modules are the exception: they are reconciled with their shipped .po as in insertLanguageForInstallation() (see resolveMigratedModule()) and seeded with their local changes only. Seeding every stored entry would turn a stale value into a permanent "local change" and lose the shipped update.

Returns
list<string> see insertLanguageForInstallation()

Definition at line 440 of file LanguageInstallationManager.php.

440 : array
441 {
442 $entries = $this->repository->getLanguageEntries($lang_key);
443 $local_changes = $this->repository->getLocalChanges($lang_key);
444 $migrated_modules = array_keys(MigratedLanguageFileSync::findShippedModuleFiles(
445 $this->language_file_directory_manager,
446 $this->absolute_path,
447 $lang_key
448 ));
449 foreach ($migrated_modules as $module) {
450 // an empty seed is fine: insertLanguage() drops a module that ends up without entries
451 $entries[$module] = $local_changes[$module] ?? [];
452 }
453
454 return $this->insertLanguage(
455 $lang_key,
456 $this->language_file_directory_manager->getCustomizingDirectories(),
457 $entries,
458 true,
459 true,
460 true
461 );
462 }
insertLanguage(string $lang_key, iterable $directories, array $lang_array, bool $merge_shipped_migrated_modules, bool $keep_local_changes, bool $delete_unchanged_migrated_rows=false)
Writes the data of the given $directories/$lang_array seed; the callers above decide both.

References $module, ILIAS\Language\ComponentTranslation\MigratedLanguageFileSync\findShippedModuleFiles(), and ILIAS\UI\examples\Deck\repository().

+ Here is the call graph for this function:

◆ insertLanguageForInstallation()

ILIAS\Language\Setup\LanguageInstallationManager::insertLanguageForInstallation ( string  $lang_key)

Installs or refreshes a language from every directory the LanguageFileDirectoryManager knows (including customizing/local), merged on top of the recorded local changes.

A migrated module's shipped .po is the only source of its shipped values; a three-way comparison per entry decides shipped vs. local (see resolveMigratedModule()).

Returns
list<string> the migrated modules whose overlay could not be written (see insertLanguage()) - empty if every overlay is in sync

Definition at line 393 of file LanguageInstallationManager.php.

393 : array
394 {
395 return $this->insertLanguage(
396 $lang_key,
397 $this->language_file_directory_manager->getAllDirectories(),
398 $this->repository->getLocalChanges($lang_key),
399 true,
400 true
401 );
402 }

Referenced by ILIAS\Language\Setup\LanguageInstallationManager\installLanguages(), and LanguageInstallationManagerMigratedSourcesAndLocksTest\testLocksAreAcquiredInSortedOrderRegardlessOfTheContributedDirectoryOrder().

+ Here is the caller graph for this function:

◆ insertLanguageForRemovingLocalChanges()

ILIAS\Language\Setup\LanguageInstallationManager::insertLanguageForRemovingLocalChanges ( string  $lang_key)

Re-seeds a language purely from the shipped global/component files (.lang, shipped .po), without customizing/local files and local changes: the write path of "remove local changes".

insertLanguageForInstallation() would re-apply the customizing file.

Also removes the overlay of every migrated module, as no delta to the shipped .po is left (the counterpart of the caller's flush of lng_data/lng_modules).

Returns
list<string> see insertLanguageForInstallation()

Definition at line 414 of file LanguageInstallationManager.php.

414 : array
415 {
416 return $this->insertLanguage(
417 $lang_key,
418 $this->language_file_directory_manager->getDirectories(),
419 [],
420 true,
421 false
422 );
423 }

◆ installLanguages()

ILIAS\Language\Setup\LanguageInstallationManager::installLanguages ( array  $lang_keys,
array  $local_keys = [] 
)

Installs the given languages, flushes all other known ones.

Mirrors ilSetupLanguage::installLanguages().

Parameters
list<string>$lang_keys
list<string>$local_keys unused, kept for backwards compatibility (as in ilSetupLanguage)
Returns
list<string>|bool list of language keys that failed validation, or true

Definition at line 229 of file LanguageInstallationManager.php.

229 : array|bool
230 {
232
233 $err_lang = [];
234 $db_langs = $this->repository->getAvailableLanguages();
235 $local_langs = $this->repository->getLocalLanguages();
236
237 foreach ($lang_keys as $lang_key) {
238 if ($this->repository->checkLanguage($lang_key)) {
239 $this->flushLanguage($lang_key, "keep_local");
240 $this->insertLanguageForInstallation($lang_key);
241
242 // an already-known language is (re-)registered below
243 if (!array_key_exists($lang_key, $db_langs)) {
244 $this->registerInstalledLanguage($lang_key, $db_langs, $local_langs);
245 }
246 } else {
247 $err_lang[] = $lang_key;
248 }
249 }
250
251 foreach ($db_langs as $key => $val) {
252 if (!in_array($key, $err_lang, true)) {
253 if (in_array($key, $lang_keys, true)) {
254 $this->registerInstalledLanguage($key, $db_langs, $local_langs);
255 } else {
256 $this->flushLanguage($key, "all");
257 $this->removeOverlays((string) $key);
258
259 if (strpos($val["status"], "installed") === 0) {
260 $query = "UPDATE object_data SET " .
261 "description = " . $ilDB->quote("not_installed", "text") . ", " .
262 "last_update = " . $ilDB->quote($this->utcTimestamp(), "timestamp") . " " .
263 "WHERE obj_id = " . $ilDB->quote($val["obj_id"], "integer") . " " .
264 "AND type = " . $ilDB->quote("lng", "text");
265 $ilDB->manipulate($query);
266 }
267 }
268 }
269 }
270
271 return ($err_lang) ?: true;
272 }
registerInstalledLanguage(string $lang_key, array $known_languages, array $local_language_keys)
Registers or updates the object_data row of a (re-)inserted language ("installed" vs.
removeOverlays(string $lang_key)
The overlay counterpart of flushLanguage($lang_key, "all") for a language that is no longer installed...
insertLanguageForInstallation(string $lang_key)
Installs or refreshes a language from every directory the LanguageFileDirectoryManager knows (includi...

References ILIAS\$db, $ilDB, ILIAS\Language\Setup\LanguageInstallationManager\flushLanguage(), ILIAS\Language\Setup\LanguageInstallationManager\insertLanguageForInstallation(), ILIAS\Language\Setup\LanguageInstallationManager\registerInstalledLanguage(), ILIAS\Language\Setup\LanguageInstallationManager\removeOverlays(), ILIAS\UI\examples\Deck\repository(), and ILIAS\Language\Setup\LanguageInstallationManager\utcTimestamp().

+ Here is the call graph for this function:

◆ invalidateLanguageCache()

ILIAS\Language\Setup\LanguageInstallationManager::invalidateLanguageCache ( string  $lang_key)
private

Definition at line 1232 of file LanguageInstallationManager.php.

1232 : void
1233 {
1234 try {
1235 $this->translation_cache->invalidate($lang_key);
1236 } catch (\Throwable $t) {
1237 // A cache that cannot be invalidated must not undo the successful database write
1238 $this->logWarning(sprintf('Could not invalidate the language cache for "%s": %s', $lang_key, $t->getMessage()));
1239 }
1240 }

◆ invalidMarkupOfCustomizingValue()

ILIAS\Language\Setup\LanguageInstallationManager::invalidMarkupOfCustomizingValue ( string  $value,
?string  $current,
?string  $shipped 
)
private

The violations of a customizing $value, only if it changes something (see TranslationMarkupPolicy::findInvalidChangedValues()).

Returns
list<string>

Definition at line 169 of file LanguageInstallationManager.php.

169 : array
170 {
171 return $this->markupPolicy()->findInvalidChangedValues(
172 ['value' => $value],
173 $current === null ? [] : ['value' => $current],
174 $shipped === null ? [] : ['value' => $shipped]
175 )['value'] ?? [];
176 }

References ILIAS\Language\Setup\LanguageInstallationManager\markupPolicy().

+ Here is the call graph for this function:

◆ isReadableOverlay()

ILIAS\Language\Setup\LanguageInstallationManager::isReadableOverlay ( string  $lang_key,
string  $module,
?string  $client_data_dir 
)
private

Definition at line 1137 of file LanguageInstallationManager.php.

1137 : bool
1138 {
1139 try {
1141 $this->language_file_directory_manager,
1142 $lang_key,
1143 $module,
1144 $client_data_dir,
1145 $this->absolute_path
1146 );
1147 } catch (\Throwable) {
1148 return false;
1149 }
1150
1151 return true;
1152 }
static loadLocalChanges(LanguageFileDirectoryManager $language_file_directory_manager, string $lang_key, string $module, ?string $client_data_dir, ?string $ilias_absolute_path=null)
Only the overlay delta of $module/$lang_key (see sync()), i.e.

References $module.

◆ loadOverlay()

ILIAS\Language\Setup\LanguageInstallationManager::loadOverlay ( string  $lang_key,
string  $module,
?string  $client_data_dir 
)
private

The overlay state of a migrated module, or null.

An unreadable overlay is reported and treated as absent, so it cannot abort a reinstall whose data was already flushed; sync() rewrites it afterwards. Returns only the delta (see MigratedLanguageFileSync::loadLocalChanges()), the rest carries the shipped value.

An entry "merge" took over into the shipped .po (see LocalChangeComments::isMergedIntoShipped()) gets its own value as "original", so a newer shipped value wins in resolveMigratedModule() instead of the taken-over value staying a local change.

Returns
array<string, array{value: string, local_change: bool, local_change_date: ?string, original: ?string}>|null

Definition at line 1165 of file LanguageInstallationManager.php.

1165 : ?array
1166 {
1167 try {
1169 $this->language_file_directory_manager,
1170 $lang_key,
1171 $module,
1172 $client_data_dir,
1173 $this->absolute_path
1174 );
1175 if ($overlay === null || $overlay === []) {
1176 return $overlay;
1177 }
1179 $this->language_file_directory_manager,
1180 $lang_key,
1181 $module,
1182 $client_data_dir,
1183 $this->absolute_path
1184 );
1185 $merged = [];
1186 foreach ($catalog === null ? [] : MigratedLanguageFileSync::moduleEntries($catalog, $module) as $entry) {
1188 $merged[$entry->getId()] = true;
1189 }
1190 }
1191 foreach ($overlay as $key => $state) {
1192 // a form of a plural message (see PluralFormKey) belongs to the entry of its identifier
1193 if (isset($merged[(string) $key]) || isset($merged[PluralFormKey::parse((string) $key)[0] ?? ''])) {
1194 $overlay[$key]['original'] = $state['value'];
1195 }
1196 }
1197
1198 return $overlay;
1199 } catch (\Throwable $t) {
1200 $this->logWarning(sprintf(
1201 'Could not read the overlay of migrated module "%s", language "%s" - ignoring it: %s',
1202 $module,
1203 $lang_key,
1204 $t->getMessage()
1205 ));
1206 return null;
1207 }
1208 }
static readOverlayCatalog(LanguageFileDirectoryManager $language_file_directory_manager, string $lang_key, string $module, ?string $client_data_dir, ?string $ilias_absolute_path=null)
The overlay .po of $module/$lang_key as it is (values, remarks, LocalChangeComments bookkeeping); nul...
static moduleEntries(TranslationCatalog $catalog, string $module)
The entries of $catalog that belong to $module: those with the module as context (msgctxt) and those ...

References $module.

◆ logWarning()

ILIAS\Language\Setup\LanguageInstallationManager::logWarning ( string  $message)
private

Definition at line 91 of file LanguageInstallationManager.php.

91 : void
92 {
93 if ($this->logger !== null) {
94 try {
95 $this->logger->warning($message);
96 return;
97 } catch (\Throwable) {
98 // a failing logger must not abort the installation
99 }
100 }
101 error_log($message);
102 }

References $message, and ILIAS\Repository\logger().

+ Here is the call graph for this function:

◆ markupPolicy()

ILIAS\Language\Setup\LanguageInstallationManager::markupPolicy ( )
private

Definition at line 158 of file LanguageInstallationManager.php.

159 {
160 return $this->markup_policy ??= new TranslationMarkupPolicy();
161 }
The HTML a translation value may contain, decided in one place for the shipped files (cleaned in Setu...

Referenced by ILIAS\Language\Setup\LanguageInstallationManager\invalidMarkupOfCustomizingValue().

+ Here is the caller graph for this function:

◆ migratedLocalChangeDates()

ILIAS\Language\Setup\LanguageInstallationManager::migratedLocalChangeDates ( string  $lang_key,
array  $modules 
)
private

lng_data.local_change of every locally changed entry of $modules in $lang_key (as written by this run), module => identifier => database timestamp - for the overlay, see MigratedLanguageFileSync::sync().

Parameters
list<string>$modules
Returns
array<string, array<string, string>>

Definition at line 1364 of file LanguageInstallationManager.php.

1364 : array
1365 {
1366 if ($modules === []) {
1367 return [];
1368 }
1369 $ilDB = $this->db;
1370 $result = $ilDB->query(sprintf(
1371 "SELECT module, identifier, local_change FROM lng_data WHERE lang_key = %s AND local_change IS NOT NULL AND %s",
1372 $ilDB->quote($lang_key, "text"),
1373 $ilDB->in('module', $modules, false, 'text')
1374 ));
1375 $dates = [];
1376 while (is_array($row = $ilDB->fetchAssoc($result))) {
1377 if (is_string($row['module'] ?? null) && is_string($row['identifier'] ?? null) && is_string($row['local_change'] ?? null)) {
1378 $dates[$row['module']][$row['identifier']] = $row['local_change'];
1379 }
1380 }
1381
1382 return $dates;
1383 }

References $ilDB.

◆ migratedModuleRemarks()

ILIAS\Language\Setup\LanguageInstallationManager::migratedModuleRemarks ( string  $lang_key,
array  $shipped_migrated_modules,
array  $customized_remarks,
array  $shipped_line_comments,
?string  $client_data_dir,
array  $overlay_only_modules = [] 
)
private

The remarks of every migrated module (identifier => remark; a plural message under its identifier): lng_data, overridden by the overlay, overridden by the customizing file.

A module whose overlay cannot be read is left out, so its sync() keeps the overlay's remarks.

Before the migration lng_data.remarks held the shipped "###" comment of the .lang line: a lng_data remark is only taken over if it differs from that comment ($shipped_line_comments), from the note of the shipped .po and is no dated "new variable" marker, i.e. an administrator's remark.

Parameters
array<string,array<string,string>>$shipped_migrated_modules
array<string,array<string,string>>$customized_remarks
array<string,array<string,string>>$shipped_line_comments module => identifier => "###" comment of the shipped .lang lines
list<string>$overlay_only_modules their lng_data remarks are not read, see insertLanguage()
Returns
array<string, array<string, string>>

Definition at line 1258 of file LanguageInstallationManager.php.

1265 : array {
1266 if ($shipped_migrated_modules === []) {
1267 return [];
1268 }
1269 $ilDB = $this->db;
1270 $database_remarks = [];
1271 $result = $ilDB->query(sprintf(
1272 "SELECT module, identifier, remarks FROM lng_data WHERE lang_key = %s AND remarks IS NOT NULL AND %s",
1273 $ilDB->quote($lang_key, "text"),
1274 $ilDB->in('module', array_map('strval', array_keys($shipped_migrated_modules)), false, 'text')
1275 ));
1276 while ($row = $ilDB->fetchAssoc($result)) {
1277 if (isset($row['module'], $row['identifier'], $row['remarks']) && (string) $row['remarks'] !== '') {
1278 $database_remarks[(string) $row['module']][(string) $row['identifier']] = (string) $row['remarks'];
1279 }
1280 }
1281 $database_remarks = array_diff_key($database_remarks, array_flip($overlay_only_modules));
1282
1284 $this->language_file_directory_manager,
1285 $this->absolute_path,
1286 $lang_key
1287 );
1288 $remarks = [];
1289 foreach ($shipped_migrated_modules as $module => $shipped_entries) {
1290 $module = (string) $module;
1291 try {
1292 $po_notes = isset($shipped_files[$module])
1293 ? array_filter(array_map(
1294 static fn(array $entry): string => (string) $entry['comment'],
1296 ), static fn(string $note): bool => $note !== '')
1297 : [];
1298 } catch (\Throwable) {
1299 $po_notes = [];
1300 }
1301 foreach ($database_remarks[$module] ?? [] as $identifier => $remark) {
1302 $plural_identifier = MigratedLanguageFileSync::pluralMessageOf((string) $identifier, $shipped_entries);
1303 $shipped_comments = [
1304 $shipped_line_comments[$module][$identifier] ?? null,
1305 $po_notes[$identifier] ?? null,
1306 $plural_identifier === null ? null : ($po_notes[PluralFormKey::of($plural_identifier, 0)] ?? null),
1307 ];
1308 if (
1309 in_array(trim($remark), array_map(static fn(?string $c): ?string => $c === null ? null : trim($c), $shipped_comments), true)
1310 || LegacyFuzzyMarker::matches($remark)
1311 ) {
1312 unset($database_remarks[$module][$identifier]);
1313 }
1314 }
1315 try {
1316 $overlay_remarks = MigratedLanguageFileSync::loadRemarks(
1317 $this->language_file_directory_manager,
1318 $lang_key,
1319 $module,
1320 $client_data_dir,
1321 $this->absolute_path
1322 ) ?? [];
1323 } catch (\Throwable) {
1324 // reported by loadOverlay() - the sync keeps what the overlay holds
1325 continue;
1326 }
1327 // a remark taken over by "merge" is a shipped note now (see ShippedPoMerger): it leaves the overlay
1328 try {
1329 $po_notes_by_message = isset($shipped_files[$module])
1331 : [];
1332 } catch (\Throwable) {
1333 $po_notes_by_message = [];
1334 }
1335 foreach ($overlay_remarks as $identifier => $remark) {
1336 $identifier = (string) $identifier;
1337 $message = MigratedLanguageFileSync::pluralMessageOf($identifier, $shipped_entries) ?? $identifier;
1338 $stored_remark = trim(TranslationEntry::plainComment((string) $remark));
1339 if (in_array($stored_remark, $po_notes_by_message[$message] ?? [], true)) {
1340 unset($overlay_remarks[$identifier]);
1341 }
1342 }
1343 $module_remarks = [];
1344 foreach ([$database_remarks[$module] ?? [], $overlay_remarks, $customized_remarks[$module] ?? []] as $source) {
1345 foreach ($source as $identifier => $remark) {
1346 $identifier = (string) $identifier;
1347 $module_remarks[MigratedLanguageFileSync::pluralMessageOf($identifier, $shipped_entries) ?? $identifier] = (string) $remark;
1348 }
1349 }
1350 $remarks[$module] = $module_remarks;
1351 }
1352
1353 return $remarks;
1354 }
static plainComment(string $comment)
$comment as addTranslatorNote()/addExtractedComment() store it.
static loadShippedNotes(string $shipped_po, string $module)
The notes (see shippedNotesOf()) of every entry of $module in the shipped .po $shipped_po,...
static loadRemarks(LanguageFileDirectoryManager $language_file_directory_manager, string $lang_key, string $module, ?string $client_data_dir, ?string $ilias_absolute_path=null)
The remarks of the overlay of $module/$lang_key (see LocalChangeComments::setRemark()),...
static loadShippedModuleEntries(string $shipped_po, string $module)
The entries of $module in the shipped .po $shipped_po (see findShippedModuleFiles(); a plural message...
static of(string $identifier, int $form)

◆ registerInstalledLanguage()

ILIAS\Language\Setup\LanguageInstallationManager::registerInstalledLanguage ( string  $lang_key,
array  $known_languages,
array  $local_language_keys 
)

Registers or updates the object_data row of a (re-)inserted language ("installed" vs.

"installed_local"); single place for this bookkeeping of installLanguages() and InstallLanguage::perform().

Parameters
array<string,array{obj_id:int,status:string}>$known_languages result of InstalledLanguageRepository::getAvailableLanguages()
list<string>$local_language_keys result of InstalledLanguageRepository::getLocalLanguages()

Definition at line 283 of file LanguageInstallationManager.php.

287 : void {
288 $db = $this->db;
289 $installation_type = in_array($lang_key, $local_language_keys, true) ? "installed_local" : "installed";
290
291 if (!array_key_exists($lang_key, $known_languages)) {
292 $language_id = $db->nextId("object_data");
293 $query = "INSERT INTO object_data " .
294 "(obj_id,type,title,description,owner,create_date,last_update) " .
295 "VALUES " .
296 "(" .
297 $db->quote($language_id, "integer") . "," .
298 $db->quote("lng", "text") . "," .
299 $db->quote($lang_key, "text") . "," .
300 $db->quote($installation_type, "text") . "," .
301 $db->quote("-1", "integer") . "," .
302 $db->now() . "," .
303 $db->now() .
304 ")";
305 $db->manipulate($query);
306 return;
307 }
308
309 $query = "UPDATE object_data SET " .
310 "description = " . $db->quote($installation_type, "text") . ", " .
311 "last_update = " . $db->quote($this->utcTimestamp(), "timestamp") . " " .
312 "WHERE obj_id = " . $db->quote($known_languages[$lang_key]["obj_id"], "integer") . " " .
313 "AND type = " . $db->quote("lng", "text");
314 $db->manipulate($query);
315 }

References ILIAS\$db.

Referenced by ILIAS\Language\Setup\LanguageInstallationManager\installLanguages().

+ Here is the caller graph for this function:

◆ removeOverlays()

ILIAS\Language\Setup\LanguageInstallationManager::removeOverlays ( string  $lang_key)
private

The overlay counterpart of flushLanguage($lang_key, "all") for a language that is no longer installed - see ilObjLanguage::uninstall().

A failure is logged and does not undo the flush.

Definition at line 321 of file LanguageInstallationManager.php.

321 : void
322 {
323 $client_data_dir = $this->clientDataDir();
325 foreach ($this->language_file_directory_manager->getDirectories() as $directory) {
326 $module = $directory->getPrefix();
327 try {
329 $this->language_file_directory_manager,
330 $lang_key,
331 $module,
332 $client_data_dir
333 );
334 } catch (\Throwable $t) {
335 $this->logWarning(sprintf(
336 'Could not remove the overlay of migrated module "%s", language "%s": %s',
337 $module,
338 $lang_key,
339 $t->getMessage()
340 ));
341 }
342 }
343 }
static forgetLocalChangesTakenOver(?string $client_data_dir, string $lang_key)
Forgets markLocalChangesTakenOver() for $lang_key (e.g.
static removeOverlay(LanguageFileDirectoryManager $language_file_directory_manager, string $lang_key, string $module, ?string $client_data_dir)
Removes the overlay of $module/$lang_key as a whole (uninstalling a language or plugin): current,...

References $module, ILIAS\Language\ComponentTranslation\MigratedLanguageFileSync\forgetLocalChangesTakenOver(), and ILIAS\Language\ComponentTranslation\MigratedLanguageFileSync\removeOverlay().

Referenced by ILIAS\Language\Setup\LanguageInstallationManager\installLanguages().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ resolveMigratedModule()

ILIAS\Language\Setup\LanguageInstallationManager::resolveMigratedModule ( array  $local_entries,
array  $customized,
array  $shipped_entries,
array  $overlay,
\Closure  $write_row,
\Closure  $delete_row 
)
private

The shipped/local decision for one migrated module: a three-way comparison per shipped entry between L = the local value (.

$local_entries: lng_data, customizing/local file or, with $overlay, the overlay), O = the shipped value the overlay tracked the entry against ("original"), and S = the value the shipped .po carries now:

  • no L -> S
  • L === S -> S, no longer a local change
  • L === O (so S changed) -> S: the "local" value only was the previously shipped one
  • otherwise -> L: a real local change is never overwritten; only "original" moves to S, so "remove local changes" resets to the current shipped value

A value from the customizing file ($customized) always stays local. Overlay entries that are neither shipped nor local are dropped. A local entry without shipped counterpart (removed from the .po or added locally) is dropped if L === O (via $delete_row; it only carries its last shipped value), otherwise or if O is unknown kept as a local change. The legacy .lang update treats a removed key alike.

Parameters
array<string,string>$local_entries identifier => value
array<string,true>$customized identifiers that came from the customizing/local file
array<string,string>$shipped_entries identifier => shipped value
array<string,array{valuestring, local_change: bool, local_change_date: ?string, original: ?string}> $overlay

Closure(string, string, ?string):void $write_row persists one lng_data row

Parameters

Closure(string):void $delete_row removes one lng_data row (after all rows were written)

Returns
array<string, string> the module's final identifier => value map

Definition at line 1090 of file LanguageInstallationManager.php.

1097 : array {
1098 foreach ($overlay as $identifier => $state) {
1099 $identifier = (string) $identifier;
1100 if ($state['local_change'] && !isset($local_entries[$identifier])) {
1101 $local_entries[$identifier] = $state['value'];
1102 $write_row($identifier, $state['value'], $state['local_change_date'] ?? $this->utcTimestamp());
1103 }
1104 }
1105
1106 foreach ($shipped_entries as $identifier => $shipped_value) {
1107 $identifier = (string) $identifier;
1108 if (isset($local_entries[$identifier]) && !isset($customized[$identifier])) {
1109 $local_value = $local_entries[$identifier];
1110 $previously_shipped_value = $overlay[$identifier]['original'] ?? null;
1111 if ($local_value !== $shipped_value && $local_value !== $previously_shipped_value) {
1112 continue;
1113 }
1114 } elseif (isset($local_entries[$identifier])) {
1115 continue;
1116 }
1117
1118 $local_entries[$identifier] = $shipped_value;
1119 $write_row($identifier, $shipped_value, null);
1120 }
1121
1122 foreach ($local_entries as $identifier => $local_value) {
1123 $identifier = (string) $identifier;
1124 if (isset($shipped_entries[$identifier]) || isset($customized[$identifier])) {
1125 continue;
1126 }
1127 $previously_shipped_value = $overlay[$identifier]['original'] ?? null;
1128 if ($previously_shipped_value !== null && $local_value === $previously_shipped_value) {
1129 unset($local_entries[$identifier]);
1130 $delete_row($identifier);
1131 }
1132 }
1133
1134 return $local_entries;
1135 }

◆ syncMigratedModules()

ILIAS\Language\Setup\LanguageInstallationManager::syncMigratedModules ( string  $lang_key,
array  $lang_array,
?string  $client_data_dir,
array  $expected_overlays = [],
array  $remarks = [],
array  $local_change_dates = [] 
)
private
Parameters
array<string,array<string,string>>$lang_array
array<string,bool|null>$expected_overlays module => $expected_overlay_exists of MigratedLanguageFileSync::sync()
array<string,array<string,string>|null>$remarks module => $remarks of MigratedLanguageFileSync::sync() (null/missing: keep the overlay's remarks)
array<string,array<string,string>>$local_change_dates module => $local_change_dates of MigratedLanguageFileSync::sync()
Returns
list<string> the modules whose overlay could not be written

Definition at line 1395 of file LanguageInstallationManager.php.

1402 : array {
1403 $failed_modules = [];
1404 foreach ($lang_array as $module => $entries) {
1405 try {
1407 $this->language_file_directory_manager,
1408 $this->absolute_path,
1409 $lang_key,
1410 (string) $module,
1411 $entries,
1412 $client_data_dir,
1413 // every caller of insertLanguage() reconciles the language with the shipped
1414 // files, see MigratedLanguageFileSync::sync()
1415 true,
1416 $expected_overlays[(string) $module] ?? null,
1417 $remarks[(string) $module] ?? null,
1418 $local_change_dates[(string) $module] ?? []
1419 );
1420 } catch (\Throwable $t) {
1421 // the lng_modules write already succeeded: the failure is returned, not rolled back
1422 $this->logWarning(sprintf(
1423 'Could not sync migrated language file for module "%s", language "%s": %s',
1424 $module,
1425 $lang_key,
1426 $t->getMessage()
1427 ));
1428 $failed_modules[] = (string) $module;
1429 }
1430 }
1431
1432 return $failed_modules;
1433 }
static sync(LanguageFileDirectoryManager $language_file_directory_manager, string $ilias_absolute_path, string $lang_key, string $module, array $entries, ?string $client_data_dir, bool $refresh_original_from_shipped=false, ?bool $expected_overlay_exists=null, ?array $remarks=null, array $local_change_dates=[])
Brings the overlay of $module/$lang_key in line with $entries, the complete final content of the modu...

References $module.

◆ utcTimestamp()

ILIAS\Language\Setup\LanguageInstallationManager::utcTimestamp ( ?int  $unix_timestamp = null)
private

Definition at line 213 of file LanguageInstallationManager.php.

213 : string
214 {
215 if ($unix_timestamp !== null) {
216 return gmdate("Y-m-d H:i:s", $unix_timestamp);
217 }
218 $now = $this->now !== null ? ($this->now)() : new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
219 return $now->format('Y-m-d H:i:s');
220 }

Referenced by ILIAS\Language\Setup\LanguageInstallationManager\installLanguages().

+ Here is the caller graph for this function:

◆ writeLanguage()

ILIAS\Language\Setup\LanguageInstallationManager::writeLanguage ( string  $lang_key,
iterable  $directories,
array  $lang_array,
array  $shipped_migrated_modules,
array  $locked_modules,
bool  $keep_local_changes,
bool  $delete_unchanged_migrated_rows,
?string  $client_data_dir,
array  $overlay_only_modules = [],
array  $skipped_modules = [] 
)
private

insertLanguage() once the overlays of $locked_modules are locked.

Parameters
iterable<LanguageFileDirectory>$directories
array<string,array<string,string>>$lang_array
array<string,array<string,string>>$shipped_migrated_modules
list<string>$locked_modules
list<string>$overlay_only_modules migrated modules whose lng_data rows are never read
list<string>$skipped_modules migrated modules of which nothing is written
Returns
list<string> see insertLanguage()

Definition at line 672 of file LanguageInstallationManager.php.

683 : array {
685 $working_dir = getcwd();
686
687 $values_sql = [];
688 // rows of migrated modules are buffered until their final content is known: the database holds a
689 // plural message only under its identifier with its default value (see MigratedLanguageFileSync::collapsePluralForms())
690 $migrated_rows = [];
691 $sql_row = static function (
692 string $module,
693 string $identifier,
694 string $value,
695 ?string $local_change,
696 ?string $remarks
697 ) use (&$values_sql, $ilDB, $lang_key): void {
698 $values_sql[] = sprintf(
699 "(%s,%s,%s,%s,%s,%s)",
700 $ilDB->quote($module, "text"),
701 $ilDB->quote($identifier, "text"),
702 $ilDB->quote($lang_key, "text"),
703 $ilDB->quote($value, "text"),
704 $ilDB->quote($local_change, "timestamp"),
705 $ilDB->quote($remarks, "text")
706 );
707 };
708 $add_row = static function (
709 string $module,
710 string $identifier,
711 string $value,
712 ?string $local_change,
713 ?string $remarks
714 ) use (&$migrated_rows, $sql_row, $shipped_migrated_modules): void {
715 if (!isset($shipped_migrated_modules[$module])) {
716 $sql_row($module, $identifier, $value, $local_change, $remarks);
717 return;
718 }
719 // a later row of the same key replaces an earlier one, like the INSERT below does
720 unset($migrated_rows[$module][$identifier]);
721 $migrated_rows[$module][$identifier] = [$value, $local_change, $remarks];
722 };
723
724 // chdir()s into each language directory; must be restored on every exit path (also exceptions),
725 // as long-lived workers would leak the cwd into later requests
726 try {
727 $customized = [];
728 $customized_remarks = [];
729 $duplicates = [];
730 // markup check of the customizing file: values before this run (the seed) and shipped values
731 $current_values = $lang_array;
732 $shipped_line_values = [];
733 $shipped_line_comments = [];
734
735 foreach ($directories as $directory) {
736 $lang_file = "ilias_" . $lang_key . ".lang" . $directory->getSuffix();
737 $path = $this->absoluteDirectoryPath($this->absolute_path, $directory);
738
739 if (!is_dir($path)) {
740 continue;
741 }
742 chdir($path);
743
744 if (!is_file($lang_file)) {
745 continue;
746 }
747
748 $content = $this->cutHeader(file($lang_file));
749 if (!$content) {
750 continue;
751 }
752
753 $prefix = $directory->getPrefix();
754 $is_local = $directory->isLocal();
755
756 // resolved once per directory, not per line
757 $change_date = null;
758 $newer_db_changes = [];
759 if ($is_local) {
760 // a local file overwrites unless the database holds a newer change for the entry
761 $newer_db_changes = array_diff_key(
762 $this->repository->getLocalChanges($lang_key, $this->utcTimestamp(filemtime($lang_file))),
763 array_flip($overlay_only_modules)
764 );
765 // of those modules from their overlay instead
766 foreach ($overlay_only_modules as $module) {
767 foreach ($this->loadOverlay($lang_key, $module, $client_data_dir) ?? [] as $identifier => $state) {
768 // without a date it counts as newer: the file must not overwrite it
769 if ($state['local_change'] && ($state['local_change_date'] === null || $state['local_change_date'] >= $this->utcTimestamp(filemtime($lang_file)))) {
770 $newer_db_changes[$module][(string) $identifier] = $state['value'];
771 }
772 }
773 }
774 $change_date = $this->utcTimestamp();
775 }
776 $duplicate_entries = $is_local
777 ? $this->duplicatesOfCustomizingLines($content, $prefix, [$lang_array, $shipped_line_values, $shipped_migrated_modules])
778 : [];
779
780 $seen_in_file = [];
781 foreach ($content as $line) {
782 $line = trim($line);
783 if ($line === '') {
784 continue;
785 }
786 $separated = explode(self::SEPARATOR, $line);
787
788 if (!empty($prefix)) {
789 array_unshift($separated, $prefix);
790 }
791
792 $pos = strpos($separated[2], self::COMMENT_SEPARATOR);
793 if ($pos !== false) {
794 $separated[3] = substr($separated[2], $pos + strlen(self::COMMENT_SEPARATOR));
795 $separated[2] = substr($separated[2], 0, $pos);
796 }
797
798 $module = $separated[0];
799 $identifier = $separated[1];
800 $value = $separated[2];
801 if (in_array($module, $skipped_modules, true)) {
802 continue;
803 }
804
805 if (isset($seen_in_file[$module][$identifier])) {
806 $duplicates[] = $path . $lang_file . ': ' . $module . self::SEPARATOR . $identifier;
807 continue;
808 }
809 $seen_in_file[$module][$identifier] = true;
810
811 if (!$is_local) {
812 $shipped_line_values[$module][$identifier] ??= $value;
813 if (($separated[3] ?? '') !== '') {
814 // the shipped "###" comment, formerly held by lng_data.remarks, see migratedModuleRemarks()
815 $shipped_line_comments[$module][$identifier] ??= $separated[3];
816 }
817 if (isset($shipped_migrated_modules[$module])) {
818 // Migrated: the shipped .po is the only source, see resolveMigratedModule()
819 continue;
820 }
821 // respect recorded local changes; an earlier directory wins
822 if (isset($lang_array[$module][$identifier])) {
823 continue;
824 }
825 } elseif (isset($newer_db_changes[$module][$identifier])) {
826 $lang_array[$module][$identifier] = $newer_db_changes[$module][$identifier];
827 continue;
828 } elseif (isset($duplicate_entries[$module . self::SEPARATOR . $identifier])) {
829 $this->skipped_duplicate_entries[$lang_key][$module . self::SEPARATOR . $identifier]
830 = $duplicate_entries[$module . self::SEPARATOR . $identifier];
831 continue;
832 } elseif (($violations = $this->invalidMarkupOfCustomizingValue(
833 $value,
834 $current_values[$module][$identifier] ?? null,
835 isset($shipped_migrated_modules[$module])
836 ? ($shipped_migrated_modules[$module][$identifier] ?? null)
837 : ($shipped_line_values[$module][$identifier] ?? null)
838 )) !== []) {
839 // a customizing value with disallowed markup is left out and reported, the language is still installed
840 $this->skipped_invalid_markup_entries[$lang_key][$module . self::SEPARATOR . $identifier] = $violations;
841 continue;
842 }
843
844 $add_row($module, $identifier, $value, $change_date, $separated[3] ?? null);
845 $lang_array[$module][$identifier] = $value;
846 if ($is_local) {
847 $customized[$module][$identifier] = true;
848 if (isset($shipped_migrated_modules[$module]) && ($separated[3] ?? '') !== '') {
849 $customized_remarks[$module][$identifier] = $separated[3];
850 }
851 }
852 }
853 }
854
855 $skipped_for_language = $this->skipped_invalid_markup_entries[$lang_key] ?? [];
856 if ($skipped_for_language !== []) {
857 $this->logWarning(PlainLogText::of(sprintf(
858 'Customizing entries of language "%s" not applied - they contain markup that is not allowed: %s',
859 $lang_key,
860 implode(', ', array_keys($skipped_for_language))
861 )));
862 }
863
864 $skipped_duplicates = $this->skipped_duplicate_entries[$lang_key] ?? [];
865 if ($skipped_duplicates !== []) {
866 $entries = [];
867 foreach ($skipped_duplicates as $key => $existing) {
868 $entries[] = $lang_key . ': ' . DuplicateIdentifierMessage::entry((string) $key, $existing);
869 }
871 }
872
873 if ($duplicates !== []) {
874 // shipped files contain duplicates (e.g. ilias_fa.lang), so aborting would block updating them:
875 // the first occurrence wins and the duplicates are reported
876 $this->logWarning(sprintf(
877 'Duplicate language entries for language "%s" (first occurrence used): %s',
878 $lang_key,
879 implode(', ', $duplicates)
880 ));
881 }
882
883 // remarks of migrated modules (also kept in their overlay); later ones win: lng_data, overlay, customizing file
884 $migrated_remarks = $keep_local_changes
885 ? $this->migratedModuleRemarks($lang_key, $shipped_migrated_modules, $customized_remarks, $shipped_line_comments, $client_data_dir, $overlay_only_modules)
886 : [];
887
888 $dropped_identifiers = [];
889 foreach ($shipped_migrated_modules as $module => $shipped_entries) {
890 $overlay = $keep_local_changes ? $this->loadOverlay($lang_key, (string) $module, $client_data_dir) : null;
891
892 // a plain value of a plural message is its default form
893 $lang_array[$module] = $this->resolveMigratedModule(
894 MigratedLanguageFileSync::mapLegacyPluralValues($lang_array[$module] ?? [], $shipped_entries),
895 MigratedLanguageFileSync::mapLegacyPluralValues($customized[$module] ?? [], $shipped_entries),
896 $shipped_entries,
897 MigratedLanguageFileSync::mapLegacyPluralValues($overlay ?? [], $shipped_entries),
898 // the database gets the shipped values as the build serves them, see databaseValues()
899 static fn(string $identifier, string $value, ?string $local_change) =>
900 $add_row(
901 $module,
902 $identifier,
903 MigratedLanguageFileSync::databaseValues([$identifier => $value], $shipped_entries)[$identifier],
904 $local_change,
905 null
906 ),
907 static function (string $identifier) use (&$dropped_identifiers, $module): void {
908 $dropped_identifiers[$module][] = $identifier;
909 }
910 );
911 if ($lang_array[$module] === []) {
912 unset($lang_array[$module]);
913 }
914 }
915 // module => identifiers of the migrated modules written below
916 $written_migrated_identifiers = array_fill_keys(array_map('strval', array_keys($shipped_migrated_modules)), []);
917 foreach ($migrated_rows as $module => $rows) {
918 foreach (self::databaseRowsOfMigratedModule($rows, $lang_array[$module] ?? [], $shipped_migrated_modules[$module], $migrated_remarks[$module] ?? []) as $identifier => [$value, $local_change, $remarks]) {
919 $sql_row((string) $module, (string) $identifier, $value, $local_change, $remarks);
920 $written_migrated_identifiers[(string) $module][] = (string) $identifier;
921 }
922 }
923
924 $write_rows = static function (\ilDBInterface $ilDB) use ($values_sql, $dropped_identifiers, $lang_key, $delete_unchanged_migrated_rows, $written_migrated_identifiers): void {
925 if ($values_sql !== []) {
926 $query = "INSERT INTO lng_data (module,identifier,lang_key,value,local_change,remarks) VALUES "
927 . implode(',', $values_sql)
928 . " ON DUPLICATE KEY UPDATE value=VALUES(value),remarks=VALUES(remarks),local_change=VALUES(local_change);";
929 $ilDB->manipulate($query);
930 }
931
932 // only after the rows are written, so an aborted write leaves the former rows instead of a gap
933 if ($delete_unchanged_migrated_rows) {
934 foreach ($written_migrated_identifiers as $module => $identifiers) {
935 $ilDB->manipulate(sprintf(
936 "DELETE FROM lng_data WHERE lang_key = %s AND local_change IS NULL AND module = %s%s",
937 $ilDB->quote($lang_key, "text"),
938 $ilDB->quote((string) $module, "text"),
939 $identifiers === [] ? '' : ' AND ' . $ilDB->in('identifier', $identifiers, true, 'text')
940 ));
941 }
942 }
943
944 foreach ($dropped_identifiers as $module => $identifiers) {
945 $ilDB->manipulate(sprintf(
946 "DELETE FROM lng_data WHERE lang_key = %s AND module = %s AND %s",
947 $ilDB->quote($lang_key, "text"),
948 $ilDB->quote((string) $module, "text"),
949 $ilDB->in('identifier', $identifiers, false, 'text')
950 ));
951 }
952 };
953 if ($delete_unchanged_migrated_rows && $shipped_migrated_modules !== []) {
954 // one table lock, so a parallel request never sees the state in between (no rollback)
955 $atom_query = $ilDB->buildAtomQuery();
956 $atom_query->addTableLock('lng_data');
957 $atom_query->addQueryCallable($write_rows);
958 $atom_query->run();
959 } else {
960 $write_rows($ilDB);
961 }
962
963 if ($lang_array === []) {
964 return [];
965 }
966
967 $modules = array_map('strval', array_keys($lang_array));
968 $inModulesToDelete = $ilDB->in('module', $modules, false, 'text');
969 $ilDB->manipulate(sprintf(
970 "DELETE FROM lng_modules WHERE lang_key = %s AND $inModulesToDelete",
971 $ilDB->quote($lang_key, "text")
972 ));
973
974 $modulesValuesSql = [];
975 foreach ($lang_array as $module => $lang_arr) {
976 // $lang_array itself stays unprocessed: sync() below compares with the raw .po
977 if (isset($shipped_migrated_modules[$module])) {
979 MigratedLanguageFileSync::collapsePluralForms($lang_arr, $shipped_migrated_modules[$module]),
980 MigratedLanguageFileSync::collapsePluralForms($shipped_migrated_modules[$module], $shipped_migrated_modules[$module])
981 );
982 }
983 $modulesValuesSql[] = sprintf(
984 "(%s,%s,%s)",
985 $ilDB->quote((string) $module, "text"),
986 $ilDB->quote($lang_key, "text"),
987 $ilDB->quote(serialize($lang_arr), "clob")
988 );
989 }
990
991 $query = "INSERT INTO lng_modules (module, lang_key, lang_array) VALUES "
992 . implode(',', $modulesValuesSql)
993 . ";";
994 $ilDB->manipulate($query);
995
996 $this->assertModulesCorrectlySaved($lang_key, $modules);
997 $this->invalidateLanguageCache($lang_key);
998
999 // a migrated module that was not locked had no overlay when this run decided on its values
1000 $expected_overlays = [];
1001 foreach (array_keys($shipped_migrated_modules) as $module) {
1002 $expected_overlays[(string) $module] = in_array((string) $module, $locked_modules, true) ? null : false;
1003 }
1004
1005 // Without keeping local changes ("remove local changes"), the remarks go as well
1006 $remarks = [];
1007 foreach (array_keys($shipped_migrated_modules) as $module) {
1008 $remarks[(string) $module] = $keep_local_changes ? ($migrated_remarks[(string) $module] ?? null) : [];
1009 }
1010
1011 // an entry that enters the overlay keeps the time of its local change
1012 $local_change_dates = $keep_local_changes
1013 ? $this->migratedLocalChangeDates($lang_key, array_map('strval', array_keys($shipped_migrated_modules)))
1014 : [];
1015
1016 return $this->syncMigratedModules($lang_key, $lang_array, $client_data_dir, $expected_overlays, $remarks, $local_change_dates);
1017 } finally {
1018 chdir($working_dir);
1019 }
1020 }
static mapLegacyPluralValues(array $entries, array $shipped_entries)
$entries with a plain value of the identifier of a plural message of $shipped_entries moved to the ke...
assertModulesCorrectlySaved(string $lang_key, array $modules)
A wrong table collation silently corrupts the serialized module arrays, which would otherwise only su...
migratedModuleRemarks(string $lang_key, array $shipped_migrated_modules, array $customized_remarks, array $shipped_line_comments, ?string $client_data_dir, array $overlay_only_modules=[])
The remarks of every migrated module (identifier => remark; a plural message under its identifier): l...
invalidMarkupOfCustomizingValue(string $value, ?string $current, ?string $shipped)
The violations of a customizing $value, only if it changes something (see TranslationMarkupPolicy::fi...
syncMigratedModules(string $lang_key, array $lang_array, ?string $client_data_dir, array $expected_overlays=[], array $remarks=[], array $local_change_dates=[])
migratedLocalChangeDates(string $lang_key, array $modules)
lng_data.local_change of every locally changed entry of $modules in $lang_key (as written by this run...
loadOverlay(string $lang_key, string $module, ?string $client_data_dir)
The overlay state of a migrated module, or null.
duplicatesOfCustomizingLines(array $content, string $prefix, array $existing)
resolveMigratedModule(array $local_entries, array $customized, array $shipped_entries, array $overlay, \Closure $write_row, \Closure $delete_row)
The shipped/local decision for one migrated module: a three-way comparison per shipped entry between ...
Interface ilDBInterface.

Field Documentation

◆ $markup_policy

TranslationMarkupPolicy ILIAS\Language\Setup\LanguageInstallationManager::$markup_policy = null
private

Definition at line 71 of file LanguageInstallationManager.php.

◆ $overlays_of_unshipped_modules

array ILIAS\Language\Setup\LanguageInstallationManager::$overlays_of_unshipped_modules = []
private

◆ $skipped_duplicate_entries

array ILIAS\Language\Setup\LanguageInstallationManager::$skipped_duplicate_entries = []
private

◆ $skipped_invalid_markup_entries

array ILIAS\Language\Setup\LanguageInstallationManager::$skipped_invalid_markup_entries = []
private

◆ $skipped_unreadable_overlays

array ILIAS\Language\Setup\LanguageInstallationManager::$skipped_unreadable_overlays = []
private

◆ $unrecorded_taken_over_modules

array ILIAS\Language\Setup\LanguageInstallationManager::$unrecorded_taken_over_modules = []
private

◆ COMMENT_SEPARATOR

const string ILIAS\Language\Setup\LanguageInstallationManager::COMMENT_SEPARATOR = "###"
private

Definition at line 45 of file LanguageInstallationManager.php.

◆ SEPARATOR

const string ILIAS\Language\Setup\LanguageInstallationManager::SEPARATOR = "#:#"
private

Definition at line 44 of file LanguageInstallationManager.php.


The documentation for this class was generated from the following file: