Files
server/usr/share/psa-pear/pear/horde-translation
2026-01-07 20:52:11 +01:00

1737 lines
67 KiB
PHP
Executable File

#!/usr/bin/env php
<?php
/**
* Translation helper application for the Horde framework.
*
* Copyright 2013-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl.
*
* @category Horde
* @copyright 2013-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl LGPL-2
* @package Horde
*/
/**
* Base class for translation script.
*
* Implicitely set properties: gettext, msgattrib, msgcat, msgcomm, msgfmt,
* msginit, msgmerge, xgettext.
*/
class Horde_Translation_Script
{
/**
* CLI interface.
*
* @var Horde_Cli
*/
public $cli;
/**
* File_Find instance.
*
* @var File_Find
*/
public $ff;
/**
* The directory at startup time.
*
* @var string
*/
public $currentDir;
/**
* The directories of all found applications.
*
* @var array
*/
public $dirs = array();
/**
* All found applications.
*
* @var array
*/
public $apps = array();
/**
* Enable debug mode?
*
* @boolean
*/
public $debug = false;
/**
* Enable test mode?
*
* @boolean
*/
public $test = false;
/**
* Silencing appendix for error output.
*
* @var string
*/
public $silence;
/**
* Appendix to redirect error messages to standard output.
*
* @var string
*/
protected $_redirErr;
/**
* Constructor.
*/
public function __construct()
{
$this->_redirErr = substr(PHP_OS, 0, 3) == 'WIN' ? '' : ' 2>&1';
$this->currentDir = getcwd();
}
/**
* Shortcut for Horde_Cli::writeln().
*
* @param string $message The text to write on the screen.
*/
public function writeln($message = '')
{
$this->cli->writeln($message);
}
/**
* Prints the footer and halts the script.
*/
public function footer()
{
$this->writeln();
$this->writeln('Please report any bugs to i18n@lists.horde.org.');
chdir($this->currentDir);
exit;
}
/**
* Prints usage information.
*/
public function usage()
{
if (count($this->options[1]) &&
($this->options[1][0] == 'help' && !empty($this->options[1][1]) ||
!empty($this->options[1][0]) && in_array($this->options[1][0], array('commit', 'compendium', 'extract', 'init', 'make', 'merge')))) {
if ($this->options[1][0] == 'help') {
$cmd = $this->options[1][1];
} else {
$cmd = $this->options[1][0];
}
$this->writeln('Usage:' . ' horde-translation [options] ' . $cmd . ' [command-options]');
if (!empty($cmd)) {
$this->writeln();
$this->writeln('Command options:');
}
switch ($cmd) {
case 'cleanup':
$this->writeln(' -l, --locale=ll Use only this locale.');
$this->writeln(' -m, --module=MODULE Cleanup PO files only for this (Horde) module.');
break;
case 'commit':
case 'commit-help':
$this->writeln(' -l, --locale=ll Use this locale.');
$this->writeln(' -m, --module=MODULE Commit translations only for this (Horde) module.');
$this->writeln(' -M, --message=MESSAGE Use this commit message instead of the default ones.');
$this->writeln(' -n, --new This is a new translation, commit also CREDITS,');
$this->writeln(' CHANGES and nls.php.');
break;
case 'compendium':
$this->writeln(' -a, --add=FILE Add this PO file to the compendium. Useful to');
$this->writeln(' include a compendium from a different branch to');
$this->writeln(' the generated compendium.');
$this->writeln(' -d, --directory=DIR Create compendium in this directory.');
$this->writeln(' -l, --locale=ll Use this locale.');
break;
case 'extract':
$this->writeln(' -m, --module=MODULE Generate POT file only for this (Horde) module.');
break;
case 'init':
$this->writeln(' -l, --locale=ll Use this locale.');
$this->writeln(' -m, --module=MODULE Create a PO file only for this (Horde) module.');
$this->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
$this->writeln(' one (compendium.po in the horde/locale directory).');
$this->writeln(' -n, --no-compendium Don\'t use a compendium.');
break;
case 'make':
$this->writeln(' -l, --locale=ll Use only this locale.');
$this->writeln(' -m, --module=MODULE Build MO files only for this (Horde) module.');
$this->writeln(' -c, --compendium=FILE Merge new translations to this compendium file');
$this->writeln(' instead of the default one (compendium.po in the');
$this->writeln(' horde/locale directory.');
$this->writeln(' -n, --no-compendium Don\'t merge new translations to the compendium.');
$this->writeln(' -s, --statistics Save translation statistics in a local file.');
break;
case 'make-help':
case 'update-help':
$this->writeln(' -l, --locale=ll Use only this locale.');
$this->writeln(' -m, --module=MODULE Update help files only for this (Horde) module.');
break;
case 'merge':
$this->writeln(' -l, --locale=ll Use this locale.');
$this->writeln(' -m, --module=MODULE Merge PO files only for this (Horde) module.');
$this->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
$this->writeln(' one (compendium.po in the horde/locale directory).');
$this->writeln(' -n, --no-compendium Don\'t use a compendium.');
break;
case 'update':
$this->writeln(' -l, --locale=ll Use this locale.');
$this->writeln(' -m, --module=MODULE Update only this (Horde) module.');
$this->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
$this->writeln(' one (compendium.po in the horde/locale directory).');
$this->writeln(' -n, --no-compendium Don\'t use a compendium.');
break;
}
} else {
$this->writeln('Usage:' . ' horde-translation [options] command [command-options]');
$this->writeln(str_repeat(' ', Horde_String::length('Usage:')) . ' horde-translation [help|-h|--help] [command]');
$this->writeln();
$this->writeln('Helper application to create and maintain translations for the Horde');
$this->writeln('framework and its applications.');
$this->writeln('For further information, see horde/docs/TRANSLATIONS.');
$this->writeln();
$this->writeln('Commands:');
$this->writeln(' help Show this help message.');
$this->writeln(' compendium Rebuild the compendium file. Warning: This overwrites the');
$this->writeln(' current compendium.');
$this->writeln(' extract Generate PO template (.pot) files.');
$this->writeln(' init Create one or more PO files for a new locale. Warning: This');
$this->writeln(' overwrites the existing PO files of this locale.');
$this->writeln(' merge Merge the current PO file with the current PO template file.');
$this->writeln(' update Run extract and merge sequent.');
$this->writeln(' update-help Extract all new and changed entries from the English XML help');
$this->writeln(' file and merge them with the existing ones.');
$this->writeln(' cleanup Cleans the PO files up from untranslated and obsolete entries.');
$this->writeln(' make Build binary MO files from the specified PO files.');
$this->writeln(' make-help Mark all entries in the XML help file being up-to-date and');
$this->writeln(' prepare the file for the next execution of update-help. You');
$this->writeln(' should only run make-help AFTER update-help and revising the');
$this->writeln(' help file.');
$this->writeln(' commit Commit translations (developers only).');
$this->writeln(' commit-help Commit help files (developers only).');
}
$this->writeln();
$this->writeln('Options:');
$this->writeln(' -b, --base=/PATH Full path to the (Horde) base directory that should be');
$this->writeln(' used.');
$this->writeln(' -d, --debug Show error messages from the executed binaries.');
$this->writeln(' -h, --help Show this help message.');
$this->writeln(' -t, --test Show the executed commands but don\'t run anything.');
}
/**
* Checks that all necessary binaries are available and have the correct
* version.
*
* Also sets the binary locations as object properties,
* e.g. $this->msgattrib, etc.
*/
public function check_binaries()
{
$this->writeln('Searching gettext binaries...');
foreach (array('gettext', 'msgattrib', 'msgcat', 'msgcomm', 'msgfmt', 'msginit', 'msgmerge', 'xgettext') as $binary) {
$this->$binary = System::which($binary);
if (!$this->$binary) {
$this->cli->message($binary . ' not found', 'cli.error');
$this->footer();
}
$this->cli->message($binary . ' found: ' . $this->$binary, 'cli.success');
}
$this->writeln();
$out = '';
exec($this->gettext . ' --version', $out, $ret);
$split = explode(' ', $out[0]);
$version_string = 'gettext version: ' . $split[count($split) - 1];
$gettext_version = explode('.', $split[count($split) - 1]);
if ($gettext_version[0] == 0 && $gettext_version[1] < 12) {
$this->writeln();
$this->cli->message($version_string, 'cli.error');
$this->cli->message('Your gettext version is too old and does not support PHP natively.', 'cli.error');
$this->footer();
}
$this->cli->message($version_string, 'cli.success');
$this->writeln();
}
/**
* Searches for files matching a PCRE.
*
* @param string $file Regular expression of the file names to search
* for.
* @param string $dir The directory to search.
* @param boolean $local Whether to search only the directory. If false,
* all sub-directories will be searched too.
*
* @return array A list of file names.
*/
public function search_file($file, $dir = '.', $local = false)
{
if (substr($file, 0, 1) != '/') {
$file = "/$file/";
}
if ($local) {
$files = $this->ff->glob($file, $dir, 'perl');
$files = array_map(create_function('$file', 'return "' . $dir . DS . '" . $file;'), $files);
return $files;
}
return $this->ff->search($file, $dir, 'perl', false);
}
/**
* Searches for files with a certain extension.
*
* @param string $ext The extension to search for.
* @param string $dir The directory to search.
* @param boolean $local Whether to search only the directory. If false,
* all sub-directories will be searched too.
*
* @return array A list of file names.
*/
public function search_ext($ext, $dir = '.', $local = false)
{
return $this->search_file("^[^.].*\\.$ext\$", $dir, $local);
}
/**
* Returns all .po files from a directory.
*
* @param string $dir The directory to search.
*
* @return array A list of .po files.
*/
public function get_po_files($dir)
{
$langs = $this->search_ext('po', $dir);
if (($key = array_search($dir . DS . 'messages.po', $langs)) !== false) {
unset($langs[$key]);
}
if (($key = array_search($dir . DS . 'compendium.po', $langs)) !== false) {
unset($langs[$key]);
}
return $langs;
}
/**
* Returns all translation languages from a directory.
*
* @param string $dir The directory to search.
*
* @return array A list of languages.
*/
public function get_languages($dir)
{
chdir($dir);
$langs = $this->get_po_files('locale');
$langs = array_map('basename', array_map('dirname', array_map('dirname', $langs)));
chdir($this->currentDir);
return $langs;
}
/**
* Searches all translateable applications and framework libraries.
*/
public function search_modules()
{
if (is_dir(BASE . '/base/locale')) {
$this->dirs[] = BASE . '/base';
} elseif (is_dir(BASE . '/locale')) {
$this->dirs[] = BASE;
}
$dh = opendir(BASE);
if (!$dh) {
return array();
}
while ($entry = readdir($dh)) {
$dir = BASE . '/' . $entry;
if (!is_dir($dir) ||
substr($entry, 0, 1) == '.' ||
fileinode(HORDE_BASE) == fileinode($dir)) {
continue;
}
$sub = opendir($dir);
if (!$sub) {
continue;
}
while ($subentry = readdir($sub)) {
if ($subentry == 'locale' && is_dir($dir . '/' . $subentry)) {
$this->dirs[] = $dir;
break;
}
if ($entry != 'framework') {
continue;
}
$framework = opendir($dir . '/' . $subentry);
if (!$framework) {
continue;
}
while ($package = readdir($framework)) {
if ($package == 'locale' &&
is_dir($dir . '/' . $subentry . '/' . $package)) {
$this->dirs[] = $dir . '/' . $subentry;
break;
}
}
}
}
$this->apps = $this->strip_horde($this->dirs);
$this->apps[0] = 'horde';
}
/**
* Converts path names into application or library names.
*
* @param string|array $file A path name or a list of path names.
*
* @return string|array A module name or a list of module names.
*/
public function strip_horde($file)
{
if (is_array($file)) {
return array_map(array($this, 'strip_horde'), $file);
}
$info = Horde_Yaml::loadFile($file . '/.horde.yml');
switch ($info['type']) {
case 'application':
return $info['id'];
case 'library':
return 'Horde_' . $info['id'];
}
return basename($file);
}
/**
* Extracts messages from the source code.
*
* @param array $options Command line arguments.
*/
public function xtract($options)
{
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'm':
case '--module':
$module = $option[1];
break;
}
}
for ($i = 0; $i < count($this->dirs); $i++) {
if (!empty($module) && $module != $this->apps[$i]) {
continue;
}
printf('Extracting from %s... ', $this->apps[$i]);
chdir($this->dirs[$i]);
/* Match all *.php and *.inc files in the current directory and
* sub-directories, unless they match *.local.php or have a
* directory name *.d/ in the path. */
$regexp = ';(?<!\.d)/[^.][^/]*?\.((?<!\.local\.)php|inc)$;';
if ($this->apps[$i] == 'horde') {
$files = glob('*.php');
foreach (array('admin', 'bin', 'config', 'lib', 'rpc', 'scripts', 'services', 'templates', 'themes', 'util') as $dir) {
$files = array_merge($files, $this->ff->search($regexp, $dir, 'perl', true));
}
} else {
$files = $this->ff->search($regexp, '.', 'perl', true);
}
$file = 'locale' . DS . $this->apps[$i] . '.pot';
/* Store the file list because it gets too long to be passed on the
* command line. */
file_put_contents($file . '.list', implode("\n", $files));
if (file_exists($file) && !is_writable($file)) {
$this->climessage(sprintf('%s is not writable.', $file), 'cli.error');
$this->footer();
}
/* We must use a .pot extension, otherwise msgcomm complains about
* an invalid charset being used in this file. */
$tmp_file = $file . '.tmp.pot';
$sh = $this->xgettext
. ' --language=PHP'
. ' --from-code=iso-8859-1'
. ' --keyword=_ --keyword=ngettext --keyword=t --keyword=n --keyword=r'
. ' --sort-output'
. ' --package-name=' . ($this->apps[$i] == 'imp' ? 'IMP' : ucfirst($this->apps[$i]))
. ' --copyright-holder="Horde LLC (http://www.horde.org/)"'
. ' --msgid-bugs-address="dev@lists.horde.org"'
. ' --files-from=' . $file . '.list'
. ' --output=' . $tmp_file;
if ($this->debug) {
$sh .= $this->silence;
}
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln($sh);
}
if (!$this->test) {
exec($sh);
}
unlink($file . '.list');
$app = $this->apps[$i] == 'imp' ? 'IMP' : ucfirst($this->apps[$i]);
file_put_contents($tmp_file, str_replace('PACKAGE package.', $app . ' package.', file_get_contents($tmp_file)));
$diff = array();
if (file_exists($tmp_file)) {
/* Search for Horde_Template template files and extract <gettext>
* tags manually. */
$files = $this->search_ext('html', 'templates');
if (!$this->test) $tmp = fopen($file . '.templates', 'w');
foreach ($files as $template) {
$fp = fopen($template, 'r');
$lineno = 0;
while (($line = fgets($fp, 4096)) !== false) {
$lineno++;
$offset = 0;
while (($left = strpos($line, '<gettext>', $offset)) !== false) {
$left += 9;
$buffer = '';
$linespan = 0;
while (($end = strpos($line, '</gettext>', $left)) === false) {
$buffer .= substr($line, $left);
$left = 0;
$line = fgets($fp, 4096);
$linespan++;
if ($line === false) {
$this->climessage(sprintf("<gettext> tag not closed in file %s.\nOpening tag found in line %d.", $template, $lineno), 'cli.warning');
break 2;
}
}
$buffer .= substr($line, $left, $end - $left);
if (!$this->test) {
fwrite($tmp, "#: $template:$lineno\n");
fwrite($tmp, 'msgid "' . str_replace(array('"', "\n"), array('\"', "\\n\"\n\""), $buffer) . "\"\n");
fwrite($tmp, 'msgstr ""' . "\n\n");
}
$offset = $end + 10;
}
}
fclose($fp);
}
/* Merge with the base .pot file. */
if (!$this->test) fclose($tmp);
$sh = $this->msgcomm . " --more-than=0 --sort-output \"$tmp_file\" \"$file.templates\" --output-file \"$tmp_file\"" . $this->silence;
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln($sh);
}
if (!$this->test) {
exec($sh);
unlink($file . '.templates');
}
/* Parse conf.xml files for <configphp> tags. */
if (file_exists('config/conf.xml')) {
if (!$this->test) $tmp = fopen($file . '.config', 'w');
$conf_content = file_get_contents('config/conf.xml');
if (!$this->test &&
preg_match_all('/<configphp .*?>([^<]*_\(".+?"\)[^<]*)<\/configphp>/s',
$conf_content, $matches)) {
foreach ($matches[1] as $configphp) {
if (!preg_match_all('/_\("(.+?)"\)/', $configphp, $strings)) {
continue;
}
foreach ($strings[1] as $string) {
fwrite($tmp, "#: config/conf.xml\n");
fwrite($tmp, 'msgid "' . $string . "\"\n");
fwrite($tmp, 'msgstr ""' . "\n\n");
}
}
}
if (!$this->test) fclose($tmp);
/* Merge with the base .pot file. */
$sh = $this->msgcomm . " --more-than=0 --sort-output \"$tmp_file\" \"$file.config\" --output-file \"$tmp_file\"" . $this->silence;
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln($sh);
}
if (!$this->test) {
exec($sh);
unlink($file . '.config');
}
}
/* Check if the new .pot file has any changed content at
* all. */
if (file_exists($file)) {
$diff = array_merge(array_diff(file($tmp_file), file($file)),
array_diff(file($file), file($tmp_file)));
$diff = preg_grep('/^("POT-Creation-Date:|"Project-Id-Version:)/', $diff, PREG_GREP_INVERT);
}
}
if (!file_exists($file) || count($diff)) {
if (file_exists($file)) {
unlink($file);
}
rename($tmp_file, $file);
$this->writeln($this->cli->green('updated'));
} else {
if (file_exists($tmp_file)) {
unlink($tmp_file);
}
$this->writeln($this->cli->bold('not changed'));
}
chdir($this->currentDir);
}
}
/**
* Merges old translations with new .pot files and optionally a compendium.
*
* @param array $options Command line arguments.
*/
public function merge($options)
{
$compendium = ' --compendium="' . HORDE_BASE . DS . 'locale' . DS . 'compendium.po"';
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
case 'c':
case '--compendium':
$compendium = ' --compendium=' . $option[1];
break;
case 'n':
case '--no-compendium':
$compendium = '';
break;
}
}
$this->cleanup($options);
for ($i = 0; $i < count($this->dirs); $i++) {
if (!empty($module) && $module != $this->apps[$i]) {
continue;
}
$this->writeln(sprintf('Merging translation for module %s...', $this->cli->bold($this->apps[$i])));
$dir = $this->dirs[$i] . DS . 'locale' . DS;
$po = $dir . '%s' . DS . 'LC_MESSAGES' . DS . $this->apps[$i] . '.po';
if (empty($lang)) {
$langs = $this->get_languages($this->dirs[$i]);
} else {
if (!file_exists(sprintf($po, $lang))) {
$this->writeln('Skipped...');
$this->writeln();
continue;
}
$langs = array($lang);
}
foreach ($langs as $locale) {
$this->writeln(sprintf('Merging locale %s... ', $this->cli->bold($locale)));
$sh = $this->msgmerge
. sprintf(' --update -v%s "%s" "%s.pot"',
$compendium, sprintf($po, $locale), $dir . $this->apps[$i]);
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln($sh);
}
if (!$this->test) exec($sh);
$this->writeln($this->cli->green('done'));
}
}
}
/**
* Unused yet.
*
* @param array $options Command line arguments.
*/
public function status($options)
{
$output = 'status.html';
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
case 'o':
case '--output':
$output = $option[1];
break;
}
}
for ($i = 0; $i < count($this->dirs); $i++) {
if (!empty($module) && $module != $this->apps[$i]) {
continue;
}
$this->writeln(sprintf('Generating status for module %s...', $this->cli->bold($this->apps[$i])));
if (empty($lang)) {
$langs = $this->get_languages($this->dirs[$i]);
} else {
if (!file_exists($this->dirs[$i] . '/locale/' . $lang . '/LC_MESSAGES/' . $this->apps[$i] . '.po')) {
$this->writeln('Skipped...');
$this->writeln();
continue;
}
$langs = array($lang);
}
foreach ($langs as $locale) {
$this->writeln(sprintf('Status for locale %s... ', $this->cli->bold($locale)));
}
}
}
/**
* Builds or updates a compendium.
*
* @param array $options Command line arguments.
*/
public function compendium($options)
{
$dir = HORDE_BASE . DS . 'locale' . DS;
$add = '';
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'd':
case '--directory':
$dir = $option[1];
break;
case 'a':
case '--add':
$add .= ' ' . $option[1];
break;
}
}
if (!isset($lang)) {
$this->cli->message('No locale specified.', 'cli.error');
$this->writeln();
$this->usage();
$this->footer();
}
printf('Merging all %s.po files to the compendium... ', $lang);
$pofiles = array();
for ($i = 0; $i < count($this->dirs); $i++) {
$pofile = $this->dirs[$i] . DS . 'locale' . DS . $lang . DS . 'LC_MESSAGES' . DS . $this->apps[$i] . '.po';
if (file_exists($pofile)) {
$pofiles[] = $pofile;
}
}
if (!empty($dir) && substr($dir, -1) != DS) {
$dir .= DS;
}
$sh = $this->msgcat . ' --sort-output ' . implode(' ', $pofiles) . $add . ' > ' . $dir . 'compendium.po ' . ($this->debug ? '' : $this->silence);
if ($this->debug || $this->test) {
$this->writeln();
$this->writeln('Executing:');
$this->writeln($sh);
}
if ($this->test) {
$ret = 0;
} else {
exec($sh, $out, $ret);
}
if ($ret == 0) {
$this->writeln($this->cli->green('done'));
} else {
$this->writeln($this->cli->red('failed'));
}
}
/**
* Creates initial translations.
*
* @param array $options Command line arguments.
*/
public function init($options)
{
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
}
}
if (empty($lang)) {
$lang = getenv('LANG');
}
for ($i = 0; $i < count($this->dirs); $i++) {
if (!empty($module) && $module != $this->apps[$i]) {
continue;
}
printf('Initializing module %s... ', $this->apps[$i]);
$dir = $this->dirs[$i] . DS . 'locale' . DS;
$targetdir = $dir . $lang . DS . 'LC_MESSAGES';
$pot = $dir . $this->apps[$i] . '.pot';
$po = $targetdir . DS . $this->apps[$i] . '.po';
if (!file_exists($pot)) {
$this->writeln();
$this->cli->message(sprintf('%s not found. Run \'translation extract\' first.', $pot), 'cli.warning');
continue;
}
if (!is_dir($targetdir)) {
if ($this->debug) {
$this->writeln(sprintf('Making directory %s', $targetdir));
}
if (!$this->test && !System::mkdir("-p $targetdir")) {
$this->cli->message(sprintf('Could not create locale directory for locale %s:', $locale), 'cli.warning');
$this->writeln($targetdir);
$this->writeln();
continue;
}
}
$sh = $this->msginit . ' --no-translator -i ' . $pot;
if (!empty($lang)) {
$lcdir = $dir . $lang . DS . 'LC_MESSAGES';
$pofile = $lcdir . DS . $this->apps[$i] . '.po';
$sh .= ' --output-file ' . $po . ' --locale=' . $lang;
if (!is_dir($lcdir) && !System::mkdir('-p ' . $lcdir)) {
$this->cli->message(sprintf('Could not create locale directory for locale %s:', $locale), 'cli.warning');
$this->writeln($lcdir);
$this->writeln();
continue;
}
}
if (!$this->debug) {
$sh .= $this->silence;
}
if ($this->debug || $this->test) {
$this->writeln();
$this->writeln('Executing:');
$this->writeln($sh);
}
if ($this->test) {
$ret = 0;
} else {
exec($sh, $out, $ret);
}
$app = $this->apps[$i] == 'imp' ? 'IMP' : ucfirst($this->apps[$i]);
file_put_contents($po,
str_replace(array('Language-Team: none',
'PACKAGE package.',
'Content-Type: text/plain; charset=ASCII'),
array('Language-Team: i18n@lists.horde.org',
$app . ' package.',
'Content-Type: text/plain; charset=UTF-8'),
file_get_contents($po)));
if ($ret == 0) {
$this->writeln($this->cli->green('done'));
} else {
$this->writeln($this->cli->red('failed'));
}
}
}
/**
* Compiles translations to .mo files.
*
* @param array $options Command line arguments.
*/
public function make($options)
{
$compendium = HORDE_BASE . DS . 'locale' . DS . 'compendium.po';
$save_stats = false;
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
case 'c':
case '--compendium':
$compendium = $option[1];
break;
case 'n':
case '--no-compendium':
$compendium = '';
break;
case 's':
case '--statistics':
$save_stats = true;
break;
}
}
$horde = array_search('horde', $this->apps);
$horde_msg = array();
$stats_array = array();
$stats = new Console_Table();
$stats->setHeaders(array('Module', 'Language', 'Translated', 'Fuzzy', 'Untranslated', 'Updated'));
for ($i = 0; $i < count($this->dirs); $i++) {
if (!empty($module) && $module != $this->apps[$i]) {
continue;
}
$this->writeln(sprintf('Building MO files for module %s...', $this->cli->bold($this->apps[$i])));
$dir = $this->dirs[$i] . DS . 'locale' . DS . '%s' . DS . 'LC_MESSAGES' . DS;
if (empty($lang)) {
$langs = $this->get_languages($this->dirs[$i]);
} else {
if (!file_exists(sprintf($dir, $lang) . $this->apps[$i] . '.po')) {
$this->writeln('Skipped...');
$this->writeln();
continue;
}
$langs = array($lang);
}
foreach ($langs as $locale) {
$this->writeln(sprintf('Building locale %s...', $this->cli->bold($locale)));
$targetdir = sprintf($dir, $locale);
$pofile = $targetdir . $this->apps[$i] . '.po';
/* Convert to unix linebreaks. */
$content = str_replace("\r", '', file_get_contents($pofile));
file_put_contents($pofile, $content);
/* Remember update date. */
$last_update = preg_match(
'/^"PO-Revision-Date: (\d{4}-\d{2}-\d{2})/m',
$content, $matches)
? $matches[1] : '';
/* Check PO file sanity. */
$sh = $this->msgfmt . " --check --output-file=/dev/null \"$pofile\" " . $this->_redirErr;
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln($sh);
}
if ($this->test) {
$ret = 0;
} else {
exec($sh, $out, $ret);
}
if ($ret != 0) {
$this->cli->message('An error has occured:', 'cli.warning');
$this->writeln(implode("\n", $out));
$this->writeln();
if ($this->apps[$i] == 'horde') {
continue 2;
}
continue;
}
/* Compile MO file. */
$sh = $this->msgfmt . ' --statistics -o "' . $targetdir . $this->apps[$i] . '.mo" ';
if ($this->apps[$i] != 'horde' &&
substr($this->apps[$i], 0, 6) != 'Horde_') {
$horde_po = $this->dirs[$horde] . DS . 'locale' . DS . $locale . DS . 'LC_MESSAGES/horde.po';
if (!is_readable($horde_po)) {
$this->cli->message(sprintf('The Horde PO file for the locale %s does not exist:', $locale), 'cli.warning');
$this->writeln($horde_po);
$this->writeln();
$sh .= '"' . $targetdir . DS . $this->apps[$i] . '.po"';
} else {
$sh = $this->msgcomm . " --more-than=0 --sort-output \"$pofile\" \"$horde_po\" | $sh -";
}
} else {
$sh .= '"' . $pofile . '"';
}
$sh .= $this->_redirErr;
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln($sh);
}
$out = '';
if ($this->test) {
$ret = 0;
} else {
exec($sh, $out, $ret);
}
if ($ret == 0) {
$this->writeln($this->cli->green('done'));
$messages = array(0, 0, 0, $last_update);
if (preg_match('/(\d+) translated/', $out[0], $match)) {
$messages[0] = $match[1];
if (substr($this->apps[$i], 0, 6) != 'Horde_' &&
isset($horde_msg[$locale])) {
$messages[0] -= $horde_msg[$locale][0];
if ($messages[0] < 0) $messages[0] = 0;
}
}
if (preg_match('/(\d+) fuzzy/', $out[0], $match)) {
$messages[1] = $match[1];
if (substr($this->apps[$i], 0, 6) != 'Horde_' &&
isset($horde_msg[$locale])) {
$messages[1] -= $horde_msg[$locale][1];
if ($messages[1] < 0) $messages[1] = 0;
}
}
if (preg_match('/(\d+) untranslated/', $out[0], $match)) {
$messages[2] = $match[1];
if (substr($this->apps[$i], 0, 6) != 'Horde_' &&
isset($horde_msg[$locale])) {
$messages[2] -= $horde_msg[$locale][2];
if ($messages[2] < 0) $messages[2] = 0;
}
}
if ($this->apps[$i] == 'horde') {
$horde_msg[$locale] = $messages;
}
$stats_array[$this->apps[$i]][$locale] = $messages;
$stats->addRow(array($this->apps[$i], $locale, $messages[0], $messages[1], $messages[2], $messages[3]));
} else {
$this->writeln($this->cli->red('failed'));
exec($sh, $out, $ret);
$this->writeln(implode("\n", $out));
}
if (count($langs) > 1) {
continue;
}
/* Merge translation into compendium. */
if (!empty($compendium)) {
printf('Merging the PO file for %s to the compendium... ', $this->cli->bold($this->apps[$i]));
if (!empty($targetdir) && substr($targetdir, -1) != DS) {
$targetdir .= DS;
}
$sh = $this->msgcat . " --sort-output \"$compendium\" \"$pofile\" > \"$compendium.tmp\"";
if (!$this->debug) {
$sh .= $this->silence;
}
if ($this->debug || $this->test) {
$this->writeln();
$this->writeln('Executing:');
$this->writeln($sh);
}
$out = '';
if ($this->test) {
$ret = 0;
} else {
exec($sh, $out, $ret);
}
unlink($compendium);
rename($compendium . '.tmp', $compendium);
if ($ret == 0) {
$this->writeln($this->cli->green('done'));
} else {
$this->writeln($this->cli->red('failed'));
}
}
$this->writeln();
}
}
if (empty($module)) {
$this->writeln('Results:');
} else {
$this->writeln('Results (including Horde):');
}
$this->writeln($stats->getTable());
if ($save_stats) {
file_put_contents('translation_stats.txt', serialize($stats_array));
}
}
/**
* Cleans up .po files, removing obsolete translations and optionally
* untranslated strings.
*
* @param array $options Command line arguments.
* @param boolean $keep_untranslated Whether to keep untranslated strings.
*/
public function cleanup($options, $keep_untranslated = false)
{
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
}
}
for ($i = 0; $i < count($this->dirs); $i++) {
if (!empty($module) && $module != $this->apps[$i]) {
continue;
}
$this->writeln(sprintf('Cleaning up PO files for module %s...', $this->cli->bold($this->apps[$i])));
$po = $this->dirs[$i] . DS . 'locale' . DS . '%s' . DS . 'LC_MESSAGES' .DS . $this->apps[$i] . '.po';
if (empty($lang)) {
$langs = $this->get_languages($this->dirs[$i]);
} else {
$this->writeln(sprintf($po, $lang));
if (!file_exists(sprintf($po, $lang))) {
$this->writeln('Skipped...');
$this->writeln();
continue;
}
$langs = array($lang);
}
foreach ($langs as $locale) {
$this->writeln(sprintf('Cleaning up locale %s... ', $this->cli->bold($locale)));
$pofile = sprintf($po, $locale);
$sh = $this->msgattrib . ($keep_untranslated ? '' : ' --translated') . " --no-obsolete --force-po \"$pofile\" > \"$pofile.tmp\"";
if (!$this->debug) {
$sh .= $this->silence;
}
if ($this->debug || $this->test) {
$this->writeln();
$this->writeln('Executing:');
$this->writeln($sh);
}
$out = '';
if ($this->test) {
$ret = 0;
} else {
exec($sh, $out, $ret);
}
if ($ret == 0) {
unlink($pofile);
rename($pofile . '.tmp', $pofile);
$this->writeln($this->cli->green('done'));
} else {
unlink($pofile . '.tmp', $pofile);
$this->writeln($this->cli->red('failed'));
}
$this->writeln();
}
}
}
/**
* Commits translations to Git.
*
* @param array $options Command line arguments.
* @param boolean $help_only Whether to only commit help files.
*/
public function commit($options, $help_only = false)
{
$dirs = $this->dirs;
$apps = $this->apps;
$docs = $lang = false;
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
case 'n':
case '--new':
$docs = true;
break;
case 'M':
case '--message':
$msg = $option[1];
break;
}
}
$files = array();
for ($i = 0; $i < count($dirs); $i++) {
if (!empty($module) && $module != $apps[$i]) {
continue;
}
if ($apps[$i] == 'horde') {
$dirs[] = $dirs[$i] . DS . 'admin';
$apps[] = 'horde/admin';
if (!empty($module)) {
$module = 'horde/admin';
}
}
if (empty($lang)) {
if ($help_only) {
$files = array_merge($files, $this->strip_horde($this->search_ext('xml', $dirs[$i] . DS . 'locale')));
} else {
$files = array_merge($files, $this->search_file('^[a-z]{2}(_[A-Z]{2})?', $dirs[$i] . DS . 'locale', true));
}
} else {
if (!is_dir($dirs[$i] . DS . 'locale' . DS . $lang)) {
continue;
}
if ($help_only &&
!file_exists($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml')) {
continue;
}
$files[] = $dirs[$i] . DS . 'locale' . DS . $lang;
}
if ($docs && !$help_only && $apps[$i]) {
if (is_dir($dirs[$i] . DS . 'docs')) {
$files[] = $this->strip_horde($dirs[$i] . DS . 'docs');
}
if ($apps[$i] == 'horde') {
$horde_conf = $dirs[array_search('horde', $dirs)] . DS . 'config' . DS;
$files[] = $this->strip_horde($horde_conf . 'nls.php');
}
}
}
chdir(BASE);
if (count($files)) {
if ($docs) {
$this->writeln('Adding new files to repository:');
$add_files = array();
foreach ($files as $file) {
if (strstr($file, 'locale')) {
$add_files[] = $file;
$this->writeln($file);
}
}
foreach ($files as $file) {
if (strstr($file, 'locale')) {
if (glob($file . DS . '*.xml')) {
$add_files[] = $file . DS . '*.xml';
$this->writeln($file . DS . '*.xml');
}
if (!$help_only) {
$add_files[] = $file . DS . 'LC_MESSAGES';
$this->writeln($file . DS . 'LC_MESSAGES');
}
}
}
if (!$help_only) {
foreach ($files as $file) {
if (strstr($file, 'locale')) {
$this->writeln($add_files[] = $file . DS . 'LC_MESSAGES' . DS . '*.po');
$this->writeln($add_files[] = $file . DS . 'LC_MESSAGES' . DS . '*.mo');
}
}
}
$this->writeln();
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln('git add ' . implode(' ', $add_files));
}
if (!$this->test) {
system('git add ' . implode(' ', $add_files));
}
$this->writeln();
}
$this->writeln('Committing:');
$this->writeln(implode(' ', $files));
if (!empty($lang)) {
$lang = ' ' . $lang;
}
if (empty($msg)) {
if ($docs) {
$msg = "Add$lang translation.";
} elseif ($help_only) {
$msg = "Update$lang help file.";
} else {
$msg = "Update$lang translation.";
}
}
$sh = 'git add ' . implode(' ', $files) . '; git commit -m "' . $msg . '"';
if ($this->debug || $this->test) {
$this->writeln('Executing:');
$this->writeln($sh);
}
if (!$this->test) system($sh);
}
}
/**
* Merges old help translations with new English versions.
*
* @param array $options Command line arguments.
*/
public function update_help($options)
{
$dirs = $this->dirs;
$apps = $this->apps;
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
}
}
$files = array();
for ($i = 0; $i < count($dirs); $i++) {
if (!empty($module) && $module != $apps[$i]) {
continue;
}
if (!is_dir("$dirs[$i]/locale")) {
continue;
}
if ($apps[$i] == 'horde') {
$dirs[] = $dirs[$i] . DS . 'admin';
$apps[] = 'horde/admin';
if (!empty($module)) {
$module = 'horde/admin';
}
}
if (empty($lang)) {
$files = $this->search_file('help.xml', $dirs[$i] . DS . 'locale');
} else {
$files = array($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml');
}
$file_en = $dirs[$i] . DS . 'locale' . DS . 'en' . DS . 'help.xml';
if (!file_exists($file_en)) {
$this->cli->message(sprintf('There doesn\'t yet exist a help file for %s.', $this->cli->bold($apps[$i])), 'cli.warning');
$this->writeln();
continue;
}
foreach ($files as $file_loc) {
$locale = substr($file_loc, 0, strrpos($file_loc, DS));
$locale = substr($locale, strrpos($locale, DS) + 1);
if ($locale == 'en') {
continue;
}
if (!file_exists($file_loc)) {
$this->cli->message(sprintf('The %s help file for %s doesn\'t yet exist. Creating a new one.', $this->cli->bold($locale), $this->cli->bold($apps[$i])), 'cli.warning');
$dir_loc = substr($file_loc, 0, -9);
if (!is_dir($dir_loc)) {
if ($this->debug || $this->test) {
$this->writeln(sprintf('Making directory %s', $dir_loc));
}
if (!$this->test && !System::mkdir("-p $dir_loc")) {
$this->cli->message(sprintf('Could not create locale directory for locale %s:', $locale), 'cli.warning');
$this->writeln($dir_loc);
$this->writeln();
continue;
}
}
if ($this->debug || $this->test) {
$this->writeln(wordwrap(sprintf('Copying %s to %s', $file_en, $file_loc)));
}
if (!$this->test && !copy($file_en, $file_loc)) {
$this->cli->message(sprintf('Could not copy %s to %s', $file_en, $file_loc), 'cli.warning');
}
$this->writeln();
continue;
}
$this->writeln(sprintf('Updating %s help file for %s.', $this->cli->bold($locale), $this->cli->bold($apps[$i])));
if (!($doc_en = DOMDocument::load($file_en))) {
$this->cli->message(sprintf('There was an error opening the file %s. Try running the translation script with the flag -d to see any error messages from the xml parser.', $file_en), 'cli.warning');
$this->writeln();
continue 2;
}
$doc_en->encoding = 'UTF-8';
$doc_en->formatOutput = true;
if (!($doc_loc = DOMDocument::load($file_loc))) {
$this->cli->message(sprintf('There was an error opening the file %s. Try running the translation script with the flag -d to see any error messages from the xml parser.', $file_loc), 'cli.warning');
$this->writeln();
continue;
}
$count_uptodate = $count_new = $count_changed = $count_unknown = 0;
$date = date('Y-m-d');
$xpath = new DOMXPath($doc_loc);
foreach ($doc_en->getElementsByTagName('entry') as $entry) {
$view = $entry->parentNode;
while ($view->tagName != 'view' && $view != $doc_en) {
$view = $view->parentNode;
}
$query = '//entry[@id="' . $entry->getAttribute('id') . '"]';
if ($view->tagName == 'view') {
$query = '//view[@id="' . $view->getAttribute('id') . '"]'. $query;
}
$list = $xpath->query($query);
if ($list->length) {
$entry_loc = $doc_en->importNode($list->item(0), true);
if ($entry_loc->hasAttribute('md5') &&
md5($entry->textContent) != $entry_loc->getAttribute('md5')) {
$comment = $doc_en->createComment(" English entry ($date):\n" . str_replace('--', '&#45;&#45;', $doc_en->saveXML($entry)));
$entry_loc->appendChild($comment);
$entry_loc->setAttribute('state', 'changed');
$count_changed++;
} else {
if (!$entry_loc->hasAttribute('state')) {
$comment = $doc_en->createComment(" English entry ($date):\n" . str_replace('--', '&#45;&#45;', $doc_en->saveXML($entry)));
$entry_loc->appendChild($comment);
$entry_loc->setAttribute('state', 'unknown');
$count_unknown++;
} else {
$count_uptodate++;
}
}
} else {
$entry_loc = $doc_en->importNode($entry, true);
$entry_loc->setAttribute('state', 'new');
$count_new++;
}
$entry->parentNode->replaceChild($entry_loc, $entry);
}
$this->writeln(wordwrap(sprintf('Entries: %d total, %d up-to-date, %d new, %d changed, %d unknown',
$count_uptodate + $count_new + $count_changed + $count_unknown,
$count_uptodate, $count_new, $count_changed, $count_unknown)));
if ($this->debug || $this->test) {
$this->writeln(wordwrap(sprintf('Writing updated help file to %s.', $file_loc)));
}
if (!$this->test) {
$doc_en->save($file_loc);
}
$this->writeln();
}
}
}
/**
* Marks all entries in help files as translated.
*
* @param array $options Command line arguments.
*/
public function make_help($options)
{
$dirs = $this->dirs;
$apps = $this->apps;
foreach ($options as $option) {
switch ($option[0]) {
case 'h':
$this->usage();
$this->footer();
case 'l':
case '--locale':
$lang = $option[1];
break;
case 'm':
case '--module':
$module = $option[1];
break;
}
}
$files = array();
for ($i = 0; $i < count($dirs); $i++) {
if (!empty($module) && $module != $apps[$i]) {
continue;
}
if (!is_dir("$dirs[$i]/locale")) continue;
if ($apps[$i] == 'horde') {
$dirs[] = $dirs[$i] . DS . 'admin';
$apps[] = 'horde/admin';
if (!empty($module)) {
$module = 'horde/admin';
}
}
if (empty($lang)) {
$files = $this->search_file('help.xml', $dirs[$i] . DS . 'locale');
} else {
$files = array($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml');
}
$file_en = $dirs[$i] . DS . 'locale' . DS . 'en' . DS . 'help.xml';
if (!file_exists($file_en)) {
continue;
}
if (!($doc_en = DOMDocument::load($file_en))) {
$this->cli->message(sprintf('There was an error opening the file %s. Try running the translation script with the flag -d to see any error messages from the xml parser.', $file_en), 'cli.warning');
$this->writeln();
continue;
}
$xpath = new DOMXPath($doc_en);
foreach ($files as $file_loc) {
if (!file_exists($file_loc)) {
$this->writeln('Skipped...');
$this->writeln();
continue;
}
$locale = substr($file_loc, 0, strrpos($file_loc, DS));
$locale = substr($locale, strrpos($locale, DS) + 1);
if ($locale == 'en') continue;
$this->writeln(sprintf('Updating %s help file for %s.', $this->cli->bold($locale), $this->cli->bold($apps[$i])));
if (!($doc_loc = DOMDocument::load($file_loc))) {
$this->cli->message(sprintf('There was an error opening the file %s. Try running the translation script with the flag -d to see any error messages from the xml parser.', $file_loc), 'cli.warning');
$this->writeln();
continue;
}
$doc_loc->encoding = 'UTF-8';
$doc_loc->formatOutput = true;
$count_all = $count = 0;
foreach ($doc_loc->getElementsByTagName('entry') as $entry) {
foreach ($entry->childNodes as $child) {
if ($child->nodeType == XML_COMMENT_NODE &&
strstr($child->nodeValue, 'English entry')) {
$entry->removeChild($child);
}
}
$count_all++;
$view = $entry->parentNode;
while ($view->tagName != 'view' && $view != $doc_en) {
$view = $view->parentNode;
}
$query = '//entry[@id="' . $entry->getAttribute('id') . '"]';
if ($view->tagName == 'view') {
$query = '//view[@id="' . $view->getAttribute('id') . '"]'. $query;
}
$list = $xpath->query($query);
if ($list->length) {
$entry->setAttribute('md5', md5($list->item(0)->textContent));
$entry->setAttribute('state', 'uptodate');
$count++;
} else {
$this->cli->message(sprintf('No entry with the id "%s" exists in the original help file.', $entry->getAttribute('id')), 'cli.warning');
}
}
if (!$this->test) {
$doc_loc->save($file_loc);
}
$this->writeln(sprintf('%d of %d entries marked as up-to-date', $count, $count_all));
$this->writeln();
}
}
}
}
define('DS', DIRECTORY_SEPARATOR);
putenv('LANG=en');
$baseFile = __DIR__ . '/../lib/core.php';
if (file_exists($baseFile)) {
require_once $baseFile;
} else {
require_once 'PEAR/Config.php';
require_once PEAR_Config::singleton()
->get('horde_dir', null, 'pear.horde.org') . '/lib/core.php';
}
$c = Horde_Cli::init();
$c->writeln($c->bold('---------------------------'));
$c->writeln($c->bold('Horde translation generator'));
$c->writeln($c->bold('---------------------------'));
/* Instantiate main object. */
$script = new Horde_Translation_Script();
$script->cli = $c;
/* Sanity checks */
if (!extension_loaded('gettext')) {
$c->message('Gettext extension not found!', 'cli.error');
$script->footer();
}
$c->writeln('Loading libraries...');
$libs_found = true;
foreach (array('Console_Getopt', 'Console_Table', 'File_Find') as $class) {
if (class_exists($class)) {
$c->message(sprintf('%s found.', $class), 'cli.success');
} else {
$c->message(sprintf('%s not found.', $class), 'cli.error');
$libs_found = false;
}
}
if (!$libs_found) {
$c->writeln();
$c->writeln('Make sure that you have PEAR installed and in your include path.');
$c->writeln('include_path: ' . ini_get('include_path'));
$script->footer();
}
$c->writeln();
/* Ensure E_STRICT is off as we are calling PEAR */
$old_error_reporting = error_reporting(E_ALL & ~E_STRICT);
/* Commandline parameters */
$args = Console_Getopt::readPHPArgv();
$options = Console_Getopt::getopt($args, 'b:dht', array('base=', 'debug', 'help', 'test'));
if (PEAR::isError($options) && $args[0] == $_SERVER['PHP_SELF']) {
array_shift($args);
$options = Console_Getopt::getopt($args, 'b:dht', array('base=', 'debug', 'help', 'test'));
}
if (PEAR::isError($options)) {
$c->message('Argument error: ' . str_replace('Console_Getopt:', '', $options->getMessage()), 'cli.error');
$c->writeln();
$script->usage();
$script->footer();
}
$script->options = $options;
/* Back to old error reporting */
error_reporting($old_error_reporting);
if (empty($options[0][0]) && empty($options[1][0])) {
$c->message('No command specified.', 'cli.error');
$c->writeln();
$script->usage();
$script->footer();
}
foreach ($options[0] as $option) {
switch ($option[0]) {
case 'b':
case '--base':
define('BASE', realpath($option[1]));
break;
case 'd':
case '--debug':
$script->debug = true;
break;
case 't':
case '--test':
$script->test = true;
break;
case 'h':
case '--help':
$script->usage();
$script->footer();
}
}
if (!$script->debug) {
ini_set('error_reporting', false);
}
if (!defined('BASE')) {
if (is_dir(HORDE_BASE . '/.git')) {
define('BASE', HORDE_BASE . '/..');
} else {
define('BASE', HORDE_BASE);
}
}
if ($options[1][0] == 'help') {
$script->usage();
$script->footer();
}
$script->silence = $script->debug || OS_WINDOWS ? '' : ' 2> /dev/null';
$options_list = array(
'cleanup' => array('hl:m:', array('module=', 'locale=')),
'commit' => array('hl:m:nM:s', array('module=', 'locale=', 'new', 'message=')),
'commit-help'=> array('hl:m:nM:', array('module=', 'locale=', 'new', 'message=')),
'compendium' => array('hl:d:a:', array('locale=', 'directory=', 'add=')),
'extract' => array('hm:', array('module=')),
'init' => array('hl:m:nc:', array('module=', 'locale=', 'no-compendium', 'compendium=')),
'merge' => array('hl:m:c:n', array('module=', 'locale=', 'compendium=', 'no-compendium')),
'make' => array('hl:m:c:ns', array('module=', 'locale=', 'compendium=', 'no-compendium', 'statistics')),
'make-help' => array('hl:m:', array('module=', 'locale=')),
'update' => array('hl:m:c:n', array('module=', 'locale=', 'compendium=', 'no-compendium')),
'update-help'=> array('hl:m:', array('module=', 'locale=')),
'status' => array('hl:m:o:', array('module=', 'locale=', 'output='))
);
$options_arr = $options[1];
$cmd = array_shift($options_arr);
if (array_key_exists($cmd, $options_list)) {
$cmd_options = Console_Getopt::getopt($options_arr, $options_list[$cmd][0], $options_list[$cmd][1]);
if (PEAR::isError($cmd_options)) {
$c->message(str_replace('Console_Getopt:', '', $cmd_options->getMessage()), 'cli.error');
$c->writeln();
$script->usage();
$script->footer();
}
}
/* Searching modules */
$script->ff = new File_Find();
$script->check_binaries();
$c->writeln(sprintf('Searching Horde modules in %s', BASE));
$script->search_modules();
if ($script->debug) {
$c->writeln('Found directories:');
$c->writeln(implode("\n", $script->dirs));
}
$c->writeln(wordwrap(sprintf('Found modules: %s', implode(', ', $script->apps))));
$c->writeln();
switch ($cmd) {
case 'cleanup':
case 'commit':
case 'compendium':
case 'merge':
$script->$cmd($cmd_options[0]);
break;
case 'commit-help':
$script->commit($cmd_options[0], true);
break;
case 'extract':
$script->xtract($cmd_options[0]);
break;
case 'init':
$script->init($cmd_options[0]);
$c->writeln();
$script->merge($cmd_options[0]);
break;
case 'make':
$script->cleanup($cmd_options[0], true);
$c->writeln();
$script->make($cmd_options[0]);
break;
case 'make-help':
$script->make_help($cmd_options[0]);
break;
case 'update':
$script->xtract($cmd_options[0]);
$c->writeln();
$script->merge($cmd_options[0]);
break;
case 'update-help':
$script->update_help($cmd_options[0]);
break;
case 'status':
$script->merge($cmd_options[0]);
break;
default:
$c->message(sprintf('Unknown command: %s', $cmd), 'cli.error');
$c->writeln();
$script->usage();
$script->footer();
}
$script->footer();