mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
281 lines
10 KiB
PHP
281 lines
10 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* @author Bernat Foj Capell <bernat@silverstripe.com>
|
||
|
* @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...<br /><br />";
|
||
|
}
|
||
|
|
||
|
//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, "<?php\n\nglobal \$lang;\n\n" . $mst . "\n?>");
|
||
|
fclose($fh);
|
||
|
if(Director::is_cli()) {
|
||
|
echo "Created file: $langFolder/" . $this->defaultLocale . ".php\n";
|
||
|
} else {
|
||
|
echo "Created file: $langFolder/" . $this->defaultLocale . ".php<br />";
|
||
|
}
|
||
|
|
||
|
} 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"]})<br>";
|
||
|
|
||
|
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"]})<br>";
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
?>
|