Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: extract.php 7945 2008-12-19 02:16:01Z gwoo $ */
3
/**
4
 * Short description for file.
5
 *
6
 * Long description for file
7
 *
8
 * PHP versions 4 and 5
9
 *
10
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
11
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
12
 *
13
 * Licensed under The MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 *
16
 * @filesource
17
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
18
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
19
 * @package       cake
20
 * @subpackage    cake.cake.console.libs
21
 * @since         CakePHP(tm) v 1.2.0.5012
22
 * @version       $Revision: 7945 $
23
 * @modifiedby    $LastChangedBy: gwoo $
24
 * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
25
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
26
 */
27
/**
28
 * Only used when -debug option
29
 */
30
	ob_start();
31
 
32
	$singularReturn = __('Singular string  return __()', true);
33
	$singularEcho = __('Singular string  echo __()');
34
 
35
	$pluralReturn = __n('% apple in the bowl (plural string return __n())', '% apples in the blowl (plural string 2 return __n())', 3, true);
36
	$pluralEcho = __n('% apple in the bowl (plural string 2 echo __n())', '% apples in the blowl (plural string 2 echo __n()', 3);
37
 
38
	$singularDomainReturn = __d('controllers', 'Singular string domain lookup return __d()', true);
39
	$singularDomainEcho = __d('controllers', 'Singular string domain lookup echo __d()');
40
 
41
	$pluralDomainReturn = __dn('controllers', '% pears in the bowl (plural string domain lookup return __dn())', '% pears in the blowl (plural string domain lookup return __dn())', 3, true);
42
	$pluralDomainEcho = __dn('controllers', '% pears in the bowl (plural string domain lookup echo __dn())', '% pears in the blowl (plural string domain lookup echo __dn())', 3);
43
 
44
	$singularDomainCategoryReturn = __dc('controllers', 'Singular string domain and category lookup return __dc()', 5, true);
45
	$singularDomainCategoryEcho = __dc('controllers', 'Singular string domain and category lookup echo __dc()', 5);
46
 
47
	$pluralDomainCategoryReturn = __dcn('controllers', '% apple in the bowl (plural string 1 domain and category lookup return __dcn())', '% apples in the blowl (plural string 2 domain and category lookup return __dcn())', 3, 5, true);
48
	$pluralDomainCategoryEcho = __dcn('controllers', '% apple in the bowl (plural string 1 domain and category lookup echo __dcn())', '% apples in the blowl (plural string 2 domain and category lookup echo __dcn())', 3, 5);
49
 
50
	$categoryReturn = __c('Category string lookup line return __c()', 5, true);
51
	$categoryEcho = __c('Category string  lookup line echo __c()', 5);
52
 
53
	ob_end_clean();
54
/**
55
 * Language string extractor
56
 *
57
 * @package       cake
58
 * @subpackage    cake.cake.console.libs
59
 */
60
class ExtractTask extends Shell{
61
/**
62
 * Path to use when looking for strings
63
 *
64
 * @var string
65
 * @access public
66
 */
67
	var $path = null;
68
/**
69
 * Files from where to extract
70
 *
71
 * @var array
72
 * @access public
73
 */
74
	var $files = array();
75
/**
76
 * Filename where to deposit translations
77
 *
78
 * @var string
79
 * @access private
80
 */
81
	var $__filename = 'default';
82
/**
83
 * True if all strings should be merged into one file
84
 *
85
 * @var boolean
86
 * @access private
87
 */
88
	var $__oneFile = true;
89
/**
90
 * Current file being processed
91
 *
92
 * @var string
93
 * @access private
94
 */
95
	var $__file = null;
96
/**
97
 * Extracted tokens
98
 *
99
 * @var array
100
 * @access private
101
 */
102
	var $__tokens = array();
103
/**
104
 * Extracted strings
105
 *
106
 * @var array
107
 * @access private
108
 */
109
	var $__strings = array();
110
/**
111
 * History of file versions
112
 *
113
 * @var array
114
 * @access private
115
 */
116
	var $__fileVersions = array();
117
/**
118
 * Destination path
119
 *
120
 * @var string
121
 * @access private
122
 */
123
	var $__output = null;
124
/**
125
 * Execution method always used for tasks
126
 *
127
 * @access public
128
 */
129
	function execute() {
130
		if (isset($this->params['files']) && !is_array($this->params['files'])) {
131
			$this->files = explode(',', $this->params['files']);
132
		}
133
		if (isset($this->params['path'])) {
134
			$this->path = $this->params['path'];
135
		} else {
136
			$response = '';
137
			while ($response == '') {
138
				$response = $this->in("What is the full path you would like to extract?\nExample: " . $this->params['root'] . DS . "myapp\n[Q]uit", null, 'Q');
139
				if (strtoupper($response) === 'Q') {
140
					$this->out('Extract Aborted');
141
					$this->_stop();
142
				}
143
			}
144
 
145
			if (is_dir($response)) {
146
				$this->path = $response;
147
			} else {
148
				$this->err('The directory path you supplied was not found. Please try again.');
149
				$this->execute();
150
			}
151
		}
152
 
153
		if (isset($this->params['debug'])) {
154
			$this->path = ROOT;
155
			$this->files = array(__FILE__);
156
		}
157
 
158
		if (isset($this->params['output'])) {
159
			$this->__output = $this->params['output'];
160
		} else {
161
			$response = '';
162
			while ($response == '') {
163
				$response = $this->in("What is the full path you would like to output?\nExample: " . $this->path . DS . "locale\n[Q]uit", null, $this->path . DS . "locale");
164
				if (strtoupper($response) === 'Q') {
165
					$this->out('Extract Aborted');
166
					$this->_stop();
167
				}
168
			}
169
 
170
			if (is_dir($response)) {
171
				$this->__output = $response . DS;
172
			} else {
173
				$this->err('The directory path you supplied was not found. Please try again.');
174
				$this->execute();
175
			}
176
		}
177
 
178
		if (empty($this->files)) {
179
			$this->files = $this->__searchDirectory();
180
		}
181
		$this->__extract();
182
	}
183
/**
184
 * Extract text
185
 *
186
 * @access private
187
 */
188
	function __extract() {
189
		$this->out('');
190
		$this->out('');
191
		$this->out(__('Extracting...', true));
192
		$this->hr();
193
		$this->out(__('Path: ', true). $this->path);
194
		$this->out(__('Output Directory: ', true). $this->__output);
195
		$this->hr();
196
 
197
		$response = '';
198
		$filename = '';
199
		while ($response == '') {
200
			$response = $this->in(__('Would you like to merge all translations into one file?', true), array('y','n'), 'y');
201
			if (strtolower($response) == 'n') {
202
				$this->__oneFile = false;
203
			} else {
204
				while ($filename == '') {
205
					$filename = $this->in(__('What should we name this file?', true), null, $this->__filename);
206
					if ($filename == '') {
207
						$this->out(__('The filesname you supplied was empty. Please try again.', true));
208
					}
209
				}
210
				$this->__filename = $filename;
211
			}
212
		}
213
		$this->__extractTokens();
214
	}
215
/**
216
 * Show help options
217
 *
218
 * @access public
219
 */
220
	function help() {
221
		$this->out(__('CakePHP Language String Extraction:', true));
222
		$this->hr();
223
		$this->out(__('The Extract script generates .pot file(s) with translations', true));
224
		$this->out(__('By default the .pot file(s) will be place in the locale directory of -app', true));
225
		$this->out(__('By default -app is ROOT/app', true));
226
		$this->hr();
227
		$this->out(__('usage: cake i18n extract [command] [path...]', true));
228
		$this->out('');
229
		$this->out(__('commands:', true));
230
		$this->out(__('   -app [path...]: directory where your application is located', true));
231
		$this->out(__('   -root [path...]: path to install', true));
232
		$this->out(__('   -core [path...]: path to cake directory', true));
233
		$this->out(__('   -path [path...]: Full path to directory to extract strings', true));
234
		$this->out(__('   -output [path...]: Full path to output directory', true));
235
		$this->out(__('   -files: [comma separated list of files, full path to file is needed]', true));
236
		$this->out(__('   cake i18n extract help: Shows this help message.', true));
237
		$this->out(__('   -debug: Perform self test.', true));
238
		$this->out('');
239
	}
240
/**
241
 * Extract tokens out of all files to be processed
242
 *
243
 * @access private
244
 */
245
	function __extractTokens() {
246
		foreach ($this->files as $file) {
247
			$this->__file = $file;
248
			$this->out(sprintf(__('Processing %s...', true), $file));
249
 
250
			$code = file_get_contents($file);
251
 
252
			$this->__findVersion($code, $file);
253
			$allTokens = token_get_all($code);
254
			$this->__tokens = array();
255
			$lineNumber = 1;
256
 
257
			foreach ($allTokens as $token) {
258
				if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) {
259
					if (is_array($token)) {
260
						$token[] = $lineNumber;
261
					}
262
					$this->__tokens[] = $token;
263
				}
264
 
265
				if (is_array($token)) {
266
					$lineNumber += count(split("\n", $token[1])) - 1;
267
				} else {
268
					$lineNumber += count(split("\n", $token)) - 1;
269
				}
270
			}
271
			unset($allTokens);
272
			$this->basic();
273
			$this->basic('__c');
274
			$this->extended();
275
			$this->extended('__dc', 2);
276
			$this->extended('__n', 0, true);
277
			$this->extended('__dn', 2, true);
278
			$this->extended('__dcn', 4, true);
279
		}
280
		$this->__buildFiles();
281
		$this->__writeFiles();
282
		$this->out('Done.');
283
	}
284
/**
285
 * Will parse  __(), __c() functions
286
 *
287
 * @param string $functionName Function name that indicates translatable string (e.g: '__')
288
 * @access public
289
 */
290
	function basic($functionName = '__') {
291
		$count = 0;
292
		$tokenCount = count($this->__tokens);
293
 
294
		while (($tokenCount - $count) > 3) {
295
			list($countToken, $parenthesis, $middle, $right) = array($this->__tokens[$count], $this->__tokens[$count + 1], $this->__tokens[$count + 2], $this->__tokens[$count + 3]);
296
			if (!is_array($countToken)) {
297
				$count++;
298
				continue;
299
			}
300
 
301
			list($type, $string, $line) = $countToken;
302
			if (($type == T_STRING) && ($string == $functionName) && ($parenthesis == '(')) {
303
 
304
				if (in_array($right, array(')', ','))
305
				&& (is_array($middle) && ($middle[0] == T_CONSTANT_ENCAPSED_STRING))) {
306
 
307
					if ($this->__oneFile === true) {
308
						$this->__strings[$this->__formatString($middle[1])][$this->__file][] = $line;
309
					} else {
310
						$this->__strings[$this->__file][$this->__formatString($middle[1])][] = $line;
311
					}
312
				} else {
313
					$this->__markerError($this->__file, $line, $functionName, $count);
314
				}
315
			}
316
			$count++;
317
		}
318
	}
319
/**
320
 * Will parse __d(), __dc(), __n(), __dn(), __dcn()
321
 *
322
 * @param string $functionName Function name that indicates translatable string (e.g: '__')
323
 * @param integer $shift Number of parameters to shift to find translateable string
324
 * @param boolean $plural Set to true if function supports plural format, false otherwise
325
 * @access public
326
 */
327
	function extended($functionName = '__d', $shift = 0, $plural = false) {
328
		$count = 0;
329
		$tokenCount = count($this->__tokens);
330
 
331
		while (($tokenCount - $count) > 7) {
332
			list($countToken, $firstParenthesis) = array($this->__tokens[$count], $this->__tokens[$count + 1]);
333
			if (!is_array($countToken)) {
334
				$count++;
335
				continue;
336
			}
337
 
338
			list($type, $string, $line) = $countToken;
339
			if (($type == T_STRING) && ($string == $functionName) && ($firstParenthesis == '(')) {
340
				$position = $count;
341
				$depth = 0;
342
 
343
				while ($depth == 0) {
344
					if ($this->__tokens[$position] == '(') {
345
						$depth++;
346
					} elseif ($this->__tokens[$position] == ')') {
347
						$depth--;
348
					}
349
					$position++;
350
				}
351
 
352
				if ($plural) {
353
					$end = $position + $shift + 7;
354
 
355
					if ($this->__tokens[$position + $shift + 5] === ')') {
356
						$end = $position + $shift + 5;
357
					}
358
 
359
					if (empty($shift)) {
360
						list($singular, $firstComma, $plural, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$end]);
361
						$condition = ($seoncdComma == ',');
362
					} else {
363
						list($domain, $firstComma, $singular, $seoncdComma, $plural, $comma3, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$position + 4], $this->__tokens[$position + 5], $this->__tokens[$end]);
364
						$condition = ($comma3 == ',');
365
					}
366
					$condition = $condition &&
367
						(is_array($singular) && ($singular[0] == T_CONSTANT_ENCAPSED_STRING)) &&
368
						(is_array($plural) && ($plural[0] == T_CONSTANT_ENCAPSED_STRING));
369
				} else {
370
					if ($this->__tokens[$position + $shift + 5] === ')') {
371
						$comma = $this->__tokens[$position + $shift + 3];
372
						$end = $position + $shift + 5;
373
					} else {
374
						$comma = null;
375
						$end = $position + $shift + 3;
376
					}
377
 
378
					list($domain, $firstComma, $text, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $comma, $this->__tokens[$end]);
379
					$condition = ($seoncdComma == ',' || $seoncdComma === null) &&
380
						(is_array($domain) && ($domain[0] == T_CONSTANT_ENCAPSED_STRING)) &&
381
						(is_array($text) && ($text[0] == T_CONSTANT_ENCAPSED_STRING));
382
				}
383
 
384
				if (($endParenthesis == ')') && $condition) {
385
					if ($this->__oneFile === true) {
386
						if ($plural) {
387
							$this->__strings[$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][$this->__file][] = $line;
388
						} else {
389
							$this->__strings[$this->__formatString($text[1])][$this->__file][] = $line;
390
						}
391
					} else {
392
						if ($plural) {
393
							$this->__strings[$this->__file][$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][] = $line;
394
						} else {
395
							$this->__strings[$this->__file][$this->__formatString($text[1])][] = $line;
396
						}
397
					}
398
				} else {
399
					$this->__markerError($this->__file, $line, $functionName, $count);
400
				}
401
			}
402
			$count++;
403
		}
404
	}
405
/**
406
 * Build the translate template file contents out of obtained strings
407
 *
408
 * @access private
409
 */
410
	function __buildFiles() {
411
		foreach ($this->__strings as $str => $fileInfo) {
412
			$output = '';
413
			$occured = $fileList = array();
414
 
415
			if ($this->__oneFile === true) {
416
				foreach ($fileInfo as $file => $lines) {
417
					$occured[] = "$file:" . join(';', $lines);
418
 
419
					if (isset($this->__fileVersions[$file])) {
420
						$fileList[] = $this->__fileVersions[$file];
421
					}
422
				}
423
				$occurances = join("\n#: ", $occured);
424
				$occurances = str_replace($this->path, '', $occurances);
425
				$output = "#: $occurances\n";
426
				$filename = $this->__filename;
427
 
428
				if (strpos($str, "\0") === false) {
429
					$output .= "msgid \"$str\"\n";
430
					$output .= "msgstr \"\"\n";
431
				} else {
432
					list($singular, $plural) = explode("\0", $str);
433
					$output .= "msgid \"$singular\"\n";
434
					$output .= "msgid_plural \"$plural\"\n";
435
					$output .= "msgstr[0] \"\"\n";
436
					$output .= "msgstr[1] \"\"\n";
437
				}
438
				$output .= "\n";
439
			} else {
440
				foreach ($fileInfo as $file => $lines) {
441
					$filename = $str;
442
					$occured = array("$str:" . join(';', $lines));
443
 
444
					if (isset($this->__fileVersions[$str])) {
445
						$fileList[] = $this->__fileVersions[$str];
446
					}
447
					$occurances = join("\n#: ", $occured);
448
					$occurances = str_replace($this->path, '', $occurances);
449
					$output .= "#: $occurances\n";
450
 
451
					if (strpos($file, "\0") === false) {
452
						$output .= "msgid \"$file\"\n";
453
						$output .= "msgstr \"\"\n";
454
					} else {
455
						list($singular, $plural) = explode("\0", $file);
456
						$output .= "msgid \"$singular\"\n";
457
						$output .= "msgid_plural \"$plural\"\n";
458
						$output .= "msgstr[0] \"\"\n";
459
						$output .= "msgstr[1] \"\"\n";
460
					}
461
					$output .= "\n";
462
				}
463
			}
464
			$this->__store($filename, $output, $fileList);
465
		}
466
	}
467
/**
468
 * Prepare a file to be stored
469
 *
470
 * @param string $file Filename
471
 * @param string $input What to store
472
 * @param array $fileList File list
473
 * @param integer $get Set to 1 to get files to store, false to set
474
 * @return mixed If $get == 1, files to store, otherwise void
475
 * @access private
476
 */
477
	function __store($file = 0, $input = 0, $fileList = array(), $get = 0) {
478
		static $storage = array();
479
 
480
		if (!$get) {
481
			if (isset($storage[$file])) {
482
				$storage[$file][1] = array_unique(array_merge($storage[$file][1], $fileList));
483
				$storage[$file][] = $input;
484
			} else {
485
				$storage[$file] = array();
486
				$storage[$file][0] = $this->__writeHeader();
487
				$storage[$file][1] = $fileList;
488
				$storage[$file][2] = $input;
489
			}
490
		} else {
491
			return $storage;
492
		}
493
	}
494
/**
495
 * Write the files that need to be stored
496
 *
497
 * @access private
498
 */
499
	function __writeFiles() {
500
		$output = $this->__store(0, 0, array(), 1);
501
		$output = $this->__mergeFiles($output);
502
 
503
		foreach ($output as $file => $content) {
504
			$tmp = str_replace(array($this->path, '.php','.ctp','.thtml', '.inc','.tpl' ), '', $file);
505
			$tmp = str_replace(DS, '.', $tmp);
506
			$file = str_replace('.', '-', $tmp) .'.pot';
507
			$fileList = $content[1];
508
 
509
			unset($content[1]);
510
 
511
			$fileList = str_replace(array($this->path), '', $fileList);
512
 
513
			if (count($fileList) > 1) {
514
				$fileList = "Generated from files:\n#  " . join("\n#  ", $fileList);
515
			} elseif (count($fileList) == 1) {
516
				$fileList = 'Generated from file: ' . join('', $fileList);
517
			} else {
518
				$fileList = 'No version information was available in the source files.';
519
			}
520
 
521
			if (is_file($this->__output . $file)) {
522
				$response = '';
523
				while ($response == '') {
524
					$response = $this->in("\n\nError: ".$file . ' already exists in this location. Overwrite?', array('y','n', 'q'), 'n');
525
					if (strtoupper($response) === 'Q') {
526
						$this->out('Extract Aborted');
527
						$this->_stop();
528
					} elseif (strtoupper($response) === 'N') {
529
						$response = '';
530
						while ($response == '') {
531
							$response = $this->in("What would you like to name this file?\nExample: new_" . $file, null, "new_" . $file);
532
							$file = $response;
533
						}
534
					}
535
				}
536
			}
537
			$fp = fopen($this->__output . $file, 'w');
538
			fwrite($fp, str_replace('--VERSIONS--', $fileList, join('', $content)));
539
			fclose($fp);
540
		}
541
	}
542
/**
543
 * Merge output files
544
 *
545
 * @param array $output Output to merge
546
 * @return array Merged output
547
 * @access private
548
 */
549
	function __mergeFiles($output) {
550
		foreach ($output as $file => $content) {
551
			if (count($content) <= 1 && $file != $this->__filename) {
552
				@$output[$this->__filename][1] = array_unique(array_merge($output[$this->__filename][1], $content[1]));
553
 
554
				if (!isset($output[$this->__filename][0])) {
555
					$output[$this->__filename][0] = $content[0];
556
				}
557
				unset($content[0]);
558
				unset($content[1]);
559
 
560
				foreach ($content as $msgid) {
561
					$output[$this->__filename][] = $msgid;
562
				}
563
				unset($output[$file]);
564
			}
565
		}
566
		return $output;
567
	}
568
/**
569
 * Build the translation template header
570
 *
571
 * @return string Translation template header
572
 * @access private
573
 */
574
	function __writeHeader() {
575
		$output  = "# LANGUAGE translation of CakePHP Application\n";
576
		$output .= "# Copyright YEAR NAME <EMAIL@ADDRESS>\n";
577
		$output .= "# --VERSIONS--\n";
578
		$output .= "#\n";
579
		$output .= "#, fuzzy\n";
580
		$output .= "msgid \"\"\n";
581
		$output .= "msgstr \"\"\n";
582
		$output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
583
		$output .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n";
584
		$output .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n";
585
		$output .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
586
		$output .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
587
		$output .= "\"MIME-Version: 1.0\\n\"\n";
588
		$output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
589
		$output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
590
		$output .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n\n";
591
		return $output;
592
	}
593
/**
594
 * Find the version number of a file looking for SVN commands
595
 *
596
 * @param string $code Source code of file
597
 * @param string $file File
598
 * @access private
599
 */
600
	function __findVersion($code, $file) {
601
		$header = '$Id' . ':';
602
		if (preg_match('/\\' . $header . ' [\\w.]* ([\\d]*)/', $code, $versionInfo)) {
603
			$version = str_replace(ROOT, '', 'Revision: ' . $versionInfo[1] . ' ' .$file);
604
			$this->__fileVersions[$file] = $version;
605
		}
606
	}
607
/**
608
 * Format a string to be added as a translateable string
609
 *
610
 * @param string $string String to format
611
 * @return string Formatted string
612
 * @access private
613
 */
614
	function __formatString($string) {
615
		$quote = substr($string, 0, 1);
616
		$string = substr($string, 1, -1);
617
		if ($quote == '"') {
618
			$string = stripcslashes($string);
619
		} else {
620
			$string = strtr($string, array("\\'" => "'", "\\\\" => "\\"));
621
		}
622
		$string = str_replace("\r\n", "\n", $string);
623
		return addcslashes($string, "\0..\37\\\"");
624
	}
625
/**
626
 * Indicate an invalid marker on a processed file
627
 *
628
 * @param string $file File where invalid marker resides
629
 * @param integer $line Line number
630
 * @param string $marker Marker found
631
 * @param integer $count Count
632
 * @access private
633
 */
634
	function __markerError($file, $line, $marker, $count) {
635
		$this->out("Invalid marker content in $file:$line\n* $marker(", true);
636
		$count += 2;
637
		$tokenCount = count($this->__tokens);
638
		$parenthesis = 1;
639
 
640
		while ((($tokenCount - $count) > 0) && $parenthesis) {
641
			if (is_array($this->__tokens[$count])) {
642
				$this->out($this->__tokens[$count][1], false);
643
			} else {
644
				$this->out($this->__tokens[$count], false);
645
				if ($this->__tokens[$count] == '(') {
646
					$parenthesis++;
647
				}
648
 
649
				if ($this->__tokens[$count] == ')') {
650
					$parenthesis--;
651
				}
652
			}
653
			$count++;
654
		}
655
		$this->out("\n", true);
656
	}
657
/**
658
 * Search the specified path for files that may contain translateable strings
659
 *
660
 * @param string $path Path (or set to null to use current)
661
 * @return array Files
662
 * @access private
663
 */
664
	function __searchDirectory($path = null) {
665
		if ($path === null) {
666
			$path = $this->path .DS;
667
		}
668
		$files = glob("$path*.{php,ctp,thtml,inc,tpl}", GLOB_BRACE);
669
		$dirs = glob("$path*", GLOB_ONLYDIR);
670
 
671
		foreach ($dirs as $dir) {
672
			if (!preg_match("!(^|.+/)(CVS|.svn)$!", $dir)) {
673
				$files = array_merge($files, $this->__searchDirectory("$dir" . DS));
674
				if (($id = array_search($dir . DS . 'extract.php', $files)) !== FALSE) {
675
					unset($files[$id]);
676
				}
677
			}
678
		}
679
		return $files;
680
	}
681
}
682
?>