From: Jan Schneider Date: Fri, 20 Aug 2010 15:36:45 +0000 (+0200) Subject: Remove remainders out of po/ directories. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=e26fa314084818661ea8b28b62415b81ac1a25b3;p=horde.git Remove remainders out of po/ directories. --- diff --git a/agora/po/.htaccess b/agora/po/.htaccess deleted file mode 100755 index 3a4288278..000000000 --- a/agora/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/agora/po/README b/agora/po/README deleted file mode 100755 index a985e94aa..000000000 --- a/agora/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/ansel/locale/.htaccess b/ansel/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/ansel/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/ansel/po/README b/ansel/po/README deleted file mode 100755 index a985e94aa..000000000 --- a/ansel/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/babel/locale/.htaccess b/babel/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/babel/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/beatnik/locale/.htaccess b/beatnik/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/beatnik/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/chora/po/.htaccess b/chora/po/.htaccess deleted file mode 100644 index 3a4288278..000000000 --- a/chora/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/chora/po/README b/chora/po/README deleted file mode 100644 index a4356a923..000000000 --- a/chora/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/crumb/po/README b/crumb/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/crumb/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/fima/locale/.htaccess b/fima/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/fima/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/fima/po/README b/fima/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/fima/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/folks/locale/.htaccess b/folks/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/folks/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/gollem/locale/.htaccess b/gollem/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/gollem/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/gollem/po/README b/gollem/po/README deleted file mode 100644 index a4356a923..000000000 --- a/gollem/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/hermes/locale/.htaccess b/hermes/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/hermes/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/hermes/po/README b/hermes/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/hermes/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/horde/bin/translation b/horde/bin/translation new file mode 100755 index 000000000..40260b61b --- /dev/null +++ b/horde/bin/translation @@ -0,0 +1,1511 @@ +#!/usr/bin/env php +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, '', $offset)) !== false) { + $left += 9; + $buffer = ''; + $linespan = 0; + while (($end = strpos($line, '', $left)) === false) { + $buffer .= substr($line, $left); + $left = 0; + $line = fgets($fp, 4096); + $linespan++; + if ($line === false) { + $c->message(sprintf(" 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 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>/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(); diff --git a/horde/docs/TRANSLATIONS b/horde/docs/TRANSLATIONS index 2e5860831..f82249806 100644 --- a/horde/docs/TRANSLATIONS +++ b/horde/docs/TRANSLATIONS @@ -2,12 +2,21 @@ Horde Translation Guide ========================= -:Author: Joris Braakman -:Author: Chuck Hagenbuch -:Author: Jan Schneider -:Contact: horde@lists.horde.org +:Author: Jan Schneider +:Author: Chuck Hagenbuch +:Author: Joris Braakman +: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 @@ -172,3 +181,496 @@ FreeBSD 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 , 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 \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:: + + selecting attachments + + UW-IMAP quirk + Address search + +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.:: + + + +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. diff --git a/horde/po/.htaccess b/horde/po/.htaccess deleted file mode 100644 index 3a4288278..000000000 --- a/horde/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/horde/po/README b/horde/po/README deleted file mode 100644 index 1f43253ff..000000000 --- a/horde/po/README +++ /dev/null @@ -1,492 +0,0 @@ -=================== - 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 , 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 \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:: - - selecting attachments - - UW-IMAP quirk - Address search - -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.:: - - - -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. diff --git a/horde/po/translation.php b/horde/po/translation.php deleted file mode 100755 index 857f47a34..000000000 --- a/horde/po/translation.php +++ /dev/null @@ -1,1511 +0,0 @@ -#!/usr/bin/php -q -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, '', $offset)) !== false) { - $left += 9; - $buffer = ''; - $linespan = 0; - while (($end = strpos($line, '', $left)) === false) { - $buffer .= substr($line, $left); - $left = 0; - $line = fgets($fp, 4096); - $linespan++; - if ($line === false) { - $c->message(sprintf(" 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 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>/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(); diff --git a/imp/po/.htaccess b/imp/po/.htaccess deleted file mode 100644 index 3a4288278..000000000 --- a/imp/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/imp/po/README b/imp/po/README deleted file mode 100644 index a4356a923..000000000 --- a/imp/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/ingo/locale/.htaccess b/ingo/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/ingo/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/ingo/po/README b/ingo/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/ingo/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/jeta/locale/.htaccess b/jeta/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/jeta/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/jeta/po/README b/jeta/po/README deleted file mode 100644 index a4356a923..000000000 --- a/jeta/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/jonah/po/.htaccess b/jonah/po/.htaccess deleted file mode 100644 index 3a4288278..000000000 --- a/jonah/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/jonah/po/README b/jonah/po/README deleted file mode 100644 index a4356a923..000000000 --- a/jonah/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/kastalia/locale/.htaccess b/kastalia/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/kastalia/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/kastalia/po/README b/kastalia/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/kastalia/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/kronolith/locale/.htaccess b/kronolith/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/kronolith/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/kronolith/po/README b/kronolith/po/README deleted file mode 100644 index a4356a923..000000000 --- a/kronolith/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/luxor/locale/.htaccess b/luxor/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/luxor/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/luxor/po/.htaccess b/luxor/po/.htaccess deleted file mode 100644 index 3a4288278..000000000 --- a/luxor/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/luxor/po/README b/luxor/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/luxor/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/mnemo/locale/.htaccess b/mnemo/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/mnemo/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/mnemo/po/README b/mnemo/po/README deleted file mode 100644 index a4356a923..000000000 --- a/mnemo/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/nag/locale/.htaccess b/nag/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/nag/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/nag/po/README b/nag/po/README deleted file mode 100644 index a4356a923..000000000 --- a/nag/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/news/locale/.htaccess b/news/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/news/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/operator/po/README b/operator/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/operator/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/passwd/locale/.htaccess b/passwd/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/passwd/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/passwd/po/.htaccess b/passwd/po/.htaccess deleted file mode 100644 index 3a4288278..000000000 --- a/passwd/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/passwd/po/README b/passwd/po/README deleted file mode 100644 index a4356a923..000000000 --- a/passwd/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/pastie/po/README b/pastie/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/pastie/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/skeleton/locale/.htaccess b/skeleton/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/skeleton/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/skeleton/po/README b/skeleton/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/skeleton/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/skoli/locale/.htaccess b/skoli/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/skoli/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/skoli/po/README b/skoli/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/skoli/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/timeobjects/locale/.htaccess b/timeobjects/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/timeobjects/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/trean/locale/.htaccess b/trean/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/trean/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/turba/po/.htaccess b/turba/po/.htaccess deleted file mode 100644 index 3a4288278..000000000 --- a/turba/po/.htaccess +++ /dev/null @@ -1 +0,0 @@ -Deny from all diff --git a/turba/po/README b/turba/po/README deleted file mode 100644 index a4356a923..000000000 --- a/turba/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/vilma/locale/.htaccess b/vilma/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/vilma/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/vilma/po/README b/vilma/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/vilma/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/whups/locale/.htaccess b/whups/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/whups/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/whups/po/README b/whups/po/README deleted file mode 100644 index a4356a923..000000000 --- a/whups/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README \ No newline at end of file diff --git a/wicked/locale/.htaccess b/wicked/locale/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/wicked/locale/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/wicked/po/README b/wicked/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/wicked/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README