false, 'default_domain' => 'messages', 'scan_hidden'=> false, 'delimiters' => array(), 'directories' => array(), 'files' => array(), 'exclude_directories' => array(), 'exclude_files' => array(), 'suffixes' => array(), 'charset' => '' ); /** * File list * * @var array */ protected $_file_list = array(); /** * Container for found gettext strings */ protected $_domains = array(); /** * Whether to display warnings or not */ protected $_warnings = true; /** * Executes scan for gettext strings */ public function __construct($args = null) { // make sure that all the requirements of this script are met $this->checkRequirements(); // import command line args $this->importArgs(); // compile list of files to be scanned $this->compileFileList(); // execute scan process $this->searchFilesForGettextStrings(); // print some statistics $this->printStatistics(); // write the output files $this->_writePOTs(); } /** * Checks if the environment meets the requirements */ protected function checkRequirements () { // make sure the script is used through the command line if (substr(php_sapi_name(), 0, 3) != 'cli') { $this->triggerError("Please run this script only using a php cli binary.\r\n"); } // make sure we're running php 5.0.0 or higher if (version_compare('5.0.3', phpversion(), '>')) { $this->triggerError("PHP 5.0.3 or higher required; 5.1.0 and higher recommended.\r\n"); } } /** * Imports arguments from command line and executes some sanity checks. * If the --help argument is supplied, the method will take care of * displaying the usage instructions. */ protected function importArgs () { // filter our arguments out of the list of command line arguments foreach ($_SERVER['argv'] as $_arg) { // handle --help argument if (preg_match('=^-{2}help=', $_arg, $matches)) { $this->_args['help'] = true; } else { $this->_args['help'] = false; } // handle --default-domain= argument if (preg_match('=^-{2}default-domain\=\"?\'?([a-z]*?)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1])) { $this->_args['default_domain'] = $matches[1]; } else { $this->_args['default_domain'] = 'messages'; } } // handle --scan-hidden= argument if (preg_match('=^-{2}scan-hidden\=\"?\'?(true|false)\"?\'?$=', $_arg, $matches)) { if ($matches[1] == 'true') { $this->_args['scan_hidden'] = true; } else { $this->_args['scan_hidden'] = false; } } // handle --delimiter= argument if (preg_match('=^-{2}delimiter\=\"?\'?([^\s]+\s[^\s]+)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1])) { list ($ldelim, $rdelim) = explode(' ', $matches[1]); $this->_args['delimiters'][] = array( 'ldelim' => $ldelim, 'rdelim' => $rdelim ); } } // handle --directory= argument if (preg_match('=^-{2}directory\=\"?\'?(.*?)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1]) && is_dir($matches[1])) { $this->_args['directories'][] = $matches[1]; } else { $this->triggerError("Given directory $matches[1] does not exist\r\n"); } } // handle --file= argument if (preg_match('=^-{2}file\=\"?\'?(.*?)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1]) && is_file($matches[1])) { $this->_args['files'][] = $matches[1]; } else { $this->triggerError("Given file $matches[1] does not exist or is not a file\r\n"); } } // handle --exclude-directory= argument if (preg_match('=^-{2}exclude-directory\=\"?\'?(.*?)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1]) && is_dir($matches[1])) { $this->_args['exclude_directories'][] = $matches[1]; } else { $this->triggerError("Given directory $matches[1] does not exist\r\n"); } } // handle --exclude-file= argument if (preg_match('=^-{2}exclude-file\=\"?\'?(.*?)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1]) && is_file($matches[1])) { $this->_args['exclude_files'][] = $matches[1]; } else { $this->triggerError("Given file $matches[1] does not exist\r\n"); } } // handle --suffix= argument if (preg_match('=^-{2}suffix\=\"?\'?(\.[a-z0-9\.]+?)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1])) { $this->_args['suffixes'][] = $matches[1]; } } // handle --charset= argument if (preg_match('=^-{2}charset\=\"?\'?(.*?)\"?\'?$=', $_arg, $matches)) { if (!empty($matches[1])) { $this->_args['charset'] = $matches[1]; } } } // if $this->_help is true, we have to output the usage instructions if ($this->_args['help']) { $this->printHelp(); exit(0); } // check arguments if (count($this->_args['delimiters']) < 1) { $this->triggerError("Define at least one pair of delimiters using --delimiter\r\n"); } if (count($this->_args['directories']) < 1 && count($this->_args['files']) < 1) { $this->triggerError("Define at least one directory or file to scan using --directory or --file\r\n"); } // print imported args $this->printImportedArgs(); } /** * Prints imported args to stderr for debugging purposes */ protected function printImportedArgs() { $this->printStderr("\r\n"); $this->printStderr("Running with the following configuration\r\n"); $this->printStderr("========================================\r\n\r\n"); $this->printStderr(sprintf("Help: %s\r\n", ($this->_args['help']) ? 'true' : 'false')); $this->printStderr(sprintf("Default domain: %s\r\n", $this->_args['default_domain'])); $this->printStderr(sprintf("Charset: %s\r\n", $this->_args['charset'])); $this->printStderr(sprintf("Scan hidden files: %s\r\n", ($this->_args['scan_hidden']) ? 'true' : 'false')); $counter = 0; foreach ($this->_args['delimiters'] as $_delimiters) { if ($counter == 0) { $this->printStderr(sprintf("Delimiters (l, r): '%s' + '%s'\r\n", $_delimiters['ldelim'], $_delimiters['rdelim'])); } else { $this->printStderr(sprintf(" '%s' + '%s'\r\n", $_delimiters['ldelim'], $_delimiters['rdelim'])); } $counter++; } $counter = 0; foreach ($this->_args['directories'] as $_directory) { if ($counter == 0) { $this->printStderr(sprintf("Directories: %s\r\n", $_directory)); } else { $this->printStderr(sprintf(" %s\r\n", $_directory)); } $counter++; } $counter = 0; foreach ($this->_args['files'] as $_file) { if ($counter == 0) { $this->printStderr(sprintf("Files: %s\r\n", $_file)); } else { $this->printStderr(sprintf(" %s\r\n", $_file)); } $counter++; } $counter = 0; foreach ($this->_args['exclude_directories'] as $_directory) { if ($counter == 0) { $this->printStderr(sprintf("Exclude directories: %s\r\n", $_directory)); } else { $this->printStderr(sprintf(" %s\r\n", $_directory)); } $counter++; } $counter = 0; foreach ($this->_args['exclude_files'] as $_file) { if ($counter == 0) { $this->printStderr(sprintf("Exclude files: %s\r\n", $_file)); } else { $this->printStderr(sprintf(" %s\r\n", $_file)); } $counter++; } $counter = 0; foreach ($this->_args['suffixes'] as $_suffix) { if ($counter == 0) { $this->printStderr(sprintf("Suffixes: %s\r\n", $_suffix)); } else { $this->printStderr(sprintf(" %s\r\n", $_suffix)); } $counter++; } $this->printStderr("\r\n"); } /** * Prints some statistics */ protected function printStatistics () { $gettext_strings_total = 0; // calculate total amount of found gettext strings foreach ($this->_domains as $_domain) { $gettext_strings_total = $gettext_strings_total + count($_domain); } $this->printStderr("\r\n"); $this->printStderr("Statistics\r\n"); $this->printStderr("==========\r\n"); $this->printStderr("\r\n"); $this->printStderr(sprintf("Files scanned: %u\r\n", count($this->_file_list))); $this->printStderr(sprintf("Gettext strings: %u\r\n", $gettext_strings_total)); $this->printStderr("\r\n"); } /** * Prepares list of files to be scanned for gettext strings. Files listed * in self::_args['exclude_files'] or self::_args['exclude_directories'] * are already removed. */ protected function compileFileList () { // append explicitly defined files to file list foreach ($this->_args['files'] as $_file) { $this->_file_list[] = realpath($_file); } // scan defined directories and append contents to file list foreach ($this->_args['directories'] as $_directory) { $this->scanDirectory($_directory); } // remove files listed in self::_args['exclude_files'] from // file list $this->removeExcludedFiles(); // remove files in directories listed in // self::_args['exclude_directories'] from file list $this->removeExcludedDirectories(); // remove duplicates in file list $this->_file_list = array_unique($this->_file_list); } /** * Scans directory for files to be appended to the file list. Executes * checks for hidden files and valid suffixes. */ protected function scanDirectory ($directory) { $directory = $this->_removeTrailingSlash($directory); foreach (scandir($directory) as $_file) { // filter hardlinks if ($_file == '.' || $_file == '..') { continue; } // skip hidden files? if (!$this->_args['scan_hidden'] && $this->isHidden($_file)) { continue; } // compile complete path to file $file_path = realpath($directory.DIRECTORY_SEPARATOR.$_file); // if the file is a directory, scan it. if it's a // file, append it to the file list. if (is_dir($file_path)) { $this->scanDirectory($file_path); } elseif (is_file($file_path)) { // is file suffix in list of allowed suffixes? if (!$this->isInSuffixList($_file)) { continue; } $this->_file_list[] = $file_path; } } } /** * Searches for all sorts of gettext strings in the files listed in * the file list. */ protected function searchFilesForGettextStrings () { foreach ($this->_file_list as $_file) { $array = file($_file); $contents = file_get_contents($_file); $file_name = basename($_file); $this->_i18nScan($contents, $array, $file_name); $this->_ni18nScan($contents, $array, $file_name); $this->_gettextScan($contents, $array, $file_name); $this->_ngettextScan($contents, $array, $file_name); } } /** * Scans file for i18n tags * * @param string File contents * @param array File contents, one line as one array item * @param string File name */ protected function _i18nScan (&$contents, &$array, $file) { foreach ($this->_args['delimiters'] as $_delimiters) { $ldelim = preg_quote($_delimiters['ldelim'], "="); $rdelim = preg_quote($_delimiters['rdelim'], "="); $pattern_base = "=%si18n\s+(.*?)%s=im"; $pattern = sprintf($pattern_base, $ldelim, $rdelim); // grep i18n strings preg_match_all($pattern, $contents, $matches, PREG_OFFSET_CAPTURE); if (!empty($matches)) { // prepare messages foreach ($matches[1] as $_match) { // calculate line number in target file $counter = 0; foreach ($array as $_key => $_value) { $strlen = strlen($_value); $counter = $counter + $strlen; if ((int)$_match[1] < (int)$counter && $_match[1] >= ($counter - $strlen)) { $line = $_key + 1; break; } } // add string to domain if (!empty($_match[0])) { $this->_domains[$this->_args['default_domain']][$_match[0]][] = array( 'file' => $file, 'line' => (int)$line, 'msgid' => $this->quoteMsgId($_match[0]), 'msgid_plural' => null ); } } } } } /** * Plural version of _i18nScan * * @param string File contents * @param array File contents, one line as one array item * @param string File name */ protected function _ni18nScan (&$contents, &$array, $file) { foreach ($this->_args['delimiters'] as $_delimiters) { $ldelim = preg_quote($_delimiters['ldelim'], "="); $rdelim = preg_quote($_delimiters['rdelim'], "="); $pattern_base = "=%si18n\W+\"\W{0,}(.*?)\W{0,}\"\W{0,}:\W{0,}\"\W{0,}". "(.*?)\W{0,}\"\W{0,}:\W{0,}(\d+)\W{0,}%s=im"; $pattern = sprintf($pattern_base, $ldelim, $rdelim); // grep i18n strings preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); if (!empty($matches)) { // prepare messages foreach ($matches as $_match_id => $_match) { // calculate line number in target file $counter = 0; foreach ($array as $_key => $_value) { $strlen = strlen($_value); $counter = $counter + $strlen; if ((int)$_match[0][1] < (int)$counter && $_match[0][1] >= ($counter - $strlen)) { $line = $_key + 1; break; } } // if warnings are enabled and the singular string already // exists, check if the plural versions differ if ($this->_warnings === true) { if (is_array($this->_domains[$this->_args['default_domain']][$_match[1][0]])) { foreach ($this->_domains[$this->_args['default_domain']][$_match[1][0]] as $_message_block) { if ($_message_block['msgid_plural'] != $_match[2][0]) { $this->printStderr(printf("Warning on %s, Line %u: msgid_plural doesn't ". "match existing string. `%s` expected, `%s` found.\r\n", $file, $line, $_message_block['msgid_plural'], $_match[2][0] )); } } } } // add string to domain if (!empty($_match[1][0])) { $this->_domains[$this->_args['default_domain']][$_match[1][0]][] = array( 'file' => $file, 'line' => (int)$line, 'msgid' => $this->quoteMsgId($_match[1][0]), 'msgid_plural' => $this->quoteMsgId($_match[2][0]) ); } } } } } /** * Scans file for gettext tags * * @param string File contents * @param array File contents, one line as one array item * @param string File name */ protected function _gettextScan (&$contents, &$array, $file) { // grep gettext strings $pattern = "=gettext\W{0,}\(\W{0,}[\"|\'](.*?)[\"|\']\W{0,}\)=im"; preg_match_all($pattern, $contents, $matches, PREG_OFFSET_CAPTURE); if (!empty($matches)) { // prepare messages foreach ($matches[1] as $_match) { // calculate line number in target file $counter = 0; foreach ($array as $_key => $_value) { $strlen = strlen($_value); $counter = $counter + $strlen; if ((int)$_match[1] < (int)$counter && $_match[1] >= ($counter - $strlen)) { $line = $_key + 1; break; } } // add string to domain if (!empty($_match[0])) { $this->_domains[$this->_args['default_domain']][$_match[0]][] = array( 'file' => $file, 'line' => (int)$line, 'msgid' => $this->quoteMsgId($_match[0]), 'msgid_plural' => null ); } } } } /** * Plural version of _gettextScan * * @param string File contents * @param array File contents, one line as one array item * @param string File name */ protected function _ngettextScan (&$contents, &$array, $file) { // grep i18n strings $pattern = "=ngettext\W{0,}\(\W{0,}[\"|\'](.*?)[\"|\']\W{0,}\,". "\W{0,}[\"|\'](.*?)[\"|\']\W{0,},\W{0,}(\d+)\W{0,}\)=im"; preg_match_all($pattern, $contents, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); if (!empty($matches)) { // prepare messages foreach ($matches as $_match_id => $_match) { // calculate line number in target file $counter = 0; foreach ($array as $_key => $_value) { $strlen = strlen($_value); $counter = $counter + $strlen; if ((int)$_match[0][1] < (int)$counter && $_match[0][1] >= ($counter - $strlen)) { $line = $_key + 1; break; } } // if warnings are enabled and the singular string already // exists, check if the plural versions differ if ($this->_warnings === true) { if (is_array($this->_domains[$this->_args['default_domain']][$_match[1][0]])) { foreach ($this->_domains[$this->_args['default_domain']][$_match[1][0]] as $_message_block) { if ($_message_block['msgid_plural'] != $_match[2][0]) { $this->printStderr(printf("Warning on %s, Line %u: msgid_plural doesn't ". "match existing string. `%s` expected, `%s` found.\r\n", $file, $line, $_message_block['msgid_plural'], $_match[2][0] )); } } } } // add string to domain if (!empty($_match[1][0])) { $this->_domains[$this->_args['default_domain']][$_match[1][0]][] = array( 'file' => $file, 'line' => (int)$line, 'msgid' => $this->quoteMsgId($_match[1][0]), 'msgid_plural' => $_match[2][0] ); } } } } /** * Writes POT files, one file for each domain */ protected function _writePOTs () { foreach ($this->_domains as $_domain => $_messages) { // set filename $domain = $_domain.".pot"; // write header // For a description how to define the plural forms, pls. see // http://www.gnu.org/software/gettext/manual/html_mono/gettext.html#SEC150 file_put_contents($domain, "# SOME DESCRIPTIVE TITLE.\n"); file_put_contents($domain, "# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n", FILE_APPEND); file_put_contents($domain, "# This file is distributed under the same license as the PACKAGE package.\n", FILE_APPEND); file_put_contents($domain, "# FIRST AUTHOR , YEAR.\n", FILE_APPEND); file_put_contents($domain, "#\n", FILE_APPEND); file_put_contents($domain, "#, fuzzy\n", FILE_APPEND); file_put_contents($domain, "msgid \"\"\n", FILE_APPEND); file_put_contents($domain, "msgstr \"\"\n", FILE_APPEND); file_put_contents($domain, "\"Project-Id-Version: PACKAGE VERSION\\n\"\n", FILE_APPEND); file_put_contents($domain, "\"Report-Msgid-Bugs-To: \\n\"\n", FILE_APPEND); file_put_contents($domain, "\"POT-Creation-Date: ".date("Y-m-d H:iO")."\\n\"\n", FILE_APPEND); file_put_contents($domain, "\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n", FILE_APPEND); file_put_contents($domain, "\"Last-Translator: FULL NAME \\n\"\n", FILE_APPEND); file_put_contents($domain, "\"Language-Team: LANGUAGE \\n\"\n", FILE_APPEND); file_put_contents($domain, "\"MIME-Version: 1.0\\n\"\n", FILE_APPEND); file_put_contents($domain, "\"Content-Type: text/plain; charset=".$this->_args['charset']."\\n\"\n", FILE_APPEND); file_put_contents($domain, "\"Content-Transfer-Encoding: 8bit\\n\"\n", FILE_APPEND); file_put_contents($domain, "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n", FILE_APPEND); // write messages to file // sample: // // #: gettext-sample.php:13 // msgid "Another string goes there" // msgstr "" // // #: gettext-sample.php:14 gettext-sample.php:15 // msgid "My singular string" // msgid_plural "My plural string" // msgstr[0] "" // msgstr[1] "" // foreach ($_messages as $_message) { $locations = array(); foreach ($_message as $_message_info) { $locations[] = $_message_info['file'].':'.$_message_info['line']; } file_put_contents($domain, "\n", FILE_APPEND); foreach ($locations as $_location) { file_put_contents($domain, "#: ".$_location."\n", FILE_APPEND); } file_put_contents($domain, "msgid \"".$_message[0]['msgid']."\"\n", FILE_APPEND); if (!empty($_message[0]['msgid_plural'])) { file_put_contents($domain, "msgid_plural \"".$_message[0]['msgid_plural']."\"\n", FILE_APPEND); file_put_contents($domain, "msgstr[0] \"\"\n", FILE_APPEND); file_put_contents($domain, "msgstr[1] \"\"\n", FILE_APPEND); } else { file_put_contents($domain, "msgstr \"\"\n", FILE_APPEND); } } // progress information if (!$this->_q) { printf("Wrote %s.\n", $domain); } } } /** * Removes files to be excluded from file list. */ protected function removeExcludedFiles () { foreach ($this->_file_list as $_key => $_file) { $_file = realpath($_file); foreach ($this->_args['exclude_files'] as $_exclude_file) { $_exclude_file = realpath($_exclude_file); if ($_file == $_exclude_file) { unset($this->_file_list[$_key]); } } } } /** * Removes files in directories to be excluded from file list. */ protected function removeExcludedDirectories () { foreach ($this->_file_list as $_key => $_file) { $_file = realpath($_file); foreach ($this->_args['exclude_directories'] as $_exclude_directory) { $_exclude_directory = realpath($_exclude_directory); $position = strpos($_file, $_exclude_directory); if ($position !== false && $position === 0) { unset($this->_file_list[$_key]); } } } } /** * Tests if given file name starts with a dot. If it starts * with a dot it's a hidden file/directory and the method * returns true */ protected function isHidden ($file_name) { if (substr($file_name, 0, 1) == '.') { return true; } else { return false; } } /** * Tests if suffix of given file name is in the list of suffixes * defined by command line arguments. Returns bool. */ protected function isInSuffixList ($file_name) { foreach ($this->_args['suffixes'] as $_suffix) { // compile pattern $pattern = sprintf("=%s$=", preg_quote($_suffix, '=')); // test if suffix matches file suffix if (preg_match($pattern, $file_name)) { return true; } } return false; } /** * Quotes quotes (") in msgid strings. */ protected function quoteMsgId ($msgid) { $msgid = preg_replace('=(?printStderr($error_string); // exit exit(1); } /** * Prints message to stdout */ protected function printStdout ($message) { // write message to stdout fwrite(STDOUT, $message); } /** * Writes message to stderr. */ protected function printStderr ($message) { // write error message to stderr fwrite(STDERR, $message); } /** * Prints help message and usage instructions to stdout. */ protected function printHelp () { $rev = '$Date: 2006-11-24 10:15:22 +0100 (Fre, 24 Nov 2006) $'; $this->printStderr("xgettext.php 1.3 ($rev)\r\n"); $this->printStderr("(c) 2005-2007 sopic GmbH\r\n"); $this->printStderr("Licensed below the terms of the Apache Software License 2.0.\r\n"); $this->printStderr("\r\n"); $this->printStderr("Usage: php xgettext.php \\\r\n"); $this->printStderr(" --help= \\\r\n"); $this->printStderr(" --default-domain= \\\r\n"); $this->printStderr(" --scan-hidden= \\\r\n"); $this->printStderr(" --delimiter= \\\r\n"); $this->printStderr(" --directory= \\\r\n"); $this->printStderr(" --file= \\\r\n"); $this->printStderr(" --exclude-directory= \\\r\n"); $this->printStderr(" --exclude-file= \\\r\n"); $this->printStderr(" --suffix= \\\r\n"); $this->printStderr(" --charset=\r\n"); $this->printStderr("\r\n"); $this->printStderr("Arguments:\r\n"); $this->printStderr(" --help:\r\n"); $this->printStderr(" Display this help text.\r\n\r\n"); $this->printStderr(" --default-domain:\r\n"); $this->printStderr(" Domain to use when scanning. Use 'messages' to get a\r\n"); $this->printStderr(" messages.pot.\r\n\r\n"); $this->printStderr(" --scan-hidden:\r\n"); $this->printStderr(" Whether to scan hidden directories and files or\r\n"); $this->printStderr(" not.\r\n\r\n"); $this->printStderr(" --delimiter:\r\n"); $this->printStderr(" Delimiters to use when going through Smarty\r\n"); $this->printStderr(" templates. Separate the left and the right delimiter\r\n"); $this->printStderr(" with a space. You can specify --delimiter multiple\r\n"); $this->printStderr(" times to define as much delimiters as you need.\r\n"); $this->printStderr(" Sample: --delimiter=\"{ }\".\r\n\r\n"); $this->printStderr(" --directory:\r\n"); $this->printStderr(" Directories where to start with scanning for gettext\r\n"); $this->printStderr(" strings. All subdirectories and their contents will\r\n"); $this->printStderr(" be scanned too. You can specifiy --directory multiple\r\n"); $this->printStderr(" times to define as much directories as you need.\r\n"); $this->printStderr(" Please note that --exclude-directory and\r\n"); $this->printStderr(" --exclude-file takes precedence.\r\n\r\n"); $this->printStderr(" --file:\r\n"); $this->printStderr(" Files to scan for gettext strings. You can specifiy\r\n"); $this->printStderr(" --file multiple times to define as much files as you\r\n"); $this->printStderr(" need.\r\n"); $this->printStderr(" Please note that --exclude-directory and\r\n"); $this->printStderr(" --exclude-file takes precedence.\r\n\r\n"); $this->printStderr(" --exclude-directory:\r\n"); $this->printStderr(" Directories to exclude when scanning for gettext\r\n"); $this->printStderr(" strings. You can specifiy --exclude-directory multiple\r\n"); $this->printStderr(" times to define as much directories to be excluded as\r\n"); $this->printStderr(" you need.\r\n\r\n"); $this->printStderr(" --exclude-file:\r\n"); $this->printStderr(" Files to exclude when scanning for gettext strings.\r\n"); $this->printStderr(" You can specifiy --exclude-file multiple times to\r\n"); $this->printStderr(" define as much directories to be excluded as you need.\r\n\r\n"); $this->printStderr(" --suffix:\r\n"); $this->printStderr(" Only scan files with the specified suffix for\r\n"); $this->printStderr(" gettext strings. You can specifiy --suffix multiple\r\n"); $this->printStderr(" times to define as much suffixes as you need.\r\n"); $this->printStderr(" Sample: --suffix=.html --suffix=.php.\r\n\r\n"); $this->printStderr(" --charset:\r\n"); $this->printStderr(" Charset name to be included in PO file header.\r\n"); $this->printStderr(" No conversion will be done.\r\n\r\n"); } /** * Removes trailing slash from given path. Takes a file system path * as first argument. Returns string with trailing slash removed. * * @param string File system path * @return string File system path */ protected function _removeTrailingSlash ($string) { if (substr($string, -1, 1) == '/' || substr($string, -1, 1) == '\\') { $string = substr($string, 0, -1); } return $string; } // End of class } ?>