+++ /dev/null
-Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
--- /dev/null
+Deny from all
+++ /dev/null
-Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+#!/usr/bin/env php
+<?php
+/**
+ * Translation helper application for the Horde framework.
+ *
+ * For usage information call:
+ * ./translation.php help
+ */
+
+function footer()
+{
+ global $c, $curdir;
+
+ $c->writeln();
+ $c->writeln('Please report any bugs to i18n@lists.horde.org.');
+
+ chdir($curdir);
+ exit;
+}
+
+function usage()
+{
+ global $options, $c;
+
+ if (count($options[1]) &&
+ ($options[1][0] == 'help' && !empty($options[1][1]) ||
+ !empty($options[1][0]) && in_array($options[1][0], array('commit', 'compendium', 'extract', 'init', 'make', 'merge')))) {
+ if ($options[1][0] == 'help') {
+ $cmd = $options[1][1];
+ } else {
+ $cmd = $options[1][0];
+ }
+ $c->writeln('Usage:' . ' translation.php [options] ' . $cmd . ' [command-options]');
+ if (!empty($cmd)) {
+ $c->writeln();
+ $c->writeln('Command options:');
+ }
+ switch ($cmd) {
+ case 'cleanup':
+ $c->writeln(' -l, --locale=ll_CC Use only this locale.');
+ $c->writeln(' -m, --module=MODULE Cleanup PO files only for this (Horde) module.');
+ break;
+ case 'commit':
+ case 'commit-help':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Commit translations only for this (Horde) module.');
+ $c->writeln(' -M, --message=MESSAGE Use this commit message instead of the default ones.');
+ $c->writeln(' -n, --new This is a new translation, commit also CREDITS,');
+ $c->writeln(' CHANGES and nls.php.');
+ $c->writeln(' -s, --skip Skip all modules that are not maintained in CVS.');
+ break;
+ case 'compendium':
+ $c->writeln(' -a, --add=FILE Add this PO file to the compendium. Useful to');
+ $c->writeln(' include a compendium from a different branch to');
+ $c->writeln(' the generated compendium.');
+ $c->writeln(' -d, --directory=DIR Create compendium in this directory.');
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ break;
+ case 'extract':
+ $c->writeln(' -m, --module=MODULE Generate POT file only for this (Horde) module.');
+ break;
+ case 'init':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Create a PO file only for this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
+ $c->writeln(' one (compendium.po in the horde/po directory).');
+ $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
+ break;
+ case 'make':
+ $c->writeln(' -l, --locale=ll_CC Use only this locale.');
+ $c->writeln(' -m, --module=MODULE Build MO files only for this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Merge new translations to this compendium file');
+ $c->writeln(' instead of the default one (compendium.po in the');
+ $c->writeln(' horde/po directory.');
+ $c->writeln(' -n, --no-compendium Don\'t merge new translations to the compendium.');
+ $c->writeln(' -s, --statistics Save translation statistics in a local file.');
+ break;
+ case 'make-help':
+ case 'update-help':
+ $c->writeln(' -l, --locale=ll_CC Use only this locale.');
+ $c->writeln(' -m, --module=MODULE Update help files only for this (Horde) module.');
+ break;
+ case 'merge':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Merge PO files only for this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
+ $c->writeln(' one (compendium.po in the horde/po directory).');
+ $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
+ break;
+ case 'update':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Update only this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
+ $c->writeln(' one (compendium.po in the horde/po directory).');
+ $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
+ break;
+ }
+ } else {
+ $c->writeln('Usage:' . ' translation.php [options] command [command-options]');
+ $c->writeln(str_repeat(' ', Horde_String::length('Usage:')) . ' translation.php [help|-h|--help] [command]');
+ $c->writeln();
+ $c->writeln('Helper application to create and maintain translations for the Horde');
+ $c->writeln('framework and its applications.');
+ $c->writeln('For an introduction read the file README in this directory.');
+ $c->writeln();
+ $c->writeln('Commands:');
+ $c->writeln(' help Show this help message.');
+ $c->writeln(' compendium Rebuild the compendium file. Warning: This overwrites the');
+ $c->writeln(' current compendium.');
+ $c->writeln(' extract Generate PO template (.pot) files.');
+ $c->writeln(' init Create one or more PO files for a new locale. Warning: This');
+ $c->writeln(' overwrites the existing PO files of this locale.');
+ $c->writeln(' merge Merge the current PO file with the current PO template file.');
+ $c->writeln(' update Run extract and merge sequent.');
+ $c->writeln(' update-help Extract all new and changed entries from the English XML help');
+ $c->writeln(' file and merge them with the existing ones.');
+ $c->writeln(' cleanup Cleans the PO files up from untranslated and obsolete entries.');
+ $c->writeln(' make Build binary MO files from the specified PO files.');
+ $c->writeln(' make-help Mark all entries in the XML help file being up-to-date and');
+ $c->writeln(' prepare the file for the next execution of update-help. You');
+ $c->writeln(' should only run make-help AFTER update-help and revising the');
+ $c->writeln(' help file.');
+ $c->writeln(' commit Commit translations to the CVS server.');
+ $c->writeln(' commit-help Commit help files to the CVS server.');
+ }
+
+ $c->writeln();
+ $c->writeln('Options:');
+ $c->writeln(' -b, --base=/PATH Full path to the (Horde) base directory that should be');
+ $c->writeln(' used.');
+ $c->writeln(' -d, --debug Show error messages from the executed binaries.');
+ $c->writeln(' -h, --help Show this help message.');
+ $c->writeln(' -t, --test Show the executed commands but don\'t run anything.');
+}
+
+function check_binaries()
+{
+ global $gettext_version, $c;
+
+ $c->writeln('Searching gettext binaries...');
+ foreach (array('gettext', 'msgattrib', 'msgcat', 'msgcomm', 'msgfmt', 'msginit', 'msgmerge', 'xgettext') as $binary) {
+ $GLOBALS[$binary] = System::which($binary);
+ if ($GLOBALS[$binary]) {
+ $c->message($binary . ' found: ' . $GLOBALS[$binary], 'cli.success');
+ } else {
+ $c->message($binary . ' not found', 'cli.error');
+ footer();
+ }
+ }
+ $c->writeln();
+
+ $out = '';
+ exec($GLOBALS['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) {
+ $GLOBALS['php_support'] = false;
+ $c->writeln();
+ $c->message($version_string, 'cli.warning');
+ $c->message('Your gettext version is too old and does not support PHP natively.', 'cli.warning');
+ $c->writeln('Not all strings will be extracted.');
+ } else {
+ $GLOBALS['php_support'] = true;
+ $c->message($version_string, 'cli.success');
+ }
+ $c->writeln();
+}
+
+function search_file($file, $dir = '.', $local = false)
+{
+ static $ff;
+ if (!isset($ff)) {
+ $ff = new File_Find();
+ }
+
+ if (substr($file, 0, 1) != DS) {
+ $file = "/$file/";
+ }
+
+ if ($local) {
+ $files = $ff->glob($file, $dir, 'perl');
+ $files = array_map(create_function('$file', 'return "' . $dir . DS . '" . $file;'), $files);
+ return $files;
+ } else {
+ return $ff->search($file, $dir, 'perl', false);
+ }
+}
+
+function search_ext($ext, $dir = '.', $local = false)
+{
+ return search_file("^[^.].*\\.$ext\$", $dir, $local);
+}
+
+function get_po_files($dir)
+{
+ $langs = 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;
+}
+
+function get_languages($dir)
+{
+ global $curdir;
+
+ chdir($dir);
+ $langs = get_po_files('locale');
+ $langs = array_map('basename', array_map('dirname', array_map('dirname', $langs)));
+ chdir($curdir);
+ return $langs;
+}
+
+function search_applications()
+{
+ $dirs = array();
+ if (is_dir(HORDE_BASE . DS . 'locale')) {
+ $dirs[] = HORDE_BASE;
+ }
+ $dh = opendir(BASE);
+ if ($dh) {
+ while ($entry = readdir($dh)) {
+ $dir = BASE . DS . $entry;
+ if (is_dir($dir) &&
+ substr($entry, 0, 1) != '.' &&
+ fileinode(HORDE_BASE) != fileinode($dir)) {
+ $sub = opendir($dir);
+ if ($sub) {
+ while ($subentry = readdir($sub)) {
+ if ($subentry == 'locale' && is_dir($dir . DS . $subentry)) {
+ $dirs[] = $dir;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $dirs;
+}
+
+function strip_horde($file)
+{
+ if (is_array($file)) {
+ return array_map('strip_horde', $file);
+ } else {
+ return str_replace(BASE . DS, '', $file);
+ }
+}
+
+function xtract()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c, $gettext_version, $silence, $curdir;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+
+ if ($GLOBALS['php_support']) {
+ $language = 'PHP';
+ } else {
+ $language = 'C++';
+ }
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ printf('Extracting from %s... ', $apps[$i]);
+ chdir($dirs[$i]);
+ $files = array();
+ if ($apps[$i] == 'horde') {
+ $files = search_ext('(php|inc)', '../framework');
+ $files[] = 'config/nls.php';
+ }
+ $files = array_merge($files, search_ext('(php|inc)'));
+ $files = array_filter($files, create_function('$file', 'return substr($file, 0, 9) != "." . DS . "config" . DS;'));
+ $files = array_merge($files, search_ext('dist', 'config'));
+ $file = 'locale' . DS . $apps[$i] . '.pot';
+ file_put_contents($file . '.list', implode("\n", $files));
+ if (file_exists($file) && !is_writable($file)) {
+ $c->message(sprintf('%s is not writable.', $file), 'cli.error');
+ footer();
+ }
+ $tmp_file = $file . '.tmp.pot';
+ $sh = $GLOBALS['xgettext'] . ' --language=' . $language .
+ ' --from-code=iso-8859-1 --keyword=_ --sort-output --copyright-holder="Horde Project" --msgid-bugs-address="dev@lists.horde.org" --files-from=' . $file . '.list --output=' . $tmp_file;
+ if ($debug) {
+ $sh .= $silence;
+ }
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ $c->writeln('In: ' . getcwd());
+ }
+ if (!$test) {
+ exec($sh);
+ }
+ unlink($file . '.list');
+ $diff = array();
+ if (file_exists($tmp_file)) {
+ $files = search_ext('html', 'templates');
+ if (!$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) {
+ $c->message(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 (!$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);
+ }
+ if (!$test) fclose($tmp);
+ $sh = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$tmp_file\" \"$file.templates\" --output-file \"$tmp_file\"" . $silence;
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) {
+ exec($sh);
+ unlink($file . '.templates');
+ }
+
+ /* Parse conf.xml files for <configphp> tags. */
+ if (file_exists('config/conf.xml')) {
+ if (!$test) $tmp = fopen($file . '.config', 'w');
+ $conf_content = file_get_contents('config/conf.xml');
+ if (preg_match_all('/<configphp .*?>([^<]*_\(".+?"\)[^<]*)<\/configphp>/s',
+ $conf_content, $matches)) {
+ foreach ($matches[1] as $configphp) {
+ if (preg_match_all('/_\("(.+?)"\)/', $configphp, $strings)) {
+ if (!$test) {
+ foreach ($strings[1] as $string) {
+ fwrite($tmp, "#: config/conf.xml\n");
+ fwrite($tmp, 'msgid "' . $string . "\"\n");
+ fwrite($tmp, 'msgstr ""' . "\n\n");
+ }
+ }
+ }
+ }
+ }
+ if (!$test) fclose($tmp);
+ $sh = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$tmp_file\" \"$file.config\" --output-file \"$tmp_file\"" . $silence;
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$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:/', $diff, PREG_GREP_INVERT);
+ }
+ }
+ if (!file_exists($file) || count($diff)) {
+ if (file_exists($file)) {
+ unlink($file);
+ }
+ rename($tmp_file, $file);
+ $c->writeln($c->green('updated'));
+ } else {
+ if (file_exists($tmp_file)) {
+ unlink($tmp_file);
+ }
+ $c->writeln($c->bold('not changed'));
+ }
+ chdir($curdir);
+ }
+}
+
+function merge()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c;
+
+ $compendium = ' --compendium="' . HORDE_BASE . DS . 'locale' . DS . 'compendium.po"';
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ 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;
+ }
+ }
+
+ cleanup();
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ $c->writeln(sprintf('Merging translation for module %s...', $c->bold($apps[$i])));
+ $dir = $dirs[$i] . DS . 'locale' . DS;
+ $po = $dir . '%s' . DS . 'LC_MESSAGES' . DS . $apps[$i] . '.po';
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!file_exists(sprintf($po, $lang))) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Merging locale %s... ', $c->bold($locale)));
+ $sh = $GLOBALS['msgmerge']
+ . sprintf(' --update -v%s "%s" "%s.pot"',
+ $compendium, sprintf($po, $locale), $dir . $apps[$i]);
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) exec($sh);
+ $c->writeln($c->green('done'));
+ }
+ }
+}
+
+function status()
+{
+ return;
+ global $cmd_options, $apps, $dirs, $debug, $test, $c;
+
+ $output = 'status.html';
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ 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($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ $c->writeln(sprintf('Generating status for module %s...', $c->bold($apps[$i])));
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!file_exists($dirs[$i] . '/locale/' . $lang . '/LC_MESSAGES/' . $apps[$i] . '.po')) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Status for locale %s... ', $c->bold($locale)));
+ }
+ }
+}
+
+function compendium()
+{
+ global $cmd_options, $dirs, $debug, $test, $c, $silence;
+
+ $dir = HORDE_BASE . DS . 'locale' . DS;
+ $add = '';
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ 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)) {
+ $c->message('No locale specified.', 'cli.error');
+ $c->writeln();
+ usage();
+ footer();
+ }
+ printf('Merging all %s.po files to the compendium... ', $lang);
+ $pofiles = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ $pofile = $dirs[$i] . DS . 'locale' . DS . $lang . DS . 'LC_MESSAGES' . DS . $apps[$i] . '.po';
+ if (file_exists($pofile)) {
+ $pofiles[] = $pofile;
+ }
+ }
+ if (!empty($dir) && substr($dir, -1) != DS) {
+ $dir .= DS;
+ }
+ $sh = $GLOBALS['msgcat'] . ' --sort-output ' . implode(' ', $pofiles) . $add . ' > ' . $dir . 'compendium.po ' . ($debug ? '' : $silence);
+ if ($debug || $test) {
+ $c->writeln();
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ } else {
+ $c->writeln($c->red('failed'));
+ }
+}
+
+function init()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+ if (empty($lang)) {
+ $lang = getenv('LANG');
+ }
+
+ $registry = new Horde_Registry(Horde_Registry::SESSION_NONE);
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ $package = ucfirst($apps[$i]);
+ $version = $registry->getVersion($apps[$i]);
+ printf('Initializing module %s... ', $apps[$i]);
+ $dir = $dirs[$i] . DS . 'locale' . DS;
+ $pot = $dir . $apps[$i] . '.pot';
+ if (!file_exists($pot)) {
+ $c->writeln();
+ $c->message(sprintf('%s not found. Run \'translation extract\' first.', $pot), 'cli.warning');
+ continue;
+ }
+ $sh = $GLOBALS['msginit'] . ' --no-translator -i ' . $pot;
+ if (!empty($lang)) {
+ $sh .= ' -o ' . $dir . $lang . '.po --locale=' . $lang;
+ }
+ if (!$debug) {
+ $sh .= $silence;
+ }
+ if (!empty($lang) && !OS_WINDOWS) {
+ $pofile = $dirs[$i] . '/po/' . $lang . '.po';
+ $sh .= "; sed 's/PACKAGE package/$package package/' $pofile " .
+ "| sed 's/PACKAGE VERSION/$package $version/' " .
+ "| sed 's/messages for PACKAGE/messages for $package/' " .
+ "| sed 's/Language-Team: none/Language-Team: i18n@lists.horde.org/' " .
+ "> $pofile.tmp";
+ }
+ if ($debug || $test) {
+ $c->writeln();
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ rename($pofile . '.tmp', $pofile);
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ } else {
+ $c->writeln($c->red('failed'));
+ }
+ }
+}
+
+function make()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence, $redir_err;
+
+ $compendium = HORDE_BASE . DS . 'locale' . DS . 'compendium.po';
+ $save_stats = false;
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ 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', $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($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ $c->writeln(sprintf('Building MO files for module %s...', $c->bold($apps[$i])));
+ $dir = $dirs[$i] . DS . 'locale' . DS . '%s' . DS . 'LC_MESSAGES' . DS;
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!file_exists(sprintf($dir, $lang) . $apps[$i] . '.po')) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Building locale %s...', $c->bold($locale)));
+ $targetdir = sprintf($dir, $locale);
+ $pofile = $targetdir . $apps[$i] . '.po';
+ if (!is_dir($targetdir)) {
+ if ($debug) {
+ $c->writeln(sprintf('Making directory %s', $targetdir));
+ }
+ if (!$test && !System::mkdir("-p $targetdir")) {
+ $c->message(sprintf('Could not create locale directory for locale %s:', $locale), 'cli.warning');
+ $c->writeln($targetdir);
+ $c->writeln();
+ continue;
+ }
+ }
+
+ /* 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 = $GLOBALS['msgfmt'] . " --check --output-file=/dev/null \"$pofile\" $redir_err";
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ if ($ret != 0) {
+ $c->message('An error has occured:', 'cli.warning');
+ $c->writeln(implode("\n", $out));
+ $c->writeln();
+ if ($apps[$i] == 'horde') {
+ continue 2;
+ }
+ continue;
+ }
+
+ /* Compile MO file. */
+ $sh = $GLOBALS['msgfmt'] . ' --statistics -o "' . $targetdir . DS . $apps[$i] . '.mo" ';
+ if ($apps[$i] != 'horde') {
+ $horde_po = $dirs[$horde] . DS . 'po' . DS . $locale . '.po';
+ if (!is_readable($horde_po)) {
+ $c->message(sprintf('The Horde PO file for the locale %s does not exist:', $locale), 'cli.warning');
+ $c->writeln($horde_po);
+ $c->writeln();
+ $sh .= '"' . $dirs[$i] . DS . 'po' . DS . $locale . '.po"';
+ } else {
+ $comm = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$pofile\"";
+ $sh = $comm . " \"$horde_po\" | $sh -";
+ }
+ } else {
+ $sh .= '"' . $pofile . '"';
+ }
+ $sh .= $redir_err;
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ $out = '';
+ if ($test) {
+ $ret = 0;
+ } else {
+ putenv('LANG=en');
+ exec($sh, $out, $ret);
+ putenv('LANG=' . $GLOBALS['language']);
+ }
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ $messages = array(0, 0, 0, $last_update);
+ if (preg_match('/(\d+) translated/', $out[0], $match)) {
+ $messages[0] = $match[1];
+ if (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 (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 (isset($horde_msg[$locale])) {
+ $messages[2] -= $horde_msg[$locale][2];
+ if ($messages[2] < 0) $messages[2] = 0;
+ }
+ }
+ if ($apps[$i] == 'horde') {
+ $horde_msg[$locale] = $messages;
+ }
+ $stats_array[$apps[$i]][$locale] = $messages;
+ $stats->addRow(array($apps[$i], $locale, $messages[0], $messages[1], $messages[2], $messages[3]));
+ } else {
+ $c->writeln($c->red('failed'));
+ exec($sh, $out, $ret);
+ $c->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... ', $c->bold($apps[$i]));
+ if (!empty($targetdir) && substr($targetdir, -1) != DS) {
+ $targetdir .= DS;
+ }
+ $sh = $GLOBALS['msgcat'] . " --sort-output \"$compendium\" \"$pofile\" > \"$compendium.tmp\"";
+ if (!$debug) {
+ $sh .= $silence;
+ }
+ if ($debug || $test) {
+ $c->writeln();
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ $out = '';
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ unlink($compendium);
+ rename($compendium . '.tmp', $compendium);
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ } else {
+ $c->writeln($c->red('failed'));
+ }
+ }
+ $c->writeln();
+ }
+ }
+ if (empty($module)) {
+ $c->writeln('Results:');
+ } else {
+ $c->writeln('Results (including Horde):');
+ }
+ $c->writeln($stats->getTable());
+ if ($save_stats) {
+ $fp = fopen('translation_stats.txt', 'w');
+ if ($fp) {
+ fwrite($fp, serialize($stats_array));
+ fclose($fp);
+ }
+ }
+}
+
+function cleanup($keep_untranslated = false)
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ $c->writeln(sprintf('Cleaning up PO files for module %s...', $c->bold($apps[$i])));
+ $po = $dirs[$i] . DS . 'locale' . DS . '%s' . DS . 'LC_MESSAGES' .DS . $apps[$i] . '.po';
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!file_exists(sprintf($po, $lang))) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Cleaning up locale %s... ', $c->bold($locale)));
+ $pofile = sprintf($po, $locale);
+ $sh = $GLOBALS['msgattrib'] . ($keep_untranslated ? '' : ' --translated') . " --no-obsolete --force-po \"$pofile\" > \"$pofile.tmp\"";
+ if (!$debug) {
+ $sh .= $silence;
+ }
+ if ($debug || $test) {
+ $c->writeln();
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ $out = '';
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ if ($ret == 0) {
+ unlink($pofile);
+ rename($pofile . '.tmp', $pofile);
+ $c->writeln($c->green('done'));
+ } else {
+ unlink($pofile . '.tmp', $pofile);
+ $c->writeln($c->red('failed'));
+ }
+ $c->writeln();
+ }
+ }
+}
+
+function commit($help_only = false)
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c;
+
+ $docs = $skip = $lang = false;
+ $git = is_dir(BASE . '/.git');
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ 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;
+ case 's':
+ case '--skip':
+ $skip = true;
+ break;
+ }
+ }
+ $files = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ if ((!empty($module) && $module != $apps[$i]) ||
+ (!$git && $skip && !is_dir($dirs[$i] . DS . 'CVS'))) {
+ 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, strip_horde(search_ext('xml', $dirs[$i] . DS . 'locale')));
+ } else {
+ $files = array_merge($files, strip_horde(get_po_files($dirs[$i] . DS . 'po')));
+ $files = array_merge($files, strip_horde(search_file('^[a-z]{2}_[A-Z]{2}', $dirs[$i] . DS . 'locale', true)));
+ }
+ } else {
+ if ($help_only &&
+ !file_exists($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml')) {
+ continue;
+ }
+ $files[] = strip_horde($dirs[$i] . DS . 'locale' . DS . $lang);
+ }
+ if ($docs && !$help_only && $apps[$i]) {
+ $files[] = strip_horde($dirs[$i] . DS . 'docs');
+ if ($apps[$i] == 'horde') {
+ $horde_conf = $dirs[array_search('horde', $dirs)] . DS . 'config' . DS;
+ $files[] = strip_horde($horde_conf . 'nls.php');
+ }
+ }
+ }
+ chdir(BASE);
+ if (count($files)) {
+ if ($docs) {
+ $c->writeln('Adding new files to repository:');
+ $add_files = array();
+ foreach ($files as $file) {
+ if (strstr($file, 'locale')) {
+ $add_files[] = $file;
+ $c->writeln($file);
+ }
+ }
+ foreach ($files as $file) {
+ if (strstr($file, 'locale')) {
+ if ($help_only) {
+ $add_files[] = $file . DS . '*.xml';
+ $c->writeln($file . DS . '*.xml');
+ } else {
+ $add_files[] = $file . DS . '*.xml ' . $file . DS . 'LC_MESSAGES';
+ $c->writeln($file . DS . "*.xml\n$file" . DS . 'LC_MESSAGES');
+ }
+ }
+ }
+ if (!$help_only) {
+ foreach ($files as $file) {
+ if (strstr($file, 'locale')) {
+ $c->writeln($add_files[] = $file . DS . 'LC_MESSAGES' . DS . '*.po');
+ $c->writeln($add_files[] = $file . DS . 'LC_MESSAGES' . DS . '*.mo');
+ }
+ }
+ }
+ $c->writeln();
+ if ($git) {
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln('git add ' . implode(' ', $add_files));
+ }
+ if (!$test) {
+ system('git add ' . implode(' ', $add_files));
+ }
+ } else {
+ foreach ($add_files as $add_file) {
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln('cvs add ' . $add_file);
+ }
+ if (!$test) {
+ system('cvs add ' . $add_file);
+ }
+ }
+ }
+ $c->writeln();
+ }
+ $c->writeln('Committing:');
+ $c->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.";
+ }
+ }
+ if ($git) {
+ $sh = 'git add ' . implode(' ', $files) . '; git commit -m "' . $msg . '"';
+ } else {
+ $sh = 'cvs commit -m "' . $msg . '" ' . implode(' ', $files);
+ }
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) system($sh);
+ if ($git) {
+ $c->message('You have to push the commit manually!', 'cli.warning');
+ }
+ }
+}
+
+function update_help()
+{
+ global $cmd_options, $dirs, $apps, $debug, $test, $last_error_msg, $c;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ 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 = 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_US' . DS . 'help.xml';
+ if (!file_exists($file_en)) {
+ $c->message(sprintf('There doesn\'t yet exist a help file for %s.', $c->bold($apps[$i])), 'cli.warning');
+ $c->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_US') {
+ continue;
+ }
+ if (!file_exists($file_loc)) {
+ $c->message(sprintf('The %s help file for %s doesn\'t yet exist. Creating a new one.', $c->bold($locale), $c->bold($apps[$i])), 'cli.warning');
+ $dir_loc = substr($file_loc, 0, -9);
+ if (!is_dir($dir_loc)) {
+ if ($debug || $test) {
+ $c->writeln(sprintf('Making directory %s', $dir_loc));
+ }
+ if (!$test && !System::mkdir("-p $dir_loc")) {
+ $c->message(sprintf('Could not create locale directory for locale %s:', $locale), 'cli.warning');
+ $c->writeln($dir_loc);
+ $c->writeln();
+ continue;
+ }
+ }
+ if ($debug || $test) {
+ $c->writeln(wordwrap(sprintf('Copying %s to %s', $file_en, $file_loc)));
+ }
+ if (!$test && !copy($file_en, $file_loc)) {
+ $c->message(sprintf('Could not copy %s to %s', $file_en, $file_loc), 'cli.warning');
+ }
+ $c->writeln();
+ continue;
+ }
+ $c->writeln(sprintf('Updating %s help file for %s.', $c->bold($locale), $c->bold($apps[$i])));
+
+ if (!($doc_en = DOMDocument::load($file_en))) {
+ $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_en), 'cli.warning');
+ $c->writeln();
+ continue 2;
+ }
+ $doc_en->encoding = 'UTF-8';
+ $doc_en->formatOutput = true;
+
+ if (!($doc_loc = DOMDocument::load($file_loc))) {
+ $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_loc), 'cli.warning');
+ $c->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) {
+ $list = $xpath->query('//entry[@id="' . $entry->getAttribute('id') . '"]');
+ 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('--', '--', $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('--', '--', $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);
+ }
+ $c->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 ($debug || $test) {
+ $c->writeln(wordwrap(sprintf('Writing updated help file to %s.', $file_loc)));
+ }
+ if (!$test) {
+ $doc_en->save($file_loc);
+ }
+ $c->writeln();
+ }
+ }
+}
+
+function make_help()
+{
+ global $cmd_options, $dirs, $apps, $debug, $test, $c;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ 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 = 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_US' . DS . 'help.xml';
+ if (!file_exists($file_en)) {
+ continue;
+ }
+
+ if (!($doc_en = DOMDocument::load($file_en))) {
+ $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_en), 'cli.warning');
+ $c->writeln();
+ continue;
+ }
+ $xpath = new DOMXPath($doc_en);
+
+ foreach ($files as $file_loc) {
+ if (!file_exists($file_loc)) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $locale = substr($file_loc, 0, strrpos($file_loc, DS));
+ $locale = substr($locale, strrpos($locale, DS) + 1);
+ if ($locale == 'en_US') continue;
+ $c->writeln(sprintf('Updating %s help file for %s.', $c->bold($locale), $c->bold($apps[$i])));
+
+ if (!($doc_loc = DOMDocument::load($file_loc))) {
+ $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_loc), 'cli.warning');
+ $c->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++;
+ $list = $xpath->query('//entry[@id="' . $entry->getAttribute('id') . '"]');
+ if ($list->length) {
+ $entry->setAttribute('md5', md5($list->item(0)->textContent));
+ $entry->setAttribute('state', 'uptodate');
+ $count++;
+ } else {
+ $c->message(sprintf('No entry with the id "%s" exists in the original help file.', $entry->getAttribute('id')), 'cli.warning');
+ }
+ }
+
+ if (!$test) {
+ $doc_loc->save($file_loc);
+ }
+ $c->writeln(sprintf('%d of %d entries marked as up-to-date', $count, $count_all));
+ $c->writeln();
+ }
+ }
+}
+
+$curdir = getcwd();
+define('DS', DIRECTORY_SEPARATOR);
+
+$language = getenv('LANG');
+if (empty($language)) {
+ $language = getenv('LANGUAGE');
+}
+
+require_once dirname(__FILE__) . '/../lib/Application.php';
+
+$c = new Horde_Cli();
+if (!$c->runningFromCLI()) {
+ $c->fatal('This script must be run from the command line.');
+}
+$c->init();
+
+$c->writeln($c->bold('---------------------------'));
+$c->writeln($c->bold('Horde translation generator'));
+$c->writeln($c->bold('---------------------------'));
+
+/* Sanity checks */
+if (!extension_loaded('gettext')) {
+ $c->message('Gettext extension not found!', 'cli.error');
+ footer();
+}
+
+$c->writeln('Loading libraries...');
+$libs_found = true;
+
+foreach (array('Console_Getopt' => 'Console/Getopt.php',
+ 'Console_Table' => 'Console/Table.php',
+ 'File_Find' => 'File/Find.php')
+ as $class => $file) {
+ include $file;
+ 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'));
+ footer();
+}
+$c->writeln();
+
+/* 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();
+ usage();
+ footer();
+}
+if (empty($options[0][0]) && empty($options[1][0])) {
+ $c->message('No command specified.', 'cli.error');
+ $c->writeln();
+ usage();
+ footer();
+}
+$debug = false;
+$test = false;
+foreach ($options[0] as $option) {
+ switch ($option[0]) {
+ case 'b':
+ case '--base':
+ define('BASE', realpath($option[1]));
+ break;
+ case 'd':
+ case '--debug':
+ $debug = true;
+ break;
+ case 't':
+ case '--test':
+ $test = true;
+ break;
+ case 'h':
+ case '--help':
+ usage();
+ footer();
+ }
+}
+if (!$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') {
+ usage();
+ footer();
+}
+$silence = $debug || OS_WINDOWS ? '' : ' 2> /dev/null';
+$redir_err = OS_WINDOWS ? '' : ' 2>&1';
+$options_list = array(
+ 'cleanup' => array('hl:m:', array('module=', 'locale=')),
+ 'commit' => array('hl:m:nM:s', array('module=', 'locale=', 'new', 'message=', 'skip')),
+ '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();
+ usage();
+ footer();
+ }
+}
+
+/* Searching applications */
+check_binaries();
+
+$c->writeln(sprintf('Searching Horde applications in %s', BASE));
+$dirs = search_applications();
+
+if ($debug) {
+ $c->writeln('Found directories:');
+ $c->writeln(implode("\n", $dirs));
+}
+
+$apps = strip_horde($dirs);
+$apps[0] = 'horde';
+$c->writeln(wordwrap(sprintf('Found applications: %s', implode(', ', $apps))));
+$c->writeln();
+
+switch ($cmd) {
+case 'cleanup':
+case 'commit':
+case 'compendium':
+case 'merge':
+ $cmd();
+ break;
+case 'commit-help':
+ commit(true);
+ break;
+case 'extract':
+ xtract();
+ break;
+case 'init':
+ init();
+ $c->writeln();
+ merge();
+ break;
+case 'make':
+ cleanup(true);
+ $c->writeln();
+ make();
+ break;
+case 'make-help':
+ make_help();
+ break;
+case 'update':
+ xtract();
+ $c->writeln();
+ merge();
+ break;
+case 'update-help':
+ update_help();
+ break;
+case 'status':
+ merge();
+ break;
+default:
+ $c->message(sprintf('Unknown command: %s', $cmd), 'cli.error');
+ $c->writeln();
+ usage();
+ footer();
+}
+
+footer();
Horde Translation Guide
=========================
-:Author: Joris Braakman <jbraakman@yahoo.com>
-:Author: Chuck Hagenbuch <chuck@horde.org>
-:Author: Jan Schneider <jan@horde.org>
-:Contact: horde@lists.horde.org
+:Author: Jan Schneider <jan@horde.org>
+:Author: Chuck Hagenbuch <chuck@horde.org>
+:Author: Joris Braakman <jbraakman@yahoo.com>
+:Contact: horde@lists.horde.org (for administrators)
+:Contact: i18n@lists.horde.org (for translators)
.. contents:: Contents
+.. section-numbering::
+
+
+---------------------
+ Administrator Guide
+---------------------
+
+For translator documentation see the `Translator Guide`_ below.
GNU gettext, PHP and Horde
To enable UTF-8 support in Horde, you also need UTF-8 support in
FreeBSD. This is not installed by default, you need to install the
``utf8locale-without-swidth-040319`` package or port.
+
+
+------------------
+ Translator Guide
+------------------
+
+
+Translation Tool
+================
+
+``translation`` is a small PHP script that should help translators doing
+their work.
+
+Any feedback, bug reports and feature requests should be send to the `i18n
+mailing list`_. This is also the right place for new translations and general
+discussions of i18n and l10n questions and problems.
+
+``translation`` is located in ``horde/bin``, so you will have to make
+``horde/bin`` your working directory in order to run the commands quoted
+below. But you can run the script from any directory, just adapt the directory
+to the script. See _Prerequisites if your PHP binary is not your binary search
+path.
+
+For a list of available commands run::
+
+ ./translation help
+
+For detailed help on a certain command run::
+
+ ./translation help command
+
+Additional information about creating translations and fixing problems can be
+found in the `Administrator Guide`_.
+
+.. _i18n mailing list: http://www.horde.org/mail/
+
+Prerequisites
+~~~~~~~~~~~~~
+
+To run this script you'll need a PHP command line executable with `gettext
+support`_ compiled in, and the basic `PEAR`_ libraries. The script expects
+your PHP executable to be in the binary search path. If your PHP executable
+cannot be found, either edit the first line of ``translation`` to reflect your
+location or call the script like::
+
+ /usr/local/bin/php translation.
+
+You'll need the `gettext`_ package version 0.12 or greater.
+
+You'll need the PEAR packages `Console_Getopt`_ 0.11 or greater,
+`Console_Table`_ and `File_Find`_. To install all needed packages, run::
+
+ pear upgrade PEAR Console_Getopt
+ pear install Console_Table File_Find
+
+or download the newest package from the `PEAR`_ server and install them
+manually in your PEAR directory.
+
+.. _gettext support: http://www.php.net/gettext/
+.. _gettext: http://www.gnu.org/software/gettext/
+.. _PEAR: http://pear.php.net/
+.. _Console_Getopt: http://pear.php.net/package/Console_Getopt/
+.. _Console_Table: http://pear.php.net/package/Console_Table/
+.. _File_Find: http://pear.php.net/package/File_Find/
+
+Creating a new translation
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To create a new translation you first have to extract all gettext messages
+from the PHP sources. There are already template files with the ``.pot``
+suffix in the ``locale`` directories that you can use if you have troubles
+extracting the messages, though these templates might be slightly
+outdated. Run::
+
+ ./translation extract
+
+You now have to create a new PO file for your locale. A locale has the form
+``ll_CC`` where ``ll`` is the two letter `ISO 639`_ code of the language and
+``CC`` the two letter `ISO 3166`_ code of the country, e.g. ``de_DE``,
+``en_US`` or ``pt_BR``.
+``translation`` is able to guess the locale from the ``LANG`` environment
+variable but it is safer to specify the locale with the ``-l`` parameter.
+To create the PO file run::
+
+ ./translation init -l ll_CC
+
+Now you can start the translation by editing the created ``ll_CC.po``
+files. It is important to set the charset for the locale in the
+``Content-Type:`` header to ``UTF-8``. You should fill out the the complete
+header of the created ``ll_CC.po`` file, e.g.::
+
+ # Dutch translation for Horde.
+ # Copyright 2004-2010 The Horde Project (http://www.horde.org/)
+ # This file is distributed under the same license as the Horde package.
+ # Joris Braakman <jbraakman@yahoo.com>, 2004.
+ #
+ msgid ""
+ msgstr ""
+ "Project-Id-Version: Horde 2.3\n"
+ "Report-Msgid-Bugs-To: dev@lists.horde.org\n"
+ "POT-Creation-Date: 2004-04-14 10:30+0200\n"
+ "PO-Revision-Date: 2004-04-14 17:17+02:00\n"
+ "Last-Translator: Joris Braakman <jbraakman@yahoo.com>\n"
+ "Language-Team: i18n@lists.horde.org\n"
+ "MIME-Version: 1.0\n"
+ "Content-Type: text/plain; charset=ISO-8859-1\n"
+ "Content-Transfer-Encoding: 8-bit\n"
+
+To compile the translations to a binary format run::
+
+ ./translation make -l ll_CC
+
+After you created the new translation you have to add entries for this locale
+in the configuration file horde/config/nls.php.
+
+If you create a new translation for a certain module but there are already
+translations for the same language for other modules, then you should use a
+compendium. This will save you a lot of time and it will make translations
+much more consistent.
+
+.. _ISO 639: http://www.loc.gov/standards/iso639-2/
+.. _ISO 3166: http://www.iso.ch/iso/en/prods-services/iso3166ma/index.html
+
+Compendiums
+~~~~~~~~~~~
+
+This tool utilizes a nice feature of gettext: compendium files. A compendium
+is a special PO file containing a set of translations recurring in many
+different packages. These compendiums will be used in the background without
+much intervention required. But you have to create and edit a compendium
+before you can use it.
+
+To create a compendium of all existing translations for a certain locale run::
+
+ ./translation compendium -l ll_CC
+
+The ``compendium.po`` being created will contain all modules' translations of
+this locale merged into a single file. You should take a closer look at this
+file because you may find a lot of special marked lines where you translated
+certain strings differently in the various modules. It's a good idea to fix
+the modules' translations now so that all modules use the same translations for
+the same strings. You can always recreate your compendium with the above
+command.
+
+If you're maintaining translations for different `branches`_ and assumed that
+you have all modules of the development branch in one directory and all of the
+stalbe branch in another, you probably want to share a compendium between
+these directories.
+
+To do this, you should first create a compendium in the stable branch, review
+it and fix all translations until you're happy with the results. Then create a
+second compendium in the head branch and include your first one with the --add
+option. Now fix the translations in this branch. If you're ready you can
+remove the first compendium and for now on use the compendium in the
+development branch for both branches. To do so, use the ``-c`` or
+``--compendium`` option to specify a path to your compendium.
+
+If you translate directly from a git checkout, this is usually not necessary
+because you work with all branches in the same directory and simply switch
+between them with ``git checkout``.
+
+.. _branches: http://www.horde.org/source/
+
+Updating translations
+~~~~~~~~~~~~~~~~~~~~~
+
+The process of updating translations is a cycle where you extract new gettext
+strings from the sources, translate those new strings or update the already
+translated strings and compile them after.
+
+To update the translation for a module, run::
+
+ ./translation update -m modulename -l ll_CC
+
+This extracts the new strings from the sources and tries to update them from
+already existing translations in the compendium. You just have to translate
+all untranslated strings in the ``modulename.po`` file in the
+``locale/ll_CC/LC_MESSAGES`` directory of the module you updated.
+
+If extracting new strings fails for some reason, you can use the provided
+``.pot`` file instead to update your translation::
+
+ ./translation merge -m modulename -l ll_CC
+
+If your compendium is in a different directory than the ``translation``
+script, you can specify the path to the compendium::
+
+ ./translation update -m modulename -l ll_CC -c /path/to/compendium
+
+Once this is done, you can compile the translation by calling::
+
+ ./translation make -m modulename -l ll_CC
+
+Extending existent translations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To have your own string (e.g. that you added to config files) displayed in
+several languages, you have to
+
+ 1. specify your texts as gettext arguments, in English, e.g. by using the
+ _() function.
+ 2. edit the translation files for the required national languages,
+ 3. compile those translation files.
+
+Be sure to keep records of your extensions, as you will probably have to
+repeat steps 2 and 3 after the next update.
+
+E.g., you plan to offer two IMAP servers to select from in the Webmail login
+screen:
+
+ 1. In ``horde/imp/config/servers.php``, you specify::
+
+ $servers['Central'] = array(
+ 'name' => _("Central Mail Service"),
+ ...
+ $servers['CompSci'] = array(
+ 'name' => _("Mail Service of Computer Science Dpt.")
+ ...
+
+ 2. In ``horde/imp/po/de_DE.po`` you add two entries::
+
+ msgid "Central Mail Service"
+ msgstr "Zentraler Mailserver"
+
+ msgid "Mail Service of Computer Science Dpt."
+ msgstr "Mailserver Informatik"
+
+ Likewise, you amend the translation files for other languages, as needed.
+
+ 3. You compile the translations using the commands::
+
+ ./translation make --module imp --no-compendium
+
+Access Keys
+===========
+
+Access keys, also known as shortcut keys, allow easy access to important
+functions, normally by hitting the Alt/Meta key in combination with another
+key. This key is marked in most user interfaces by being underlined.
+
+As the access key is part of the word representing the action being executed,
+it is in the translators responsibility to select an access key when he
+translates these words. The action is always a link in Horde. The access key
+of a link is selected by prefixing it with an underscore.
+
+The help link in the menu for example is always "_Help". This means that the
+"H" of the link will be underlined and the help can be opened by hitting
+Alt+H. In the PO file this string will appear as::
+
+ #: templates/menu/menu.inc:53
+ msgid "_Help"
+ msgstr ""
+
+A Spanish translator might want to translate this to::
+
+ #: templates/menu/menu.inc:53
+ msgid "_Help"
+ msgstr "_Ayuda"
+
+Translators of multibyte languages need to do this a bit differently as only
+ASCII characters are allowed for access keys. A Traditional Chinese translator
+might want to use::
+
+ #: templates/menu/menu.inc:53
+ msgid "_Help"
+ msgstr "_H說明"
+
+This renders to "說明(H)" in the interface and you can
+access this link with "H" as the access key.
+
+
+Right-to-Left Languages
+=======================
+
+Translations for languages that are written from right to left might cause
+unexpected behavior if parenthesis or similar characters appear inside a
+translated string. To fix this broken string rendering you have to insert
+special Unicode codepoints into the translated string.
+
+Before such a string in parenthesis, add the U+202D codepoint. If there is
+more right-to-left text to come after the closing parenthesis, add the U+202E
+codepoint after it. If using the PO mode of the Emacs editor you can add
+codepoints with the "ucs-insert" command.
+
+
+Help Texts
+==========
+
+Organization of the help files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The help texts are available in the ``horde/locale/``, and
+``horde/APP/locale/``, directories, where APP is any Horde application. Every
+available translation is kept in a file called ``help.xml``, in a subdirectory
+named according to RFC 3066. Examples:
+
+- Horde's original help texts are in the ``horde/locale/en_US/help.xml`` file.
+- IMP's Brazilian help texts are in the ``horde/imp/locale/pt_BR/help.xml``
+ file.
+
+When, for any desired application and locale, there is no ``help.xml`` file
+available, Horde's help system will use the application's
+``locale/en_US/help.xml`` file, instead.
+
+The help files must be encoded in the UTF-8 character set.
+
+There is no compilation step involved: Every modification to, or addition of,
+a ``help.xml`` file takes immediate effect.
+
+Syntax of the help files
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each help file must consist of syntactically valid XML code.
+
+There are no predefined entities beyond the XML standard entities:
+
+- < (less than)
+- > (greater than)
+- & (ampersand)
+- ' (ASCII apostrophe)
+- " (ASCII quotation mark)
+
+Any character available in the language's preferred character set can be
+entered as a numerical character reference (based on its Unicode scalar
+value), such as `` `` for the No-Break Space character.
+
+The general structure can be learned from the existing examples; of course,
+the XML tags must be syntactically valid, and properly nested.
+
+.. Note:: A PHP error message like ``Undefined index: url in
+ /var/www/horde/lib/Horde/Help.php`` means that you have mis-spelled,
+ or omitted, an attribute (``url``, in this example).
+
+The following tags are available:
+
++---------+--------+---------------+-------------------------------+
+| XML-Tag | Parent | Attributes | Purpose |
++=========+========+===============+===============================+
+| help | — | — | List of help texts |
++---------+--------+---------------+-------------------------------+
+| entry | help | id | Help text |
++---------+--------+---------------+-------------------------------+
+| title | entry | — | Entry in the help index |
++---------+--------+---------------+-------------------------------+
+| heading | entry | — | Level 2 heading |
++---------+--------+---------------+-------------------------------+
+| para | entry | — | Paragraph |
++---------+--------+---------------+-------------------------------+
+| ref | para | module, entry | Link into the help system |
++---------+--------+---------------+-------------------------------+
+| eref | para | url | External link |
++---------+--------+---------------+-------------------------------+
+| href | para | app, url | Link into a Horde application |
++---------+--------+---------------+-------------------------------+
+| b | para | — | Bold text |
++---------+--------+---------------+-------------------------------+
+| i | para | — | Italic text on new line |
++---------+--------+---------------+-------------------------------+
+| pre | entry | — | Example box |
++---------+--------+---------------+-------------------------------+
+| tip | entry | — | Tip/hint box |
++---------+--------+---------------+-------------------------------+
+| warn | entry | — | Warning box |
++---------+--------+---------------+-------------------------------+
+
+The elements marked ``para``, in the Parent column, may also be used within
+``pre``, ``tip``, and ``warn`` elements.
+
+The following attributes are available:
+
++---------+-----------+----------------------------------+
+| XML-Tag | Attribute | Purpose/Syntax |
++=========+===========+==================================+
+| entry | id | Anchor |
++---------+-----------+----------------------------------+
+| ref | module | Index of Horde application, see |
+| | | ``horde/config/registry.php`` |
+| +-----------+----------------------------------+
+| | entry | Id of help file entry, see above |
++---------+-----------+----------------------------------+
+| eref | url | arbitrary URL |
++---------+-----------+----------------------------------+
+| href | app | Index of Horde application, see |
+| | | ``horde/config/registry.php`` |
+| +-----------+----------------------------------+
+| | url | URL within Horde application |
++---------+-----------+----------------------------------+
+
+Examples::
+
+ <ref module="imp" entry="compose-attachments">selecting attachments</ref>
+ <eref url="http://wiki.horde.org/FAQ/User/IMP?referer=FAQ/User#toc17">
+ UW-IMAP quirk</eref>
+ <href app="turba" url="search.php">Address search</href>
+
+The `Translation Tool`_ will introduce additional attributes:
+
++---------+-----------+--------------------------------+
+| XML-Tag | Attribute | Purpose/Syntax |
++=========+===========+================================+
+| entry | state | See `Finishing a translation`_ |
+| +-----------+--------------------------------+
+| | md5 | Used internally |
++---------+-----------+--------------------------------+
+
+Reference from the Horde applications
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The typical incantation, e.g.::
+
+ <?php echo Horde_Help::link('imp', 'compose-buttons') ?>
+
+will yield a link, adorned with the ``help.png`` icon, to the entry marked
+``id="compose-buttons"`` in the IMP help file pertaining to the user's current
+locale. Usually, these links are placed in the template files, next to the
+item to be explained by the pertinent help entry.
+
+The Horde menu will usually contain a general help item, which is generated in
+``lib/Horde/Menu.php``.
+
+Creating a new translation
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If necessary, define a suitable locale id ``ll_CC``, complying with RFC
+3066. E.g., if you plan to add an Austrian translation, you would use
+``de_AT`` as your locale id.
+
+For Horde, and for all relevant applications, copy the ``help.xml`` files from
+the ``locale/en_US/`` subdirectories to the ``locale/ll_CC/`` subdirectories,
+creating them if necessary.
+
+Translate each new ``locale/ll_CC/help.xml`` file, leaving all tags and
+attributes unchanged. Just translate the text between the tags.
+
+.. Important:: The ``id`` attributes must be kept unchanged, under any
+ circumstances.
+
+If the application is already translated and only the help files are missing,
+then be sure to use the same terminology as the existing translation. In any
+case, try to use a lucid, coherent terminology.
+
+Finishing a translation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want to submit a translation to be included in the Horde code base,
+make sure that you have completed all translations. Then run::
+
+ ./translation make-help
+
+This will mark all entries as being up-to-date so that you or other
+translators can later see which entries have been changed since then.
+
+Updating an existing translation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Run::
+
+ ./translation update-help
+
+This will merge your existing help file with all changes from the original,
+English help file since your last translation. Changed entries are marked with
+the attribute ``state="changed"``, new entries with ``state="new"``. The
+original entry from the English help file is added in a comment below a
+changed entry, so that you can easily compare them.
+
+Translate the remaining English phrases, like discussed above. When your
+finished, remove any English entries and follow the steps at `Finishing a
+translation`_.
+
+Extending existent translations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you feel that some feature should be better explained to the end user, then
+proceed as following:
+
+1. Determine, whether you will have to extend an existing entry, or add a new
+ one.
+
+2. In the latter case, choose a suitable ``id`` for your new help entry.
+
+3. Edit the existing entry in, or add a new entry to, the application's
+ ``en_US/help.xml`` file. Change all translations needed in your site in the
+ same way. Be sure to use the same ``id`` everywhere.
+
+4. If you have added a new entry, you might want to add a link to it from a
+ template, as explained above in `Reference from the Horde applications`_.
+
+5. If you feel that your enhancement may be useful to other users, file an
+ enhancement request at http://bugs.horde.org/ with a patch including your
+ changes to the template and the ``help.xml`` files.
+
+6. In any case, keep notes of your changes, so you can apply them to later
+ versions if necessary.
+++ /dev/null
-Deny from all
+++ /dev/null
-===================
- Translation Guide
-===================
-
-:Authors: Jan Schneider
-:Contact: i18n@lists.horde.org
-
-.. contents::
-.. section-numbering::
-
-Translation Tool
-================
-
-``translation.php`` is a small PHP script that should help translators doing
-their work.
-
-Any feedback, bug reports and feature requests should be send to the `i18n
-mailing list`_. This is also the right place for new translations and general
-discussions of i18n and l10n questions and problems.
-
-``translation.php`` is located in ``horde/po``, so you will have to make
-``horde/po`` your working directory in order to run the commands quoted
-below. But you can run the script from any directory, just adapt the directory
-to the script. See _Prerequisites if your PHP binary is not located in
-``/usr/bin/php``.
-
-For a list of available commands run::
-
- ./translation.php help
-
-For detailed help on a certain command run::
-
- ./translation.php help command
-
-Additional information about creating translations and fixing problems can be
-found in the file ``horde/docs/TRANSLATION``.
-
-.. _i18n mailing list: http://www.horde.org/mail/
-
-Prerequisites
-~~~~~~~~~~~~~
-
-To run this script you'll need a PHP command line executable with `gettext
-support`_ compiled in, and the basic `PEAR`_ libraries. The script expects your
-PHP executable to be at ``/usr/bin/php``. If your executable is at another
-place, either edit the first line of ``translation.php`` to reflect your
-location or call the script like::
-
- /usr/local/bin/php translation.php.
-
-You'll need the `gettext`_ package version 0.12 or greater.
-
-You'll need the PEAR packages `Console_Getopt`_ 0.11 or greater,
-`Console_Table`_ and `File_Find`_. To install all needed packages, run::
-
- pear upgrade PEAR Console_Getopt
- pear install Console_Table File_Find
-
-or download the newest package from the `PEAR`_ server and install them
-manually in your PEAR directory.
-
-.. _gettext support: http://www.php.net/gettext/
-.. _gettext: http://www.gnu.org/software/gettext/
-.. _PEAR: http://pear.php.net/
-.. _Console_Getopt: http://pear.php.net/package/Console_Getopt/
-.. _Console_Table: http://pear.php.net/package/Console_Table/
-.. _File_Find: http://pear.php.net/package/File_Find/
-
-Creating a new translation
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To create a new translation you first have to extract all gettext messages
-from the PHP sources. There are already template files with the ``.pot``
-suffix in the ``po`` directories that you can use if you have troubles
-extracting the messages, though these templates might be slightly
-outdated. Run::
-
- ./translation.php extract
-
-You now have to create a new PO file for your locale. A locale has the form
-``ll_CC`` where ``ll`` is the two letter `ISO 639`_ code of the language and
-``CC`` the two letter `ISO 3166`_ code of the country, e.g. ``de_DE``,
-``en_US`` or ``pt_BR``.
-``translation.php`` is able to guess the locale from the ``LANG`` environment
-variable but it is safer to specify the locale with the ``-l`` parameter.
-To create the PO file run::
-
- ./translation.php init -l ll_CC
-
-Now you can start the translation by editing the created ``ll_CC.po``
-files. It is important to set the correct charset for the locale in the
-``Content-Type:`` header.
-You should fill out the the complete header of the created ``ll_CC.po`` file,
-e.g.::
-
- # Dutch translation for Horde.
- # Copyright 2004-2010 The Horde Project (http://www.horde.org/)
- # This file is distributed under the same license as the Horde package.
- # Joris Braakman <jbraakman@yahoo.com>, 2004.
- #
- msgid ""
- msgstr ""
- "Project-Id-Version: Horde 2.3\n"
- "Report-Msgid-Bugs-To: dev@lists.horde.org\n"
- "POT-Creation-Date: 2004-04-14 10:30+0200\n"
- "PO-Revision-Date: 2004-04-14 17:17+02:00\n"
- "Last-Translator: Joris Braakman <jbraakman@yahoo.com>\n"
- "Language-Team: i18n@lists.horde.org\n"
- "MIME-Version: 1.0\n"
- "Content-Type: text/plain; charset=ISO-8859-1\n"
- "Content-Transfer-Encoding: 8-bit\n"
-
-To compile the translations to a binary format run::
-
- ./translation.php make -l ll_CC
-
-After you created the new translation you have to add entries for this locale
-in the configuration file horde/config/nls.php.
-
-If you create a new translation for a certain module but there are already
-translations for the same language for other modules, then you should use a
-compendium. This will save you a lot of time and it will make translations
-much more consistent.
-
-.. _ISO 639: http://www.loc.gov/standards/iso639-2/
-.. _ISO 3166: http://www.iso.ch/iso/en/prods-services/iso3166ma/index.html
-
-Compendiums
-~~~~~~~~~~~
-
-This tool utilizes a nice feature of gettext: compendium files. A compendium
-is a special PO file containing a set of translations recurring in many
-different packages. These compendiums will be used in the background without
-much intervention required. But you have to create and edit a compendium
-before you can use it.
-
-To create a compendium of all existing translations for a certain locale run::
-
- ./translation.php compendium -l ll_CC
-
-The ``compendium.po`` being created will contain all modules' translations of
-this locale merged into a single file. You should take a closer look at this
-file because you may find a lot of special marked lines where you translated
-certain strings differently in the various modules. It's a good idea to fix
-the modules' translations now so that all modules use the same translations for
-the same strings. You can always recreate your compendium with the above
-command.
-
-If you're maintaining translations for different `branches`_ and assumed that
-you have all modules of the ``HEAD`` branches in one directory and all of the
-``FRAMEWORK_3`` branches in another, you probably want to share a compendium
-between these directories.
-
-To do this, you should first create a compendium in the ``FRAMEWORK_3``
-branch, review it and fix all translations until you're happy with the
-results. Then create a second compendium in the ``HEAD`` branch and include
-your first one with the --add option. Now fix the translations in this
-branch. If you're ready you can remove the first compendium and for now on use
-the compendium in the ``HEAD`` branch for both branches. To do so, use the
-``-c`` or ``--compendium`` option to specify a path to your compendium.
-
-.. _branches: http://www.horde.org/source/
-
-Updating translations
-~~~~~~~~~~~~~~~~~~~~~
-
-The process of updating translations is a cycle where you extract new gettext
-strings from the sources, translate those new strings or update the already
-translated strings and compile them after.
-
-To update the translation for a module, run::
-
- ./translation.php update -m modulename -l ll_CC
-
-This extracts the new strings from the sources and tries to update them from
-already existing translations in the compendium. You just have to translate
-all untranslated strings in the ``ll_CC.po`` file in the ``po`` directory of
-the module you updated.
-
-If extracting new strings fails for some reason, you can use the provided
-``.pot`` file to update your translation::
-
- ./translation.php merge -m modulename -l ll_CC
-
-If your compendium is in a different directory than the ``translation.php``
-script, you can specify the path to the compendium::
-
- ./translation.php update -m modulename -l ll_CC -c /path/to/compendium
-
-Once this is done, you can compile the translation by calling::
-
- ./translation.php make -m modulename -l ll_CC
-
-Extending existent translations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To have your own string (e.g. that you added to config files) displayed in
-several languages, you have to
-
- 1. specify your texts as gettext arguments, in English,
- 2. edit the translation files for the required national languages,
- 3. compile those translation files.
-
-Be sure to keep records of your extensions, as you will probably have to
-repeat steps 2 and 3 after the next update.
-
-E.g., you plan to offer two IMAP servers to select from in the Webmail login
-screen:
-
- 1. In ``horde/imp/config/servers.php``, you specify::
-
- $servers['Central'] = array(
- 'name' => _("Central Mail Service"),
- ...
- $servers['CompSci'] = array(
- 'name' => _("Mail Service of Computer Science Dpt.")
- ...
-
- 2. In ``horde/imp/po/de_DE.po`` you add two entries::
-
- msgid "Central Mail Service"
- msgstr "Zentraler Mailserver"
-
- msgid "Mail Service of Computer Science Dpt."
- msgstr "Mailserver Informatik"
-
- Likewise, you amend the translation files for other languages, as needed.
-
- 3. You compile the translations using the commands::
-
- ./po/translation.php make --module imp --no-compendium
-
-Access Keys
-===========
-
-Access keys, also known as shortcut keys, allow easy access to important
-functions, normally by hitting the Alt/Meta key in combination with another
-key. This key is marked in most user interfaces by being underlined.
-
-As the access key is part of the word representing the action being executed,
-it is in the translators responsibility to select an access key when he
-translates these words. The action is always a link in Horde. The access key
-of a link is selected by prefixing it with an underscore.
-
-The help link in the menu for example is always "_Help". This means that the
-"H" of the link will be underlined and the help can be opened by hitting
-Alt+H. In the PO file this string will appear as::
-
- #: templates/menu/menu.inc:53
- msgid "_Help"
- msgstr ""
-
-A Spanish translator might want to translate this to::
-
- #: templates/menu/menu.inc:53
- msgid "_Help"
- msgstr "_Ayuda"
-
-Translators of multibyte languages need to do this a bit differently as only
-ASCII characters are allowed for access keys. A Traditional Chinese translator
-might want to use::
-
- #: templates/menu/menu.inc:53
- msgid "_Help"
- msgstr "_H說明"
-
-This renders to "說明(H)" in the interface and you can
-access this link with "H" as the access key.
-
-
-Right-to-Left Languages
-=======================
-
-Translations for languages that are written from right to left might cause
-unexpected behavior if parenthesis or similar characters appear inside a
-translated string. To fix this broken string rendering you have to insert
-special Unicode codepoints into the translated string.
-
-Before such a string in parenthesis, add the U+202D codepoint. If there is
-more right-to-left text to come after the closing parenthesis, add the U+202E
-codepoint after it. If using the PO mode of the Emacs editor you can add
-codepoints with the "ucs-insert" command.
-
-
-Help Texts
-==========
-
-Organization of the help files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The help texts are available in the ``horde/locale/``, and
-``horde/APP/locale/``, directories, where APP is any Horde application. Every
-available translation is kept in a file called ``help.xml``, in a subdirectory
-named according to RFC 3066. Examples:
-
-- Horde's original help texts are in the ``horde/locale/en_US/help.xml`` file.
-- IMP's Brazilian help texts are in the ``horde/imp/locale/pt_BR/help.xml``
- file.
-
-When, for any desired application and locale, there is no ``help.xml`` file
-available, Horde's help system will use the application's
-``locale/en_US/help.xml`` file, instead.
-
-The help files must be encoded in the language's preferred character set.
-
-There is no compilation step involved: Every modification to, or addition of,
-a ``help.xml`` file takes immediate effect.
-
-Syntax of the help files
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Each help file must consist of syntactically valid XML code.
-
-There are no predefined entities beyond the XML standard entities:
-
-- < (less than)
-- > (greater than)
-- & (ampersand)
-- ' (ASCII apostrophe)
-- " (ASCII quotation mark)
-
-Any character available in the language's preferred character set can be
-entered as a numerical character reference (based on its Unicode scalar
-value), such as `` `` for the No-Break Space character.
-
-The general structure can be learned from the existing examples; of course,
-the XML tags must be syntactically valid, and properly nested.
-
-.. Note:: A PHP error message like ``Undefined index: url in
- /opt/test-webmail/horde/lib/Horde/Help.php`` means that you have
- mis-spelled, or omitted, an attribute (``url``, in this example).
-
-The following tags are available:
-
-+---------+--------+---------------+-------------------------------+
-| XML-Tag | Parent | Attributes | Purpose |
-+=========+========+===============+===============================+
-| help | — | — | List of help texts |
-+---------+--------+---------------+-------------------------------+
-| entry | help | id | Help text |
-+---------+--------+---------------+-------------------------------+
-| title | entry | — | Entry in the help index |
-+---------+--------+---------------+-------------------------------+
-| heading | entry | — | Level 2 heading |
-+---------+--------+---------------+-------------------------------+
-| para | entry | — | Paragraph |
-+---------+--------+---------------+-------------------------------+
-| ref | para | module, entry | Link into the help system |
-+---------+--------+---------------+-------------------------------+
-| eref | para | url | External link |
-+---------+--------+---------------+-------------------------------+
-| href | para | app, url | Link into a Horde application |
-+---------+--------+---------------+-------------------------------+
-| b | para | — | Bold text |
-+---------+--------+---------------+-------------------------------+
-| i | para | — | Italic text on new line |
-+---------+--------+---------------+-------------------------------+
-| pre | entry | — | Example box |
-+---------+--------+---------------+-------------------------------+
-| tip | entry | — | Tip/hint box |
-+---------+--------+---------------+-------------------------------+
-| warn | entry | — | Warning box |
-+---------+--------+---------------+-------------------------------+
-
-The elements marked ``para``, in the Parent column, may also be used within
-``pre``, ``tip``, and ``warn`` elements.
-
-The following attributes are available:
-
-+---------+-----------+----------------------------------+
-| XML-Tag | Attribute | Purpose/Syntax |
-+=========+===========+==================================+
-| entry | id | Anchor |
-+---------+-----------+----------------------------------+
-| ref | module | Index of Horde application, see |
-| | | ``horde/config/registry.php`` |
-| +-----------+----------------------------------+
-| | entry | Id of help file entry, see above |
-+---------+-----------+----------------------------------+
-| eref | url | arbitrary URL |
-+---------+-----------+----------------------------------+
-| href | app | Index of Horde application, see |
-| | | ``horde/config/registry.php`` |
-| +-----------+----------------------------------+
-| | url | URL within Horde application |
-+---------+-----------+----------------------------------+
-
-Examples::
-
- <ref module="imp" entry="compose-attachments">selecting attachments</ref>
- <eref url="http://wiki.horde.org/FAQ/User/IMP?referer=FAQ/User#toc17">
- UW-IMAP quirk</eref>
- <href app="turba" url="search.php">Address search</href>
-
-The `Translation Tool`_ will introduce additional attributes:
-
-+---------+-----------+--------------------------------+
-| XML-Tag | Attribute | Purpose/Syntax |
-+=========+===========+================================+
-| entry | state | See `Finishing a translation`_ |
-| +-----------+--------------------------------+
-| | md5 | Used internally |
-+---------+-----------+--------------------------------+
-
-Reference from the Horde applications
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The typical incantation, e.g.::
-
- <?php echo Horde_Help::link('imp', 'compose-buttons') ?>
-
-will yield a link, adorned with the ``help.png`` icon, to the entry marked
-``id="compose-buttons"`` in the IMP help file pertaining to the user's current
-locale. Usually, these links are placed in the template files, next to the
-item to be explained by the pertinent help entry.
-
-The Horde menu will usually contain a general help item, which is generated in
-``lib/Horde/Menu.php``.
-
-Creating a new translation
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If necessary, define a suitable locale id ``ll_CC``, complying with RFC
-3066. E.g., if you plan to add an Austrian translation, you would use
-``de_AT`` as your locale id.
-
-For Horde, and for all relevant applications, copy the ``help.xml`` files from
-the ``locale/en_US/`` subdirectories to the ``locale/ll_CC/`` subdirectories,
-creating them if necessary.
-
-Translate each new ``locale/ll_CC/help.xml`` file, leaving all tags and
-attributes unchanged. Just translate the text between the tags.
-
-.. Important:: The ``id`` attributes must be kept unchanged, under any
- circumstances.
-
-If the application is already translated and only the help files are missing,
-then be sure to use the same terminology as the existing translation. In any
-case, try to use a lucid, coherent terminology.
-
-Finishing a translation
-~~~~~~~~~~~~~~~~~~~~~~~
-
-If you want to submit a translation to be included in the Horde code base,
-make sure that you have completed all translations. Then run::
-
- ./translation.php make-help
-
-This will mark all entries as being up-to-date so that you or other
-translators can later see which entries have been changed since then.
-
-Updating an existing translation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Run::
-
- ./translation.php update-help
-
-This will merge your existing help file with all changes from the original,
-English help file since your last translation. Changed entries are marked with
-the attribute ``state="changed"``, new entries with ``state="new"``. The
-original entry from the English help file is added in a comment below a
-changed entry, so that you can easily compare them.
-
-Translate the remaining English phrases, like discussed above. When your
-finished, remove any English entries and follow the steps at `Finishing a
-translation`_.
-
-Extending existent translations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you feel that some feature should be better explained to the end user, then
-proceed as following:
-
-1. Determine, whether you will have to extend an existing entry, or add a new
- one.
-
-2. In the latter case, choose a suitable ``id`` for your new help entry.
-
-3. Edit the existing entry in, or add a new entry to, the application's
- ``en_US/help.xml`` file. Change all translations needed in your site in the
- same way. Be sure to use the same ``id`` everywhere.
-
-4. If you have added a new entry, you might want to add a link to it from a
- template, as explained above in `Reference from the Horde applications`_.
-
-5. If you feel that your enhancement may be useful to other users, file an
- enhancement request at http://bugs.horde.org/ with a patch including your
- changes to the template and the ``help.xml`` files.
-
-6. In any case, keep notes of your changes, so you can apply them to later
- versions if necessary.
+++ /dev/null
-#!/usr/bin/php -q
-<?php
-/**
- * Translation helper application for the Horde framework.
- *
- * For usage information call:
- * ./translation.php help
- */
-
-function footer()
-{
- global $c, $curdir;
-
- $c->writeln();
- $c->writeln('Please report any bugs to i18n@lists.horde.org.');
-
- chdir($curdir);
- exit;
-}
-
-function usage()
-{
- global $options, $c;
-
- if (count($options[1]) &&
- ($options[1][0] == 'help' && !empty($options[1][1]) ||
- !empty($options[1][0]) && in_array($options[1][0], array('commit', 'compendium', 'extract', 'init', 'make', 'merge')))) {
- if ($options[1][0] == 'help') {
- $cmd = $options[1][1];
- } else {
- $cmd = $options[1][0];
- }
- $c->writeln('Usage:' . ' translation.php [options] ' . $cmd . ' [command-options]');
- if (!empty($cmd)) {
- $c->writeln();
- $c->writeln('Command options:');
- }
- switch ($cmd) {
- case 'cleanup':
- $c->writeln(' -l, --locale=ll_CC Use only this locale.');
- $c->writeln(' -m, --module=MODULE Cleanup PO files only for this (Horde) module.');
- break;
- case 'commit':
- case 'commit-help':
- $c->writeln(' -l, --locale=ll_CC Use this locale.');
- $c->writeln(' -m, --module=MODULE Commit translations only for this (Horde) module.');
- $c->writeln(' -M, --message=MESSAGE Use this commit message instead of the default ones.');
- $c->writeln(' -n, --new This is a new translation, commit also CREDITS,');
- $c->writeln(' CHANGES and nls.php.');
- $c->writeln(' -s, --skip Skip all modules that are not maintained in CVS.');
- break;
- case 'compendium':
- $c->writeln(' -a, --add=FILE Add this PO file to the compendium. Useful to');
- $c->writeln(' include a compendium from a different branch to');
- $c->writeln(' the generated compendium.');
- $c->writeln(' -d, --directory=DIR Create compendium in this directory.');
- $c->writeln(' -l, --locale=ll_CC Use this locale.');
- break;
- case 'extract':
- $c->writeln(' -m, --module=MODULE Generate POT file only for this (Horde) module.');
- break;
- case 'init':
- $c->writeln(' -l, --locale=ll_CC Use this locale.');
- $c->writeln(' -m, --module=MODULE Create a PO file only for this (Horde) module.');
- $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
- $c->writeln(' one (compendium.po in the horde/po directory).');
- $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
- break;
- case 'make':
- $c->writeln(' -l, --locale=ll_CC Use only this locale.');
- $c->writeln(' -m, --module=MODULE Build MO files only for this (Horde) module.');
- $c->writeln(' -c, --compendium=FILE Merge new translations to this compendium file');
- $c->writeln(' instead of the default one (compendium.po in the');
- $c->writeln(' horde/po directory.');
- $c->writeln(' -n, --no-compendium Don\'t merge new translations to the compendium.');
- $c->writeln(' -s, --statistics Save translation statistics in a local file.');
- break;
- case 'make-help':
- case 'update-help':
- $c->writeln(' -l, --locale=ll_CC Use only this locale.');
- $c->writeln(' -m, --module=MODULE Update help files only for this (Horde) module.');
- break;
- case 'merge':
- $c->writeln(' -l, --locale=ll_CC Use this locale.');
- $c->writeln(' -m, --module=MODULE Merge PO files only for this (Horde) module.');
- $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
- $c->writeln(' one (compendium.po in the horde/po directory).');
- $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
- break;
- case 'update':
- $c->writeln(' -l, --locale=ll_CC Use this locale.');
- $c->writeln(' -m, --module=MODULE Update only this (Horde) module.');
- $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
- $c->writeln(' one (compendium.po in the horde/po directory).');
- $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
- break;
- }
- } else {
- $c->writeln('Usage:' . ' translation.php [options] command [command-options]');
- $c->writeln(str_repeat(' ', Horde_String::length('Usage:')) . ' translation.php [help|-h|--help] [command]');
- $c->writeln();
- $c->writeln('Helper application to create and maintain translations for the Horde');
- $c->writeln('framework and its applications.');
- $c->writeln('For an introduction read the file README in this directory.');
- $c->writeln();
- $c->writeln('Commands:');
- $c->writeln(' help Show this help message.');
- $c->writeln(' compendium Rebuild the compendium file. Warning: This overwrites the');
- $c->writeln(' current compendium.');
- $c->writeln(' extract Generate PO template (.pot) files.');
- $c->writeln(' init Create one or more PO files for a new locale. Warning: This');
- $c->writeln(' overwrites the existing PO files of this locale.');
- $c->writeln(' merge Merge the current PO file with the current PO template file.');
- $c->writeln(' update Run extract and merge sequent.');
- $c->writeln(' update-help Extract all new and changed entries from the English XML help');
- $c->writeln(' file and merge them with the existing ones.');
- $c->writeln(' cleanup Cleans the PO files up from untranslated and obsolete entries.');
- $c->writeln(' make Build binary MO files from the specified PO files.');
- $c->writeln(' make-help Mark all entries in the XML help file being up-to-date and');
- $c->writeln(' prepare the file for the next execution of update-help. You');
- $c->writeln(' should only run make-help AFTER update-help and revising the');
- $c->writeln(' help file.');
- $c->writeln(' commit Commit translations to the CVS server.');
- $c->writeln(' commit-help Commit help files to the CVS server.');
- }
-
- $c->writeln();
- $c->writeln('Options:');
- $c->writeln(' -b, --base=/PATH Full path to the (Horde) base directory that should be');
- $c->writeln(' used.');
- $c->writeln(' -d, --debug Show error messages from the executed binaries.');
- $c->writeln(' -h, --help Show this help message.');
- $c->writeln(' -t, --test Show the executed commands but don\'t run anything.');
-}
-
-function check_binaries()
-{
- global $gettext_version, $c;
-
- $c->writeln('Searching gettext binaries...');
- foreach (array('gettext', 'msgattrib', 'msgcat', 'msgcomm', 'msgfmt', 'msginit', 'msgmerge', 'xgettext') as $binary) {
- $GLOBALS[$binary] = System::which($binary);
- if ($GLOBALS[$binary]) {
- $c->message($binary . ' found: ' . $GLOBALS[$binary], 'cli.success');
- } else {
- $c->message($binary . ' not found', 'cli.error');
- footer();
- }
- }
- $c->writeln();
-
- $out = '';
- exec($GLOBALS['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) {
- $GLOBALS['php_support'] = false;
- $c->writeln();
- $c->message($version_string, 'cli.warning');
- $c->message('Your gettext version is too old and does not support PHP natively.', 'cli.warning');
- $c->writeln('Not all strings will be extracted.');
- } else {
- $GLOBALS['php_support'] = true;
- $c->message($version_string, 'cli.success');
- }
- $c->writeln();
-}
-
-function search_file($file, $dir = '.', $local = false)
-{
- static $ff;
- if (!isset($ff)) {
- $ff = new File_Find();
- }
-
- if (substr($file, 0, 1) != DS) {
- $file = "/$file/";
- }
-
- if ($local) {
- $files = $ff->glob($file, $dir, 'perl');
- $files = array_map(create_function('$file', 'return "' . $dir . DS . '" . $file;'), $files);
- return $files;
- } else {
- return $ff->search($file, $dir, 'perl', false);
- }
-}
-
-function search_ext($ext, $dir = '.', $local = false)
-{
- return search_file("^[^.].*\\.$ext\$", $dir, $local);
-}
-
-function get_po_files($dir)
-{
- $langs = 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;
-}
-
-function get_languages($dir)
-{
- global $curdir;
-
- chdir($dir);
- $langs = get_po_files('locale');
- $langs = array_map('basename', array_map('dirname', array_map('dirname', $langs)));
- chdir($curdir);
- return $langs;
-}
-
-function search_applications()
-{
- $dirs = array();
- if (is_dir(HORDE_BASE . DS . 'locale')) {
- $dirs[] = HORDE_BASE;
- }
- $dh = opendir(BASE);
- if ($dh) {
- while ($entry = readdir($dh)) {
- $dir = BASE . DS . $entry;
- if (is_dir($dir) &&
- substr($entry, 0, 1) != '.' &&
- fileinode(HORDE_BASE) != fileinode($dir)) {
- $sub = opendir($dir);
- if ($sub) {
- while ($subentry = readdir($sub)) {
- if ($subentry == 'locale' && is_dir($dir . DS . $subentry)) {
- $dirs[] = $dir;
- break;
- }
- }
- }
- }
- }
- }
-
- return $dirs;
-}
-
-function strip_horde($file)
-{
- if (is_array($file)) {
- return array_map('strip_horde', $file);
- } else {
- return str_replace(BASE . DS, '', $file);
- }
-}
-
-function xtract()
-{
- global $cmd_options, $apps, $dirs, $debug, $test, $c, $gettext_version, $silence, $curdir;
-
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- footer();
- case 'm':
- case '--module':
- $module = $option[1];
- break;
- }
- }
-
- if ($GLOBALS['php_support']) {
- $language = 'PHP';
- } else {
- $language = 'C++';
- }
- for ($i = 0; $i < count($dirs); $i++) {
- if (!empty($module) && $module != $apps[$i]) {
- continue;
- }
- printf('Extracting from %s... ', $apps[$i]);
- chdir($dirs[$i]);
- $files = array();
- if ($apps[$i] == 'horde') {
- $files = search_ext('(php|inc)', '../framework');
- $files[] = 'config/nls.php';
- }
- $files = array_merge($files, search_ext('(php|inc)'));
- $files = array_filter($files, create_function('$file', 'return substr($file, 0, 9) != "." . DS . "config" . DS;'));
- $files = array_merge($files, search_ext('dist', 'config'));
- $file = 'locale' . DS . $apps[$i] . '.pot';
- file_put_contents($file . '.list', implode("\n", $files));
- if (file_exists($file) && !is_writable($file)) {
- $c->message(sprintf('%s is not writable.', $file), 'cli.error');
- footer();
- }
- $tmp_file = $file . '.tmp.pot';
- $sh = $GLOBALS['xgettext'] . ' --language=' . $language .
- ' --from-code=iso-8859-1 --keyword=_ --sort-output --copyright-holder="Horde Project" --msgid-bugs-address="dev@lists.horde.org" --files-from=' . $file . '.list --output=' . $tmp_file;
- if ($debug) {
- $sh .= $silence;
- }
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln($sh);
- $c->writeln('In: ' . getcwd());
- }
- if (!$test) {
- exec($sh);
- }
- unlink($file . '.list');
- $diff = array();
- if (file_exists($tmp_file)) {
- $files = search_ext('html', 'templates');
- if (!$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) {
- $c->message(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 (!$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);
- }
- if (!$test) fclose($tmp);
- $sh = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$tmp_file\" \"$file.templates\" --output-file \"$tmp_file\"" . $silence;
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- if (!$test) {
- exec($sh);
- unlink($file . '.templates');
- }
-
- /* Parse conf.xml files for <configphp> tags. */
- if (file_exists('config/conf.xml')) {
- if (!$test) $tmp = fopen($file . '.config', 'w');
- $conf_content = file_get_contents('config/conf.xml');
- if (preg_match_all('/<configphp .*?>([^<]*_\(".+?"\)[^<]*)<\/configphp>/s',
- $conf_content, $matches)) {
- foreach ($matches[1] as $configphp) {
- if (preg_match_all('/_\("(.+?)"\)/', $configphp, $strings)) {
- if (!$test) {
- foreach ($strings[1] as $string) {
- fwrite($tmp, "#: config/conf.xml\n");
- fwrite($tmp, 'msgid "' . $string . "\"\n");
- fwrite($tmp, 'msgstr ""' . "\n\n");
- }
- }
- }
- }
- }
- if (!$test) fclose($tmp);
- $sh = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$tmp_file\" \"$file.config\" --output-file \"$tmp_file\"" . $silence;
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- if (!$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:/', $diff, PREG_GREP_INVERT);
- }
- }
- if (!file_exists($file) || count($diff)) {
- if (file_exists($file)) {
- unlink($file);
- }
- rename($tmp_file, $file);
- $c->writeln($c->green('updated'));
- } else {
- if (file_exists($tmp_file)) {
- unlink($tmp_file);
- }
- $c->writeln($c->bold('not changed'));
- }
- chdir($curdir);
- }
-}
-
-function merge()
-{
- global $cmd_options, $apps, $dirs, $debug, $test, $c;
-
- $compendium = ' --compendium="' . HORDE_BASE . DS . 'locale' . DS . 'compendium.po"';
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- 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;
- }
- }
-
- cleanup();
-
- for ($i = 0; $i < count($dirs); $i++) {
- if (!empty($module) && $module != $apps[$i]) {
- continue;
- }
- $c->writeln(sprintf('Merging translation for module %s...', $c->bold($apps[$i])));
- $dir = $dirs[$i] . DS . 'locale' . DS;
- $po = $dir . '%s' . DS . 'LC_MESSAGES' . DS . $apps[$i] . '.po';
- if (empty($lang)) {
- $langs = get_languages($dirs[$i]);
- } else {
- if (!file_exists(sprintf($po, $lang))) {
- $c->writeln('Skipped...');
- $c->writeln();
- continue;
- }
- $langs = array($lang);
- }
- foreach ($langs as $locale) {
- $c->writeln(sprintf('Merging locale %s... ', $c->bold($locale)));
- $sh = $GLOBALS['msgmerge']
- . sprintf(' --update -v%s "%s" "%s.pot"',
- $compendium, sprintf($po, $locale), $dir . $apps[$i]);
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- if (!$test) exec($sh);
- $c->writeln($c->green('done'));
- }
- }
-}
-
-function status()
-{
- return;
- global $cmd_options, $apps, $dirs, $debug, $test, $c;
-
- $output = 'status.html';
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- 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($dirs); $i++) {
- if (!empty($module) && $module != $apps[$i]) {
- continue;
- }
- $c->writeln(sprintf('Generating status for module %s...', $c->bold($apps[$i])));
- if (empty($lang)) {
- $langs = get_languages($dirs[$i]);
- } else {
- if (!file_exists($dirs[$i] . '/locale/' . $lang . '/LC_MESSAGES/' . $apps[$i] . '.po')) {
- $c->writeln('Skipped...');
- $c->writeln();
- continue;
- }
- $langs = array($lang);
- }
- foreach ($langs as $locale) {
- $c->writeln(sprintf('Status for locale %s... ', $c->bold($locale)));
- }
- }
-}
-
-function compendium()
-{
- global $cmd_options, $dirs, $debug, $test, $c, $silence;
-
- $dir = HORDE_BASE . DS . 'locale' . DS;
- $add = '';
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- 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)) {
- $c->message('No locale specified.', 'cli.error');
- $c->writeln();
- usage();
- footer();
- }
- printf('Merging all %s.po files to the compendium... ', $lang);
- $pofiles = array();
- for ($i = 0; $i < count($dirs); $i++) {
- $pofile = $dirs[$i] . DS . 'locale' . DS . $lang . DS . 'LC_MESSAGES' . DS . $apps[$i] . '.po';
- if (file_exists($pofile)) {
- $pofiles[] = $pofile;
- }
- }
- if (!empty($dir) && substr($dir, -1) != DS) {
- $dir .= DS;
- }
- $sh = $GLOBALS['msgcat'] . ' --sort-output ' . implode(' ', $pofiles) . $add . ' > ' . $dir . 'compendium.po ' . ($debug ? '' : $silence);
- if ($debug || $test) {
- $c->writeln();
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- if ($test) {
- $ret = 0;
- } else {
- exec($sh, $out, $ret);
- }
- if ($ret == 0) {
- $c->writeln($c->green('done'));
- } else {
- $c->writeln($c->red('failed'));
- }
-}
-
-function init()
-{
- global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence;
-
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- footer();
- case 'l':
- case '--locale':
- $lang = $option[1];
- break;
- case 'm':
- case '--module':
- $module = $option[1];
- break;
- }
- }
- if (empty($lang)) {
- $lang = getenv('LANG');
- }
-
- $registry = new Horde_Registry(Horde_Registry::SESSION_NONE);
-
- for ($i = 0; $i < count($dirs); $i++) {
- if (!empty($module) && $module != $apps[$i]) {
- continue;
- }
- $package = ucfirst($apps[$i]);
- $version = $registry->getVersion($apps[$i]);
- printf('Initializing module %s... ', $apps[$i]);
- $dir = $dirs[$i] . DS . 'locale' . DS;
- $pot = $dir . $apps[$i] . '.pot';
- if (!file_exists($pot)) {
- $c->writeln();
- $c->message(sprintf('%s not found. Run \'translation extract\' first.', $pot), 'cli.warning');
- continue;
- }
- $sh = $GLOBALS['msginit'] . ' --no-translator -i ' . $pot;
- if (!empty($lang)) {
- $sh .= ' -o ' . $dir . $lang . '.po --locale=' . $lang;
- }
- if (!$debug) {
- $sh .= $silence;
- }
- if (!empty($lang) && !OS_WINDOWS) {
- $pofile = $dirs[$i] . '/po/' . $lang . '.po';
- $sh .= "; sed 's/PACKAGE package/$package package/' $pofile " .
- "| sed 's/PACKAGE VERSION/$package $version/' " .
- "| sed 's/messages for PACKAGE/messages for $package/' " .
- "| sed 's/Language-Team: none/Language-Team: i18n@lists.horde.org/' " .
- "> $pofile.tmp";
- }
- if ($debug || $test) {
- $c->writeln();
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- if ($test) {
- $ret = 0;
- } else {
- exec($sh, $out, $ret);
- }
- rename($pofile . '.tmp', $pofile);
- if ($ret == 0) {
- $c->writeln($c->green('done'));
- } else {
- $c->writeln($c->red('failed'));
- }
- }
-}
-
-function make()
-{
- global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence, $redir_err;
-
- $compendium = HORDE_BASE . DS . 'locale' . DS . 'compendium.po';
- $save_stats = false;
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- 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', $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($dirs); $i++) {
- if (!empty($module) && $module != $apps[$i]) {
- continue;
- }
- $c->writeln(sprintf('Building MO files for module %s...', $c->bold($apps[$i])));
- $dir = $dirs[$i] . DS . 'locale' . DS . '%s' . DS . 'LC_MESSAGES' . DS;
- if (empty($lang)) {
- $langs = get_languages($dirs[$i]);
- } else {
- if (!file_exists(sprintf($dir, $lang) . $apps[$i] . '.po')) {
- $c->writeln('Skipped...');
- $c->writeln();
- continue;
- }
- $langs = array($lang);
- }
- foreach ($langs as $locale) {
- $c->writeln(sprintf('Building locale %s...', $c->bold($locale)));
- $targetdir = sprintf($dir, $locale);
- $pofile = $targetdir . $apps[$i] . '.po';
- if (!is_dir($targetdir)) {
- if ($debug) {
- $c->writeln(sprintf('Making directory %s', $targetdir));
- }
- if (!$test && !System::mkdir("-p $targetdir")) {
- $c->message(sprintf('Could not create locale directory for locale %s:', $locale), 'cli.warning');
- $c->writeln($targetdir);
- $c->writeln();
- continue;
- }
- }
-
- /* 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 = $GLOBALS['msgfmt'] . " --check --output-file=/dev/null \"$pofile\" $redir_err";
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- if ($test) {
- $ret = 0;
- } else {
- exec($sh, $out, $ret);
- }
- if ($ret != 0) {
- $c->message('An error has occured:', 'cli.warning');
- $c->writeln(implode("\n", $out));
- $c->writeln();
- if ($apps[$i] == 'horde') {
- continue 2;
- }
- continue;
- }
-
- /* Compile MO file. */
- $sh = $GLOBALS['msgfmt'] . ' --statistics -o "' . $targetdir . DS . $apps[$i] . '.mo" ';
- if ($apps[$i] != 'horde') {
- $horde_po = $dirs[$horde] . DS . 'po' . DS . $locale . '.po';
- if (!is_readable($horde_po)) {
- $c->message(sprintf('The Horde PO file for the locale %s does not exist:', $locale), 'cli.warning');
- $c->writeln($horde_po);
- $c->writeln();
- $sh .= '"' . $dirs[$i] . DS . 'po' . DS . $locale . '.po"';
- } else {
- $comm = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$pofile\"";
- $sh = $comm . " \"$horde_po\" | $sh -";
- }
- } else {
- $sh .= '"' . $pofile . '"';
- }
- $sh .= $redir_err;
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- $out = '';
- if ($test) {
- $ret = 0;
- } else {
- putenv('LANG=en');
- exec($sh, $out, $ret);
- putenv('LANG=' . $GLOBALS['language']);
- }
- if ($ret == 0) {
- $c->writeln($c->green('done'));
- $messages = array(0, 0, 0, $last_update);
- if (preg_match('/(\d+) translated/', $out[0], $match)) {
- $messages[0] = $match[1];
- if (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 (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 (isset($horde_msg[$locale])) {
- $messages[2] -= $horde_msg[$locale][2];
- if ($messages[2] < 0) $messages[2] = 0;
- }
- }
- if ($apps[$i] == 'horde') {
- $horde_msg[$locale] = $messages;
- }
- $stats_array[$apps[$i]][$locale] = $messages;
- $stats->addRow(array($apps[$i], $locale, $messages[0], $messages[1], $messages[2], $messages[3]));
- } else {
- $c->writeln($c->red('failed'));
- exec($sh, $out, $ret);
- $c->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... ', $c->bold($apps[$i]));
- if (!empty($targetdir) && substr($targetdir, -1) != DS) {
- $targetdir .= DS;
- }
- $sh = $GLOBALS['msgcat'] . " --sort-output \"$compendium\" \"$pofile\" > \"$compendium.tmp\"";
- if (!$debug) {
- $sh .= $silence;
- }
- if ($debug || $test) {
- $c->writeln();
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- $out = '';
- if ($test) {
- $ret = 0;
- } else {
- exec($sh, $out, $ret);
- }
- unlink($compendium);
- rename($compendium . '.tmp', $compendium);
- if ($ret == 0) {
- $c->writeln($c->green('done'));
- } else {
- $c->writeln($c->red('failed'));
- }
- }
- $c->writeln();
- }
- }
- if (empty($module)) {
- $c->writeln('Results:');
- } else {
- $c->writeln('Results (including Horde):');
- }
- $c->writeln($stats->getTable());
- if ($save_stats) {
- $fp = fopen('translation_stats.txt', 'w');
- if ($fp) {
- fwrite($fp, serialize($stats_array));
- fclose($fp);
- }
- }
-}
-
-function cleanup($keep_untranslated = false)
-{
- global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence;
-
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- footer();
- case 'l':
- case '--locale':
- $lang = $option[1];
- break;
- case 'm':
- case '--module':
- $module = $option[1];
- break;
- }
- }
-
- for ($i = 0; $i < count($dirs); $i++) {
- if (!empty($module) && $module != $apps[$i]) {
- continue;
- }
- $c->writeln(sprintf('Cleaning up PO files for module %s...', $c->bold($apps[$i])));
- $po = $dirs[$i] . DS . 'locale' . DS . '%s' . DS . 'LC_MESSAGES' .DS . $apps[$i] . '.po';
- if (empty($lang)) {
- $langs = get_languages($dirs[$i]);
- } else {
- if (!file_exists(sprintf($po, $lang))) {
- $c->writeln('Skipped...');
- $c->writeln();
- continue;
- }
- $langs = array($lang);
- }
- foreach ($langs as $locale) {
- $c->writeln(sprintf('Cleaning up locale %s... ', $c->bold($locale)));
- $pofile = sprintf($po, $locale);
- $sh = $GLOBALS['msgattrib'] . ($keep_untranslated ? '' : ' --translated') . " --no-obsolete --force-po \"$pofile\" > \"$pofile.tmp\"";
- if (!$debug) {
- $sh .= $silence;
- }
- if ($debug || $test) {
- $c->writeln();
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- $out = '';
- if ($test) {
- $ret = 0;
- } else {
- exec($sh, $out, $ret);
- }
- if ($ret == 0) {
- unlink($pofile);
- rename($pofile . '.tmp', $pofile);
- $c->writeln($c->green('done'));
- } else {
- unlink($pofile . '.tmp', $pofile);
- $c->writeln($c->red('failed'));
- }
- $c->writeln();
- }
- }
-}
-
-function commit($help_only = false)
-{
- global $cmd_options, $apps, $dirs, $debug, $test, $c;
-
- $docs = $skip = $lang = false;
- $git = is_dir(BASE . '/.git');
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- 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;
- case 's':
- case '--skip':
- $skip = true;
- break;
- }
- }
- $files = array();
- for ($i = 0; $i < count($dirs); $i++) {
- if ((!empty($module) && $module != $apps[$i]) ||
- (!$git && $skip && !is_dir($dirs[$i] . DS . 'CVS'))) {
- 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, strip_horde(search_ext('xml', $dirs[$i] . DS . 'locale')));
- } else {
- $files = array_merge($files, strip_horde(get_po_files($dirs[$i] . DS . 'po')));
- $files = array_merge($files, strip_horde(search_file('^[a-z]{2}_[A-Z]{2}', $dirs[$i] . DS . 'locale', true)));
- }
- } else {
- if ($help_only &&
- !file_exists($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml')) {
- continue;
- }
- $files[] = strip_horde($dirs[$i] . DS . 'locale' . DS . $lang);
- }
- if ($docs && !$help_only && $apps[$i]) {
- $files[] = strip_horde($dirs[$i] . DS . 'docs');
- if ($apps[$i] == 'horde') {
- $horde_conf = $dirs[array_search('horde', $dirs)] . DS . 'config' . DS;
- $files[] = strip_horde($horde_conf . 'nls.php');
- }
- }
- }
- chdir(BASE);
- if (count($files)) {
- if ($docs) {
- $c->writeln('Adding new files to repository:');
- $add_files = array();
- foreach ($files as $file) {
- if (strstr($file, 'locale')) {
- $add_files[] = $file;
- $c->writeln($file);
- }
- }
- foreach ($files as $file) {
- if (strstr($file, 'locale')) {
- if ($help_only) {
- $add_files[] = $file . DS . '*.xml';
- $c->writeln($file . DS . '*.xml');
- } else {
- $add_files[] = $file . DS . '*.xml ' . $file . DS . 'LC_MESSAGES';
- $c->writeln($file . DS . "*.xml\n$file" . DS . 'LC_MESSAGES');
- }
- }
- }
- if (!$help_only) {
- foreach ($files as $file) {
- if (strstr($file, 'locale')) {
- $c->writeln($add_files[] = $file . DS . 'LC_MESSAGES' . DS . '*.po');
- $c->writeln($add_files[] = $file . DS . 'LC_MESSAGES' . DS . '*.mo');
- }
- }
- }
- $c->writeln();
- if ($git) {
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln('git add ' . implode(' ', $add_files));
- }
- if (!$test) {
- system('git add ' . implode(' ', $add_files));
- }
- } else {
- foreach ($add_files as $add_file) {
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln('cvs add ' . $add_file);
- }
- if (!$test) {
- system('cvs add ' . $add_file);
- }
- }
- }
- $c->writeln();
- }
- $c->writeln('Committing:');
- $c->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.";
- }
- }
- if ($git) {
- $sh = 'git add ' . implode(' ', $files) . '; git commit -m "' . $msg . '"';
- } else {
- $sh = 'cvs commit -m "' . $msg . '" ' . implode(' ', $files);
- }
- if ($debug || $test) {
- $c->writeln('Executing:');
- $c->writeln($sh);
- }
- if (!$test) system($sh);
- if ($git) {
- $c->message('You have to push the commit manually!', 'cli.warning');
- }
- }
-}
-
-function update_help()
-{
- global $cmd_options, $dirs, $apps, $debug, $test, $last_error_msg, $c;
-
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- 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 = 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_US' . DS . 'help.xml';
- if (!file_exists($file_en)) {
- $c->message(sprintf('There doesn\'t yet exist a help file for %s.', $c->bold($apps[$i])), 'cli.warning');
- $c->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_US') {
- continue;
- }
- if (!file_exists($file_loc)) {
- $c->message(sprintf('The %s help file for %s doesn\'t yet exist. Creating a new one.', $c->bold($locale), $c->bold($apps[$i])), 'cli.warning');
- $dir_loc = substr($file_loc, 0, -9);
- if (!is_dir($dir_loc)) {
- if ($debug || $test) {
- $c->writeln(sprintf('Making directory %s', $dir_loc));
- }
- if (!$test && !System::mkdir("-p $dir_loc")) {
- $c->message(sprintf('Could not create locale directory for locale %s:', $locale), 'cli.warning');
- $c->writeln($dir_loc);
- $c->writeln();
- continue;
- }
- }
- if ($debug || $test) {
- $c->writeln(wordwrap(sprintf('Copying %s to %s', $file_en, $file_loc)));
- }
- if (!$test && !copy($file_en, $file_loc)) {
- $c->message(sprintf('Could not copy %s to %s', $file_en, $file_loc), 'cli.warning');
- }
- $c->writeln();
- continue;
- }
- $c->writeln(sprintf('Updating %s help file for %s.', $c->bold($locale), $c->bold($apps[$i])));
-
- if (!($doc_en = DOMDocument::load($file_en))) {
- $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_en), 'cli.warning');
- $c->writeln();
- continue 2;
- }
- $doc_en->encoding = 'UTF-8';
- $doc_en->formatOutput = true;
-
- if (!($doc_loc = DOMDocument::load($file_loc))) {
- $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_loc), 'cli.warning');
- $c->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) {
- $list = $xpath->query('//entry[@id="' . $entry->getAttribute('id') . '"]');
- 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('--', '--', $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('--', '--', $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);
- }
- $c->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 ($debug || $test) {
- $c->writeln(wordwrap(sprintf('Writing updated help file to %s.', $file_loc)));
- }
- if (!$test) {
- $doc_en->save($file_loc);
- }
- $c->writeln();
- }
- }
-}
-
-function make_help()
-{
- global $cmd_options, $dirs, $apps, $debug, $test, $c;
-
- foreach ($cmd_options[0] as $option) {
- switch ($option[0]) {
- case 'h':
- usage();
- 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 = 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_US' . DS . 'help.xml';
- if (!file_exists($file_en)) {
- continue;
- }
-
- if (!($doc_en = DOMDocument::load($file_en))) {
- $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_en), 'cli.warning');
- $c->writeln();
- continue;
- }
- $xpath = new DOMXPath($doc_en);
-
- foreach ($files as $file_loc) {
- if (!file_exists($file_loc)) {
- $c->writeln('Skipped...');
- $c->writeln();
- continue;
- }
- $locale = substr($file_loc, 0, strrpos($file_loc, DS));
- $locale = substr($locale, strrpos($locale, DS) + 1);
- if ($locale == 'en_US') continue;
- $c->writeln(sprintf('Updating %s help file for %s.', $c->bold($locale), $c->bold($apps[$i])));
-
- if (!($doc_loc = DOMDocument::load($file_loc))) {
- $c->message(sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_loc), 'cli.warning');
- $c->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++;
- $list = $xpath->query('//entry[@id="' . $entry->getAttribute('id') . '"]');
- if ($list->length) {
- $entry->setAttribute('md5', md5($list->item(0)->textContent));
- $entry->setAttribute('state', 'uptodate');
- $count++;
- } else {
- $c->message(sprintf('No entry with the id "%s" exists in the original help file.', $entry->getAttribute('id')), 'cli.warning');
- }
- }
-
- if (!$test) {
- $doc_loc->save($file_loc);
- }
- $c->writeln(sprintf('%d of %d entries marked as up-to-date', $count, $count_all));
- $c->writeln();
- }
- }
-}
-
-$curdir = getcwd();
-define('DS', DIRECTORY_SEPARATOR);
-
-$language = getenv('LANG');
-if (empty($language)) {
- $language = getenv('LANGUAGE');
-}
-
-require_once dirname(__FILE__) . '/../lib/Application.php';
-
-$c = new Horde_Cli();
-if (!$c->runningFromCLI()) {
- $c->fatal('This script must be run from the command line.');
-}
-$c->init();
-
-$c->writeln($c->bold('---------------------------'));
-$c->writeln($c->bold('Horde translation generator'));
-$c->writeln($c->bold('---------------------------'));
-
-/* Sanity checks */
-if (!extension_loaded('gettext')) {
- $c->message('Gettext extension not found!', 'cli.error');
- footer();
-}
-
-$c->writeln('Loading libraries...');
-$libs_found = true;
-
-foreach (array('Console_Getopt' => 'Console/Getopt.php',
- 'Console_Table' => 'Console/Table.php',
- 'File_Find' => 'File/Find.php')
- as $class => $file) {
- include $file;
- 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'));
- footer();
-}
-$c->writeln();
-
-/* 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();
- usage();
- footer();
-}
-if (empty($options[0][0]) && empty($options[1][0])) {
- $c->message('No command specified.', 'cli.error');
- $c->writeln();
- usage();
- footer();
-}
-$debug = false;
-$test = false;
-foreach ($options[0] as $option) {
- switch ($option[0]) {
- case 'b':
- case '--base':
- define('BASE', realpath($option[1]));
- break;
- case 'd':
- case '--debug':
- $debug = true;
- break;
- case 't':
- case '--test':
- $test = true;
- break;
- case 'h':
- case '--help':
- usage();
- footer();
- }
-}
-if (!$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') {
- usage();
- footer();
-}
-$silence = $debug || OS_WINDOWS ? '' : ' 2> /dev/null';
-$redir_err = OS_WINDOWS ? '' : ' 2>&1';
-$options_list = array(
- 'cleanup' => array('hl:m:', array('module=', 'locale=')),
- 'commit' => array('hl:m:nM:s', array('module=', 'locale=', 'new', 'message=', 'skip')),
- '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();
- usage();
- footer();
- }
-}
-
-/* Searching applications */
-check_binaries();
-
-$c->writeln(sprintf('Searching Horde applications in %s', BASE));
-$dirs = search_applications();
-
-if ($debug) {
- $c->writeln('Found directories:');
- $c->writeln(implode("\n", $dirs));
-}
-
-$apps = strip_horde($dirs);
-$apps[0] = 'horde';
-$c->writeln(wordwrap(sprintf('Found applications: %s', implode(', ', $apps))));
-$c->writeln();
-
-switch ($cmd) {
-case 'cleanup':
-case 'commit':
-case 'compendium':
-case 'merge':
- $cmd();
- break;
-case 'commit-help':
- commit(true);
- break;
-case 'extract':
- xtract();
- break;
-case 'init':
- init();
- $c->writeln();
- merge();
- break;
-case 'make':
- cleanup(true);
- $c->writeln();
- make();
- break;
-case 'make-help':
- make_help();
- break;
-case 'update':
- xtract();
- $c->writeln();
- merge();
- break;
-case 'update-help':
- update_help();
- break;
-case 'status':
- merge();
- break;
-default:
- $c->message(sprintf('Unknown command: %s', $cmd), 'cli.error');
- $c->writeln();
- usage();
- footer();
-}
-
-footer();
+++ /dev/null
-Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
+++ /dev/null
-Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
--- /dev/null
+Deny from all
+++ /dev/null
-Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README
\ No newline at end of file
--- /dev/null
+Deny from all
+++ /dev/null
-see horde/po/README