"._("Please enter translated string"); $empty_action = ( $copyoriginalvalue? _("or leave empty to copy original message"): _("or leave empty to pass") ); $question .= " (i"; if (!$copyoriginalvalue) $question .= "/c"; $question .= "/e/q/? $empty_action): "; while (true) { if ($context) fwrite(STDERR, "\n# $context\n"); fwrite(STDERR, $question); $in = trim(fgets(STDIN)); switch($in) { case 'q': // Exit interactive mode case 'Q': $interactive_exit = true; return True; case 'i': // Ignore case 'I': return True; case 'c': case 'C': // Copy if (!$copyoriginalvalue) return $msg; case 'e': case 'E': // Open editor $tmp_file = tempnam(sys_get_temp_dir(), 'ldapsaisie_translation'); if ($tmp_file === false) { fwrite(STDERR, _('Fail to create temporary file.')); break; } $fd = fopen($tmp_file, 'w'); if ($fd === false) { fwrite(STDERR, _('Fail to open temporary file.')); break; } $nb_lines = 1; if ($context) { fwrite($fd, "# $context\n"); $nb_lines++; } $lines = explode("\n", $msg); $nb_lines += count($lines); fwrite($fd, "# ".implode("\n# ", $lines)."\n\n"); fclose($fd); system("editor +$nb_lines $tmp_file > `tty`", $exit_code); $result = array(); foreach(file($tmp_file) as $line) if (!preg_match('/^# /', $line) || $result) $result[] = preg_replace("/\n$/", "", $line); unlink($tmp_file); return implode("\n", $result); case '?': // Help message fwrite(STDERR, _("Available choices:\n")); fwrite(STDERR, _(" - i: ignore this message\n")); if (!$copyoriginalvalue) fwrite(STDERR, _(" - c: copy original message\n")); fwrite(STDERR, _(" - e: translate message in text editor\n")); fwrite(STDERR, _(" - q: quit interactive mode and ignore all following untranslated messages\n")); fwrite(STDERR, _(" - ?: Show this message\n")); if ($copyoriginalvalue) fwrite(STDERR, _("Or leave empty to copy original message.\n")); else fwrite(STDERR, _("Or leave empty to pass.\n")); break; case "": // Empty // On copy orignal value mode, return $msg if ($copyoriginalvalue) return $msg; // Otherwise, leave translation empty return ""; default: // Return user input return $in; } } // Supposed to never happen return true; } /** * CLI helper to add string to translate * @param string $msg The string to translate * @param string|null $context The context of the string to translate * @return void */ function _cli_add_str_to_translate($msg, $context=null) { global $LSlang_cli_logger, $lang, $data, $translations, $interactive, $interactive_exit, $copyoriginalvalue, $format; $LSlang_cli_logger -> trace("_cli_add_str_to_translate($msg, $context)"); if ($msg == '') return; if (!is_null($lang) && _($msg) != "$msg") return; // It is a LSformat string with a simple substitution (for instance: "%{test}")? if (preg_match("/^%[{(][^{(]+[})]$/", $msg)) { $LSlang_cli_logger -> trace( "_cli_add_str_to_translate($msg, $context): LSformat string with a simple substitution, ". "ignore it" ); return; } // Message already exists ? if (array_key_exists($msg, $data)) { if ($context && !in_array($context, $data[$msg]['contexts'])) $data[$msg]['contexts'][] = $context; return; } // Handle translation $translation = ""; if (array_key_exists($msg, $translations)) { $translation = $translations[$msg]; } elseif (!is_null($lang) && _($msg) != $msg) { $translation = _($msg); } elseif ($interactive && $format != 'pot') { $translation = _cli_interactive_ask($context, $msg); if (!is_string($translation)) return; } $data[$msg] = array ( 'translation' => $translation, 'contexts' => ($context?array($context):array()), ); } /** * CLI helper to add string to translate from LSconfig * @param string $pattern The LSconfig parameter pattern used to find strings to translate * @param string $what Where to find string to translate. Possible values: * - key : The LSconfig parameter key * - value : The LSconfig parameter value * @param array $excludes Array of string to exclude * @return void */ function _cli_add_str_to_translate_from_LSconfig($pattern, $what='value', $excludes=array()) { global $LSlang_cli_logger; $LSlang_cli_logger -> trace("_cli_add_str_to_translate_from_LSconfig($pattern, array(".implode(',', $excludes)."))"); $keys = LSconfig :: getMatchingKeys($pattern); $LSlang_cli_logger -> trace("addFromLSconfig : ".count($keys)." matching key(s)"); foreach ($keys as $key => $value) { $LSlang_cli_logger -> trace("addFromLSconfig : $key = ".varDump($value)); if ($what == 'key') { // Get the last key parts as value and all other as key $key_parts = explode('.', $key); $value = $key_parts[count($key_parts)-1]; $key = implode('.', array_slice($key_parts, 0, count($key_parts)-1)); } if (!in_array($value, $excludes) && is_string($value)) _cli_add_str_to_translate($value, $key); } } /** * CLI helper to add string to translate from list of possible values retreive from LSconfig * @param string $context The context == prefix of the LSconfig parameter key to retreive possible values * @param array $withouts FIXME * @param integer $level The level of the possible value (used to identify recursion level) * @return void */ function _cli_add_possible_values_from_LSconfig($context, $withouts, $level=0) { global $LSlang_cli_logger; $LSlang_cli_logger -> trace("_cli_add_possible_values_from_LSconfig($context)"); if (in_array('select-list', $withouts)) return; if (!LSconfig :: get("$context.translate_labels", True, "bool")) return; foreach(LSconfig :: get("$context.possible_values", array()) as $pkey => $plabel) { if (is_array($plabel)) { // Sub possible values // Check level if ($level > 1) { $LSlang_cli_logger -> warning( "_cli_add_possible_values_from_LSconfig($context): Level to hight to handle sub possible values of $context.possible_values.$pkey" ); return; } _cli_add_str_to_translate_from_LSconfig("$context.possible_values.$pkey.label"); $LSlang_cli_logger -> trace("_cli_add_possible_values_from_LSconfig($context): handle sub possible values of $context.possible_values.$pkey"); _cli_add_possible_values_from_LSconfig("$context.possible_values.$pkey", $withouts, $level+1); } else { switch ($pkey) { case 'OTHER_OBJECT': $LSlang_cli_logger -> trace("_cli_add_possible_values_from_LSconfig($context): ignore $context.possible_values.$pkey (OTHER_OBJECT)"); break; case 'OTHER_ATTRIBUTE': if (is_array($plabel)) { if (isset($plabel['json_component_key'])) _cli_add_str_to_translate_from_LSconfig("$context.possible_values.OTHER_ATTRIBUTE.json_component_label"); else _cli_add_str_to_translate_from_LSconfig("$context.possible_values.OTHER_ATTRIBUTE.*"); } else { $LSlang_cli_logger -> warning("_cli_add_possible_values_from_LSconfig($context): invalid $context.possible_values.OTHER_ATTRIBUTE config => Must be an array."); } break; default: _cli_add_str_to_translate($plabel, "$context.possible_values.$pkey"); break; } } } } /** * CLI helper to parse a template file to locate strings to translate * @param string $file The file path * @return void */ function _cli_parse_template_file($file) { global $LSlang_cli_logger; $LSlang_cli_logger -> debug("Looking for string to translate in '$file' template file"); foreach(file($file) as $line_count => $line) { $line_count ++; if (preg_match_all('/\{ *tr +.*msg=(["\'])(?P[^"}]+)\1.*\}/', $line, $matches)) { foreach($matches["msg"] as $t) { $t = preg_replace('/[\'"]\|escape\:.*$/', '', $t); $LSlang_cli_logger -> trace(" - \"$t\" # Line $line_count"); _cli_add_str_to_translate($t, _cli_absolute2relative_path($file).":$line_count"); } } } $LSlang_cli_logger -> trace("_cli_parse_template_file($file) : done."); } /** * CLI helper to find and parse all template files of the specified directory * to locate strings to translate * @param string $dir The directory path * @return void */ function _cli_find_and_parse_template_file($dir) { global $LSlang_cli_logger; if (is_dir($dir)) { $LSlang_cli_logger -> debug("Looking for string to translate in template directory '$dir'"); if ($dh = opendir($dir)) { while (($file = readdir($dh)) !== false) { if ($file=='.' || $file=='..') continue; if (is_dir("$dir/$file")) { _cli_find_and_parse_template_file("$dir/$file"); } elseif (is_file($dir."/".$file) && preg_match('/\.tpl$/',$file)) { _cli_parse_template_file("$dir/$file"); } } closedir($dh); } } } /** * CLI helper to parse a PHP file to locate strings to translate * @param string $file The file path * @return void */ function _cli_parse_php_file($file) { global $LSlang_cli_logger; $LSlang_cli_logger -> debug("Looking for string to translate in '$file' PHP file"); $quote = ''; $res = ''; $comment = null; $line_break_continue = false; $inside_keyword = false; $inside_comment = false; $end_comment_pos = false; $keyword = null; foreach(file($file) as $line_count => $line) { $line = preg_replace("/\n$/", "", $line); $line_count++; $LSlang_cli_logger -> trace("Handle line #$line_count of '$file' PHP file : '$line'"); $offset = 0; while ( $line_break_continue || (!$inside_comment && preg_match('#^([ \t])*(//|/\*)#', substr($line, $offset), $comment)) || ($inside_comment && $end_comment_pos = strpos(substr($line, $offset), '*/')) || preg_match('/[^a-zA-Z0-9\'\"]_{1,3}\(/', substr($line, $offset), $keyword, PREG_OFFSET_CAPTURE) ) { if ($line_break_continue) { $LSlang_cli_logger -> trace("$file:$line_count: continue from previous line after a line break"); $line_break_continue = false; $offset = 0; $inside_keyword = true; } elseif ($comment) { $pos = $offset + strlen($comment[1]); if ($comment[2] == '//') { $LSlang_cli_logger -> trace( "$file:$line_count: entering comment detected '".$comment[2]."' at position #$pos, ". "ignore the line ('$line', offset=$offset)"); break; } $LSlang_cli_logger -> trace( "$file:$line_count: entering comment detected '".$comment[2]."' at position #$pos ". "('$line', offset=$offset)"); $inside_comment = true; $offset = $pos + strlen($comment[2]); $comment = null; } elseif ($inside_comment) { if ($end_comment_pos) { $pos = $offset + $end_comment_pos; $LSlang_cli_logger -> trace( "$file:$line_count: end of comment detected at position #$pos, continue ". "('$line', offset=$offset)"); $offset = $pos; $end_comment_pos = null; $inside_comment = false; continue; } if ($keyword) { $pos = $offset + $keyword[0][1]; $LSlang_cli_logger -> trace( "$file:$line_count: detect keyword '".substr($keyword[0][0], 1).")' at position #$pos,". " but inside comment => ignore it ('$line', offset=$offset)"); $offset += strlen($keyword[0][0]) + $keyword[0][1]; } else { $LSlang_cli_logger -> fatal( "$file:$line_count: not supported case (inside comment & !keyword), stop"); return; } } else if ($keyword) { $pos = $offset + $keyword[0][1]; $LSlang_cli_logger -> trace( "$file:$line_count: detect keyword '".substr($keyword[0][0], 1).")' at position #$pos ". "('$line', offset=$offset)"); $offset += strlen($keyword[0][0]) + $keyword[0][1]; $closed = false; $inside_keyword = true; } else { $LSlang_cli_logger -> fatal( "$file:$line_count: not supported case, stop"); return; } $ignore = false; $concatenation_need = false; for ($i=$offset; $i trace( "$file:$line_count: End of comment detected at position #$i ('$line')"); $inside_comment = false; } $i++; } elseif (empty($quote)) { // Quote char not detected : try to detect it if ($line[$i]=='\\' || $line[$i]==" " || $line[$i]=="\t") { // Space or escape char : pass $i++; } elseif (in_array($line[$i], array('"', "'")) && !$concatenation_need) { // Quote detected $LSlang_cli_logger -> trace( "$file:$line_count: Quote (".$line[$i].") detected at position #$i ('$line')"); $quote=$line[$i]; } elseif ($line[$i]=='$') { // Variable translation not possible or end function call detected $LSlang_cli_logger -> trace( "$file:$line_count: Variable translation detected at position #$i, ignore it ". "('$line')"); $ignore = true; break; } elseif ($line[$i]==')' && $inside_keyword) { // End function call detected $LSlang_cli_logger -> trace( "$file:$line_count: End function call detected at position #$i ('$line')"); $inside_keyword = false; break; } elseif ($line[$i] == '.' && $concatenation_need) { // Concatenation char detected $LSlang_cli_logger -> trace( "$file:$line_count: Concatenation char detected at position #$i ('$line')"); $concatenation_need = false; } elseif (preg_match('/[A-Za-z]/', $line[$i])) { // Constant or function call detected $LSlang_cli_logger -> trace( "$file:$line_count: Constant or function call detected at position #$i, ignore it ('$line')"); $ignore = true; break; } else { // Unknown case : continue $LSlang_cli_logger -> trace( "$file:$line_count: Unknown case before quote at position #$i ('".$line[$i]."'), ". "ignore it and continue ('$line')"); $i++; } } elseif ($quote) { // Quote char already detected : try to detect end quote char if ($line[$i] == '\\') { // Escape char detected : keep it and the following one $LSlang_cli_logger -> trace( "$file:$line_count: Escape char detected inside quote at position #$i, keep it and ". "the following one ('$line')"); $res .= $line[$i]; $i++; $res .= $line[$i]; } elseif ($line[$i] == $quote) { // End quote char detected : reset quote and set concatenation as need $LSlang_cli_logger -> trace( "$file:$line_count: End quote char detected at position #$i, reset quote and set ". "concatenation as need ('$line')"); $quote = ''; $concatenation_need = true; } else { // End quote char not detected : append current char to result $res .= $line[$i]; } } } // Include detected string if not empty and quote char was detected and reseted if ($inside_comment) { $LSlang_cli_logger -> trace( "$file:$line_count: line break detected inside comment ('$line')"); } elseif (!empty($res) && empty($quote) && !$inside_keyword) { _cli_add_str_to_translate($res, _cli_absolute2relative_path($file).":$line_count"); $res = ''; } else if (!$ignore) { $LSlang_cli_logger -> trace( "$file:$line_count: line break detected inside keyword, continue on next line ('$line')"); $line_break_continue = true; if ($quote) $res .= "\n"; break; } } } } /** * CLI helper to find and parse all PHP files of the specified directory * to locate strings to translate * @param string $dir The directory path * @return void */ function _cli_find_and_parse_php_file($dir, $filename_regex=null, $recursive=false, $ignore_dirs=null) { global $LSlang_cli_logger; $filename_regex = $filename_regex?$filename_regex:'/^(.+)\.php$/'; if (is_dir($dir)) { $LSlang_cli_logger -> debug("Looking for string to translate in PHP files of directory '$dir'"); if ($dh = opendir($dir)) { while (($file = readdir($dh)) !== false) { if ($file == '.' || $file == '..') continue; if (is_link("$dir/$file")) continue; if ( is_dir("$dir/$file") && $recursive && (!is_array($ignore_dirs) || !in_array("$dir/$file", $ignore_dirs)) ) { _cli_find_and_parse_php_file("$dir/$file", $filename_regex, true, $ignore_dirs); } elseif (is_file($dir."/".$file) && preg_match($filename_regex, $file)) { _cli_parse_php_file("$dir/$file"); } } closedir($dh); } } } /** * CLI helper to generate output PHP translation file * @param resource $fd The file descriptor where to write to generated output * @return void */ function _cli_output_php($fd) { global $LSlang_cli_logger, $additionalfileformat, $data, $copyoriginalvalue, $keep_unused, $translations; fwrite($fd, " $key_data) { if ($copyoriginalvalue && $key_data['translation'] == "") { $val = $key; } else $val = $key_data['translation']; $key=str_replace('"','\\"',$key); $val=str_replace('"','\\"',$val); foreach ($key_data['contexts'] as $context) fwrite($fd, "\n# $context"); if ($additionalfileformat) { fwrite($fd, "\n\$GLOBALS['LSlang'][\"$key\"] = \"$val\";\n"); } else { fwrite($fd, "\n\"$key\" =>\n \"$val\",\n"); } } // Handle keep unused mode if ($keep_unused) { $unused_msgs = array(); foreach ($translations as $msg => $trans) if (!array_key_exists($msg, $data)) $unused_msgs[$msg] = $trans; if ($unused_msgs) { fwrite($fd, "\n\n"); fwrite($fd, "######################################################################\n"); fwrite($fd, "# Unused translations keeped #\n"); fwrite($fd, "######################################################################\n"); foreach($unused_msgs as $key => $val) { $key=str_replace('"','\\"',$key); $val=str_replace('"','\\"',$val); if ($additionalfileformat) { fwrite($fd, "\n\$GLOBALS['LSlang'][\"$key\"] = \"$val\";\n"); } else { fwrite($fd, "\n\"$key\" =>\n \"$val\",\n"); } } } } if (!$additionalfileformat) fwrite($fd, "\n);\n"); } /** * CLI helper to clean a orignal or translated message before writing it in a POT file * @param string $val The string to clean * @return string The cleaned string */ function _cli_clean_for_pot_file($val) { $val = preg_replace('/([^\\\\])"/', '$1\"', $val); return str_replace("\n", '\n', $val); } /** * CLI helper to generate output POT translation file * @param resource $fd The file descriptor where to write to generated output * @return void */ function _cli_output_pot($fd) { global $LSlang_cli_logger, $data, $copyoriginalvalue; foreach($data as $key => $key_data) { if ($copyoriginalvalue && $key_data['translation'] == "") { $val = $key; } else $val = $key_data['translation']; foreach ($key_data['contexts'] as $context) fwrite($fd, "#: $context\n"); $key = _cli_clean_for_pot_file($key); $val = _cli_clean_for_pot_file($val); fwrite($fd, "msgid \"$key\"\nmsgstr \"$val\"\n\n"); } } $available_onlys = array("config", "templates", "addons", "auth_methods", "includes", "php"); $available_withouts = array_merge($available_onlys, array("select-list")); /** * CLI generate_lang_file command * * @param string $command_args Command arguments * * @return boolean True on success, false otherwise **/ function cli_generate_lang_file($command_args) { // Use global variables to share it with sub-functions global $LSlang_cli_logger, $available_onlys, $available_withouts, $data, $translations, $interactive, $interactive_exit, $copyoriginalvalue, $format, $curdir, $additionalfileformat, $copyoriginalvalue, $keep_unused, $lang; // Initialize logger (if not already initialized by another CLI command) if (!isset($LSlang_cli_logger)) $LSlang_cli_logger = LSlog :: get_logger('generate_lang_file'); // Store existing translations $translations = array(); // Store output translations $data = array(); // Parameters $only = null; $withouts = array(); $include_upstream = false; $copyoriginalvalue = False; $interactive = False; $interactive_exit = False; // Exit flag set when user type 'q' $output = False; $additionalfileformat = False; $keep_unused = False; $fix_utf8 = False; $lang = null; $encoding = null; $available_formats = array('php', 'pot'); $format=$available_formats[0]; $debug = false; $load_files = array(); // Change directory $curdir = getcwd(); chdir(dirname(__FILE__).'/../'); for ($i=0; $i < count($command_args); $i++) { switch ($command_args[$i]) { case '--without': case '-W': $i++; $without = strtolower($command_args[$i]); if (!in_array($without, $available_withouts)) LScli :: usage(_("Invalid -W/--without parameter. Must be one of the following values: %s.'"), implode("','", $available_withouts)); elseif ($only) LScli :: usage(_("You could not use -W/--without parameter combined with -O/--only parameter.")); $withouts[] = $without; break; case '--only': case '-O': $i++; if ($only) LScli :: usage(_("You could specify only one -O/--only parameter.")); $only = strtolower($command_args[$i]); if (!in_array($only, $available_onlys)) LScli :: usage(_("Invalid -O/--only parameter. Must be one of the following values: '%s'."), implode("', '", $available_onlys)); elseif ($withouts) LScli :: usage(_("You could not use -W/--without parameter combined with -O/--only parameter.")); break; case '-I': case '--include-upstream': $include_upstream=True; break; case '--copy-original-value': case '-c': $copyoriginalvalue=True; break; case '--interactive': case '-i': $interactive=True; break; case '--additional-file-format': case '-a': $additionalfileformat=True; break; case '--lang': case '-l': $i++; $parse_lang = explode('.', $command_args[$i]); if (count($parse_lang) == 2) { $lang = $parse_lang[0]; $encoding = $parse_lang[1]; } else { LScli :: usage(_("Invalid -l/--lang parameter. Must be compose in format : [lang].[encoding]")); } break; case '--output': case '-o': $i++; $output = $command_args[$i]; break; case '--format': case '-f': $i++; $format = strtolower($command_args[$i]); if (!in_array($format, $available_formats)) { LScli :: usage(_("Invalid -f/--format parameter. Must be one of the following values: %s.'"), implode("','", $available_formats)); } break; case '--debug': case '-d': $debug = true; break; case '--keep-unused': case '-K': $keep_unused = true; break; case '--fix-utf8': case '-F': $fix_utf8 = true; break; default: $path = _cli_relative2absolute_path($command_args[$i]); if (is_file($path)) $load_files[] = $path; else LScli :: usage(_("%s: Invalid parameter or lang file to load."), $command_args[$i]); } } // In fix-utf8 mode, load ForceUT8/Encoding lib if ($fix_utf8) LSsession :: includeFile(LS_LIB_DIR . "ForceUTF8/Encoding.php"); // Load translation files foreach($load_files as $path) { $LSlang_cli_logger -> debug("Load $path lang file"); @include($path); foreach($GLOBALS['LSlang'] as $msg => $trans) { if ($fix_utf8) $translations[\ForceUTF8\Encoding::fixUTF8($msg)] = \ForceUTF8\Encoding::fixUTF8($trans); else $translations[$msg] = $trans; } } // Initialize session LSlang :: setLocale($lang, $encoding); // Load lang string if lang was specify if ($lang && $encoding && isset($GLOBALS['LSlang']) && is_array($GLOBALS['LSlang'])) { foreach($GLOBALS['LSlang'] as $msg => $trans) { if ($fix_utf8) $translations[\ForceUTF8\Encoding::fixUTF8($msg)] = \ForceUTF8\Encoding::fixUTF8($trans); else $translations[$msg] = $trans; } } /* * Manage configuration parameters */ if (!in_array('config', $withouts) && (!$only || $only == 'config')) { // LDAP Servers $objects = array(); $LSlang_cli_logger -> info("Looking for string to translate configuration of LDAP servers"); foreach(LSconfig :: keys('ldap_servers') as $ldap_server_id) { $LSlang_cli_logger -> debug("Looking for string to translate configuration of LDAP server #$ldap_server_id"); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.name"); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.subDnLabel"); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.recoverPassword.recoveryHashMail.subject"); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.recoverPassword.recoveryHashMail.msg"); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.recoverPassword.newPasswordMail.subject"); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.recoverPassword.newPasswordMail.msg"); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.subDn.*", 'key', array("LSobject")); _cli_add_str_to_translate_from_LSconfig("ldap_servers.$ldap_server_id.LSprofiles.*.label"); // LSaccess foreach (LSconfig :: get("ldap_servers.$ldap_server_id.LSaccess", array()) as $LSobject) { if (is_string($LSobject) && !in_array($LSobject, $objects) && LSsession :: loadLSobject($LSobject)) { $objects[] = $LSobject; } } // Sub DN LSobjects foreach (LSconfig :: getMatchingKeys("ldap_servers.$ldap_server_id.subDn.*.LSobjects.*") as $LSobject) if (is_string($LSobject) && !in_array($LSobject, $objects) && LSsession :: loadLSobject($LSobject)) $objects[] = $LSobject; } $LSlang_cli_logger -> debug('LSobjects list : '.implode(', ', $objects)); // LSobject foreach($objects as $obj) { $LSlang_cli_logger -> info("Looking for string to translate configuration of object type $obj"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.label"); // LSrelation _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSrelation.*.label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSrelation.*.emptyText"); // Custom Actions _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.customActions.*.label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.customActions.*.helpInfo"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.customActions.*.question_format"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.customActions.*.onSuccessMsgFormat"); // LSform _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSform.layout.*.label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSform.dataEntryForm.*.label"); // LSioFormat _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.ioFormat.*.label"); // LSsearch _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.predefinedFilters.*"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.extraDisplayedColumns.*.label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.extraDisplayedColumns.*.LSformat"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.extraDisplayedColumns.*.alternativeLSformats.*"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.extraDisplayedColumns.*.formaterLSformat"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.customActions.*.label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.customActions.*.question_format"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.LSsearch.customActions.*.onSuccessMsgFormat"); // Attributes foreach(LSconfig :: keys("LSobjects.$obj.attrs") as $attr) { _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.help_info"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.no_value_label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.check_data.*.msg"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.validation.*.msg"); // HTML Options $html_type = LSconfig :: get("LSobjects.$obj.attrs.$attr.html_type"); switch($html_type) { case 'boolean': _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.true_label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.false_label"); break; case 'jsonCompositeAttribute': $components = LSconfig :: keys("LSobjects.$obj.attrs.$attr.html_options.components"); foreach($components as $c) { _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.components.$c.label"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.components.$c.help_info"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.components.$c.check_data.*.msg"); if (LSconfig :: get("LSobjects.$obj.attrs.$attr.html_options.components.$c.type") == 'select_list') _cli_add_possible_values_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.components.$c.options", $withouts); } break; case 'labeledValue': if (LSconfig :: get("LSobjects.$obj.attrs.$attr.html_options.translate_labels", True, "bool")) _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.labels.*"); break; case 'password': _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.mail.subject"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.mail.msg"); _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.confirmChangeQuestion"); break; case 'select_list': case 'select_box': _cli_add_possible_values_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options", $withouts); break; case 'valueWithUnit': _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.units.*"); break; case 'date': _cli_add_str_to_translate_from_LSconfig("LSobjects.$obj.attrs.$attr.html_options.special_values.*"); break; } } } } /* * Manage template file */ if (!in_array('templates', $withouts) && (!$only || $only == 'templates')) { $LSlang_cli_logger -> info("Looking for string to translate in templates file"); if ($include_upstream) _cli_find_and_parse_template_file(LS_ROOT_DIR.'/'.LS_TEMPLATES_DIR); _cli_find_and_parse_template_file(LS_ROOT_DIR.'/'.LS_LOCAL_DIR.LS_TEMPLATES_DIR); } /* * Manage custom PHP code/config files */ // Manage includes files if (!in_array('includes', $withouts) && (!$only || $only == 'includes')) { // Note: Upstream code most only use gettext translation, do not handle it here if ($include_upstream) _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_INCLUDE_DIR); _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_LOCAL_DIR.LS_INCLUDE_DIR); if ($include_upstream) _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_CLASS_DIR, '/^class\.(.+)\.php$/'); _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_LOCAL_DIR.LS_CLASS_DIR, '/^class\.(.+)\.php$/'); } // Manage addons files if (!in_array('addons', $withouts) && (!$only || $only == 'addons')) { $LSlang_cli_logger -> info("Looking for string to translate in LSaddons PHP code"); if ($include_upstream) _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_ADDONS_DIR, '/^LSaddons\.(.+)\.php$/'); _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_LOCAL_DIR.LS_ADDONS_DIR, '/^LSaddons\.(.+)\.php$/'); $LSlang_cli_logger -> info("Looking for string to translate in LSaddons configuration files"); if ($include_upstream) _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_CONF_DIR.'/LSaddons', '/^config\.LSaddons\.(.+)\.php$$/'); _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_LOCAL_DIR.LS_CONF_DIR.'/LSaddons', '/^config\.LSaddons\.(.+)\.php$$/'); } // Manage auth methods files if (!in_array('auth_methods', $withouts) && (!$only || $only == 'auth_methods')) { $LSlang_cli_logger -> info("Looking for string to translate in LSauthMethods configuration files"); if ($include_upstream) _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_CONF_DIR.'/LSauth', '/^config\.(.+)\.php$$/'); _cli_find_and_parse_php_file(LS_ROOT_DIR.'/'.LS_LOCAL_DIR.LS_CONF_DIR.'/LSauth', '/^config\.(.+)\.php$$/'); } // Manage all PHP files if ($only == 'php') { $LSlang_cli_logger -> info("Looking for string to translate in PHP files"); _cli_find_and_parse_php_file(LS_ROOT_DIR, null, true, array(LS_ROOT_DIR.'/'.LS_LOCAL_DIR)); } // Sort resulting strings ksort($data); /* * Handle output file format */ // Determine where to write result if ($output) { $output = _cli_relative2absolute_path($output); $LSlang_cli_logger -> info("Write result in output file ($output)"); $fd = null; try { $LSlang_cli_logger -> debug("Open output file ($output)"); $fd = fopen($output, 'w'); } catch(Exception $e) { $LSlang_cli_logger -> error('Error occured opening output file : '.$e->getMessage(), "\n"); } if (!$fd) { $LSlang_cli_logger -> error("Use stdout out instead.\n"); $fd = STDOUT; $output = false; } } else $fd = STDOUT; // Generate output $LSlang_cli_logger -> debug("Output format : $format"); switch($format) { case 'pot': _cli_output_pot($fd); break; case 'php': default: _cli_output_php($fd); break; } // Close output file (is specified) if ($output && $fd != STDOUT) { $LSlang_cli_logger -> debug("Close output file ($output)"); fclose($fd); } return true; } /** * Args autocompleter for CLI command generate_lang_file * * @param array $comp_words List of already typed words of the command * @param int $comp_word_num The command word number to autocomplete * @param string $comp_word The command word to autocomplete * @param array $opts List of global available options * * @return array List of available options for the word to autocomplete **/ function cli_generate_lang_file_args_autocompleter($comp_words, $comp_word_num, $comp_word, $opts) { global $available_withouts, $available_onlys; switch ($comp_words[$comp_word_num-1]) { case '-W': case '--without': return LScli :: autocomplete_opts($available_withouts, $comp_word); break; case '-O': case '--only': return LScli :: autocomplete_opts($available_onlys, $comp_word); break; case '-l': case '--lang': return LScli :: autocomplete_opts(LSlang :: getLangList(false, true), $comp_word); break; case '-o': case '--output': return array(); break; case '-f': case '--format': return LScli :: autocomplete_opts(array('php', 'pot'), $comp_word); break; } $opts = array_merge( $opts, array ( '-W', '--without', '-O', '--only', '-c', '--copy-original-value', '-i', '--interactive', '-a', '--additional-file-format', '-l', '--lang', '-o', '--output', '-f', '--format', '-I', '--include-upstream', '-K', '--keep-unused', '-F', '--fix-utf8', ) ); return LScli :: autocomplete_opts($opts, $comp_word); } LScli :: add_command( 'generate_lang_file', 'cli_generate_lang_file', ___('Generate lang.php file'), '-l [lang] [-o output.file] [file1] [file2] [-h] [options]', array( ___( " -W/--without Disable specified messages. Must be one of the following values:"), " - ".implode("\n - ", $available_withouts), ___( " -O/--only Only handle specified messages. Must be one of the following values :"), " - ".implode("\n - ", $available_onlys), ___( " -I/--include-upstream Include upstream code to message lookup -c/--copy-original-value Copy original value as translated value when no translated value exists -i/--interactive Interactive mode : ask user to enter translated on each translation needed -a/--additional-file-format Additional file format output -l/--lang Language of the translation Format: [lang].[encoding] -o/--output Output file (default: stdout) -f/--format Output file format : php or pot (default: php) -K/--keep-unused Keep unused translations in resulting file -F/--fix-utf8 Try to load and fix broken UTF-8 characters in existing lang files.") ), false, // This command does not need LDAP connection 'cli_generate_lang_file_args_autocompleter' ); /** * CLI generate_ldapsaisie_pot command * * @param string $command_args Command arguments * * @return boolean True on success, false otherwise **/ function cli_generate_ldapsaisie_pot($command_args) { global $LSlang_cli_logger; // Parameters $use_internal_parser = false; for ($i=0; $i < count($command_args); $i++) { switch ($command_args[$i]) { case '--internal': case '-I': $use_internal_parser = true; break; default: LScli :: usage(_("%s: Invalid parameter."), $command_args[$i]); } } // Initialize logger (if not already initialized by another CLI command) if (!isset($LSlang_cli_logger)) $LSlang_cli_logger = LSlog :: get_logger('generate_ldapsaisie_pot'); // Clean php file in tmp directory if (is_dir(LS_TMP_DIR_PATH)) { foreach(listFiles(LS_TMP_DIR_PATH, '/\.php$/') as $file) { $tmp_file = LS_TMP_DIR_PATH.$file; $LSlang_cli_logger -> debug("Remove temporary file '$tmp_file'"); if (!unlink($tmp_file)) { $LSlang_cli_logger -> fatal("Fail to delete temporary file '$tmp_file'."); } } } // List PHP files to parse $php_files = LScli :: run_external_command( array('find', '-name', "'*.php'"), null, // no STDIN data false, // do not escape command args (already done) LS_ROOT_DIR // run in LdapSaisie root directory to retreive relative paths ); if (!is_array($php_files) || $php_files[0] != 0) { $LSlang_cli_logger -> fatal("Fail to list PHP files."); return false; } // Extract messages from LdapSaisie PHP files if ($use_internal_parser) { // Using internal parser $result = LScli :: run_command( 'generate_lang_file', array ( "-o", LS_I18N_DIR_PATH."/ldapsaisie-main.pot", "-f", "pot", "--only", "php", ), false // do not exit ); if (!$result) $LSlang_cli_logger -> fatal("Fail to extract messages from PHP files using generate_lang_file command."); } else { // Using xgettext $result = LScli :: run_external_command( array( "xgettext", "--from-code utf-8", "--language=PHP", "-o", LS_I18N_DIR_PATH."/ldapsaisie-main.pot", // Output "--omit-header", // No POT header "--keyword=__", // Handle custom __() translation function "--keyword=___", // Handle custom ___() translation function "--files=-" // Read files to parse from STDIN ), $php_files[1], // Pass PHP files list via STDIN true, // Escape parameters LS_ROOT_DIR // Run in LdapSaisie root directory ); if (!is_array($result) || $result[0] != 0) $LSlang_cli_logger -> fatal("Fail to extract messages from PHP files using xgettext."); } // Extract other messages from LdapSaisie templates files $result = LScli :: run_command( 'generate_lang_file', array ( "-o", LS_I18N_DIR_PATH."/ldapsaisie-templates.pot", "-f", "pot", "--only", "templates", "--include-upstream", ), false // do not exit ); if (!$result) $LSlang_cli_logger -> fatal("Fail to extract messages from template files using generate_lang_file command."); // Merge previous results in ldapsaisie.pot file using msgcat $result = LScli :: run_external_command(array( 'msgcat', LS_I18N_DIR_PATH."/ldapsaisie-main.pot", LS_I18N_DIR_PATH."/ldapsaisie-templates.pot", "-o", LS_I18N_DIR_PATH."/ldapsaisie.pot", )); if (!is_array($result) || $result[0] != 0) $LSlang_cli_logger -> fatal("Fail to merge messages using msgcat."); return true; } LScli :: add_command( 'generate_ldapsaisie_pot', 'cli_generate_ldapsaisie_pot', ___('Generate POT files:'), null, array( ___("This command generate 3 POT files:"), " - ".LS_I18N_DIR_PATH."/ldapsaisie-main.pot", ___(" => contains messages from PHP files"), " - ".LS_I18N_DIR_PATH."/ldapsaisie-templates.pot", ___(" => contains messages from templates files"), " - ".LS_I18N_DIR_PATH."/ldapsaisie.pot", ___(" => contains all messages"), null, ___( "Note: Use -I / --internal parameter to use internal parser instead of xgettext for PHP files."), ), false // This command does not need LDAP connection );