diff options
author | Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com> | 2013-08-02 22:40:51 +0200 |
---|---|---|
committer | Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com> | 2013-08-02 22:40:51 +0200 |
commit | a4565e88edbc8e3bd092a475469769c86a4c350c (patch) | |
tree | a6a3c935b03a23ff87575c8c315cf8ba78fe68c2 /inc/Twig/Extensions | |
parent | f6c9baab3efeec1d0efa151e276fc08d5b58f9e9 (diff) | |
download | wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.tar.gz wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.tar.zst wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.zip |
add Twig & refactor poche
Diffstat (limited to 'inc/Twig/Extensions')
30 files changed, 1493 insertions, 0 deletions
diff --git a/inc/Twig/Extensions/Autoloader.php b/inc/Twig/Extensions/Autoloader.php new file mode 100644 index 00000000..f23cced6 --- /dev/null +++ b/inc/Twig/Extensions/Autoloader.php | |||
@@ -0,0 +1,45 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | /** | ||
13 | * Autoloads Twig Extensions classes. | ||
14 | * | ||
15 | * @package twig | ||
16 | * @author Fabien Potencier <fabien.potencier@symfony-project.com> | ||
17 | */ | ||
18 | class Twig_Extensions_Autoloader | ||
19 | { | ||
20 | /** | ||
21 | * Registers Twig_Extensions_Autoloader as an SPL autoloader. | ||
22 | */ | ||
23 | static public function register() | ||
24 | { | ||
25 | spl_autoload_register(array(new self, 'autoload')); | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * Handles autoloading of classes. | ||
30 | * | ||
31 | * @param string $class A class name. | ||
32 | * | ||
33 | * @return boolean Returns true if the class has been loaded | ||
34 | */ | ||
35 | static public function autoload($class) | ||
36 | { | ||
37 | if (0 !== strpos($class, 'Twig_Extensions')) { | ||
38 | return; | ||
39 | } | ||
40 | |||
41 | if (file_exists($file = dirname(__FILE__).'/../../'.str_replace('_', '/', $class).'.php')) { | ||
42 | require $file; | ||
43 | } | ||
44 | } | ||
45 | } | ||
diff --git a/inc/Twig/Extensions/Extension/Debug.php b/inc/Twig/Extensions/Extension/Debug.php new file mode 100644 index 00000000..8974ce20 --- /dev/null +++ b/inc/Twig/Extensions/Extension/Debug.php | |||
@@ -0,0 +1,34 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Extension_Debug extends Twig_Extension | ||
12 | { | ||
13 | /** | ||
14 | * Returns the token parser instance to add to the existing list. | ||
15 | * | ||
16 | * @return array An array of Twig_TokenParser instances | ||
17 | */ | ||
18 | public function getTokenParsers() | ||
19 | { | ||
20 | return array( | ||
21 | new Twig_Extensions_TokenParser_Debug(), | ||
22 | ); | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * Returns the name of the extension. | ||
27 | * | ||
28 | * @return string The extension name | ||
29 | */ | ||
30 | public function getName() | ||
31 | { | ||
32 | return 'debug'; | ||
33 | } | ||
34 | } | ||
diff --git a/inc/Twig/Extensions/Extension/I18n.php b/inc/Twig/Extensions/Extension/I18n.php new file mode 100644 index 00000000..3702aa22 --- /dev/null +++ b/inc/Twig/Extensions/Extension/I18n.php | |||
@@ -0,0 +1,44 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Extension_I18n extends Twig_Extension | ||
12 | { | ||
13 | /** | ||
14 | * Returns the token parser instances to add to the existing list. | ||
15 | * | ||
16 | * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances | ||
17 | */ | ||
18 | public function getTokenParsers() | ||
19 | { | ||
20 | return array(new Twig_Extensions_TokenParser_Trans()); | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * Returns a list of filters to add to the existing list. | ||
25 | * | ||
26 | * @return array An array of filters | ||
27 | */ | ||
28 | public function getFilters() | ||
29 | { | ||
30 | return array( | ||
31 | 'trans' => new Twig_Filter_Function('gettext'), | ||
32 | ); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * Returns the name of the extension. | ||
37 | * | ||
38 | * @return string The extension name | ||
39 | */ | ||
40 | public function getName() | ||
41 | { | ||
42 | return 'i18n'; | ||
43 | } | ||
44 | } | ||
diff --git a/inc/Twig/Extensions/Extension/Intl.php b/inc/Twig/Extensions/Extension/Intl.php new file mode 100644 index 00000000..40f7fc20 --- /dev/null +++ b/inc/Twig/Extensions/Extension/Intl.php | |||
@@ -0,0 +1,66 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | class Twig_Extensions_Extension_Intl extends Twig_Extension | ||
13 | { | ||
14 | public function __construct() | ||
15 | { | ||
16 | if (!class_exists('IntlDateFormatter')) { | ||
17 | throw new RuntimeException('The intl extension is needed to use intl-based filters.'); | ||
18 | } | ||
19 | } | ||
20 | |||
21 | /** | ||
22 | * Returns a list of filters to add to the existing list. | ||
23 | * | ||
24 | * @return array An array of filters | ||
25 | */ | ||
26 | public function getFilters() | ||
27 | { | ||
28 | return array( | ||
29 | 'localizeddate' => new Twig_Filter_Function('twig_localized_date_filter', array('needs_environment' => true)), | ||
30 | ); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Returns the name of the extension. | ||
35 | * | ||
36 | * @return string The extension name | ||
37 | */ | ||
38 | public function getName() | ||
39 | { | ||
40 | return 'intl'; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | function twig_localized_date_filter(Twig_Environment $env, $date, $dateFormat = 'medium', $timeFormat = 'medium', $locale = null, $timezone = null, $format = null) | ||
45 | { | ||
46 | $date = twig_date_converter($env, $date, $timezone); | ||
47 | |||
48 | $formatValues = array( | ||
49 | 'none' => IntlDateFormatter::NONE, | ||
50 | 'short' => IntlDateFormatter::SHORT, | ||
51 | 'medium' => IntlDateFormatter::MEDIUM, | ||
52 | 'long' => IntlDateFormatter::LONG, | ||
53 | 'full' => IntlDateFormatter::FULL, | ||
54 | ); | ||
55 | |||
56 | $formatter = IntlDateFormatter::create( | ||
57 | $locale !== null ? $locale : Locale::getDefault(), | ||
58 | $formatValues[$dateFormat], | ||
59 | $formatValues[$timeFormat], | ||
60 | $date->getTimezone()->getName(), | ||
61 | IntlDateFormatter::GREGORIAN, | ||
62 | $format | ||
63 | ); | ||
64 | |||
65 | return $formatter->format($date->getTimestamp()); | ||
66 | } | ||
diff --git a/inc/Twig/Extensions/Extension/Text.php b/inc/Twig/Extensions/Extension/Text.php new file mode 100644 index 00000000..0a3dc35e --- /dev/null +++ b/inc/Twig/Extensions/Extension/Text.php | |||
@@ -0,0 +1,109 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | * | ||
11 | * @author Henrik Bjornskov <hb@peytz.dk> | ||
12 | * @package Twig | ||
13 | * @subpackage Twig-extensions | ||
14 | */ | ||
15 | class Twig_Extensions_Extension_Text extends Twig_Extension | ||
16 | { | ||
17 | /** | ||
18 | * Returns a list of filters. | ||
19 | * | ||
20 | * @return array | ||
21 | */ | ||
22 | public function getFilters() | ||
23 | { | ||
24 | $filters = array( | ||
25 | 'truncate' => new Twig_Filter_Function('twig_truncate_filter', array('needs_environment' => true)), | ||
26 | 'wordwrap' => new Twig_Filter_Function('twig_wordwrap_filter', array('needs_environment' => true)), | ||
27 | ); | ||
28 | |||
29 | if (version_compare(Twig_Environment::VERSION, '1.5.0-DEV', '<')) { | ||
30 | $filters['nl2br'] = new Twig_Filter_Function('twig_nl2br_filter', array('pre_escape' => 'html', 'is_safe' => array('html'))); | ||
31 | } | ||
32 | |||
33 | return $filters; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Name of this extension | ||
38 | * | ||
39 | * @return string | ||
40 | */ | ||
41 | public function getName() | ||
42 | { | ||
43 | return 'Text'; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | function twig_nl2br_filter($value, $sep = '<br />') | ||
48 | { | ||
49 | return str_replace("\n", $sep."\n", $value); | ||
50 | } | ||
51 | |||
52 | if (function_exists('mb_get_info')) { | ||
53 | function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...') | ||
54 | { | ||
55 | if (mb_strlen($value, $env->getCharset()) > $length) { | ||
56 | if ($preserve) { | ||
57 | if (false !== ($breakpoint = mb_strpos($value, ' ', $length, $env->getCharset()))) { | ||
58 | $length = $breakpoint; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | return rtrim(mb_substr($value, 0, $length, $env->getCharset())) . $separator; | ||
63 | } | ||
64 | |||
65 | return $value; | ||
66 | } | ||
67 | |||
68 | function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false) | ||
69 | { | ||
70 | $sentences = array(); | ||
71 | |||
72 | $previous = mb_regex_encoding(); | ||
73 | mb_regex_encoding($env->getCharset()); | ||
74 | |||
75 | $pieces = mb_split($separator, $value); | ||
76 | mb_regex_encoding($previous); | ||
77 | |||
78 | foreach ($pieces as $piece) { | ||
79 | while(!$preserve && mb_strlen($piece, $env->getCharset()) > $length) { | ||
80 | $sentences[] = mb_substr($piece, 0, $length, $env->getCharset()); | ||
81 | $piece = mb_substr($piece, $length, 2048, $env->getCharset()); | ||
82 | } | ||
83 | |||
84 | $sentences[] = $piece; | ||
85 | } | ||
86 | |||
87 | return implode($separator, $sentences); | ||
88 | } | ||
89 | } else { | ||
90 | function twig_truncate_filter(Twig_Environment $env, $value, $length = 30, $preserve = false, $separator = '...') | ||
91 | { | ||
92 | if (strlen($value) > $length) { | ||
93 | if ($preserve) { | ||
94 | if (false !== ($breakpoint = strpos($value, ' ', $length))) { | ||
95 | $length = $breakpoint; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | return rtrim(substr($value, 0, $length)) . $separator; | ||
100 | } | ||
101 | |||
102 | return $value; | ||
103 | } | ||
104 | |||
105 | function twig_wordwrap_filter(Twig_Environment $env, $value, $length = 80, $separator = "\n", $preserve = false) | ||
106 | { | ||
107 | return wordwrap($value, $length, $separator, !$preserve); | ||
108 | } | ||
109 | } \ No newline at end of file | ||
diff --git a/inc/Twig/Extensions/Gettext/Extractor.php b/inc/Twig/Extensions/Gettext/Extractor.php new file mode 100644 index 00000000..e7fa1af2 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Extractor.php | |||
@@ -0,0 +1,95 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * This file is part of the Twig Gettext utility. | ||
5 | * | ||
6 | * (c) Саша Стаменковић <umpirsky@gmail.com> | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | namespace Twig\Gettext; | ||
13 | |||
14 | use Symfony\Component\Filesystem\Filesystem; | ||
15 | |||
16 | /** | ||
17 | * Extracts translations from twig templates. | ||
18 | * | ||
19 | * @author Саша Стаменковић <umpirsky@gmail.com> | ||
20 | */ | ||
21 | class Extractor | ||
22 | { | ||
23 | /** | ||
24 | * @var \Twig_Environment | ||
25 | */ | ||
26 | protected $environment; | ||
27 | |||
28 | /** | ||
29 | * Template cached file names. | ||
30 | * | ||
31 | * @var string[] | ||
32 | */ | ||
33 | protected $templates; | ||
34 | |||
35 | /** | ||
36 | * Gettext parameters. | ||
37 | * | ||
38 | * @var string[] | ||
39 | */ | ||
40 | protected $parameters; | ||
41 | |||
42 | public function __construct(\Twig_Environment $environment) | ||
43 | { | ||
44 | $this->environment = $environment; | ||
45 | $this->reset(); | ||
46 | } | ||
47 | |||
48 | protected function reset() | ||
49 | { | ||
50 | $this->templates = array(); | ||
51 | $this->parameters = array(); | ||
52 | } | ||
53 | |||
54 | public function addTemplate($path) | ||
55 | { | ||
56 | $this->environment->loadTemplate($path); | ||
57 | $this->templates[] = $this->environment->getCacheFilename($path); | ||
58 | } | ||
59 | |||
60 | public function addGettextParameter($parameter) | ||
61 | { | ||
62 | $this->parameters[] = $parameter; | ||
63 | } | ||
64 | |||
65 | public function setGettextParameters(array $parameters) | ||
66 | { | ||
67 | $this->parameters = $parameters; | ||
68 | } | ||
69 | |||
70 | public function extract() | ||
71 | { | ||
72 | $command = 'xgettext'; | ||
73 | $command .= ' '.join(' ', $this->parameters); | ||
74 | $command .= ' '.join(' ', $this->templates); | ||
75 | |||
76 | $error = 0; | ||
77 | $output = system($command, $error); | ||
78 | if (0 !== $error) { | ||
79 | throw new \RuntimeException(sprintf( | ||
80 | 'Gettext command "%s" failed with error code %s and output: %s', | ||
81 | $command, | ||
82 | $error, | ||
83 | $output | ||
84 | )); | ||
85 | } | ||
86 | |||
87 | $this->reset(); | ||
88 | } | ||
89 | |||
90 | public function __destruct() | ||
91 | { | ||
92 | $filesystem = new Filesystem(); | ||
93 | $filesystem->remove($this->environment->getCache()); | ||
94 | } | ||
95 | } | ||
diff --git a/inc/Twig/Extensions/Gettext/Loader/Filesystem.php b/inc/Twig/Extensions/Gettext/Loader/Filesystem.php new file mode 100644 index 00000000..b011b032 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Loader/Filesystem.php | |||
@@ -0,0 +1,58 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * This file is part of the Twig Gettext utility. | ||
5 | * | ||
6 | * (c) Саша Стаменковић <umpirsky@gmail.com> | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | namespace Twig\Gettext\Loader; | ||
13 | |||
14 | /** | ||
15 | * Loads template from the filesystem. | ||
16 | * | ||
17 | * @author Саша Стаменковић <umpirsky@gmail.com> | ||
18 | */ | ||
19 | class Filesystem extends \Twig_Loader_Filesystem | ||
20 | { | ||
21 | /** | ||
22 | * Hacked find template to allow loading templates by absolute path. | ||
23 | * | ||
24 | * @param string $name template name or absolute path | ||
25 | */ | ||
26 | protected function findTemplate($name) | ||
27 | { | ||
28 | // normalize name | ||
29 | $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')); | ||
30 | |||
31 | if (isset($this->cache[$name])) { | ||
32 | return $this->cache[$name]; | ||
33 | } | ||
34 | |||
35 | $this->validateName($name); | ||
36 | |||
37 | $namespace = '__main__'; | ||
38 | if (isset($name[0]) && '@' == $name[0]) { | ||
39 | if (false === $pos = strpos($name, '/')) { | ||
40 | throw new \InvalidArgumentException(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); | ||
41 | } | ||
42 | |||
43 | $namespace = substr($name, 1, $pos - 1); | ||
44 | |||
45 | $name = substr($name, $pos + 1); | ||
46 | } | ||
47 | |||
48 | if (!isset($this->paths[$namespace])) { | ||
49 | throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); | ||
50 | } | ||
51 | |||
52 | if (is_file($name)) { | ||
53 | return $this->cache[$name] = $name; | ||
54 | } | ||
55 | |||
56 | return __DIR__.'/../Test/Fixtures/twig/empty.twig'; | ||
57 | } | ||
58 | } | ||
diff --git a/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php b/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php new file mode 100644 index 00000000..9e3431bd --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Routing/Generator/UrlGenerator.php | |||
@@ -0,0 +1,39 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * This file is part of the Twig Gettext utility. | ||
5 | * | ||
6 | * (c) Саша Стаменковић <umpirsky@gmail.com> | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | namespace Twig\Gettext\Routing\Generator; | ||
13 | |||
14 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
15 | use Symfony\Component\Routing\RequestContext; | ||
16 | |||
17 | /** | ||
18 | * Dummy url generator. | ||
19 | * | ||
20 | * @author Саша Стаменковић <umpirsky@gmail.com> | ||
21 | */ | ||
22 | class UrlGenerator implements UrlGeneratorInterface | ||
23 | { | ||
24 | protected $context; | ||
25 | |||
26 | public function generate($name, $parameters = array(), $absolute = false) | ||
27 | { | ||
28 | } | ||
29 | |||
30 | public function getContext() | ||
31 | { | ||
32 | return $this->context; | ||
33 | } | ||
34 | |||
35 | public function setContext(RequestContext $context) | ||
36 | { | ||
37 | $this->context = $context; | ||
38 | } | ||
39 | } | ||
diff --git a/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php b/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php new file mode 100644 index 00000000..d467835f --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/ExtractorTest.php | |||
@@ -0,0 +1,123 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * This file is part of the Twig Gettext utility. | ||
5 | * | ||
6 | * (c) Саша Стаменковић <umpirsky@gmail.com> | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | namespace Twig\Gettext\Test; | ||
13 | |||
14 | use Twig\Gettext\Extractor; | ||
15 | use Twig\Gettext\Loader\Filesystem; | ||
16 | use Symfony\Component\Translation\Loader\PoFileLoader; | ||
17 | |||
18 | /** | ||
19 | * @author Саша Стаменковић <umpirsky@gmail.com> | ||
20 | */ | ||
21 | class ExtractorTest extends \PHPUnit_Framework_TestCase | ||
22 | { | ||
23 | /** | ||
24 | * @var \Twig_Environment | ||
25 | */ | ||
26 | protected $twig; | ||
27 | |||
28 | /** | ||
29 | * @var PoFileLoader | ||
30 | */ | ||
31 | protected $loader; | ||
32 | |||
33 | protected function setUp() | ||
34 | { | ||
35 | $this->twig = new \Twig_Environment(new Filesystem('/'), array( | ||
36 | 'cache' => '/tmp/cache/'.uniqid(), | ||
37 | 'auto_reload' => true | ||
38 | )); | ||
39 | $this->twig->addExtension(new \Twig_Extensions_Extension_I18n()); | ||
40 | |||
41 | $this->loader = new PoFileLoader(); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * @dataProvider testExtractDataProvider | ||
46 | */ | ||
47 | public function testExtract(array $templates, array $parameters, array $messages) | ||
48 | { | ||
49 | $extractor = new Extractor($this->twig); | ||
50 | |||
51 | foreach ($templates as $template) { | ||
52 | $extractor->addTemplate($template); | ||
53 | } | ||
54 | foreach ($parameters as $parameter) { | ||
55 | $extractor->addGettextParameter($parameter); | ||
56 | } | ||
57 | |||
58 | $extractor->extract(); | ||
59 | |||
60 | $catalog = $this->loader->load($this->getPotFile(), null); | ||
61 | |||
62 | foreach ($messages as $message) { | ||
63 | $this->assertTrue( | ||
64 | $catalog->has($message), | ||
65 | sprintf('Message "%s" not found in catalog.', $message) | ||
66 | ); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | public function testExtractDataProvider() | ||
71 | { | ||
72 | return array( | ||
73 | array( | ||
74 | array( | ||
75 | __DIR__.'/Fixtures/twig/singular.twig', | ||
76 | __DIR__.'/Fixtures/twig/plural.twig', | ||
77 | ), | ||
78 | $this->getGettextParameters(), | ||
79 | array( | ||
80 | 'Hello %name%!', | ||
81 | 'Hello World!', | ||
82 | 'Hey %name%, I have one apple.', | ||
83 | 'Hey %name%, I have %count% apples.', | ||
84 | ), | ||
85 | ), | ||
86 | ); | ||
87 | } | ||
88 | |||
89 | public function testExtractNoTranslations() | ||
90 | { | ||
91 | $extractor = new Extractor($this->twig); | ||
92 | |||
93 | $extractor->addTemplate(__DIR__.'/Fixtures/twig/empty.twig'); | ||
94 | $extractor->setGettextParameters($this->getGettextParameters()); | ||
95 | |||
96 | $extractor->extract(); | ||
97 | |||
98 | $catalog = $this->loader->load($this->getPotFile(), null); | ||
99 | |||
100 | $this->assertEmpty($catalog->all('messages')); | ||
101 | } | ||
102 | |||
103 | private function getPotFile() | ||
104 | { | ||
105 | return __DIR__.'/Fixtures/messages.pot'; | ||
106 | } | ||
107 | |||
108 | private function getGettextParameters() | ||
109 | { | ||
110 | return array( | ||
111 | '--force-po', | ||
112 | '-o', | ||
113 | $this->getPotFile(), | ||
114 | ); | ||
115 | } | ||
116 | |||
117 | protected function tearDown() | ||
118 | { | ||
119 | if (file_exists($this->getPotFile())) { | ||
120 | unlink($this->getPotFile()); | ||
121 | } | ||
122 | } | ||
123 | } | ||
diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig new file mode 100644 index 00000000..05f0d26a --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/empty.twig | |||
@@ -0,0 +1 @@ | |||
Nothing to translate here. | |||
diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig new file mode 100644 index 00000000..f9754ff4 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/plural.twig | |||
@@ -0,0 +1,5 @@ | |||
1 | {% trans %} | ||
2 | Hey {{ name }}, I have one apple. | ||
3 | {% plural apple_count %} | ||
4 | Hey {{ name }}, I have {{ count }} apples. | ||
5 | {% endtrans %} | ||
diff --git a/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig new file mode 100644 index 00000000..d757cf90 --- /dev/null +++ b/inc/Twig/Extensions/Gettext/Test/Fixtures/twig/singular.twig | |||
@@ -0,0 +1,9 @@ | |||
1 | {% trans "Hello World!" %} | ||
2 | |||
3 | {% trans %} | ||
4 | Hello World! | ||
5 | {% endtrans %} | ||
6 | |||
7 | {% trans %} | ||
8 | Hello {{ name }}! | ||
9 | {% endtrans %} | ||
diff --git a/inc/Twig/Extensions/Grammar.php b/inc/Twig/Extensions/Grammar.php new file mode 100644 index 00000000..4d031b19 --- /dev/null +++ b/inc/Twig/Extensions/Grammar.php | |||
@@ -0,0 +1,30 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | abstract class Twig_Extensions_Grammar implements Twig_Extensions_GrammarInterface | ||
12 | { | ||
13 | protected $name; | ||
14 | protected $parser; | ||
15 | |||
16 | public function __construct($name) | ||
17 | { | ||
18 | $this->name = $name; | ||
19 | } | ||
20 | |||
21 | public function setParser(Twig_ParserInterface $parser) | ||
22 | { | ||
23 | $this->parser = $parser; | ||
24 | } | ||
25 | |||
26 | public function getName() | ||
27 | { | ||
28 | return $this->name; | ||
29 | } | ||
30 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Arguments.php b/inc/Twig/Extensions/Grammar/Arguments.php new file mode 100644 index 00000000..158c05ac --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Arguments.php | |||
@@ -0,0 +1,22 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Arguments extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | public function __toString() | ||
14 | { | ||
15 | return sprintf('<%s:arguments>', $this->name); | ||
16 | } | ||
17 | |||
18 | public function parse(Twig_Token $token) | ||
19 | { | ||
20 | return $this->parser->getExpressionParser()->parseArguments(); | ||
21 | } | ||
22 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Array.php b/inc/Twig/Extensions/Grammar/Array.php new file mode 100644 index 00000000..34aece0f --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Array.php | |||
@@ -0,0 +1,22 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Array extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | public function __toString() | ||
14 | { | ||
15 | return sprintf('<%s:array>', $this->name); | ||
16 | } | ||
17 | |||
18 | public function parse(Twig_Token $token) | ||
19 | { | ||
20 | return $this->parser->getExpressionParser()->parseArrayExpression(); | ||
21 | } | ||
22 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Body.php b/inc/Twig/Extensions/Grammar/Body.php new file mode 100644 index 00000000..540cfc75 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Body.php | |||
@@ -0,0 +1,39 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Body extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | protected $end; | ||
14 | |||
15 | public function __construct($name, $end = null) | ||
16 | { | ||
17 | parent::__construct($name); | ||
18 | |||
19 | $this->end = null === $end ? 'end'.$name : $end; | ||
20 | } | ||
21 | |||
22 | public function __toString() | ||
23 | { | ||
24 | return sprintf('<%s:body>', $this->name); | ||
25 | } | ||
26 | |||
27 | public function parse(Twig_Token $token) | ||
28 | { | ||
29 | $stream = $this->parser->getStream(); | ||
30 | $stream->expect(Twig_Token::BLOCK_END_TYPE); | ||
31 | |||
32 | return $this->parser->subparse(array($this, 'decideBlockEnd'), true); | ||
33 | } | ||
34 | |||
35 | public function decideBlockEnd(Twig_Token $token) | ||
36 | { | ||
37 | return $token->test($this->end); | ||
38 | } | ||
39 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Boolean.php b/inc/Twig/Extensions/Grammar/Boolean.php new file mode 100644 index 00000000..c0048090 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Boolean.php | |||
@@ -0,0 +1,24 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Boolean extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | public function __toString() | ||
14 | { | ||
15 | return sprintf('<%s:boolean>', $this->name); | ||
16 | } | ||
17 | |||
18 | public function parse(Twig_Token $token) | ||
19 | { | ||
20 | $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, array('true', 'false')); | ||
21 | |||
22 | return new Twig_Node_Expression_Constant('true' === $token->getValue() ? true : false, $token->getLine()); | ||
23 | } | ||
24 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Constant.php b/inc/Twig/Extensions/Grammar/Constant.php new file mode 100644 index 00000000..9df60458 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Constant.php | |||
@@ -0,0 +1,37 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Constant extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | protected $type; | ||
14 | |||
15 | public function __construct($name, $type = null) | ||
16 | { | ||
17 | $this->name = $name; | ||
18 | $this->type = null === $type ? Twig_Token::NAME_TYPE : $type; | ||
19 | } | ||
20 | |||
21 | public function __toString() | ||
22 | { | ||
23 | return $this->name; | ||
24 | } | ||
25 | |||
26 | public function parse(Twig_Token $token) | ||
27 | { | ||
28 | $this->parser->getStream()->expect($this->type, $this->name); | ||
29 | |||
30 | return $this->name; | ||
31 | } | ||
32 | |||
33 | public function getType() | ||
34 | { | ||
35 | return $this->type; | ||
36 | } | ||
37 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Expression.php b/inc/Twig/Extensions/Grammar/Expression.php new file mode 100644 index 00000000..4c33df0e --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Expression.php | |||
@@ -0,0 +1,22 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Expression extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | public function __toString() | ||
14 | { | ||
15 | return sprintf('<%s>', $this->name); | ||
16 | } | ||
17 | |||
18 | public function parse(Twig_Token $token) | ||
19 | { | ||
20 | return $this->parser->getExpressionParser()->parseExpression(); | ||
21 | } | ||
22 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Hash.php b/inc/Twig/Extensions/Grammar/Hash.php new file mode 100644 index 00000000..98b07d20 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Hash.php | |||
@@ -0,0 +1,22 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Hash extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | public function __toString() | ||
14 | { | ||
15 | return sprintf('<%s:hash>', $this->name); | ||
16 | } | ||
17 | |||
18 | public function parse(Twig_Token $token) | ||
19 | { | ||
20 | return $this->parser->getExpressionParser()->parseHashExpression(); | ||
21 | } | ||
22 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Number.php b/inc/Twig/Extensions/Grammar/Number.php new file mode 100644 index 00000000..f0857d20 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Number.php | |||
@@ -0,0 +1,24 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Number extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | public function __toString() | ||
14 | { | ||
15 | return sprintf('<%s:number>', $this->name); | ||
16 | } | ||
17 | |||
18 | public function parse(Twig_Token $token) | ||
19 | { | ||
20 | $this->parser->getStream()->expect(Twig_Token::NUMBER_TYPE); | ||
21 | |||
22 | return new Twig_Node_Expression_Constant($token->getValue(), $token->getLine()); | ||
23 | } | ||
24 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Optional.php b/inc/Twig/Extensions/Grammar/Optional.php new file mode 100644 index 00000000..da427485 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Optional.php | |||
@@ -0,0 +1,69 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Optional extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | protected $grammar; | ||
14 | |||
15 | public function __construct() | ||
16 | { | ||
17 | $this->grammar = array(); | ||
18 | foreach (func_get_args() as $grammar) { | ||
19 | $this->addGrammar($grammar); | ||
20 | } | ||
21 | } | ||
22 | |||
23 | public function __toString() | ||
24 | { | ||
25 | $repr = array(); | ||
26 | foreach ($this->grammar as $grammar) { | ||
27 | $repr[] = (string) $grammar; | ||
28 | } | ||
29 | |||
30 | return sprintf('[%s]', implode(' ', $repr)); | ||
31 | } | ||
32 | |||
33 | public function addGrammar(Twig_Extensions_GrammarInterface $grammar) | ||
34 | { | ||
35 | $this->grammar[] = $grammar; | ||
36 | } | ||
37 | |||
38 | public function parse(Twig_Token $token) | ||
39 | { | ||
40 | // test if we have the optional element before consuming it | ||
41 | if ($this->grammar[0] instanceof Twig_Extensions_Grammar_Constant) { | ||
42 | if (!$this->parser->getStream()->test($this->grammar[0]->getType(), $this->grammar[0]->getName())) { | ||
43 | return array(); | ||
44 | } | ||
45 | } elseif ($this->grammar[0] instanceof Twig_Extensions_Grammar_Name) { | ||
46 | if (!$this->parser->getStream()->test(Twig_Token::NAME_TYPE)) { | ||
47 | return array(); | ||
48 | } | ||
49 | } elseif ($this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) { | ||
50 | // if this is not a Constant or a Name, it must be the last element of the tag | ||
51 | |||
52 | return array(); | ||
53 | } | ||
54 | |||
55 | $elements = array(); | ||
56 | foreach ($this->grammar as $grammar) { | ||
57 | $grammar->setParser($this->parser); | ||
58 | |||
59 | $element = $grammar->parse($token); | ||
60 | if (is_array($element)) { | ||
61 | $elements = array_merge($elements, $element); | ||
62 | } else { | ||
63 | $elements[$grammar->getName()] = $element; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | return $elements; | ||
68 | } | ||
69 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Switch.php b/inc/Twig/Extensions/Grammar/Switch.php new file mode 100644 index 00000000..4245f2c8 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Switch.php | |||
@@ -0,0 +1,24 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Switch extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | public function __toString() | ||
14 | { | ||
15 | return sprintf('<%s:switch>', $this->name); | ||
16 | } | ||
17 | |||
18 | public function parse(Twig_Token $token) | ||
19 | { | ||
20 | $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, $this->name); | ||
21 | |||
22 | return new Twig_Node_Expression_Constant(true, $token->getLine()); | ||
23 | } | ||
24 | } | ||
diff --git a/inc/Twig/Extensions/Grammar/Tag.php b/inc/Twig/Extensions/Grammar/Tag.php new file mode 100644 index 00000000..727f2610 --- /dev/null +++ b/inc/Twig/Extensions/Grammar/Tag.php | |||
@@ -0,0 +1,56 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_Grammar_Tag extends Twig_Extensions_Grammar | ||
12 | { | ||
13 | protected $grammar; | ||
14 | |||
15 | public function __construct() | ||
16 | { | ||
17 | $this->grammar = array(); | ||
18 | foreach (func_get_args() as $grammar) { | ||
19 | $this->addGrammar($grammar); | ||
20 | } | ||
21 | } | ||
22 | |||
23 | public function __toString() | ||
24 | { | ||
25 | $repr = array(); | ||
26 | foreach ($this->grammar as $grammar) { | ||
27 | $repr[] = (string) $grammar; | ||
28 | } | ||
29 | |||
30 | return implode(' ', $repr); | ||
31 | } | ||
32 | |||
33 | public function addGrammar(Twig_Extensions_GrammarInterface $grammar) | ||
34 | { | ||
35 | $this->grammar[] = $grammar; | ||
36 | } | ||
37 | |||
38 | public function parse(Twig_Token $token) | ||
39 | { | ||
40 | $elements = array(); | ||
41 | foreach ($this->grammar as $grammar) { | ||
42 | $grammar->setParser($this->parser); | ||
43 | |||
44 | $element = $grammar->parse($token); | ||
45 | if (is_array($element)) { | ||
46 | $elements = array_merge($elements, $element); | ||
47 | } else { | ||
48 | $elements[$grammar->getName()] = $element; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); | ||
53 | |||
54 | return $elements; | ||
55 | } | ||
56 | } | ||
diff --git a/inc/Twig/Extensions/GrammarInterface.php b/inc/Twig/Extensions/GrammarInterface.php new file mode 100644 index 00000000..22713bf2 --- /dev/null +++ b/inc/Twig/Extensions/GrammarInterface.php | |||
@@ -0,0 +1,18 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | interface Twig_Extensions_GrammarInterface | ||
12 | { | ||
13 | function setParser(Twig_ParserInterface $parser); | ||
14 | |||
15 | function parse(Twig_Token $token); | ||
16 | |||
17 | function getName(); | ||
18 | } | ||
diff --git a/inc/Twig/Extensions/Node/Debug.php b/inc/Twig/Extensions/Node/Debug.php new file mode 100644 index 00000000..7d01bbe5 --- /dev/null +++ b/inc/Twig/Extensions/Node/Debug.php | |||
@@ -0,0 +1,69 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | /** | ||
13 | * Represents a debug node. | ||
14 | * | ||
15 | * @package twig | ||
16 | * @subpackage Twig-extensions | ||
17 | * @author Fabien Potencier <fabien.potencier@symfony-project.com> | ||
18 | * @version SVN: $Id$ | ||
19 | */ | ||
20 | class Twig_Extensions_Node_Debug extends Twig_Node | ||
21 | { | ||
22 | public function __construct(Twig_Node_Expression $expr = null, $lineno, $tag = null) | ||
23 | { | ||
24 | parent::__construct(array('expr' => $expr), array(), $lineno, $tag); | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * Compiles the node to PHP. | ||
29 | * | ||
30 | * @param Twig_Compiler A Twig_Compiler instance | ||
31 | */ | ||
32 | public function compile(Twig_Compiler $compiler) | ||
33 | { | ||
34 | $compiler->addDebugInfo($this); | ||
35 | |||
36 | $compiler | ||
37 | ->write("if (\$this->env->isDebug()) {\n") | ||
38 | ->indent() | ||
39 | ; | ||
40 | |||
41 | if (null === $this->getNode('expr')) { | ||
42 | // remove embedded templates (macros) from the context | ||
43 | $compiler | ||
44 | ->write("\$vars = array();\n") | ||
45 | ->write("foreach (\$context as \$key => \$value) {\n") | ||
46 | ->indent() | ||
47 | ->write("if (!\$value instanceof Twig_Template) {\n") | ||
48 | ->indent() | ||
49 | ->write("\$vars[\$key] = \$value;\n") | ||
50 | ->outdent() | ||
51 | ->write("}\n") | ||
52 | ->outdent() | ||
53 | ->write("}\n") | ||
54 | ->write("var_dump(\$vars);\n") | ||
55 | ; | ||
56 | } else { | ||
57 | $compiler | ||
58 | ->write("var_dump(") | ||
59 | ->subcompile($this->getNode('expr')) | ||
60 | ->raw(");\n") | ||
61 | ; | ||
62 | } | ||
63 | |||
64 | $compiler | ||
65 | ->outdent() | ||
66 | ->write("}\n") | ||
67 | ; | ||
68 | } | ||
69 | } | ||
diff --git a/inc/Twig/Extensions/Node/Trans.php b/inc/Twig/Extensions/Node/Trans.php new file mode 100644 index 00000000..d12564a7 --- /dev/null +++ b/inc/Twig/Extensions/Node/Trans.php | |||
@@ -0,0 +1,133 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | |||
12 | /** | ||
13 | * Represents a trans node. | ||
14 | * | ||
15 | * @package twig | ||
16 | * @author Fabien Potencier <fabien.potencier@symfony-project.com> | ||
17 | */ | ||
18 | class Twig_Extensions_Node_Trans extends Twig_Node | ||
19 | { | ||
20 | public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null) | ||
21 | { | ||
22 | parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag); | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * Compiles the node to PHP. | ||
27 | * | ||
28 | * @param Twig_Compiler A Twig_Compiler instance | ||
29 | */ | ||
30 | public function compile(Twig_Compiler $compiler) | ||
31 | { | ||
32 | $compiler->addDebugInfo($this); | ||
33 | |||
34 | list($msg, $vars) = $this->compileString($this->getNode('body')); | ||
35 | |||
36 | if (null !== $this->getNode('plural')) { | ||
37 | list($msg1, $vars1) = $this->compileString($this->getNode('plural')); | ||
38 | |||
39 | $vars = array_merge($vars, $vars1); | ||
40 | } | ||
41 | |||
42 | $function = null === $this->getNode('plural') ? 'gettext' : 'ngettext'; | ||
43 | |||
44 | if ($vars) { | ||
45 | $compiler | ||
46 | ->write('echo strtr('.$function.'(') | ||
47 | ->subcompile($msg) | ||
48 | ; | ||
49 | |||
50 | if (null !== $this->getNode('plural')) { | ||
51 | $compiler | ||
52 | ->raw(', ') | ||
53 | ->subcompile($msg1) | ||
54 | ->raw(', abs(') | ||
55 | ->subcompile($this->getNode('count')) | ||
56 | ->raw(')') | ||
57 | ; | ||
58 | } | ||
59 | |||
60 | $compiler->raw('), array('); | ||
61 | |||
62 | foreach ($vars as $var) { | ||
63 | if ('count' === $var->getAttribute('name')) { | ||
64 | $compiler | ||
65 | ->string('%count%') | ||
66 | ->raw(' => abs(') | ||
67 | ->subcompile($this->getNode('count')) | ||
68 | ->raw('), ') | ||
69 | ; | ||
70 | } else { | ||
71 | $compiler | ||
72 | ->string('%'.$var->getAttribute('name').'%') | ||
73 | ->raw(' => ') | ||
74 | ->subcompile($var) | ||
75 | ->raw(', ') | ||
76 | ; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | $compiler->raw("));\n"); | ||
81 | } else { | ||
82 | $compiler | ||
83 | ->write('echo '.$function.'(') | ||
84 | ->subcompile($msg) | ||
85 | ; | ||
86 | |||
87 | if (null !== $this->getNode('plural')) { | ||
88 | $compiler | ||
89 | ->raw(', ') | ||
90 | ->subcompile($msg1) | ||
91 | ->raw(', abs(') | ||
92 | ->subcompile($this->getNode('count')) | ||
93 | ->raw(')') | ||
94 | ; | ||
95 | } | ||
96 | |||
97 | $compiler->raw(");\n"); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | protected function compileString(Twig_NodeInterface $body) | ||
102 | { | ||
103 | if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant || $body instanceof Twig_Node_Expression_TempName) { | ||
104 | return array($body, array()); | ||
105 | } | ||
106 | |||
107 | $vars = array(); | ||
108 | if (count($body)) { | ||
109 | $msg = ''; | ||
110 | |||
111 | foreach ($body as $node) { | ||
112 | if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof Twig_Node_SetTemp) { | ||
113 | $node = $node->getNode(1); | ||
114 | } | ||
115 | |||
116 | if ($node instanceof Twig_Node_Print) { | ||
117 | $n = $node->getNode('expr'); | ||
118 | while ($n instanceof Twig_Node_Expression_Filter) { | ||
119 | $n = $n->getNode('node'); | ||
120 | } | ||
121 | $msg .= sprintf('%%%s%%', $n->getAttribute('name')); | ||
122 | $vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine()); | ||
123 | } else { | ||
124 | $msg .= $node->getAttribute('data'); | ||
125 | } | ||
126 | } | ||
127 | } else { | ||
128 | $msg = $body->getAttribute('data'); | ||
129 | } | ||
130 | |||
131 | return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars); | ||
132 | } | ||
133 | } | ||
diff --git a/inc/Twig/Extensions/SimpleTokenParser.php b/inc/Twig/Extensions/SimpleTokenParser.php new file mode 100644 index 00000000..49546487 --- /dev/null +++ b/inc/Twig/Extensions/SimpleTokenParser.php | |||
@@ -0,0 +1,132 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | abstract class Twig_Extensions_SimpleTokenParser extends Twig_TokenParser | ||
12 | { | ||
13 | /** | ||
14 | * Parses a token and returns a node. | ||
15 | * | ||
16 | * @param Twig_Token $token A Twig_Token instance | ||
17 | * | ||
18 | * @return Twig_NodeInterface A Twig_NodeInterface instance | ||
19 | */ | ||
20 | public function parse(Twig_Token $token) | ||
21 | { | ||
22 | $grammar = $this->getGrammar(); | ||
23 | if (!is_object($grammar)) { | ||
24 | $grammar = self::parseGrammar($grammar); | ||
25 | } | ||
26 | |||
27 | $grammar->setParser($this->parser); | ||
28 | $values = $grammar->parse($token); | ||
29 | |||
30 | return $this->getNode($values, $token->getLine()); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Gets the grammar as an object or as a string. | ||
35 | * | ||
36 | * @return string|Twig_Extensions_Grammar A Twig_Extensions_Grammar instance or a string | ||
37 | */ | ||
38 | abstract protected function getGrammar(); | ||
39 | |||
40 | /** | ||
41 | * Gets the nodes based on the parsed values. | ||
42 | * | ||
43 | * @param array $values An array of values | ||
44 | * @param integer $line The parser line | ||
45 | */ | ||
46 | abstract protected function getNode(array $values, $line); | ||
47 | |||
48 | protected function getAttribute($node, $attribute, $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY, $line = -1) | ||
49 | { | ||
50 | return new Twig_Node_Expression_GetAttr( | ||
51 | $node instanceof Twig_NodeInterface ? $node : new Twig_Node_Expression_Name($node, $line), | ||
52 | $attribute instanceof Twig_NodeInterface ? $attribute : new Twig_Node_Expression_Constant($attribute, $line), | ||
53 | $arguments instanceof Twig_NodeInterface ? $arguments : new Twig_Node($arguments), | ||
54 | $type, | ||
55 | $line | ||
56 | ); | ||
57 | } | ||
58 | |||
59 | protected function call($node, $attribute, $arguments = array(), $line = -1) | ||
60 | { | ||
61 | return $this->getAttribute($node, $attribute, $arguments, Twig_Node_Expression_GetAttr::TYPE_METHOD, $line); | ||
62 | } | ||
63 | |||
64 | protected function markAsSafe(Twig_NodeInterface $node, $line = -1) | ||
65 | { | ||
66 | return new Twig_Node_Expression_Filter( | ||
67 | $node, | ||
68 | new Twig_Node_Expression_Constant('raw', $line), | ||
69 | new Twig_Node(), | ||
70 | $line | ||
71 | ); | ||
72 | } | ||
73 | |||
74 | protected function output(Twig_NodeInterface $node, $line = -1) | ||
75 | { | ||
76 | return new Twig_Node_Print($node, $line); | ||
77 | } | ||
78 | |||
79 | protected function getNodeValues(array $values) | ||
80 | { | ||
81 | $nodes = array(); | ||
82 | foreach ($values as $value) { | ||
83 | if ($value instanceof Twig_NodeInterface) { | ||
84 | $nodes[] = $value; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | return $nodes; | ||
89 | } | ||
90 | |||
91 | static public function parseGrammar($str, $main = true) | ||
92 | { | ||
93 | static $cursor; | ||
94 | |||
95 | if (true === $main) { | ||
96 | $cursor = 0; | ||
97 | $grammar = new Twig_Extensions_Grammar_Tag(); | ||
98 | } else { | ||
99 | $grammar = new Twig_Extensions_Grammar_Optional(); | ||
100 | } | ||
101 | |||
102 | while ($cursor < strlen($str)) { | ||
103 | if (preg_match('/\s+/A', $str, $match, null, $cursor)) { | ||
104 | $cursor += strlen($match[0]); | ||
105 | } elseif (preg_match('/<(\w+)(?:\:(\w+))?>/A', $str, $match, null, $cursor)) { | ||
106 | $class = sprintf('Twig_Extensions_Grammar_%s', ucfirst(isset($match[2]) ? $match[2] : 'Expression')); | ||
107 | if (!class_exists($class)) { | ||
108 | throw new Twig_Error_Runtime(sprintf('Unable to understand "%s" in grammar (%s class does not exist)', $match[0], $class)); | ||
109 | } | ||
110 | $grammar->addGrammar(new $class($match[1])); | ||
111 | $cursor += strlen($match[0]); | ||
112 | } elseif (preg_match('/\w+/A', $str, $match, null, $cursor)) { | ||
113 | $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0])); | ||
114 | $cursor += strlen($match[0]); | ||
115 | } elseif (preg_match('/,/A', $str, $match, null, $cursor)) { | ||
116 | $grammar->addGrammar(new Twig_Extensions_Grammar_Constant($match[0], Twig_Token::PUNCTUATION_TYPE)); | ||
117 | $cursor += strlen($match[0]); | ||
118 | } elseif (preg_match('/\[/A', $str, $match, null, $cursor)) { | ||
119 | $cursor += strlen($match[0]); | ||
120 | $grammar->addGrammar(self::parseGrammar($str, false)); | ||
121 | } elseif (true !== $main && preg_match('/\]/A', $str, $match, null, $cursor)) { | ||
122 | $cursor += strlen($match[0]); | ||
123 | |||
124 | return $grammar; | ||
125 | } else { | ||
126 | throw new Twig_Error_Runtime(sprintf('Unable to parse grammar "%s" near "...%s..."', $str, substr($str, $cursor, 10))); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | return $grammar; | ||
131 | } | ||
132 | } | ||
diff --git a/inc/Twig/Extensions/TokenParser/Debug.php b/inc/Twig/Extensions/TokenParser/Debug.php new file mode 100644 index 00000000..4a7dfcc0 --- /dev/null +++ b/inc/Twig/Extensions/TokenParser/Debug.php | |||
@@ -0,0 +1,42 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009-2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_TokenParser_Debug extends Twig_TokenParser | ||
12 | { | ||
13 | /** | ||
14 | * Parses a token and returns a node. | ||
15 | * | ||
16 | * @param Twig_Token $token A Twig_Token instance | ||
17 | * | ||
18 | * @return Twig_NodeInterface A Twig_NodeInterface instance | ||
19 | */ | ||
20 | public function parse(Twig_Token $token) | ||
21 | { | ||
22 | $lineno = $token->getLine(); | ||
23 | |||
24 | $expr = null; | ||
25 | if (!$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) { | ||
26 | $expr = $this->parser->getExpressionParser()->parseExpression(); | ||
27 | } | ||
28 | $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); | ||
29 | |||
30 | return new Twig_Extensions_Node_Debug($expr, $lineno, $this->getTag()); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Gets the tag name associated with this token parser. | ||
35 | * | ||
36 | * @param string The tag name | ||
37 | */ | ||
38 | public function getTag() | ||
39 | { | ||
40 | return 'debug'; | ||
41 | } | ||
42 | } | ||
diff --git a/inc/Twig/Extensions/TokenParser/Trans.php b/inc/Twig/Extensions/TokenParser/Trans.php new file mode 100644 index 00000000..5e2dc464 --- /dev/null +++ b/inc/Twig/Extensions/TokenParser/Trans.php | |||
@@ -0,0 +1,80 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2010 Fabien Potencier | ||
7 | * | ||
8 | * For the full copyright and license information, please view the LICENSE | ||
9 | * file that was distributed with this source code. | ||
10 | */ | ||
11 | class Twig_Extensions_TokenParser_Trans extends Twig_TokenParser | ||
12 | { | ||
13 | /** | ||
14 | * Parses a token and returns a node. | ||
15 | * | ||
16 | * @param Twig_Token $token A Twig_Token instance | ||
17 | * | ||
18 | * @return Twig_NodeInterface A Twig_NodeInterface instance | ||
19 | */ | ||
20 | public function parse(Twig_Token $token) | ||
21 | { | ||
22 | $lineno = $token->getLine(); | ||
23 | $stream = $this->parser->getStream(); | ||
24 | $count = null; | ||
25 | $plural = null; | ||
26 | |||
27 | if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) { | ||
28 | $body = $this->parser->getExpressionParser()->parseExpression(); | ||
29 | } else { | ||
30 | $stream->expect(Twig_Token::BLOCK_END_TYPE); | ||
31 | $body = $this->parser->subparse(array($this, 'decideForFork')); | ||
32 | if ('plural' === $stream->next()->getValue()) { | ||
33 | $count = $this->parser->getExpressionParser()->parseExpression(); | ||
34 | $stream->expect(Twig_Token::BLOCK_END_TYPE); | ||
35 | $plural = $this->parser->subparse(array($this, 'decideForEnd'), true); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | $stream->expect(Twig_Token::BLOCK_END_TYPE); | ||
40 | |||
41 | $this->checkTransString($body, $lineno); | ||
42 | |||
43 | return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag()); | ||
44 | } | ||
45 | |||
46 | public function decideForFork(Twig_Token $token) | ||
47 | { | ||
48 | return $token->test(array('plural', 'endtrans')); | ||
49 | } | ||
50 | |||
51 | public function decideForEnd(Twig_Token $token) | ||
52 | { | ||
53 | return $token->test('endtrans'); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * Gets the tag name associated with this token parser. | ||
58 | * | ||
59 | * @param string The tag name | ||
60 | */ | ||
61 | public function getTag() | ||
62 | { | ||
63 | return 'trans'; | ||
64 | } | ||
65 | |||
66 | protected function checkTransString(Twig_NodeInterface $body, $lineno) | ||
67 | { | ||
68 | foreach ($body as $i => $node) { | ||
69 | if ( | ||
70 | $node instanceof Twig_Node_Text | ||
71 | || | ||
72 | ($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name) | ||
73 | ) { | ||
74 | continue; | ||
75 | } | ||
76 | |||
77 | throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno); | ||
78 | } | ||
79 | } | ||
80 | } | ||