pax_global_header00006660000000000000000000000064146105224620014514gustar00rootroot0000000000000052 comment=69817404a3f6981d2b98b293e8ddfec6e8feb923 twig-i18n-extension-4.1.1/000077500000000000000000000000001461052246200152605ustar00rootroot00000000000000twig-i18n-extension-4.1.1/CHANGELOG.md000066400000000000000000000032251461052246200170730ustar00rootroot00000000000000# Change Log ## [4.1.1] - 2024-04-19 * Add support for Twig 3.9 (#12) ## [4.1.0] - 2023-09-12 * Drop support for PHP 7.1 * Add support for PHP 8.3 ## [4.0.1] - 2021-06-10 * Fix TransNode constructor optional parameters ## [4.0.0] - 2021-02-25 * Add support for domain translation (#4) * TransNode constructor signature changed, new $domain parameter `?Node $notes, ?Node $domain = null, int $lineno` (#4) * TransNode constructor signature changed, new $context parameter `?AbstractExpression $count, ?Node $context = null, ?Node $notes` (#6) * Add support for contexts in translations (#6) * Add support for enabling `phpmyadmin/motranslator` or complex non php-gettext supported functions (#6) * Add support for custom notes labels (#6) * Make debug info disabled by default, `TransNode::$enableAddDebugInfo = true;` will add it back * Some slight performance improvements * Added tests for all the code ## [3.0.0] - 2020-06-14 * Add a .gitattributes file * Support Twig 3 * Remove extra field from composer.json * Add support field in composer.json * Require php >= 7.1 * Setup and apply phpmyadmin/coding-standard * Apply changes for php 8.0 compatibility (https://github.com/twigphp/Twig/issues/3327) ## 2.0.0 - 2020-01-14 * First release of this library. [4.1.1]: https://github.com/phpmyadmin/twig-i18n-extension/compare/4.1.0...4.1.1 [4.1.0]: https://github.com/phpmyadmin/twig-i18n-extension/compare/v4.0.1...4.1.0 [4.0.1]: https://github.com/phpmyadmin/twig-i18n-extension/compare/v4.0.0...v4.0.1 [4.0.0]: https://github.com/phpmyadmin/twig-i18n-extension/compare/v3.0.0...v4.0.0 [3.0.0]: https://github.com/phpmyadmin/twig-i18n-extension/compare/v2.0.0...v3.0.0 twig-i18n-extension-4.1.1/LICENSE000066400000000000000000000021311461052246200162620ustar00rootroot00000000000000Copyright (c) 2010-2019 Fabien Potencier Copyright (c) 2019-2021 phpMyAdmin contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. twig-i18n-extension-4.1.1/README.rst000066400000000000000000000153171461052246200167560ustar00rootroot00000000000000Twig i18n Extension =================== The ``i18n`` extension adds `gettext`_ support to Twig. It defines one tag, ``trans``. Code status ----------- .. image:: https://github.com/phpmyadmin/twig-i18n-extension/workflows/Run%20tests/badge.svg?branch=master :alt: Tests :target: https://github.com/phpmyadmin/twig-i18n-extension/actions .. image:: https://codecov.io/gh/phpmyadmin/twig-i18n-extension/branch/master/graph/badge.svg :alt: Code coverage :target: https://codecov.io/gh/phpmyadmin/twig-i18n-extension Installation ------------ This library can be installed via Composer running the following from the command line: .. code-block:: bash composer require phpmyadmin/twig-i18n-extension Configuration ------------- You need to register this extension before using the ``trans`` block .. code-block:: php use PhpMyAdmin\Twig\Extensions\I18nExtension; $twig->addExtension(new I18nExtension()); Note that you must configure the ``gettext`` extension before rendering any internationalized template. Here is a simple configuration example from the PHP `documentation`_ .. code-block:: php // Set language to French putenv('LC_ALL=fr_FR'); setlocale(LC_ALL, 'fr_FR'); // Specify the location of the translation tables bindtextdomain('myAppPhp', 'includes/locale'); bind_textdomain_codeset('myAppPhp', 'UTF-8'); // Choose domain textdomain('myAppPhp'); .. caution:: The ``i18n`` extension only works if the PHP `gettext`_ extension is enabled. Usage ----- Use the ``trans`` block to mark parts in the template as translatable: .. code-block:: twig {% trans "Hello World!" %} {% trans string_var %} {% trans %} Hello World! {% endtrans %} In a translatable string, you can embed variables: .. code-block:: twig {% trans %} Hello {{ name }}! {% endtrans %} During the gettext lookup these placeholders are converted. ``{{ name }}`` becomes ``%name%`` so the gettext ``msgid`` for this string would be ``Hello %name%!``. .. note:: ``{% trans "Hello {{ name }}!" %}`` is not a valid statement. If you need to apply filters to the variables, you first need to assign the result to a variable: .. code-block:: twig {% set name = name|capitalize %} {% trans %} Hello {{ name }}! {% endtrans %} To pluralize a translatable string, use the ``plural`` block: .. code-block:: twig {% trans %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% endtrans %} The ``plural`` tag should provide the ``count`` used to select the right string. Within the translatable string, the special ``count`` variable always contain the count value (here the value of ``apple_count``). To add notes for translators, use the ``notes`` block: .. code-block:: twig {% trans %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% notes %} This is shown in the user menu. This string should be shorter than 30 chars {% endtrans %} You can use ``notes`` with or without ``plural``. Once you get your templates compiled you should configure the ``gettext`` parser to get something like this: ``xgettext --add-comments=notes`` Within an expression or in a tag, you can use the ``trans`` filter to translate simple strings or variables: .. code-block:: twig {{ var|default(default_value|trans) }} Complex Translations within an Expression or Tag ------------------------------------------------ Translations can be done with both the ``trans`` tag and the ``trans`` filter. The filter is less powerful as it only works for simple variables or strings. For more complex scenario, like pluralization, you can use a two-step strategy: .. code-block:: twig {# assign the translation to a temporary variable #} {% set default_value %} {% trans %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% endtrans %} {% endset %} {# use the temporary variable within an expression #} {{ var|default(default_value|trans) }} Extracting Template Strings --------------------------- If you use the Twig I18n extension, you will probably need to extract the template strings at some point. Using Poedit 2 ~~~~~~~~~~~~~~ Poedit 2 has native support for extracting from Twig files and no extra setup is necessary (Pro version). Using ``xgettext`` or Poedit 1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unfortunately, the ``xgettext`` utility does not understand Twig templates natively and neither do tools based on it such as free versions of Poedit. But there is a simple workaround: as Twig converts templates to PHP files, you can use ``xgettext`` on the template cache instead. Create a script that forces the generation of the cache for all your templates. Here is a simple example to get you started .. code-block:: php use Twig\Environment; use Twig\Loader\FilesystemLoader; use PhpMyAdmin\Twig\Extensions\I18nExtension; $tplDir = __DIR__ . '/templates'; $tmpDir = '/tmp/cache/'; $loader = new FilesystemLoader($tplDir); // force auto-reload to always have the latest version of the template $twig = new Environment($loader, [ 'auto_reload' => true, 'cache' => $tmpDir, ]); $twig->addExtension(new I18nExtension()); // configure Twig the way you want // iterate over all your templates foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tplDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { // force compilation if ($file->isFile()) { $twig->loadTemplate(str_replace($tplDir . '/', '', $file)); } } Use the standard ``xgettext`` utility as you would have done with plain PHP code: .. code-block:: text xgettext --default-domain=messages -p ./locale --from-code=UTF-8 -n --omit-header -L PHP /tmp/cache/*.php Another workaround is to use `Twig Gettext Extractor`_ and extract the template strings right from `Poedit`_. .. _`gettext`: https://www.php.net/gettext .. _`documentation`: https://www.php.net/manual/en/function.gettext.php .. _`Twig Gettext Extractor`: https://github.com/umpirsky/Twig-Gettext-Extractor#readme .. _`Poedit`: https://poedit.net/ History ------- This project was forked in 2019 by the phpMyAdmin team, since it was abandoned by the `Twig project`_ but was still in use for phpMyAdmin. .. _`Twig project`: https://github.com/twigphp/Twig-extensions If you find this work useful, or have a pull request to contribute, please find us on `Github`_. .. _`Github`: https://github.com/phpmyadmin/twig-i18n-extension/ twig-i18n-extension-4.1.1/composer.json000066400000000000000000000027731461052246200200130ustar00rootroot00000000000000{ "name": "phpmyadmin/twig-i18n-extension", "description": "Internationalization support for Twig via the gettext library", "keywords": ["i18n","gettext"], "type": "library", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "The phpMyAdmin Team", "email": "developers@phpmyadmin.net", "homepage": "https://www.phpmyadmin.net/team/" } ], "support": { "issues": "https://github.com/phpmyadmin/twig-i18n-extension/issues", "source": "https://github.com/phpmyadmin/twig-i18n-extension" }, "require": { "php": "^7.2 || ^8.0", "twig/twig": "^1.42.3|^2.0|^3.0" }, "require-dev": { "phpmyadmin/coding-standard": "^3.0.0", "phpmyadmin/motranslator": "^5.2", "phpstan/phpstan": "^1.9.4", "phpunit/phpunit": "^7 || ^8 || ^9" }, "scripts": { "phpstan": "./vendor/bin/phpstan analyse", "phpunit": "phpunit", "phpcs": "phpcs", "phpcbf": "phpcbf" }, "autoload": { "psr-4": { "PhpMyAdmin\\Twig\\Extensions\\": "src/" } }, "autoload-dev": { "psr-4": { "PhpMyAdmin\\Tests\\Twig\\Extensions\\": "test/" } }, "config":{ "sort-packages": true, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, "phpstan/extension-installer": true } } } twig-i18n-extension-4.1.1/phpunit.xml.dist000066400000000000000000000013671461052246200204420ustar00rootroot00000000000000 ./test/ ./src/ twig-i18n-extension-4.1.1/src/000077500000000000000000000000001461052246200160475ustar00rootroot00000000000000twig-i18n-extension-4.1.1/src/I18nExtension.php000066400000000000000000000030341461052246200211740ustar00rootroot00000000000000 */ #[YieldReady] class TransNode extends Node { /** * The label for gettext notes to be exported * * @var string */ public static $notesLabel = '// notes: '; /** * Enable MoTranslator functions * * @var bool */ public static $enableMoTranslator = false; /** * Enable calls to addDebugInfo * * @var bool */ public static $enableAddDebugInfo = false; /** * Enables context functions usage * * @var bool */ public static $hasContextFunctions = false; public function __construct( Node $body, ?Node $plural, ?AbstractExpression $count, ?Node $context = null, ?Node $notes = null, ?Node $domain = null, int $lineno = 0, ?string $tag = null ) { $nodes = ['body' => $body]; if ($count !== null) { $nodes['count'] = $count; } if ($plural !== null) { $nodes['plural'] = $plural; } if ($notes !== null) { $nodes['notes'] = $notes; } if ($domain !== null) { $nodes['domain'] = $domain; } if ($context !== null) { $nodes['context'] = $context; } parent::__construct($nodes, [], $lineno, $tag); } /** * {@inheritdoc} */ public function compile(Compiler $compiler) { if (self::$enableAddDebugInfo) { $compiler->addDebugInfo($this); } [$msg, $vars] = $this->compileString($this->getNode('body')); $hasPlural = $this->hasNode('plural'); if ($hasPlural) { [$msg1, $vars1] = $this->compileString($this->getNode('plural')); $vars = array_merge($vars, $vars1); } $hasDomain = $this->hasNode('domain'); $hasContext = $this->hasNode('context'); $function = $this->getTransFunction($hasPlural, $hasContext, $hasDomain); if ($this->hasNode('notes')) { $message = trim($this->getNode('notes')->getAttribute('data')); // line breaks are not allowed cause we want a single line comment $message = str_replace(["\n", "\r"], ' ', $message); $compiler->raw(static::$notesLabel . $message . "\n"); } if ($vars) { $compiler ->raw($this->echoOrYield() . ' strtr(' . $function . '('); if ($hasDomain) { [$domain] = $this->compileString($this->getNode('domain')); $compiler ->subcompile($domain) ->raw(', '); } if ($hasContext && (static::$hasContextFunctions || static::$enableMoTranslator)) { [$context] = $this->compileString($this->getNode('context')); $compiler ->subcompile($context) ->raw(', '); } $compiler ->subcompile($msg); if ($hasPlural) { $compiler ->raw(', ') ->subcompile($msg1) ->raw(', abs(') ->subcompile($this->getNode('count')) ->raw(')'); } $compiler->raw('), array('); foreach ($vars as $var) { $attributeName = $var->getAttribute('name'); if ($attributeName === 'count') { $compiler ->string('%count%') ->raw(' => abs(') ->subcompile($this->getNode('count')) ->raw('), '); } else { $compiler ->string('%' . $attributeName . '%') ->raw(' => ') ->subcompile($var) ->raw(', '); } } $compiler->raw("));\n"); } else { $compiler ->raw($this->echoOrYield() . ' ' . $function . '('); if ($hasDomain) { [$domain] = $this->compileString($this->getNode('domain')); $compiler ->subcompile($domain) ->raw(', '); } if ($hasContext) { if (static::$hasContextFunctions || static::$enableMoTranslator) { [$context] = $this->compileString($this->getNode('context')); $compiler ->subcompile($context) ->raw(', '); } } $compiler ->subcompile($msg); if ($hasPlural) { $compiler ->raw(', ') ->subcompile($msg1) ->raw(', abs(') ->subcompile($this->getNode('count')) ->raw(')'); } $compiler->raw(");\n"); } } /** * Keep this method protected instead of private some implementations may use it */ protected function compileString(Node $body): array { if ( $body instanceof NameExpression || $body instanceof ConstantExpression || $body instanceof TempNameExpression ) { return [$body, []]; } $vars = []; if (count($body)) { $msg = ''; foreach ($body as $node) { if ($node instanceof PrintNode) { $n = $node->getNode('expr'); while ($n instanceof FilterExpression) { $n = $n->getNode('node'); } while ($n instanceof CheckToStringNode) { $n = $n->getNode('expr'); } $attributeName = $n->getAttribute('name'); $msg .= sprintf('%%%s%%', $attributeName); $vars[] = new NameExpression($attributeName, $n->getTemplateLine()); } else { /** @phpstan-var TextNode $node */ $msg .= $node->getAttribute('data'); } } } else { $msg = $body->getAttribute('data'); } return [new Node([new ConstantExpression(trim($msg), $body->getTemplateLine())]), $vars]; } /** * Keep this protected to allow people to override it with their own logic */ protected function getTransFunction(bool $hasPlural, bool $hasContext, bool $hasDomain): string { $functionPrefix = ''; if (static::$enableMoTranslator) { // The functions are prefixed with an underscore $functionPrefix = '_'; } // If it has not context function support or not MoTranslator if (! static::$hasContextFunctions && ! static::$enableMoTranslator) { // Not found on native PHP: dnpgettext, npgettext, dpgettext, pgettext // No domain plural context support // No domain context support // No context support // No plural context support if ($hasDomain) { // dngettext($domain, $msgid, $msgidPlural, $number); // dgettext($domain, $msgid); return $functionPrefix . ($hasPlural ? 'dngettext' : 'dgettext'); } // ngettext($msgid, $msgidPlural, $number); // gettext($msgid); return $functionPrefix . ($hasPlural ? 'ngettext' : 'gettext'); } if ($hasDomain) { if ($hasPlural) { // dnpgettext($domain, $msgctxt, $msgid, $msgidPlural, $number); // dngettext($domain, $msgid, $msgidPlural, $number); return $functionPrefix . ($hasContext ? 'dnpgettext' : 'dngettext'); } // dpgettext($domain, $msgctxt, $msgid); // dgettext($domain, $msgid); return $functionPrefix . ($hasContext ? 'dpgettext' : 'dgettext'); } if ($hasPlural) { // npgettext($msgctxt, $msgid, $msgidPlural, $number); // ngettext($msgid, $msgidPlural, $number); return $functionPrefix . ($hasContext ? 'npgettext' : 'ngettext'); } // pgettext($msgctxt, $msgid); // gettext($msgid); return $functionPrefix . ($hasContext ? 'pgettext' : 'gettext'); } private function echoOrYield(): string { return class_exists(YieldReady::class) ? 'yield' : 'echo'; } } twig-i18n-extension-4.1.1/src/TokenParser/000077500000000000000000000000001461052246200203045ustar00rootroot00000000000000twig-i18n-extension-4.1.1/src/TokenParser/TransTokenParser.php000066400000000000000000000111641461052246200242650ustar00rootroot00000000000000preParse($token); return new TransNode($body, $plural, $count, $context, $notes, $domain, $lineno, $tag); } protected function preParse(Token $token): array { $lineno = $token->getLine(); $stream = $this->parser->getStream(); $domain = null; $count = null; $plural = null; $notes = null; $context = null; /* If we aren't closing the block, do we have a domain? */ if ($stream->test(Token::NAME_TYPE)) { $stream->expect(Token::NAME_TYPE, 'from'); $domain = $this->parser->getExpressionParser()->parseExpression(); } if (! $stream->test(Token::BLOCK_END_TYPE)) { $body = $this->parser->getExpressionParser()->parseExpression(); } else { $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideForFork']); $next = $stream->next()->getValue(); if ($next === 'plural') { $count = $this->parser->getExpressionParser()->parseExpression(); $stream->expect(Token::BLOCK_END_TYPE); $plural = $this->parser->subparse([$this, 'decideForFork']); $next = $stream->next()->getValue(); if ($next === 'notes') { $stream->expect(Token::BLOCK_END_TYPE); $notes = $this->parser->subparse([$this, 'decideForEnd'], true); } elseif ($next === 'context') { $stream->expect(Token::BLOCK_END_TYPE); $context = $this->parser->subparse([$this, 'decideForEnd'], true); } } elseif ($next === 'context') { $stream->expect(Token::BLOCK_END_TYPE); $context = $this->parser->subparse([$this, 'decideForEnd'], true); } elseif ($next === 'notes') { $stream->expect(Token::BLOCK_END_TYPE); $notes = $this->parser->subparse([$this, 'decideForEnd'], true); } } $stream->expect(Token::BLOCK_END_TYPE); $this->checkTransString($body, $lineno); if ($notes instanceof TextNode) { // Don't use TextNode for $notes to avoid it getting merged with $body in Twig >= 3.9.0. $notes = new Node([], ['data' => $notes->getAttribute('data')], $notes->getTemplateLine()); } if ($context instanceof TextNode) { // Don't use TextNode for $context to avoid it getting merged with $body in Twig >= 3.9.0. $context = new Node([], ['data' => $context->getAttribute('data')], $context->getTemplateLine()); } return [$body, $plural, $count, $context, $notes, $domain, $lineno, $this->getTag()]; } /** * @return bool */ public function decideForFork(Token $token) { return $token->test(['plural', 'context', 'notes', 'endtrans']); } /** * @return bool */ public function decideForEnd(Token $token) { return $token->test('endtrans'); } /** * {@inheritdoc} */ public function getTag() { return 'trans'; } /** * @return void * * @throws SyntaxError */ protected function checkTransString(Node $body, int $lineno) { foreach ($body as $i => $node) { if ( $node instanceof TextNode || ($node instanceof PrintNode && $node->getNode('expr') instanceof NameExpression) ) { continue; } throw new SyntaxError( 'The text to be translated with "trans" can only contain references to simple variables.', $lineno ); } } } twig-i18n-extension-4.1.1/test/000077500000000000000000000000001461052246200162375ustar00rootroot00000000000000twig-i18n-extension-4.1.1/test/Fixtures/000077500000000000000000000000001461052246200200505ustar00rootroot00000000000000twig-i18n-extension-4.1.1/test/Fixtures/DomainContext.test000066400000000000000000000003411461052246200235230ustar00rootroot00000000000000--TEST-- A simple trans with context and a domain --TEMPLATE-- {% trans from "core" %}Some text{% context %}This text is all about bla, foo, bar, blog{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Some texttwig-i18n-extension-4.1.1/test/Fixtures/DomainPluralContext.test000066400000000000000000000006401461052246200247050ustar00rootroot00000000000000--TEST-- Test a plural example with context and a domain --TEMPLATE-- {# Plural tag with domain #} {% set name = 'Jim' %} {% set apple_count = 1 %} {% trans from 'core' %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% context %}Context for the plural block {% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Jim, I have one apple.twig-i18n-extension-4.1.1/test/Fixtures/ObjectTrans.test000066400000000000000000000011421461052246200231650ustar00rootroot00000000000000--TEST-- Test a plural example with "exotic" code --TEMPLATE-- {# Plural tag without domain #} {% set name = 'Jim' %} {% set apple_count = 2 %} {% trans from obj %} Hey {{ name }}, I have one apple. {% plural [a][0] %} Hey {{ name }}, I have {{ count }} apples. {% context %} {{ obj|upper }} {{ obj }} {% endtrans %} --DATA-- if (! class_exists('CustomObj')) { class CustomObj { public $count = 2; public function __toString() { return 'Jim'; } } } return ['obj' => new CustomObj(), 'a' => 2] --CONFIG-- return [] --EXPECT-- Hey Jim, I have 2 apples.twig-i18n-extension-4.1.1/test/Fixtures/PluralContext.test000066400000000000000000000006071461052246200235600ustar00rootroot00000000000000--TEST-- Test a plural example with context --TEMPLATE-- {# Plural tag with domain #} {% set name = 'Jim' %} {% set apple_count = 1 %} {% trans %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% context %}Context for the plural block {% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Jim, I have one apple.twig-i18n-extension-4.1.1/test/Fixtures/PluralExample.test000066400000000000000000000005221461052246200235230ustar00rootroot00000000000000--TEST-- Test a plural example --TEMPLATE-- {# Plural tag without domain #} {% set name = 'Jim' %} {% set apple_count = 2 %} {% trans %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Jim, I have 2 apples.twig-i18n-extension-4.1.1/test/Fixtures/PluralExampleDomain.test000066400000000000000000000005331461052246200246550ustar00rootroot00000000000000--TEST-- Test a plural example --TEMPLATE-- {# Plural tag with domain #} {% set name = 'Jim' %} {% set apple_count = 2 %} {% trans from 'core' %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Jim, I have 2 apples.twig-i18n-extension-4.1.1/test/Fixtures/PluralOneExample.test000066400000000000000000000005231461052246200241660ustar00rootroot00000000000000--TEST-- Test a plural example --TEMPLATE-- {# Plural tag without domain #} {% set name = 'Jim' %} {% set apple_count = 1 %} {% trans %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Jim, I have one apple.twig-i18n-extension-4.1.1/test/Fixtures/PluralOneExampleDomain.test000066400000000000000000000005541461052246200253220ustar00rootroot00000000000000--TEST-- Test a plural one example with domain --TEMPLATE-- {# Plural tag with domain #} {% set name = 'Jim' %} {% set apple_count = 1 %} {% trans from 'core' %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Jim, I have one apple.twig-i18n-extension-4.1.1/test/Fixtures/PluralWithNotes.test000066400000000000000000000006151461052246200240570ustar00rootroot00000000000000--TEST-- Test a plural example with notes --TEMPLATE-- {# Plural tag with domain #} {% set name = 'Jim' %} {% set apple_count = 1 %} {% trans from 'core' %} Hey {{ name }}, I have one apple. {% plural apple_count %} Hey {{ name }}, I have {{ count }} apples. {% notes %}Notes for the plural block {% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Jim, I have one apple.twig-i18n-extension-4.1.1/test/Fixtures/PluralWithSprintf.test000066400000000000000000000004261461052246200244140ustar00rootroot00000000000000--TEST-- Test a plural example without variables in the content --CONFIG-- return [] --TEMPLATE-- {% trans %}%s table{% plural num_tables %}%s tables{% endtrans %} --DATA-- return [ 'num_tables' => 1] --EXPECT-- %s table --DATA-- return [ 'num_tables' => 2] --EXPECT-- %s tablestwig-i18n-extension-4.1.1/test/Fixtures/SimpleContext.test000066400000000000000000000003101461052246200235410ustar00rootroot00000000000000--TEST-- A simple trans with context --TEMPLATE-- {% trans %}Some text{% context %}This text is all about bla, foo, bar, blog{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Some texttwig-i18n-extension-4.1.1/test/Fixtures/SimpleDomain.test000066400000000000000000000002771461052246200233400ustar00rootroot00000000000000--TEST-- Test a simple domain example --TEMPLATE-- {# With specific domain #} {% trans from "core" %}Castle Quote{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Castle Quotetwig-i18n-extension-4.1.1/test/Fixtures/SimpleExample.test000066400000000000000000000002231461052246200235130ustar00rootroot00000000000000--TEST-- Test a simple trans tag --TEMPLATE-- {% trans %}Castle Quote{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Castle Quotetwig-i18n-extension-4.1.1/test/Fixtures/SimpleXssExample.test000066400000000000000000000003051461052246200242120ustar00rootroot00000000000000--TEST-- Test a simple trans tag against XSS injection --TEMPLATE-- {% trans %}{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- twig-i18n-extension-4.1.1/test/Fixtures/SpecificTrans.test000066400000000000000000000003001461052246200234770ustar00rootroot00000000000000--TEST-- Test a specific trans syntax --TEMPLATE-- {% trans from 'foo' 'Hey Matthieu, why so many bugs ?' %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Hey Matthieu, why so many bugs ?twig-i18n-extension-4.1.1/test/Fixtures/TransFilter.test000066400000000000000000000003301461052246200232020ustar00rootroot00000000000000--TEST-- Test a simple trans filter --TEMPLATE-- {{ 'Castle Quote'|trans }} {# Filter before (default domain) #} {{ 'Castle Quote'|trans }} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Castle Quote Castle Quotetwig-i18n-extension-4.1.1/test/Fixtures/TransFilterDomain.test000066400000000000000000000002541461052246200243370ustar00rootroot00000000000000--TEST-- Test a simple trans filter --TEMPLATE-- {# Filter with domain #} {{ 'Castle Quote'|trans('core') }} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Castle Quotetwig-i18n-extension-4.1.1/test/Fixtures/TransFilterXss.test000066400000000000000000000003111461052246200236770ustar00rootroot00000000000000--TEST-- Test a simple trans filter against XSS injection --TEMPLATE-- {{ ''|trans }} --DATA-- return [] --CONFIG-- return [] --EXPECT-- <script>alert(1);</script>twig-i18n-extension-4.1.1/test/Fixtures/TransFunctionOutput.test000066400000000000000000000002401461052246200247630ustar00rootroot00000000000000--TEST-- Test with a function output --TEMPLATE-- {{ max({2: "e", 1: "a", 3: "b", 5: "d", 4: "c"})|trans }} --DATA-- return [] --CONFIG-- return [] --EXPECT-- etwig-i18n-extension-4.1.1/test/Fixtures/TransTagSetVariable.test000066400000000000000000000003011461052246200246100ustar00rootroot00000000000000--TEST-- Test assigning to a variable with set --TEMPLATE-- A {% set variable %}{% trans 'text' %}{% endset %} B {{ variable }} C --DATA-- return [] --CONFIG-- return [] --EXPECT-- A B text C twig-i18n-extension-4.1.1/test/Fixtures/TransTagWithNotes.test000066400000000000000000000003751461052246200243460ustar00rootroot00000000000000--TEST-- Test with notes --TEMPLATE-- {% trans %}calendar-month-year{% notes %}Month-year order for calendar, use either "calendar-month-year" or "calendar-year-month".{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- calendar-month-yeartwig-i18n-extension-4.1.1/test/Fixtures/TransTooComplex.test000066400000000000000000000004521461052246200240530ustar00rootroot00000000000000--TEST-- Test with a too complex string --TEMPLATE-- {% trans %}{{ items.current }}{% endtrans %} --DATA-- return ['b' => 18] --CONFIG-- return [] --EXCEPTION-- Twig\Error\SyntaxError: The text to be translated with "trans" can only contain references to simple variables in "index.twig" at line 2.twig-i18n-extension-4.1.1/test/Fixtures/TransVariableAndData.test000066400000000000000000000002151461052246200247210ustar00rootroot00000000000000--TEST-- Test with a function and data --TEMPLATE-- {{ max([1,2,3,b])|trans }} --DATA-- return ['b' => 18] --CONFIG-- return [] --EXPECT-- 18twig-i18n-extension-4.1.1/test/Fixtures/VariableDomain.test000066400000000000000000000003231461052246200236240ustar00rootroot00000000000000--TEST-- Test a simple domain example --TEMPLATE-- {# With domain variable #} {% set var = 'core' %} {% trans from var %}Castle Quote{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Castle Quotetwig-i18n-extension-4.1.1/test/FixturesWithSandbox/000077500000000000000000000000001461052246200222235ustar00rootroot00000000000000twig-i18n-extension-4.1.1/test/FixturesWithSandbox/Plural.test000066400000000000000000000011421461052246200243610ustar00rootroot00000000000000--TEST-- Test a plural example with "exotic" code --TEMPLATE-- {# Plural tag without domain #} {% set name = 'Jim' %} {% set apple_count = 2 %} {% trans from obj %} Hey {{ name }}, I have one apple. {% plural [a][0] %} Hey {{ name }}, I have {{ count }} apples. {% context %} {{ obj|upper }} {{ obj }} {% endtrans %} --DATA-- if (! class_exists('CustomObj')) { class CustomObj { public $count = 2; public function __toString() { return 'Jim'; } } } return ['obj' => new CustomObj(), 'a' => 2] --CONFIG-- return [] --EXPECT-- Hey Jim, I have 2 apples.twig-i18n-extension-4.1.1/test/I18nExtensionMoTranslatorDebugTest.php000066400000000000000000000020501461052246200255360ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-4.1.1/test/I18nExtensionMoTranslatorSandboxTest.php000066400000000000000000000025261461052246200261160ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-4.1.1/test/I18nExtensionMoTranslatorTest.php000066400000000000000000000016151461052246200245750ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-4.1.1/test/I18nExtensionTest.php000066400000000000000000000015061461052246200222260ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-4.1.1/test/MoTranslator/000077500000000000000000000000001461052246200206645ustar00rootroot00000000000000twig-i18n-extension-4.1.1/test/MoTranslator/I18nExtensionDebug.php000066400000000000000000000014341461052246200247620ustar00rootroot00000000000000assertEquals($body, $node->getNode('body')); $this->assertEquals($count, $node->getNode('count')); $this->assertEquals($plural, $node->getNode('plural')); $this->assertEquals($notes, $node->getNode('notes')); $this->assertEquals($domain, $node->getNode('domain')); $this->assertEquals($context, $node->getNode('context')); } /** * @return array[] */ public function getTests(): array { $tests = []; $body = new NameExpression('foo', 0); $domain = new Node([ new TextNode('coredomain', 0), ], [], 0); $node = new TransNode($body, null, null, null, null, $domain, 0); $tests[] = [ $node, sprintf($this->echoOrYield() . ' _dgettext("coredomain", %s);', $this->getVariableGetter('foo')), ]; $body = new NameExpression('foo', 0); $domain = new Node([ new TextNode('coredomain', 0), ], [], 0); $context = new Node([ new TextNode('The context', 0), ], [], 0); $node = new TransNode($body, null, null, $context, null, $domain, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' _dpgettext("coredomain", "The context", %s);', $this->getVariableGetter('foo') ), ]; $body = new Node([ new TextNode('J\'ai ', 0), new PrintNode(new NameExpression('foo', 0), 0), new TextNode(' pommes', 0), ], [], 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(_gettext("J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo') ), ]; $count = new ConstantExpression(12, 0); $body = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have one apple', 0), ], [], 0); $plural = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' apples', 0), ], [], 0); $node = new TransNode($body, $plural, $count, null, null, null, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(_ngettext("Hey %%name%%, I have one apple", "Hey %%name%%,' . ' I have %%count%% apples", abs(12)), array("%%name%%" => %s,' . ' "%%name%%" => %s, "%%count%%" => abs(12), ));', $this->getVariableGetter('name'), $this->getVariableGetter('name') ), ]; $body = new Node([ new TextNode('J\'ai ', 0), new PrintNode(new NameExpression('foo', 0), 0), new TextNode(' pommes', 0), ], [], 0); $context = new Node([ new TextNode('The context', 0), ], [], 0); $node = new TransNode($body, null, null, $context, null, null, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(_pgettext("The context", "J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo') ), ]; $count = new ConstantExpression(12, 0); $body = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have one apple', 0), ], [], 0); $context = new Node([ new TextNode('The context', 0), ], [], 0); $plural = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' apples', 0), ], [], 0); $node = new TransNode($body, $plural, $count, $context, null, null, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(_npgettext("The context", "Hey %%name%%, I have one apple", "Hey %%name%%,' . ' I have %%count%% apples", abs(12)), array("%%name%%" => %s,' . ' "%%name%%" => %s, "%%count%%" => abs(12), ));', $this->getVariableGetter('name'), $this->getVariableGetter('name') ), ]; $body = new Node([ new TextNode('J\'ai ', 0), new PrintNode(new NameExpression('foo', 0), 0), new TextNode(' pommes', 0), ], [], 0); $context = new Node([ new TextNode('The context', 0), ], [], 0); $domain = new Node([ new TextNode('mydomain', 0), ], [], 0); $node = new TransNode($body, null, null, $context, null, $domain, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(_dpgettext("mydomain", "The context", "J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo') ), ]; $count = new ConstantExpression(12, 0); $body = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have one apple', 0), ], [], 0); $context = new Node([ new TextNode('The context', 0), ], [], 0); $domain = new Node([ new TextNode('mydomain', 0), ], [], 0); $plural = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' apples', 0), ], [], 0); $node = new TransNode($body, $plural, $count, $context, null, $domain, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(_dnpgettext("mydomain", "The context", "Hey %%name%%, I have one apple",' . ' "Hey %%name%%, I have %%count%% apples", abs(12)), array("%%name%%" => %s,' . ' "%%name%%" => %s, "%%count%%" => abs(12), ));', $this->getVariableGetter('name'), $this->getVariableGetter('name') ), ]; return $tests; } } twig-i18n-extension-4.1.1/test/Node/TransTest.php000066400000000000000000000230401461052246200215630ustar00rootroot00000000000000assertEquals($body, $node->getNode('body')); $this->assertEquals($count, $node->getNode('count')); $this->assertEquals($plural, $node->getNode('plural')); } public function testConstructorWithDomain(): void { $count = new ConstantExpression(12, 0); $body = new Node([ new TextNode('Hello', 0), ], [], 0); $domain = new Node([ new TextNode('coredomain', 0), ], [], 0); $plural = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' apples', 0), ], [], 0); $node = new TransNode($body, $plural, $count, null, null, $domain, 0); $this->assertEquals($body, $node->getNode('body')); $this->assertEquals($count, $node->getNode('count')); $this->assertEquals($plural, $node->getNode('plural')); $this->assertEquals($domain, $node->getNode('domain')); } public function testEnableDebugNotEnabled(): void { $count = new ConstantExpression(5, 0); $body = new TextNode('There is 1 pending task', 0); $plural = new Node([ new TextNode('There are ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' pending tasks', 0), ], [], 0); $notes = new TextNode('Notes for translators', 0); TransNode::$enableAddDebugInfo = false; TransNode::$notesLabel = '// custom: '; $node = new TransNode($body, $plural, $count, null, $notes, null, 80); $compiler = $this->getCompiler(); $this->assertEmpty($compiler->getDebugInfo()); $sourceCode = $compiler->compile($node)->getSource(); $this->assertSame( '// custom: Notes for translators' . "\n" . $this->echoOrYield() . ' strtr(ngettext("There is 1 pending task",' . ' "There are %count% pending tasks", abs(5)), array("%count%" => abs(5), ));' . "\n", $sourceCode ); $this->assertSame([], $compiler->getDebugInfo()); TransNode::$enableAddDebugInfo = false; TransNode::$notesLabel = '// notes: '; } public function testEnableDebugEnabled(): void { $count = new ConstantExpression(5, 0); $body = new TextNode('There is 1 pending task', 0); $plural = new Node([ new TextNode('There are ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' pending tasks', 0), ], [], 0); $notes = new TextNode('Notes for translators', 0); TransNode::$enableAddDebugInfo = true; TransNode::$notesLabel = '// custom: '; $node = new TransNode($body, $plural, $count, null, $notes, null, 80); $compiler = $this->getCompiler(); $this->assertEmpty($compiler->getDebugInfo()); $sourceCode = $compiler->compile($node)->getSource(); $this->assertSame( '// line 80' . "\n" . '// custom: Notes for translators' . "\n" . $this->echoOrYield() . ' strtr(ngettext("There' . ' is 1 pending task", "There are %count% pending tasks", abs(5)), array("%count%" => abs(5), ));' . "\n", $sourceCode ); $this->assertSame([2 => 80], $compiler->getDebugInfo()); TransNode::$enableAddDebugInfo = false; TransNode::$notesLabel = '// notes: '; } /** * @return array[] */ public function getTests(): array { $tests = []; $body = new NameExpression('foo', 0); $domain = new Node([ new TextNode('coredomain', 0), ], [], 0); $node = new TransNode($body, null, null, null, null, $domain, 0); $tests[] = [ $node, sprintf($this->echoOrYield() . ' dgettext("coredomain", %s);', $this->getVariableGetter('foo')), ]; $body = new NameExpression('foo', 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [$node, sprintf($this->echoOrYield() . ' gettext(%s);', $this->getVariableGetter('foo'))]; $body = new ConstantExpression('Hello', 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [$node, $this->echoOrYield() . ' gettext("Hello");']; $body = new Node([ new TextNode('Hello', 0), ], [], 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [$node, $this->echoOrYield() . ' gettext("Hello");']; $body = new Node([ new TextNode('J\'ai ', 0), new PrintNode(new NameExpression('foo', 0), 0), new TextNode(' pommes', 0), ], [], 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(gettext("J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo') ), ]; $count = new ConstantExpression(12, 0); $body = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have one apple', 0), ], [], 0); $plural = new Node([ new TextNode('Hey ', 0), new PrintNode(new NameExpression('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' apples', 0), ], [], 0); $node = new TransNode($body, $plural, $count, null, null, null, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(ngettext("Hey %%name%%, I have one apple", "Hey %%name%%, I have' . ' %%count%% apples", abs(12)), array("%%name%%" => %s,' . ' "%%name%%" => %s, "%%count%%" => abs(12), ));', $this->getVariableGetter('name'), $this->getVariableGetter('name') ), ]; // with escaper extension set to on $body = new Node([ new TextNode('J\'ai ', 0), new PrintNode( new FilterExpression(new NameExpression('foo', 0), new ConstantExpression('escape', 0), new Node(), 0), 0 ), new TextNode(' pommes', 0), ], [], 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [ $node, sprintf( $this->echoOrYield() . ' strtr(gettext("J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo') ), ]; // with notes $body = new ConstantExpression('Hello', 0); $notes = new TextNode('Notes for translators', 0); $node = new TransNode($body, null, null, null, $notes, null, 0); $tests[] = [$node, "// notes: Notes for translators\n" . $this->echoOrYield() . ' gettext("Hello");']; $body = new ConstantExpression('Hello', 0); $notes = new TextNode("Notes for translators\nand line breaks", 0); $node = new TransNode($body, null, null, null, $notes, null, 0); $tests[] = [ $node, "// notes: Notes for translators and line breaks\n" . $this->echoOrYield() . ' gettext("Hello");', ]; $count = new ConstantExpression(5, 0); $body = new TextNode('There is 1 pending task', 0); $plural = new Node([ new TextNode('There are ', 0), new PrintNode(new NameExpression('count', 0), 0), new TextNode(' pending tasks', 0), ], [], 0); $notes = new TextNode('Notes for translators', 0); $node = new TransNode($body, $plural, $count, null, $notes, null, 0); $tests[] = [ $node, '// notes: Notes for translators' . "\n" . $this->echoOrYield() . ' strtr(ngettext("There is 1 pending task",' . ' "There are %count% pending tasks", abs(5)), array("%count%" => abs(5), ));', ]; return $tests; } }