From 78943402b27bc5afb0736089b25a020cd33024b6 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 26 Mar 2009 04:36:00 +0000 Subject: Convert the L10n scanner from a library to a helper. In order to make the class static, I had to remove the index cache. I'll restore that and cache the index keys in the task context in a subsequent change. For now, I've put in a @todo to add the caching back in. --- core/helpers/l10n_scanner.php | 182 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 core/helpers/l10n_scanner.php (limited to 'core/helpers') diff --git a/core/helpers/l10n_scanner.php b/core/helpers/l10n_scanner.php new file mode 100644 index 00000000..81764900 --- /dev/null +++ b/core/helpers/l10n_scanner.php @@ -0,0 +1,182 @@ +getFilename(), PATHINFO_EXTENSION) == "php") { + l10n_scanner::_scan_php_file($file); + } else { + l10n_scanner::_scan_info_file($file); + } + } + } + + static function process_message($message) { + // @todo this is O(N) queries over the number of messages. Precache all message keys + // in the task context and then do lookups over that to get it down to O(1). + $key = I18n::get_message_key($message); + $entry = ORM::factory("incoming_translation", array("key" => $key)); + if (!$entry->loaded) { + $entry->key = $key; + $entry->message = serialize($message); + $entry->locale = "root"; + $entry->save(); + } + } + + private static function _scan_php_file($file) { + $code = file_get_contents($file); + $raw_tokens = token_get_all($code); + unset($code); + + $tokens = array(); + $func_token_list = array("t" => array(), "t2" => array()); + $token_number = 0; + // Filter out HTML / whitespace, and build a lookup for global function calls. + foreach ($raw_tokens as $token) { + if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) { + if (is_array($token)) { + if ($token[0] == T_STRING && in_array($token[1], array("t", "t2"))) { + $func_token_list[$token[1]][] = $token_number; + } + } + $tokens[] = $token; + $token_number++; + } + } + unset($raw_tokens); + + if (!empty($func_token_list["t"])) { + l10n_scanner::_parse_t_calls($tokens, $func_token_list["t"]); + } + if (!empty($func_token_list["t2"])) { + l10n_scanner::_parse_plural_calls($tokens, $func_token_list["t2"]); + } + } + + private static function _scan_info_file($file) { + $code = file_get_contents($file); + if (preg_match("#name\s*?=\s*(.*?)\ndescription\s*?=\s*(.*)\n#", $code, $matches)) { + unset($matches[0]); + foreach ($matches as $string) { + l10n_scanner::process_message($string); + } + } + } + + private static function _parse_t_calls(&$tokens, &$call_list) { + foreach ($call_list as $index) { + $function_name = $tokens[$index++]; + $parens = $tokens[$index++]; + $first_param = $tokens[$index++]; + $next_token = $tokens[$index]; + + if ($parens == "(") { + if (in_array($next_token, array(")", ",")) + && (is_array($first_param) && ($first_param[0] == T_CONSTANT_ENCAPSED_STRING))) { + $message = self::_escape_quoted_string($first_param[1]); + l10n_scanner::process_message($message); + } else { + // t() found, but inside is something which is not a string literal. + // TODO(andy_st): Call status callback with error filename/line. + } + } + } + } + + private static function _parse_plural_calls(&$tokens, &$call_list) { + foreach ($call_list as $index) { + $function_name = $tokens[$index++]; + $parens = $tokens[$index++]; + $first_param = $tokens[$index++]; + $first_separator = $tokens[$index++]; + $second_param = $tokens[$index++]; + $next_token = $tokens[$index]; + + if ($parens == "(") { + if ($first_separator == "," && $next_token == "," + && is_array($first_param) && $first_param[0] == T_CONSTANT_ENCAPSED_STRING + && is_array($second_param) && $second_param[0] == T_CONSTANT_ENCAPSED_STRING) { + $singular = self::_escape_quoted_string($first_param[1]); + $plural = self::_escape_quoted_string($first_param[1]); + l10n_scanner::process_message(array("one" => $singular, "other" => $plural)); + } else { + // t2() found, but inside is something which is not a string literal. + // TODO(andy_st): Call status callback with error filename/line. + } + } + } + } + + /** + * Escape quotes in a strings depending on the surrounding + * quote type used. + * + * @param $str The strings to escape + */ + private static function _escape_quoted_string($str) { + $quo = substr($str, 0, 1); + $str = substr($str, 1, -1); + if ($quo == '"') { + $str = stripcslashes($str); + } else { + $str = strtr($str, array("\\'" => "'", "\\\\" => "\\")); + } + return addcslashes($str, "\0..\37\\\""); + } +} + +class L10n_Scanner_Directory_Filter_Iterator extends RecursiveFilterIterator { + function accept() { + if ($this->getInnerIterator()->isFile()) { + return true; + } + + // Skip anything that doesn't need to be localized. + $folder = $this->getInnerIterator()->getFilename(); + $path_name = $this->getInnerIterator()->getPathName(); + return !( + $folder == ".svn" || + $folder == "tests" || + strpos($path_name, DOCROOT . "var") !== false || + strpos($path_name . "/", SYSPATH) !== false); + } +} + +class L10n_Scanner_File_Filter_Iterator extends FilterIterator { + function accept() { + // Skip anything that doesn't need to be localized. + $filename = $this->getInnerIterator()->getFilename(); + return in_array(pathinfo($filename, PATHINFO_EXTENSION), array("php", "info")); + } +} -- cgit v1.2.3