1 <?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
30 class i18nTextCollector extends Object {
31
32 protected $defaultLocale;
33
34 35 36 37 38
39 public $basePath;
40
41 42 43 44 45 46
47 public $baseSavePath;
48
49 50 51
52 function __construct($locale = null) {
53 $this->defaultLocale = ($locale) ? $locale : i18n::default_locale();
54 $this->basePath = Director::baseFolder();
55 $this->baseSavePath = Director::baseFolder();
56
57 parent::__construct();
58 }
59
60 61 62 63 64 65 66 67 68
69 public function run($restrictToModules = null) {
70
71
72 $modules = array();
73
74
75 $entitiesByModule = array();
76
77
78 if($restrictToModules && count($restrictToModules)) {
79 foreach($restrictToModules as $restrictToModule) {
80 $modules[] = basename($restrictToModule);
81 }
82 } else {
83 $modules = scandir($this->basePath);
84 }
85
86 foreach($modules as $module) {
87
88 $isValidModuleFolder = (
89 is_dir("$this->basePath/$module")
90 && is_file("$this->basePath/$module/_config.php")
91 && substr($module,0,1) != '.'
92 );
93 if(!$isValidModuleFolder) continue;
94
95
96 $processedEntities = $this->processModule($module);
97 if(isset($entitiesByModule[$module])) {
98 $entitiesByModule[$module] = array_merge_recursive($entitiesByModule[$module], $processedEntities);
99 } else {
100 $entitiesByModule[$module] = $processedEntities;
101 }
102
103
104 foreach($entitiesByModule[$module] as $fullName => $spec) {
105 if(isset($spec[3]) && $spec[3] != $module) {
106 $othermodule = $spec[3];
107 if(!isset($entitiesByModule[$othermodule])) $entitiesByModule[$othermodule] = array();
108 unset($spec[3]);
109 $entitiesByModule[$othermodule][$fullName] = $spec;
110 unset($entitiesByModule[$module][$fullName]);
111 }
112 }
113
114
115 foreach($entitiesByModule[$module] as $fullName => $spec) {
116 if(isset($spec[3]) && $spec[3] != $module) {
117 $othermodule = $spec[3];
118 if(!isset($entitiesByModule[$othermodule])) $entitiesByModule[$othermodule] = array();
119 unset($spec[3]);
120 $entitiesByModule[$othermodule][$fullName] = $spec;
121 }
122 }
123 }
124
125
126 $this->writeMasterStringFile($entitiesByModule);
127
128
129 }
130
131 132 133 134 135
136 protected function processModule($module) {
137 $entitiesArr = array();
138
139
140
141
142 if(is_dir("$this->basePath/$module/code")) {
143 $fileList = $this->getFilesRecursive("$this->basePath/$module/code");
144 } else if($module == 'sapphire') {
145
146 $fileList = $this->getFilesRecursive("$this->basePath/$module");
147 }
148 foreach($fileList as $filePath) {
149
150 if(substr($filePath,-3) == 'php') {
151 $content = file_get_contents($filePath);
152 $entitiesArr = array_merge($entitiesArr,(array)$this->collectFromCode($content, $module));
153 $entitiesArr = array_merge($entitiesArr, (array)$this->collectFromEntityProviders($filePath, $module));
154 }
155 }
156
157
158 if(is_dir("$this->basePath/$module/templates")) {
159 $fileList = $this->getFilesRecursive("$this->basePath/$module/templates");
160 foreach($fileList as $index => $filePath) {
161 $content = file_get_contents($filePath);
162
163 $namespace = basename($filePath);
164 $entitiesArr = array_merge($entitiesArr, (array)$this->collectFromTemplate($content, $module, $namespace));
165 }
166 }
167
168
169 ksort($entitiesArr);
170
171 return $entitiesArr;
172 }
173
174 public function collectFromCode($content, $module) {
175 $entitiesArr = array();
176
177 $regexRule = '_t[[:space:]]*\(' .
178 '[[:space:]]*("[^"]*"|\\\'[^\']*\\\')[[:space:]]*,' .
179 '[[:space:]]*(("([^"]|\\\")*"|\'([^\']|\\\\\')*\')' .
180 '([[:space:]]*\\.[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))*)' .
181 '([[:space:]]*,[[:space:]]*[^,)]*)?([[:space:]]*,' .
182 '[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))?[[:space:]]*' .
183 '\)';
184
185 while (ereg($regexRule, $content, $regs)) {
186 $entitiesArr = array_merge($entitiesArr, (array)$this->entitySpecFromRegexMatches($regs));
187
188
189 $content = str_replace($regs[0],"",$content);
190 }
191
192 ksort($entitiesArr);
193
194 return $entitiesArr;
195 }
196
197 public function collectFromTemplate($content, $module, $fileName) {
198 $entitiesArr = array();
199
200
201 preg_match_all('/<' . '% include +([A-Za-z0-9_]+) +%' . '>/', $content, $regs, PREG_SET_ORDER);
202 foreach($regs as $reg) {
203 $includeName = $reg[1];
204 $includeFileName = "{$includeName}.ss";
205 $filePath = SSViewer::getTemplateFileByType($includeName, 'Includes');
206 if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main');
207 if($filePath) {
208 $includeContent = file_get_contents($filePath);
209 $entitiesArr = array_merge($entitiesArr,(array)$this->collectFromTemplate($includeContent, $module, $includeFileName));
210 }
211
212 }
213
214
215 $regexRule = '_t[[:space:]]*\(' .
216 '[[:space:]]*("[^"]*"|\\\'[^\']*\\\')[[:space:]]*,' .
217 '[[:space:]]*(("([^"]|\\\")*"|\'([^\']|\\\\\')*\')' .
218 '([[:space:]]*\\.[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))*)' .
219 '([[:space:]]*,[[:space:]]*[^,)]*)?([[:space:]]*,' .
220 '[[:space:]]*("([^"]|\\\")*"|\'([^\']|\\\\\')*\'))?[[:space:]]*' .
221 '\)';
222 while(ereg($regexRule,$content,$regs)) {
223 $entitiesArr = array_merge($entitiesArr,(array)$this->entitySpecFromRegexMatches($regs, $fileName));
224
225 $content = str_replace($regs[0],"",$content);
226 }
227
228 ksort($entitiesArr);
229
230 return $entitiesArr;
231 }
232
233 234 235
236 function collectFromEntityProviders($filePath) {
237 $entitiesArr = array();
238
239 $classes = ClassInfo::classes_for_file($filePath);
240 if($classes) foreach($classes as $class) {
241
242
243
244 if(class_exists($class) && in_array('i18nEntityProvider', class_implements($class))) {
245 $reflectionClass = new ReflectionClass($class);
246 if($reflectionClass->isAbstract()) continue;
247
248 $obj = singleton($class);
249 $entitiesArr = array_merge($entitiesArr,(array)$obj->provideI18nEntities());
250 }
251 }
252
253 ksort($entitiesArr);
254
255 return $entitiesArr;
256 }
257
258 259 260
261 protected function entitySpecFromRegexMatches($regs, $_namespace = null) {
262
263 $fullName = substr($regs[1],1,-1);
264
265
266 $entityParts = explode('.', $fullName);
267 if(count($entityParts) > 1) {
268
269 $entity = array_pop($entityParts);
270
271 $namespace = implode('.',$entityParts);
272 } else {
273 $entity = array_pop($entityParts);
274 $namespace = $_namespace;
275 }
276
277
278
279
280
281
282 if(strpos('$', $entity) !== FALSE) return false;
283
284
285 $value = ($regs[2]) ? substr($regs[2],1,-1) : null;
286
287 $value = ereg_replace("([^\\])['\"][[:space:]]*\.[[:space:]]*['\"]",'\\1',$value);
288
289
290
291
292 if(substr($regs[2],0,1) == '"') {
293
294 $value = str_replace('\\"','"', $value);
295
296 $value = str_replace("'","\\'", $value);
297 }
298
299
300
301 $eol = PHP_EOL;
302 $prio = ($regs[10]) ? trim(preg_replace("/$eol/", '', substr($regs[10],1))) : null;
303
304
305 $comment = ($regs[12]) ? substr($regs[12],1,-1) : null;
306
307 return array(
308 "{$namespace}.{$entity}" => array(
309 $value,
310 $prio,
311 $comment
312 )
313 );
314 }
315
316 317 318 319 320 321
322 public function langArrayCodeForEntitySpec($entityFullName, $entitySpec) {
323 $php = '';
324 $eol = PHP_EOL;
325
326 $entityParts = explode('.', $entityFullName);
327 if(count($entityParts) > 1) {
328
329 $entity = array_pop($entityParts);
330
331 $namespace = implode('.',$entityParts);
332 } else {
333 user_error("i18nTextCollector::langArrayCodeForEntitySpec(): Wrong entity format for $entityFullName with values" . var_export($entitySpec, true), E_USER_WARNING);
334 return false;
335 }
336
337 $value = $entitySpec[0];
338 $prio = (isset($entitySpec[1])) ? addcslashes($entitySpec[1],'\'') : null;
339 $comment = (isset($entitySpec[2])) ? addcslashes($entitySpec[2],'\'') : null;
340
341 $php .= '$lang[\'' . $this->defaultLocale . '\'][\'' . $namespace . '\'][\'' . $entity . '\'] = ';
342 if ($prio) {
343 $php .= "array($eol\t'" . $value . "',$eol\t" . $prio;
344 if ($comment) {
345 $php .= ",$eol\t'" . $comment . '\'';
346 }
347 $php .= "$eol);";
348 } else {
349 $php .= '\'' . $value . '\';';
350 }
351 $php .= "$eol";
352
353 return $php;
354 }
355
356 357 358
359 protected function writeMasterStringFile($entitiesByModule) {
360
361 if($entitiesByModule) foreach($entitiesByModule as $module => $entities) {
362 $php = '';
363 $eol = PHP_EOL;
364
365
366 $langFolder = $this->baseSavePath . '/' . $module . '/lang';
367 if(!file_exists($langFolder)) {
368 Filesystem::makeFolder($langFolder, Filesystem::$folder_create_mask);
369 touch($langFolder . '/_manifest_exclude');
370 }
371
372
373 $langFile = $langFolder . '/' . $this->defaultLocale . '.php';
374 if($fh = fopen($langFile, "w")) {
375 if($entities) foreach($entities as $fullName => $spec) {
376 $php .= $this->langArrayCodeForEntitySpec($fullName, $spec);
377 }
378
379
380 try{
381 eval($php);
382 } catch(Exception $e) {
383 user_error('i18nTextCollector->writeMasterStringFile(): Invalid PHP language file. Error: ' . $e->toString(), E_USER_ERROR);
384 }
385
386 fwrite($fh, "<"."?php{$eol}{$eol}global \$lang;{$eol}{$eol}" . $php . "{$eol}?".">");
387 fclose($fh);
388
389
390 } else {
391 user_error("Cannot write language file! Please check permissions of $langFolder/" . $this->defaultLocale . ".php", E_USER_ERROR);
392 }
393 }
394
395 }
396
397 398 399 400 401 402
403 protected function getFilesRecursive($folder, &$fileList = null) {
404 if(!$fileList) $fileList = array();
405 $items = scandir($folder);
406 $isValidFolder = (
407 !in_array('_manifest_exclude', $items)
408 && !preg_match('/\/tests$/', $folder)
409 );
410
411 if($items && $isValidFolder) foreach($items as $item) {
412 if(substr($item,0,1) == '.') continue;
413 if(substr($item,-4) == '.php') $fileList[substr($item,0,-4)] = "$folder/$item";
414 else if(substr($item,-3) == '.ss') $fileList[$item] = "$folder/$item";
415 else if(is_dir("$folder/$item")) $this->getFilesRecursive("$folder/$item", $fileList);
416 }
417 return $fileList;
418 }
419
420 public function getDefaultLocale() {
421 return $this->defaultLocale;
422 }
423
424 public function setDefaultLocale($locale) {
425 $this->defaultLocale = $locale;
426 }
427 }
428 ?>
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-