19declare(strict_types=1);
84if (PHP_SAPI !==
'cli') {
88require dirname(__DIR__, 5) .
'/vendor/composer/vendor/autoload.php';
105 return addcslashes(
$text,
"\0..\37\177");
112 $content = file_get_contents($file);
113 if ($content ===
false) {
114 throw new RuntimeException(
"Cannot read $file");
126 $lines = explode(
"\n", $content);
127 foreach ($lines as $number => $line) {
128 if (trim($line) ===
'<!-- language file start -->') {
129 return array_slice($lines, $number + 1,
null,
true);
137 foreach (explode(
"\n", $content) as $line) {
138 if (trim($line) ===
'<!-- language file start -->') {
153 foreach (
$body_lines($content) as $number => $line) {
155 $parts = explode(
'#:#', trim($line));
156 if (count($parts) < 3) {
159 if (count($parts) > 3) {
161 fwrite(STDERR, sprintf(
162 "WARNING: %s:%d contains \"#:#\" in its value - only the part before it is used, like the installer does.\n",
167 $entries[] = [$parts[0], $parts[1], $parts[2]];
178 foreach (
$read_lines($file, $content) as [$mod, $key, $raw_value]) {
180 $lines[] = [$key, $raw_value];
193 foreach ($lines as [$key, $raw_value]) {
196 $comment_pos = strpos($raw_value,
'###');
197 if ($comment_pos !==
false) {
198 $value = substr($raw_value, 0, $comment_pos);
199 $comment = trim(substr($raw_value, $comment_pos + 3));
201 $entries[$key] = [
'value' => $value,
'comment' =>
$comment];
224 foreach ($lines as [$key, $raw_value]) {
225 $raw_values[$key][] = $raw_value;
228 foreach ($raw_values as $key => $values) {
229 if (count($values) > 1) {
230 $duplicates[(string) $key] = count(array_unique($values)) === 1;
241 return implode(
', ', array_map(
242 static fn(
int|
string $key):
string => (
string) $key ===
'' ?
'"" (empty key)' :
$printable((
string) $key),
255 $lines = preg_split(
'/(?<=\n)/', $content, -1, PREG_SPLIT_NO_EMPTY);
257 foreach ($lines as $line) {
258 if (trim($line) ===
'<!-- language file start -->') {
265 foreach ($lines as $line) {
267 $in_body = trim($line) ===
'<!-- language file start -->';
271 $parts = explode(
'#:#', trim($line));
272 if (count($parts) >= 3 && $parts[0] ===
$module) {
278 $new_content = implode(
'', $kept);
280 if ($removed > 0 && !str_ends_with($content,
"\n") && str_ends_with($new_content,
"\n")) {
281 $new_content = substr($new_content, 0, str_ends_with($new_content,
"\r\n") ? -2 : -1);
284 return [$new_content, $removed];
299 if ($plural_forms->getCount() < 2) {
302 $index = $plural_forms->formIndexFor(1);
303 foreach ([...range(0, 200), 1000, 1001, 1011, 100000, 1000000, 1000001, 2000000] as $n) {
304 if ($n !== 1 && $plural_forms->formIndexFor($n) === $index) {
327 $singular_key = $definition[
'singular'] ??
null;
329 ($entries[$key][
'value'] ??
'') ===
''
330 || $singular_key ===
null
331 || ($entries[$singular_key][
'value'] ??
'') ===
''
332 || isset($filled_keys[$singular_key])
336 $singular_comment = $entries[$singular_key][
'comment'] ??
null;
356 $forms = array_fill(0, $plural_forms->getCount(), $entries[$key][
'value'] ??
'');
357 $singular_form =
$singular_form_of($key, $definition, $entries, $plural_forms, $filled_keys);
358 if ($singular_form !==
null) {
359 $forms[$singular_form] = $entries[$definition[
'singular']][
'value'];
379 $own_forms = $plural_forms->getCount()
380 - (
$singular_form_of($key, $definition, $entries, $plural_forms, $filled_keys) ===
null ? 0 : 1);
382 return ($entries[$key][
'value'] ??
'') !==
'' && $own_forms > 1;
398 ?array $translation_entries,
402 ?
string $language_name =
null,
403 array $filled_keys = [],
404 bool $is_source_language = false
406 $is_template = $translation_entries ===
null;
409 $catalog->setHeader($name, $value);
411 if (!$is_template && $plural_forms !==
null) {
412 $catalog->setHeader(
'Plural-Forms', $plural_forms->getHeader());
414 $catalog->setHeader(
'MIME-Version',
'1.0');
415 $catalog->setHeader(
'Content-Type',
'text/plain; charset=UTF-8');
416 $catalog->setHeader(
'Content-Transfer-Encoding',
'8bit');
417 $catalog->setHeader(
'X-Domain',
$module);
419 $catalog->setHeader(
'Language', $lang_key);
421 if (!$is_template && $language_name !==
null) {
422 $catalog->setHeader(
'Language-Team', $language_name);
431 if ($plural_definition !==
null) {
433 $plural_definition[
'plural_id'] ?? $key .
'_plural',
434 $is_template ? [
'',
''] :
$plural_forms_of((
string) $key, $plural_definition, $translation_entries, $plural_forms, $filled_keys)
439 $own = $translation_entries[$key] ??
null;
440 if ($plural_definition ===
null) {
441 $entry->translate($own[
'value'] ??
'');
444 $own_comment = $own[
'comment'] ??
null;
445 if (isset($filled_keys[(
string) $key])) {
446 $entry->addFlag(
'fuzzy');
449 $entry->addFlag(
'fuzzy');
450 } elseif ($own_comment !==
null && $own_comment !==
'' && $own_comment !==
$comment) {
451 $entry->addTranslatorNote($own_comment);
454 if (!$is_source_language && $plural_definition !==
null &&
$copies_own_value((
string) $key, $plural_definition, $translation_entries, $plural_forms, $filled_keys)) {
455 $entry->addFlag(
'fuzzy');
460 $entry->addExtractedComment(
$comment);
463 $catalog->add($entry);
480 if ($output_argument ===
null) {
481 return 'the outputDir argument is required - the existing lang/ directory of the component that contributes the module.';
483 $resolved = realpath($output_argument);
484 if ($resolved ===
false || !is_dir($resolved)) {
485 return "outputDir '" .
$printable($output_argument) .
"' does not exist - the component's lang/ directory must exist before the run.";
490 || preg_match(
'#\A' . preg_quote(
$components,
'#') .
'/([^/]+)/([^/]+)/lang\z#', $resolved, $m) !== 1
492 return "outputDir '" .
$printable($output_argument) .
"' is not components/<Vendor>/<Component>/lang of this repository.";
494 $component_file =
"$components/{$m[1]}/{$m[2]}/{$m[2]}.php";
495 $source = is_file($component_file) ? file_get_contents($component_file) :
false;
496 if ($source ===
false) {
497 return "the component class $component_file was not found.";
500 foreach (token_get_all($source) as
$token) {
501 if (!is_array(
$token) || !in_array(
$token[0], [T_COMMENT, T_DOC_COMMENT],
true)) {
505 $class =
'(?:\\\\?(?:\w+\\\\)*)?';
506 $string =
static fn(
int $quote,
int $text):
string =>
"(['\"])(?<t$text>[^'\"]*)\\$quote";
508 '/\$contribute\s*\[\s*' . $class .
'LanguageFileDirectory::class\s*\]\s*=[^;]*?new\s+' . $class
509 .
'ComponentLanguageFileDirectory\s*\(\s*\$this\s*,\s*' . $string(1, 1)
510 .
'\s*(?:,\s*' . $string(3, 2) .
'\s*)?(?:,\s*' . $string(5, 3) .
'\s*)?,?\s*\)/',
515 foreach ($contributions as $contribution) {
516 if ($contribution[
't1'] !==
$module) {
519 $path_inside = ($contribution[
't2'] ??
'') ===
'' ?
'lang/' : $contribution[
't2'];
520 $contributed_pattern = ($contribution[
't3'] ??
'') ===
'' ?
$module .
'_%s' : $contribution[
't3'];
521 if (trim($path_inside,
'/') !==
'lang') {
522 return "$component_file contributes module '$module' from '" .
$printable($path_inside) .
"', not from lang/.";
524 if ($contributed_pattern !==
$pattern) {
525 return "$component_file contributes module '$module' with the file name pattern '" .
$printable($contributed_pattern)
532 return "$component_file contributes no ComponentLanguageFileDirectory for module '$module' (expected: "
533 .
"\$contribute[LanguageFileDirectory::class] = fn() => new ComponentLanguageFileDirectory(\$this, '$module'); "
534 .
"register the contribution first).";
539 return is_file($file) ? TranslationCatalog::fromPoFile($file)->getHeaders() : [];
540 }
catch (RuntimeException
$e) {
541 fwrite(STDERR,
"WARNING: the existing $file is not readable, its headers are not kept (the file is replaced): "
542 .
$e->getMessage() .
"\n");
547$write =
static function (
string $file,
string $content):
void {
548 if (file_put_contents($file, $content) ===
false) {
549 throw new RuntimeException(
"Cannot write $file");
561foreach (array_slice($argv, 1) as $argument) {
562 if (str_starts_with($argument,
'--pattern=')) {
563 $pattern = substr($argument, strlen(
'--pattern='));
566 if ($argument ===
'--skip-unmigratable-keys') {
570 if ($argument ===
'--remove-from-lang') {
575 if (str_starts_with($argument,
'-')) {
576 fwrite(STDERR,
"FAILURE: unknown option '" .
$printable($argument) .
"' (known: --pattern=<pattern>, --skip-unmigratable-keys, --remove-from-lang).\n");
581$usage =
"Usage: php convert_module_to_po.php [--pattern=<pattern>] [--skip-unmigratable-keys] [--remove-from-lang]"
582 .
" <module> [referenceLangKey] [outputDir]\n";
584 fwrite(STDERR,
"FAILURE: the module is required. $usage");
589 fwrite(STDERR,
"FAILURE: too many arguments ('$surplus'). $usage");
594 fwrite(STDERR,
"FAILURE: outputDir '" .
$printable(
$arguments[2]) .
"' does not exist. Nothing was written.\n");
601 fwrite(STDERR,
"FAILURE: module and reference language may only consist of A-Z, a-z, 0-9 and \"_\".\n");
606 MigratedLanguageFilePaths::assertValidShippedFileNamePattern(
$pattern);
607}
catch (InvalidArgumentException
$e) {
608 fwrite(STDERR,
$e->getMessage() .
"\n");
617 if ($target_problem !==
null) {
618 fwrite(STDERR,
"FAILURE: --remove-from-lang: $target_problem Nothing was written.\n");
628 }
catch (JsonException
$e) {
629 fwrite(STDERR,
"FAILURE: $plural_config_file is no valid JSON: {$e->getMessage()}\n");
633 echo
"NOTE: no plurals.json next to the tool - no Plural-Forms headers and no plural messages are written.\n";
636foreach (
$plural_config[
'plural_forms'] ?? [] as $lang_key => $header) {
638 $plural_rules[(string) $lang_key] = PluralForms::fromHeader((
string) $header);
639 }
catch (InvalidArgumentException
$e) {
640 fwrite(STDERR,
"FAILURE: invalid Plural-Forms of '$lang_key' in $plural_config_file: {$e->getMessage()}\n");
646 static fn(mixed $definition): array => is_array($definition) ? $definition : [],
655 fwrite(STDERR,
"No lang files found under $lang_dir\n");
668 if (preg_match(
'/ilias_([a-z]+)\.lang\z/', $file, $m) !== 1) {
669 fwrite(STDERR,
"WARNING: skipping $file - no language key in its name.\n");
673 if (is_link($file) || !is_file($file)) {
678 if (!is_file($file)) {
679 fwrite(STDERR,
"WARNING: skipping $file - no regular file.\n");
693 $removal_problems[] =
"$file: " . count($lines) .
" line(s) of the module were read, but $removed would be removed";
698 'hash' => hash(
'sha256', $content),
699 'lines' => count($lines),
700 'new_content' => $new_content,
701 'removed' => $removed,
706 fwrite(STDERR,
"FAILURE: --remove-from-lang only changes regular files, these are symbolic links or no files (nothing was written):\n "
711 fwrite(STDERR,
"FAILURE: --remove-from-lang: these files have no line \"<!-- language file start -->\", the installer"
716 fwrite(STDERR,
"WARNING: skipping $file - no line \"<!-- language file start -->\", the installer does not read it.\n");
719 fwrite(STDERR,
"FAILURE: --remove-from-lang (nothing was written):\n " . implode(
"\n ",
$removal_problems) .
"\n");
726 foreach ($entries as $key => $entry) {
727 if (!mb_check_encoding((
string) $key . $entry[
'value'] . ($entry[
'comment'] ??
''),
'UTF-8')) {
728 $invalid_encoding[$lang_key][] = mb_check_encoding((
string) $key,
'UTF-8') ? (string) $key : bin2hex((
string) $key) .
' (hex)';
733 fwrite(STDERR,
"FAILURE: module '$module' has lines that are not valid UTF-8 in the source .lang files (nothing was written):\n");
735 fwrite(STDERR,
" $lang_key: " .
$format_keys($keys) .
"\n");
742if (array_sum(array_column(
$read_files,
'lines')) === 0) {
743 fwrite(STDERR,
"FAILURE: module '$module' has no lines in any of the " . count(
$read_files) .
" lang/ilias_*.lang files under $lang_dir"
744 .
" - most likely it is migrated already (its lines removed with --remove-from-lang), or the module name is misspelled."
745 .
" Nothing was written; existing .po/.pot files are unchanged.\n");
750 fwrite(STDERR,
"Reference language '$reference_lang_key' not found.\n");
760 foreach ($keys as $key => $identical) {
761 $left_out = (string) $key ===
'' || str_contains((
string) $key,
' [') || !in_array((
string) $key,
$reference_keys,
true);
770 fwrite(STDERR,
"FAILURE: module '$module' has duplicate identifiers in the source .lang files (nothing was written):\n");
775 fwrite(STDERR,
" (--skip-unmigratable-keys keeps one occurrence of a key whose occurrences all have the same value)\n");
780 fwrite(STDERR,
"WARNING: module '$module' has duplicate identifiers in the source .lang files - all occurrences the same, one is used; or left out anyway, see below (--skip-unmigratable-keys):\n");
790 $keys = array_map(
'strval', array_keys($entries));
792 if (isset($entries[
''])) {
793 $unmigratable = array_values(array_unique([
'', ...$unmigratable]));
795 $form_like = array_filter($keys,
static fn(
string $key):
bool => str_contains($key,
' ['));
796 $unmigratable = array_values(array_unique([...$unmigratable, ...$form_like]));
797 if ($unmigratable !== []) {
802 fwrite(STDERR,
"FAILURE: module '$module' has keys that the reference language '$reference_lang_key' does not have, the empty key, or a key containing \" [\" (the syntax of a plural form) (nothing was written):\n");
804 fwrite(STDERR,
" $lang_key: " .
$format_keys($keys) .
"\n");
806 fwrite(STDERR,
" (--skip-unmigratable-keys leaves these keys out instead)\n");
810 fwrite(STDERR,
"WARNING: module '$module' has keys that the reference language '$reference_lang_key' does not have, the empty key, or a key containing \" [\" (the syntax of a plural form) - left out (--skip-unmigratable-keys):\n");
812 fwrite(STDERR,
" $lang_key: " .
$format_keys($keys) .
"\n");
813 foreach ($keys as $key) {
824 $languages_with_lines = array_keys(array_filter(
$read_files,
static fn(array $read):
bool => $read[
'lines'] > 0));
825 fwrite(STDERR,
"FAILURE: module '$module' has no (migratable) entries in the reference language '$reference_lang_key',"
826 .
" but lines in " . implode(
', ', $languages_with_lines) .
". Nothing was written.\n");
835 foreach ($entries as $key => $entry) {
837 if ($violations === []) {
846 !$emptied =>
'kept, the build cleans it',
848 =>
'empty once cleaned, left empty like a missing value',
849 default =>
'empty once cleaned, replaced by the reference value (fuzzy)',
855 fwrite(STDERR,
"WARNING: module '$module': values with markup the build removes - $message:\n"
856 . implode(
"\n", $lines) .
"\n");
866 $key = (string) $key;
867 if ($ref[
'value'] !==
'' && ($entries[$key][
'value'] ??
'') ===
'') {
868 $per_language[$lang_key][$key] = [
'value' => $ref[
'value'],
'comment' => $entries[$key][
'comment'] ??
null];
869 $filled[$lang_key][$key] =
true;
879 if (isset($definition[
'singular']) && !isset(
$reference_entries[$definition[
'singular']])) {
880 $plural_problems[] =
"singular key '{$definition['singular']}' of '$key' is no key of the module";
891 fwrite(STDERR,
"FAILURE: plurals.json does not fit module '$module' (nothing was written):\n " . implode(
"\n ", array_map(
$printable,
$plural_problems)) .
"\n");
899 $name = trim($entry[
'value']);
900 if (str_starts_with((
string) $key,
'meta_l_') && $name !==
'') {
907 foreach (glob(
$repo_root .
'/components/*/*/lang/meta_en.po') ?: [] as $meta_po) {
909 foreach (TranslationCatalog::fromPoFile($meta_po)->getEntries() as $entry) {
910 $name = trim($entry->getTranslation());
911 if (in_array($entry->getContext(), [
null,
'meta'],
true) && str_starts_with($entry->getId(),
'meta_l_') && $name !==
'') {
915 }
catch (RuntimeException
$e) {
916 fwrite(STDERR,
"WARNING: $meta_po cannot be read for the language names: {$e->getMessage()}\n");
922 echo
"NOTE: no meta_l_$lang_key in lang/ilias_en.lang (or a shipped meta_en.po) - the Language-Team header of '$lang_key' is left as it is.\n";
933}
catch (RuntimeException
$e) {
934 fwrite(STDERR,
"FAILURE: the template of module '$module' cannot be written: {$e->getMessage()} Nothing was written.\n");
954 $parsed = TranslationCatalog::fromPoString(
$contents[$po_path]);
955 }
catch (RuntimeException
$e) {
957 $po_mismatches[] =
'(not readable back) ' .
$e->getMessage();
961 $expected_keys = array_unique(array_merge(
963 array_map(
'strval', array_keys($entries))
965 foreach ($parsed !==
null ? $expected_keys : [] as $key) {
966 $expected = $entries[$key][
'value'] ??
'';
967 $parsed_entry = $parsed->find(
null, $key);
969 if ($parsed_entry?->getContext() !==
null) {
970 $po_mismatches[] = $key;
973 if (isset(
$filled[$lang_key][$key]) && $parsed_entry?->hasFlag(
'fuzzy') !==
true) {
974 $po_mismatches[] = $key .
' (not fuzzy)';
978 if ($plural_definition ===
null) {
979 if ($parsed_entry?->isPlural() !==
false || $parsed_entry->getTranslation() !== $expected) {
980 $po_mismatches[] = $key;
986 $parsed_forms = array_pad(array_slice($parsed_entry?->getPluralTranslations() ?? [], 0, $rule->getCount()), $rule->getCount(),
'');
988 $parsed_entry?->getPluralId() !== ($plural_definition[
'plural_id'] ?? $key .
'_plural')
989 || count($expected_forms) !== $rule->getCount()
990 || $parsed_forms !== $expected_forms
991 || $rule->defaultValueOf($parsed_forms) !== $expected
993 $po_mismatches[] = $key .
' (plural)';
996 foreach ($parsed?->getEntries() ?? [] as $parsed_entry) {
997 if ($parsed_entry->getContext() !==
null || !in_array($parsed_entry->getId(), $expected_keys,
true)) {
998 $po_mismatches[] =
'(unexpected) ' . $parsed_entry->getId();
1004 $has_known_markup =
static fn(
string $message): bool => isset($known_markup[
$message])
1010 static function (
string $identifier) use (&$po_mismatches, $has_known_markup): void {
1011 if (!$has_known_markup(
PluralFormKey::parse($identifier)[0] ?? $identifier)) {
1012 $po_mismatches[] =
'(markup) ' . $identifier;
1015 static function (
string $message) use (&$po_mismatches):
void {
1016 $po_mismatches[] =
'(build) ' .
$message;
1019 }
catch (RuntimeException
$e) {
1021 $po_mismatches[] =
'(not compilable) ' .
$e->getMessage();
1023 foreach ($expected_keys as $key) {
1026 && !$has_known_markup($key)
1027 && ($compiled[
'values'][$key] ??
'') !== ($entries[$key][
'value'] ??
'')
1029 $po_mismatches[] = $key .
' (build)';
1034 foreach ($entries as
$e) {
1041 'lang' => $lang_key,
1043 'fuzzy' => $fuzzy_count,
1044 'filled' => count(
$filled[$lang_key] ?? []),
1045 'po_ok' => $po_mismatches === [],
1046 'po_mismatches' => $po_mismatches,
1052printf(
"%-6s %8s %8s %8s %8s\n",
'lang',
'entries',
'fuzzy',
'filled',
'po_ok');
1055 printf(
"%-6s %8d %8d %8d %8s\n", $row[
'lang'], $row[
'entries'], $row[
'fuzzy'], $row[
'filled'], $row[
'po_ok'] ?
'yes' :
'NO');
1056 if (!$row[
'po_ok']) {
1058 fwrite(STDERR,
" po_mismatches: " .
$printable(implode(
', ', $row[
'po_mismatches'])) .
"\n");
1064 fwrite(STDERR,
"FAILURE: at least one language did not round-trip correctly, see stderr above. Nothing was written.\n");
1069 fwrite(STDERR,
"FAILURE: cannot create $output_dir. Nothing was written.\n");
1073foreach (
$contents as $path => $content) {
1076 }
catch (RuntimeException
$e) {
1077 fwrite(STDERR,
"FAILURE: {$e->getMessage()} - written already: " . (
$written === [] ?
'nothing' : implode(
', ',
$written))
1078 .
". No .lang file was changed.\n");
1083echo
"OK: all " . count(
$report) .
" languages round-trip identically through PO ("
1084 . array_sum(array_column(
$report,
'filled')) .
" entries filled from '$reference_lang_key', flagged fuzzy).\n";
1094 $content = file_get_contents($read[
'path']);
1095 if ($content ===
false || hash(
'sha256', $content) !== $read[
'hash']) {
1099 if ($read[
'removed'] > 0) {
1104 fwrite(STDERR,
"FAILURE: --remove-from-lang: no .lang file was changed (the .po/.pot files are written), these were"
1105 .
" changed during the run or cannot be read again:\n " . implode(
"\n ",
$changed_files) .
"\n");
1114 AtomicFileWriter::write($path, $new_content,
$lang_dir);
1115 }
catch (RuntimeException
$e) {
1116 fwrite(STDERR,
"FAILURE: --remove-from-lang: {$e->getMessage()} - the files listed above are changed already, the others not.\n");
1120 printf(
"removed %5d line(s) of '%s' from %s\n", $removed,
$module,
'lang/' . basename($path));
1122echo
"OK: removed $removed_total line(s) of module '$module' from " . count(
$new_lang_contents) .
" .lang file(s).\n";
Writes a file so that concurrent readers only see the complete old or new content: a temporary file i...
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).
The dated "not translated yet" marker of the legacy .lang files ("28 08 2012 new variable") in a ### ...
The single place that knows where the files of a module migrated to PO/MO live:
Compiles the messages of a migrated module into the .mo native gettext serves (see NativeGettext): th...
The HTML a translation value may contain, decided in one place for the shipped files (cleaned in Setu...
$body_lines
The lines of $content the installer reads (see LanguageFileParsing::cutHeader()).
$copies_own_value
Whether the own value of the plural message $key was copied into several forms (flagged fuzzy,...
$find_duplicate_keys
A legacy .lang file can define the same identifier twice for one module (uniqueness validation of the...
if($lang_files===[]) $per_language
foreach($plural_definitions as $key=> $definition) if( $plural_definitions !==[]) if($plural_problems !==[]) $language_names
foreach( $per_language as $lang_key=> $entries) if($invalid_encoding !==[]) if(array_sum(array_column( $read_files, 'lines'))===0) if(!isset($per_language[$reference_lang_key])) $reference_keys
if($remove_from_lang) $plural_config_file
catch(InvalidArgumentException $e) $repo_root
$without_module_lines
$content without the lines of $module - exactly those $read_lines reports, and only behind the line "...
if(!isset($arguments[0])) if(count( $arguments) > 3) if(! $remove_from_lang &&isset($arguments[2]) &&!is_dir($arguments[2])) $module
$singular_form_index
The form that serves n = 1 if it serves no other n, else null.
foreach($lang_files as $file) if( $unsafe_files !==[]) if($files_without_marker !==[] && $remove_from_lang) foreach( $files_without_marker as $file) if($removal_problems !==[]) $invalid_encoding
foreach($per_language as $lang_key=> $entries) if( $unmigratable_keys !==[] &&! $skip_unmigratable_keys) if($unmigratable_keys !==[]) $reference_entries
$removal_target_problem
Why $output_argument is no place –remove-from-lang may rely on, or null: it must be the existing comp...
$identical_duplicate_report
if(! $all_ok) if(!is_dir($output_dir) &&! @mkdir($output_dir, 0775, true) &&!is_dir($output_dir)) $written
foreach($per_language as $lang_key=> $entries) $plural_problems
$read_file
Read once, so the lines and the hash checked by –remove-from-lang come from the same bytes.
$singular_form_of
The form of the plural message $key that gets the singular key's value, or null: only if the singular...
foreach( $per_language as $lang_key=> $entries) foreach($markup_messages as $message=> $lines) $own_entry_counts
if(is_file($english_lang_file)) if( $language_names===[]) foreach(array_keys($per_language) as $lang_key) $pot_path
$printable
Makes control characters (e.g.
$is_fuzzy_marker
Whether a ### comment is the dated "not translated yet" marker (-> "#, fuzzy") rather than a note (->...
if($reference_entries===[]) $markup_policy
foreach(array_slice($argv, 1) as $argument) $usage
foreach($duplicates as $lang_key=> $keys) if( $duplicate_report !==[]) if($identical_duplicate_report !==[]) $unmigratable_keys
if(! $remove_from_lang) $new_lang_contents
if(PHP_SAPI !=='cli')
Pilot conversion tool for the PO/MO migration.
exit
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...