pax_global_header00006660000000000000000000000064147374770340014532gustar00rootroot0000000000000052 comment=bbd5204acea9e62feb79080ea90d647bd93f1933 twig-i18n-extension-5.0.0/000077500000000000000000000000001473747703400152755ustar00rootroot00000000000000twig-i18n-extension-5.0.0/CHANGELOG.md000066400000000000000000000042511473747703400171100ustar00rootroot00000000000000# Change Log ## [5.0.0] - 2025-01-08 * Drop support for Twig < 3.17.0 * Drop support for PHP 7.2, PHP 7.3, PHP 7.4, PHP 8.0 and PHP 8.1 * Fix Twig deprecations (#16, #17, #18) ## [4.1.3] - 2024-09-08 * Add support for Twig 3.13 (#15) ## [4.1.2] - 2024-08-31 * Add support for Twig 3.12 (#14) ## [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. [Unreleased]: https://github.com/phpmyadmin/twig-i18n-extension/compare/4.1.x...HEAD [4.1.3]: https://github.com/phpmyadmin/twig-i18n-extension/compare/4.1.2...4.1.3 [4.1.2]: https://github.com/phpmyadmin/twig-i18n-extension/compare/4.1.1...4.1.2 [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-5.0.0/LICENSE000066400000000000000000000021311473747703400162770ustar00rootroot00000000000000Copyright (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-5.0.0/README.rst000066400000000000000000000153171473747703400167730ustar00rootroot00000000000000Twig 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-5.0.0/composer.json000066400000000000000000000033061473747703400200210ustar00rootroot00000000000000{ "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": "^8.2", "twig/twig": "^3.17" }, "require-dev": { "phpmyadmin/coding-standard": "^4.0", "phpmyadmin/motranslator": "^6.0-dev", "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "^1.10", "phpstan/phpstan-phpunit": "^1.3", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "^9.6" }, "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 } }, "extra": { "branch-alias": { "dev-master": "5.0-dev" } } } twig-i18n-extension-5.0.0/phpstan-baseline.neon000066400000000000000000000100071473747703400214110ustar00rootroot00000000000000parameters: ignoreErrors: - message: "#^Only booleans are allowed in an if condition, array\\ given\\.$#" count: 1 path: src/Node/TransNode.php - message: "#^Only booleans are allowed in an if condition, int\\<0, max\\> given\\.$#" count: 1 path: src/Node/TransNode.php - message: "#^Parameter \\#1 \\$name of class Twig\\\\Node\\\\Expression\\\\Variable\\\\ContextVariable constructor expects string, mixed given\\.$#" count: 1 path: src/Node/TransNode.php - message: "#^Parameter \\#1 \\$string of function trim expects string, mixed given\\.$#" count: 2 path: src/Node/TransNode.php - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" count: 1 path: src/Node/TransNode.php - message: "#^Method PhpMyAdmin\\\\Twig\\\\Extensions\\\\TokenParser\\\\TransTokenParser\\:\\:preParse\\(\\) should return array\\{Twig\\\\Node\\\\Node, Twig\\\\Node\\\\Node\\|null, Twig\\\\Node\\\\Expression\\\\AbstractExpression\\|null, Twig\\\\Node\\\\Node\\|null, Twig\\\\Node\\\\Node\\|null, Twig\\\\Node\\\\Node\\|null, int, string\\} but returns array\\{mixed, Twig\\\\Node\\\\Node\\|null, mixed, Twig\\\\Node\\\\Node\\|null, Twig\\\\Node\\\\Node\\|null, mixed, int, string\\}\\.$#" count: 1 path: src/TokenParser/TransTokenParser.php - message: "#^Parameter \\#1 \\$body of method PhpMyAdmin\\\\Twig\\\\Extensions\\\\TokenParser\\\\TransTokenParser\\:\\:checkTransString\\(\\) expects Twig\\\\Node\\\\Node, mixed given\\.$#" count: 1 path: src/TokenParser/TransTokenParser.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotEmpty\\(\\)\\.$#" count: 1 path: test/I18nExtensionMoTranslatorDebugTest.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotEmpty\\(\\)\\.$#" count: 1 path: test/I18nExtensionMoTranslatorSandboxTest.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotEmpty\\(\\)\\.$#" count: 1 path: test/I18nExtensionSandboxTest.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotEmpty\\(\\)\\.$#" count: 1 path: test/I18nExtensionTest.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertEquals\\(\\)\\.$#" count: 6 path: test/Node/MoTranslatorTransTest.php - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" count: 8 path: test/Node/MoTranslatorTransTest.php - message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" count: 3 path: test/Node/MoTranslatorTransTest.php - message: "#^Cannot call method compile\\(\\) on mixed\\.$#" count: 2 path: test/Node/TransTest.php - message: "#^Cannot call method getDebugInfo\\(\\) on mixed\\.$#" count: 4 path: test/Node/TransTest.php - message: "#^Cannot call method getSource\\(\\) on mixed\\.$#" count: 2 path: test/Node/TransTest.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertEmpty\\(\\)\\.$#" count: 2 path: test/Node/TransTest.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertEquals\\(\\)\\.$#" count: 7 path: test/Node/TransTest.php - message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\)\\.$#" count: 4 path: test/Node/TransTest.php - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" count: 5 path: test/Node/TransTest.php - message: "#^Parameter \\#3 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" count: 1 path: test/Node/TransTest.php twig-i18n-extension-5.0.0/phpunit.xml.dist000066400000000000000000000015631473747703400204550ustar00rootroot00000000000000 test src twig-i18n-extension-5.0.0/src/000077500000000000000000000000001473747703400160645ustar00rootroot00000000000000twig-i18n-extension-5.0.0/src/I18nExtension.php000066400000000000000000000030401473747703400212060ustar00rootroot00000000000000 $attributes An array of attributes (should not be nodes) * @param int $lineno The line number */ public function __construct(Node|null $node, array $attributes, int $lineno) { parent::__construct($node === null ? [] : [$node], $attributes, $lineno); } } twig-i18n-extension-5.0.0/src/Node/TransNode.php000066400000000000000000000223421473747703400213620ustar00rootroot00000000000000 */ #[YieldReady] class TransNode extends Node { /** * The label for gettext notes to be exported */ public static string $notesLabel = '// notes: '; /** * Enable MoTranslator functions */ public static bool $enableMoTranslator = false; /** * Enable calls to addDebugInfo */ public static bool $enableAddDebugInfo = false; /** * Enables context functions usage */ public static bool $hasContextFunctions = false; /** @phpstan-ignore constructor.unusedParameter */ public function __construct( Node $body, Node|null $plural, AbstractExpression|null $count, Node|null $context = null, Node|null $notes = null, Node|null $domain = null, int $lineno = 0, string|null $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); } /** * {@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 because we want a single line comment $message = str_replace(["\n", "\r"], ' ', $message); $compiler->raw(static::$notesLabel . $message . "\n"); } if ($vars) { $compiler->raw('yield 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('yield ' . $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 * * @psalm-return array{Node, list} */ protected function compileString(Node $body): array { if ($body instanceof ContextVariable || $body instanceof ConstantExpression || $body instanceof LocalVariable) { 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 ContextVariable($attributeName, $n->getTemplateLine()); } else { /** @phpstan-var TextNode $node */ $msg .= $node->getAttribute('data'); } } } else { $msg = $body->getAttribute('data'); } return [new I18nNode(new ConstantExpression(trim($msg), $body->getTemplateLine()), [], 0), $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'); } } twig-i18n-extension-5.0.0/src/TokenParser/000077500000000000000000000000001473747703400203215ustar00rootroot00000000000000twig-i18n-extension-5.0.0/src/TokenParser/TransTokenParser.php000066400000000000000000000113771473747703400243100ustar00rootroot00000000000000preParse($token); return new TransNode($body, $plural, $count, $context, $notes, $domain, $lineno, $tag); } /** @psalm-return array{Node, Node|null, AbstractExpression|null, Node|null, Node|null, Node|null, int, string} */ 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 when optimizing. $notes = new I18nNode(null, ['data' => $notes->getAttribute('data')], $notes->getTemplateLine()); } if ($context instanceof TextNode) { // Don't use TextNode for $context to avoid it getting merged with $body when optimizing. $context = new I18nNode(null, ['data' => $context->getAttribute('data')], $context->getTemplateLine()); } return [$body, $plural, $count, $context, $notes, $domain, $lineno, $this->getTag()]; } public function decideForFork(Token $token): bool { return $token->test(['plural', 'context', 'notes', 'endtrans']); } public function decideForEnd(Token $token): bool { return $token->test('endtrans'); } /** * {@inheritdoc} */ public function getTag() { return 'trans'; } /** @throws SyntaxError */ protected function checkTransString(Node $body, int $lineno): void { foreach ($body as $i => $node) { if ( $node instanceof TextNode || ($node instanceof PrintNode && $node->getNode('expr') instanceof ContextVariable) ) { continue; } throw new SyntaxError( 'The text to be translated with "trans" can only contain references to simple variables.', $lineno, ); } } } twig-i18n-extension-5.0.0/test/000077500000000000000000000000001473747703400162545ustar00rootroot00000000000000twig-i18n-extension-5.0.0/test/Fixtures/000077500000000000000000000000001473747703400200655ustar00rootroot00000000000000twig-i18n-extension-5.0.0/test/Fixtures/DomainContext.test000066400000000000000000000003411473747703400235400ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/DomainPluralContext.test000066400000000000000000000006401473747703400247220ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/ObjectTrans.test000066400000000000000000000011421473747703400232020ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/PluralContext.test000066400000000000000000000006071473747703400235750ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/PluralExample.test000066400000000000000000000005221473747703400235400ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/PluralExampleDomain.test000066400000000000000000000005331473747703400246720ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/PluralOneExample.test000066400000000000000000000005231473747703400242030ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/PluralOneExampleDomain.test000066400000000000000000000005541473747703400253370ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/PluralWithNotes.test000066400000000000000000000006151473747703400240740ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/PluralWithSprintf.test000066400000000000000000000004261473747703400244310ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/SimpleContext.test000066400000000000000000000003101473747703400235560ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/SimpleDomain.test000066400000000000000000000002771473747703400233550ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/SimpleExample.test000066400000000000000000000002231473747703400235300ustar00rootroot00000000000000--TEST-- Test a simple trans tag --TEMPLATE-- {% trans %}Castle Quote{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Castle Quotetwig-i18n-extension-5.0.0/test/Fixtures/SimpleXssExample.test000066400000000000000000000003051473747703400242270ustar00rootroot00000000000000--TEST-- Test a simple trans tag against XSS injection --TEMPLATE-- {% trans %}{% endtrans %} --DATA-- return [] --CONFIG-- return [] --EXPECT-- twig-i18n-extension-5.0.0/test/Fixtures/SpecificTrans.test000066400000000000000000000003001473747703400235140ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/TransFilter.test000066400000000000000000000003301473747703400232170ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/TransFilterDomain.test000066400000000000000000000002541473747703400243540ustar00rootroot00000000000000--TEST-- Test a simple trans filter --TEMPLATE-- {# Filter with domain #} {{ 'Castle Quote'|trans('core') }} --DATA-- return [] --CONFIG-- return [] --EXPECT-- Castle Quotetwig-i18n-extension-5.0.0/test/Fixtures/TransFilterXss.test000066400000000000000000000003111473747703400237140ustar00rootroot00000000000000--TEST-- Test a simple trans filter against XSS injection --TEMPLATE-- {{ ''|trans }} --DATA-- return [] --CONFIG-- return [] --EXPECT-- <script>alert(1);</script>twig-i18n-extension-5.0.0/test/Fixtures/TransFunctionOutput.test000066400000000000000000000002401473747703400250000ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/TransTagSetVariable.test000066400000000000000000000003011473747703400246250ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/TransTagWithNotes.test000066400000000000000000000003751473747703400243630ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/TransTooComplex.test000066400000000000000000000004521473747703400240700ustar00rootroot00000000000000--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-5.0.0/test/Fixtures/TransVariableAndData.test000066400000000000000000000002151473747703400247360ustar00rootroot00000000000000--TEST-- Test with a function and data --TEMPLATE-- {{ max([1,2,3,b])|trans }} --DATA-- return ['b' => 18] --CONFIG-- return [] --EXPECT-- 18twig-i18n-extension-5.0.0/test/Fixtures/VariableDomain.test000066400000000000000000000003231473747703400236410ustar00rootroot00000000000000--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-5.0.0/test/FixturesWithSandbox/000077500000000000000000000000001473747703400222405ustar00rootroot00000000000000twig-i18n-extension-5.0.0/test/FixturesWithSandbox/Plural.test000066400000000000000000000011421473747703400243760ustar00rootroot00000000000000--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-5.0.0/test/I18nExtensionMoTranslatorDebugTest.php000066400000000000000000000020071473747703400255550ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-5.0.0/test/I18nExtensionMoTranslatorSandboxTest.php000066400000000000000000000024651473747703400261350ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-5.0.0/test/I18nExtensionMoTranslatorTest.php000066400000000000000000000015541473747703400246140ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-5.0.0/test/I18nExtensionTest.php000066400000000000000000000014451473747703400222450ustar00rootroot00000000000000assertNotEmpty((new I18nExtension())->getName()); } } twig-i18n-extension-5.0.0/test/MoTranslator/000077500000000000000000000000001473747703400207015ustar00rootroot00000000000000twig-i18n-extension-5.0.0/test/MoTranslator/I18nExtensionDebug.php000066400000000000000000000014341473747703400247770ustar00rootroot00000000000000assertEquals($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 mixed[] */ public function getTests(): array { $tests = []; $body = new ContextVariable('foo', 0); $domain = new Nodes([ new TextNode('coredomain', 0), ]); $node = new TransNode($body, null, null, null, null, $domain, 0); $tests[] = [ $node, sprintf('yield _dgettext("coredomain", %s);', $this->getVariableGetter('foo')), ]; $body = new ContextVariable('foo', 0); $domain = new Nodes([ new TextNode('coredomain', 0), ]); $context = new Nodes([ new TextNode('The context', 0), ]); $node = new TransNode($body, null, null, $context, null, $domain, 0); $tests[] = [ $node, sprintf( 'yield _dpgettext("coredomain", "The context", %s);', $this->getVariableGetter('foo'), ), ]; $body = new Nodes([ new TextNode('J\'ai ', 0), new PrintNode(new ContextVariable('foo', 0), 0), new TextNode(' pommes', 0), ]); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [ $node, sprintf( 'yield strtr(_gettext("J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo'), ), ]; $count = new ConstantExpression(12, 0); $body = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have one apple', 0), ]); $plural = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' apples', 0), ]); $node = new TransNode($body, $plural, $count, null, null, null, 0); $tests[] = [ $node, sprintf( 'yield 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 Nodes([ new TextNode('J\'ai ', 0), new PrintNode(new ContextVariable('foo', 0), 0), new TextNode(' pommes', 0), ]); $context = new Nodes([ new TextNode('The context', 0), ]); $node = new TransNode($body, null, null, $context, null, null, 0); $tests[] = [ $node, sprintf( 'yield strtr(_pgettext("The context", "J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo'), ), ]; $count = new ConstantExpression(12, 0); $body = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have one apple', 0), ]); $context = new Nodes([ new TextNode('The context', 0), ]); $plural = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' apples', 0), ]); $node = new TransNode($body, $plural, $count, $context, null, null, 0); $tests[] = [ $node, sprintf( 'yield 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 Nodes([ new TextNode('J\'ai ', 0), new PrintNode(new ContextVariable('foo', 0), 0), new TextNode(' pommes', 0), ]); $context = new Nodes([ new TextNode('The context', 0), ]); $domain = new Nodes([ new TextNode('mydomain', 0), ]); $node = new TransNode($body, null, null, $context, null, $domain, 0); $tests[] = [ $node, sprintf( 'yield strtr(_dpgettext("mydomain", "The context", "J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo'), ), ]; $count = new ConstantExpression(12, 0); $body = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have one apple', 0), ]); $context = new Nodes([ new TextNode('The context', 0), ]); $domain = new Nodes([ new TextNode('mydomain', 0), ]); $plural = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' apples', 0), ]); $node = new TransNode($body, $plural, $count, $context, null, $domain, 0); $tests[] = [ $node, sprintf( 'yield 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-5.0.0/test/Node/TransTest.php000066400000000000000000000221661473747703400216100ustar00rootroot00000000000000assertEquals($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 Nodes([ new TextNode('Hello', 0), ]); $domain = new Nodes([ new TextNode('coredomain', 0), ]); $plural = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' apples', 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 Nodes([ new TextNode('There are ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' pending tasks', 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" . 'yield 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 Nodes([ new TextNode('There are ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' pending tasks', 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" . 'yield 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 mixed[] */ public function getTests(): array { $tests = []; $body = new ContextVariable('foo', 0); $domain = new Nodes([ new TextNode('coredomain', 0), ]); $node = new TransNode($body, null, null, null, null, $domain, 0); $tests[] = [ $node, sprintf('yield dgettext("coredomain", %s);', $this->getVariableGetter('foo')), ]; $body = new ContextVariable('foo', 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [$node, sprintf('yield gettext(%s);', $this->getVariableGetter('foo'))]; $body = new ConstantExpression('Hello', 0); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [$node, 'yield gettext("Hello");']; $body = new Nodes([ new TextNode('Hello', 0), ]); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [$node, 'yield gettext("Hello");']; $body = new Nodes([ new TextNode('J\'ai ', 0), new PrintNode(new ContextVariable('foo', 0), 0), new TextNode(' pommes', 0), ]); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [ $node, sprintf( 'yield strtr(gettext("J\'ai %%foo%% pommes"), array("%%foo%%" => %s, ));', $this->getVariableGetter('foo'), ), ]; $count = new ConstantExpression(12, 0); $body = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have one apple', 0), ]); $plural = new Nodes([ new TextNode('Hey ', 0), new PrintNode(new ContextVariable('name', 0), 0), new TextNode(', I have ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' apples', 0), ]); $node = new TransNode($body, $plural, $count, null, null, null, 0); $tests[] = [ $node, sprintf( 'yield 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 $contextFoo = new ContextVariable('foo', 0); $body = new Nodes([ new TextNode('J\'ai ', 0), new PrintNode( new FilterExpression($contextFoo, new ConstantExpression('escape', 0), new Nodes(), 0), 0, ), new TextNode(' pommes', 0), ]); $node = new TransNode($body, null, null, null, null, null, 0); $tests[] = [ $node, sprintf( 'yield 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" . 'yield 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" . 'yield gettext("Hello");', ]; $count = new ConstantExpression(5, 0); $body = new TextNode('There is 1 pending task', 0); $plural = new Nodes([ new TextNode('There are ', 0), new PrintNode(new ContextVariable('count', 0), 0), new TextNode(' pending tasks', 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" . 'yield strtr(ngettext("There is 1 pending task",' . ' "There are %count% pending tasks", abs(5)), array("%count%" => abs(5), ));', ]; return $tests; } }