parse(); $headers = $catalog->getHeader(); $messages = array(); foreach ($catalog->getEntries() as $entry) { // msg id json format $msg = $entry->getMsgStr(); if ($entry->isPlural()) $msg = array($msg, $entry->getMsgIdPlural()); $messages[$entry->getMsgId()] = $msg; } return json_encode(array( 'messages' => $messages, 'locale' => $locale, 'domain' => TEXT_DOMAIN, 'plural_expr' => '(n > 1)', )); } /** * Command to extract messages from PHP/JS & template files and * generate the lang/messages.pot file. * * @param array $command_args The command arguments * @return void */ function cli_extract_messages($command_args) { global $root_dir_path, $root_lang_dir, $smarty_templates_dir; // List PHP files to parse $php_files = run_external_command( array('find', escapeshellarg($root_dir_path), '-name', "'*.php'"), null, // no STDIN data false // do not escape command args (already done) ); if (!is_array($php_files) || $php_files[0] != 0) { logging('FATAL', _("Fail to list PHP files.")); } // Extract messages from PHP files using xgettext $result = run_external_command( array( "xgettext", "--from-code utf-8", "--language=PHP", "-o", "$root_lang_dir/php-messages.pot", // Output "--omit-header", // No POT header "--keyword=___", // Handle custom ___() translation function "--files=-" // Read files to parse from STDIN ), $php_files[1] // Pass PHP files list via STDIN ); if (!is_array($result) || $result[0] != 0) logging('FATAL', _("Fail to extract messages from PHP files using xgettext.")); // List JS files to parse $js_files = run_external_command( array('find', escapeshellarg("$root_dir_path/public_html/js"), '-name', "'*.js'"), null, // no STDIN data false // do not escape command args (already done) ); if (!is_array($js_files) || $js_files[0] != 0) { logging('FATAL', _("Fail to list JS files.")); } // Extract messages from JS files using xgettext $result = run_external_command( array( "xgettext", "--from-code utf-8", "--language=JavaScript", "-o", "$root_lang_dir/js-messages.pot", // Output "--omit-header", // No POT header "--keyword=___", // Handle custom ___() translation function "--files=-" // Read files to parse from STDIN ), $js_files[1] // Pass JS files list via STDIN ); if (!is_array($result) || $result[0] != 0) logging('FATAL', _("Fail to extract messages from JS files using xgettext.")); // Extract messages from templates files using tsmarty2c.php $result = run_external_command( array ( "$root_dir_path/vendor/bin/tsmarty2c.php", "-o", "$root_lang_dir/templates-messages.pot", $smarty_templates_dir, ) ); if (!is_array($result) || $result[0] != 0) logging( 'FATAL', _("Fail to extract messages from template files using tsmarty2c.php script.")); $fd = fopen("$root_lang_dir/headers.pot", 'w'); $headers = array( 'msgid ""', 'msgstr ""', '"POT-Creation-Date: '.date('Y-m-d H:iO').'\n"', '"PO-Revision-Date: '.date('Y-m-d H:iO').'\n"', '"MIME-Version: 1.0\n"', '"Content-Type: text/plain; charset=utf-8\n"', '"Content-Transfer-Encoding: 8bit\n"', ); fwrite($fd, implode("\n", $headers)); fclose($fd); // Merge previous results in messages.pot file using msgcat $result = run_external_command(array( 'msgcat', "$root_lang_dir/headers.pot", "$root_lang_dir/php-messages.pot", "$root_lang_dir/js-messages.pot", "$root_lang_dir/templates-messages.pot", "-t", "utf-8", "--use-first", "-o", "$root_lang_dir/messages.pot", )); if (!is_array($result) || $result[0] != 0) logging('FATAL', _("Fail to merge messages using msgcat.")); } add_cli_command( 'extract_messages', 'cli_extract_messages', ___("Extract messages that need to be translated"), null, ___("This command could be used to generate/update lang/messages.pot file.") ); /** * Command to update messages from lang/messages.pot file to * all PO file in lang/[lang]/LC_MESSAGES. * * @param array $command_args The command arguments * @return void */ function cli_update_messages($command_args) { global $root_dir_path, $root_lang_dir, $smarty_templates_dir; $compendium_args = array(); foreach ($command_args as $path) { if (!file_exists($path)) logging('FATAL', _("Compendium file %s not found."), $path); $compendium_args[] = '-C'; $compendium_args[] = $path; } $pot_file = "$root_lang_dir/messages.pot"; if (!is_file($pot_file)) logging('FATAL', _("POT file not found (%s). Please run extract_messages first."), $pot_file); if ($dh = opendir($root_lang_dir)) { $error = False; while (($file = readdir($dh)) !== false) { if ( !is_dir($root_lang_dir . '/' . $file) || in_array($file, array('.', '..')) || is_link($root_lang_dir . '/' . $file) ) continue; logging('DEBUG', _("Lang directory '%s' found"), $file); // Check LC_MESSAGES directory exists $lang = $file; $lang_dir = $root_lang_dir . '/' . $file . '/LC_MESSAGES' ; if (!is_dir($lang_dir)) { logging('DEBUG', _("LC_MESSAGES directory not found in lang '%s' directory, ignore it."), $lang); continue; } $po_file = $lang_dir . '/' . TEXT_DOMAIN . '.po'; $created = false; if (!is_file($po_file)) { // Init PO file from POT file using msginit $result = run_external_command( array("msginit", "-i", "$pot_file", "-l", "$lang", "-o", $po_file) ); if (is_array($result) && $result[0] == 0) { $created = true; } else { logging('ERROR', _("Fail to init messages in %s PO file using msginit (%s)."), $lang, $po_file); $error = True; } } // Update messages in PO file from POT file using msgmerge // Note: msginit does not accept compendium files, so we also run // msgmerge on creation with compendium file(s). if (is_file($po_file) && (!$created || $compendium_args)) { $result = run_external_command( array_merge( array("msgmerge", "-q", "-U"), $compendium_args, array($po_file, $pot_file) ) ); if (!is_array($result) || $result[0] != 0) { logging('ERROR', _("Fail to update messages in %s PO file using msgmerge (%s)."), $lang, $po_file); $error = True; } } elseif (!$created) { logging('DEBUG', _("PO file not found in lang '%s' directory, ignore it."), $lang); } } closedir($dh); return !$error; } logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path); } add_cli_command( 'update_messages', 'cli_update_messages', ___("Update messages in translation PO lang files"), null, ___("This command could be used to init/update PO files in lang/*/LC_MESSAGES directories.") ); /** * Command to compile messages from existing translation PO lang files * to corresponding MO files and as JSON catalog (for translation in JS). * * @param array $command_args The command arguments * @return void */ function cli_compile_messages($command_args) { global $root_dir_path, $root_lang_dir, $smarty_templates_dir; if ($dh = opendir($root_lang_dir)) { $error = False; while (($file = readdir($dh)) !== false) { if ( !is_dir($root_lang_dir . '/' . $file) || in_array($file, array('.', '..')) ) continue; if (is_link($root_lang_dir . '/' . $file)) { $real_lang_dir = readlink($root_lang_dir . '/' . $file); if (dirname($real_lang_dir) != '.' || !is_dir($root_lang_dir . '/' . $real_lang_dir)) continue; $lang = $file; logging('DEBUG', _("Lang alias symlink found: %s -> %s"), $lang, $real_lang_dir); // Create JSON catalog symlink (if not exists) $js_link = "$root_dir_path/public_html/translations/$lang.js"; $link_target = "$real_lang_dir.js"; if (!file_exists($js_link)) { if (symlink($link_target, $js_link)) { logging('INFO', _("JSON catalog symlink for %s -> %s created (%s)"), $lang, $real_lang_dir, $js_link); } else { logging('ERROR', _("Fail to create JSON catalog symlink for %s -> %s (%s)"), $lang, $real_lang_dir, $js_link); $error = True; } } elseif (readlink($js_link) == $link_target) { logging('DEBUG', _("JSON catalog symlink for %s -> %s already exist (%s)"), $lang, $real_lang_dir, $js_link); } else { logging( 'WARNING', _("JSON catalog file for %s already exist, but it's not a symlink to %s (%s)"), $lang, $real_lang_dir, $js_link ); $error = True; } continue; } logging('DEBUG', _("Lang directory '%s' found"), $file); // Check LC_MESSAGES directory exists $lang = $file; $lang_dir = $root_lang_dir . '/' . $file . '/LC_MESSAGES' ; if (!is_dir($lang_dir)) { logging('DEBUG', _("LC_MESSAGES directory not found in lang '%s' directory, ignore it."), $lang); continue; } // Test .PO file is present $po_file = $lang_dir . '/' . TEXT_DOMAIN . '.po'; if (!is_file($po_file)) { logging('DEBUG', _("PO file not found in lang '%s' directory, ignore it."), $lang); continue; } $mo_file = preg_replace('/\.po$/', '.mo', $po_file); // Compile messages from PO file to MO file using msgfmt $result = run_external_command( array("msgfmt", "-o", $mo_file, $po_file) ); if (!is_array($result) || $result[0] != 0) { logging( 'ERROR', _("Fail to compile messages from %s PO file as MO file using msgfmt (%s)."), $lang, $po_file ); $error = True; } // Compile messages from PO file to JSON catalog file $json_catalog = po2json($lang, $po_file); $js_file = "$root_dir_path/public_html/translations/$lang.js"; if(!$fd = fopen($js_file, 'w')) { logging('ERROR', _("Fail to open %s JSON catalog file in write mode (%s)."), $lang, $js_file); $error = True; } elseif (fwrite($fd, sprintf("translations_data = %s;", $json_catalog)) === false) { logging('ERROR', _("Fail to write %s JSON catalog in file (%s)."), $lang, $js_file); $error = True; } else { logging('INFO', _("%s JSON catalog writed (%s)."), $lang, $js_file); } } closedir($dh); return !$error; } logging('FATAL', _("Fail to open root lang directory (%s)."), $root_dir_path); } add_cli_command( 'compile_messages', 'cli_compile_messages', ___( "Compile messages from existing translation PO lang files to ". "corresponding MO files and JSON catalogs" ), null, ___( "This command could be used to compile PO files in lang/*/LC_MESSAGES ". "directories to MO files and as JSON catalogs in public_html/translations." ) );