Benjamin Renard
6fdc5447f1
* Code cleaning and fix some small errors using Phpstan * Configure pre-commit to run Phpstan before each commit * Some little improvments and logging, mail, smarty & URL libs * Add Sentry integration * Add Webstat JS code inclusion * Install Smarty dependency using composer Breaking changes: * Rename Event class as HookEvent to avoid conflict with PECL event * URL with refresh GET parameter now automatically trigger redirection without it after page loading to avoid to keep it in URL
374 lines
12 KiB
PHP
374 lines
12 KiB
PHP
<?php
|
|
|
|
/*
|
|
********************************************************************
|
|
* Translations CLI commands *
|
|
********************************************************************
|
|
*/
|
|
|
|
if (php_sapi_name() != "cli")
|
|
return true;
|
|
|
|
/**
|
|
* Convert PO file to JSON file
|
|
*
|
|
* @param string $locale The locale of the input PO file
|
|
* @param string $path The path of the input PO file
|
|
*
|
|
* @return string JSON encoded file content
|
|
*/
|
|
function po2json($locale, $path) {
|
|
$fileHandler = new \Sepia\PoParser\SourceHandler\FileSystem($path);
|
|
$poparser = new \Sepia\PoParser\Parser($fileHandler);
|
|
$catalog = $poparser->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 bool
|
|
*/
|
|
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);
|
|
return false;
|
|
}
|
|
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 bool
|
|
*/
|
|
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);
|
|
return false;
|
|
}
|
|
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."
|
|
)
|
|
);
|
|
|
|
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|