. /** * Helper functions converting moodle strings to json. */ function detect_languages($languages) { echo "\n\n\n"; $all_languages = glob(LANGPACKSFOLDER.'/*' , GLOB_ONLYDIR); function get_lang_from_dir($dir) { return str_replace('_', '-', explode('/', $dir)[3]); } function get_lang_not_wp($langname) { return (substr($langname, -3) !== '-wp'); } $all_languages = array_map('get_lang_from_dir', $all_languages); $all_languages = array_filter($all_languages, 'get_lang_not_wp'); $detect_lang = array_diff($all_languages, $languages); $new_langs = []; foreach ($detect_lang as $lang) { $new = detect_lang($lang); if ($new) { $new_langs[$lang] = $lang; } } return $new_langs; } function build_languages($languages, $added_langs = []) { // Process the languages. foreach ($languages as $lang) { if (build_lang($lang)) { $added_langs[$lang] = $lang; } } return $added_langs; } /** * Loads lang index keys. */ function load_langindex() { global $STATS; global $LANGINDEX; $local = 0; $total = 0; // Process the index file, just once. $langindexjson = load_json('langindex.json'); $LANGINDEX = []; foreach ($langindexjson as $appkey => $value) { if ($value == APPMODULENAME) { $file = $value; $lmskey = $appkey; $local++; } else { $exp = explode('/', $value, 2); $file = $exp[0]; if (count($exp) == 2) { $lmskey = $exp[1]; } else { $exp = explode('.', $appkey, 3); if (count($exp) == 3) { $lmskey = $exp[2]; } else { $lmskey = $exp[1]; } } } if (!isset($LANGINDEX[$file])) { $LANGINDEX[$file] = []; } $LANGINDEX[$file][$appkey] = $lmskey; $total++; } $STATS = new StdClass(); $STATS->local = $local; $STATS->total = $total; 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) { $changed = false; $config_langs = get_object_vars($config['languages']); foreach ($langs as $lang) { if (!isset($config_langs[$lang])) { $langfoldername = get_langfolder($lang); $lmsstring = get_translation_strings($langfoldername, 'langconfig'); $config['languages']->$lang = $lmsstring['thislanguage']; $changed = true; } } if ($changed) { // Sort languages by key. $config['languages'] = json_decode( json_encode( $config['languages'] ), true ); ksort($config['languages']); $config['languages'] = json_decode( json_encode( $config['languages'] ), false ); save_json(CONFIG, $config); } } /** * Save json data. * * @param $path Path of the file to load. * @param $content Content string to save. */ function save_json($path, $content) { 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) { $folder = LANGPACKSFOLDER.'/'.str_replace('-', '_', $lang); if (!is_dir($folder) || !is_file($folder.'/langconfig.php')) { return false; } 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) { $lmsstring = import_translation_strings($langfoldername, $file); if ($override_folder) { $override = import_translation_strings($override_folder, $file); $lmsstring = array_merge($lmsstring, $override); } return $lmsstring; } /** * 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) { $path = $langfoldername.'/'.$file.'.php'; // Apply translations. if (!file_exists($path)) { return []; } $string = []; include($path); return $string; } /** * Build translations files from langpack. * * @param lang Language code. * @return Wether it succeeded. */ function build_lang($lang) { global $STATS; global $LANGINDEX; $langfoldername = get_langfolder($lang); if (!$langfoldername) { echo "Cannot translate $lang, folder not found"; return false; } if (OVERRIDE_LANG_SUFIX) { $override_langfolder = get_langfolder($lang.OVERRIDE_LANG_SUFIX); } else { $override_langfolder = false; } $total = $STATS->total; $local = 0; $langparts = explode('-', $lang, 2); $parentname = $langparts[0] ? $langparts[0] : ""; $parent = ""; echo "Processing $lang"; // Check parent language exists. if ($parentname != $lang && get_langfolder($parentname)) { echo " ($parentname)"; $parent = $parentname; } $langFile = false; if (file_exists(ASSETSPATH.$lang.'.json')) { // Load lang files just once. $langFile = load_json(ASSETSPATH.$lang.'.json'); } $translations = []; // Add the translation to the array. foreach ($LANGINDEX as $file => $keys) { $lmsstring = get_translation_strings($langfoldername, $file, $override_langfolder); foreach ($keys as $appkey => $lmskey) { // Apply translations. if (empty($lmsstring)) { if ($file == 'donottranslate') { // Restore it form the json. if ($langFile && is_array($langFile) && isset($langFile[$appkey])) { $translations[$appkey] = $langFile[$appkey]; } else { // If not present, do not count it in the total. $total--; } continue; } if (TOTRANSLATE) { echo "\n\t\tTo translate $lmskey on $file"; } continue; } if (!isset($lmsstring[$lmskey]) || ($lang == 'en' && $file == APPMODULENAME)) { // Not yet translated. Do not override. if ($langFile && is_array($langFile) && isset($langFile[$appkey])) { $translations[$appkey] = $langFile[$appkey]; if ($file == APPMODULENAME) { $local++; } } if (TOTRANSLATE && !isset($lmsstring[$lmskey])) { echo "\n\t\tTo translate $lmskey on $file"; } continue; } $text = $lmsstring[$lmskey]; 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('/@@.+?@@(
)?\\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)) { $translations['core.parentlanguage'] = $parent; } else if (isset($translations['core.parentlanguage'])) { unset($translations['core.parentlanguage']); } // Sort and save. ksort($translations); save_json(ASSETSPATH.$lang.'.json', $translations); $success = count($translations); $percentage = floor($success/$total * 100); $bar = progressbar($percentage); if (strlen($lang) <= 2 && !$parent) { echo "\t"; } echo "\t\t$success of $total -> $percentage% $bar ($local local)\n"; if ($lang == 'en') { generate_local_module_file($LANGINDEX[APPMODULENAME], $translations); override_component_lang_files($translations); } return true; } /** * Generates an ASCII progress bar. * * @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); } /** * 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); if (!$langfoldername) { echo "Cannot translate $lang, folder not found"; return false; } $total = $STATS->total; $success = 0; $local = 0; $lmsstring = get_translation_strings($langfoldername, 'langconfig'); $parent = isset($lmsstring['parentlanguage']) ? $lmsstring['parentlanguage'] : ""; if (!isset($lmsstring['thislanguage'])) { echo "Cannot translate $lang, translated name not found"; return false; } $title = $lang; if ($parent != "" && $parent != $lang) { $title .= " ($parent)"; } $langname = $lmsstring['thislanguage']; $title .= " ".$langname." -D"; $lmsstring = get_translation_strings($langfoldername, APPMODULENAME); if (!empty($lmsstring)) { // Add the translation to the array. foreach ($LANGINDEX as $file => $keys) { $lmsstring = get_translation_strings($langfoldername, $file); // Apply translations. if (empty($lmsstring)) { // Do not count non translatable in the totals. if ($file == 'donottranslate') { $total -= count($keys); } continue; } foreach ($keys as $lmskey) { if (!isset($lmsstring[$lmskey])) { continue; } if ($file == APPMODULENAME) { $local++; } $success++; } } } echo "Checking ".$title.str_repeat("\t", 7 - floor(mb_strlen($title, 'UTF-8')/8)); if ($local == 0) { echo "\tNo Mobile App strings found\n"; } 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"; } 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) { $file = load_json($filePath); $value = html_entity_decode($value); if (!isset($file[$key]) || $file[$key] != $value) { $file[$key] = $value; ksort($file); save_json($filePath, $file); } } /** * 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"; foreach ($translations as $key => $value) { $path = '../src/'; $exp = explode('.', $key, 3); $type = $exp[0]; if (count($exp) == 3) { $component = $exp[1]; $plainid = $exp[2]; } else { $component = 'moodle'; $plainid = $exp[1]; } switch($type) { case 'core': switch($component) { case 'moodle': $path .= 'core/lang.json'; break; default: $path .= 'core/features/'.str_replace('_', '/', $component).'/lang.json'; break; } break; case 'addon': $path .= 'addons/'.str_replace('_', '/', $component).'/lang.json'; break; case 'assets': $path .= $type.'/'.$component.'.json'; break; default: $path .= $type.'/lang.json'; break; } if (is_file($path)) { save_key($plainid, $value, $path); } else { echo "Cannot override: $path not found.\n"; } } } /** * Generates local module file to update languages in AMOS. * * @param [array] $appindex Translation appindex. * @param [array] $translations English translations. */ function generate_local_module_file($appindex, $translations) { echo 'Generate '.APPMODULENAME."\n"; $lmsstring = ""; foreach ($appindex as $appkey => $lmskey) { if (isset($translations[$appkey])) { $lmsstring .= '$string[\''.$appkey.'\'] = \''.str_replace("'", "\'", $translations[$appkey]).'\';'."\n"; } } if (empty($lmsstring)) { echo "ERROR, translations not found, you probably didn't run gulp lang!\n"; return; } $filepath = '../../moodle-'.APPMODULENAME.'/lang/en/'.APPMODULENAME.'.php'; $filecontents = file_get_contents($filepath); $startcomment = "/* AUTO START */\n"; $endcomment = '/* AUTO END */'; $start = strpos($filecontents, $startcomment); $start = $start === false ? 0 : $start + strlen($startcomment); $end = strpos($filecontents, $endcomment, $start); $end = $end === false ? strlen($filecontents) : $end; $filecontents = substr_replace($filecontents, $lmsstring, $start, $end - $start); file_put_contents($filepath, $filecontents); }