diff options
author | Andy Staudacher <andy.st@gmail.com> | 2009-02-19 07:20:26 +0000 |
---|---|---|
committer | Andy Staudacher <andy.st@gmail.com> | 2009-02-19 07:20:26 +0000 |
commit | d47e4c9b43aca6092c1555c392ae072d449bb888 (patch) | |
tree | 239b119acc5c05b86bff806e6a4abee82937e3d3 | |
parent | dcc42328b12839b43ca52058eeab1106e094e8e1 (diff) |
Adding a l10n scanner - scans for t() and t2() calls, inserts the extracted messages into incoming_translations.
See: https://apps.sourceforge.net/trac/gallery/ticket/74
-rw-r--r-- | core/libraries/L10n_Scanner.php | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/core/libraries/L10n_Scanner.php b/core/libraries/L10n_Scanner.php new file mode 100644 index 00000000..87fdd7e3 --- /dev/null +++ b/core/libraries/L10n_Scanner.php @@ -0,0 +1,194 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2008 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * Scans all source code for messages that need to be localized. + */ +class L10n_Scanner_Core { + // Based on Drupal's potx module, originally written by: + // Gbor Hojtsy http://drupal.org/user/4166 + + private static $_instance; + + private $_index_keys; + + private function __construct() {} + + static function instance() { + if (self::$_instance == null) { + self::$_instance = new L10n_Scanner_Core(); + } + + return self::$_instance; + } + + // TODO(andy_st): Report progress via callback + function update_index() { + // Load the current index into memory + $this->_index_keys = array(); + foreach (Database::instance() + ->select("key") + ->from("incoming_translations") + ->where(array("locale" => "root")) + ->get() + ->as_array() as $row) { + $this->_index_keys[$row->key] = true; + } + + // Index all files + $dir = new L10n_Scanner_File_Filter_Iterator( + new RecursiveIteratorIterator( + new L10n_Scanner_Directory_Filter_Iterator( + new RecursiveDirectoryIterator(DOCROOT)))); + foreach ($dir as $file) { + $this->_scan_file($file, $this); + } + } + + function process_message($message) { + $key = I18n::get_message_key($message); + if (!isset($this->_index_keys[$key])) { + $entry = ORM::factory("incoming_translation"); + $entry->key = $key; + $entry->message = serialize($message); + $entry->locale = 'root'; + $entry->save(); + + $this->_index_keys[$key] = true; + } + } + + private function _scan_file($file, &$message_handler) { + $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"])) { + $this->_parse_t_calls($tokens, $func_token_list["t"], $message_handler); + } + if (!empty($func_token_list["t2"])) { + $this->_parse_plural_calls($tokens, $func_token_list["t2"], $message_handler); + } + } + + private function _parse_t_calls(&$tokens, &$call_list, &$message_handler) { + 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]); + $message_handler->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 function _parse_plural_calls(&$tokens, &$call_list, &$message_handler) { + 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]); + $message_handler->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 substr($filename, -4, 4) == ".php"; + } +}
\ No newline at end of file |