MOBILE-4081 lang: Improve lang scripts performance

main
Pau Ferrer Ocaña 2022-06-17 13:15:16 +02:00
parent 9962183fea
commit ce2b3d868d
2 changed files with 241 additions and 161 deletions

View File

@ -18,7 +18,7 @@
* Helper functions converting moodle strings to json. * Helper functions converting moodle strings to json.
*/ */
function detect_languages($languages, $keys) { function detect_languages($languages) {
echo "\n\n\n"; echo "\n\n\n";
$all_languages = glob(LANGPACKSFOLDER.'/*' , GLOB_ONLYDIR); $all_languages = glob(LANGPACKSFOLDER.'/*' , GLOB_ONLYDIR);
@ -32,10 +32,9 @@ function detect_languages($languages, $keys) {
$all_languages = array_filter($all_languages, 'get_lang_not_wp'); $all_languages = array_filter($all_languages, 'get_lang_not_wp');
$detect_lang = array_diff($all_languages, $languages); $detect_lang = array_diff($all_languages, $languages);
$new_langs = array(); $new_langs = [];
foreach ($detect_lang as $lang) { foreach ($detect_lang as $lang) {
reset_translations_strings(); $new = detect_lang($lang);
$new = detect_lang($lang, $keys);
if ($new) { if ($new) {
$new_langs[$lang] = $lang; $new_langs[$lang] = $lang;
} }
@ -44,12 +43,10 @@ function detect_languages($languages, $keys) {
return $new_langs; return $new_langs;
} }
function build_languages($languages, $keys, $added_langs = []) { function build_languages($languages, $added_langs = []) {
// Process the languages. // Process the languages.
foreach ($languages as $lang) { foreach ($languages as $lang) {
reset_translations_strings(); if (build_lang($lang)) {
$ok = build_lang($lang, $keys);
if ($ok) {
$added_langs[$lang] = $lang; $added_langs[$lang] = $lang;
} }
} }
@ -57,53 +54,70 @@ function build_languages($languages, $keys, $added_langs = []) {
return $added_langs; return $added_langs;
} }
function get_langindex_keys() { /**
$local = 0; * Loads lang index keys.
// Process the index file, just once. */
$keys = file_get_contents('langindex.json'); function load_langindex() {
$keys = (array) json_decode($keys); global $STATS;
global $LANGINDEX;
foreach ($keys as $key => $value) { $local = 0;
$map = new StdClass(); $total = 0;
if ($value == 'local_moodlemobileapp') { // Process the index file, just once.
$map->file = $value; $langindexjson = load_json('langindex.json');
$map->string = $key;
$LANGINDEX = [];
foreach ($langindexjson as $appkey => $value) {
if ($value == APPMODULENAME) {
$file = $value;
$lmskey = $appkey;
$local++; $local++;
} else { } else {
$exp = explode('/', $value, 2); $exp = explode('/', $value, 2);
$map->file = $exp[0]; $file = $exp[0];
if (count($exp) == 2) { if (count($exp) == 2) {
$map->string = $exp[1]; $lmskey = $exp[1];
} else { } else {
$exp = explode('.', $key, 3); $exp = explode('.', $appkey, 3);
if (count($exp) == 3) { if (count($exp) == 3) {
$map->string = $exp[2]; $lmskey = $exp[2];
} else { } else {
$map->string = $exp[1]; $lmskey = $exp[1];
} }
} }
} }
$keys[$key] = $map; if (!isset($LANGINDEX[$file])) {
$LANGINDEX[$file] = [];
}
$LANGINDEX[$file][$appkey] = $lmskey;
$total++;
} }
$total = count($keys); $STATS = new StdClass();
echo "Total strings to translate $total ($local local)\n"; $STATS->local = $local;
$STATS->total = $total;
return $keys; echo "Total strings to translate $total ($local local)\n";
} }
/**
* Add lang names to config file.
*
* @param $langs Array of language codes to add.
* @param $config Loaded config file.
*/
function add_langs_to_config($langs, $config) { function add_langs_to_config($langs, $config) {
$changed = false; $changed = false;
$config_langs = get_object_vars($config['languages']); $config_langs = get_object_vars($config['languages']);
foreach ($langs as $lang) { foreach ($langs as $lang) {
if (!isset($config_langs[$lang])) { if (!isset($config_langs[$lang])) {
$langfoldername = str_replace('-', '_', $lang); $langfoldername = get_langfolder($lang);
$string = []; $lmsstring = get_translation_strings($langfoldername, 'langconfig');
include(LANGPACKSFOLDER.'/'.$langfoldername.'/langconfig.php'); $config['languages']->$lang = $lmsstring['thislanguage'];
$config['languages']->$lang = $string['thislanguage'];
$changed = true; $changed = true;
} }
} }
@ -119,11 +133,31 @@ function add_langs_to_config($langs, $config) {
/** /**
* Save json data. * Save json data.
*
* @param $path Path of the file to load.
* @param $content Content string to save.
*/ */
function save_json($path, $content) { function save_json($path, $content) {
file_put_contents($path, str_replace('\/', '/', json_encode($content, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT))."\n"); file_put_contents($path, str_replace('\/', '/', json_encode($content, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT))."\n");
} }
/**
* Load json data.
*
* @param $path Path of the file to load.
* @return Associative array obtained from json.
*/
function load_json($path) {
$file = file_get_contents($path);
return (array) json_decode($file);
}
/**
* Get's lang folder from lang code.
*
* @param $lang Lang code.
* @return Folder path.
*/
function get_langfolder($lang) { function get_langfolder($lang) {
$folder = LANGPACKSFOLDER.'/'.str_replace('-', '_', $lang); $folder = LANGPACKSFOLDER.'/'.str_replace('-', '_', $lang);
if (!is_dir($folder) || !is_file($folder.'/langconfig.php')) { if (!is_dir($folder) || !is_file($folder.'/langconfig.php')) {
@ -133,49 +167,56 @@ function get_langfolder($lang) {
return $folder; return $folder;
} }
/**
* Import translation file from langpack and returns it.
*
* @param $langfoldername Lang folder path.
* @param $file File name (excluding extension).
* @param $override_folder If needed, the folder of the file to override strings.
* @return String array.
*/
function get_translation_strings($langfoldername, $file, $override_folder = false) { function get_translation_strings($langfoldername, $file, $override_folder = false) {
global $strings; $lmsstring = import_translation_strings($langfoldername, $file);
if ($override_folder) {
if (isset($strings[$file])) { $override = import_translation_strings($override_folder, $file);
return $strings[$file]; $lmsstring = array_merge($lmsstring, $override);
} }
$string = import_translation_strings($langfoldername, $file); return $lmsstring;
$string_override = $override_folder ? import_translation_strings($override_folder, $file) : false;
if ($string) {
$strings[$file] = $string;
if ($string_override) {
$strings[$file] = array_merge($strings[$file], $string_override);
}
} else if ($string_override) {
$strings[$file] = $string_override;
} else {
$strings[$file] = false;
}
return $strings[$file];
} }
/**
* Import translation file from langpack and returns it.
*
* @param $langfoldername Lang folder path.
* @param $file File name (excluding extension).
* @return String array.
*/
function import_translation_strings($langfoldername, $file) { function import_translation_strings($langfoldername, $file) {
$path = $langfoldername.'/'.$file.'.php'; $path = $langfoldername.'/'.$file.'.php';
// Apply translations. // Apply translations.
if (!file_exists($path)) { if (!file_exists($path)) {
return false; return [];
} }
$string = []; $string = [];
include($path); include($path);
return $string; return $string;
} }
function reset_translations_strings() { /**
global $strings; * Build translations files from langpack.
$strings = []; *
} * @param lang Language code.
* @return Wether it succeeded.
*/
function build_lang($lang) {
global $STATS;
global $LANGINDEX;
function build_lang($lang, $keys) {
$langfoldername = get_langfolder($lang); $langfoldername = get_langfolder($lang);
if (!$langfoldername) { if (!$langfoldername) {
echo "Cannot translate $lang, folder not found"; echo "Cannot translate $lang, folder not found";
@ -189,7 +230,7 @@ function build_lang($lang, $keys) {
$override_langfolder = false; $override_langfolder = false;
} }
$total = count($keys); $total = $STATS->total;
$local = 0; $local = 0;
$langparts = explode('-', $lang, 2); $langparts = explode('-', $lang, 2);
@ -204,71 +245,71 @@ function build_lang($lang, $keys) {
} }
$langFile = false; $langFile = false;
// Not yet translated. Do not override.
if (file_exists(ASSETSPATH.$lang.'.json')) { if (file_exists(ASSETSPATH.$lang.'.json')) {
// Load lang files just once. // Load lang files just once.
$langFile = file_get_contents(ASSETSPATH.$lang.'.json'); $langFile = load_json(ASSETSPATH.$lang.'.json');
$langFile = (array) json_decode($langFile);
} }
$translations = []; $translations = [];
// Add the translation to the array. // Add the translation to the array.
foreach ($keys as $key => $value) { foreach ($LANGINDEX as $file => $keys) {
$string = get_translation_strings($langfoldername, $value->file, $override_langfolder); $lmsstring = get_translation_strings($langfoldername, $file, $override_langfolder);
// Apply translations. foreach ($keys as $appkey => $lmskey) {
if (!$string) { // Apply translations.
if ($value->file == 'donottranslate') { if (empty($lmsstring)) {
// Restore it form the json. if ($file == 'donottranslate') {
if ($langFile && is_array($langFile) && isset($langFile[$key])) { // Restore it form the json.
$translations[$key] = $langFile[$key]; if ($langFile && is_array($langFile) && isset($langFile[$appkey])) {
} else { $translations[$appkey] = $langFile[$appkey];
// If not present, do not count it in the total. } else {
$total--; // If not present, do not count it in the total.
$total--;
}
continue;
} }
if (TOTRANSLATE) {
echo "\n\t\tTo translate $lmskey on $file";
}
continue; continue;
} }
if (TOTRANSLATE) { if (!isset($lmsstring[$lmskey]) || ($lang == 'en' && $file == APPMODULENAME)) {
echo "\n\t\tTo translate $value->string on $value->file"; // Not yet translated. Do not override.
} if ($langFile && is_array($langFile) && isset($langFile[$appkey])) {
continue; $translations[$appkey] = $langFile[$appkey];
}
if (!isset($string[$value->string]) || ($lang == 'en' && $value->file == 'local_moodlemobileapp')) { if ($file == APPMODULENAME) {
// Not yet translated. Do not override. $local++;
if ($langFile && is_array($langFile) && isset($langFile[$key])) { }
$translations[$key] = $langFile[$key];
if ($value->file == 'local_moodlemobileapp') {
$local++;
} }
if (TOTRANSLATE && !isset($lmsstring[$lmskey])) {
echo "\n\t\tTo translate $lmskey on $file";
}
continue;
} }
if (TOTRANSLATE && !isset($string[$value->string])) {
echo "\n\t\tTo translate $value->string on $value->file";
}
continue;
} else {
$text = $string[$value->string];
}
if ($value->file != 'local_moodlemobileapp') { $text = $lmsstring[$lmskey];
$text = str_replace('$a->@', '$a.', $text);
$text = str_replace('$a->', '$a.', $text);
$text = str_replace('{$a', '{{$a', $text);
$text = str_replace('}', '}}', $text);
$text = preg_replace('/@@.+?@@(<br>)?\\s*/', '', $text);
// Prevent double.
$text = str_replace(array('{{{', '}}}'), array('{{', '}}'), $text);
} else {
// @TODO: Remove that line when core.cannotconnect and core.login.invalidmoodleversion are completelly changed to use $a
if (($key == 'core.cannotconnect' || $key == 'core.login.invalidmoodleversion') && strpos($text, '2.4') != false) {
$text = str_replace('2.4', '{{$a}}', $text);
}
$local++;
}
$translations[$key] = html_entity_decode($text); if ($file != APPMODULENAME) {
$text = str_replace('$a->@', '$a.', $text);
$text = str_replace('$a->', '$a.', $text);
$text = str_replace('{$a', '{{$a', $text);
$text = str_replace('}', '}}', $text);
$text = preg_replace('/@@.+?@@(<br>)?\\s*/', '', $text);
// Prevent double.
$text = str_replace(['{{{', '}}}'], ['{{', '}}'], $text);
} else {
// @TODO: Remove that line when core.cannotconnect and core.login.invalidmoodleversion are completelly changed to use $a
if (($appkey == 'core.cannotconnect' || $appkey == 'core.login.invalidmoodleversion') && strpos($text, '2.4') != false) {
$text = str_replace('2.4', '{{$a}}', $text);
}
$local++;
}
$translations[$appkey] = html_entity_decode($text);
}
} }
if (!empty($parent)) { if (!empty($parent)) {
@ -290,19 +331,35 @@ function build_lang($lang, $keys) {
echo "\t\t$success of $total -> $percentage% $bar ($local local)\n"; echo "\t\t$success of $total -> $percentage% $bar ($local local)\n";
if ($lang == 'en') { if ($lang == 'en') {
generate_local_moodlemobileapp($keys, $translations); generate_local_module_file($LANGINDEX[APPMODULENAME], $translations);
override_component_lang_files($keys, $translations); override_component_lang_files($translations);
} }
return true; return true;
} }
function progressbar($percentage) { /**
$done = floor($percentage/10); * Generates an ASCII progress bar.
return "\t".str_repeat('=', $done) . str_repeat('-', 10-$done); *
* @param $percentage Done part.
* @param $length Length of the text.
* @return Text generated.
*/
function progressbar($percentage, $length = 10) {
$done = floor($percentage / $length);
return "\t".str_repeat('=', $done) . str_repeat('-', $length - $done);
} }
function detect_lang($lang, $keys) { /**
* Check translations on langpack and detects if the language should be added.
*
* @param lang Language code.
* @return If the file should be added to the app.
*/
function detect_lang($lang) {
global $STATS;
global $LANGINDEX;
$langfoldername = get_langfolder($lang); $langfoldername = get_langfolder($lang);
if (!$langfoldername) { if (!$langfoldername) {
echo "Cannot translate $lang, folder not found"; echo "Cannot translate $lang, folder not found";
@ -310,13 +367,13 @@ function detect_lang($lang, $keys) {
return false; return false;
} }
$total = count ($keys); $total = $STATS->total;
$success = 0; $success = 0;
$local = 0; $local = 0;
$string = get_translation_strings($langfoldername, 'langconfig'); $lmsstring = get_translation_strings($langfoldername, 'langconfig');
$parent = isset($string['parentlanguage']) ? $string['parentlanguage'] : ""; $parent = isset($lmsstring['parentlanguage']) ? $lmsstring['parentlanguage'] : "";
if (!isset($string['thislanguage'])) { if (!isset($lmsstring['thislanguage'])) {
echo "Cannot translate $lang, translated name not found"; echo "Cannot translate $lang, translated name not found";
return false; return false;
} }
@ -325,51 +382,66 @@ function detect_lang($lang, $keys) {
if ($parent != "" && $parent != $lang) { if ($parent != "" && $parent != $lang) {
$title .= " ($parent)"; $title .= " ($parent)";
} }
$langname = $string['thislanguage']; $langname = $lmsstring['thislanguage'];
$title .= " ".$langname." -D"; $title .= " ".$langname." -D";
// Add the translation to the array. $lmsstring = get_translation_strings($langfoldername, APPMODULENAME);
foreach ($keys as $key => $value) { if (!empty($lmsstring)) {
$string = get_translation_strings($langfoldername, $value->file); // Add the translation to the array.
// Apply translations. foreach ($LANGINDEX as $file => $keys) {
if (!$string) { $lmsstring = get_translation_strings($langfoldername, $file);
// Do not count non translatable in the totals.
if ($value->file == 'donottranslate') { // Apply translations.
$total--; if (empty($lmsstring)) {
// Do not count non translatable in the totals.
if ($file == 'donottranslate') {
$total -= count($keys);
}
continue;
} }
continue;
}
if (!isset($string[$value->string])) { foreach ($keys as $lmskey) {
continue; if (!isset($lmsstring[$lmskey])) {
} else { continue;
$text = $string[$value->string]; }
}
if ($value->file == 'local_moodlemobileapp') { if ($file == APPMODULENAME) {
$local++; $local++;
} }
$success++; $success++;
}
}
} }
$percentage = floor($success/$total * 100);
$bar = progressbar($percentage);
echo "Checking ".$title.str_repeat("\t", 7 - floor(mb_strlen($title, 'UTF-8')/8)); echo "Checking ".$title.str_repeat("\t", 7 - floor(mb_strlen($title, 'UTF-8')/8));
echo "\t$success of $total -> $percentage% $bar ($local local)";
if (($percentage > 75 && $local > 50) || ($percentage > 50 && $local > 75)) { if ($local == 0) {
echo " \t DETECTED\n"; echo "\tNo Mobile App strings found\n";
return true; } else {
$percentage = floor($success/$total * 100);
$bar = progressbar($percentage);
echo "\t$success of $total -> $percentage% $bar ($local local)";
if (($percentage > 75 && $local > 50) || ($percentage > 50 && $local > 75)) {
echo " \t DETECTED\n";
return true;
}
echo "\n";
} }
echo "\n";
return false; return false;
} }
/**
* Save a key - value pair into a json file.
*
* @param key Key of the json object.
* @param value Value of the json object.
* @param filePath Path of the json file.
*/
function save_key($key, $value, $filePath) { function save_key($key, $value, $filePath) {
$file = file_get_contents($filePath); $file = load_json($filePath);
$file = (array) json_decode($file);
$value = html_entity_decode($value); $value = html_entity_decode($value);
if (!isset($file[$key]) || $file[$key] != $value) { if (!isset($file[$key]) || $file[$key] != $value) {
$file[$key] = $value; $file[$key] = $value;
@ -378,7 +450,12 @@ function save_key($key, $value, $filePath) {
} }
} }
function override_component_lang_files($keys, $translations) { /**
* Take newer ENGLISH translations from the langpacks and applies it to the app lang.json files.
*
* @param [array] $translations English translations.
*/
function override_component_lang_files($translations) {
echo "Override component lang files.\n"; echo "Override component lang files.\n";
foreach ($translations as $key => $value) { foreach ($translations as $key => $value) {
$path = '../src/'; $path = '../src/';
@ -424,14 +501,14 @@ function override_component_lang_files($keys, $translations) {
} }
/** /**
* Generates local moodle mobile app file to update languages in AMOS. * Generates local module file to update languages in AMOS.
* *
* @param [array] $keys Translation keys. * @param [array] $appindex Translation appindex.
* @param [array] $translations English translations. * @param [array] $translations English translations.
*/ */
function generate_local_moodlemobileapp($keys, $translations) { function generate_local_module_file($appindex, $translations) {
echo "Generate local_moodlemobileapp.\n"; echo "Generate local_moodlemobileapp.\n";
$string = '<?php $lmsstring = '<?php
// This file is part of Moodle - http://moodle.org/ // This file is part of Moodle - http://moodle.org/
// //
// Moodle is free software: you can redistribute it and/or modify // Moodle is free software: you can redistribute it and/or modify
@ -477,12 +554,14 @@ Read and modify the contents of your SD card - Contents are downloaded to the SD
Network access - To be able to connect with your Moodle site and check if you are connected or not to switch to offline mode Network access - To be able to connect with your Moodle site and check if you are connected or not to switch to offline mode
Run at startup - So you receive local notifications even when the app is running in the background Run at startup - So you receive local notifications even when the app is running in the background
Prevent phone from sleeping - So you can receive push notifications anytime\';'."\n"; Prevent phone from sleeping - So you can receive push notifications anytime\';'."\n";
foreach ($keys as $key => $value) { foreach ($appindex as $appkey => $lmskey) {
if (isset($translations[$key]) && $value->file == 'local_moodlemobileapp') { if (isset($translations[$appkey])) {
$string .= '$string[\''.$key.'\'] = \''.str_replace("'", "\'", $translations[$key]).'\';'."\n"; $lmsstring .= '$string[\''.$appkey.'\'] = \''.str_replace("'", "\'", $translations[$appkey]).'\';'."\n";
} }
} }
$string .= '$string[\'pluginname\'] = \'Moodle Mobile language strings\';'."\n"; $lmsstring .= '$string[\'pluginname\'] = \'Moodle Mobile language strings\';'."\n";
file_put_contents('../../moodle-local_moodlemobileapp/lang/en/local_moodlemobileapp.php', $string."\n"); $filepath = '../../moodle-'.APPMODULENAME.'/lang/en/'.APPMODULENAME.'.php';
file_put_contents($filepath, $lmsstring."\n");
} }

View File

@ -24,6 +24,7 @@ if (isset($_SERVER['REMOTE_ADDR'])) {
} }
define('MOODLE_INTERNAL', 1); define('MOODLE_INTERNAL', 1);
define('LANGPACKSFOLDER', '../../moodle-langpacks'); define('LANGPACKSFOLDER', '../../moodle-langpacks');
define('APPMODULENAME','local_moodlemobileapp');
define('ASSETSPATH', '../src/assets/lang/'); define('ASSETSPATH', '../src/assets/lang/');
define('CONFIG', '../moodle.config.json'); define('CONFIG', '../moodle.config.json');
define('OVERRIDE_LANG_SUFIX', false); define('OVERRIDE_LANG_SUFIX', false);
@ -51,16 +52,16 @@ if (!file_exists(ASSETSPATH)) {
} }
$keys = get_langindex_keys(); load_langindex();
$added_langs = build_languages($languages, $keys); $added_langs = build_languages($languages);
if ($forcedetect) { if ($forcedetect) {
$new_langs = detect_languages($languages, $keys); $new_langs = detect_languages($languages);
if (!empty($new_langs)) { if (!empty($new_langs)) {
echo "\n\n\nThe following languages are going to be added\n\n\n"; echo "\n\n\nThe following languages are going to be added\n\n\n";
$added_langs = build_languages($new_langs, $keys, $added_langs); $added_langs = build_languages($new_langs, $added_langs);
} }
} }