diff --git a/core/i18n.php b/core/i18n.php
index fef318c08..cababae68 100755
--- a/core/i18n.php
+++ b/core/i18n.php
@@ -30,7 +30,7 @@
* @package sapphire
* @subpackage misc
*/
-class i18n extends Controller {
+class i18n extends Object {
/**
* This static variable is used to store the current defined locale.
@@ -843,7 +843,9 @@ class i18n extends Controller {
$moduleLocales = scandir($langDir);
foreach($moduleLocales as $moduleLocale) {
if(preg_match('/(.*)\.php$/',$moduleLocale, $matches)) {
- $locales[$matches[1]] = self::$all_locales[$matches[1]];
+ if(isset($matches[1]) && isset(self::$all_locales[$matches[1]])) {
+ $locales[$matches[1]] = self::$all_locales[$matches[1]];
+ }
}
}
}
@@ -951,220 +953,6 @@ class i18n extends Controller {
}
- /**
- * Build the module's master string table
- *
- * @param string $baseDir Silverstripe's base directory
- * @param string $module Module's name
- * @return string Generated master string table
- */
- protected static function process_module($baseDir, $module) {
-
- // Only search for calls in folder with a _config.php file (which means they are modules)
- if(is_dir("$baseDir/$module") && is_file("$baseDir/$module/_config.php") && substr($module,0,1) != '.') {
-
- $mst = '';
- // Search for calls in code files if these exists
- if(is_dir("$baseDir/$module/code")) {
- $fileList = array();
- self::get_files_rec("$baseDir/$module/code", $fileList);
- foreach($fileList as $file) {
- $mst .= self::report_calls_code($file);
- }
- } else if('sapphire' == $module) {
- // sapphire doesn't have the usual module structure, so we'll scan all subfolders
- $fileList = array();
- self::get_files_rec("$baseDir/$module", $fileList);
- foreach($fileList as $file) {
- if('.ss' != substr($file,-3)) $mst .= self::report_calls_code($file);
- }
- }
-
- // Search for calls in template files if these exists
- if(is_dir("$baseDir/$module/templates")) {
- $fileList = array();
- $includedtpl[$module] = array();
- self::get_files_rec("$baseDir/$module/templates", $fileList);
- foreach($fileList as $index => $file) {
- $mst .= self::report_calls_tpl($index, $file, $includedtpl[$module]);
- }
- }
-
- return $mst;
-
- } else return false;
- }
-
- /**
- * Write the master string table of every processed module
- *
- * @param string $baseDir Silverstripe's base directory
- * @param array $allmst Module's master string tables
- * @param array $includedtpl Templates included by other templates
- */
- protected static function write_mst($baseDir, $allmst, $includedtpl) {
-
- // Evaluate the constructed mst
- foreach($allmst as $mst) eval($mst);
-
- // Resolve template dependencies
- foreach($includedtpl as $tplmodule => $includers) {
- // Variable initialization
- $stringsCode = '';
- $moduleCode = '';
- $modulestoinclude = array();
-
- foreach($includers as $includertpl => $allincluded)
- foreach($allincluded as $included)
- // we will only add code if the included template has localizable strings
- if(isset($lang[self::$default_locale]["$included.ss"])) {
- $module = self::get_owner_module("$included.ss");
-
- /* if the module of the included template is not the same as the includer's one
- * we will need to load the first one in order to have these included strings in memory
- */
- if ($module != $tplmodule) $modulestoinclude[$module] = $included;
-
- // Give the includer name to the included strings in order to be used from the includer template
- $stringsCode .= "\$lang['" . self::$default_locale . "']['$includertpl'] = " .
- "array_merge(\$lang['" . self::$default_locale . "']['$includertpl'], \$lang['" . self::$default_locale . "']['$included.ss']);\n";
- }
-
- // Include a template for every needed module (the module language file will then be autoloaded)
- foreach($modulestoinclude as $tpltoinclude) $moduleCode .= "self::include_by_class('$tpltoinclude.ss');\n";
-
- // Add the extra code to the existing module mst
- if ($stringsCode) $allmst[$tplmodule] .= "\n$moduleCode$stringsCode";
- }
-
- // Write each module language file
- foreach($allmst as $module => $mst) {
- // Create folder for lang files
- $langFolder = $baseDir . '/' . $module . '/lang';
- if(!file_exists($baseDir. '/' . $module . '/lang')) {
- mkdir($langFolder, Filesystem::$folder_create_mask);
- touch($baseDir. '/' . $module . '/lang/_manifest_exclude');
- }
-
- // Open the English file and write the Master String Table
- if($fh = fopen($langFolder . '/' . self::$default_locale . '.php', "w")) {
- fwrite($fh, "");
- fclose($fh);
- if(Director::is_cli()) {
- echo "Created file: $langFolder/" . self::$default_locale . ".php\n";
- } else {
- echo "Created file: $langFolder/" . self::$default_locale . ".php
";
- }
-
- } else {
- user_error("Cannot write language file! Please check permissions of $langFolder/" . self::$default_locale . ".php", E_USER_ERROR);
- }
- }
-
- }
-
- /**
- * Helper function that searches for potential files to be parsed
- *
- * @param string $folder base directory to scan (will scan recursively)
- * @param array $fileList Array where potential files will be added to
- */
- protected static function get_files_rec($folder, &$fileList) {
- $items = scandir($folder);
- if($items) foreach($items as $item) {
- if(substr($item,0,1) == '.') continue;
- if(substr($item,-4) == '.php') $fileList[substr($item,0,-4)] = "$folder/$item";
- else if(substr($item,-3) == '.ss') $fileList[$item] = "$folder/$item";
- else if(is_dir("$folder/$item")) self::get_files_rec("$folder/$item", $fileList);
- }
- }
-
- /**
- * Look for calls to the underscore function in php files and build our MST
- *
- * @param string $file Path to the file to be parsed
- * @return string Built Master String Table from this file
- */
- protected static function report_calls_code($file) {
- static $callMap;
- $content = file_get_contents($file);
- $mst = '';
- while (ereg('_t[[:space:]]*\([[:space:]]*("[^"]*"|\\\'[^\']*\\\')[[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\')([[:space:]]*,[[:space:]]*[^,)]*)?([[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))?[[:space:]]*\)', $content, $regs)) {
- $entityParts = explode('.',substr($regs[1],1,-1));
- $entity = array_pop($entityParts);
- $class = implode('.',$entityParts);
-
- if (isset($callMap["$class--$entity"]))
- echo "Warning! Redeclaring entity $entity in file $file (previously declared in {$callMap["$class--$entity"]})
";
-
- if (substr($regs[2],0,1) == '"') $regs[2] = addcslashes($regs[2],'\'');
- $mst .= '$lang[\'' . self::$default_locale . '\'][\'' . $class . '\'][\'' . $entity . '\'] = ';
- if ($regs[5]) {
- $mst .= "array(\n\t'" . substr($regs[2],1,-1) . "',\n\t" . substr($regs[5],1);
- if ($regs[7]) {
- if (substr($regs[7],0,1) == '"') $regs[7] = addcslashes($regs[7],'\'');
- $mst .= ",\n\t'" . substr($regs[7],1,-1) . '\'';
- }
- $mst .= "\n);";
- } else $mst .= '\'' . substr($regs[2],1,-1) . '\';';
- $mst .= "\n";
- $content = str_replace($regs[0],"",$content);
-
- $callMap["$class--$entity"] = $file;
- }
-
- return $mst;
- }
-
- /**
- * Look for calls to the underscore function in template files and build our MST
- * Template version - no "class" argument
- *
- * @param string $index Index used to namespace strings
- * @param string $file Path to the file to be parsed
- * @param string $included List of explicitly included templates
- * @return string Built Master String Table from this file
- */
- protected static function report_calls_tpl($index, $file, &$included) {
- static $callMap;
- $content = file_get_contents($file);
-
- // Search for included templates
- preg_match_all('/<' . '% include +([A-Za-z0-9_]+) +%' . '>/', $content, $inc, PREG_SET_ORDER);
- foreach ($inc as $template) {
- if (!isset($included[$index])) $included[$index] = array();
- array_push($included[$index], $template[1]);
- }
-
- $mst = '';
- while (ereg('_t[[:space:]]*\([[:space:]]*("[^"]*"|\\\'[^\']*\\\')[[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\')([[:space:]]*,[[:space:]]*[^,)]*)?([[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))?[[:space:]]*\)',$content,$regs)) {
-
- $entityParts = explode('.',substr($regs[1],1,-1));
- $entity = array_pop($entityParts);
-
- // Entity redeclaration check
- if (isset($callMap["$index--$entity"]))
- echo "Warning! Redeclaring entity $entity in file $file (previously declared in {$callMap["$index--$entity"]})
";
-
- if (substr($regs[2],0,1) == '"') $regs[2] = addcslashes($regs[2],'\'');
- $mst .= '$lang[\'' . self::$default_locale . '\'][\'' . $index . '\'][\'' . $entity . '\'] = ';
- if ($regs[5]) {
- $mst .= "array(\n\t'" . substr($regs[2],1,-1) . "',\n\t" . substr($regs[5],1);
- if ($regs[7]) {
- if (substr($regs[7],0,1) == '"') $regs[7] = addcslashes($regs[7],'\'\\');
- $mst .= ",\n\t'" . substr($regs[7],1,-1) . '\'';
- }
- $mst .= "\n);";
- } else $mst .= '\'' . substr($regs[2],1,-1) . '\';';
- $mst .= "\n";
- $content = str_replace($regs[0],"",$content);
-
- $callMap["$index--$entity"] = $file;
- }
-
- return $mst;
- }
-
/**
* Set the current locale
* See http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html for a list of possible locales
@@ -1220,11 +1008,6 @@ class i18n extends Controller {
Translatable::disable();
}
- /**
- * Include a locale file determined by module name and locale
- *
- * @param string $module Module that contains the locale file
-
/**
* Include a locale file determined by module name and locale
*
@@ -1287,52 +1070,6 @@ class i18n extends Controller {
}
echo "Language {$this->urlParams['ID']} successfully removed";
}
-
-
- /**
- * This is the main method to build the master string tables with the original strings.
- * It will search for existent modules that use the i18n feature, parse the _t() calls
- * and write the resultant files in the lang folder of each module.
- */
- public function textcollector() {
- // allows textcollector to run in CLI without admin-check
- if(!Permission::check("ADMIN") && !Director::is_cli()) {
- user_error("You must be an admin or use CLI-mode to enable text collector", E_USER_ERROR);
- }
-
- if(Director::is_cli()) {
- echo "Collecting text...\n";
- } else {
- echo "Collecting text...
";
- }
-
- //Calculate base directory
- $baseDir = Director::baseFolder();
-
- // A master string tables array (one mst per module)
- $mst = array();
- // A list of included templates dependencies
- $includedtpl = array();
-
- //Search for and process existent modules, or use the passed one instead
- if (!isset($_GET['module'])) {
- $topLevel = scandir($baseDir);
- foreach($topLevel as $module) {
- // we store the master string tables
- $processed = self::process_module($baseDir, $module, $includedtpl);
- if ($processed) $mst[$module] = $processed;
- }
- } else {
- $module = basename($_GET['module']);
- $processed = self::process_module($baseDir, $_GET['module'], $includedtpl);
- if ($processed) $mst[$module] = $processed;
- }
-
- // Write the generated master string tables
- self::write_mst($baseDir, $mst, $includedtpl);
-
- echo "Done!\n";
- }
}
-?>
+?>
\ No newline at end of file
diff --git a/core/i18nTextCollector.php b/core/i18nTextCollector.php
new file mode 100644
index 000000000..58745a4e3
--- /dev/null
+++ b/core/i18nTextCollector.php
@@ -0,0 +1,281 @@
+
+ * @package sapphire
+ * @subpackage misc
+ */
+class i18nTextCollector extends Object {
+
+ protected $defaultLocale;
+
+ /**
+ * @param $locale
+ */
+ function __construct($locale = null) {
+ $this->defaultLocale = ($locale) ? $locale : i18n::default_locale();
+
+ parent::__construct();
+ }
+
+ /**
+ * This is the main method to build the master string tables with the original strings.
+ * It will search for existent modules that use the i18n feature, parse the _t() calls
+ * and write the resultant files in the lang folder of each module.
+ *
+ * @uses DataObject->collectI18nStatics()
+ */
+ public function run($module = null) {
+ if(Director::is_cli()) {
+ echo "Collecting text...\n";
+ } else {
+ echo "Collecting text...
";
+ }
+
+ //Calculate base directory
+ $baseDir = Director::baseFolder();
+
+ // A master string tables array (one mst per module)
+ $mst = array();
+
+ // A list of included templates dependencies
+ $includedtpl = array();
+
+ //Search for and process existent modules, or use the passed one instead
+ if (!isset($module)) {
+ $topLevel = scandir($baseDir);
+ foreach($topLevel as $module) {
+ // we store the master string tables
+ $processed = $this->processModule($baseDir, $module, $includedtpl);
+ if ($processed) $mst[$module] = $processed;
+ }
+ } else {
+ $module = basename($module);
+ $processed = $this->processModule($baseDir, $module, $includedtpl);
+ if ($processed) $mst[$module] = $processed;
+ }
+
+ // Write the generated master string tables
+ $this->writeMasterStringFile($baseDir, $mst, $includedtpl);
+
+ echo "Done!\n";
+ }
+
+ /**
+ * Build the module's master string table
+ *
+ * @param string $baseDir Silverstripe's base directory
+ * @param string $module Module's name
+ * @return string Generated master string table
+ */
+ protected function processModule($baseDir, $module) {
+
+ // Only search for calls in folder with a _config.php file (which means they are modules)
+ if(
+ is_dir("$baseDir/$module")
+ && is_file("$baseDir/$module/_config.php")
+ && substr($module,0,1) != '.'
+ ) {
+ Debug::message("Processing Module '{$module}'", false);
+
+ $mst = '';
+ // Search for calls in code files if these exists
+ if(is_dir("$baseDir/$module/code")) {
+ $fileList = $this->getFilesRecursive("$baseDir/$module/code");
+ foreach($fileList as $file) {
+ if(substr($file,-3) == '.php') $mst .= $this->collectFromCode($file);
+ }
+ } else if('sapphire' == $module) {
+ // sapphire doesn't have the usual module structure, so we'll scan all subfolders
+ $fileList = $this->getFilesRecursive("$baseDir/$module");
+ foreach($fileList as $file) {
+ // exclude ss-templates, they're scanned separately
+ if(substr($file,-3) == '.php') $mst .= $this->collectFromCode($file);
+ }
+ }
+
+ // Search for calls in template files if these exists
+ if(is_dir("$baseDir/$module/templates")) {
+ $includedtpl[$module] = array();
+ $fileList = $this->getFilesRecursive("$baseDir/$module/templates");
+ foreach($fileList as $index => $file) {
+ $mst .= $this->collectFromTemplates($index, $file, $includedtpl[$module]);
+ }
+ }
+
+ return $mst;
+
+ } else return false;
+ }
+
+ /**
+ * Write the master string table of every processed module
+ *
+ * @param string $baseDir Silverstripe's base directory
+ * @param array $allmst Module's master string tables
+ * @param array $includedtpl Templates included by other templates
+ */
+ protected function writeMasterStringFile($baseDir, $allmst, $includedtpl) {
+ // Evaluate the constructed mst
+ foreach($allmst as $mst) eval($mst);
+
+ // Resolve template dependencies
+ foreach($includedtpl as $tplmodule => $includers) {
+ // Variable initialization
+ $stringsCode = '';
+ $moduleCode = '';
+ $modulestoinclude = array();
+
+ foreach($includers as $includertpl => $allincluded)
+ foreach($allincluded as $included)
+ // we will only add code if the included template has localizable strings
+ if(isset($lang[$this->defaultLocale]["$included.ss"])) {
+ $module = i18n::get_owner_module("$included.ss");
+
+ /* if the module of the included template is not the same as the includer's one
+ * we will need to load the first one in order to have these included strings in memory
+ */
+ if ($module != $tplmodule) $modulestoinclude[$module] = $included;
+
+ // Give the includer name to the included strings in order to be used from the includer template
+ $stringsCode .= "\$lang['" . $this->defaultLocale . "']['$includertpl'] = " .
+ "array_merge(\$lang['" . $this->defaultLocale . "']['$includertpl'], \$lang['" . $this->defaultLocale . "']['$included.ss']);\n";
+ }
+
+ // Include a template for every needed module (the module language file will then be autoloaded)
+ foreach($modulestoinclude as $tpltoinclude) $moduleCode .= "self::include_by_class('$tpltoinclude.ss');\n";
+
+ // Add the extra code to the existing module mst
+ if ($stringsCode) $allmst[$tplmodule] .= "\n$moduleCode$stringsCode";
+ }
+
+ // Write each module language file
+ foreach($allmst as $module => $mst) {
+ // Create folder for lang files
+ $langFolder = $baseDir . '/' . $module . '/lang';
+ if(!file_exists($baseDir. '/' . $module . '/lang')) {
+ mkdir($langFolder, Filesystem::$folder_create_mask);
+ touch($baseDir. '/' . $module . '/lang/_manifest_exclude');
+ }
+
+ // Open the English file and write the Master String Table
+ if($fh = fopen($langFolder . '/' . $this->defaultLocale . '.php', "w")) {
+ fwrite($fh, "");
+ fclose($fh);
+ if(Director::is_cli()) {
+ echo "Created file: $langFolder/" . $this->defaultLocale . ".php\n";
+ } else {
+ echo "Created file: $langFolder/" . $this->defaultLocale . ".php
";
+ }
+
+ } else {
+ user_error("Cannot write language file! Please check permissions of $langFolder/" . $this->defaultLocale . ".php", E_USER_ERROR);
+ }
+ }
+
+ }
+
+ /**
+ * Helper function that searches for potential files to be parsed
+ *
+ * @param string $folder base directory to scan (will scan recursively)
+ * @param array $fileList Array where potential files will be added to
+ */
+ protected function getFilesRecursive($folder, &$fileList = null) {
+ if(!$fileList) $fileList = array();
+ $items = scandir($folder);
+ if($items) foreach($items as $item) {
+ if(substr($item,0,1) == '.') continue;
+ if(substr($item,-4) == '.php') $fileList[substr($item,0,-4)] = "$folder/$item";
+ else if(substr($item,-3) == '.ss') $fileList[$item] = "$folder/$item";
+ else if(is_dir("$folder/$item")) $this->getFilesRecursive("$folder/$item", $fileList);
+ }
+ return $fileList;
+ }
+
+ /**
+ * Look for calls to the underscore function in php files and build our MST
+ *
+ * @param string $file Path to the file to be parsed
+ * @return string Built Master String Table from this file
+ */
+ protected function collectFromCode($file) {
+ $callMap = array();
+ $content = file_get_contents($file);
+ $mst = '';
+ while (ereg('_t[[:space:]]*\([[:space:]]*("[^"]*"|\\\'[^\']*\\\')[[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\')([[:space:]]*,[[:space:]]*[^,)]*)?([[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))?[[:space:]]*\)', $content, $regs)) {
+ $entityParts = explode('.',substr($regs[1],1,-1));
+ $entity = array_pop($entityParts);
+ $class = implode('.',$entityParts);
+
+ if (isset($callMap["$class--$entity"]))
+ echo "Warning! Redeclaring entity $entity in file $file (previously declared in {$callMap["$class--$entity"]})
";
+
+ if (substr($regs[2],0,1) == '"') $regs[2] = addcslashes($regs[2],'\'');
+ $mst .= '$lang[\'' . $this->defaultLocale . '\'][\'' . $class . '\'][\'' . $entity . '\'] = ';
+ if ($regs[5]) {
+ $mst .= "array(\n\t'" . substr($regs[2],1,-1) . "',\n\t" . substr($regs[5],1);
+ if ($regs[7]) {
+ if (substr($regs[7],0,1) == '"') $regs[7] = addcslashes($regs[7],'\'');
+ $mst .= ",\n\t'" . substr($regs[7],1,-1) . '\'';
+ }
+ $mst .= "\n);";
+ } else $mst .= '\'' . substr($regs[2],1,-1) . '\';';
+ $mst .= "\n";
+ $content = str_replace($regs[0],"",$content);
+
+ $callMap["$class--$entity"] = $file;
+ }
+
+ return $mst;
+ }
+
+ /**
+ * Look for calls to the underscore function in template files and build our MST
+ * Template version - no "class" argument
+ *
+ * @param string $index Index used to namespace strings
+ * @param string $file Path to the file to be parsed
+ * @param string $included List of explicitly included templates
+ * @return string Built Master String Table from this file
+ */
+ protected function collectFromTemplates($index, $file, &$included) {
+ $callMap = array();
+ $content = file_get_contents($file);
+
+ // Search for included templates
+ preg_match_all('/<' . '% include +([A-Za-z0-9_]+) +%' . '>/', $content, $inc, PREG_SET_ORDER);
+ foreach ($inc as $template) {
+ if (!isset($included[$index])) $included[$index] = array();
+ array_push($included[$index], $template[1]);
+ }
+
+ $mst = '';
+ while (ereg('_t[[:space:]]*\([[:space:]]*("[^"]*"|\\\'[^\']*\\\')[[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\')([[:space:]]*,[[:space:]]*[^,)]*)?([[:space:]]*,[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))?[[:space:]]*\)',$content,$regs)) {
+
+ $entityParts = explode('.',substr($regs[1],1,-1));
+ $entity = array_pop($entityParts);
+
+ // Entity redeclaration check
+ if (isset($callMap["$index--$entity"]))
+ echo "Warning! Redeclaring entity $entity in file $file (previously declared in {$callMap["$index--$entity"]})
";
+
+ if (substr($regs[2],0,1) == '"') $regs[2] = addcslashes($regs[2],'\'');
+ $mst .= '$lang[\'' . $this->defaultLocale . '\'][\'' . $index . '\'][\'' . $entity . '\'] = ';
+ if ($regs[5]) {
+ $mst .= "array(\n\t'" . substr($regs[2],1,-1) . "',\n\t" . substr($regs[5],1);
+ if ($regs[7]) {
+ if (substr($regs[7],0,1) == '"') $regs[7] = addcslashes($regs[7],'\'\\');
+ $mst .= ",\n\t'" . substr($regs[7],1,-1) . '\'';
+ }
+ $mst .= "\n);";
+ } else $mst .= '\'' . substr($regs[2],1,-1) . '\';';
+ $mst .= "\n";
+ $content = str_replace($regs[0],"",$content);
+
+ $callMap["$index--$entity"] = $file;
+ }
+
+ return $mst;
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/i18nTextCollectorTask.php b/core/i18nTextCollectorTask.php
new file mode 100644
index 000000000..9e37511b0
--- /dev/null
+++ b/core/i18nTextCollectorTask.php
@@ -0,0 +1,32 @@
+collectI18nStatics()
+ */
+ public function run($request) {
+ $c = new i18nTextCollector($request->getVar('module'));
+ return $c->run();
+ }
+}
+?>
\ No newline at end of file