diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..c62e0b22 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +/CHANGELOG.md export-ignore +/CONTRIBUTING.md export-ignore +/README.md export-ignore +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..1ab42fed --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,61 @@ +# yamllint disable rule:line-length +# yamllint disable rule:braces + +name: CI + +on: + pull_request: + push: + branches: + - "master" + +jobs: + phpunit: + name: "PHPUnit" + runs-on: "ubuntu-20.04" + + strategy: + fail-fast: false + matrix: + symfony-version: + - '^4.4' + - '^5.4' + - '^6.4' + php-version: + - "7.4" + - "8.0" + - "8.1" + dependencies: + - "lowest" + - "highest" + exclude: + - symfony-version: '^6.4' + php-version: '7.4' + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + with: + fetch-depth: 2 + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + coverage: "pcov" + ini-values: "zend.assertions=1" + + - name: Configure symfony version + uses: php-actions/composer@v6 + with: + command: config + args: extra.symfony.require ${{ matrix.symfony-version }} + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@v1" + with: + dependency-versions: "${{ matrix.dependencies }}" + composer-options: "${{ matrix.composer-options }}" + + - name: Run tests + run: | + SYMFONY_DEPRECATIONS_HELPER=weak vendor/bin/simple-phpunit ${PHPUNIT_FLAGS} diff --git a/.github/workflows/coding-standards.yaml b/.github/workflows/coding-standards.yaml new file mode 100644 index 00000000..40c98e65 --- /dev/null +++ b/.github/workflows/coding-standards.yaml @@ -0,0 +1,35 @@ +name: "Coding Standards" + +on: + pull_request: + push: + branches: + - "master" + +jobs: + coding-standards: + name: "Coding Standards" + runs-on: "ubuntu-20.04" + + strategy: + matrix: + php-version: + - "7.4" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + tools: "cs2pr" + extensions: pdo_sqlite + + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@v1" + + - name: "Run PHP_CodeSniffer" + run: "vendor/bin/phpcs" diff --git a/.gitignore b/.gitignore index 5de5676c..3ca416f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,42 @@ phpunit.xml composer.lock vendor/ +.phpunit.result.cache +/.idea +.phpcs-cache +phpcs.xml + + +/bin/ +/config/ +/public/ +/src/ +/templates +/tests +/translations +/var/ +/.env* +/.phpunit.result.cache + +.idea/ +/symfony.lock + +###> symfony/framework-bundle ### +/.env.local +/.env.local.php +/.env.*.local +/config/secrets/prod/prod.decrypt.private.php +/public/bundles/ +/var/ +/vendor/ +###< symfony/framework-bundle ### + +###> squizlabs/php_codesniffer ### +/.phpcs-cache +/phpcs.xml +###< squizlabs/php_codesniffer ### + +###> symfony/phpunit-bridge ### +.phpunit.result.cache +/phpunit.xml +###< symfony/phpunit-bridge ### diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 838c1680..00000000 --- a/.travis.yml +++ /dev/null @@ -1,62 +0,0 @@ -language: php -sudo: false -dist: trusty - -cache: - directories: - - $HOME/.composer/cache - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - hhvm - -env: - global: - - TEST_COMMAND="./vendor/bin/phpunit" - matrix: - - SYMFONY_VERSION=2.8.* - -matrix: - fast_finish: true - allow_failures: - - php: hhvm - - include: - - php: 5.6 - env: SYMFONY_VERSION=2.7.* - - php: 7.1 - env: SYMFONY_VERSION=2.7.* - - php: 7.2 - env: SYMFONY_VERSION=2.7.* - - php: 7.1 - env: SYMFONY_VERSION=2.8.* - - php: 7.2 - env: SYMFONY_VERSION=2.8.* - - php: 5.6 - env: SYMFONY_VERSION=^3.0 - - php: 7.1 - env: SYMFONY_VERSION=^3.0 - - php: 7.2 - env: SYMFONY_VERSION=^3.0 - - php: 7.1 - env: SYMFONY_VERSION=^4.0 - - php: 7.2 - env: SYMFONY_VERSION=^4.0 - -before_install: - - travis_retry composer self-update - -install: - - composer require symfony/symfony:${SYMFONY_VERSION} --no-update - - COMPOSER_MEMORY_LIMIT=-1 composer update ${COMPOSER_FLAGS} --prefer-source --no-interaction - -script: - - $TEST_COMMAND - -after_success: - - if [[ "$COVERAGE" = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi - - if [[ "$COVERAGE" = true ]]; then php ocular.phar code-coverage:upload --format=php-clover build/coverage.xml; fi diff --git a/Annotation/Desc.php b/Annotation/Desc.php index a352fb89..35da5695 100644 --- a/Annotation/Desc.php +++ b/Annotation/Desc.php @@ -1,5 +1,7 @@ * diff --git a/Annotation/Ignore.php b/Annotation/Ignore.php index 3e933310..f0dfb49a 100644 --- a/Annotation/Ignore.php +++ b/Annotation/Ignore.php @@ -1,5 +1,7 @@ * diff --git a/Annotation/Meaning.php b/Annotation/Meaning.php index 79ffd775..05dd0931 100644 --- a/Annotation/Meaning.php +++ b/Annotation/Meaning.php @@ -1,5 +1,7 @@ * @@ -30,9 +32,6 @@ final class Meaning /** @var string @Required */ public $text; - /** - * Meaning constructor. - */ public function __construct() { if (0 === func_num_args()) { diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d2e4ebed --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,510 @@ +# Changelog + +## [1.6.0](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.6.0) (2021-01-16) + +- Php8 [\#556](https://github.com/schmittjoh/JMSTranslationBundle/pull/556) ([VincentLanglet](https://github.com/VincentLanglet)) +- Use composer v1 \(fix ci pipeline\) [\#555](https://github.com/schmittjoh/JMSTranslationBundle/pull/555) ([goetas](https://github.com/goetas)) +- Prevent all non message Form Constraint option from being extracted [\#554](https://github.com/schmittjoh/JMSTranslationBundle/pull/554) ([nfragnet](https://github.com/nfragnet)) +- Add the option to dump files to the ICU message format [\#551](https://github.com/schmittjoh/JMSTranslationBundle/pull/551) ([mark-gerarts](https://github.com/mark-gerarts)) +- Update FormExtractor.php [\#549](https://github.com/schmittjoh/JMSTranslationBundle/pull/549) ([TheRatG](https://github.com/TheRatG)) +- Added support to extract translations from form constraints [\#546](https://github.com/schmittjoh/JMSTranslationBundle/pull/546) ([balazscsaba2006](https://github.com/balazscsaba2006)) +- strtolower\(\) expects parameter 1 to be string, object given Issue \#544 [\#545](https://github.com/schmittjoh/JMSTranslationBundle/pull/545) ([TheRatG](https://github.com/TheRatG)) + +## [1.5.4](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.5.4) (2020-04-21) + +**Fixed bugs:** + +- test translation state is preserved for xliff [\#540](https://github.com/schmittjoh/JMSTranslationBundle/pull/540) ([goetas](https://github.com/goetas)) + +## [1.5.3](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.5.3) (2020-04-19) + +**Closed issues:** + +- ExtractTranslationCommand unlink\(\) expects parameter 1 to be a valid path, object given [\#537](https://github.com/schmittjoh/JMSTranslationBundle/issues/537) +- 1.5 =\> simplexml\_load\_file\(\) expects parameter 1 to be a valid path, object given [\#535](https://github.com/schmittjoh/JMSTranslationBundle/issues/535) + +**Merged pull requests:** + +- Update Updater.php [\#538](https://github.com/schmittjoh/JMSTranslationBundle/pull/538) ([TheRatG](https://github.com/TheRatG)) +- Remove duplicated Changelog [\#534](https://github.com/schmittjoh/JMSTranslationBundle/pull/534) ([franmomu](https://github.com/franmomu)) + +## [1.5.2](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.5.2) (2020-03-19) + +**Merged pull requests:** + +- Fix casting int to string [\#536](https://github.com/schmittjoh/JMSTranslationBundle/pull/536) ([franmomu](https://github.com/franmomu)) + +## [1.5.1](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.5.1) (2020-03-17) + +**Merged pull requests:** + +- Fixed object instead of string error introduced by strict\_types [\#533](https://github.com/schmittjoh/JMSTranslationBundle/pull/533) ([ViniTou](https://github.com/ViniTou)) + +## [1.5.0](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.5.0) (2020-03-15) + +**Closed issues:** + +- New major release [\#527](https://github.com/schmittjoh/JMSTranslationBundle/issues/527) +- Requirement of nikic/php-parser does not match packgist page [\#514](https://github.com/schmittjoh/JMSTranslationBundle/issues/514) +- The format "yaml~" does not exist. [\#512](https://github.com/schmittjoh/JMSTranslationBundle/issues/512) +- ClassLoader deprecation notice [\#511](https://github.com/schmittjoh/JMSTranslationBundle/issues/511) +- Symfony 4 : Unable to find template "JMSTranslationBundle::base.html.twig" [\#503](https://github.com/schmittjoh/JMSTranslationBundle/issues/503) +- Symfony 2.7 should be required in composer.json [\#263](https://github.com/schmittjoh/JMSTranslationBundle/issues/263) + +**Merged pull requests:** + +- Apply cs [\#532](https://github.com/schmittjoh/JMSTranslationBundle/pull/532) ([franmomu](https://github.com/franmomu)) +- Use array access to twig argument nodes [\#531](https://github.com/schmittjoh/JMSTranslationBundle/pull/531) ([franmomu](https://github.com/franmomu)) +- Allow Symfony 5 and Twig 3 [\#530](https://github.com/schmittjoh/JMSTranslationBundle/pull/530) ([franmomu](https://github.com/franmomu)) +- Remove extending from ContainerAwareCommand [\#529](https://github.com/schmittjoh/JMSTranslationBundle/pull/529) ([franmomu](https://github.com/franmomu)) +- Allow Translation Contracts [\#528](https://github.com/schmittjoh/JMSTranslationBundle/pull/528) ([franmomu](https://github.com/franmomu)) +- Fix calls passing the right scalar type [\#526](https://github.com/schmittjoh/JMSTranslationBundle/pull/526) ([franmomu](https://github.com/franmomu)) +- Add Doctrine Coding Standards in tests [\#525](https://github.com/schmittjoh/JMSTranslationBundle/pull/525) ([franmomu](https://github.com/franmomu)) +- Test minimum requirements [\#524](https://github.com/schmittjoh/JMSTranslationBundle/pull/524) ([franmomu](https://github.com/franmomu)) +- Cleanup tests [\#523](https://github.com/schmittjoh/JMSTranslationBundle/pull/523) ([franmomu](https://github.com/franmomu)) +- Replace kernel.root\_dir with kernel.project\_dir [\#522](https://github.com/schmittjoh/JMSTranslationBundle/pull/522) ([franmomu](https://github.com/franmomu)) +- Remove duplicated packages in composer [\#521](https://github.com/schmittjoh/JMSTranslationBundle/pull/521) ([franmomu](https://github.com/franmomu)) +- Drop symfony \< 3.4 and \< 4.3 for 4.x [\#520](https://github.com/schmittjoh/JMSTranslationBundle/pull/520) ([franmomu](https://github.com/franmomu)) +- Address namespace change in Twig [\#519](https://github.com/schmittjoh/JMSTranslationBundle/pull/519) ([greg0ire](https://github.com/greg0ire)) +- Compile nodes before comparing them [\#518](https://github.com/schmittjoh/JMSTranslationBundle/pull/518) ([greg0ire](https://github.com/greg0ire)) +- Remove deprecated usage of TreeBuilder on Symfony 4 [\#513](https://github.com/schmittjoh/JMSTranslationBundle/pull/513) ([emodric](https://github.com/emodric)) +- Update for Symfony 4.1 [\#504](https://github.com/schmittjoh/JMSTranslationBundle/pull/504) ([tilimac](https://github.com/tilimac)) + +## [1.4.4](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.4.4) (2019-05-14) + +**Closed issues:** + +- Invalid type annotation for the return value of getTranslationMessages\(\) [\#508](https://github.com/schmittjoh/JMSTranslationBundle/issues/508) +- php 7.2 method return type throw error [\#494](https://github.com/schmittjoh/JMSTranslationBundle/issues/494) +- The annotation "@created" , "@optional" ... was never imported. [\#488](https://github.com/schmittjoh/JMSTranslationBundle/issues/488) +- Unable to find template "@JMSTranslation/translate/index.html.twig" [\#474](https://github.com/schmittjoh/JMSTranslationBundle/issues/474) +- Symfony 4 support? [\#471](https://github.com/schmittjoh/JMSTranslationBundle/issues/471) +- Translatable options for ArrayChoiceList not being translated \(moving from ChoiceList\) [\#469](https://github.com/schmittjoh/JMSTranslationBundle/issues/469) +- symfony 3.4 problem [\#466](https://github.com/schmittjoh/JMSTranslationBundle/issues/466) +- Deleting translation entries : how to prevent certain messages being culled when running translation:extract ? [\#448](https://github.com/schmittjoh/JMSTranslationBundle/issues/448) + +**Merged pull requests:** + +- Test if filter exists before use [\#510](https://github.com/schmittjoh/JMSTranslationBundle/pull/510) ([gnat42](https://github.com/gnat42)) +- Import Message, fixes \#508 [\#509](https://github.com/schmittjoh/JMSTranslationBundle/pull/509) ([arnaud-lb](https://github.com/arnaud-lb)) + +## [1.4.3](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.4.3) (2018-06-28) + +**Merged pull requests:** + +- SF4 Compatibility [\#493](https://github.com/schmittjoh/JMSTranslationBundle/pull/493) ([gnat42](https://github.com/gnat42)) + +## [1.4.2](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.4.2) (2018-06-27) + +**Closed issues:** + +- PHP-Parser v4.0 compatibility [\#476](https://github.com/schmittjoh/JMSTranslationBundle/issues/476) +- Possibility to remove the jms:reference-file from the dumped .xlf [\#409](https://github.com/schmittjoh/JMSTranslationBundle/issues/409) + +**Merged pull requests:** + +- fix memory limit issues [\#491](https://github.com/schmittjoh/JMSTranslationBundle/pull/491) ([gnat42](https://github.com/gnat42)) +- Fix nikic/php-parser v4 compatibility [\#490](https://github.com/schmittjoh/JMSTranslationBundle/pull/490) ([soullivaneuh](https://github.com/soullivaneuh)) +- Allow nikic/php-parser v4 [\#477](https://github.com/schmittjoh/JMSTranslationBundle/pull/477) ([soullivaneuh](https://github.com/soullivaneuh)) + +## [1.4.1](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.4.1) (2018-02-09) + +**Closed issues:** + +- The last release is not on packagist [\#480](https://github.com/schmittjoh/JMSTranslationBundle/issues/480) +- The format "yml" does not exist [\#468](https://github.com/schmittjoh/JMSTranslationBundle/issues/468) + +## [1.4.0](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.4.0) (2018-02-07) + +**Closed issues:** + +- Get rid of JMSDI [\#457](https://github.com/schmittjoh/JMSTranslationBundle/issues/457) +- JMS translation doesn't extract dynamic form build? [\#444](https://github.com/schmittjoh/JMSTranslationBundle/issues/444) +- Improve management of xlf files [\#372](https://github.com/schmittjoh/JMSTranslationBundle/issues/372) +- Remove dependency on DiExtraBundle [\#355](https://github.com/schmittjoh/JMSTranslationBundle/issues/355) + +**Merged pull requests:** + +- Manage legacy aliases if exist [\#479](https://github.com/schmittjoh/JMSTranslationBundle/pull/479) ([soullivaneuh](https://github.com/soullivaneuh)) +- Add missing Symfony validator dependency [\#470](https://github.com/schmittjoh/JMSTranslationBundle/pull/470) ([bocharsky-bw](https://github.com/bocharsky-bw)) +- Remove dependency on JMSDiExtraBundle [\#464](https://github.com/schmittjoh/JMSTranslationBundle/pull/464) ([tommygnr](https://github.com/tommygnr)) +- Avoid method call on null in FormExtractor [\#463](https://github.com/schmittjoh/JMSTranslationBundle/pull/463) ([dontub](https://github.com/dontub)) +- Allow user to filter messages in the web-ui [\#460](https://github.com/schmittjoh/JMSTranslationBundle/pull/460) ([azine](https://github.com/azine)) +- use precise for php5.3 [\#456](https://github.com/schmittjoh/JMSTranslationBundle/pull/456) ([gnat42](https://github.com/gnat42)) +- Fixed return typehint. [\#455](https://github.com/schmittjoh/JMSTranslationBundle/pull/455) ([racinmat](https://github.com/racinmat)) +- Update version of the bundle [\#454](https://github.com/schmittjoh/JMSTranslationBundle/pull/454) ([FabienSalles](https://github.com/FabienSalles)) +- Fixes translation\_domain detection with setDefault [\#453](https://github.com/schmittjoh/JMSTranslationBundle/pull/453) ([simitter](https://github.com/simitter)) +- Fixed twig node visitor signature [\#449](https://github.com/schmittjoh/JMSTranslationBundle/pull/449) ([deguif](https://github.com/deguif)) +- Fix PHP Notice: "Array to string conversion" [\#445](https://github.com/schmittjoh/JMSTranslationBundle/pull/445) ([snpy](https://github.com/snpy)) +- Use \Twig\_BaseNodeVisitor instead of duplicated visitors code [\#441](https://github.com/schmittjoh/JMSTranslationBundle/pull/441) ([emodric](https://github.com/emodric)) + +## [1.3.2](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.3.2) (2017-04-20) + +**Closed issues:** + +- PHPUnit Errors on a clean forked clone of master HEAD [\#436](https://github.com/schmittjoh/JMSTranslationBundle/issues/436) +- Incompatibility between bundle 1.3.1 and Symfony 3.2.3 [\#429](https://github.com/schmittjoh/JMSTranslationBundle/issues/429) +- PHP error with Twig 2.0 [\#425](https://github.com/schmittjoh/JMSTranslationBundle/issues/425) +- Call to a member function getNames\(\) on null | TranslateController.php:64 [\#417](https://github.com/schmittjoh/JMSTranslationBundle/issues/417) +- abstract keys are lost when desc exists [\#414](https://github.com/schmittjoh/JMSTranslationBundle/issues/414) +- LogicException Node "domain" does not exist for Node "Symfony\Bridge\Twig\Node\TransNode". [\#402](https://github.com/schmittjoh/JMSTranslationBundle/issues/402) +- Translation of variable placeholders [\#401](https://github.com/schmittjoh/JMSTranslationBundle/issues/401) +- ReferenceError: event is not defined with firebug. [\#397](https://github.com/schmittjoh/JMSTranslationBundle/issues/397) +- The Twig\_Node\_Expression\_ExtensionReference class is deprecated [\#387](https://github.com/schmittjoh/JMSTranslationBundle/issues/387) + +**Merged pull requests:** + +- support nikic/php-parser 3.0 / php7.1 [\#440](https://github.com/schmittjoh/JMSTranslationBundle/pull/440) ([gnat42](https://github.com/gnat42)) +- Twig 2 compatibility [\#439](https://github.com/schmittjoh/JMSTranslationBundle/pull/439) ([emodric](https://github.com/emodric)) +- Add configuration options to disable date/sources in xliff dump [\#421](https://github.com/schmittjoh/JMSTranslationBundle/pull/421) ([artursvonda](https://github.com/artursvonda)) +- Fix wrong error message [\#413](https://github.com/schmittjoh/JMSTranslationBundle/pull/413) ([mahmouds](https://github.com/mahmouds)) +- Fix compatibility with recent Symfony versions [\#405](https://github.com/schmittjoh/JMSTranslationBundle/pull/405) ([stof](https://github.com/stof)) +- Fixed wrong offset [\#400](https://github.com/schmittjoh/JMSTranslationBundle/pull/400) ([deguif](https://github.com/deguif)) + +## [1.3.1](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.3.1) (2016-08-13) + +**Closed issues:** + +- New translations [\#392](https://github.com/schmittjoh/JMSTranslationBundle/issues/392) + +**Merged pull requests:** + +- Prepare for 1.3.1 [\#396](https://github.com/schmittjoh/JMSTranslationBundle/pull/396) ([Nyholm](https://github.com/Nyholm)) +- Fix XLIFF messages status not being set appropiately [\#393](https://github.com/schmittjoh/JMSTranslationBundle/pull/393) ([snamor](https://github.com/snamor)) +- Fixed relative path calculation [\#391](https://github.com/schmittjoh/JMSTranslationBundle/pull/391) ([ABM-Dan](https://github.com/ABM-Dan)) + +## [1.3.0](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.3.0) (2016-08-07) + +**Fixed bugs:** + +- Full paths instead of relative paths since upgrade to 1.2.3 [\#366](https://github.com/schmittjoh/JMSTranslationBundle/issues/366) + +**Closed issues:** + +- The format "php~" does not exist. [\#375](https://github.com/schmittjoh/JMSTranslationBundle/issues/375) +- Custom form with constraint [\#373](https://github.com/schmittjoh/JMSTranslationBundle/issues/373) +- Add contribution guide [\#361](https://github.com/schmittjoh/JMSTranslationBundle/issues/361) +- Extract error with optipng & jpegoptim filters [\#346](https://github.com/schmittjoh/JMSTranslationBundle/issues/346) +- Translation extract error [\#340](https://github.com/schmittjoh/JMSTranslationBundle/issues/340) +- The license of the bundle. [\#316](https://github.com/schmittjoh/JMSTranslationBundle/issues/316) +- Roadmap for version 1.3.0 [\#306](https://github.com/schmittjoh/JMSTranslationBundle/issues/306) +- Extracting a concatenated string [\#296](https://github.com/schmittjoh/JMSTranslationBundle/issues/296) +- How to use a different context for form labels translation [\#288](https://github.com/schmittjoh/JMSTranslationBundle/issues/288) +- Not working on symfony 3 [\#287](https://github.com/schmittjoh/JMSTranslationBundle/issues/287) +- Cant launch Web UI [\#272](https://github.com/schmittjoh/JMSTranslationBundle/issues/272) +- It is ignoring existing messages while dumping translation [\#266](https://github.com/schmittjoh/JMSTranslationBundle/issues/266) +- output\_dir should accept @BundlePaths [\#264](https://github.com/schmittjoh/JMSTranslationBundle/issues/264) +- File paths next to the translation line. [\#257](https://github.com/schmittjoh/JMSTranslationBundle/issues/257) +- You can only merge messages with the same id. [\#249](https://github.com/schmittjoh/JMSTranslationBundle/issues/249) +- The format "yml~" does not exist. [\#245](https://github.com/schmittjoh/JMSTranslationBundle/issues/245) +- php parser dependency [\#244](https://github.com/schmittjoh/JMSTranslationBundle/issues/244) +- The annotation "@brief" in class Converter\Converter was never imported. [\#243](https://github.com/schmittjoh/JMSTranslationBundle/issues/243) +- JMS Translation Bundle does not extract keys from controllers in Symfony2 - getTranslationMessages\(\) [\#239](https://github.com/schmittjoh/JMSTranslationBundle/issues/239) +- Support for /\* @Ignore \*/ [\#234](https://github.com/schmittjoh/JMSTranslationBundle/issues/234) +- The extension with alias "jms\_translation" does not have its getConfiguration\(\) method setup [\#232](https://github.com/schmittjoh/JMSTranslationBundle/issues/232) +- Change location of translation files [\#231](https://github.com/schmittjoh/JMSTranslationBundle/issues/231) +- xliff dumper error [\#229](https://github.com/schmittjoh/JMSTranslationBundle/issues/229) +- Extractor misunderstands my "label" key [\#227](https://github.com/schmittjoh/JMSTranslationBundle/issues/227) +- "Translations in this page" debug toolbar helper [\#226](https://github.com/schmittjoh/JMSTranslationBundle/issues/226) +- error on extract [\#224](https://github.com/schmittjoh/JMSTranslationBundle/issues/224) +- Invalid output [\#218](https://github.com/schmittjoh/JMSTranslationBundle/issues/218) +- Messages inside propel validators aren't extracted [\#216](https://github.com/schmittjoh/JMSTranslationBundle/issues/216) +- usage of Twig\_Extension\_StringLoader [\#211](https://github.com/schmittjoh/JMSTranslationBundle/issues/211) +- Generate empty translations [\#208](https://github.com/schmittjoh/JMSTranslationBundle/issues/208) +- Errors when using the global namespace [\#201](https://github.com/schmittjoh/JMSTranslationBundle/issues/201) +- Customize jmstranslation web ui [\#200](https://github.com/schmittjoh/JMSTranslationBundle/issues/200) +- Indentation mess in ultilines translations using PHP dumper [\#192](https://github.com/schmittjoh/JMSTranslationBundle/issues/192) +- Twig parser ignore {% for ... else ... endfor %} syntax [\#189](https://github.com/schmittjoh/JMSTranslationBundle/issues/189) +- How to only update routes.\*.yml files ? [\#167](https://github.com/schmittjoh/JMSTranslationBundle/issues/167) +- phpunit annotations like @covers crashes the extractor [\#157](https://github.com/schmittjoh/JMSTranslationBundle/issues/157) +- Keep XLIFF attributes [\#147](https://github.com/schmittjoh/JMSTranslationBundle/issues/147) +- cant create routes in other langs, keep giving me erros [\#142](https://github.com/schmittjoh/JMSTranslationBundle/issues/142) +- Exception at Translation Extract with bundle [\#141](https://github.com/schmittjoh/JMSTranslationBundle/issues/141) +- PrettyPrint: disable in config.yml [\#136](https://github.com/schmittjoh/JMSTranslationBundle/issues/136) +- Preserve the approve status as set by QT Linguist [\#115](https://github.com/schmittjoh/JMSTranslationBundle/issues/115) +- Throws an exception for grouped choice items in form class [\#88](https://github.com/schmittjoh/JMSTranslationBundle/issues/88) +- Display in UI full path [\#87](https://github.com/schmittjoh/JMSTranslationBundle/issues/87) +- Incorrect translation when multiple @Desc for the same key on the same file [\#86](https://github.com/schmittjoh/JMSTranslationBundle/issues/86) +- Translator chokes on numerical IDs [\#83](https://github.com/schmittjoh/JMSTranslationBundle/issues/83) +- Definition of source-language [\#78](https://github.com/schmittjoh/JMSTranslationBundle/issues/78) +- \[RFC\] Open referenced sources from web UI [\#110](https://github.com/schmittjoh/JMSTranslationBundle/issues/110) + +**Merged pull requests:** + +- Keep new lines inside translations [\#383](https://github.com/schmittjoh/JMSTranslationBundle/pull/383) ([Nyholm](https://github.com/Nyholm)) +- Display available format list for invalid format exception [\#371](https://github.com/schmittjoh/JMSTranslationBundle/pull/371) ([thomasbeaujean](https://github.com/thomasbeaujean)) +- typo [\#365](https://github.com/schmittjoh/JMSTranslationBundle/pull/365) ([Nyholm](https://github.com/Nyholm)) +- Wrong assets path jms.js [\#363](https://github.com/schmittjoh/JMSTranslationBundle/pull/363) ([jacomensink](https://github.com/jacomensink)) +- Added a contribution guide [\#362](https://github.com/schmittjoh/JMSTranslationBundle/pull/362) ([Nyholm](https://github.com/Nyholm)) +- Improved documentation [\#360](https://github.com/schmittjoh/JMSTranslationBundle/pull/360) ([Nyholm](https://github.com/Nyholm)) +- Suggest a new PR template [\#359](https://github.com/schmittjoh/JMSTranslationBundle/pull/359) ([Nyholm](https://github.com/Nyholm)) +- Xliff attributes rebased [\#356](https://github.com/schmittjoh/JMSTranslationBundle/pull/356) ([Nyholm](https://github.com/Nyholm)) +- Fix extraction for global namespace [\#354](https://github.com/schmittjoh/JMSTranslationBundle/pull/354) ([cmfcmf](https://github.com/cmfcmf)) +- Allow to extend the DefaultPhpFileExtractor watched methods [\#187](https://github.com/schmittjoh/JMSTranslationBundle/pull/187) ([damienalexandre](https://github.com/damienalexandre)) +- Release [\#388](https://github.com/schmittjoh/JMSTranslationBundle/pull/388) ([Nyholm](https://github.com/Nyholm)) +- Making source file clickable in WebUI [\#385](https://github.com/schmittjoh/JMSTranslationBundle/pull/385) ([Nyholm](https://github.com/Nyholm)) +- Make sure Message id is always a string [\#384](https://github.com/schmittjoh/JMSTranslationBundle/pull/384) ([Nyholm](https://github.com/Nyholm)) +- Relative path [\#382](https://github.com/schmittjoh/JMSTranslationBundle/pull/382) ([Nyholm](https://github.com/Nyholm)) +- Sort the sources to use alpha order in the xliff file. [\#380](https://github.com/schmittjoh/JMSTranslationBundle/pull/380) ([thomasbeaujean](https://github.com/thomasbeaujean)) + +## [1.2.3](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.2.3) (2016-05-08) + +**Fixed bugs:** + +- Handle attr as a variable [\#348](https://github.com/schmittjoh/JMSTranslationBundle/pull/348) ([gnat42](https://github.com/gnat42)) + +**Closed issues:** + +- Translation Web UI - Fatal error [\#341](https://github.com/schmittjoh/JMSTranslationBundle/issues/341) +- Form extractor crashes when label is false [\#126](https://github.com/schmittjoh/JMSTranslationBundle/issues/126) +- List also available placeholders [\#113](https://github.com/schmittjoh/JMSTranslationBundle/issues/113) +- Duplicated extraction on Inherited Validation Constraints of different bundles classes. [\#79](https://github.com/schmittjoh/JMSTranslationBundle/issues/79) + +**Merged pull requests:** + +- Updated changelog prior to release [\#357](https://github.com/schmittjoh/JMSTranslationBundle/pull/357) ([gnat42](https://github.com/gnat42)) +- Move inline JS to a class [\#352](https://github.com/schmittjoh/JMSTranslationBundle/pull/352) ([mwoynarski](https://github.com/mwoynarski)) +- fix tests broken by merges [\#351](https://github.com/schmittjoh/JMSTranslationBundle/pull/351) ([gnat42](https://github.com/gnat42)) +- Do not allow to fail on SF3 [\#350](https://github.com/schmittjoh/JMSTranslationBundle/pull/350) ([Nyholm](https://github.com/Nyholm)) +- Dev nyholm placeholder [\#349](https://github.com/schmittjoh/JMSTranslationBundle/pull/349) ([Nyholm](https://github.com/Nyholm)) +- Check response content when update message [\#347](https://github.com/schmittjoh/JMSTranslationBundle/pull/347) ([AntoineLemaire](https://github.com/AntoineLemaire)) +- Feature refactor enter node [\#342](https://github.com/schmittjoh/JMSTranslationBundle/pull/342) ([gnat42](https://github.com/gnat42)) +- Do not truncate source file path [\#331](https://github.com/schmittjoh/JMSTranslationBundle/pull/331) ([sustmi](https://github.com/sustmi)) +- Set the reference and the reference position as optionnal [\#330](https://github.com/schmittjoh/JMSTranslationBundle/pull/330) ([thomasbeaujean](https://github.com/thomasbeaujean)) +- Set Loggers on NodeVisitors at construct time [\#267](https://github.com/schmittjoh/JMSTranslationBundle/pull/267) ([bburnichon](https://github.com/bburnichon)) + +## [1.2.2](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.2.2) (2016-04-03) + +**Closed issues:** + +- Extracting choices not keys from forms [\#332](https://github.com/schmittjoh/JMSTranslationBundle/issues/332) +- Run the test suite [\#329](https://github.com/schmittjoh/JMSTranslationBundle/issues/329) +- Remove branch 'dataCollector' [\#323](https://github.com/schmittjoh/JMSTranslationBundle/issues/323) +- Add test case for bundle config and service definition [\#318](https://github.com/schmittjoh/JMSTranslationBundle/issues/318) +- Update current translation files [\#298](https://github.com/schmittjoh/JMSTranslationBundle/issues/298) +- jms xliff loader not working in Symfony 3.0 [\#281](https://github.com/schmittjoh/JMSTranslationBundle/issues/281) +- Bundle does not work in Symfony 3. [\#279](https://github.com/schmittjoh/JMSTranslationBundle/issues/279) +- Fix Deprecations for Symfony 2.8 preparing for Symfony 3.0 [\#278](https://github.com/schmittjoh/JMSTranslationBundle/issues/278) +- Add a way to extract custom Validation messageProperties [\#159](https://github.com/schmittjoh/JMSTranslationBundle/issues/159) +- Impossible to update the bundle via composer. [\#138](https://github.com/schmittjoh/JMSTranslationBundle/issues/138) +- getClassMetadata\(\) is deprecated since symfony 2.2 [\#129](https://github.com/schmittjoh/JMSTranslationBundle/issues/129) +- Cannot redeclare class Doctrine\ORM\Mapping\Annotation on extraction [\#124](https://github.com/schmittjoh/JMSTranslationBundle/issues/124) +- OutputLogger not conform to new LoggerInterface \(PsrLogger\) [\#95](https://github.com/schmittjoh/JMSTranslationBundle/issues/95) +- One case where @Desc before doesn't work [\#77](https://github.com/schmittjoh/JMSTranslationBundle/issues/77) +- Change definitions for XLIFF loader and dumper to accomodate XLF [\#29](https://github.com/schmittjoh/JMSTranslationBundle/issues/29) + +**Merged pull requests:** + +- Updated changelog prior release. [\#337](https://github.com/schmittjoh/JMSTranslationBundle/pull/337) ([Nyholm](https://github.com/Nyholm)) +- Set xlf as default file format [\#335](https://github.com/schmittjoh/JMSTranslationBundle/pull/335) ([wimvds](https://github.com/wimvds)) +- Solving translation of choices label using SF \> 2.7 [\#334](https://github.com/schmittjoh/JMSTranslationBundle/pull/334) ([benjamin-hubert](https://github.com/benjamin-hubert)) +- Fix logger interface calls [\#325](https://github.com/schmittjoh/JMSTranslationBundle/pull/325) ([gnat42](https://github.com/gnat42)) +- Changing the default answer to "Tests pass?" to "no" [\#322](https://github.com/schmittjoh/JMSTranslationBundle/pull/322) ([Nyholm](https://github.com/Nyholm)) +- Removed installation instructions for Symfony 2.0.x [\#321](https://github.com/schmittjoh/JMSTranslationBundle/pull/321) ([cmfcmf](https://github.com/cmfcmf)) +- Added tests for Extension, compiler passes and config [\#320](https://github.com/schmittjoh/JMSTranslationBundle/pull/320) ([Nyholm](https://github.com/Nyholm)) +- update php-parser to either 1.4.1 or 2.x [\#314](https://github.com/schmittjoh/JMSTranslationBundle/pull/314) ([gnat42](https://github.com/gnat42)) +- Symfony 3.0 compat [\#297](https://github.com/schmittjoh/JMSTranslationBundle/pull/297) ([MisatoTremor](https://github.com/MisatoTremor)) + +## [1.2.1](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.2.1) (2016-03-23) + +**Fixed bugs:** + +- Error when choice label is not defined [\#242](https://github.com/schmittjoh/JMSTranslationBundle/issues/242) +- Fatal Error : FormExtractor due to empty index array [\#106](https://github.com/schmittjoh/JMSTranslationBundle/issues/106) + +**Closed issues:** + +- Supported Symfony versions [\#305](https://github.com/schmittjoh/JMSTranslationBundle/issues/305) +- New release [\#300](https://github.com/schmittjoh/JMSTranslationBundle/issues/300) +- Truncate Filter [\#299](https://github.com/schmittjoh/JMSTranslationBundle/issues/299) +- \[Symfony\Component\Debug\Exception\ContextErrorException\] Catchable Fatal Error: Argument 2 passed to JMS\TranslationBundle\Translation\Extractor\FileExtractor::\_\_construct\(\) must be an instance of Symfony\Component\HttpKernel\Log\LoggerInterface, instance of Symfony\Bridge\Monolog\Logger given, called in... [\#293](https://github.com/schmittjoh/JMSTranslationBundle/issues/293) +- Maintainer not responding [\#280](https://github.com/schmittjoh/JMSTranslationBundle/issues/280) +- you might want to do a version \(tag\) 1.2.0? [\#236](https://github.com/schmittjoh/JMSTranslationBundle/issues/236) +- Is this bundle abandoned ? [\#213](https://github.com/schmittjoh/JMSTranslationBundle/issues/213) +- Please update to use newer parser version [\#155](https://github.com/schmittjoh/JMSTranslationBundle/issues/155) +- Unit Tests [\#103](https://github.com/schmittjoh/JMSTranslationBundle/issues/103) + +**Merged pull requests:** + +- Prepare for release 1.2.1 [\#315](https://github.com/schmittjoh/JMSTranslationBundle/pull/315) ([Nyholm](https://github.com/Nyholm)) +- getting rid of the twig text extension [\#313](https://github.com/schmittjoh/JMSTranslationBundle/pull/313) ([benjamin-hubert](https://github.com/benjamin-hubert)) +- remove unused injected container [\#312](https://github.com/schmittjoh/JMSTranslationBundle/pull/312) ([gnat42](https://github.com/gnat42)) +- remove unused use statement [\#311](https://github.com/schmittjoh/JMSTranslationBundle/pull/311) ([gnat42](https://github.com/gnat42)) +- injected request into controller action [\#310](https://github.com/schmittjoh/JMSTranslationBundle/pull/310) ([gnat42](https://github.com/gnat42)) +- Only support official supported symfony versions [\#309](https://github.com/schmittjoh/JMSTranslationBundle/pull/309) ([Nyholm](https://github.com/Nyholm)) +- Added gitter badge [\#308](https://github.com/schmittjoh/JMSTranslationBundle/pull/308) ([Nyholm](https://github.com/Nyholm)) +- updated jquery version to v1.12.0 [\#307](https://github.com/schmittjoh/JMSTranslationBundle/pull/307) ([gnat42](https://github.com/gnat42)) +- Added travis build matrix [\#304](https://github.com/schmittjoh/JMSTranslationBundle/pull/304) ([Nyholm](https://github.com/Nyholm)) +- Added template for PRs and issues [\#303](https://github.com/schmittjoh/JMSTranslationBundle/pull/303) ([Nyholm](https://github.com/Nyholm)) +- add sf3 compatibility: remove Symfony\Component\HttpKernel\Log\Logger… [\#285](https://github.com/schmittjoh/JMSTranslationBundle/pull/285) ([MaximeThoonsen](https://github.com/MaximeThoonsen)) + +## [1.2.0](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.2.0) (2016-03-23) + +**Closed issues:** + +- Is this bundle dead ? [\#294](https://github.com/schmittjoh/JMSTranslationBundle/issues/294) +- twig deprecation [\#273](https://github.com/schmittjoh/JMSTranslationBundle/issues/273) +- @Ignore does not work on custom Extractor [\#265](https://github.com/schmittjoh/JMSTranslationBundle/issues/265) +- Conflict with nikic/php-parser 1.4.1 [\#262](https://github.com/schmittjoh/JMSTranslationBundle/issues/262) +- PHP Parser 0.9.1 cannot recognize new php 5.6 syntax. [\#255](https://github.com/schmittjoh/JMSTranslationBundle/issues/255) +- security.yml configuration [\#252](https://github.com/schmittjoh/JMSTranslationBundle/issues/252) +- Error with logger dependency in symfony 2.6.\* [\#237](https://github.com/schmittjoh/JMSTranslationBundle/issues/237) +- config.yml issue [\#214](https://github.com/schmittjoh/JMSTranslationBundle/issues/214) +- Can't extract "%kernel.root\_dir%/../src" dir in config.yml \(translation:extract command\) [\#210](https://github.com/schmittjoh/JMSTranslationBundle/issues/210) +- Cannot use object of type PHPParser\_Comment\_Doc as array [\#205](https://github.com/schmittjoh/JMSTranslationBundle/issues/205) +- Ignore comment cause Fatal error [\#204](https://github.com/schmittjoh/JMSTranslationBundle/issues/204) +- Error on placeholder extraction [\#193](https://github.com/schmittjoh/JMSTranslationBundle/issues/193) +- Tags and versioning [\#190](https://github.com/schmittjoh/JMSTranslationBundle/issues/190) +- Composer installed a fork instead if this bundle [\#177](https://github.com/schmittjoh/JMSTranslationBundle/issues/177) +- Error "no DTD found" [\#169](https://github.com/schmittjoh/JMSTranslationBundle/issues/169) +- cave [\#168](https://github.com/schmittjoh/JMSTranslationBundle/issues/168) +- Admin UI - no cache options when updating translations [\#151](https://github.com/schmittjoh/JMSTranslationBundle/issues/151) +- Exception in FormExtractor [\#145](https://github.com/schmittjoh/JMSTranslationBundle/issues/145) +- jms/di-extra-bundle should be required in composer.json [\#144](https://github.com/schmittjoh/JMSTranslationBundle/issues/144) +- Web interface error [\#139](https://github.com/schmittjoh/JMSTranslationBundle/issues/139) +- Error with DI [\#133](https://github.com/schmittjoh/JMSTranslationBundle/issues/133) +- XLIFF Wrong file extension [\#128](https://github.com/schmittjoh/JMSTranslationBundle/issues/128) +- Extractor overrides empty "" values with "translation.identifier" [\#125](https://github.com/schmittjoh/JMSTranslationBundle/issues/125) +- Not so helpfull error message [\#84](https://github.com/schmittjoh/JMSTranslationBundle/issues/84) + +**Merged pull requests:** + +- add PHP 7 [\#291](https://github.com/schmittjoh/JMSTranslationBundle/pull/291) ([ickbinhier](https://github.com/ickbinhier)) +- Fix FileWriter::write signature [\#286](https://github.com/schmittjoh/JMSTranslationBundle/pull/286) ([artursvonda](https://github.com/artursvonda)) +- Update TranslateController.php [\#271](https://github.com/schmittjoh/JMSTranslationBundle/pull/271) ([nursultanturdaliev](https://github.com/nursultanturdaliev)) +- \[WIP\] add support for symfony 3 [\#270](https://github.com/schmittjoh/JMSTranslationBundle/pull/270) ([shieldo](https://github.com/shieldo)) +- renamed Symfony2 to Symfony [\#259](https://github.com/schmittjoh/JMSTranslationBundle/pull/259) ([OskarStark](https://github.com/OskarStark)) +- Removed deprecated Twig features [\#256](https://github.com/schmittjoh/JMSTranslationBundle/pull/256) ([XWB](https://github.com/XWB)) +- php parser dependency \#244 [\#246](https://github.com/schmittjoh/JMSTranslationBundle/pull/246) ([skotaa](https://github.com/skotaa)) +- Fixed example config in the docs [\#215](https://github.com/schmittjoh/JMSTranslationBundle/pull/215) ([attilabukor](https://github.com/attilabukor)) +- Fix error due to changed PHPParser API [\#207](https://github.com/schmittjoh/JMSTranslationBundle/pull/207) ([vansante](https://github.com/vansante)) +- Added PHP 5.6 and HHVM to travis.yml [\#203](https://github.com/schmittjoh/JMSTranslationBundle/pull/203) ([Nyholm](https://github.com/Nyholm)) +- Update composer.json [\#175](https://github.com/schmittjoh/JMSTranslationBundle/pull/175) ([flip111](https://github.com/flip111)) +- Moved alert message to previous TD [\#170](https://github.com/schmittjoh/JMSTranslationBundle/pull/170) ([Jaspur](https://github.com/Jaspur)) +- Update to the last version of phpparser [\#166](https://github.com/schmittjoh/JMSTranslationBundle/pull/166) ([benji07](https://github.com/benji07)) +- Method addBuilder to permit to inject aditionnal builders at runtime [\#165](https://github.com/schmittjoh/JMSTranslationBundle/pull/165) ([paulandrieux](https://github.com/paulandrieux)) +- Added PHP5.5 for travis tests [\#163](https://github.com/schmittjoh/JMSTranslationBundle/pull/163) ([tristanbes](https://github.com/tristanbes)) +- Sort translation locales [\#161](https://github.com/schmittjoh/JMSTranslationBundle/pull/161) ([slu125](https://github.com/slu125)) +- Change ValidationExtractor to extract all properties ending with 'Message' [\#160](https://github.com/schmittjoh/JMSTranslationBundle/pull/160) ([vansante](https://github.com/vansante)) +- Update FileUtils.php [\#158](https://github.com/schmittjoh/JMSTranslationBundle/pull/158) ([slu125](https://github.com/slu125)) +- Ints should be translatable in choice lists. [\#156](https://github.com/schmittjoh/JMSTranslationBundle/pull/156) ([tarjei](https://github.com/tarjei)) +- Handle variants where attr is not an array but a function pointer. [\#153](https://github.com/schmittjoh/JMSTranslationBundle/pull/153) ([tarjei](https://github.com/tarjei)) +- Fixed multiple occurences in extraction leading to overwritten desc-valu... [\#149](https://github.com/schmittjoh/JMSTranslationBundle/pull/149) ([peetersdiet](https://github.com/peetersdiet)) +- Alter value with desc or id only if new message [\#143](https://github.com/schmittjoh/JMSTranslationBundle/pull/143) ([ghost](https://github.com/ghost)) +- Added extractor for "empty\_value" as array [\#140](https://github.com/schmittjoh/JMSTranslationBundle/pull/140) ([goetas](https://github.com/goetas)) +- Fix order of JMS custom elements as per XLIFF spec [\#137](https://github.com/schmittjoh/JMSTranslationBundle/pull/137) ([azogheb](https://github.com/azogheb)) +- Added Service to fix XLIFF file extension [\#135](https://github.com/schmittjoh/JMSTranslationBundle/pull/135) ([carlcraig](https://github.com/carlcraig)) +- \[FormExtractor\] fixes Fatal error: Call to a member function getDocComment\(\) on a non-object [\#130](https://github.com/schmittjoh/JMSTranslationBundle/pull/130) ([chbruyand](https://github.com/chbruyand)) +- Extract translations from form widgets 'placeholder'- and 'title'-attributes [\#120](https://github.com/schmittjoh/JMSTranslationBundle/pull/120) ([azine](https://github.com/azine)) + +## [1.1.0](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.1.0) (2013-06-08) + +**Closed issues:** + +- master and sf \<2.3 [\#116](https://github.com/schmittjoh/JMSTranslationBundle/issues/116) +- Manually add translation [\#109](https://github.com/schmittjoh/JMSTranslationBundle/issues/109) +- PUT no longer allowed by default in Symfony 2.2 [\#99](https://github.com/schmittjoh/JMSTranslationBundle/issues/99) +- Extractor searching same directories mulitple times [\#97](https://github.com/schmittjoh/JMSTranslationBundle/issues/97) +- Translation extractor delete unfound keys [\#94](https://github.com/schmittjoh/JMSTranslationBundle/issues/94) +- PHPParser\_Error with no information to debug [\#92](https://github.com/schmittjoh/JMSTranslationBundle/issues/92) +- Parse strings in annotations [\#91](https://github.com/schmittjoh/JMSTranslationBundle/issues/91) +- Composer does not work with symfony 2.0.x [\#90](https://github.com/schmittjoh/JMSTranslationBundle/issues/90) +- Translation could not be saved [\#89](https://github.com/schmittjoh/JMSTranslationBundle/issues/89) +- Inconsistent defaults between translation:extract and translation:update [\#85](https://github.com/schmittjoh/JMSTranslationBundle/issues/85) +- Regarding Setting Default domain for templates [\#82](https://github.com/schmittjoh/JMSTranslationBundle/issues/82) +- Regarding Forcing Translator to look into a perticular directory [\#81](https://github.com/schmittjoh/JMSTranslationBundle/issues/81) +- Regarding inclusion of Multiple domains [\#80](https://github.com/schmittjoh/JMSTranslationBundle/issues/80) +- Wrong php code generated when using |trans with parameters in twig [\#73](https://github.com/schmittjoh/JMSTranslationBundle/issues/73) +- Use framework.session.cookie\_domain to store "hl" cookie ? [\#72](https://github.com/schmittjoh/JMSTranslationBundle/issues/72) +- Position of @Desc in PHP [\#16](https://github.com/schmittjoh/JMSTranslationBundle/issues/16) + +**Merged pull requests:** + +- Missing sprintf\(\) invocation. [\#123](https://github.com/schmittjoh/JMSTranslationBundle/pull/123) ([Crozin](https://github.com/Crozin)) +- Add anchors in translation UI [\#121](https://github.com/schmittjoh/JMSTranslationBundle/pull/121) ([tkleinhakisa](https://github.com/tkleinhakisa)) +- Class metadata interface changes in symfony [\#119](https://github.com/schmittjoh/JMSTranslationBundle/pull/119) ([ajohnstone](https://github.com/ajohnstone)) +- FormExtractor extracts labels from create [\#108](https://github.com/schmittjoh/JMSTranslationBundle/pull/108) ([geecu](https://github.com/geecu)) +- Search translation in ui [\#107](https://github.com/schmittjoh/JMSTranslationBundle/pull/107) ([tkleinhakisa](https://github.com/tkleinhakisa)) +- Twig Embed Support [\#104](https://github.com/schmittjoh/JMSTranslationBundle/pull/104) ([gmoreira](https://github.com/gmoreira)) +- Fixe scan directories multiple times [\#102](https://github.com/schmittjoh/JMSTranslationBundle/pull/102) ([tkleinhakisa](https://github.com/tkleinhakisa)) +- Fix for \#99 [\#100](https://github.com/schmittjoh/JMSTranslationBundle/pull/100) ([nurikabe](https://github.com/nurikabe)) +- Fix for issue \#95 [\#98](https://github.com/schmittjoh/JMSTranslationBundle/pull/98) ([nurikabe](https://github.com/nurikabe)) +- Refactor the form extractor to allow extraction from FormListeners [\#96](https://github.com/schmittjoh/JMSTranslationBundle/pull/96) ([acasademont](https://github.com/acasademont)) +- The Desc attribute can now be one line before call to trans or transChoice. The same applies to forms. [\#76](https://github.com/schmittjoh/JMSTranslationBundle/pull/76) ([mvrhov](https://github.com/mvrhov)) +- fix composer for symfony 2.2 [\#75](https://github.com/schmittjoh/JMSTranslationBundle/pull/75) ([gimler](https://github.com/gimler)) +- Clear previous config when setConfig on TranslationUpdater [\#74](https://github.com/schmittjoh/JMSTranslationBundle/pull/74) ([stephpy](https://github.com/stephpy)) + +## [1.0.0](https://github.com/schmittjoh/JMSTranslationBundle/tree/1.0.0) (2012-09-21) + +**Closed issues:** + +- Twig Error when launch extract command [\#70](https://github.com/schmittjoh/JMSTranslationBundle/issues/70) +- Example of JMS\TranslationBundle\Annotation\Ignore [\#68](https://github.com/schmittjoh/JMSTranslationBundle/issues/68) +- non-existent service config\_factory [\#67](https://github.com/schmittjoh/JMSTranslationBundle/issues/67) +- Translation\_domain on forms [\#61](https://github.com/schmittjoh/JMSTranslationBundle/issues/61) +- Readme add how to test this bundle as it does not test tand-alone [\#58](https://github.com/schmittjoh/JMSTranslationBundle/issues/58) +- OS ordering seems to be causing git merge conflicts [\#57](https://github.com/schmittjoh/JMSTranslationBundle/issues/57) +- Ids on xliff entries on generation [\#56](https://github.com/schmittjoh/JMSTranslationBundle/issues/56) +- can't tag service jms\_translation.file\_vistor or not working [\#54](https://github.com/schmittjoh/JMSTranslationBundle/issues/54) +- Advanced translations depending on variables gender, number, etc... [\#52](https://github.com/schmittjoh/JMSTranslationBundle/issues/52) +- FileExtractor would not work with current php-parser [\#50](https://github.com/schmittjoh/JMSTranslationBundle/issues/50) +- translation:extract command for a Bundle not correct [\#49](https://github.com/schmittjoh/JMSTranslationBundle/issues/49) +- Issue running script for extracts from the cli and website [\#48](https://github.com/schmittjoh/JMSTranslationBundle/issues/48) +- Look for FormBuilderInterface instances too [\#46](https://github.com/schmittjoh/JMSTranslationBundle/issues/46) +- Extract options/second\_options labels of form field repeated type [\#45](https://github.com/schmittjoh/JMSTranslationBundle/issues/45) +- Fatal Error when exporting translations [\#42](https://github.com/schmittjoh/JMSTranslationBundle/issues/42) +- error in 'clean' install \(used to work before\) [\#41](https://github.com/schmittjoh/JMSTranslationBundle/issues/41) +- illegal XLIFF file format due to jms:reference-file elements [\#39](https://github.com/schmittjoh/JMSTranslationBundle/issues/39) +- Invalid WebUI configurations [\#38](https://github.com/schmittjoh/JMSTranslationBundle/issues/38) +- translation:extract --bundle=XXXBundle =\> grabbing messages from all bundles [\#37](https://github.com/schmittjoh/JMSTranslationBundle/issues/37) +- Keep empty strings [\#36](https://github.com/schmittjoh/JMSTranslationBundle/issues/36) +- yml and linebreaks [\#35](https://github.com/schmittjoh/JMSTranslationBundle/issues/35) +- translating messages from validation.yml [\#33](https://github.com/schmittjoh/JMSTranslationBundle/issues/33) +- Can't run tests [\#30](https://github.com/schmittjoh/JMSTranslationBundle/issues/30) +- Problem with WebUi [\#26](https://github.com/schmittjoh/JMSTranslationBundle/issues/26) +- Please remove dependency on the DI Bundle [\#23](https://github.com/schmittjoh/JMSTranslationBundle/issues/23) +- The twig 'desc' filter does not work with the 'transchoice' filter [\#22](https://github.com/schmittjoh/JMSTranslationBundle/issues/22) +- @Ignore gets ignored for plain text form field labels [\#20](https://github.com/schmittjoh/JMSTranslationBundle/issues/20) +- Extract empty\_values of form field choice/entity type [\#17](https://github.com/schmittjoh/JMSTranslationBundle/issues/17) +- Content of desc filter not dumped [\#14](https://github.com/schmittjoh/JMSTranslationBundle/issues/14) +- How to use @Ignore? [\#13](https://github.com/schmittjoh/JMSTranslationBundle/issues/13) +- WebUI: Update fail without domain "message" \(Bug fix include\) [\#11](https://github.com/schmittjoh/JMSTranslationBundle/issues/11) +- Inline Editing? [\#10](https://github.com/schmittjoh/JMSTranslationBundle/issues/10) +- Empty meaning and wrong description in Message while dumping [\#9](https://github.com/schmittjoh/JMSTranslationBundle/issues/9) +- Use of removed MessageCatalogue-\>all\(\) method in Updater [\#8](https://github.com/schmittjoh/JMSTranslationBundle/issues/8) +- export option to remove jms specific xliff code [\#7](https://github.com/schmittjoh/JMSTranslationBundle/issues/7) +- Translation.loader dependency [\#1](https://github.com/schmittjoh/JMSTranslationBundle/issues/1) + +**Merged pull requests:** + +- Bugfix in RuntimeException when when trying to load an incorrectly formatted XML [\#71](https://github.com/schmittjoh/JMSTranslationBundle/pull/71) ([skwi](https://github.com/skwi)) +- Use the "validators" domain for the "invalid\_message" options in forms. [\#66](https://github.com/schmittjoh/JMSTranslationBundle/pull/66) ([bvleur](https://github.com/bvleur)) +- Parse default option for translation\_domain [\#65](https://github.com/schmittjoh/JMSTranslationBundle/pull/65) ([bvleur](https://github.com/bvleur)) +- Extract invalid\_message option for repeated field type [\#64](https://github.com/schmittjoh/JMSTranslationBundle/pull/64) ([bvleur](https://github.com/bvleur)) +- Support translation\_domain option on forms [\#63](https://github.com/schmittjoh/JMSTranslationBundle/pull/63) ([bvleur](https://github.com/bvleur)) +- \[\#57\] fix problem with undetermined comparison callback on uasort\( , strcasecmp\) [\#62](https://github.com/schmittjoh/JMSTranslationBundle/pull/62) ([cordoval](https://github.com/cordoval)) +- \[\#56\] fix ids on xliff files to be sha1 to avoid merging conflicts [\#59](https://github.com/schmittjoh/JMSTranslationBundle/pull/59) ([cordoval](https://github.com/cordoval)) +- Support labels for "repeated" field groups [\#55](https://github.com/schmittjoh/JMSTranslationBundle/pull/55) ([bvleur](https://github.com/bvleur)) +- Look for FormBuilderInterface instances too [\#47](https://github.com/schmittjoh/JMSTranslationBundle/pull/47) ([albyrock87](https://github.com/albyrock87)) +- Change incorrect "master-dev" to "dev-master" [\#44](https://github.com/schmittjoh/JMSTranslationBundle/pull/44) ([jonathaningram](https://github.com/jonathaningram)) +- Keep empty strings, Yml line breaks [\#43](https://github.com/schmittjoh/JMSTranslationBundle/pull/43) ([cdfre](https://github.com/cdfre)) +- Specify a php-parser version [\#40](https://github.com/schmittjoh/JMSTranslationBundle/pull/40) ([nikic](https://github.com/nikic)) +- Fix "Argument 7 passed \(...\) must be an array, null given" [\#34](https://github.com/schmittjoh/JMSTranslationBundle/pull/34) ([frosas](https://github.com/frosas)) +- check consistency of messages desc [\#32](https://github.com/schmittjoh/JMSTranslationBundle/pull/32) ([arnaud-lb](https://github.com/arnaud-lb)) +- Added CDATA support for XLIFF format [\#31](https://github.com/schmittjoh/JMSTranslationBundle/pull/31) ([dlsniper](https://github.com/dlsniper)) +- Changed the XLIFF dumper so that it supports CDATA for target and source [\#28](https://github.com/schmittjoh/JMSTranslationBundle/pull/28) ([dlsniper](https://github.com/dlsniper)) +- don't pass the message id in the URL \(workaround for https://github.com/symfony/symfony/issues/2962 \) [\#27](https://github.com/schmittjoh/JMSTranslationBundle/pull/27) ([arnaud-lb](https://github.com/arnaud-lb)) +- add composer file [\#25](https://github.com/schmittjoh/JMSTranslationBundle/pull/25) ([cdfre](https://github.com/cdfre)) +- Add Support for Choices [\#21](https://github.com/schmittjoh/JMSTranslationBundle/pull/21) ([AlexKa](https://github.com/AlexKa)) +- Fix DefaultApplyingNodeVisitor when |trans has replacement param [\#18](https://github.com/schmittjoh/JMSTranslationBundle/pull/18) ([arnaud-lb](https://github.com/arnaud-lb)) +- set validator messages' domain to "validators" [\#15](https://github.com/schmittjoh/JMSTranslationBundle/pull/15) ([arnaud-lb](https://github.com/arnaud-lb)) +- Resolve issue \#11 [\#12](https://github.com/schmittjoh/JMSTranslationBundle/pull/12) ([jpierront](https://github.com/jpierront)) +- Add an option to load external resources [\#6](https://github.com/schmittjoh/JMSTranslationBundle/pull/6) ([rande](https://github.com/rande)) +- Add LoggerAwareInterface, display error messages on parsing errors [\#5](https://github.com/schmittjoh/JMSTranslationBundle/pull/5) ([rande](https://github.com/rande)) +- Add domain management [\#4](https://github.com/schmittjoh/JMSTranslationBundle/pull/4) ([rande](https://github.com/rande)) +- add an option to keep old translation [\#3](https://github.com/schmittjoh/JMSTranslationBundle/pull/3) ([rande](https://github.com/rande)) +- Add only domains option [\#2](https://github.com/schmittjoh/JMSTranslationBundle/pull/2) ([rande](https://github.com/rande)) + + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/Changelog.md b/Changelog.md deleted file mode 100644 index 87b5c94b..00000000 --- a/Changelog.md +++ /dev/null @@ -1,59 +0,0 @@ -# Changelog - -### 1.3.2 to 1.4.2 -* Remove dependencies on JMSDiExtraBundle and JMSAopBundle. If you do not use these bundles elsewhere in your application you will need to remove the reference to them in `registerBundles` of your `AppKernel` -* Added support for nikic/parser v4 - -### 1.3.1 to 1.3.2 -* Added configuration options to disable date/sources in xliff dump -* Fixed trim bug with @XBundle notation -* Added support for php 7.1 -* Added Twig 2 support - -### 1.3.0 to 1.3.1 - -* Fixed new messages not showing at the top in WebUI when in XLIFF format. -* Fixed relative path calculation when file path is not expressed in the form of `kernel.root_dir."/../whatever"` - -### 1.2.3 to 1.3.0 - -* XliffMessage is improved with note elements and attributes. -* Keeping new lines inside translations -* Fix global namespace extraction. -* Better documentation -* Clearer Exception messages -* The `DefaultPhpFileExtractor` could be extended an modified which function we should extraction messages from. -* Make sure Message ID is always a string to avoid issues with numerial IDs. -* Make source files clickable in WebUI -* Message sources in Xliff files will be sorted in alphabetical order - -### 1.2.2 to 1.2.3 - -* Support for placeholders and empty_value. -* Slightly easier WebUI extension with new extensible js class -* Extracted strings have the full source path stored -* Improvements to WebUI logic -* Various extraction bug fixes (attr as variable) -* Various improvements to xliff dumper -* Ensure full Symfony 3.0 support and tests - -### 1.2.1 to 1.2.2 - -* Support nikic/php-parser 1.4.x and 2.0.x -* Set XLF as default output format of the ExtractTranslationCommand -* Better compatibility with Symfony 3.0 -* Bugfixes with calls to the logger -* Added more tests for controllers, extension, compiler passes and config -* Code and doc cleanup - -### 1.2.0 to 1.2.1 - -* New maintainers: @gnat42, @gouaille and @nyholm. -* Updated Jquery from 1.6.1 to 1.12.0. -* Dropping support for Symfony 2.1, 2.2, 2.4, 2.5 and 2.6. -* Removed the requirement on Twig text extension. -* Removed use of the deprecated `Symfony\Component\HttpKernel\Log\LoggerInterface` in favor for `Psr\Log\LoggerInterface`. - -### 1.1 to 1.2 - -* Lots of minor changes and improvements from June 2013. diff --git a/Command/ExtractTranslationCommand.php b/Command/ExtractTranslationCommand.php index 3f871ad8..ae58d570 100644 --- a/Command/ExtractTranslationCommand.php +++ b/Command/ExtractTranslationCommand.php @@ -1,5 +1,7 @@ * @@ -18,30 +20,51 @@ namespace JMS\TranslationBundle\Command; +use JMS\TranslationBundle\Logger\OutputLogger; use JMS\TranslationBundle\Translation\ConfigBuilder; -use JMS\TranslationBundle\Exception\RuntimeException; +use JMS\TranslationBundle\Translation\ConfigFactory; +use JMS\TranslationBundle\Translation\Updater; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; -use JMS\TranslationBundle\Translation\Config; -use JMS\TranslationBundle\Logger\OutputLogger; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; /** * Command for extracting translations. * * @author Johannes M. Schmitt */ -class ExtractTranslationCommand extends ContainerAwareCommand +class ExtractTranslationCommand extends Command { /** - * {@inheritdoc} + * @var ConfigFactory + */ + private $configFactory; + + /** + * @var Updater + */ + private $updater; + + /** + * @var array */ - protected function configure() + private $locales; + + public function __construct(ConfigFactory $configFactory, Updater $updater, array $locales) + { + $this->configFactory = $configFactory; + $this->updater = $updater; + $this->locales = $locales; + + parent::__construct(); + } + + protected function configure(): void { $this - ->setName('translation:extract') + ->setName('jms:translation:extract') ->setDescription('Extracts translation messages from your code.') ->addArgument('locales', InputArgument::IS_ARRAY, 'The locales for which to extract messages.') ->addOption('enable-extractor', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The alias of an extractor which should be enabled.') @@ -56,28 +79,23 @@ protected function configure() ->addOption('output-dir', null, InputOption::VALUE_REQUIRED, 'The directory where files should be written to.') ->addOption('dry-run', null, InputOption::VALUE_NONE, 'When specified, changes are _NOT_ persisted to disk.') ->addOption('output-format', null, InputOption::VALUE_REQUIRED, 'The output format that should be used (in most cases, it is better to change only the default-output-format).') + ->addOption('intl-icu', null, InputOption::VALUE_NONE, 'Flag to indicate if translations should be dumped to using the ICU message format.') ->addOption('default-output-format', null, InputOption::VALUE_REQUIRED, 'The default output format (defaults to xlf).') ->addOption('keep', null, InputOption::VALUE_NONE, 'Define if the updater service should keep the old translation (defaults to false).') - ->addOption('external-translations-dir', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Load external translation resources') - ; + ->addOption('external-translations-dir', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Load external translation resources'); } - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return void - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $builder = $input->getOption('config') ? - $this->getContainer()->get('jms_translation.config_factory')->getBuilder($input->getOption('config')) + $this->configFactory->getBuilder($input->getOption('config')) : new ConfigBuilder(); $this->updateWithInput($input, $builder); $locales = $input->getArgument('locales'); if (empty($locales)) { - $locales = $this->getContainer()->getParameter('jms_translation.locales'); + $locales = $this->locales; } if (empty($locales)) { @@ -93,51 +111,48 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln(sprintf('Directories: %s', implode(', ', $config->getScanDirs()))); $output->writeln(sprintf('Excluded Directories: %s', $config->getExcludedDirs() ? implode(', ', $config->getExcludedDirs()) : '# none #')); $output->writeln(sprintf('Excluded Names: %s', $config->getExcludedNames() ? implode(', ', $config->getExcludedNames()) : '# none #')); - $output->writeln(sprintf('Output-Format: %s', $config->getOutputFormat() ? $config->getOutputFormat() : '# whatever is present, if nothing then '.$config->getDefaultOutputFormat().' #')); + $output->writeln(sprintf('Output-Format: %s', $config->getOutputFormat() ? $config->getOutputFormat() : '# whatever is present, if nothing then ' . $config->getDefaultOutputFormat() . ' #')); $output->writeln(sprintf('Custom Extractors: %s', $config->getEnabledExtractors() ? implode(', ', array_keys($config->getEnabledExtractors())) : '# none #')); $output->writeln('============================================================'); - $updater = $this->getContainer()->get('jms_translation.updater'); - $updater->setLogger($logger = new OutputLogger($output)); + $this->updater->setLogger($logger = new OutputLogger($output)); if (!$input->getOption('verbose')) { $logger->setLevel(OutputLogger::ALL ^ OutputLogger::DEBUG); } if ($input->getOption('dry-run')) { - $changeSet = $updater->getChangeSet($config); + $changeSet = $this->updater->getChangeSet($config); - $output->writeln('Added Messages: '.count($changeSet->getAddedMessages())); + $output->writeln('Added Messages: ' . count($changeSet->getAddedMessages())); if ($input->hasParameterOption('--verbose')) { foreach ($changeSet->getAddedMessages() as $message) { - $output->writeln($message->getId(). '-> '.$message->getDesc()); + $output->writeln($message->getId() . '-> ' . $message->getDesc()); } } if ($config->isKeepOldMessages()) { $output->writeln('Deleted Messages: # none as "Keep Old Translations" is true #'); } else { - $output->writeln('Deleted Messages: '.count($changeSet->getDeletedMessages())); + $output->writeln('Deleted Messages: ' . count($changeSet->getDeletedMessages())); if ($input->hasParameterOption('--verbose')) { foreach ($changeSet->getDeletedMessages() as $message) { - $output->writeln($message->getId(). '-> '.$message->getDesc()); + $output->writeln($message->getId() . '-> ' . $message->getDesc()); } } } - return; + return 0; } - $updater->process($config); + $this->updater->process($config); } $output->writeln('done!'); + + return 0; } - /** - * @param InputInterface $input - * @param ConfigBuilder $builder - */ private function updateWithInput(InputInterface $input, ConfigBuilder $builder) { if ($bundle = $input->getOption('bundle')) { @@ -146,8 +161,8 @@ private function updateWithInput(InputInterface $input, ConfigBuilder $builder) } $bundle = $this->getApplication()->getKernel()->getBundle($bundle); - $builder->setTranslationsDir($bundle->getPath().'/Resources/translations'); - $builder->setScanDirs(array($bundle->getPath())); + $builder->setTranslationsDir($bundle->getPath() . '/Resources/translations'); + $builder->setScanDirs([$bundle->getPath()]); } if ($dirs = $input->getOption('dir')) { @@ -162,6 +177,10 @@ private function updateWithInput(InputInterface $input, ConfigBuilder $builder) $builder->setOutputFormat($outputFormat); } + if ($input->hasParameterOption('--intl-icu')) { + $builder->setUseIcuMessageFormat(true); + } + if ($input->getOption('ignore-domain')) { foreach ($input->getOption('ignore-domain') as $domain) { $builder->addIgnoredDomain($domain); diff --git a/Command/ResourcesListCommand.php b/Command/ResourcesListCommand.php index d9e9c741..b18482d2 100644 --- a/Command/ResourcesListCommand.php +++ b/Command/ResourcesListCommand.php @@ -1,5 +1,7 @@ * @@ -18,59 +20,75 @@ namespace JMS\TranslationBundle\Command; -use JMS\TranslationBundle\Translation\ConfigBuilder; -use JMS\TranslationBundle\Exception\RuntimeException; -use JMS\TranslationBundle\Translation\Config; -use JMS\TranslationBundle\Logger\OutputLogger; -use Symfony\Component\Console\Input\InputOption; +use JMS\TranslationBundle\Util\FileUtils; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Finder\Finder; -use JMS\TranslationBundle\Util\FileUtils; /** * @author Fabien Potencier * @author Thomas Rabaix */ -class ResourcesListCommand extends ContainerAwareCommand +class ResourcesListCommand extends Command { /** - * {@inheritdoc} + * @var string + */ + private $projectDir; + + /** + * @var string|null + */ + private $rootDir; + + /** + * @var array */ - protected function configure() + private $bundles; + + public function __construct(string $projectDir, array $bundles, ?string $rootDir) + { + $this->projectDir = $projectDir; + $this->bundles = $bundles; + $this->rootDir = $rootDir; + + parent::__construct(); + } + + protected function configure(): void { $this - ->setName('translation:list-resources') + ->setName('jms:translation:list-resources') ->setDescription('List translation resources available.') - ->addOption('files', null, InputOption::VALUE_OPTIONAL, 'Display only files') - ; + ->addOption('files', null, InputOption::VALUE_OPTIONAL, 'Display only files'); } - /** - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output - * @return void - */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { - $rootPath = realpath($this->getContainer()->getParameter('kernel.root_dir')); - $basePath = realpath($this->getContainer()->getParameter('kernel.root_dir').'/..'); + $directoriesToSearch = []; + + // TODO: Remove this block when dropping support of Symfony 4 as it will always be false + if ($this->rootDir !== null) { + $directoriesToSearch[] = realpath($this->rootDir); + } + + $basePath = realpath($this->projectDir); + + $directoriesToSearch[] = $basePath; $dirs = $this->retrieveDirs(); if (!$input->hasParameterOption('--files')) { $output->writeln('Directories list :'); foreach ($dirs as $dir) { - $path = str_replace($rootPath, '%kernel.root_dir%', $dir); - $path = str_replace($basePath, '%kernel.root_dir%/..', $path); + $path = str_replace($directoriesToSearch, ['%kernel.root_dir%', '%kernel.project_dir%'], $dir); $output->writeln(sprintf(' - %s', $path)); } $output->writeln('done!'); - return; + return 0; } $output->writeln('Resources list :'); @@ -78,20 +96,23 @@ protected function execute(InputInterface $input, OutputInterface $output) $files = $this->retrieveFiles($dirs); foreach ($files as $file) { - $path = str_replace($basePath, '%kernel.root_dir%', $file); - $output->writeln(sprintf(' - %s', $path)); + $path = str_replace($basePath, '%kernel.project_dir%', (string) $file); + $output->writeln(sprintf(' - %s', $path)); } $output->writeln('done!'); + + return 0; } /** * @param array $dirs + * * @return array */ private function retrieveFiles(array $dirs) { - $files = array(); + $files = []; // Register translation resources foreach ($dirs as $dir) { foreach (FileUtils::findTranslationFiles($dir) as $catalogue => $locales) { @@ -112,15 +133,23 @@ private function retrieveFiles(array $dirs) private function retrieveDirs() { // Discover translation directories - $dirs = array(); - foreach ($this->getContainer()->getParameter('kernel.bundles') as $bundle) { + $dirs = []; + foreach ($this->bundles as $bundle) { $reflection = new \ReflectionClass($bundle); - if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) { + if (is_dir($dir = dirname($reflection->getFilename()) . '/Resources/translations')) { $dirs[] = $dir; } } - if (is_dir($dir = $this->getContainer()->getParameter('kernel.root_dir').'/Resources/translations')) { + // TODO: Remove this block when dropping support of Symfony 4 + if ( + $this->rootDir !== null && + is_dir($dir = $this->rootDir . '/Resources/translations') + ) { + $dirs[] = $dir; + } + + if (is_dir($dir = $this->projectDir . '/translations')) { $dirs[] = $dir; } diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 0c5b53dc..09af04e8 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -1,5 +1,7 @@ * @@ -21,17 +23,17 @@ use JMS\TranslationBundle\Exception\RuntimeException; use JMS\TranslationBundle\Translation\ConfigFactory; use JMS\TranslationBundle\Translation\Updater; -use Symfony\Component\HttpFoundation\Response; use JMS\TranslationBundle\Util\FileUtils; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; /** - * @Route("/api", service="jms_translation.controller.api_controller") - * * @author Johannes M. Schmitt + * + * @Route("/api") */ +#[Route('/api')] class ApiController { /** @@ -44,12 +46,6 @@ class ApiController */ private $updater; - /** - * ApiController constructor. - * - * @param ConfigFactory $configFactory - * @param Updater $updater - */ public function __construct(ConfigFactory $configFactory, Updater $updater) { $this->configFactory = $configFactory; @@ -57,18 +53,20 @@ public function __construct(ConfigFactory $configFactory, Updater $updater) } /** - * @Route("/configs/{config}/domains/{domain}/locales/{locale}/messages", - * name="jms_translation_update_message", - * defaults = {"id" = null}, - * options = {"i18n" = false}) - * @Method("PUT") * @param Request $request * @param string $config * @param string $domain * @param string $locale * * @return Response + * + * @Route("/configs/{config}/domains/{domain}/locales/{locale}/messages", + * methods={"PUT"}, + * name="jms_translation_update_message", + * defaults = {"id" = null}, + * options = {"i18n" = false}) */ + #[Route('/configs/{config}/domains/{domain}/locales/{locale}/messages', name: 'jms_translation_update_message', methods: [Request::METHOD_PUT], defaults: ['id' => null], options: ['i18n' => false])] public function updateMessageAction(Request $request, $config, $domain, $locale) { $id = $request->query->get('id'); @@ -84,10 +82,14 @@ public function updateMessageAction(Request $request, $config, $domain, $locale) // the loaders of the translation component as these currently simply discard // the extra information that is contained in these files - list($format, $file) = $files[$domain][$locale]; + [$format, $file] = $files[$domain][$locale]; $this->updater->updateTranslation( - $file, $format, $domain, $locale, $id, + $file->getPathname(), + $format, + $domain, + $locale, + $id, $request->request->get('message') ); diff --git a/Controller/TranslateController.php b/Controller/TranslateController.php index a031e89b..a28001d2 100644 --- a/Controller/TranslateController.php +++ b/Controller/TranslateController.php @@ -1,5 +1,7 @@ * @@ -22,14 +24,15 @@ use JMS\TranslationBundle\Translation\ConfigFactory; use JMS\TranslationBundle\Translation\LoaderManager; use JMS\TranslationBundle\Util\FileUtils; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; +use Twig\Environment; /** * Translate Controller. * - * @Route(service="jms_translation.controller.translate_controller") * @author Johannes M. Schmitt */ class TranslateController @@ -45,20 +48,20 @@ class TranslateController private $loader; /** - * @var string + * @var Environment|null */ - private $sourceLanguage; + private $twig; /** - * TranslateController constructor. - * - * @param ConfigFactory $configFactory - * @param LoaderManager $loader + * @var string */ - public function __construct(ConfigFactory $configFactory, LoaderManager $loader) + private $sourceLanguage; + + public function __construct(ConfigFactory $configFactory, LoaderManager $loader, ?Environment $twig = null) { $this->configFactory = $configFactory; $this->loader = $loader; + $this->twig = $twig; } /** @@ -70,11 +73,14 @@ public function setSourceLanguage($lang) } /** + * @param Request $request + * + * @return Response|array + * * @Route("/", name="jms_translation_index", options = {"i18n" = false}) * @Template("@JMSTranslation/Translate/index.html.twig") - * @param Request $request - * @return array */ + #[Route('/', name: 'jms_translation_index', options: ['i18n' => false])] public function indexAction(Request $request) { $configs = $this->configFactory->getNames(); @@ -112,7 +118,7 @@ public function indexAction(Request $request) // create alternative messages // TODO: We should probably also add these to the XLIFF file for external translators, // and the specification already supports it - $alternativeMessages = array(); + $alternativeMessages = []; foreach ($locales as $otherLocale) { if ($locale === $otherLocale) { continue; @@ -129,7 +135,7 @@ public function indexAction(Request $request) } } - $newMessages = $existingMessages = array(); + $newMessages = $existingMessages = []; foreach ($catalogue->getDomain($domain)->all() as $id => $message) { if ($message->isNew()) { $newMessages[$id] = $message; @@ -139,7 +145,7 @@ public function indexAction(Request $request) $existingMessages[$id] = $message; } - return array( + $variables = [ 'selectedConfig' => $config, 'configs' => $configs, 'selectedDomain' => $domain, @@ -150,9 +156,15 @@ public function indexAction(Request $request) 'newMessages' => $newMessages, 'existingMessages' => $existingMessages, 'alternativeMessages' => $alternativeMessages, - 'isWriteable' => is_writeable($files[$domain][$locale][1]), + 'isWriteable' => is_writable((string) $files[$domain][$locale][1]), 'file' => (string) $files[$domain][$locale][1], 'sourceLanguage' => $this->sourceLanguage, - ); + ]; + + if (null !== $this->twig) { + return new Response($this->twig->render('@JMSTranslation/Translate/index.html.twig', $variables)); + } + + return $variables; } } diff --git a/DependencyInjection/Compiler/IntegrationPass.php b/DependencyInjection/Compiler/IntegrationPass.php index 996dcb3b..99e5466a 100644 --- a/DependencyInjection/Compiler/IntegrationPass.php +++ b/DependencyInjection/Compiler/IntegrationPass.php @@ -1,5 +1,7 @@ * @@ -18,12 +20,12 @@ namespace JMS\TranslationBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; class IntegrationPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('translation.loader.xliff')) { return; diff --git a/DependencyInjection/Compiler/MountDumpersPass.php b/DependencyInjection/Compiler/MountDumpersPass.php index 9a382896..2ba046ee 100644 --- a/DependencyInjection/Compiler/MountDumpersPass.php +++ b/DependencyInjection/Compiler/MountDumpersPass.php @@ -1,5 +1,7 @@ * @@ -20,33 +22,28 @@ use JMS\TranslationBundle\Exception\RuntimeException; use Symfony\Component\DependencyInjection\ChildDefinition; -use Symfony\Component\DependencyInjection\DefinitionDecorator; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; class MountDumpersPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('jms_translation.file_writer')) { return; } - $dumpers = array(); + $dumpers = []; $i = 0; foreach ($container->findTaggedServiceIds('translation.dumper') as $id => $attr) { if (!isset($attr[0]['alias'])) { throw new RuntimeException(sprintf('The "alias" attribute must be set for tag "translation.dumper" for service "%s".', $id)); } - if (class_exists('Symfony\Component\DependencyInjection\ChildDefinition')) { - $def = new ChildDefinition('jms_translation.dumper.symfony_adapter'); - } else { - $def = new DefinitionDecorator('jms_translation.dumper.symfony_adapter'); - } + $def = new ChildDefinition('jms_translation.dumper.symfony_adapter'); $def->addArgument(new Reference($id))->addArgument($attr[0]['alias']); - $container->setDefinition($id = 'jms_translation.dumper.wrapped_symfony_dumper.'.($i++), $def); + $container->setDefinition($id = 'jms_translation.dumper.wrapped_symfony_dumper.' . ($i++), $def); $dumpers[$attr[0]['alias']] = new Reference($id); } @@ -61,7 +58,6 @@ public function process(ContainerBuilder $container) $container ->getDefinition('jms_translation.file_writer') - ->addArgument($dumpers) - ; + ->addArgument($dumpers); } } diff --git a/DependencyInjection/Compiler/MountExtractorsPass.php b/DependencyInjection/Compiler/MountExtractorsPass.php index 21656520..12deff22 100644 --- a/DependencyInjection/Compiler/MountExtractorsPass.php +++ b/DependencyInjection/Compiler/MountExtractorsPass.php @@ -1,5 +1,7 @@ * @@ -19,20 +21,20 @@ namespace JMS\TranslationBundle\DependencyInjection\Compiler; use JMS\TranslationBundle\Exception\RuntimeException; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; class MountExtractorsPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('jms_translation.extractor_manager')) { return; } $def = $container->getDefinition('jms_translation.extractor_manager'); - $extractors = array(); + $extractors = []; foreach ($container->findTaggedServiceIds('jms_translation.extractor') as $id => $attr) { if (!isset($attr[0]['alias'])) { throw new RuntimeException(sprintf('The "alias" attribute must be set for tag "jms_translation.extractor" of service "%s".', $id)); diff --git a/DependencyInjection/Compiler/MountFileVisitorsPass.php b/DependencyInjection/Compiler/MountFileVisitorsPass.php index 3226197c..805420b5 100644 --- a/DependencyInjection/Compiler/MountFileVisitorsPass.php +++ b/DependencyInjection/Compiler/MountFileVisitorsPass.php @@ -1,5 +1,7 @@ * @@ -18,20 +20,20 @@ namespace JMS\TranslationBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; class MountFileVisitorsPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition('jms_translation.extractor.file_extractor')) { return; } $def = $container->getDefinition('jms_translation.extractor.file_extractor'); - $visitors = array(); + $visitors = []; foreach ($container->findTaggedServiceIds('jms_translation.file_visitor') as $id => $attr) { $visitors[] = new Reference($id); } diff --git a/DependencyInjection/Compiler/MountLoadersPass.php b/DependencyInjection/Compiler/MountLoadersPass.php index 3791404c..bd8365fe 100644 --- a/DependencyInjection/Compiler/MountLoadersPass.php +++ b/DependencyInjection/Compiler/MountLoadersPass.php @@ -1,5 +1,7 @@ * @@ -20,33 +22,28 @@ use JMS\TranslationBundle\Exception\RuntimeException; use Symfony\Component\DependencyInjection\ChildDefinition; -use Symfony\Component\DependencyInjection\Reference; -use Symfony\Component\DependencyInjection\DefinitionDecorator; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; class MountLoadersPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { - if (!$container->hasDefinition(('jms_translation.loader_manager'))) { + if (!$container->hasDefinition('jms_translation.loader_manager')) { return; } - $loaders = array(); + $loaders = []; $i = 0; foreach ($container->findTaggedServiceIds('translation.loader') as $id => $attr) { if (!isset($attr[0]['alias'])) { throw new RuntimeException(sprintf('The attribute "alias" must be defined for tag "translation.loader" for service "%s".', $id)); } - if (class_exists('Symfony\Component\DependencyInjection\ChildDefinition')) { - $def = new ChildDefinition('jms_translation.loader.symfony_adapter'); - } else { - $def = new DefinitionDecorator('jms_translation.loader.symfony_adapter'); - } + $def = new ChildDefinition('jms_translation.loader.symfony_adapter'); $def->addArgument(new Reference($id)); - $container->setDefinition($id = 'jms_translation.loader.wrapped_symfony_loader.'.($i++), $def); + $container->setDefinition($id = 'jms_translation.loader.wrapped_symfony_loader.' . ($i++), $def); $loaders[$attr[0]['alias']] = new Reference($id); if (isset($attr[0]['legacy_alias'])) { @@ -64,7 +61,6 @@ public function process(ContainerBuilder $container) $container ->getDefinition('jms_translation.loader_manager') - ->addArgument($loaders) - ; + ->addArgument($loaders); } } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index ee3eceff..63e7c6b4 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -1,5 +1,7 @@ * @@ -19,8 +21,8 @@ namespace JMS\TranslationBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; class Configuration implements ConfigurationInterface { @@ -31,97 +33,102 @@ public function __construct(ContainerBuilder $container) $this->container = $container; } - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $c = $this->container; - $tb = new TreeBuilder(); - $tb - ->root('jms_translation') - ->fixXmlConfig('config') - ->children() - ->arrayNode('locales') - ->prototype('scalar')->end() + $tb = new TreeBuilder('jms_translation'); + // Keep compatibility with symfony/config < 4.2 + if (!method_exists($tb, 'getRootNode')) { + $rootNode = $tb->root('jms_translation'); + } else { + $rootNode = $tb->getRootNode(); + } + + $rootNode + ->fixXmlConfig('config') + ->children() + ->arrayNode('locales') + ->prototype('scalar')->end() + ->end() + ->arrayNode('dumper') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('add_date')->defaultTrue()->end() + ->booleanNode('add_references')->defaultTrue()->end() ->end() - ->arrayNode('dumper') - ->addDefaultsIfNotSet() + ->end() + ->scalarNode('source_language')->defaultValue('en')->end() + ->arrayNode('configs') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('dir', 'dirs') + ->fixXmlConfig('excluded_dir') + ->fixXmlConfig('excluded_name') + ->fixXmlConfig('ignore_domain') + ->fixXmlConfig('external_translations_dir') + ->fixXmlConfig('domain') + ->fixXmlConfig('extractor') ->children() - ->booleanNode('add_date')->defaultTrue()->end() - ->booleanNode('add_references')->defaultTrue()->end() - ->end() - ->end() - ->scalarNode('source_language')->defaultValue('en')->end() - ->arrayNode('configs') - ->useAttributeAsKey('name') - ->prototype('array') - ->fixXmlConfig('dir', 'dirs') - ->fixXmlConfig('excluded_dir') - ->fixXmlConfig('excluded_name') - ->fixXmlConfig('ignore_domain') - ->fixXmlConfig('external_translations_dir') - ->fixXmlConfig('domain') - ->fixXmlConfig('extractor') - ->children() - ->arrayNode('extractors') - ->prototype('scalar')->end() - ->end() - ->arrayNode('dirs') - ->requiresAtLeastOneElement() - ->prototype('scalar') - ->validate() - ->always(function ($v) use ($c) { - $v = str_replace(DIRECTORY_SEPARATOR, '/', $v); - - if ('@' === $v[0]) { - if (false === $pos = strpos($v, '/')) { - $bundleName = substr($v, 1); - } else { - $bundleName = substr($v, 1, $pos - 1); - } - - $bundles = $c->getParameter('kernel.bundles'); - if (!isset($bundles[$bundleName])) { - throw new \Exception(sprintf('The bundle "%s" does not exist. Available bundles: %s', $bundleName, implode(', ', array_keys($bundles)))); - } + ->arrayNode('extractors') + ->prototype('scalar')->end() + ->end() + ->arrayNode('dirs') + ->requiresAtLeastOneElement() + ->prototype('scalar') + ->validate() + ->always(static function ($v) use ($c) { + $v = str_replace(DIRECTORY_SEPARATOR, '/', $v); - $ref = new \ReflectionClass($bundles[$bundleName]); - $v = false === $pos ? dirname($ref->getFileName()) : dirname($ref->getFileName()).substr($v, $pos); + if ('@' === $v[0]) { + if (false === $pos = strpos($v, '/')) { + $bundleName = substr($v, 1); + } else { + $bundleName = substr($v, 1, $pos - 1); } - if (!is_dir($v)) { - throw new \Exception(sprintf('The directory "%s" does not exist.', $v)); + $bundles = $c->getParameter('kernel.bundles'); + if (!isset($bundles[$bundleName])) { + throw new \Exception(sprintf('The bundle "%s" does not exist. Available bundles: %s', $bundleName, implode(', ', array_keys($bundles)))); } - return $v; - }) - ->end() + $ref = new \ReflectionClass($bundles[$bundleName]); + $v = false === $pos ? dirname($ref->getFileName()) : dirname($ref->getFileName()) . substr($v, $pos); + } + + if (!is_dir($v)) { + throw new \Exception(sprintf('The directory "%s" does not exist.', $v)); + } + + return $v; + }) ->end() ->end() - ->arrayNode('excluded_dirs') - ->prototype('scalar')->end() - ->end() - ->arrayNode('excluded_names') - ->prototype('scalar')->end() - ->end() - ->arrayNode('external_translations_dirs') - ->prototype('scalar')->end() - ->end() - ->scalarNode('output_format')->end() - ->scalarNode('default_output_format')->end() - ->arrayNode('ignored_domains') - ->prototype('scalar')->end() - ->end() - ->arrayNode('domains') - ->prototype('scalar')->end() - ->end() - ->scalarNode('output_dir')->isRequired()->cannotBeEmpty()->end() - ->scalarNode('keep')->defaultValue(false)->end() ->end() + ->arrayNode('excluded_dirs') + ->prototype('scalar')->end() + ->end() + ->arrayNode('excluded_names') + ->prototype('scalar')->end() + ->end() + ->arrayNode('external_translations_dirs') + ->prototype('scalar')->end() + ->end() + ->scalarNode('output_format')->end() + ->scalarNode('default_output_format')->end() + ->scalarNode('intl_icu')->defaultValue(false)->end() + ->arrayNode('ignored_domains') + ->prototype('scalar')->end() + ->end() + ->arrayNode('domains') + ->prototype('scalar')->end() + ->end() + ->scalarNode('output_dir')->isRequired()->cannotBeEmpty()->end() + ->scalarNode('keep')->defaultValue(false)->end() ->end() ->end() ->end() - ->end() - ; + ->end(); return $tb; } diff --git a/DependencyInjection/JMSTranslationExtension.php b/DependencyInjection/JMSTranslationExtension.php index f1f375ea..faf4b713 100644 --- a/DependencyInjection/JMSTranslationExtension.php +++ b/DependencyInjection/JMSTranslationExtension.php @@ -1,5 +1,7 @@ * @@ -18,87 +20,89 @@ namespace JMS\TranslationBundle\DependencyInjection; -use Symfony\Component\DependencyInjection\Definition; +use JMS\TranslationBundle\Translation\ConfigBuilder; use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; class JMSTranslationExtension extends Extension { - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $config = $this->processConfiguration(new Configuration($container), $configs); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.xml'); - - if (!class_exists('Symfony\Component\ClassLoader\ClassLoader')) { - $loader->load('console.xml'); - } + $loader->load('console.xml'); $container->setParameter('jms_translation.source_language', $config['source_language']); $container->setParameter('jms_translation.locales', $config['locales']); foreach ($config['dumper'] as $option => $value) { - $container->setParameter("jms_translation.dumper.{$option}", $value); + $container->setParameter('jms_translation.dumper.' . $option, $value); } - $requests = array(); + $requests = []; foreach ($config['configs'] as $name => $extractConfig) { - $def = new Definition('JMS\TranslationBundle\Translation\ConfigBuilder'); - $def->addMethodCall('setTranslationsDir', array($extractConfig['output_dir'])); - $def->addMethodCall('setScanDirs', array($extractConfig['dirs'])); + $def = new Definition(ConfigBuilder::class); + $def->addMethodCall('setTranslationsDir', [$extractConfig['output_dir']]); + $def->addMethodCall('setScanDirs', [$extractConfig['dirs']]); if (isset($extractConfig['ignored_domains'])) { - $ignored = array(); + $ignored = []; foreach ($extractConfig['ignored_domains'] as $domain) { $ignored[$domain] = true; } - $def->addMethodCall('setIgnoredDomains', array($ignored)); + $def->addMethodCall('setIgnoredDomains', [$ignored]); } if (isset($extractConfig['domains'])) { - $domains = array(); + $domains = []; foreach ($extractConfig['domains'] as $domain) { $domains[$domain] = true; } - $def->addMethodCall('setDomains', array($domains)); + $def->addMethodCall('setDomains', [$domains]); } if (isset($extractConfig['extractors'])) { - $extractors = array(); + $extractors = []; foreach ($extractConfig['extractors'] as $alias) { $extractors[$alias] = true; } - $def->addMethodCall('setEnabledExtractors', array($extractors)); + $def->addMethodCall('setEnabledExtractors', [$extractors]); } if (isset($extractConfig['excluded_dirs'])) { - $def->addMethodCall('setExcludedDirs', array($extractConfig['excluded_dirs'])); + $def->addMethodCall('setExcludedDirs', [$extractConfig['excluded_dirs']]); } if (isset($extractConfig['excluded_names'])) { - $def->addMethodCall('setExcludedNames', array($extractConfig['excluded_names'])); + $def->addMethodCall('setExcludedNames', [$extractConfig['excluded_names']]); } if (isset($extractConfig['output_format'])) { - $def->addMethodCall('setOutputFormat', array($extractConfig['output_format'])); + $def->addMethodCall('setOutputFormat', [$extractConfig['output_format']]); } if (isset($extractConfig['default_output_format'])) { - $def->addMethodCall('setDefaultOutputFormat', array($extractConfig['default_output_format'])); + $def->addMethodCall('setDefaultOutputFormat', [$extractConfig['default_output_format']]); + } + + if (isset($extractConfig['intl_icu'])) { + $def->addMethodCall('setUseIcuMessageFormat', [$extractConfig['intl_icu']]); } if (isset($extractConfig['keep'])) { - $def->addMethodCall('setKeepOldTranslations', array($extractConfig['keep'])); + $def->addMethodCall('setKeepOldTranslations', [$extractConfig['keep']]); } if (isset($extractConfig['external_translations_dirs'])) { - $def->addMethodCall('setLoadResources', array($extractConfig['external_translations_dirs'])); + $def->addMethodCall('setLoadResources', [$extractConfig['external_translations_dirs']]); } $requests[$name] = $def; @@ -106,7 +110,6 @@ public function load(array $configs, ContainerBuilder $container) $container ->getDefinition('jms_translation.config_factory') - ->addArgument($requests) - ; + ->addArgument($requests); } } diff --git a/Exception/Exception.php b/Exception/Exception.php index 0991f7b9..535aeaf0 100644 --- a/Exception/Exception.php +++ b/Exception/Exception.php @@ -1,5 +1,7 @@ * diff --git a/Exception/InvalidArgumentException.php b/Exception/InvalidArgumentException.php index 04ef6cf9..c36fcd49 100644 --- a/Exception/InvalidArgumentException.php +++ b/Exception/InvalidArgumentException.php @@ -1,5 +1,7 @@ * diff --git a/Exception/RuntimeException.php b/Exception/RuntimeException.php index fcef20bf..65d172aa 100644 --- a/Exception/RuntimeException.php +++ b/Exception/RuntimeException.php @@ -1,5 +1,7 @@ * diff --git a/JMSTranslationBundle.php b/JMSTranslationBundle.php index 4349eec6..57094e95 100644 --- a/JMSTranslationBundle.php +++ b/JMSTranslationBundle.php @@ -30,7 +30,7 @@ class JMSTranslationBundle extends Bundle { const VERSION = '1.1.0-DEV'; - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { $container->addCompilerPass(new IntegrationPass()); $container->addCompilerPass(new MountFileVisitorsPass()); diff --git a/Logger/LoggerAwareInterface.php b/Logger/LoggerAwareInterface.php index 82e7715c..37513ad5 100644 --- a/Logger/LoggerAwareInterface.php +++ b/Logger/LoggerAwareInterface.php @@ -1,5 +1,7 @@ * @@ -25,8 +27,5 @@ */ interface LoggerAwareInterface { - /** - * @param LoggerInterface $logger - */ public function setLogger(LoggerInterface $logger); } diff --git a/Logger/OutputLogger.php b/Logger/OutputLogger.php index 83edd6a4..484bab9e 100644 --- a/Logger/OutputLogger.php +++ b/Logger/OutputLogger.php @@ -1,5 +1,7 @@ * @@ -18,20 +20,20 @@ namespace JMS\TranslationBundle\Logger; -use Symfony\Component\Console\Output\OutputInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Output\OutputInterface; class OutputLogger implements LoggerInterface { - const EMERG = 1; - const ALERT = 2; - const CRIT = 4; - const ERR = 8; - const WARN = 16; - const NOTICE = 32; - const INFO = 64; - const DEBUG = 128; - const ALL = 255; + public const EMERG = 1; + public const ALERT = 2; + public const CRIT = 4; + public const ERR = 8; + public const WARN = 16; + public const NOTICE = 32; + public const INFO = 64; + public const DEBUG = 128; + public const ALL = 255; /** * @var OutputInterface @@ -43,10 +45,6 @@ class OutputLogger implements LoggerInterface */ private $level = self::ALL; - /** - * OutputLogger constructor. - * @param OutputInterface $output - */ public function __construct(OutputInterface $output) { $this->output = $output; @@ -63,9 +61,10 @@ public function setLevel($level) /** * @param string $message * @param array $context + * * @return void */ - public function emergency($message, array $context = array()) + public function emergency($message, array $context = []) { $this->emerg($message, $context); } @@ -73,37 +72,40 @@ public function emergency($message, array $context = array()) /** * @param string $message * @param array $context + * * @return void */ - public function emerg($message, array $context = array()) + public function emerg($message, array $context = []) { if (0 === ($this->level & self::EMERG)) { return; } - $this->output->writeln(''.$message.''); + $this->output->writeln('' . $message . ''); } /** * @param string $message * @param array $context + * * @return void */ - public function alert($message, array $context = array()) + public function alert($message, array $context = []) { if (0 === ($this->level & self::ALERT)) { return; } - $this->output->writeln(''.$message.''); + $this->output->writeln('' . $message . ''); } /** * @param string $message * @param array $context + * * @return void */ - public function critical($message, array $context = array()) + public function critical($message, array $context = []) { $this->crit($message, $context); } @@ -111,23 +113,25 @@ public function critical($message, array $context = array()) /** * @param string $message * @param array $context + * * @return void */ - public function crit($message, array $context = array()) + public function crit($message, array $context = []) { if (0 === ($this->level & self::CRIT)) { return; } - $this->output->writeln(''.$message.''); + $this->output->writeln('' . $message . ''); } /** * @param string $message * @param array $context + * * @return void */ - public function error($message, array $context = array()) + public function error($message, array $context = []) { $this->err($message, $context); } @@ -135,33 +139,36 @@ public function error($message, array $context = array()) /** * @param string $message * @param array $context + * * @return void */ - public function err($message, array $context = array()) + public function err($message, array $context = []) { if (0 === ($this->level & self::ERR)) { return; } - $this->output->writeln(''.$message.''); + $this->output->writeln('' . $message . ''); } /** * @param string $message * @param array $context + * * @return void */ - public function warning($message, array $context = array()) + public function warning($message, array $context = []) { $this->warn($message, $context); } /** - * @param $message + * @param string $message * @param array $context + * * @return void */ - public function warn($message, array $context = array()) + public function warn($message, array $context = []) { if (0 === ($this->level & self::WARN)) { return; @@ -173,9 +180,10 @@ public function warn($message, array $context = array()) /** * @param string $message * @param array $context + * * @return void */ - public function notice($message, array $context = array()) + public function notice($message, array $context = []) { if (0 === ($this->level & self::NOTICE)) { return; @@ -187,9 +195,10 @@ public function notice($message, array $context = array()) /** * @param string $message * @param array $context + * * @return void */ - public function info($message, array $context = array()) + public function info($message, array $context = []) { if (0 === ($this->level & self::INFO)) { return; @@ -201,9 +210,10 @@ public function info($message, array $context = array()) /** * @param string $message * @param array $context + * * @return void */ - public function debug($message, array $context = array()) + public function debug($message, array $context = []) { if (0 === ($this->level & self::DEBUG)) { return; @@ -216,9 +226,10 @@ public function debug($message, array $context = array()) * @param mixed $level * @param string $message * @param array $context + * * @return void */ - public function log($level, $message, array $context = array()) + public function log($level, $message, array $context = []) { if (0 === ($this->level & $level)) { return; diff --git a/Model/FileSource.php b/Model/FileSource.php index c6c29258..11448a24 100644 --- a/Model/FileSource.php +++ b/Model/FileSource.php @@ -1,5 +1,7 @@ * @@ -36,10 +38,9 @@ class FileSource implements SourceInterface private $column; /** - * FileSource constructor. - * @param $path string - * @param null|int $line - * @param null|int $column + * @param string $path + * @param int|null $line + * @param int|null $column */ public function __construct($path, $line = null, $column = null) { @@ -57,7 +58,7 @@ public function getPath() } /** - * @return null|int + * @return int|null */ public function getLine() { @@ -65,7 +66,7 @@ public function getLine() } /** - * @return null|int + * @return int|null */ public function getColumn() { @@ -74,6 +75,7 @@ public function getColumn() /** * @param SourceInterface $source + * * @return bool */ public function equals(SourceInterface $source) @@ -90,11 +92,7 @@ public function equals(SourceInterface $source) return false; } - if ($this->column !== $source->getColumn()) { - return false; - } - - return true; + return $this->column === $source->getColumn(); } /** @@ -105,10 +103,10 @@ public function __toString() $str = $this->path; if (null !== $this->line) { - $str .= ' on line '.$this->line; + $str .= ' on line ' . $this->line; if (null !== $this->column) { - $str .= ' at column '.$this->column; + $str .= ' at column ' . $this->column; } } diff --git a/Model/Message.php b/Model/Message.php index c3b1276c..4e97cafc 100644 --- a/Model/Message.php +++ b/Model/Message.php @@ -1,5 +1,7 @@ * @@ -70,23 +72,23 @@ class Message * * @var array */ - private $sources = array(); + private $sources = []; /** - * @static + * @deprecated Will be removed in 2.0. Use the FileSourceFactory * - * @param $id + * @param string $id * @param string $domain * * @return Message * - * @deprecated Will be removed in 2.0. Use the FileSourceFactory + * @static */ public static function forThisFile($id, $domain = 'messages') { $message = new static($id, $domain); - $trace = debug_backtrace(false); + $trace = debug_backtrace(0); if (isset($trace[0]['file'])) { $message->addSource(new FileSource($trace[0]['file'])); } @@ -95,12 +97,12 @@ public static function forThisFile($id, $domain = 'messages') } /** - * @static - * - * @param $id + * @param string $id * @param string $domain * - * @return Message + * @return static + * + * @static */ public static function create($id, $domain = 'messages') { @@ -168,7 +170,7 @@ public function isNew() */ public function getLocaleString() { - return $this->localeString !== null ? $this->localeString : ($this->new ? ($this->desc !== null ? $this->desc : $this->id) : ''); + return $this->localeString ?? ($this->new ? ($this->desc ?? $this->id) : ''); } /** @@ -256,7 +258,7 @@ public function setLocaleString($str) return $this; } - public function setSources(array $sources = array()) + public function setSources(array $sources = []) { $this->sources = $sources; @@ -318,9 +320,9 @@ public function merge(Message $message) * Do not use this if you want to merge a message from an extracted catalogue. * In these cases, use merge() instead. * - * @param Message $message - * * @deprecated not in use atm + * + * @param Message $message */ public function mergeExisting(Message $message) { @@ -351,9 +353,9 @@ public function mergeExisting(Message $message) * The result of both methods is the same, except that the result will end up in the existing message, * instead of the scanned message, so extra information read from the existing message is not discarded. * - * @param Message $message - * * @author Dieter Peeters + * + * @param Message $message */ public function mergeScanned(Message $message) { @@ -369,7 +371,7 @@ public function mergeScanned(Message $message) $this->desc = $message->getDesc(); } - $this->sources = array(); + $this->sources = []; foreach ($message->getSources() as $source) { $this->addSource($source); } diff --git a/Model/Message/XliffMessage.php b/Model/Message/XliffMessage.php index c6b9f855..d272d19b 100644 --- a/Model/Message/XliffMessage.php +++ b/Model/Message/XliffMessage.php @@ -1,5 +1,7 @@ * @@ -23,34 +25,34 @@ /** * Represents an _existing_ message from an XLIFF-file. - * + * * Currently supports preservation of: * - note-elements (as child of trans-unit element) * - attribute trans-unit['approved'] * - attribute target['state'] - * - * @see http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html * * @author Dieter Peeters + * + * @see http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html */ class XliffMessage extends Message { - protected static $states = array(); - const STATE_NONE = null; - const STATE_FINAL = 'final'; - const STATE_NEEDS_ADAPTATION = 'needs-adaptation'; - const STATE_NEEDS_L10N = 'needs-l10n'; - const STATE_NEEDS_REVIEW_ADAPTATION = 'needs-review-adaptation'; - const STATE_NEEDS_REVIEW_L10N = 'needs-review-l10n'; - const STATE_NEEDS_REVIEW_TRANSLATION = 'needs-review-translation'; - const STATE_NEEDS_TRANSLATION = 'needs-translation'; - const STATE_NEW = 'new'; - const STATE_SIGNED_OFF = 'signed-off'; - const STATE_TRANSLATED = 'translated'; + protected static $states = []; + public const STATE_NONE = null; + public const STATE_FINAL = 'final'; + public const STATE_NEEDS_ADAPTATION = 'needs-adaptation'; + public const STATE_NEEDS_L10N = 'needs-l10n'; + public const STATE_NEEDS_REVIEW_ADAPTATION = 'needs-review-adaptation'; + public const STATE_NEEDS_REVIEW_L10N = 'needs-review-l10n'; + public const STATE_NEEDS_REVIEW_TRANSLATION = 'needs-review-translation'; + public const STATE_NEEDS_TRANSLATION = 'needs-translation'; + public const STATE_NEW = 'new'; + public const STATE_SIGNED_OFF = 'signed-off'; + public const STATE_TRANSLATED = 'translated'; protected $approved = false; protected $state; - protected $notes = array(); + protected $notes = []; /** * @return bool @@ -61,7 +63,7 @@ public function isApproved() } /** - * @param $approved + * @param bool $approved * * @return $this */ @@ -151,16 +153,16 @@ public function getNotes() } /** - * @param $text + * @param string $text * @param null $from * * @return $this */ public function addNote($text, $from = null) { - $note = array( + $note = [ 'text' => (string) $text, - ); + ]; if (isset($from)) { $note['from'] = (string) $from; } @@ -174,7 +176,7 @@ public function addNote($text, $from = null) * * @return $this */ - public function setNotes(array $notes = array()) + public function setNotes(array $notes = []) { $this->notes = $notes; diff --git a/Model/Message/XliffMessageState.php b/Model/Message/XliffMessageState.php index f0bef7cc..51599053 100755 --- a/Model/Message/XliffMessageState.php +++ b/Model/Message/XliffMessageState.php @@ -1,5 +1,7 @@ * @@ -27,15 +29,15 @@ */ class XliffMessageState extends Message { - const STATE_NONE = null; - const STATE_FINAL = 'final'; - const STATE_NEEDS_ADAPTATION = 'needs-adaptation'; - const STATE_NEEDS_L10N = 'needs-l10n'; - const STATE_NEEDS_REVIEW_ADAPTATION = 'needs-review-adaptation'; - const STATE_NEEDS_REVIEW_L10N = 'needs-review-l10n'; - const STATE_NEEDS_REVIEW_TRANSLATION = 'needs-review-translation'; - const STATE_NEEDS_TRANSLATION = 'needs-translation'; - const STATE_NEW = 'new'; - const STATE_SIGNED_OFF = 'signed-off'; - const STATE_TRANSLATED = 'translated'; + public const STATE_NONE = null; + public const STATE_FINAL = 'final'; + public const STATE_NEEDS_ADAPTATION = 'needs-adaptation'; + public const STATE_NEEDS_L10N = 'needs-l10n'; + public const STATE_NEEDS_REVIEW_ADAPTATION = 'needs-review-adaptation'; + public const STATE_NEEDS_REVIEW_L10N = 'needs-review-l10n'; + public const STATE_NEEDS_REVIEW_TRANSLATION = 'needs-review-translation'; + public const STATE_NEEDS_TRANSLATION = 'needs-translation'; + public const STATE_NEW = 'new'; + public const STATE_SIGNED_OFF = 'signed-off'; + public const STATE_TRANSLATED = 'translated'; } diff --git a/Model/MessageCatalogue.php b/Model/MessageCatalogue.php index 7f6a3ce7..d2bdeeb9 100644 --- a/Model/MessageCatalogue.php +++ b/Model/MessageCatalogue.php @@ -1,5 +1,7 @@ * @@ -42,11 +44,8 @@ class MessageCatalogue /** * @var array */ - private $domains = array(); + private $domains = []; - /** - * @param $locale - */ public function setLocale($locale) { $this->locale = $locale; @@ -60,33 +59,27 @@ public function getLocale() return $this->locale; } - /** - * @param Message $message - */ public function add(Message $message) { $this ->getOrCreateDomain($message->getDomain()) - ->add($message) - ; + ->add($message); } - /** - * @param Message $message - */ public function set(Message $message, $force = false) { $this ->getOrCreateDomain($message->getDomain()) - ->set($message, $force) - ; + ->set($message, $force); } /** - * @param $id - * @param $domain - * @throws \JMS\TranslationBundle\Exception\InvalidArgumentException + * @param string $id + * @param string $domain + * * @return Message + * + * @throws InvalidArgumentException */ public function get($id, $domain = 'messages') { @@ -95,6 +88,7 @@ public function get($id, $domain = 'messages') /** * @param Message $message + * * @return bool */ public function has(Message $message) @@ -106,9 +100,6 @@ public function has(Message $message) return $this->getDomain($message->getDomain())->has($message->getId()); } - /** - * @param MessageCatalogue $catalogue - */ public function merge(MessageCatalogue $catalogue) { foreach ($catalogue->getDomains() as $name => $domainCatalogue) { @@ -118,6 +109,7 @@ public function merge(MessageCatalogue $catalogue) /** * @param string $domain + * * @return bool */ public function hasDomain($domain) @@ -127,6 +119,7 @@ public function hasDomain($domain) /** * @param string $domain + * * @return MessageCollection */ public function getDomain($domain) @@ -148,6 +141,7 @@ public function getDomains() /** * @param string $domain + * * @return MessageCollection */ private function getOrCreateDomain($domain) diff --git a/Model/MessageCollection.php b/Model/MessageCollection.php index d412418e..907a244b 100644 --- a/Model/MessageCollection.php +++ b/Model/MessageCollection.php @@ -1,5 +1,7 @@ * @@ -39,11 +41,8 @@ class MessageCollection /** * @var array */ - private $messages = array(); + private $messages = []; - /** - * @param MessageCatalogue $catalogue - */ public function setCatalogue(MessageCatalogue $catalogue) { $this->catalogue = $catalogue; @@ -57,9 +56,6 @@ public function getCatalogue() return $this->catalogue; } - /** - * @param Message $message - */ public function add(Message $message) { if (isset($this->messages[$id = $message->getId()])) { @@ -72,9 +68,6 @@ public function add(Message $message) $this->messages[$id] = $message; } - /** - * @param Message $message - */ public function set(Message $message, $force = false) { $id = $message->getId(); @@ -86,9 +79,11 @@ public function set(Message $message, $force = false) } /** - * @param $id + * @param string $id + * * @return mixed - * @throws \JMS\TranslationBundle\Exception\InvalidArgumentException + * + * @throws InvalidArgumentException */ public function get($id) { @@ -100,8 +95,9 @@ public function get($id) } /** - * @param $id - * @return Boolean + * @param string $id + * + * @return bool */ public function has($id) { @@ -109,8 +105,9 @@ public function has($id) } /** - * @param $callback - * @throws \JMS\TranslationBundle\Exception\InvalidArgumentException + * @param callable $callback + * + * @throws InvalidArgumentException */ public function sort($callback) { @@ -122,8 +119,9 @@ public function sort($callback) } /** - * @param $callback - * @throws \JMS\TranslationBundle\Exception\InvalidArgumentException + * @param callable $callback + * + * @throws InvalidArgumentException */ public function filter($callback) { @@ -150,9 +148,6 @@ public function all() return $this->messages; } - /** - * @param MessageCollection $domain - */ public function merge(MessageCollection $domain) { foreach ($domain->all() as $id => $message) { @@ -160,16 +155,12 @@ public function merge(MessageCollection $domain) } } - /** - * @param Message $oldMessage - * @param Message $newMessage - */ private function checkConsistency(Message $oldMessage, Message $newMessage) { $oldDesc = $oldMessage->getDesc(); $newDesc = $newMessage->getDesc(); - if (0 < strlen($oldDesc) && 0 < strlen($newDesc) && $oldDesc != $newDesc) { + if (0 < strlen((string) $oldDesc) && 0 < strlen((string) $newDesc) && $oldDesc !== $newDesc) { throw new \RuntimeException(sprintf("The message '%s' exists with two different descs: '%s' in %s, and '%s' in %s", $oldMessage->getId(), $oldMessage->getDesc(), current($oldMessage->getSources()), $newMessage->getDesc(), current($newMessage->getSources()))); } } diff --git a/Model/SourceInterface.php b/Model/SourceInterface.php index ac926739..1a7a8d62 100644 --- a/Model/SourceInterface.php +++ b/Model/SourceInterface.php @@ -1,5 +1,7 @@ * @@ -21,5 +23,6 @@ interface SourceInterface { public function equals(SourceInterface $source); + public function __toString(); } diff --git a/Resources/.gitattributes b/Resources/.gitattributes new file mode 100644 index 00000000..70038ee5 --- /dev/null +++ b/Resources/.gitattributes @@ -0,0 +1,2 @@ +/.gitattributes export-ignore +/docs export-ignore diff --git a/Resources/config/console.xml b/Resources/config/console.xml index c4a09f45..bcbcab01 100644 --- a/Resources/config/console.xml +++ b/Resources/config/console.xml @@ -6,11 +6,17 @@ - + + + + %jms_translation.locales% - + + %kernel.project_dir% + %kernel.bundles% + container.hasParameter('kernel.root_dir') ? parameter('kernel.root_dir') : null diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 485278f6..e57de45b 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -41,15 +41,20 @@ + %jms_translation.source_language% + + + + @@ -61,7 +66,8 @@ - %kernel.root_dir% + container.hasParameter('kernel.root_dir') ? parameter('kernel.root_dir') : parameter('kernel.project_dir') + %kernel.project_dir% diff --git a/Resources/doc/cookbook/config_reference.rst b/Resources/doc/cookbook/config_reference.rst index 8abaceab..23a0ebc2 100644 --- a/Resources/doc/cookbook/config_reference.rst +++ b/Resources/doc/cookbook/config_reference.rst @@ -14,10 +14,10 @@ On this page you will find all available configuration options and their meaning # Create a configuration named "app" app: # List of directories we should extract translations keys from - dirs: ["%kernel.root_dir%", "%kernel.root_dir%/../src"] + dirs: ["%kernel.project_dir%/src", "%kernel.project_dir%/templates"] # Where to write the translation files - output_dir: "%kernel.root_dir%/Resources/translations" + output_dir: "%kernel.project_dir%/translations" # Whitelist domains domains: ["messages"] @@ -44,6 +44,11 @@ On this page you will find all available configuration options and their meaning # The default output format (defaults to xlf) default_output_format: "xlf" + # Whether or not to use the ICU message format. This causes + # translation files to be suffixed with +intl-icu, e.g. + # messages+intl-icu.en.yaml + intl_icu: false + # If true, we will never remove messages from the translation files. # If false, the translation files are up to date with the source. - keep: false \ No newline at end of file + keep: false diff --git a/Resources/doc/cookbook/extraction_configs.rst b/Resources/doc/cookbook/extraction_configs.rst index bf0db8de..a94f48dc 100644 --- a/Resources/doc/cookbook/extraction_configs.rst +++ b/Resources/doc/cookbook/extraction_configs.rst @@ -10,8 +10,8 @@ also set-up some pre-defined settings via the configuration: jms_translation: configs: app: - dirs: ["%kernel.root_dir%", "%kernel.root_dir%/../src"] - output_dir: "%kernel.root_dir%/Resources/translations" + dirs: ["%kernel.project_dir%/templates", "%kernel.project_dir%/src"] + output_dir: "%kernel.project_dir%/translations" ignored_domains: [routes] excluded_names: ["*TestCase.php", "*Test.php"] excluded_dirs: [cache, data, logs] @@ -26,7 +26,7 @@ You can then run the extraction process with this configuration with the followi .. code-block :: bash php app/console translation:extract de --config=app - + The ``--config`` option also supports overriding via command-line options. Let's assume that you would like to change the output format that has been defined in the config, but leave all other settings the same, you would run: diff --git a/Resources/public/js/jms.js b/Resources/public/js/jms.js index 976db53c..dd4e81a3 100644 --- a/Resources/public/js/jms.js +++ b/Resources/public/js/jms.js @@ -211,11 +211,22 @@ function JMSTranslationManager(updateMessagePath, isWritable) $(JMS.messageFilter.selector).val(window.location.hash.substr(1)); }, filter: function () { - var filterString = $(JMS.messageFilter.selector).val().trim().replace(/[-[\]{}()+?,\\^$|#\s]/g, "\\$&"); + var filter = $(JMS.messageFilter.selector); + var messageRow = $(".messageRow"); + + if (filter.length === 0) { + messageRow.each(function () { + $(this).show(); + }); + + return; + } + + var filterString = filter.val().trim().replace(/[-[\]{}()+?,\\^$|#\s]/g, "\\$&"); var regExp = new RegExp(".*" + filterString + ".*", "i"); window.location.hash = filterString; if (filterString !== "") { - $(".messageRow").each(function () { + messageRow.each(function () { var id = this.id.substr(4); if (id.match(regExp)) { $(this).show(); @@ -223,12 +234,12 @@ function JMSTranslationManager(updateMessagePath, isWritable) $(this).hide(); } }); - - } else { - $(".messageRow").each(function () { - $(this).show(); - }); + return ; } + + messageRow.each(function () { + $(this).show(); + }); } }; }; diff --git a/Resources/views/Translate/index.html.twig b/Resources/views/Translate/index.html.twig index c3982276..8afcb92b 100644 --- a/Resources/views/Translate/index.html.twig +++ b/Resources/views/Translate/index.html.twig @@ -1,4 +1,4 @@ -{% extends "JMSTranslationBundle::base.html.twig" %} +{% extends "@JMSTranslation/base.html.twig" %} {% block javascripts %} {{ parent() }} @@ -58,12 +58,12 @@ {% if newMessages is not empty %}

New Messages

- {% include "JMSTranslationBundle:Translate:messages.html.twig" with {"messages": newMessages} %} + {% include "@JMSTranslation/Translate/messages.html.twig" with {"messages": newMessages} %} {% endif %} {% if existingMessages is not empty %}

Existing Messages

- {% include "JMSTranslationBundle:Translate:messages.html.twig" with {"messages": existingMessages} %} + {% include "@JMSTranslation/Translate/messages.html.twig" with {"messages": existingMessages} %} {% endif %} {% endblock %} diff --git a/Tests/BaseTestCase.php b/Tests/BaseTestCase.php deleted file mode 100644 index ba5a2e24..00000000 --- a/Tests/BaseTestCase.php +++ /dev/null @@ -1,27 +0,0 @@ -getMockBuilder($class) - ->disableOriginalConstructor() - ->disableOriginalClone() - ->disableArgumentCloning() - ->getMock(); - } -} diff --git a/Tests/DependencyInjection/Compiler/MountDumpersPassTest.php b/Tests/DependencyInjection/Compiler/MountDumpersPassTest.php index 807093d4..69eca9ea 100644 --- a/Tests/DependencyInjection/Compiler/MountDumpersPassTest.php +++ b/Tests/DependencyInjection/Compiler/MountDumpersPassTest.php @@ -1,5 +1,7 @@ addCompilerPass(new MountDumpersPass()); } @@ -21,13 +23,13 @@ protected function registerCompilerPass(ContainerBuilder $container) /** * @test */ - public function if_compiler_pass_collects_services_by_argument_these_will_exist() + public function ifCompilerPassCollectsServicesByArgumentTheseWillExist() { $collectingService = new Definition(); $this->setDefinition('jms_translation.file_writer', $collectingService); $collectedService = new Definition(); - $collectedService->addTag('jms_translation.dumper', array('format' => 'foo')); + $collectedService->addTag('jms_translation.dumper', ['format' => 'foo']); $this->setDefinition('service0', $collectedService); $this->compile(); @@ -35,7 +37,7 @@ public function if_compiler_pass_collects_services_by_argument_these_will_exist( $this->assertContainerBuilderHasServiceDefinitionWithArgument( 'jms_translation.file_writer', 0, - array('foo' => new Reference('service0')) + ['foo' => new Reference('service0')] ); } } diff --git a/Tests/DependencyInjection/Compiler/MountExtractorsPassTest.php b/Tests/DependencyInjection/Compiler/MountExtractorsPassTest.php index ca215725..ee0c80b1 100644 --- a/Tests/DependencyInjection/Compiler/MountExtractorsPassTest.php +++ b/Tests/DependencyInjection/Compiler/MountExtractorsPassTest.php @@ -1,5 +1,7 @@ addCompilerPass(new MountExtractorsPass()); } @@ -21,13 +23,13 @@ protected function registerCompilerPass(ContainerBuilder $container) /** * @test */ - public function if_compiler_pass_collects_services_by_argument_these_will_exist() + public function ifCompilerPassCollectsServicesByArgumentTheseWillExist() { $collectingService = new Definition(); $this->setDefinition('jms_translation.extractor_manager', $collectingService); $collectedService = new Definition(); - $collectedService->addTag('jms_translation.extractor', array('alias' => 'foo')); + $collectedService->addTag('jms_translation.extractor', ['alias' => 'foo']); $this->setDefinition('service0', $collectedService); $this->compile(); @@ -35,7 +37,7 @@ public function if_compiler_pass_collects_services_by_argument_these_will_exist( $this->assertContainerBuilderHasServiceDefinitionWithArgument( 'jms_translation.extractor_manager', 0, - array('foo' => new Reference('service0')) + ['foo' => new Reference('service0')] ); } } diff --git a/Tests/DependencyInjection/Compiler/MountFileVisitorsPassTest.php b/Tests/DependencyInjection/Compiler/MountFileVisitorsPassTest.php index 1379a6a6..398600c5 100644 --- a/Tests/DependencyInjection/Compiler/MountFileVisitorsPassTest.php +++ b/Tests/DependencyInjection/Compiler/MountFileVisitorsPassTest.php @@ -1,5 +1,7 @@ addCompilerPass(new MountFileVisitorsPass()); } @@ -21,7 +23,7 @@ protected function registerCompilerPass(ContainerBuilder $container) /** * @test */ - public function if_compiler_pass_collects_services_by_argument_these_will_exist() + public function ifCompilerPassCollectsServicesByArgumentTheseWillExist() { $collectingService = new Definition(); $this->setDefinition('jms_translation.extractor.file_extractor', $collectingService); @@ -35,7 +37,7 @@ public function if_compiler_pass_collects_services_by_argument_these_will_exist( $this->assertContainerBuilderHasServiceDefinitionWithArgument( 'jms_translation.extractor.file_extractor', 0, - array(new Reference('service0')) + [new Reference('service0')] ); } } diff --git a/Tests/DependencyInjection/Compiler/MountLoadersPassTest.php b/Tests/DependencyInjection/Compiler/MountLoadersPassTest.php index f704dfb9..be5a1770 100644 --- a/Tests/DependencyInjection/Compiler/MountLoadersPassTest.php +++ b/Tests/DependencyInjection/Compiler/MountLoadersPassTest.php @@ -1,5 +1,7 @@ addCompilerPass(new MountLoadersPass()); } @@ -21,13 +23,13 @@ protected function registerCompilerPass(ContainerBuilder $container) /** * @test */ - public function if_compiler_pass_collects_services_by_argument_these_will_exist() + public function ifCompilerPassCollectsServicesByArgumentTheseWillExist() { $collectingService = new Definition(); $this->setDefinition('jms_translation.loader_manager', $collectingService); $collectedService = new Definition(); - $collectedService->addTag('jms_translation.loader', array('format' => 'foo')); + $collectedService->addTag('jms_translation.loader', ['format' => 'foo']); $this->setDefinition('service0', $collectedService); $this->compile(); @@ -35,7 +37,7 @@ public function if_compiler_pass_collects_services_by_argument_these_will_exist( $this->assertContainerBuilderHasServiceDefinitionWithArgument( 'jms_translation.loader_manager', 0, - array('foo' => new Reference('service0')) + ['foo' => new Reference('service0')] ); } } diff --git a/Tests/DependencyInjection/JMSTranslationExtensionTest.php b/Tests/DependencyInjection/JMSTranslationExtensionTest.php index e51572aa..1c29edbd 100644 --- a/Tests/DependencyInjection/JMSTranslationExtensionTest.php +++ b/Tests/DependencyInjection/JMSTranslationExtensionTest.php @@ -1,5 +1,7 @@ load(); @@ -30,10 +32,10 @@ public function default_parameters_after_loading() /** * @test */ - public function basic_parameters_after_loading() + public function basicParametersAfterLoading() { - $locales = array('en', 'fr', 'es'); - $this->load(array('source_language' => 'fr', 'locales' => $locales)); + $locales = ['en', 'fr', 'es']; + $this->load(['source_language' => 'fr', 'locales' => $locales]); $this->assertContainerBuilderHasParameter('jms_translation.source_language', 'fr'); $this->assertContainerBuilderHasParameter('jms_translation.locales', $locales); diff --git a/Tests/Functional/AppKernel.php b/Tests/Functional/AppKernel.php index 28d08bae..23d51052 100644 --- a/Tests/Functional/AppKernel.php +++ b/Tests/Functional/AppKernel.php @@ -1,5 +1,7 @@ * @@ -19,49 +21,80 @@ namespace JMS\TranslationBundle\Tests\Functional; use JMS\TranslationBundle\Exception\RuntimeException; -use Symfony\Component\Filesystem\Filesystem; +use JMS\TranslationBundle\JMSTranslationBundle; +use JMS\TranslationBundle\Tests\Functional\Fixture\TestBundle\TestBundle; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\TwigBundle\TwigBundle; use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Kernel; class AppKernel extends Kernel { private $config; - public function __construct($config) + private $fwConfig; + + public function __construct(string $fwConfig, ?string $config) { parent::__construct('test', true); $fs = new Filesystem(); - if (!$fs->isAbsolutePath($config)) { - $config = __DIR__.'/config/'.$config; - } + if ($config) { + if (!$fs->isAbsolutePath($config)) { + $config = __DIR__ . '/config/' . $config; + } - if (!file_exists($config)) { - throw new RuntimeException(sprintf('The config file "%s" does not exist.', $config)); + if (!file_exists($config)) { + throw new RuntimeException(sprintf('The config file "%s" does not exist.', $config)); + } } - $this->config = $config; + + if (!$fs->isAbsolutePath($fwConfig)) { + $fwConfig = __DIR__ . '/config/' . $fwConfig; + } + $this->fwConfig = $fwConfig; } - public function registerBundles() + public function registerBundles(): iterable { - return array( - new \JMS\TranslationBundle\Tests\Functional\Fixture\TestBundle\TestBundle(), - new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new \Symfony\Bundle\TwigBundle\TwigBundle(), - new \JMS\TranslationBundle\JMSTranslationBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), - ); + return [ + new TestBundle(), + new FrameworkBundle(), + new TwigBundle(), + new JMSTranslationBundle(), + new SensioFrameworkExtraBundle(), + ]; } public function registerContainerConfiguration(LoaderInterface $loader) { - $loader->load($this->config); + $loader->load($this->fwConfig); + if ($this->config) { + $loader->load($this->config); + } + } + + public function getCacheDir(): string + { + return $this->getBaseDir() . '/cache'; + } + + public function getLogDir(): string + { + return $this->getBaseDir() . '/logs'; + } + + public function getProjectDir(): string + { + return __DIR__; } - public function getCacheDir() + private function getBaseDir(): string { - return sys_get_temp_dir().'/JMSTranslationBundle'; + return sys_get_temp_dir() . '/JMSTranslationBundle'; } public function serialize() diff --git a/Tests/Functional/BaseTestCase.php b/Tests/Functional/BaseTestCase.php index 90c9c03d..84b13800 100644 --- a/Tests/Functional/BaseTestCase.php +++ b/Tests/Functional/BaseTestCase.php @@ -1,5 +1,7 @@ * @@ -19,13 +21,25 @@ namespace JMS\TranslationBundle\Tests\Functional; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelInterface; class BaseTestCase extends WebTestCase { - protected static function createKernel(array $options = array()) + protected static function createKernel(array $options = []): KernelInterface { - return new AppKernel( - isset($options['config']) ? $options['config'] : 'default.yml' - ); + $isSf5 = version_compare(Kernel::VERSION, '5.0.0') >= 0; + + $default = $isSf5 ? 'default_sf5.yml' : 'default.yml'; + + if (version_compare(Kernel::VERSION, '6.0.0') >= 0) { + $conf = 'framework_sf6.yml'; + } elseif (version_compare(Kernel::VERSION, '5.0.0') >= 0) { + $conf = 'framework.yml'; + } else { + $conf = 'framework.yml'; + } + + return new AppKernel($conf, $options['config'] ?? $default); } } diff --git a/Tests/Functional/Command/BaseCommandTestCase.php b/Tests/Functional/Command/BaseCommandTestCase.php index 6a038a4a..1cbf33bf 100644 --- a/Tests/Functional/Command/BaseCommandTestCase.php +++ b/Tests/Functional/Command/BaseCommandTestCase.php @@ -1,5 +1,7 @@ * @@ -23,7 +25,7 @@ abstract class BaseCommandTestCase extends BaseTestCase { - protected function getApp(array $options = array()) + protected function getApp(array $options = []) { $kernel = $this->createKernel($options); $kernel->boot(); diff --git a/Tests/Functional/Command/ExtractCommandTest.php b/Tests/Functional/Command/ExtractCommandTest.php index 469a9528..46f54687 100644 --- a/Tests/Functional/Command/ExtractCommandTest.php +++ b/Tests/Functional/Command/ExtractCommandTest.php @@ -1,5 +1,7 @@ * @@ -18,39 +20,36 @@ namespace JMS\TranslationBundle\Tests\Functional\Command; -use JMS\TranslationBundle\Command\ExtractTranslationCommand; use JMS\TranslationBundle\Util\FileUtils; -use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Input\ArgvInput; class ExtractCommandTest extends BaseCommandTestCase { public function testExtract() { - $input = new ArgvInput(array( + $input = new ArgvInput([ 'app/console', - 'translation:extract', + 'jms:translation:extract', 'en', - '--dir='.($inputDir = __DIR__.'/../../Translation/Extractor/Fixture/SimpleTest'), - '--output-dir='.($outputDir = sys_get_temp_dir().'/'.uniqid('extract')) - )); + '--dir=' . $inputDir = __DIR__ . '/../../Translation/Extractor/Fixture/SimpleTest', + '--output-dir=' . ($outputDir = sys_get_temp_dir() . '/' . uniqid('extract')), + ]); $expectedOutput = - 'Extracting Translations for locale en'."\n" - .'Keep old translations: No'."\n" - .'Output-Path: '.$outputDir."\n" - .'Directories: '.$inputDir."\n" - .'Excluded Directories: Tests'."\n" - .'Excluded Names: *Test.php, *TestCase.php'."\n" - .'Output-Format: # whatever is present, if nothing then xlf #'."\n" - .'Custom Extractors: # none #'."\n" - .'============================================================'."\n" - .'Loading catalogues from "'.$outputDir.'"'."\n" - .'Extracting translation keys'."\n" - .'Extracting messages from directory : '.$inputDir."\n" - .'Writing translation file "'.$outputDir.'/messages.en.xlf".'."\n" - .'done!'."\n" - ; + 'Extracting Translations for locale en' . "\n" + . 'Keep old translations: No' . "\n" + . 'Output-Path: ' . $outputDir . "\n" + . 'Directories: ' . $inputDir . "\n" + . 'Excluded Directories: Tests' . "\n" + . 'Excluded Names: *Test.php, *TestCase.php' . "\n" + . 'Output-Format: # whatever is present, if nothing then xlf #' . "\n" + . 'Custom Extractors: # none #' . "\n" + . '============================================================' . "\n" + . 'Loading catalogues from "' . $outputDir . '"' . "\n" + . 'Extracting translation keys' . "\n" + . 'Extracting messages from directory : ' . $inputDir . "\n" + . 'Writing translation file "' . $outputDir . '/messages.en.xlf".' . "\n" + . 'done!' . "\n"; $this->getApp()->run($input, $output = new Output()); $this->assertEquals($expectedOutput, $output->getContent()); @@ -58,20 +57,20 @@ public function testExtract() $files = FileUtils::findTranslationFiles($outputDir); $this->assertTrue(isset($files['messages']['en'])); } - + public function testExtractDryRun() { - $input = new ArgvInput(array( + $input = new ArgvInput([ 'app/console', - 'translation:extract', + 'jms:translation:extract', 'en', - '--dir='.($inputDir = __DIR__.'/../../Translation/Extractor/Fixture/SimpleTest'), - '--output-dir='.($outputDir = sys_get_temp_dir().'/'.uniqid('extract')), + '--dir=' . $inputDir = __DIR__ . '/../../Translation/Extractor/Fixture/SimpleTest', + '--output-dir=' . ($outputDir = sys_get_temp_dir() . '/' . uniqid('extract')), '--dry-run', - '--verbose' - )); + '--verbose', + ]); - $expectedOutput = array( + $expectedOutput = [ 'php.foo->', 'php.bar-> Bar', 'php.baz->', @@ -83,12 +82,12 @@ public function testExtractDryRun() 'form.foo->', 'form.bar->', 'controller.foo-> Foo', - ); + ]; $this->getApp()->run($input, $output = new Output()); - + foreach ($expectedOutput as $transID) { - $this->assertContains($transID, $output->getContent()); + $this->assertStringContainsString($transID, $output->getContent()); } } } diff --git a/Tests/Functional/Command/Output.php b/Tests/Functional/Command/Output.php index 561aebc8..75466966 100644 --- a/Tests/Functional/Command/Output.php +++ b/Tests/Functional/Command/Output.php @@ -1,5 +1,7 @@ * @@ -28,9 +30,11 @@ public function doWrite($content, $newline) { $this->content .= $content; - if ($newline) { - $this->content .= "\n"; + if (! $newline) { + return; } + + $this->content .= "\n"; } public function getContent() diff --git a/Tests/Functional/Command/ResourcesListCommandTest.php b/Tests/Functional/Command/ResourcesListCommandTest.php new file mode 100644 index 00000000..64104e15 --- /dev/null +++ b/Tests/Functional/Command/ResourcesListCommandTest.php @@ -0,0 +1,59 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\TranslationBundle\Tests\Functional\Command; + +use Symfony\Component\Console\Input\ArgvInput; + +final class ResourcesListCommandTest extends BaseCommandTestCase +{ + public function testList(): void + { + $input = new ArgvInput([ + 'app/console', + 'jms:translation:list-resources', + ]); + + $expectedOutput = + 'Directories list :' . "\n" + . ' - %kernel.root_dir%/Fixture/TestBundle/Resources/translations' . "\n" + . 'done!' . "\n"; + + $this->getApp()->run($input, $output = new Output()); + $this->assertEquals($expectedOutput, $output->getContent()); + } + + public function testListFiles(): void + { + $input = new ArgvInput([ + 'app/console', + 'jms:translation:list-resources', + '--files', + ]); + + $expectedOutput = + 'Resources list :' . "\n" + . ' - %kernel.project_dir%/Fixture/TestBundle/Resources/translations/messages.en.php' . "\n" + . 'done!' . "\n"; + + $this->getApp()->run($input, $output = new Output()); + $this->assertEquals($expectedOutput, $output->getContent()); + } +} diff --git a/Tests/Functional/Controller/ApiControllerTest.php b/Tests/Functional/Controller/ApiControllerTest.php index 85aa6522..d63c0a37 100644 --- a/Tests/Functional/Controller/ApiControllerTest.php +++ b/Tests/Functional/Controller/ApiControllerTest.php @@ -1,10 +1,12 @@ @@ -13,34 +15,33 @@ class ApiControllerTest extends BaseTestCase { public function testUpdateAction() { - $isSf4 = version_compare(Kernel::VERSION,'4.0.0') >= 0; + // Start application + $client = static::createClient(['config' => 'test_updating_translations.yml']); + $outputDir = $client->getContainer()->getParameter('translation_output_dir'); + + $isSf4 = version_compare(Kernel::VERSION, '4.0.0') >= 0; // Add a file - $file = $isSf4 ? __DIR__.'/../Fixture/TestBundle/Resources/translations/navigation.en.yaml' : __DIR__.'/../Fixture/TestBundle/Resources/translations/navigation.en.yml'; + $file = $isSf4 ? $outputDir . '/navigation.en.yaml' : $outputDir . '/navigation.en.yml'; $written = file_put_contents($file, 'main.home: Home'); $this->assertTrue($written !== false && $written > 0); - // Start application - $client = static::createClient(); - $client->request('POST', '/_trans/api/configs/app/domains/navigation/locales/en/messages?id=main.home', array('_method'=>'PUT', 'message'=>'Away')); -if($client->getResponse()->getStatusCode() !== 200) { - file_put_contents('/tmp/test-update.html',$client->getResponse()); -} + $client->request('POST', '/_trans/api/configs/app/domains/navigation/locales/en/messages?id=main.home', ['_method' => 'PUT', 'message' => 'Away']); + + $fileContent = is_file($file) ? file_get_contents($file) : ''; + unlink($file); $this->assertEquals(200, $client->getResponse()->getStatusCode()); // Verify that the file has new content - $array = Yaml::parse(file_get_contents($file)); + $array = Yaml::parse($fileContent); if ($isSf4) { - $this->assertTrue(isset($array['main.home']),print_r($array,true)); + $this->assertTrue(isset($array['main.home']), print_r($array, true)); $this->assertEquals('Away', $array['main.home']); } else { $this->assertTrue(isset($array['main'])); $this->assertTrue(isset($array['main']['home'])); $this->assertEquals('Away', $array['main']['home']); } - - // Remove the file - unlink($file); } } diff --git a/Tests/Functional/Controller/TranslateControllerTest.php b/Tests/Functional/Controller/TranslateControllerTest.php index 4401e203..67302fbb 100644 --- a/Tests/Functional/Controller/TranslateControllerTest.php +++ b/Tests/Functional/Controller/TranslateControllerTest.php @@ -1,5 +1,7 @@ request('GET', '/_trans/'); -if($client->getResponse()->getStatusCode() !== 200) { -file_put_contents('/tmp/test-index.html',$client->getResponse()); -} - $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertGreaterThan( 0, diff --git a/Tests/Functional/Fixture/TestBundle/Controller/AppleController.php b/Tests/Functional/Fixture/TestBundle/Controller/AppleController.php index b0843e79..ea7af4d4 100644 --- a/Tests/Functional/Fixture/TestBundle/Controller/AppleController.php +++ b/Tests/Functional/Fixture/TestBundle/Controller/AppleController.php @@ -1,13 +1,16 @@ 5); + return ['nbApples' => 5]; + } + + /** + * @Route("/view_sf5") + * @Template("@Test/Apple/view_sf5.html.twig") + */ + public function viewsf5Action() + { + return ['nbApples' => 5]; } } diff --git a/Tests/Functional/Fixture/TestBundle/Resources/views/Apple/view.html.twig b/Tests/Functional/Fixture/TestBundle/Resources/views/Apple/view.html.twig index fcb7592e..3eb6f30b 100644 --- a/Tests/Functional/Fixture/TestBundle/Resources/views/Apple/view.html.twig +++ b/Tests/Functional/Fixture/TestBundle/Resources/views/Apple/view.html.twig @@ -4,4 +4,4 @@ {# This translation does exist in our messages.en.yml file #} {{ "text.apples_remaining"|transchoice(nbApples) - |desc("Some default message which should never be applied because it instead is coming from the messages file.") }} \ No newline at end of file + |desc("Some default message which should never be applied because it instead is coming from the messages file.") }} diff --git a/Tests/Functional/Fixture/TestBundle/Resources/views/Apple/view_sf5.html.twig b/Tests/Functional/Fixture/TestBundle/Resources/views/Apple/view_sf5.html.twig new file mode 100644 index 00000000..8d947968 --- /dev/null +++ b/Tests/Functional/Fixture/TestBundle/Resources/views/Apple/view_sf5.html.twig @@ -0,0 +1,2 @@ +{{ "text.apples_remaining"|trans({'%count%': nbApples}) + |desc("Some default message which should never be applied because it instead is coming from the messages file.") }} diff --git a/Tests/Functional/Fixture/TestBundle/TestBundle.php b/Tests/Functional/Fixture/TestBundle/TestBundle.php index 44a98d9e..04954d04 100644 --- a/Tests/Functional/Fixture/TestBundle/TestBundle.php +++ b/Tests/Functional/Fixture/TestBundle/TestBundle.php @@ -1,5 +1,7 @@ = 0; + + $url = $isSf5 ? '/apples/view_sf5' : '/apples/view'; $client = $this->createClient(); - $client->request('GET', '/apples/view'); - $response = $client->getResponse(); + $client->request('GET', $url); + $response = $client->getResponse(); - $this->assertEquals(200, $response->getStatusCode(), substr($response, 0, 2000)); - $this->assertEquals("There are 5 apples\n\nThere are 5 apples", $response->getContent()); + $this->assertEquals(200, $response->getStatusCode(), $response->getContent()); + $expected = $isSf5 ? "There are 5 apples\n" : "There are 5 apples\n\nThere are 5 apples\n"; + $this->assertEquals($expected, $response->getContent()); } } diff --git a/Tests/Functional/config/bundle.yml b/Tests/Functional/config/bundle.yml index 1ae6f7aa..89f43d10 100644 --- a/Tests/Functional/config/bundle.yml +++ b/Tests/Functional/config/bundle.yml @@ -2,9 +2,9 @@ jms_translation: configs: app: dirs: - - "%kernel.root_dir%" - - "%kernel.root_dir%/Fixture/TestBundle" - output_dir: "%kernel.root_dir%/Fixture/TestBundle/Resources/translations" + - "%kernel.project_dir%" + - "%kernel.project_dir%/Fixture/TestBundle" + output_dir: "%translation_output_dir%" ignored_domains: [routes] excluded_names: ["*TestCase.php", "*Test.php"] excluded_dirs: [cache, data, logs] diff --git a/Tests/Functional/config/default.yml b/Tests/Functional/config/default.yml index 06d2fdd2..58aeb703 100644 --- a/Tests/Functional/config/default.yml +++ b/Tests/Functional/config/default.yml @@ -3,6 +3,13 @@ imports: - { resource: twig.yml } - { resource: bundle.yml } +framework: + templating: + engines: [twig, php] + +parameters: + translation_output_dir: "%kernel.project_dir%/Fixture/TestBundle/Resources/translations" + services: logger: - class: Psr\Log\NullLogger \ No newline at end of file + class: Psr\Log\NullLogger diff --git a/Tests/Functional/config/default_sf5.yml b/Tests/Functional/config/default_sf5.yml new file mode 100644 index 00000000..b0fbfefc --- /dev/null +++ b/Tests/Functional/config/default_sf5.yml @@ -0,0 +1,10 @@ +imports: + - { resource: twig.yml } + - { resource: bundle.yml } + +parameters: + translation_output_dir: "%kernel.project_dir%/Fixture/TestBundle/Resources/translations" + +services: + logger: + class: Psr\Log\NullLogger diff --git a/Tests/Functional/config/framework.yml b/Tests/Functional/config/framework.yml index 131f2437..4d136549 100644 --- a/Tests/Functional/config/framework.yml +++ b/Tests/Functional/config/framework.yml @@ -1,14 +1,16 @@ framework: secret: test test: ~ + assets: ~ session: storage_id: session.storage.mock_file form: true csrf_protection: true - validation: + annotations: true + property_access: true + validation: enabled: true - enable_annotations: true translator: enabled: true router: - resource: "%kernel.root_dir%/config/routing.yml" \ No newline at end of file + resource: "%kernel.project_dir%/config/routing.yml" diff --git a/Tests/Functional/config/framework_sf6.yml b/Tests/Functional/config/framework_sf6.yml new file mode 100644 index 00000000..59349537 --- /dev/null +++ b/Tests/Functional/config/framework_sf6.yml @@ -0,0 +1,16 @@ +framework: + secret: test + test: ~ + assets: ~ + session: + storage_factory_id: session.storage.factory.mock_file + form: true + csrf_protection: true + annotations: true + property_access: true + validation: + enabled: true + translator: + enabled: true + router: + resource: "%kernel.project_dir%/config/routing.yml" diff --git a/Tests/Functional/config/routing.yml b/Tests/Functional/config/routing.yml index 5c249b41..7e829169 100644 --- a/Tests/Functional/config/routing.yml +++ b/Tests/Functional/config/routing.yml @@ -5,4 +5,4 @@ JMSTranslationBundle_ui: TestBundle: type: annotation - resource: "@TestBundle/Controller/" \ No newline at end of file + resource: "@TestBundle/Controller/" diff --git a/Tests/Functional/config/test_updating_translations.yml b/Tests/Functional/config/test_updating_translations.yml new file mode 100644 index 00000000..6fb7d25b --- /dev/null +++ b/Tests/Functional/config/test_updating_translations.yml @@ -0,0 +1,3 @@ +parameters: + translation_output_dir: "%kernel.cache_dir%/translations" + diff --git a/Tests/Functional/config/twig.yml b/Tests/Functional/config/twig.yml index a6f61d03..ec8a7859 100644 --- a/Tests/Functional/config/twig.yml +++ b/Tests/Functional/config/twig.yml @@ -1,7 +1,3 @@ -framework: - templating: - engines: [twig, php] - twig: debug: "%kernel.debug%" strict_variables: "%kernel.debug%" diff --git a/Tests/Model/FileSourceTest.php b/Tests/Model/FileSourceTest.php index bf7a651e..bd6b2776 100644 --- a/Tests/Model/FileSourceTest.php +++ b/Tests/Model/FileSourceTest.php @@ -1,5 +1,7 @@ * @@ -19,9 +21,10 @@ namespace JMS\TranslationBundle\Tests\Model; use JMS\TranslationBundle\Model\FileSource; -use JMS\TranslationBundle\Tests\BaseTestCase; +use JMS\TranslationBundle\Model\SourceInterface; +use PHPUnit\Framework\TestCase; -class FileSourceTest extends BaseTestCase +class FileSourceTest extends TestCase { public function testGetPath() { @@ -64,55 +67,54 @@ public function testEquals($r1, $r2, $expected) public function getEqualityTests() { - $tests = array(); + $tests = []; - $tests[] = array( + $tests[] = [ new FileSource('foo'), new FileSource('foo'), true, - ); + ]; - $tests[] = array( + $tests[] = [ new FileSource('foo'), new FileSource('bar'), false, - ); + ]; - $tests[] = array( + $tests[] = [ new FileSource('foo', 1), new FileSource('foo', 1), true, - ); + ]; - $tests[] = array( + $tests[] = [ new FileSource('foo', 1), new FileSource('foo', 2), false, - ); + ]; - $tests[] = array( + $tests[] = [ new FileSource('foo', 1, 2), new FileSource('foo', 1, 2), true, - ); + ]; - $tests[] = array( + $tests[] = [ new FileSource('foo', 1, 2), new FileSource('foo', 1, 3), false, - ); + ]; - $source = $this->createMock('JMS\TranslationBundle\Model\SourceInterface'); + $source = $this->createMock(SourceInterface::class); $source ->expects($this->once()) ->method('equals') - ->will($this->returnValue(false)) - ; - $tests[] = array( + ->willReturn(false); + $tests[] = [ new FileSource('foo'), $source, false, - ); + ]; return $tests; } @@ -127,13 +129,13 @@ public function testToString($r, $expected) public function getToStringTests() { - $tests = array(); + $tests = []; - $tests[] = array(new FileSource('foo/bar'), 'foo/bar'); - $tests[] = array(new FileSource('foo/bar', 1), 'foo/bar on line 1'); - $tests[] = array(new FileSource('foo/bar', null, 2), 'foo/bar'); - $tests[] = array(new FileSource('foo/bar', 1, 2), 'foo/bar on line 1 at column 2'); - $tests[] = array(new FileSource('a/b/c/foo/bar'), 'a/b/c/foo/bar'); + $tests[] = [new FileSource('foo/bar'), 'foo/bar']; + $tests[] = [new FileSource('foo/bar', 1), 'foo/bar on line 1']; + $tests[] = [new FileSource('foo/bar', null, 2), 'foo/bar']; + $tests[] = [new FileSource('foo/bar', 1, 2), 'foo/bar on line 1 at column 2']; + $tests[] = [new FileSource('a/b/c/foo/bar'), 'a/b/c/foo/bar']; return $tests; } diff --git a/Tests/Model/Message/XliffMessageTest.php b/Tests/Model/Message/XliffMessageTest.php index fd99928b..12c5060d 100644 --- a/Tests/Model/Message/XliffMessageTest.php +++ b/Tests/Model/Message/XliffMessageTest.php @@ -1,5 +1,7 @@ * @@ -21,6 +23,7 @@ use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\Message\XliffMessage; +use JMS\TranslationBundle\Model\SourceInterface; use JMS\TranslationBundle\Tests\Model\MessageTest; class XliffMessageTest extends MessageTest @@ -92,12 +95,12 @@ public function testisWritable() public function testGetNotes() { $message = new XliffMessage('foo'); - $this->assertEquals(array(), $message->getNotes()); + $this->assertEquals([], $message->getNotes()); $this->assertSame($message, $message->addNote('foo')); - $this->assertSame(array(array('text' => 'foo')), $message->getNotes()); + $this->assertSame([['text' => 'foo']], $message->getNotes()); $this->assertSame($message, $message->addNote('bar', 'foo')); - $this->assertSame(array(array('text' => 'foo'), array('text' => 'bar', 'from' => 'foo')), $message->getNotes()); - $this->assertSame($message, $message->setNotes($notes = array(array('text' => 'foo', 'from' => 'bar')))); + $this->assertSame([['text' => 'foo'], ['text' => 'bar', 'from' => 'foo']], $message->getNotes()); + $this->assertSame($message, $message->setNotes($notes = [['text' => 'foo', 'from' => 'bar']])); $this->assertSame($notes, $message->getNotes()); } @@ -106,13 +109,13 @@ public function testMerge() $messageWrite = new XliffMessage('foo'); $messageWrite->setDesc('foo'); $messageWrite->setMeaning('foo'); - $messageWrite->addSource($s1 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface')); + $messageWrite->addSource($s1 = $this->createMock(SourceInterface::class)); $messageRead = new XliffMessage('foo'); $messageRead->setDesc('bar'); $messageRead->setApproved(true); $messageRead->setState(XliffMessage::STATE_TRANSLATED); - $messageRead->addSource($s2 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface')); + $messageRead->addSource($s2 = $this->createMock(SourceInterface::class)); $messageRead2 = new Message('foo'); $messageRead2->setDesc('bar'); @@ -123,7 +126,7 @@ public function testMerge() $message1->merge($message2); $this->assertEquals('bar', $message1->getDesc()); $this->assertEquals('foo', $message1->getMeaning()); - $this->assertSame(array($s1, $s2), $message1->getSources()); + $this->assertSame([$s1, $s2], $message1->getSources()); $this->assertTrue($message1->isApproved()); $this->assertEquals(XliffMessage::STATE_TRANSLATED, $message1->getState()); @@ -143,7 +146,7 @@ public function testMerge() $message5->merge($message6); $this->assertEquals('bar', $message5->getDesc()); $this->assertEquals('foo', $message5->getMeaning()); - $this->assertSame(array($s1, $s2), $message5->getSources()); + $this->assertSame([$s1, $s2], $message5->getSources()); $this->assertFalse($message5->isApproved()); $this->assertEquals(XliffMessage::STATE_NEW, $message5->getState()); @@ -173,13 +176,13 @@ public function testMergeExisting() $existingMessage2->setNew(false); $existingMessage2->addSource(new FileSource('bar')); - $scannedMessage1 = clone $scannedMessage; + $scannedMessage1 = clone $scannedMessage; $existingMessage1 = clone $existingMessage; $scannedMessage1->mergeExisting($existingMessage1); $this->assertEquals('foo', $scannedMessage1->getDesc()); $this->assertEquals('bar', $scannedMessage1->getLocaleString()); $this->assertFalse($scannedMessage1->isNew()); - $this->assertEquals(array(), $scannedMessage1->getSources()); + $this->assertEquals([], $scannedMessage1->getSources()); $this->assertFalse($scannedMessage1->isApproved()); $this->assertEquals(XliffMessage::STATE_NONE, $scannedMessage1->getState()); @@ -195,13 +198,13 @@ public function testMergeExisting() $this->assertTrue($scannedMessage2->isApproved()); $this->assertEquals(XliffMessage::STATE_TRANSLATED, $scannedMessage2->getState()); - $scannedMessage3 = clone $scannedMessage; + $scannedMessage3 = clone $scannedMessage; $existingMessage3 = clone $existingMessage1; $scannedMessage3->mergeExisting($existingMessage3); $this->assertEquals('foo', $scannedMessage3->getDesc()); $this->assertEquals('bar', $scannedMessage3->getLocaleString()); $this->assertFalse($scannedMessage3->isNew()); - $this->assertEquals(array(), $scannedMessage3->getSources()); + $this->assertEquals([], $scannedMessage3->getSources()); $this->assertFalse($scannedMessage3->isApproved()); $this->assertEquals(XliffMessage::STATE_NONE, $scannedMessage3->getState()); @@ -233,12 +236,12 @@ public function testMergeScanned() $scannedMessage1->setDesc('foo'); $existingMessage1 = clone $existingMessage; - $scannedMessage1 = clone $scannedMessage; + $scannedMessage1 = clone $scannedMessage; $existingMessage1->mergeScanned($scannedMessage1); $this->assertEquals('foo', $existingMessage1->getDesc()); $this->assertEquals('bar', $existingMessage1->getLocaleString()); $this->assertFalse($existingMessage1->isNew()); - $this->assertEquals(array(), $existingMessage1->getSources()); + $this->assertEquals([], $existingMessage1->getSources()); $this->assertTrue($existingMessage1->isApproved()); $this->assertEquals(XliffMessage::STATE_TRANSLATED, $existingMessage1->getState()); @@ -253,17 +256,17 @@ public function testMergeScanned() $existingMessage2->mergeScanned($scannedMessage2); $this->assertEquals('bar', $existingMessage2->getDesc()); $this->assertFalse($existingMessage2->isNew()); - $this->assertEquals(array(), $existingMessage2->getSources()); + $this->assertEquals([], $existingMessage2->getSources()); $this->assertTrue($existingMessage2->isApproved()); $this->assertEquals(XliffMessage::STATE_TRANSLATED, $existingMessage2->getState()); $existingMessage3 = clone $existingMessage; - $scannedMessage3 = clone $scannedMessage1; + $scannedMessage3 = clone $scannedMessage1; $existingMessage3->mergeScanned($scannedMessage3); $this->assertEquals('foo', $existingMessage3->getDesc()); $this->assertEquals('bar', $existingMessage3->getLocaleString()); $this->assertFalse($existingMessage3->isNew()); - $this->assertEquals(array(), $existingMessage3->getSources()); + $this->assertEquals([], $existingMessage3->getSources()); $this->assertTrue($existingMessage3->isApproved()); $this->assertEquals(XliffMessage::STATE_TRANSLATED, $existingMessage3->getState()); @@ -275,7 +278,7 @@ public function testMergeScanned() $scannedMessage4->setDesc('foo'); $existingMessage4->mergeScanned($scannedMessage4); $this->assertEquals('bar', $existingMessage4->getDesc()); - $this->assertEquals(array(), $existingMessage4->getSources()); + $this->assertEquals([], $existingMessage4->getSources()); $this->assertTrue($existingMessage4->isApproved()); $this->assertEquals(XliffMessage::STATE_TRANSLATED, $existingMessage4->getState()); } diff --git a/Tests/Model/MessageCatalogueTest.php b/Tests/Model/MessageCatalogueTest.php index 80be484a..9cdf629e 100644 --- a/Tests/Model/MessageCatalogueTest.php +++ b/Tests/Model/MessageCatalogueTest.php @@ -1,5 +1,7 @@ * @@ -20,8 +22,10 @@ use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Model\MessageCollection; +use PHPUnit\Framework\TestCase; -class MessageCatalogueTest extends \PHPUnit_Framework_TestCase +class MessageCatalogueTest extends TestCase { public function testAdd() { @@ -29,7 +33,7 @@ public function testAdd() $catalogue->add($m = new Message('foo')); $this->assertTrue($catalogue->hasDomain('messages')); - $this->assertEquals(array('foo' => $m), $catalogue->getDomain('messages')->all()); + $this->assertEquals(['foo' => $m], $catalogue->getDomain('messages')->all()); } public function testGet() @@ -41,11 +45,10 @@ public function testGet() $this->assertSame($message, $catalogue->get('foo')); } - /** - * @expectedException \InvalidArgumentException - */ public function testGetThrowsExceptionWhenMessageDoesNotExist() { + $this->expectException(\InvalidArgumentException::class); + $catalogue = new MessageCatalogue(); $catalogue->getDomain('foo'); } @@ -74,15 +77,14 @@ public function testGetDomain() $catalogue->add(new Message('foo')); $col = $catalogue->getDomain('messages'); - $this->assertInstanceOf('JMS\TranslationBundle\Model\MessageCollection', $col); - $this->assertEquals(array('foo'), array_keys($col->all())); + $this->assertInstanceOf(MessageCollection::class, $col); + $this->assertEquals(['foo'], array_keys($col->all())); } - /** - * @expectedException \InvalidArgumentException - */ public function testGetDomainWhenDomainDoesNotExist() { + $this->expectException(\InvalidArgumentException::class); + $catalogue = new MessageCatalogue(); $catalogue->getDomain('messages'); } @@ -93,8 +95,8 @@ public function testGetDomains() $cat->add(new Message('foo')); $cat->add(new Message('foo', 'foo')); - $this->assertEquals(array('messages', 'foo'), array_keys($domains = $cat->getDomains())); - $this->assertInstanceOf('JMS\TranslationBundle\Model\MessageCollection', $domains['foo']); + $this->assertEquals(['messages', 'foo'], array_keys($domains = $cat->getDomains())); + $this->assertInstanceOf(MessageCollection::class, $domains['foo']); } public function testMerge() @@ -107,10 +109,10 @@ public function testMerge() $cat->merge($cat2); - $this->assertEquals(array('foo', 'bar'), array_keys($domains = $cat->getDomains())); - $this->assertEquals(array('bar'), array_keys($cat2->getDomains())); + $this->assertEquals(['foo', 'bar'], array_keys($domains = $cat->getDomains())); + $this->assertEquals(['bar'], array_keys($cat2->getDomains())); - $this->assertEquals(array('foo'), array_keys($domains['foo']->all())); - $this->assertEquals(array('foo'), array_keys($domains['bar']->all())); + $this->assertEquals(['foo'], array_keys($domains['foo']->all())); + $this->assertEquals(['foo'], array_keys($domains['bar']->all())); } } diff --git a/Tests/Model/MessageCollectionTest.php b/Tests/Model/MessageCollectionTest.php index b078e1b5..39e15c3f 100644 --- a/Tests/Model/MessageCollectionTest.php +++ b/Tests/Model/MessageCollectionTest.php @@ -1,5 +1,7 @@ * @@ -18,26 +20,26 @@ namespace JMS\TranslationBundle\Tests\Model; +use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCollection; -use JMS\TranslationBundle\Model\FileSource; -use JMS\TranslationBundle\Tests\BaseTestCase; +use PHPUnit\Framework\TestCase; -class MessageCollectionTest extends BaseTestCase +class MessageCollectionTest extends TestCase { public function testAdd() { $domain = new MessageCollection(); $domain->add($m = new Message('foo')); - $this->assertSame(array('foo' => $m), $domain->all()); + $this->assertSame(['foo' => $m], $domain->all()); } public function testAddMerges() { - $m2 = $this->createMock('JMS\TranslationBundle\Model\Message'); + $m2 = $this->createMock(Message::class); - $m1 = $this->createMock('JMS\TranslationBundle\Model\Message'); + $m1 = $this->createMock(Message::class); $m1->expects($this->once()) ->method('merge') ->with($m2); @@ -56,11 +58,10 @@ public function testGet() $this->assertSame($message, $domain->get('foo')); } - /** - * @expectedException \InvalidArgumentException - */ public function testGetThrowsExceptionWhenMessageDoesNotExist() { + $this->expectException(\InvalidArgumentException::class); + $catalogue = new MessageCollection(); $catalogue->get('foo'); } @@ -76,17 +77,17 @@ public function testSet() public function testSetDoesNotMerge() { - $m2 = $this->createMock('JMS\TranslationBundle\Model\Message'); - $m2->expects($this->any()) + $m2 = $this->createMock(Message::class); + $m2 ->method('getId') - ->will($this->returnValue('foo')); + ->willReturn('foo'); - $m1 = $this->createMock('JMS\TranslationBundle\Model\Message'); + $m1 = $this->createMock(Message::class); $m1->expects($this->never()) ->method('merge'); - $m1->expects($this->any()) + $m1 ->method('getId') - ->will($this->returnValue('foo')); + ->willReturn('foo'); $col = new MessageCollection(); $col->set($m1); @@ -102,10 +103,10 @@ public function testSort() $col->add(new Message('c')); $col->add(new Message('a')); - $this->assertEquals(array('b', 'c', 'a'), array_keys($col->all())); + $this->assertEquals(['b', 'c', 'a'], array_keys($col->all())); $col->sort('strcasecmp'); - $this->assertEquals(array('a', 'b', 'c'), array_keys($col->all())); + $this->assertEquals(['a', 'b', 'c'], array_keys($col->all())); } public function testFilter() @@ -114,9 +115,11 @@ public function testFilter() $col->add($m = new Message('a')); $col->add(new Message('b')); $col->add(new Message('c')); - $col->filter(function ($v) { return 'a' === $v->getId(); }); + $col->filter(static function ($v) { + return $v->getId() === 'a'; + }); - $this->assertEquals(array('a'), array_keys($col->all())); + $this->assertEquals(['a'], array_keys($col->all())); $this->assertSame($m, $col->get('a')); } @@ -129,15 +132,14 @@ public function testMerge() $col2->add(new Message('b')); $col->merge($col2); - $this->assertEquals(array('a', 'b'), array_keys($col->all())); + $this->assertEquals(['a', 'b'], array_keys($col->all())); } - /** - * @expectedException RuntimeException - * @expectedExceptionMessage The message 'a' exists with two different descs: 'a' in foo on line 1, and 'b' in bar on line 2 - */ public function testAddChecksConsistency() { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The message \'a\' exists with two different descs: \'a\' in foo on line 1, and \'b\' in bar on line 2'); + $col = new MessageCollection(); $msg = new Message('a'); @@ -152,13 +154,16 @@ public function testAddChecksConsistency() $col->add($msg2); } + /** + * @doesNotPerformAssertions + */ public function testAddChecksConsistencyButAllowsEmptyDescs() { $col = new MessageCollection(); // both message have not desc - $msg = new Message('a'); + $msg = new Message('a'); $msg2 = new Message('a'); $col->add($msg); @@ -196,12 +201,11 @@ public function testAddChecksConsistencyButAllowsEmptyDescs() $col->add($msg2); } - /** - * @expectedException RuntimeException - * @expectedExceptionMessage The message 'a' exists with two different descs: 'a' in foo on line 1, and 'b' in bar on line 2 - */ public function testSetChecksConsistency() { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The message \'a\' exists with two different descs: \'a\' in foo on line 1, and \'b\' in bar on line 2'); + $col = new MessageCollection(); $msg = new Message('a'); @@ -216,13 +220,16 @@ public function testSetChecksConsistency() $col->set($msg2); } + /** + * @doesNotPerformAssertions + */ public function testSetChecksConsistencyButAllowsEmptyDescs() { $col = new MessageCollection(); // both message have not desc - $msg = new Message('a'); + $msg = new Message('a'); $msg2 = new Message('a'); $col->set($msg); diff --git a/Tests/Model/MessageTest.php b/Tests/Model/MessageTest.php index 3f7b2efc..2934f7a4 100644 --- a/Tests/Model/MessageTest.php +++ b/Tests/Model/MessageTest.php @@ -1,5 +1,7 @@ * @@ -20,15 +22,16 @@ use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Tests\BaseTestCase; +use JMS\TranslationBundle\Model\SourceInterface; +use PHPUnit\Framework\TestCase; -class MessageTest extends BaseTestCase +class MessageTest extends TestCase { public function testCreate() { $message = Message::create('id', 'foo'); - $this->assertInstanceOf('JMS\TranslationBundle\Model\Message', $message); + $this->assertInstanceOf(Message::class, $message); $this->assertEquals('id', $message->getId()); $this->assertEquals('foo', $message->getDomain()); } @@ -37,7 +40,7 @@ public function testForThisFile() { $message = Message::forThisFile('foo', 'bar'); - $this->assertInstanceOf('JMS\TranslationBundle\Model\Message', $message); + $this->assertInstanceOf(Message::class, $message); $this->assertEquals('foo', $message->getId()); $this->assertEquals('bar', $message->getDomain()); @@ -78,12 +81,12 @@ public function testGetMeaning() public function testGetSources() { $message = new Message('foo'); - $this->assertEquals(array(), $message->getSources()); + $this->assertEquals([], $message->getSources()); - $this->assertSame($message, $message->addSource($source = $this->createMock('JMS\TranslationBundle\Model\SourceInterface'))); - $this->assertSame(array($source), $message->getSources()); - $this->assertSame($message, $message->setSources(array($source2 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface')))); - $this->assertSame(array($source2), $message->getSources()); + $this->assertSame($message, $message->addSource($source = $this->createMock(SourceInterface::class))); + $this->assertSame([$source], $message->getSources()); + $this->assertSame($message, $message->setSources([$source2 = $this->createMock(SourceInterface::class)])); + $this->assertSame([$source2], $message->getSources()); } public function testMerge() @@ -91,17 +94,17 @@ public function testMerge() $message = new Message('foo'); $message->setDesc('foo'); $message->setMeaning('foo'); - $message->addSource($s1 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface')); + $message->addSource($s1 = $this->createMock(SourceInterface::class)); $message2 = new Message('foo'); $message2->setDesc('bar'); - $message2->addSource($s2 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface')); + $message2->addSource($s2 = $this->createMock(SourceInterface::class)); $message->merge($message2); $this->assertEquals('bar', $message->getDesc()); $this->assertEquals('foo', $message->getMeaning()); - $this->assertSame(array($s1, $s2), $message->getSources()); + $this->assertSame([$s1, $s2], $message->getSources()); } public function testMergeRememberDesc() @@ -109,17 +112,17 @@ public function testMergeRememberDesc() $message = new Message('foo_id'); $message->setDesc('foo_desc'); $message->setMeaning('foo_meaning'); - $message->addSource($s1 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface')); + $message->addSource($s1 = $this->createMock(SourceInterface::class)); $message2 = new Message('foo_id'); $message2->setMeaning('bar_meaning'); - $message2->addSource($s2 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface')); + $message2->addSource($s2 = $this->createMock(SourceInterface::class)); $message->merge($message2); $this->assertEquals('foo_desc', $message->getDesc()); $this->assertEquals('bar_meaning', $message->getMeaning()); - $this->assertSame(array($s1, $s2), $message->getSources()); + $this->assertSame([$s1, $s2], $message->getSources()); } public function testMergeExisting() @@ -137,7 +140,7 @@ public function testMergeExisting() $this->assertEquals('bar', $message->getDesc()); $this->assertEquals('foobar', $message->getLocaleString()); $this->assertFalse($message->isNew()); - $this->assertEquals(array(), $message->getSources()); + $this->assertEquals([], $message->getSources()); } public function testMergeScanned() @@ -155,7 +158,7 @@ public function testMergeScanned() $this->assertEquals('foobar', $message->getDesc()); $this->assertEquals('foobar', $message->getLocaleString()); $this->assertFalse($message->isNew()); - $this->assertEquals(array(), $message->getSources()); + $this->assertEquals([], $message->getSources()); } public function testGetIsNew() @@ -177,15 +180,14 @@ public function hasSource() { $message = new Message('foo'); - $s2 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface'); + $s2 = $this->createMock(SourceInterface::class); - $s1 = $this->createMock('JMS\TranslationBundle\Model\SourceInterface'); + $s1 = $this->createMock(SourceInterface::class); $s1 ->expects($this->once()) ->method('equals') ->with($s2) - ->will($this->returnValue(true)) - ; + ->willReturn(true); $message->addSource($s1); $this->assertTrue($message->hasSource($s2)); diff --git a/Tests/Translation/Comparison/CatalogueComparatorTest.php b/Tests/Translation/Comparison/CatalogueComparatorTest.php index 7810eb30..67ddc383 100644 --- a/Tests/Translation/Comparison/CatalogueComparatorTest.php +++ b/Tests/Translation/Comparison/CatalogueComparatorTest.php @@ -1,5 +1,7 @@ * @@ -18,12 +20,13 @@ namespace JMS\TranslationBundle\Tests\Translation\Comparison; -use JMS\TranslationBundle\Translation\Comparison\CatalogueComparator; -use JMS\TranslationBundle\Translation\Comparison\ChangeSet; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Comparison\CatalogueComparator; +use JMS\TranslationBundle\Translation\Comparison\ChangeSet; +use PHPUnit\Framework\TestCase; -class CatalogueComparatorTest extends \PHPUnit_Framework_TestCase +class CatalogueComparatorTest extends TestCase { public function testCompareWithMultipleDomains() { @@ -35,9 +38,9 @@ public function testCompareWithMultipleDomains() $new->add(new Message('foo')); $new->add(new Message('bar')); - $expected = new ChangeSet( - array(new Message('bar')), - array(Message::create('bar', 'routes')->setLocaleString('baz')) + $expected = new ChangeSet( + [new Message('bar')], + [Message::create('bar', 'routes')->setLocaleString('baz')] ); $comparator = new CatalogueComparator(); diff --git a/Tests/Translation/Dumper/ArrayStructureDumperTest.php b/Tests/Translation/Dumper/ArrayStructureDumperTest.php index 4817aa7a..91407c7b 100644 --- a/Tests/Translation/Dumper/ArrayStructureDumperTest.php +++ b/Tests/Translation/Dumper/ArrayStructureDumperTest.php @@ -1,5 +1,7 @@ * @@ -20,8 +22,10 @@ use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Dumper\ArrayStructureDumper; +use PHPUnit\Framework\TestCase; -class ArrayStructureDumperTest extends \PHPUnit_Framework_TestCase +class ArrayStructureDumperTest extends TestCase { public function testPathWithSubPath() { @@ -34,20 +38,19 @@ public function testPathWithSubPath() $dumper ->expects($this->once()) ->method('dumpStructure') - ->with(array( - 'foo' => array( + ->with([ + 'foo' => [ 'bar' => new Message('foo.bar'), 'bar.baz' => new Message('foo.bar.baz'), - ), - )) - ->will($this->returnValue('foo')) - ; + ], + ]) + ->willReturn('foo'); $this->assertEquals('foo', $dumper->dump($catalogue, 'messages')); } private function getDumper() { - return $this->getMockForAbstractClass('JMS\TranslationBundle\Translation\Dumper\ArrayStructureDumper'); + return $this->getMockForAbstractClass(ArrayStructureDumper::class); } } diff --git a/Tests/Translation/Dumper/BaseDumperTest.php b/Tests/Translation/Dumper/BaseDumperTest.php index bd4486ab..5c3f5963 100644 --- a/Tests/Translation/Dumper/BaseDumperTest.php +++ b/Tests/Translation/Dumper/BaseDumperTest.php @@ -1,5 +1,7 @@ * @@ -18,11 +20,12 @@ namespace JMS\TranslationBundle\Tests\Translation\Dumper; -use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Model\MessageCatalogue; +use PHPUnit\Framework\TestCase; -abstract class BaseDumperTest extends \PHPUnit_Framework_TestCase +abstract class BaseDumperTest extends TestCase { public function testSimpleDump() { @@ -81,6 +84,7 @@ public function testDumpStructureWithMetadata() } abstract protected function getDumper(); + abstract protected function getOutput($key); private function dump(MessageCatalogue $catalogue, $domain) diff --git a/Tests/Translation/Dumper/PhpDumperTest.php b/Tests/Translation/Dumper/PhpDumperTest.php index 7d8d76b1..f6b0b2d5 100644 --- a/Tests/Translation/Dumper/PhpDumperTest.php +++ b/Tests/Translation/Dumper/PhpDumperTest.php @@ -1,5 +1,7 @@ * @@ -44,10 +46,11 @@ protected function getDumper() protected function getOutput($key) { - if (!is_file($file = __DIR__.'/php/'.$key.'.php')) { + $fileRealPath = __DIR__ . '/php/' . $key . '.php'; + if (! is_file($fileRealPath)) { throw new InvalidArgumentException(sprintf('There is no output for key "%s".', $key)); } - return file_get_contents($file); + return file_get_contents($fileRealPath); } } diff --git a/Tests/Translation/Dumper/XliffDumperTest.php b/Tests/Translation/Dumper/XliffDumperTest.php index 655a9869..d3e82678 100644 --- a/Tests/Translation/Dumper/XliffDumperTest.php +++ b/Tests/Translation/Dumper/XliffDumperTest.php @@ -1,5 +1,7 @@ * @@ -18,10 +20,10 @@ namespace JMS\TranslationBundle\Tests\Translation\Dumper; +use JMS\TranslationBundle\Exception\InvalidArgumentException; use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; -use JMS\TranslationBundle\Exception\InvalidArgumentException; use JMS\TranslationBundle\Translation\Dumper\XliffDumper; class XliffDumperTest extends BaseDumperTest @@ -149,7 +151,8 @@ protected function getDumper() protected function getOutput($key) { - if (!is_file($file = __DIR__.'/xliff/'.$key.'.xml')) { + $fileRealPath = __DIR__ . '/xliff/' . $key . '.xml'; + if (! is_file($fileRealPath)) { throw new InvalidArgumentException(sprintf('There is no output for key "%s".', $key)); } @@ -157,6 +160,6 @@ protected function getOutput($key) // $doc = \DOMDocument::load($file); // $this->assertTrue($doc->schemaValidate(__DIR__.'/../../../Resources/schema/xliff-core-1.2-strict.xsd')); - return file_get_contents($file); + return file_get_contents($fileRealPath); } } diff --git a/Tests/Translation/Dumper/YamlDumperTest.php b/Tests/Translation/Dumper/YamlDumperTest.php index aa9d19f7..efba624f 100644 --- a/Tests/Translation/Dumper/YamlDumperTest.php +++ b/Tests/Translation/Dumper/YamlDumperTest.php @@ -1,5 +1,7 @@ * @@ -44,10 +46,11 @@ protected function getDumper() protected function getOutput($key) { - if (!is_file($file = __DIR__.'/yml/'.$key.'.yml')) { + $fileRealPath = __DIR__ . '/yml/' . $key . '.yml'; + if (! is_file($fileRealPath)) { throw new InvalidArgumentException(sprintf('There is no output for key "%s".', $key)); } - return file_get_contents($file); + return file_get_contents($fileRealPath); } } diff --git a/Tests/Translation/Extractor/File/AuthenticationMessagesExtractorTest.php b/Tests/Translation/Extractor/File/AuthenticationMessagesExtractorTest.php index e42906c1..e33076f7 100644 --- a/Tests/Translation/Extractor/File/AuthenticationMessagesExtractorTest.php +++ b/Tests/Translation/Extractor/File/AuthenticationMessagesExtractorTest.php @@ -1,5 +1,7 @@ * @@ -29,16 +31,16 @@ public function testExtract() $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyAuthException.php'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/MyAuthException.php'); $message = new Message('security.authentication_error.foo', 'authentication'); $message->setDesc('%foo% is invalid.'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 31)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 33)); $expected->add($message); $message = new Message('security.authentication_error.bar', 'authentication'); $message->setDesc('An authentication error occurred.'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 35)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 37)); $expected->add($message); $this->assertEquals($expected, $this->extract('MyAuthException.php')); diff --git a/Tests/Translation/Extractor/File/BasePhpFileExtractorTest.php b/Tests/Translation/Extractor/File/BasePhpFileExtractorTest.php index 588a8639..7792a460 100644 --- a/Tests/Translation/Extractor/File/BasePhpFileExtractorTest.php +++ b/Tests/Translation/Extractor/File/BasePhpFileExtractorTest.php @@ -1,5 +1,7 @@ * @@ -18,39 +20,43 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File; -use JMS\TranslationBundle\Model\MessageCatalogue; use Doctrine\Common\Annotations\DocParser; +use JMS\TranslationBundle\Annotation\Desc; +use JMS\TranslationBundle\Annotation\Ignore; +use JMS\TranslationBundle\Annotation\Meaning; +use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; use JMS\TranslationBundle\Translation\FileSourceFactory; use PhpParser\Lexer; use PhpParser\Parser; use PhpParser\ParserFactory; +use PHPUnit\Framework\TestCase; -abstract class BasePhpFileExtractorTest extends \PHPUnit_Framework_TestCase +abstract class BasePhpFileExtractorTest extends TestCase { - final protected function extract($file, FileVisitorInterface $extractor = null) + final protected function extract($file, ?FileVisitorInterface $extractor = null) { - if (!is_file($file = __DIR__.'/Fixture/'.$file)) { - throw new RuntimeException(sprintf('The file "%s" does not exist.', $file)); + $fileRealPath = __DIR__ . '/Fixture/' . $file; + if (! is_file($fileRealPath)) { + throw new \RuntimeException(sprintf('The file "%s" does not exist.', $fileRealPath)); } - $file = new \SplFileInfo($file); - if (null === $extractor) { + if ($extractor === null) { $extractor = $this->getDefaultExtractor(); } $lexer = new Lexer(); - if (class_exists('PhpParser\ParserFactory')) { + if (class_exists(ParserFactory::class)) { $factory = new ParserFactory(); - $parser = $factory->create(ParserFactory::PREFER_PHP7, $lexer); + $parser = \method_exists($factory, 'create') ? $factory->create(ParserFactory::PREFER_PHP7, $lexer) : $factory->createForNewestSupportedVersion(); } else { $parser = new Parser($lexer); } - $ast = $parser->parse(file_get_contents($file)); + $ast = $parser->parse(file_get_contents($fileRealPath)); $catalogue = new MessageCatalogue(); - $extractor->visitPhpFile($file, $catalogue, $ast); + $extractor->visitPhpFile(new \SplFileInfo($fileRealPath), $catalogue, $ast); return $catalogue; } @@ -60,11 +66,11 @@ abstract protected function getDefaultExtractor(); final protected function getDocParser() { $docParser = new DocParser(); - $docParser->setImports(array( - 'desc' => 'JMS\TranslationBundle\Annotation\Desc', - 'meaning' => 'JMS\TranslationBundle\Annotation\Meaning', - 'ignore' => 'JMS\TranslationBundle\Annotation\Ignore', - )); + $docParser->setImports([ + 'desc' => Desc::class, + 'meaning' => Meaning::class, + 'ignore' => Ignore::class, + ]); $docParser->setIgnoreNotImportedAnnotations(true); return $docParser; diff --git a/Tests/Translation/Extractor/File/DefaultPhpFileExtractorTest.php b/Tests/Translation/Extractor/File/DefaultPhpFileExtractorTest.php index 07371136..12ba9900 100644 --- a/Tests/Translation/Extractor/File/DefaultPhpFileExtractorTest.php +++ b/Tests/Translation/Extractor/File/DefaultPhpFileExtractorTest.php @@ -1,5 +1,7 @@ * @@ -29,37 +31,37 @@ public function testExtractController() $catalogue = $this->extract('Controller.php'); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/Controller.php'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/Controller.php'); $expected = new MessageCatalogue(); $message = new Message('text.foo_bar'); $message->setDesc('Foo bar'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 45)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 47)); $expected->add($message); $message = new Message('text.sign_up_successful'); $message->setDesc('Welcome %name%! Thanks for signing up.'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 52)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 55)); $expected->add($message); $message = new Message('button.archive'); $message->setDesc('Archive Message'); $message->setMeaning('The verb (to archive), describes an action'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 59)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 64)); $expected->add($message); $message = new Message('text.irrelevant_doc_comment', 'baz'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 71)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 77)); $expected->add($message); $message = new Message('text.array_method_call'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 76)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 82)); $expected->add($message); $message = new Message('text.var.assign'); $message->setDesc('The var %foo% should be assigned.'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 82)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 88)); $expected->add($message); $this->assertEquals($expected, $catalogue); @@ -67,9 +69,9 @@ public function testExtractController() public function testExtractTemplate() { - $expected = new MessageCatalogue(); + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/template.html.php'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/template.html.php'); $message = new Message('foo.bar'); $message->addSource($fileSourceFactory->create($fixtureSplInfo, 1)); diff --git a/Tests/Translation/Extractor/File/Fixture/Controller.php b/Tests/Translation/Extractor/File/Fixture/Controller.php index 7f691546..c97aae7b 100644 --- a/Tests/Translation/Extractor/File/Fixture/Controller.php +++ b/Tests/Translation/Extractor/File/Fixture/Controller.php @@ -1,5 +1,7 @@ * @@ -37,7 +39,7 @@ class Controller public function __construct(TranslatorInterface $translator, Session $session) { $this->translator = $translator; - $this->session = $session; + $this->session = $session; } public function indexAction() @@ -47,28 +49,32 @@ public function indexAction() public function welcomeAction() { - $this->session->setFlash('bar', + $this->session->setFlash( + 'bar', /** @Desc("Welcome %name%! Thanks for signing up.") */ - $this->translator->trans('text.sign_up_successful', array('name' => 'Johannes'))); + $this->translator->trans('text.sign_up_successful', ['name' => 'Johannes']) + ); } public function foobarAction() { - $this->session->setFlash('archive', + $this->session->setFlash( + 'archive', /** @Desc("Archive Message") @Meaning("The verb (to archive), describes an action") */ - $this->translator->trans('button.archive')); + $this->translator->trans('button.archive') + ); } public function nonExtractableButIgnoredAction() { /** @Ignore */ $this->translator->trans($foo); /** Foobar */ - /** @Ignore */ $this->translator->trans('foo', array(), $baz); + /** @Ignore */ $this->translator->trans('foo', [], $baz); } public function irrelevantDocComment() { - /** @Foo @Bar */ $this->translator->trans('text.irrelevant_doc_comment', array(), 'baz'); + /** @Foo @Bar */ $this->translator->trans('text.irrelevant_doc_comment', [], 'baz'); } public function arrayAccess() @@ -79,6 +85,6 @@ public function arrayAccess() public function assignToVar() { /** @Desc("The var %foo% should be assigned.") */ - return $this->translator->trans('text.var.assign', array('%foo%' => 'fooVar')); + return $this->translator->trans('text.var.assign', ['%foo%' => 'fooVar']); } } diff --git a/Tests/Translation/Extractor/File/Fixture/MyAttrArrayType.php b/Tests/Translation/Extractor/File/Fixture/MyAttrArrayType.php index f81f84d5..fbecb27c 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyAttrArrayType.php +++ b/Tests/Translation/Extractor/File/Fixture/MyAttrArrayType.php @@ -1,4 +1,7 @@ * @@ -24,15 +27,10 @@ class MyAttrArrayType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { - $attr = array('something'=>'whatever'); + $attr = ['something' => 'whatever']; $builder - ->add('firstname', 'text', array( - 'label' => 'form.label.firstname', - )) - ->add('field_with_attr_as_array','text',array( - 'attr'=>$attr - )); + ->add('firstname', 'text', ['label' => 'form.label.firstname']) + ->add('field_with_attr_as_array', 'text', ['attr' => $attr]); } - } diff --git a/Tests/Translation/Extractor/File/Fixture/MyAuthException.php b/Tests/Translation/Extractor/File/Fixture/MyAuthException.php index ca3fb3af..273d45b4 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyAuthException.php +++ b/Tests/Translation/Extractor/File/Fixture/MyAuthException.php @@ -1,5 +1,7 @@ * @@ -24,9 +26,9 @@ class MyAuthException extends AuthenticationException { private $foo; - public function getMessageKey() + public function getMessageKey(): string { - if (!empty($this->foo)) { + if (! empty($this->foo)) { /** @Desc("%foo% is invalid.") */ return 'security.authentication_error.foo'; } @@ -37,6 +39,6 @@ public function getMessageKey() public function getMessageParameters() { - return array('foo' => $foo); + return ['foo' => $foo]; } } diff --git a/Tests/Translation/Extractor/File/Fixture/MyEntity.php b/Tests/Translation/Extractor/File/Fixture/MyEntity.php index 103a0950..2585adba 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyEntity.php +++ b/Tests/Translation/Extractor/File/Fixture/MyEntity.php @@ -1,12 +1,11 @@ * @@ -25,19 +27,19 @@ class MyFormModel implements TranslationContainerInterface { - private static $choices = array( + private static $choices = [ 'foo' => 'form.label.choice.foo', 'bar' => 'form.label.choice.bar', - ); + ]; /** * @Assert\NotBlank(message = "form.error.name_required") */ - private $name; + public $name; public static function getTranslationMessages() { - $messages = array(); + $messages = []; foreach (self::$choices as $trans) { $message = new Message($trans); diff --git a/Tests/Translation/Extractor/File/Fixture/MyFormSubscriber.php b/Tests/Translation/Extractor/File/Fixture/MyFormSubscriber.php index 80f0285b..d41b003d 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyFormSubscriber.php +++ b/Tests/Translation/Extractor/File/Fixture/MyFormSubscriber.php @@ -1,10 +1,13 @@ 'preSetData'); + return [FormEvents::PRE_SET_DATA => 'preSetData']; } public function preSetData(DataEvent $event) @@ -27,20 +30,15 @@ public function preSetData(DataEvent $event) $data = $event->getData(); $form = $event->getForm(); - if (null === $data) { + if ($data === null) { return; } $form - ->add($this->factory->createNamed('password', 'repeated', null, array( - 'first_options' => array( - 'label' => 'form.label.password' - ), - 'second_options' => array( - 'label' => /** @Desc("Repeat password") */ 'form.label.password_repeated' - ), - 'invalid_message' => /** @Desc("The entered passwords do not match") */ 'form.error.password_mismatch' - ))) - ; + ->add($this->factory->createNamed('password', 'repeated', null, [ + 'first_options' => ['label' => 'form.label.password'], + 'second_options' => ['label' => /** @Desc("Repeat password") */ 'form.label.password_repeated'], + 'invalid_message' => /** @Desc("The entered passwords do not match") */ 'form.error.password_mismatch', + ])); } } diff --git a/Tests/Translation/Extractor/File/Fixture/MyFormType.php b/Tests/Translation/Extractor/File/Fixture/MyFormType.php index 9393a014..6eadd6c4 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyFormType.php +++ b/Tests/Translation/Extractor/File/Fixture/MyFormType.php @@ -1,5 +1,7 @@ * @@ -18,91 +20,91 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File\Fixture; -use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Validator\Constraints\NotBlank; class MyFormType extends AbstractType { - public function buildForm(FormBuilder $builder, array $options) + public const CHOICES = ['choices' => [null]]; + + public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('firstname', 'text', array( - 'label' => 'form.label.firstname', - )) - ->add('lastname', 'text', array( - 'label' => /** @Desc("Lastname") */ 'form.label.lastname', - )) - ->add('states', 'choice', array( - 'choices' => array('foo' => 'bar'), + ->add('firstname', 'text', ['label' => 'form.label.firstname']) + ->add('lastname', 'text', ['label' => /** @Desc("Lastname") */ 'form.label.lastname']) + ->add('states', 'choice', [ + 'choices' => ['foo' => 'bar'], 'empty_value' => /** @Desc("Please select a state") */ 'form.states.empty_value', - )) - ->add('countries', 'choice', array('empty_value' => false)) - ->add('password', 'repeated', array( - 'first_options' => array( - 'label' => 'form.label.password' - ), - 'second_options' => array( - 'label' => /** @Desc("Repeat password") */ 'form.label.password_repeated' - ), - 'invalid_message' => /** @Desc("The entered passwords do not match") */ 'form.error.password_mismatch' - )) - ->add('street', 'text', array( + ]) + ->add('countries', 'choice', ['empty_value' => false]) + ->add('password', 'repeated', [ + 'first_options' => ['label' => 'form.label.password'], + 'second_options' => ['label' => /** @Desc("Repeat password") */ 'form.label.password_repeated'], + 'invalid_message' => /** @Desc("The entered passwords do not match") */ 'form.error.password_mismatch', + ]) + ->add('street', 'text', [ 'label' => /** @Desc("Street") */ 'form.label.street', - 'translation_domain' => 'address' - )) - ->add('zip', 'text', array( + 'translation_domain' => 'address', + 'constraints' => [ + new NotBlank(['message' => /** @Desc("You should fill in the street") */ 'form.street.empty_value']), + new Length(['max' => 100]), + // https://github.com/schmittjoh/JMSTranslationBundle/issues/553 + ], + ]) + ->add('zip', 'text', [ /** @Desc("ZIP") */ 'label' => 'form.label.zip', - 'translation_domain' => 'address' - )) - ->add('field_with_placeholder', 'text', array( + 'translation_domain' => 'address', + ]) + ->add('field_with_placeholder', 'text', [ 'label' => 'field.with.placeholder', - 'attr' => array('placeholder' => /** @Desc("Field with a placeholder value") */ 'form.placeholder.text') - )) - ->add('field_without_label', 'text', array( + 'attr' => ['placeholder' => /** @Desc("Field with a placeholder value") */ 'form.placeholder.text'], + ]) + ->add('field_without_label', 'text', [ 'label' => false, - 'attr' => array('placeholder' => /** @Desc("Field with a placeholder but no label") */ 'form.placeholder.text.but.no.label') - )) - ->add('field_with_choice_as_values', 'choice', array( - 'choices' => array( + 'attr' => ['placeholder' => /** @Desc("Field with a placeholder but no label") */ 'form.placeholder.text.but.no.label'], + ]) + ->add('field_with_choice_as_values', 'choice', [ + 'choices' => [ 'form.choice.choice_as_values.label.foo' => 'form.choice.choice_as_values.value.foo', - 'form.choice.choice_as_values.label.bar' => 'form.choice.choice_as_values.value.bar' - ), + 'form.choice.choice_as_values.label.bar' => 'form.choice.choice_as_values.value.bar', + ], 'choices_as_values' => true, - )) - ; - $child = $builder->create('created', 'text', array( - 'label' => 'form.label.created' - )) - ; - $builder->add('dueDate', 'date', array( - 'empty_value' => array('year' => 'form.dueDate.empty.year', 'month' => 'form.dueDate.empty.month', 'day'=>'form.dueDate.empty.day') - )); + ]); + + $child = $builder->create('created', 'text', ['label' => 'form.label.created']); + $builder->add('dueDate', 'date', [ + 'empty_value' => [ + 'year' => 'form.dueDate.empty.year', + 'month' => 'form.dueDate.empty.month', + 'day' => 'form.dueDate.empty.day', + ], + ]); $builder - ->add('choices_with_translation_domain', 'choice', array( - 'choices' => array('form.choices_with_translation_domain.label' => 'form.choices_with_translation_domain.value'), - 'choice_translation_domain' => 'choice-domain' - )) - ->add('choices_without_translation', 'choice', array( - 'choices' => array('form.choices_without_translation.label' => 'form.choices_without_translation.value'), + ->add('choices_with_translation_domain', 'choice', [ + 'choices' => ['form.choices_with_translation_domain.label' => 'form.choices_with_translation_domain.value'], + 'choice_translation_domain' => 'choice-domain', + ]) + ->add('choices_without_translation', 'choice', [ + 'choices' => ['form.choices_without_translation.label' => 'form.choices_without_translation.value'], 'choice_translation_domain' => false, - )) - ->add('untranslatable_label', 'text', array( + ]) + ->add('untranslatable_label', 'text', [ 'label' => 'form.untranslatable_label.label', 'translation_domain' => false, - )) - ; + ]); - $builder->add('untranslateable_field_with_placeholder', 'text', array( + $builder->add('untranslateable_field_with_placeholder', 'text', [ 'label' => 'field.with.placeholder.no.translation.domain', - 'attr' => array('placeholder' => /** @Desc("Field with a placeholder value") */ 'form.placeholder.text.skip'), + 'attr' => ['placeholder' => /** @Desc("Field with a placeholder value") */ 'form.placeholder.text.skip'], 'translation_domain' => false, - )); + ]); - $builder->add('custom_domain_field_with_placeholder', 'text', array( - 'attr' => array('placeholder' => 'form.custom_domain_field_with_placeholder.attr.placeholder'), + $builder->add('custom_domain_field_with_placeholder', 'text', [ + 'attr' => ['placeholder' => 'form.custom_domain_field_with_placeholder.attr.placeholder'], 'translation_domain' => 'custom_domain_field_with_placeholder', - )); + ]); } } diff --git a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomain.php b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomain.php index e6ada9bf..8659d924 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomain.php +++ b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomain.php @@ -1,5 +1,7 @@ * @@ -18,32 +20,25 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File\Fixture; -use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\AbstractType; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; -class MyFormType extends AbstractType +class MyFormTypeWithDefaultDomain extends AbstractType { - public function buildForm(FormBuilder $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('firstname', 'text', array( - 'label' => 'form.label.firstname', - )) - ->add('lastname', 'text', array( - 'label' => /** @Desc("Lastname") */ 'form.label.lastname', - )) - ->add('street', 'text', array( + ->add('firstname', 'text', ['label' => 'form.label.firstname']) + ->add('lastname', 'text', ['label' => /** @Desc("Lastname") */ 'form.label.lastname']) + ->add('street', 'text', [ 'label' => /** @Desc("Street") */ 'form.label.street', - 'translation_domain' => 'address' - )) - ; + 'translation_domain' => 'address', + ]); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { - $resolver->setDefaults(array( - 'translation_domain' => 'person' - )); + $resolver->setDefaults(['translation_domain' => 'person']); } } diff --git a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomainSetDefault.php b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomainSetDefault.php index ac39d929..2c320b29 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomainSetDefault.php +++ b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithDefaultDomainSetDefault.php @@ -1,5 +1,7 @@ * @@ -18,29 +20,24 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File\Fixture; -use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\AbstractType; -use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; -class MyFormType extends AbstractType +class MyFormTypeWithDefaultDomainSetDefault extends AbstractType { - public function buildForm(FormBuilder $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('firstname', 'text', array( - 'label' => 'form.label.firstname', - )) - ->add('lastname', 'text', array( - 'label' => /** @Desc("Lastname") */ 'form.label.lastname', - )) - ->add('street', 'text', array( + ->add('firstname', 'text', ['label' => 'form.label.firstname']) + ->add('lastname', 'text', ['label' => /** @Desc("Lastname") */ 'form.label.lastname']) + ->add('street', 'text', [ 'label' => /** @Desc("Street") */ 'form.label.street', - 'translation_domain' => 'address' - )) - ; + 'translation_domain' => 'address', + ]); } - public function setDefaultOptions(OptionsResolverInterface $resolver) + public function configureOptions(OptionsResolver $resolver) { $resolver->setDefault('translation_domain', 'person'); $resolver->setDefault('other_option', 'should_not_override_domain'); diff --git a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithInterface.php b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithInterface.php index 1653c30c..bff4d06b 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithInterface.php +++ b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithInterface.php @@ -1,5 +1,7 @@ * @@ -18,25 +20,20 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File\Fixture; -use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; class MyFormTypeWithInterface extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('firstname', 'text', array( - 'label' => 'form.label.firstname', - )) - ->add('lastname', 'text', array( - 'label' => /** @Desc("Lastname") */ 'form.label.lastname', - )) - ->add('states', 'choice', array( - 'choices' => array('foo' => 'bar'), + ->add('firstname', 'text', ['label' => 'form.label.firstname']) + ->add('lastname', 'text', ['label' => /** @Desc("Lastname") */ 'form.label.lastname']) + ->add('states', 'choice', [ + 'choices' => ['foo' => 'bar'], 'empty_value' => /** @Desc("Please select a state") */ 'form.states.empty_value', - )) - ->add('countries', 'choice', array('empty_value' => false)) - ; + ]) + ->add('countries', 'choice', ['empty_value' => false]); } } diff --git a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithSubscriberAndListener.php b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithSubscriberAndListener.php index 05c309f8..62033a2c 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithSubscriberAndListener.php +++ b/Tests/Translation/Extractor/File/Fixture/MyFormTypeWithSubscriberAndListener.php @@ -1,5 +1,7 @@ * @@ -18,44 +20,38 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File\Fixture; -use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; class MyFormTypeWithSubscriberAndListener extends AbstractType { - public function buildForm(FormBuilder $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options) { $formFactory = $builder->getFormFactory(); $builder - ->add('firstname', 'text', array( - 'label' => 'form.label.firstname', - )) - ->add('lastname', 'text', array( - 'label' => /** @Desc("Lastname") */ 'form.label.lastname', - )) + ->add('firstname', 'text', ['label' => 'form.label.firstname']) + ->add('lastname', 'text', ['label' => /** @Desc("Lastname") */ 'form.label.lastname']) ->addEventSubscriber(new MyFormSubscriber($formFactory)) - ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) { + ->addEventListener(FormEvents::PRE_SET_DATA, static function (FormEvent $event) use ($formFactory) { $data = $event->getData(); $form = $event->getForm(); - if (null === $data) { + if ($data === null) { return; } $form - ->add($formFactory->createNamed('zip', 'text', null, array( + ->add($formFactory->createNamed('zip', 'text', null, [ /** @Desc("ZIP") */ 'label' => 'form.label.zip', - 'translation_domain' => 'address' - ))) - ; - }) - ; + 'translation_domain' => 'address', + ])); + }); } - + public function getName() { return 'my_form_with_subscriber_and_listener'; diff --git a/Tests/Translation/Extractor/File/Fixture/MyPlaceholderFormType.php b/Tests/Translation/Extractor/File/Fixture/MyPlaceholderFormType.php index 6cc065ac..95024682 100644 --- a/Tests/Translation/Extractor/File/Fixture/MyPlaceholderFormType.php +++ b/Tests/Translation/Extractor/File/Fixture/MyPlaceholderFormType.php @@ -1,4 +1,7 @@ * @@ -17,28 +20,23 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File\Fixture; -use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilderInterface; class MyPlaceholderFormType extends AbstractType { - public function buildForm(FormBuilder $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options) { $builder - ->add('field_with_attr_placeholder', 'text', array( + ->add('field_with_attr_placeholder', 'text', [ 'label' => 'field.with.placeholder', - 'attr' => array('placeholder' => /** @Desc("Field with a placeholder value") */ 'form.placeholder.text') - )) - ->add('field_without_label_with_attr_placeholder', 'text', array( + 'attr' => ['placeholder' => /** @Desc("Field with a placeholder value") */ 'form.placeholder.text'], + ]) + ->add('field_without_label_with_attr_placeholder', 'text', [ 'label' => false, - 'attr' => array('placeholder' => /** @Desc("Field with a placeholder but no label") */ 'form.placeholder.text.but.no.label') - )) - ->add('field_placeholder', 'choice', array( - 'placeholder' => /** @Desc("Choice field with a placeholder") */ 'form.choice_placeholder' - )) - ->add('field_empty_value', 'choice', array( - 'empty_value' => /** @Desc("Choice field with an empty_value") */ 'form.choice_empty_value' - )) - ; + 'attr' => ['placeholder' => /** @Desc("Field with a placeholder but no label") */ 'form.placeholder.text.but.no.label'], + ]) + ->add('field_placeholder', 'choice', ['placeholder' => /** @Desc("Choice field with a placeholder") */ 'form.choice_placeholder']) + ->add('field_empty_value', 'choice', ['empty_value' => /** @Desc("Choice field with an empty_value") */ 'form.choice_empty_value']); } } diff --git a/Tests/Translation/Extractor/File/Fixture/simple_template_sf5.html.twig b/Tests/Translation/Extractor/File/Fixture/simple_template_sf5.html.twig new file mode 100644 index 00000000..09b09664 --- /dev/null +++ b/Tests/Translation/Extractor/File/Fixture/simple_template_sf5.html.twig @@ -0,0 +1,19 @@ +{{ "text.foo"|trans|desc("Foo Bar")|meaning("Some Meaning")}} + +{{ "text.bar"|trans|desc("Foo") }} + +{{ "text.baz"|trans|meaning("Bar") }} + +{{ "text.foo_bar"|trans({}, "foo") }} + +{% trans with {'%name%': 'Johannes'} from "app" %}text.name{% endtrans %} + +{{ "foo.bar" | trans }} + +{{ "foo.bar2" | trans({'%count%': 5}) }} + +{{ "foo.bar3" | trans({'%name%': 'Johannes'}, "app") }} + +{{ "foo.bar4" | trans({'%count%': 5, '%name%': 'Johannes'}, 'app') }} + +{% trans %}text.default_domain{% endtrans %} diff --git a/Tests/Translation/Extractor/File/FormExtractorTest.php b/Tests/Translation/Extractor/File/FormExtractorTest.php index cd3dce0e..262a18e3 100644 --- a/Tests/Translation/Extractor/File/FormExtractorTest.php +++ b/Tests/Translation/Extractor/File/FormExtractorTest.php @@ -1,5 +1,7 @@ * @@ -18,10 +20,9 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File; -use JMS\TranslationBundle\Translation\Extractor\File\FormExtractor; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; -use Symfony\Component\HttpKernel\Kernel; +use JMS\TranslationBundle\Translation\Extractor\File\FormExtractor; class FormExtractorTest extends BasePhpFileExtractorTest { @@ -30,27 +31,27 @@ class FormExtractorTest extends BasePhpFileExtractorTest */ public function testPlaceholderExtract() { - $expected = new MessageCatalogue(); + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyPlaceholderFormType.php'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/MyPlaceholderFormType.php'); $message = new Message('field.with.placeholder'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 29)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 32)); $expected->add($message); $message = new Message('form.placeholder.text'); $message->setDesc('Field with a placeholder value'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 30)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 33)); $expected->add($message); $message = new Message('form.placeholder.text.but.no.label'); $message->setDesc('Field with a placeholder but no label'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 34)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 37)); $expected->add($message); $message = new Message('form.choice_placeholder'); $message->setDesc('Choice field with a placeholder'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 37)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 39)); $expected->add($message); $message = new Message('form.choice_empty_value'); @@ -66,32 +67,26 @@ public function testPlaceholderExtract() */ public function testExtract() { - $expected = new MessageCatalogue(); + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyFormType.php'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/MyFormType.php'); - // Symfony >= 3.0 switch the default behavior of the choice field following a BC break introduced in 2.7 - // @see https://github.com/symfony/symfony/blob/master/UPGRADE-3.0.md#choices_as_values - if (Kernel::VERSION_ID >= 30000) { - $message = new Message('foo'); - } else { - $message = new Message('bar'); - } - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 36)); + $message = new Message('foo'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 37)); $expected->add($message); $message = new Message('form.states.empty_value'); $message->setDesc('Please select a state'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 37)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 38)); $expected->add($message); $message = new Message('form.label.lastname'); $message->setDesc('Lastname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 33)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 35)); $expected->add($message); $message = new Message('form.label.firstname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 30)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 34)); $expected->add($message); $message = new Message('form.label.password'); @@ -100,40 +95,45 @@ public function testExtract() $message = new Message('form.label.password_repeated'); $message->setDesc('Repeat password'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 45)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 43)); $expected->add($message); $message = new Message('form.label.street', 'address'); $message->setDesc('Street'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 47)); + $expected->add($message); + + $message = new Message('form.street.empty_value', 'validators'); + $message->setDesc('You should fill in the street'); $message->addSource($fileSourceFactory->create($fixtureSplInfo, 50)); $expected->add($message); $message = new Message('form.label.zip', 'address'); $message->setDesc('ZIP'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 55)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 57)); $expected->add($message); $message = new Message('form.error.password_mismatch', 'validators'); $message->setDesc('The entered passwords do not match'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 47)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 44)); $expected->add($message); $message = new Message('form.label.created'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 75)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 76)); $expected->add($message); $message = new Message('field.with.placeholder'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 59)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 61)); $expected->add($message); $message = new Message('form.placeholder.text'); $message->setDesc('Field with a placeholder value'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 60)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 62)); $expected->add($message); $message = new Message('form.placeholder.text.but.no.label'); $message->setDesc('Field with a placeholder but no label'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 64)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 66)); $expected->add($message); $message = new Message('form.dueDate.empty.year'); @@ -141,87 +141,71 @@ public function testExtract() $expected->add($message); $message = new Message('form.dueDate.empty.month'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 79)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 80)); $expected->add($message); $message = new Message('form.dueDate.empty.day'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 79)); - $expected->add($message); - - $message = new Message('form.choice.choice_as_values.label.foo'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 68)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 81)); $expected->add($message); - $message = new Message('form.choice.choice_as_values.label.bar'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 69)); + $message = new Message(0); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, -1)); $expected->add($message); $message = new Message('form.choices_with_translation_domain.label', 'choice-domain'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 84)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 87)); $expected->add($message); - $message = new Message('form.custom_domain_field_with_placeholder.attr.placeholder', 'custom_domain_field_with_placeholder'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 104)); + $message = new Message('form.untranslatable_label.label', 'messages'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 95)); $expected->add($message); - $this->assertEquals($expected, $this->extract('MyFormType.php')); - } + $message = new Message('field.with.placeholder.no.translation.domain', 'messages'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 100)); + $expected->add($message); - /** - * This test is used to check compatibility with Symfony 2.1 - * In Symfony 2.1 the AbstractType must use FormBuilderInterface instead of FormBuilder - */ - public function testExtractWithInterface() - { - $expected = new MessageCatalogue(); - $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyFormTypeWithInterface.php'); + $message = new Message('form.choices_without_translation.label', 'messages'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 91)); + $expected->add($message); - // Symfony >= 3.0 switch the default behavior of the choice field following a BC break introduced in 2.7 - // @see https://github.com/symfony/symfony/blob/master/UPGRADE-3.0.md#choices_as_values - if (Kernel::VERSION_ID >= 30000) { - $message = new Message('foo'); - } else { - $message = new Message('bar'); - } - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 36)); + $message = new Message('form.placeholder.text.skip', 'messages'); + $message->setDesc('Field with a placeholder value'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 101)); $expected->add($message); - $message = new Message('form.states.empty_value'); - $message->setDesc('Please select a state'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 37)); + $message = new Message('form.custom_domain_field_with_placeholder.attr.placeholder', 'custom_domain_field_with_placeholder'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 106)); $expected->add($message); - $message = new Message('form.label.lastname'); - $message->setDesc('Lastname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 33)); + $message = new Message('form.choice.choice_as_values.label.foo'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 70)); $expected->add($message); - $message = new Message('form.label.firstname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 30)); + $message = new Message('form.choice.choice_as_values.label.bar'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 71)); $expected->add($message); - $this->assertEquals($expected, $this->extract('MyFormTypeWithInterface.php')); + $this->assertEquals($expected, $this->extract('MyFormType.php')); } protected function getDefaultDomainFixture($fixtureFile) { - $expected = new MessageCatalogue(); + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo($fixtureFile); + $fixtureSplInfo = new \SplFileInfo($fixtureFile); $message = new Message('form.label.lastname', 'person'); $message->setDesc('Lastname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 34)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 33)); $expected->add($message); $message = new Message('form.label.firstname', 'person'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 31)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 32)); $expected->add($message); $message = new Message('form.label.street', 'address'); $message->setDesc('Street'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 37)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 35)); $expected->add($message); return $expected; @@ -233,7 +217,7 @@ protected function getDefaultDomainFixture($fixtureFile) */ public function testExtractWithDefaultDomain() { - $this->assertEquals($this->getDefaultDomainFixture(__DIR__.'/Fixture/MyFormTypeWithDefaultDomain.php'), $this->extract('MyFormTypeWithDefaultDomain.php')); + $this->assertEquals($this->getDefaultDomainFixture(__DIR__ . '/Fixture/MyFormTypeWithDefaultDomain.php'), $this->extract('MyFormTypeWithDefaultDomain.php')); } /** @@ -242,31 +226,31 @@ public function testExtractWithDefaultDomain() */ public function testExtractWithDefaultDomainSetDefault() { - $this->assertEquals($this->getDefaultDomainFixture(__DIR__.'/Fixture/MyFormTypeWithDefaultDomainSetDefault.php'), $this->extract('MyFormTypeWithDefaultDomainSetDefault.php')); + $this->assertEquals($this->getDefaultDomainFixture(__DIR__ . '/Fixture/MyFormTypeWithDefaultDomainSetDefault.php'), $this->extract('MyFormTypeWithDefaultDomainSetDefault.php')); } /** * This test is used to check if translation from subscriber classes and even closures * are correctly extracted */ - public function testExtractWithWithSubscriberAndListener() + public function testExtractWithSubscriberAndListener() { - $expected = new MessageCatalogue(); - $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyFormTypeWithSubscriberAndListener.php'); - $subscriberFixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyFormSubscriber.php'); + $expected = new MessageCatalogue(); + $fileSourceFactory = $this->getFileSourceFactory(); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/MyFormTypeWithSubscriberAndListener.php'); + $subscriberFixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/MyFormSubscriber.php'); $message = new Message('form.label.lastname'); $message->setDesc('Lastname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 36)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 35)); $expected->add($message); $message = new Message('form.label.firstname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 33)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 34)); $expected->add($message); $message = new Message('form.label.password'); - $message->addSource($fileSourceFactory->create($subscriberFixtureSplInfo, 37)); + $message->addSource($fileSourceFactory->create($subscriberFixtureSplInfo, 39)); $expected->add($message); $message = new Message('form.label.password_repeated'); @@ -276,12 +260,12 @@ public function testExtractWithWithSubscriberAndListener() $message = new Message('form.label.zip', 'address'); $message->setDesc('ZIP'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 51)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 49)); $expected->add($message); $message = new Message('form.error.password_mismatch', 'validators'); $message->setDesc('The entered passwords do not match'); - $message->addSource($fileSourceFactory->create($subscriberFixtureSplInfo, 42)); + $message->addSource($fileSourceFactory->create($subscriberFixtureSplInfo, 41)); $expected->add($message); $catalogue = $this->extract('MyFormTypeWithSubscriberAndListener.php'); @@ -303,16 +287,15 @@ public function testExtractWithNoDefaultDomainAfterDefaultDomainExtraction() public function testAttrArrayForm() { - $expected = new MessageCatalogue(); + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyAttrArrayType.php'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/MyAttrArrayType.php'); $message = new Message('form.label.firstname'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 31)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 33)); $expected->add($message); $this->assertEquals($expected, $this->extract('MyAttrArrayType.php')); - } protected function getDefaultExtractor() diff --git a/Tests/Translation/Extractor/File/TranslationContainerExtractorTest.php b/Tests/Translation/Extractor/File/TranslationContainerExtractorTest.php index 17066d8c..7d65e560 100644 --- a/Tests/Translation/Extractor/File/TranslationContainerExtractorTest.php +++ b/Tests/Translation/Extractor/File/TranslationContainerExtractorTest.php @@ -1,5 +1,7 @@ * @@ -21,18 +23,19 @@ use JMS\TranslationBundle\Exception\RuntimeException; use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Translation\Extractor\File\TranslationContainerExtractor; use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Extractor\File\TranslationContainerExtractor; use PhpParser\Lexer; use PhpParser\Parser; use PhpParser\ParserFactory; +use PHPUnit\Framework\TestCase; -class TranslationContainerExtractorTest extends \PHPUnit_Framework_TestCase +class TranslationContainerExtractorTest extends TestCase { public function testExtractFormModel() { $expected = new MessageCatalogue(); - $path = __DIR__.'/Fixture/MyFormModel.php'; + $path = __DIR__ . '/Fixture/MyFormModel.php'; $message = new Message('form.label.choice.foo'); $message->addSource(new FileSource($path, 13)); @@ -45,29 +48,29 @@ public function testExtractFormModel() $this->assertEquals($expected, $this->extract('MyFormModel.php')); } - private function extract($file, TranslationContainerExtractor $extractor = null) + private function extract($file, ?TranslationContainerExtractor $extractor = null) { - if (!is_file($file = __DIR__.'/Fixture/'.$file)) { - throw new RuntimeException(sprintf('The file "%s" does not exist.', $file)); + $fileRealPath = __DIR__ . '/Fixture/' . $file; + if (! is_file($fileRealPath)) { + throw new RuntimeException(sprintf('The file "%s" does not exist.', $fileRealPath)); } - $file = new \SplFileInfo($file); - if (null === $extractor) { + if ($extractor === null) { $extractor = new TranslationContainerExtractor(); } $lexer = new Lexer(); - if (class_exists('PhpParser\ParserFactory')) { + if (class_exists(ParserFactory::class)) { $factory = new ParserFactory(); - $parser = $factory->create(ParserFactory::PREFER_PHP7, $lexer); + $parser = \method_exists($factory, 'create') ? $factory->create(ParserFactory::PREFER_PHP7, $lexer) : $factory->createForNewestSupportedVersion(); } else { $parser = new Parser($lexer); } - $ast = $parser->parse(file_get_contents($file)); + $ast = $parser->parse(file_get_contents($fileRealPath)); $catalogue = new MessageCatalogue(); - $extractor->visitPhpFile($file, $catalogue, $ast); + $extractor->visitPhpFile(new \SplFileInfo($fileRealPath), $catalogue, $ast); return $catalogue; } diff --git a/Tests/Translation/Extractor/File/TwigFileExtractorTest.php b/Tests/Translation/Extractor/File/TwigFileExtractorTest.php index 27539ceb..3818baa6 100644 --- a/Tests/Translation/Extractor/File/TwigFileExtractorTest.php +++ b/Tests/Translation/Extractor/File/TwigFileExtractorTest.php @@ -1,5 +1,7 @@ * @@ -26,26 +28,91 @@ use JMS\TranslationBundle\Twig\DefaultApplyingNodeVisitor; use JMS\TranslationBundle\Twig\RemovingNodeVisitor; use JMS\TranslationBundle\Twig\TranslationExtension; +use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\FormExtension; use Symfony\Bridge\Twig\Extension\RoutingExtension; use Symfony\Bridge\Twig\Extension\TranslationExtension as SymfonyTranslationExtension; -use Symfony\Bridge\Twig\Form\TwigRenderer; -use Symfony\Bridge\Twig\Form\TwigRendererEngine; -use Symfony\Component\Form\FormRenderer; +use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Translation\IdentityTranslator; -use Symfony\Component\Translation\MessageSelector; use Twig\Environment; +use Twig\Loader\ArrayLoader; +use Twig\Source; -class TwigFileExtractorTest extends \PHPUnit_Framework_TestCase +class TwigFileExtractorTest extends TestCase { + public function testExtractSimpleTemplateInSF5() + { + $isSF5 = version_compare(Kernel::VERSION, '5.0.0') >= 0; + + if (! $isSF5) { + $this->markTestSkipped('Test only available with Symfony 5+'); + } + + $expected = new MessageCatalogue(); + $fileSourceFactory = $this->getFileSourceFactory(); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/simple_template_sf5.html.twig'); + + $message = new Message('text.foo'); + $message->setDesc('Foo Bar'); + $message->setMeaning('Some Meaning'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 1)); + $expected->add($message); + + $message = new Message('text.bar'); + $message->setDesc('Foo'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 3)); + $expected->add($message); + + $message = new Message('text.baz'); + $message->setMeaning('Bar'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 5)); + $expected->add($message); + + $message = new Message('text.foo_bar', 'foo'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 7)); + $expected->add($message); + + $message = new Message('text.name', 'app'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 9)); + $expected->add($message); + + $message = new Message('foo.bar'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 11)); + $expected->add($message); + + $message = new Message('foo.bar2'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 13)); + $expected->add($message); + + $message = new Message('foo.bar3', 'app'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 15)); + $expected->add($message); + + $message = new Message('foo.bar4', 'app'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 17)); + $expected->add($message); + + $message = new Message('text.default_domain'); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 19)); + $expected->add($message); + + $this->assertEquals($expected, $this->extract('simple_template_sf5.html.twig')); + } + public function testExtractSimpleTemplate() { - $expected = new MessageCatalogue(); + $isSF5 = version_compare(Kernel::VERSION, '5.0.0') >= 0; + + if ($isSF5) { + $this->markTestSkipped('Test only available with Symfony < 5'); + } + + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/simple_template.html.twig'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/simple_template.html.twig'); $message = new Message('text.foo'); $message->setDesc('Foo Bar'); @@ -100,15 +167,15 @@ public function testExtractSimpleTemplate() public function testExtractEdit() { - $expected = new MessageCatalogue(); + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/edit.html.twig'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/edit.html.twig'); $message = new Message('header.edit_profile'); $message->addSource($fileSourceFactory->create($fixtureSplInfo, 10)); $expected->add($message); - $message = new Message("text.archive"); + $message = new Message('text.archive'); $message->setDesc('Archive'); $message->setMeaning('The verb'); $message->addSource($fileSourceFactory->create($fixtureSplInfo, 13)); @@ -128,9 +195,9 @@ public function testExtractEdit() public function testEmbeddedTemplate() { - $expected = new MessageCatalogue(); + $expected = new MessageCatalogue(); $fileSourceFactory = $this->getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/embedded_template.html.twig'); + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/embedded_template.html.twig'); $message = new Message('foo'); $message->addSource($fileSourceFactory->create($fixtureSplInfo, 3)); @@ -139,39 +206,39 @@ public function testEmbeddedTemplate() $this->assertEquals($expected, $this->extract('embedded_template.html.twig')); } - private function extract($file, TwigFileExtractor $extractor = null) + private function extract($file, ?TwigFileExtractor $extractor = null) { - if (!is_file($file = __DIR__.'/Fixture/'.$file)) { - throw new RuntimeException(sprintf('The file "%s" does not exist.', $file)); + $fileRealPath = __DIR__ . '/Fixture/' . $file; + if (! is_file($fileRealPath)) { + throw new RuntimeException(sprintf('The file "%s" does not exist.', $fileRealPath)); } - $env = new \Twig_Environment(new \Twig_Loader_Array(array())); - $env->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator(new MessageSelector()))); + $env = new Environment(new ArrayLoader([])); + $env->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator())); $env->addExtension(new TranslationExtension($translator, true)); $env->addExtension(new RoutingExtension(new UrlGenerator(new RouteCollection(), new RequestContext()))); - $env->addExtension(new FormExtension( - class_exists('Symfony\Bridge\Twig\Form\TwigRenderer') ? - new TwigRenderer(new TwigRendererEngine()) : - new FormRenderer(new TwigRendererEngine([], $env)) - )); + $env->addExtension(new FormExtension()); foreach ($env->getNodeVisitors() as $visitor) { if ($visitor instanceof DefaultApplyingNodeVisitor) { $visitor->setEnabled(false); } - if ($visitor instanceof RemovingNodeVisitor) { - $visitor->setEnabled(false); + + if (! ($visitor instanceof RemovingNodeVisitor)) { + continue; } + + $visitor->setEnabled(false); } - if (null === $extractor) { + if ($extractor === null) { $extractor = new TwigFileExtractor($env, new FileSourceFactory('faux')); } - $ast = $env->parse($env->tokenize(new \Twig_Source(file_get_contents($file), $file))); + $ast = $env->parse($env->tokenize(new Source(file_get_contents($fileRealPath), $fileRealPath))); $catalogue = new MessageCatalogue(); - $extractor->visitTwigFile(new \SplFileInfo($file), $catalogue, $ast); + $extractor->visitTwigFile(new \SplFileInfo($fileRealPath), $catalogue, $ast); return $catalogue; } diff --git a/Tests/Translation/Extractor/File/ValidationContextExtractorTest.php b/Tests/Translation/Extractor/File/ValidationContextExtractorTest.php index ee258efa..7651ce47 100644 --- a/Tests/Translation/Extractor/File/ValidationContextExtractorTest.php +++ b/Tests/Translation/Extractor/File/ValidationContextExtractorTest.php @@ -1,5 +1,7 @@ getFileSourceFactory(); - $fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyEntity.php'); - + $fixtureSplInfo = new \SplFileInfo(__DIR__ . '/Fixture/MyEntity.php'); $expected = new MessageCatalogue(); $message = new Message('entity.default'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 15)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 14)); $expected->add($message); $message = new Message('entity.custom-domain', 'custom-domain'); - $message->addSource($fileSourceFactory->create($fixtureSplInfo, 22)); + $message->addSource($fileSourceFactory->create($fixtureSplInfo, 21)); $expected->add($message); $this->assertEquals($expected, $this->extract('MyEntity.php')); diff --git a/Tests/Translation/Extractor/File/ValidationExtractorTest.php b/Tests/Translation/Extractor/File/ValidationExtractorTest.php index bd5e2975..234788c7 100644 --- a/Tests/Translation/Extractor/File/ValidationExtractorTest.php +++ b/Tests/Translation/Extractor/File/ValidationExtractorTest.php @@ -1,5 +1,7 @@ * @@ -18,26 +20,24 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor\File; -use JMS\TranslationBundle\Exception\RuntimeException; use Doctrine\Common\Annotations\AnnotationReader; +use JMS\TranslationBundle\Exception\RuntimeException; +use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Extractor\File\ValidationExtractor; use PhpParser\Lexer; use PhpParser\Parser; use PhpParser\ParserFactory; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; -use Symfony\Component\Validator\Mapping\ClassMetadataFactory; -use JMS\TranslationBundle\Translation\Extractor\File\ValidationExtractor; -use Doctrine\Common\Annotations\DocParser; -use JMS\TranslationBundle\Translation\Extractor\File\FormExtractor; -use JMS\TranslationBundle\Model\FileSource; -use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Model\MessageCatalogue; -class ValidationExtractorTest extends \PHPUnit_Framework_TestCase +class ValidationExtractorTest extends TestCase { public function testExtractConstraints() { $expected = new MessageCatalogue(); - $path = __DIR__.'/Fixture/MyFormModel.php'; + $path = __DIR__ . '/Fixture/MyFormModel.php'; $message = new Message('form.error.name_required', 'validators'); $expected->add($message); @@ -45,37 +45,32 @@ public function testExtractConstraints() $this->assertEquals($expected, $this->extract('MyFormModel.php')); } - private function extract($file, ValidationExtractor $extractor = null) + private function extract($file, ?ValidationExtractor $extractor = null) { - if (!is_file($file = __DIR__.'/Fixture/'.$file)) { - throw new RuntimeException(sprintf('The file "%s" does not exist.', $file)); + $fileRealPath = __DIR__ . '/Fixture/' . $file; + if (! is_file($fileRealPath)) { + throw new RuntimeException(sprintf('The file "%s" does not exist.', $fileRealPath)); } - $file = new \SplFileInfo($file); - //use correct factory class depending on whether using Symfony 2 or 3 - if (class_exists('Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory')) { - $metadataFactoryClass = 'Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory'; - } else { - $metadataFactoryClass = 'Symfony\Component\Validator\Mapping\ClassMetadataFactory'; - } + $metadataFactoryClass = LazyLoadingMetadataFactory::class; - if (null === $extractor) { - $factory = new $metadataFactoryClass(new AnnotationLoader(new AnnotationReader())); + if ($extractor === null) { + $factory = new $metadataFactoryClass(new AnnotationLoader(new AnnotationReader())); $extractor = new ValidationExtractor($factory); } $lexer = new Lexer(); - if (class_exists('PhpParser\ParserFactory')) { + if (class_exists(ParserFactory::class)) { $factory = new ParserFactory(); - $parser = $factory->create(ParserFactory::PREFER_PHP7, $lexer); + $parser = \method_exists($factory, 'create') ? $factory->create(ParserFactory::PREFER_PHP7, $lexer) : $factory->createForNewestSupportedVersion(); } else { $parser = new Parser($lexer); } - $ast = $parser->parse(file_get_contents($file)); + $ast = $parser->parse(file_get_contents($fileRealPath)); $catalogue = new MessageCatalogue(); - $extractor->visitPhpFile($file, $catalogue, $ast); + $extractor->visitPhpFile(new \SplFileInfo($fileRealPath), $catalogue, $ast); return $catalogue; } diff --git a/Tests/Translation/Extractor/FileExtractorTest.php b/Tests/Translation/Extractor/FileExtractorTest.php index 71ceae72..f57eccb5 100644 --- a/Tests/Translation/Extractor/FileExtractorTest.php +++ b/Tests/Translation/Extractor/FileExtractorTest.php @@ -1,5 +1,7 @@ * @@ -18,37 +20,41 @@ namespace JMS\TranslationBundle\Tests\Translation\Extractor; +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\DocParser; +use JMS\TranslationBundle\Annotation\Desc; +use JMS\TranslationBundle\Annotation\Ignore; +use JMS\TranslationBundle\Annotation\Meaning; +use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Translation\Extractor\File\DefaultPhpFileExtractor; +use JMS\TranslationBundle\Translation\Extractor\File\FormExtractor; +use JMS\TranslationBundle\Translation\Extractor\File\TranslationContainerExtractor; use JMS\TranslationBundle\Translation\Extractor\File\TwigFileExtractor; +use JMS\TranslationBundle\Translation\Extractor\File\ValidationExtractor; +use JMS\TranslationBundle\Translation\Extractor\FileExtractor; use JMS\TranslationBundle\Translation\FileSourceFactory; use JMS\TranslationBundle\Twig\TranslationExtension; +use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; -use Doctrine\Common\Annotations\DocParser; -use JMS\TranslationBundle\Translation\Extractor\File\FormExtractor; -use Doctrine\Common\Annotations\AnnotationReader; -use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; -use Symfony\Component\Validator\Mapping\ClassMetadataFactory; -use JMS\TranslationBundle\Translation\Extractor\File\ValidationExtractor; -use JMS\TranslationBundle\Model\FileSource; -use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Model\MessageCatalogue; -use JMS\TranslationBundle\Translation\Extractor\File\TranslationContainerExtractor; -use JMS\TranslationBundle\Translation\Extractor\File\DefaultPhpFileExtractor; -use Symfony\Component\Translation\MessageSelector; -use Symfony\Component\Translation\IdentityTranslator; use Symfony\Bridge\Twig\Extension\TranslationExtension as SymfonyTranslationExtension; -use JMS\TranslationBundle\Translation\Extractor\FileExtractor; +use Symfony\Component\Translation\IdentityTranslator; +use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; +use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; +use Twig\Environment; +use Twig\Loader\ArrayLoader; +use Twig\Loader\FilesystemLoader; -class FileExtractorTest extends \PHPUnit_Framework_TestCase +class FileExtractorTest extends TestCase { public function testExtractWithSimpleTestFixtures() { - $expected = array(); - $basePath = __DIR__.'/Fixture/SimpleTest/'; + $expected = []; + $basePath = __DIR__ . '/Fixture/SimpleTest/'; $fileSourceFactory = new FileSourceFactory('faux'); // Controller $message = new Message('controller.foo'); - $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath.'Controller/DefaultController.php'), 27)); + $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath . 'Controller/DefaultController.php'), 29)); $message->setDesc('Foo'); $expected['controller.foo'] = $message; @@ -57,35 +63,35 @@ public function testExtractWithSimpleTestFixtures() $expected['form.bar'] = new Message('form.bar'); // Templates - foreach (array('php', 'twig') as $engine) { - $message = new Message($engine.'.foo'); - $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath.'Resources/views/'.$engine.'_template.html.'.$engine), 1)); - $expected[$engine.'.foo'] = $message; + foreach (['php', 'twig'] as $engine) { + $message = new Message($engine . '.foo'); + $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath . 'Resources/views/' . $engine . '_template.html.' . $engine), 1)); + $expected[$engine . '.foo'] = $message; - $message = new Message($engine.'.bar'); + $message = new Message($engine . '.bar'); $message->setDesc('Bar'); - $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath.'Resources/views/'.$engine.'_template.html.'.$engine), 3)); - $expected[$engine.'.bar'] = $message; + $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath . 'Resources/views/' . $engine . '_template.html.' . $engine), 3)); + $expected[$engine . '.bar'] = $message; - $message = new Message($engine.'.baz'); + $message = new Message($engine . '.baz'); $message->setMeaning('Baz'); - $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath.'Resources/views/'.$engine.'_template.html.'.$engine), 5)); - $expected[$engine.'.baz'] = $message; + $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath . 'Resources/views/' . $engine . '_template.html.' . $engine), 5)); + $expected[$engine . '.baz'] = $message; - $message = new Message($engine.'.foo_bar'); + $message = new Message($engine . '.foo_bar'); $message->setDesc('Foo'); $message->setMeaning('Bar'); - $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath.'Resources/views/'.$engine.'_template.html.'.$engine), 7)); - $expected[$engine.'.foo_bar'] = $message; + $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath . 'Resources/views/' . $engine . '_template.html.' . $engine), 7)); + $expected[$engine . '.foo_bar'] = $message; } // File with global namespace. $message = new Message('globalnamespace.foo'); - $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath.'GlobalNamespace.php'), 27)); + $message->addSource($fileSourceFactory->create(new \SplFileInfo($basePath . 'GlobalNamespace.php'), 29)); $message->setDesc('Bar'); $expected['globalnamespace.foo'] = $message; - $actual = $this->extract(__DIR__.'/Fixture/SimpleTest')->getDomain('messages')->all(); + $actual = $this->extract(__DIR__ . '/Fixture/SimpleTest')->getDomain('messages')->all(); asort($expected); asort($actual); @@ -95,38 +101,33 @@ public function testExtractWithSimpleTestFixtures() private function extract($directory) { - $twig = new \Twig_Environment(new \Twig_Loader_Array(array())); - $twig->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator(new MessageSelector()))); + $twig = new Environment(new ArrayLoader([])); + $twig->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator())); $twig->addExtension(new TranslationExtension($translator)); - $loader=new \Twig_Loader_Filesystem(realpath(__DIR__."/Fixture/SimpleTest/Resources/views/")); + $loader = new FilesystemLoader(realpath(__DIR__ . '/Fixture/SimpleTest/Resources/views/')); $twig->setLoader($loader); $docParser = new DocParser(); - $docParser->setImports(array( - 'desc' => 'JMS\TranslationBundle\Annotation\Desc', - 'meaning' => 'JMS\TranslationBundle\Annotation\Meaning', - 'ignore' => 'JMS\TranslationBundle\Annotation\Ignore', - )); + $docParser->setImports([ + 'desc' => Desc::class, + 'meaning' => Meaning::class, + 'ignore' => Ignore::class, + ]); $docParser->setIgnoreNotImportedAnnotations(true); - //use correct factory class depending on whether using Symfony 2 or 3 - if (class_exists('Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory')) { - $metadataFactoryClass = 'Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory'; - } else { - $metadataFactoryClass = 'Symfony\Component\Validator\Mapping\ClassMetadataFactory'; - } + $metadataFactoryClass = LazyLoadingMetadataFactory::class; $factory = new $metadataFactoryClass(new AnnotationLoader(new AnnotationReader())); $dummyFileSourceFactory = new FileSourceFactory('faux'); - $extractor = new FileExtractor($twig, new NullLogger(), array( + $extractor = new FileExtractor($twig, new NullLogger(), [ new DefaultPhpFileExtractor($docParser, $dummyFileSourceFactory), new TranslationContainerExtractor(), new TwigFileExtractor($twig, $dummyFileSourceFactory), new ValidationExtractor($factory), new FormExtractor($docParser, $dummyFileSourceFactory), - )); + ]); $extractor->setDirectory($directory); return $extractor->extract(); diff --git a/Tests/Translation/Extractor/Fixture/SimpleTest/Controller/DefaultController.php b/Tests/Translation/Extractor/Fixture/SimpleTest/Controller/DefaultController.php index 052eadc2..be19071a 100644 --- a/Tests/Translation/Extractor/Fixture/SimpleTest/Controller/DefaultController.php +++ b/Tests/Translation/Extractor/Fixture/SimpleTest/Controller/DefaultController.php @@ -1,5 +1,7 @@ * diff --git a/Tests/Translation/Extractor/Fixture/SimpleTest/Form/FormModel.php b/Tests/Translation/Extractor/Fixture/SimpleTest/Form/FormModel.php index 5d6358a0..f4aa9835 100644 --- a/Tests/Translation/Extractor/Fixture/SimpleTest/Form/FormModel.php +++ b/Tests/Translation/Extractor/Fixture/SimpleTest/Form/FormModel.php @@ -1,5 +1,7 @@ * @@ -25,9 +27,9 @@ class FormModel implements TranslationContainerInterface { public static function getTranslationMessages() { - return array( + return [ new Message('form.foo'), new Message('form.bar'), - ); + ]; } } diff --git a/Tests/Translation/Extractor/Fixture/SimpleTest/GlobalNamespace.php b/Tests/Translation/Extractor/Fixture/SimpleTest/GlobalNamespace.php index 6d09ea06..2fe65bac 100644 --- a/Tests/Translation/Extractor/Fixture/SimpleTest/GlobalNamespace.php +++ b/Tests/Translation/Extractor/Fixture/SimpleTest/GlobalNamespace.php @@ -1,5 +1,7 @@ * @@ -18,7 +20,7 @@ namespace { - class SomeRandomClassNameWhichIsnTUsedAnywhereJMSTranslatorTest + class GlobalNamespace { private $translator; @@ -27,4 +29,4 @@ public function method() return /** @Desc("Bar") */ $this->translator->trans('globalnamespace.foo'); } } -} \ No newline at end of file +} diff --git a/Tests/Translation/ExtractorManagerTest.php b/Tests/Translation/ExtractorManagerTest.php index 04675a89..8ad39250 100644 --- a/Tests/Translation/ExtractorManagerTest.php +++ b/Tests/Translation/ExtractorManagerTest.php @@ -1,5 +1,7 @@ * @@ -20,63 +22,61 @@ use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; -use JMS\TranslationBundle\Tests\BaseTestCase; -use Psr\Log\NullLogger; use JMS\TranslationBundle\Translation\Extractor\FileExtractor; +use JMS\TranslationBundle\Translation\ExtractorInterface; use JMS\TranslationBundle\Translation\ExtractorManager; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Twig\Environment; +use Twig\Loader\ArrayLoader; -class ExtractorManagerTest extends BaseTestCase +class ExtractorManagerTest extends TestCase { - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage There is no extractor with alias "foo". Available extractors: # none # - */ public function testSetEnabledCustomExtractorsThrowsExceptionWhenAliasInvalid() { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('There is no extractor with alias "foo". Available extractors: # none #'); + $manager = $this->getManager(); - $manager->setEnabledExtractors(array('foo' => true)); + $manager->setEnabledExtractors(['foo' => true]); } public function testOnlySomeExtractorsEnabled() { - $foo = $this->createMock('JMS\TranslationBundle\Translation\ExtractorInterface'); + $foo = $this->createMock(ExtractorInterface::class); $foo ->expects($this->never()) - ->method('extract') - ; + ->method('extract'); $catalogue = new MessageCatalogue(); $catalogue->add(new Message('foo')); - $bar = $this->createMock('JMS\TranslationBundle\Translation\ExtractorInterface'); + $bar = $this->createMock(ExtractorInterface::class); $bar ->expects($this->once()) ->method('extract') - ->will($this->returnValue($catalogue)) - ; + ->willReturn($catalogue); - $manager = $this->getManager(null, array( + $manager = $this->getManager(null, [ 'foo' => $foo, 'bar' => $bar, - )); - $manager->setEnabledExtractors(array('bar' => true)); + ]); + $manager->setEnabledExtractors(['bar' => true]); $this->assertEquals($catalogue, $manager->extract()); } public function testReset() { - $foo = $this->createMock('JMS\TranslationBundle\Translation\ExtractorInterface'); + $foo = $this->createMock(ExtractorInterface::class); $logger = new NullLogger(); - $extractor = new FileExtractor(new \Twig_Environment(new \Twig_Loader_Array(array())), $logger, array()); - $extractor->setExcludedNames(array('foo', 'bar')); - $extractor->setExcludedDirs(array('baz')); + $extractor = new FileExtractor(new Environment(new ArrayLoader([])), $logger, []); + $extractor->setExcludedNames(['foo', 'bar']); + $extractor->setExcludedDirs(['baz']); - $manager = $this->getManager($extractor, array( - 'foo' => $foo, - )); - $manager->setEnabledExtractors(array('foo' => true)); - $manager->setDirectories(array('/')); + $manager = $this->getManager($extractor, ['foo' => $foo]); + $manager->setEnabledExtractors(['foo' => true]); + $manager->setDirectories(['/']); $managerReflection = new \ReflectionClass($manager); $extractorReflection = new \ReflectionClass($extractor); @@ -93,25 +93,25 @@ public function testReset() $excludedDirsProperty = $extractorReflection->getProperty('excludedDirs'); $excludedDirsProperty->setAccessible(true); - $this->assertEquals(array('foo' => true), $enabledExtractorsProperty->getValue($manager)); - $this->assertEquals(array('/'), $directoriesProperty->getValue($manager)); - $this->assertEquals(array('foo', 'bar'), $excludedNamesProperty->getValue($extractor)); - $this->assertEquals(array('baz'), $excludedDirsProperty->getValue($extractor)); + $this->assertEquals(['foo' => true], $enabledExtractorsProperty->getValue($manager)); + $this->assertEquals(['/'], $directoriesProperty->getValue($manager)); + $this->assertEquals(['foo', 'bar'], $excludedNamesProperty->getValue($extractor)); + $this->assertEquals(['baz'], $excludedDirsProperty->getValue($extractor)); $manager->reset(); - $this->assertEquals(array(), $enabledExtractorsProperty->getValue($manager)); - $this->assertEquals(array(), $directoriesProperty->getValue($manager)); - $this->assertEquals(array(), $excludedNamesProperty->getValue($extractor)); - $this->assertEquals(array(), $excludedDirsProperty->getValue($extractor)); + $this->assertEquals([], $enabledExtractorsProperty->getValue($manager)); + $this->assertEquals([], $directoriesProperty->getValue($manager)); + $this->assertEquals([], $excludedNamesProperty->getValue($extractor)); + $this->assertEquals([], $excludedDirsProperty->getValue($extractor)); } - private function getManager(FileExtractor $extractor = null, array $extractors = array()) + private function getManager(?FileExtractor $extractor = null, array $extractors = []) { $logger = new NullLogger(); - if (null === $extractor) { - $extractor = new FileExtractor(new \Twig_Environment(new \Twig_Loader_Array(array())), $logger, array()); + if ($extractor === null) { + $extractor = new FileExtractor(new Environment(new ArrayLoader([])), $logger, []); } return new ExtractorManager($extractor, $logger, $extractors); diff --git a/Tests/Translation/FileSourceFactoryTest.php b/Tests/Translation/FileSourceFactoryTest.php index f0659652..a822ff13 100644 --- a/Tests/Translation/FileSourceFactoryTest.php +++ b/Tests/Translation/FileSourceFactoryTest.php @@ -1,53 +1,91 @@ assertEquals($expected, $result, $message); } public function pathProvider() { - return array( - array( + return [ + [ '/user/foo/application/app', + null, '/user/foo/application/src/bundle/controller/index.php', '/../src/bundle/controller/index.php', - ), + ], - array( + [ '/user/foo/application/app/foo/bar', + null, '/user/foo/application/src/bundle/controller/index.php', '/../../../src/bundle/controller/index.php', - ), + ], - array( + [ '/user/foo/application/app', + null, '/user/foo/application/app/../src/AppBundle/Controller/DefaultController.php', '/../src/AppBundle/Controller/DefaultController.php', 'Test with "/../" in the file path', - ), + ], - array( + [ '/user/foo/application/app/foo/bar/baz/biz/foo', + null, '/user/foo/application/src/bundle/controller/index.php', '/../../../../../../src/bundle/controller/index.php', 'Test when the root path is longer that file path', - ), - ); + ], + + [ + '/user/foo/application/app', + '/user/foo/application', + '/user/foo/application/src/bundle/controller/index.php', + '/src/bundle/controller/index.php', + ], + + [ + '/user/foo/application/app/foo/bar', + '/user/foo/application/src/foo/bar', + '/user/foo/application/src/bundle/controller/index.php', + '/../../bundle/controller/index.php', + ], + + [ + '/user/foo/application/app', + '/user/foo/application', + '/user/foo/application/app/../src/AppBundle/Controller/DefaultController.php', + '/app/../src/AppBundle/Controller/DefaultController.php', + 'Test with "/../" in the file path', + ], + + [ + '/user/foo/application/app/foo/bar/baz/biz/foo', + '/user/foo/application/src/foo/bar/baz/biz/foo', + '/user/foo/application/src/bundle/controller/index.php', + '/../../../../../bundle/controller/index.php', + 'Test when the root path is longer that file path', + ], + + ]; } } diff --git a/Tests/Translation/FileWriterTest.php b/Tests/Translation/FileWriterTest.php index 6a8f7a28..32267b5b 100644 --- a/Tests/Translation/FileWriterTest.php +++ b/Tests/Translation/FileWriterTest.php @@ -1,5 +1,7 @@ * @@ -20,27 +22,25 @@ use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; -use JMS\TranslationBundle\Tests\BaseTestCase; +use JMS\TranslationBundle\Translation\Dumper\DumperInterface; use JMS\TranslationBundle\Translation\FileWriter; +use PHPUnit\Framework\TestCase; -class FileWriterTest extends BaseTestCase +class FileWriterTest extends TestCase { public function testCatalogueIsSortedBeforeBeingDumped() { - $dumper = $this->createMock('JMS\TranslationBundle\Translation\Dumper\DumperInterface'); + $dumper = $this->createMock(DumperInterface::class); $self = $this; $dumper ->expects($this->once()) ->method('dump') - ->will($this->returnCallback(function ($v) use ($self) { - $self->assertEquals(array('foo.bar', 'foo.bar.baz'), array_keys($v->getDomain('messages')->all())); - })) - ; - - $writer = new FileWriter(array( - 'test' => $dumper, - )); + ->willReturnCallback(static function ($v) use ($self) { + $self->assertEquals(['foo.bar', 'foo.bar.baz'], array_keys($v->getDomain('messages')->all())); + }); + + $writer = new FileWriter(['test' => $dumper]); $catalogue = new MessageCatalogue(); $catalogue->setLocale('fr'); diff --git a/Tests/Translation/Loader/Symfony/BaseLoaderTest.php b/Tests/Translation/Loader/Symfony/BaseLoaderTest.php index a5c597a1..1a7c6839 100644 --- a/Tests/Translation/Loader/Symfony/BaseLoaderTest.php +++ b/Tests/Translation/Loader/Symfony/BaseLoaderTest.php @@ -1,5 +1,7 @@ * @@ -18,15 +20,16 @@ namespace JMS\TranslationBundle\Tests\Translation\Loader\Symfony; +use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\MessageCatalogue; -abstract class BaseLoaderTest extends \PHPUnit_Framework_TestCase +abstract class BaseLoaderTest extends TestCase { public function testLoadSimple() { $expected = new MessageCatalogue('en'); - $expected->add(array('foo' => 'foo')); + $expected->add(['foo' => 'foo']); $file = $this->getInputFile('simple'); $expected->addResource(new FileResource($file)); @@ -37,11 +40,11 @@ public function testLoadSimple() public function testLoadStructureWithMetadata() { $expected = new MessageCatalogue('en'); - $expected->add(array( + $expected->add([ 'foo.bar.baz' => 'Foo', 'foo.bar.moo' => 'foo.bar.moo', 'foo.baz' => 'foo.baz', - )); + ]); $file = $this->getInputFile('structure_with_metadata'); $expected->addResource(new FileResource($file)); @@ -52,9 +55,7 @@ public function testLoadStructureWithMetadata() public function testLoadStructure() { $expected = new MessageCatalogue('en'); - $expected->add(array( - 'foo.bar.baz' => 'foo.bar.baz', - )); + $expected->add(['foo.bar.baz' => 'foo.bar.baz']); $file = $this->getInputFile('structure'); $expected->addResource(new FileResource($file)); @@ -65,9 +66,7 @@ public function testLoadStructure() public function testLoadWithMetadata() { $expected = new MessageCatalogue('en'); - $expected->add(array( - 'foo' => 'bar', - )); + $expected->add(['foo' => 'bar']); $file = $this->getInputFile('with_metadata'); $expected->addResource(new FileResource($file)); @@ -76,6 +75,7 @@ public function testLoadWithMetadata() } abstract protected function getLoader(); + abstract protected function getInputFile($key); private function load($file, $locale = 'en') diff --git a/Tests/Translation/Loader/Symfony/PhpLoaderTest.php b/Tests/Translation/Loader/Symfony/PhpLoaderTest.php index 9a417878..d65d07d1 100644 --- a/Tests/Translation/Loader/Symfony/PhpLoaderTest.php +++ b/Tests/Translation/Loader/Symfony/PhpLoaderTest.php @@ -1,5 +1,7 @@ * @@ -30,10 +32,11 @@ protected function getLoader() protected function getInputFile($key) { - if (!is_file($file = __DIR__.'/../../Dumper/php/'.$key.'.php')) { + $fileRealPath = __DIR__ . '/../../Dumper/php/' . $key . '.php'; + if (! is_file($fileRealPath)) { throw new InvalidArgumentException(sprintf('The input file for key "%s" does not exist.', $key)); } - return $file; + return $fileRealPath; } } diff --git a/Tests/Translation/Loader/Symfony/XliffLoaderTest.php b/Tests/Translation/Loader/Symfony/XliffLoaderTest.php index f14d75db..cd7986b5 100644 --- a/Tests/Translation/Loader/Symfony/XliffLoaderTest.php +++ b/Tests/Translation/Loader/Symfony/XliffLoaderTest.php @@ -1,5 +1,7 @@ * @@ -19,23 +21,23 @@ namespace JMS\TranslationBundle\Tests\Translation\Loader\Symfony; use JMS\TranslationBundle\Exception\InvalidArgumentException; +use JMS\TranslationBundle\Translation\Loader\Symfony\XliffLoader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\MessageCatalogue; -use JMS\TranslationBundle\Translation\Loader\Symfony\XliffLoader; class XliffLoaderTest extends BaseLoaderTest { public function testLoadOldFormat() { $expected = new MessageCatalogue('en'); - $expected->add(array( + $expected->add([ 'foo1' => 'bar', 'foo2' => 'bar', 'foo3' => 'bar', 'foo4' => 'bar', - )); + ]); - $file = __DIR__.'/xliff/old_format.xml'; + $file = __DIR__ . '/xliff/old_format.xml'; $expected->addResource(new FileResource($file)); $this->assertEquals($expected, $this->getLoader()->load($file, 'en')); @@ -43,11 +45,12 @@ public function testLoadOldFormat() protected function getInputFile($key) { - if (!is_file($file = __DIR__.'/../../Dumper/xliff/'.$key.'.xml')) { + $fileRealPath = __DIR__ . '/../../Dumper/xliff/' . $key . '.xml'; + if (! is_file($fileRealPath)) { throw new InvalidArgumentException(sprintf('The input file for key "%s" does not exist.', $key)); } - return $file; + return $fileRealPath; } protected function getLoader() diff --git a/Tests/Translation/Loader/Symfony/YamlLoaderTest.php b/Tests/Translation/Loader/Symfony/YamlLoaderTest.php index 1b17dd56..dc443aa3 100644 --- a/Tests/Translation/Loader/Symfony/YamlLoaderTest.php +++ b/Tests/Translation/Loader/Symfony/YamlLoaderTest.php @@ -1,5 +1,7 @@ * @@ -30,10 +32,11 @@ protected function getLoader() protected function getInputFile($key) { - if (!is_file($file = __DIR__.'/../../Dumper/yml/'.$key.'.yml')) { + $fileRealPath = __DIR__ . '/../../Dumper/yml/' . $key . '.yml'; + if (! is_file($fileRealPath)) { throw new InvalidArgumentException(sprintf('The input file for key "%s" does not exist.', $key)); } - return $file; + return $fileRealPath; } } diff --git a/Tests/Translation/Loader/Symfony/xliff/workflow.xml b/Tests/Translation/Loader/Symfony/xliff/workflow.xml new file mode 100644 index 00000000..504b5a5d --- /dev/null +++ b/Tests/Translation/Loader/Symfony/xliff/workflow.xml @@ -0,0 +1,14 @@ + + + +
+ The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. +
+ + + foo1 + bar + + +
+
diff --git a/Tests/Translation/Loader/SymfonyLoaderAdapterTest.php b/Tests/Translation/Loader/SymfonyLoaderAdapterTest.php index 204a9a11..b7043c9c 100644 --- a/Tests/Translation/Loader/SymfonyLoaderAdapterTest.php +++ b/Tests/Translation/Loader/SymfonyLoaderAdapterTest.php @@ -1,5 +1,7 @@ * @@ -18,30 +20,32 @@ namespace JMS\TranslationBundle\Tests\Translation\Loader; -use JMS\TranslationBundle\Tests\BaseTestCase; +use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Loader\SymfonyLoaderAdapter; -use Symfony\Component\Translation\MessageCatalogue; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue as SymfonyMessageCatalogue; -class SymfonyLoaderAdapterTest extends BaseTestCase +class SymfonyLoaderAdapterTest extends TestCase { public function testLoad() { - $symfonyCatalogue = new MessageCatalogue('en'); - $symfonyCatalogue->add(array('foo' => 'bar')); - - $symfonyLoader = $this->createMock('Symfony\Component\Translation\Loader\LoaderInterface'); + $symfonyCatalogue = new SymfonyMessageCatalogue('en'); + $symfonyCatalogue->add(['foo' => 'bar']); + + $symfonyLoader = $this->createMock(LoaderInterface::class); $symfonyLoader->expects($this->once()) ->method('load') ->with('foo', 'en', 'messages') - ->will($this->returnValue($symfonyCatalogue)); - - $adapter = new SymfonyLoaderAdapter($symfonyLoader); + ->willReturn($symfonyCatalogue); + + $adapter = new SymfonyLoaderAdapter($symfonyLoader); $bundleCatalogue = $adapter->load('foo', 'en', 'messages'); - $this->assertInstanceOf('JMS\TranslationBundle\Model\MessageCatalogue', $bundleCatalogue); + $this->assertInstanceOf(MessageCatalogue::class, $bundleCatalogue); $this->assertEquals('en', $bundleCatalogue->getLocale()); $this->assertTrue($bundleCatalogue->hasDomain('messages')); $this->assertTrue($bundleCatalogue->getDomain('messages')->has('foo')); - + $message = $bundleCatalogue->getDomain('messages')->get('foo'); $this->assertEquals('bar', $message->getLocaleString()); $this->assertFalse($message->isNew()); diff --git a/Tests/Translation/Loader/XliffLoaderTest.php b/Tests/Translation/Loader/XliffLoaderTest.php index 37515eac..6cc43e9f 100644 --- a/Tests/Translation/Loader/XliffLoaderTest.php +++ b/Tests/Translation/Loader/XliffLoaderTest.php @@ -1,5 +1,7 @@ * @@ -19,24 +21,26 @@ namespace JMS\TranslationBundle\Tests\Translation\Loader; use JMS\TranslationBundle\Model\Message\XliffMessage; +use JMS\TranslationBundle\Model\Message\XliffMessageState; use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Dumper\XliffDumper; use JMS\TranslationBundle\Translation\Loader\XliffLoader; +use PHPUnit\Framework\TestCase; -class XliffLoaderTest extends \PHPUnit_Framework_TestCase +class XliffLoaderTest extends TestCase { /** * @dataProvider getTestFiles */ public function testLoadIntegration($file) { - $loader = new XliffLoader(); + $loader = new XliffLoader(); $catalogue = $loader->load($file, 'en'); $dumper = new XliffDumper(); $dumper->setAddDate(false); - $this->assertEquals(file_get_contents($file), $dumper->dump($catalogue)); + $this->assertStringEqualsFile($file, $dumper->dump($catalogue)); } public function testLoadWithSymfonyFormat() @@ -56,17 +60,35 @@ public function testLoadWithSymfonyFormat() $this->assertEquals( $expected, - $loader->load(__DIR__.'/Symfony/xliff/old_format.xml', 'en') + $loader->load(__DIR__ . '/Symfony/xliff/old_format.xml', 'en') + ); + } + + public function testWorkflowAttributes() + { + $loader = new XliffLoader(); + + $expected = new MessageCatalogue(); + $expected->setLocale('en'); + $expected->add(XliffMessage::create('foo1') + ->setDesc('foo1') + ->setLocaleString('bar') + ->setState(XliffMessageState::STATE_NEEDS_ADAPTATION) + ->setApproved(true)); + + $this->assertEquals( + $expected, + $loader->load(__DIR__ . '/Symfony/xliff/workflow.xml', 'en') ); } public function getTestFiles() { - $files = array(); - $files[] = array(__DIR__.'/../Dumper/xliff/simple.xml'); - $files[] = array(__DIR__.'/../Dumper/xliff/structure_with_metadata.xml'); - $files[] = array(__DIR__.'/../Dumper/xliff/structure.xml'); - $files[] = array(__DIR__.'/../Dumper/xliff/with_metadata.xml'); + $files = []; + $files[] = [__DIR__ . '/../Dumper/xliff/simple.xml']; + $files[] = [__DIR__ . '/../Dumper/xliff/structure_with_metadata.xml']; + $files[] = [__DIR__ . '/../Dumper/xliff/structure.xml']; + $files[] = [__DIR__ . '/../Dumper/xliff/with_metadata.xml']; return $files; } diff --git a/Tests/Twig/BaseTwigTestCase.php b/Tests/Twig/BaseTwigTestCase.php index 181b6fe4..9e40f3f9 100644 --- a/Tests/Twig/BaseTwigTestCase.php +++ b/Tests/Twig/BaseTwigTestCase.php @@ -1,5 +1,7 @@ * @@ -18,21 +20,24 @@ namespace JMS\TranslationBundle\Tests\Twig; -use Symfony\Component\Translation\MessageSelector; -use Symfony\Component\Translation\IdentityTranslator; -use Symfony\Bridge\Twig\Extension\TranslationExtension as SymfonyTranslationExtension; use JMS\TranslationBundle\Twig\TranslationExtension; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Twig\Extension\TranslationExtension as SymfonyTranslationExtension; +use Symfony\Component\Translation\IdentityTranslator; +use Twig\Environment; +use Twig\Loader\ArrayLoader; +use Twig\Source; -abstract class BaseTwigTestCase extends \PHPUnit_Framework_TestCase +abstract class BaseTwigTestCase extends TestCase { final protected function parse($file, $debug = false) { - $content = file_get_contents(__DIR__.'/Fixture/'.$file); + $content = file_get_contents(__DIR__ . '/Fixture/' . $file); - $env = new \Twig_Environment(new \Twig_Loader_Array(array())); - $env->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator(new MessageSelector()))); + $env = new Environment(new ArrayLoader([])); + $env->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator())); $env->addExtension(new TranslationExtension($translator, $debug)); - return $env->parse($env->tokenize(new \Twig_Source($content, null)))->getNode('body'); + return $env->compile($env->parse($env->tokenize(new Source($content, 'whatever')))->getNode('body')); } } diff --git a/Tests/Twig/DefaultApplyingNodeVisitorTest.php b/Tests/Twig/DefaultApplyingNodeVisitorTest.php index 2d4bf7e9..606979c7 100644 --- a/Tests/Twig/DefaultApplyingNodeVisitorTest.php +++ b/Tests/Twig/DefaultApplyingNodeVisitorTest.php @@ -1,5 +1,7 @@ * diff --git a/Tests/Twig/Fixture/simple_template.html.twig b/Tests/Twig/Fixture/simple_template.html.twig index 2d56ebef..ef0bf9e2 100644 --- a/Tests/Twig/Fixture/simple_template.html.twig +++ b/Tests/Twig/Fixture/simple_template.html.twig @@ -18,4 +18,4 @@ {{ "foo.bar4" | transchoice(5, {'%name%': 'Johannes'}, 'app') }} -{% trans %}text.default_domain{% endtrans %} \ No newline at end of file +{% trans %}text.default_domain{% endtrans %} diff --git a/Tests/Twig/Fixture/simple_template_compiled.html.twig b/Tests/Twig/Fixture/simple_template_compiled.html.twig index 4f262627..4a36b580 100644 --- a/Tests/Twig/Fixture/simple_template_compiled.html.twig +++ b/Tests/Twig/Fixture/simple_template_compiled.html.twig @@ -18,4 +18,4 @@ {{ "foo.bar4" | transchoice(5, {'%name%': 'Johannes'}, 'app') }} -{% trans %}text.default_domain{% endtrans %} \ No newline at end of file +{% trans %}text.default_domain{% endtrans %} diff --git a/Tests/Twig/Fixture/simple_template_compiled_sf5.html.twig b/Tests/Twig/Fixture/simple_template_compiled_sf5.html.twig new file mode 100644 index 00000000..ab6e9b94 --- /dev/null +++ b/Tests/Twig/Fixture/simple_template_compiled_sf5.html.twig @@ -0,0 +1,19 @@ +{{ "text.foo"|trans }} + +{{ "text.bar"|trans }} + +{{ "text.baz"|trans }} + +{{ "text.foo_bar"|trans({}, "foo") }} + +{% trans with {'%name%': 'Johannes'} from "app" %}text.name{% endtrans %} + +{{ "foo.bar" | trans }} + +{{ "foo.bar2" | trans({'%count%': 5}) }} + +{{ "foo.bar3" | trans({'%name%': 'Johannes'}, "app") }} + +{{ "foo.bar4" | trans({'%count%': 5, '%name%': 'Johannes'}, 'app') }} + +{% trans %}text.default_domain{% endtrans %} diff --git a/Tests/Twig/Fixture/simple_template_sf5.html.twig b/Tests/Twig/Fixture/simple_template_sf5.html.twig new file mode 100644 index 00000000..09b09664 --- /dev/null +++ b/Tests/Twig/Fixture/simple_template_sf5.html.twig @@ -0,0 +1,19 @@ +{{ "text.foo"|trans|desc("Foo Bar")|meaning("Some Meaning")}} + +{{ "text.bar"|trans|desc("Foo") }} + +{{ "text.baz"|trans|meaning("Bar") }} + +{{ "text.foo_bar"|trans({}, "foo") }} + +{% trans with {'%name%': 'Johannes'} from "app" %}text.name{% endtrans %} + +{{ "foo.bar" | trans }} + +{{ "foo.bar2" | trans({'%count%': 5}) }} + +{{ "foo.bar3" | trans({'%name%': 'Johannes'}, "app") }} + +{{ "foo.bar4" | trans({'%count%': 5, '%name%': 'Johannes'}, 'app') }} + +{% trans %}text.default_domain{% endtrans %} diff --git a/Tests/Twig/NormalizingNodeVisitorTest.php b/Tests/Twig/NormalizingNodeVisitorTest.php index 23bcc859..ed3d52c6 100644 --- a/Tests/Twig/NormalizingNodeVisitorTest.php +++ b/Tests/Twig/NormalizingNodeVisitorTest.php @@ -1,5 +1,7 @@ * diff --git a/Tests/Twig/RemovingNodeVisitorTest.php b/Tests/Twig/RemovingNodeVisitorTest.php index 92d0510c..f44b3eb3 100644 --- a/Tests/Twig/RemovingNodeVisitorTest.php +++ b/Tests/Twig/RemovingNodeVisitorTest.php @@ -1,5 +1,7 @@ * @@ -18,12 +20,18 @@ namespace JMS\TranslationBundle\Tests\Twig; +use Symfony\Component\HttpKernel\Kernel; + class RemovingNodeVisitorTest extends BaseTwigTestCase { - public function testRemovalWithSimpleTemplate() + public function testRemovalWithSimpleTemplate(): void { - $expected = $this->parse('simple_template_compiled.html.twig'); - $actual = $this->parse('simple_template.html.twig'); + $isSF5 = version_compare(Kernel::VERSION, '5.0.0') >= 0; + + $templateSuffix = $isSF5 ? '_sf5' : ''; + + $expected = $this->parse('simple_template_compiled' . $templateSuffix . '.html.twig'); + $actual = $this->parse('simple_template' . $templateSuffix . '.html.twig'); $this->assertEquals($expected, $actual); } diff --git a/Tests/Twig/TranslationExtensionTest.php b/Tests/Twig/TranslationExtensionTest.php new file mode 100644 index 00000000..7f02b3bb --- /dev/null +++ b/Tests/Twig/TranslationExtensionTest.php @@ -0,0 +1,34 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\TranslationBundle\Tests\Twig; + +use JMS\TranslationBundle\Twig\TranslationExtension; +use PHPUnit\Framework\TestCase; + +final class TranslationExtensionTest extends TestCase +{ + public function testInvalidConstruction(): void + { + $this->expectException(\InvalidArgumentException::class); + + new TranslationExtension(new \StdClass()); + } +} diff --git a/Tests/Util/FileUtilsTest.php b/Tests/Util/FileUtilsTest.php new file mode 100644 index 00000000..d058c782 --- /dev/null +++ b/Tests/Util/FileUtilsTest.php @@ -0,0 +1,138 @@ + + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace JMS\TranslationBundle\Tests\Util; + +use JMS\TranslationBundle\Util\FileUtils; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\SplFileInfo; + +class FileUtilsTest extends TestCase +{ + /** + * @var string + */ + private $translationDirectory; + + /** + * @var Filesystem + */ + private $fileSystem; + + public function __construct($name = null, array $data = [], $dataName = '') + { + parent::__construct($name, $data, $dataName); + $this->fileSystem = new Filesystem(); + } + + public function testAnEmptyDirectoryReturnsNoFiles(): void + { + $files = FileUtils::findTranslationFiles($this->translationDirectory); + + $this->assertEquals([], $files); + } + + public function testNestedDirectoriesAreIgnored(): void + { + $nestedDirectory = $this->translationDirectory . '/nested'; + mkdir($nestedDirectory); + touch($nestedDirectory . '/messages.en.xliff'); + + $files = FileUtils::findTranslationFiles($this->translationDirectory); + + $this->assertEquals([], $files); + } + + public function testOnlyTranslationFilesArePickedUp(): void + { + $this->createTranslationFile('not_a_translation_file.yaml'); + $this->createTranslationFile('not_a_translation_file.xliff'); + $this->createTranslationFile('not_a_translation_file.en'); + $this->createTranslationFile('messages.nl.xliff'); + $this->createTranslationFile('some-other.en.yaml'); + + $files = FileUtils::findTranslationFiles($this->translationDirectory); + + $this->assertCount(2, $files); + $this->assertArrayHasKey('messages', $files); + $this->assertArrayHasKey('some-other', $files); + } + + public function testRegularTranslationFileNamesAreParsed(): void + { + $this->createTranslationFile('messages.nl.yaml'); + + $files = FileUtils::findTranslationFiles($this->translationDirectory); + + $this->assertArrayHasKey('messages', $files); + $this->assertArrayHasKey('nl', $files['messages']); + $this->assertEquals('yaml', $files['messages']['nl'][0]); + $this->assertInstanceOf(SplFileInfo::class, $files['messages']['nl'][1]); + $this->assertFalse($files['messages']['nl'][2]); + } + + public function testIntlIcuTranslationFileNamesAreParsed(): void + { + $this->createTranslationFile('messages+intl-icu.en_GB.xliff'); + + $files = FileUtils::findTranslationFiles($this->translationDirectory); + + $this->assertArrayHasKey('messages', $files); + $this->assertArrayHasKey('en_GB', $files['messages']); + $this->assertEquals('xliff', $files['messages']['en_GB'][0]); + $this->assertInstanceOf(SplFileInfo::class, $files['messages']['en_GB'][1]); + $this->assertTrue($files['messages']['en_GB'][2]); + } + + /** + * Creates a temporary directory which we can use to run the file utils + * tests. It gets cleaned up afterwards. + */ + protected function setUp(): void + { + $tempDir = sys_get_temp_dir(); + if (!is_writable($tempDir)) { + $this->markTestSkipped(sprintf( + "Can't execute FileUtils tests because %s is not writable", + $tempDir + )); + } + + $this->translationDirectory = $tempDir . '/' . uniqid('jms_test_'); + $directoryCreated = mkdir($this->translationDirectory); + if (!$directoryCreated) { + $this->markTestSkipped(sprintf( + "Can't execute FileUtils tests because %s could not be created", + $this->translationDirectory + )); + } + } + + protected function tearDown(): void + { + $this->fileSystem->remove($this->translationDirectory); + } + + private function createTranslationFile(string $filename): void + { + touch($this->translationDirectory . '/' . $filename); + } +} diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php index ff26d8a8..bac8e959 100644 --- a/Tests/bootstrap.php +++ b/Tests/bootstrap.php @@ -1,11 +1,16 @@ * @@ -27,8 +29,8 @@ */ class CatalogueComparator { - private $domains = array(); - private $ignoredDomains = array(); + private $domains = []; + private $ignoredDomains = []; public function setDomains(array $domains) { @@ -48,11 +50,12 @@ public function setIgnoredDomains(array $domains) * * @param MessageCatalogue $current * @param MessageCatalogue $new + * * @return ChangeSet */ public function compare(MessageCatalogue $current, MessageCatalogue $new) { - $newMessages = array(); + $newMessages = []; foreach ($new->getDomains() as $name => $domain) { if ($this->domains && !isset($this->domains[$name])) { @@ -74,7 +77,7 @@ public function compare(MessageCatalogue $current, MessageCatalogue $new) } } - $deletedMessages = array(); + $deletedMessages = []; foreach ($current->getDomains() as $name => $domain) { if ($this->domains && !isset($this->domains[$name])) { continue; diff --git a/Translation/Comparison/ChangeSet.php b/Translation/Comparison/ChangeSet.php index 2ea0bee4..baf0c6da 100644 --- a/Translation/Comparison/ChangeSet.php +++ b/Translation/Comparison/ChangeSet.php @@ -1,5 +1,7 @@ * @@ -31,7 +33,6 @@ class ChangeSet private $deletedMessages; /** - * ChangeSet constructor. * @param array $addedMessages * @param array $deletedMessages */ diff --git a/Translation/Config.php b/Translation/Config.php index 0cf5d26b..3792ee11 100644 --- a/Translation/Config.php +++ b/Translation/Config.php @@ -1,5 +1,7 @@ * @@ -60,6 +62,11 @@ final class Config */ private $defaultOutputFormat; + /** + * @var bool + */ + private $useIcuMessageFormat; + /** * @var array */ @@ -91,13 +98,12 @@ final class Config private $loadResources; /** - * Config constructor. - * @param $translationsDir - * @param $locale + * @param string $translationsDir + * @param string $locale * @param array $ignoredDomains * @param array $domains - * @param $outputFormat - * @param $defaultOutputFormat + * @param string $outputFormat + * @param string $defaultOutputFormat * @param array $scanDirs * @param array $excludedDirs * @param array $excludedNames @@ -105,7 +111,7 @@ final class Config * @param bool $keepOldMessages * @param array $loadResources */ - public function __construct($translationsDir, $locale, array $ignoredDomains, array $domains, $outputFormat, $defaultOutputFormat, array $scanDirs, array $excludedDirs, array $excludedNames, array $enabledExtractors, $keepOldMessages, array $loadResources) + public function __construct($translationsDir, $locale, array $ignoredDomains, array $domains, $outputFormat, $defaultOutputFormat, $useIcuMessageFormat, array $scanDirs, array $excludedDirs, array $excludedNames, array $enabledExtractors, $keepOldMessages, array $loadResources) { if (empty($translationsDir)) { throw new InvalidArgumentException('The directory where translations are must be set.'); @@ -138,6 +144,7 @@ public function __construct($translationsDir, $locale, array $ignoredDomains, ar $this->domains = $domains; $this->outputFormat = $outputFormat; $this->defaultOutputFormat = $defaultOutputFormat; + $this->useIcuMessageFormat = $useIcuMessageFormat; $this->locale = $locale; $this->scanDirs = $scanDirs; $this->excludedDirs = $excludedDirs; @@ -156,7 +163,8 @@ public function getTranslationsDir() } /** - * @param $domain + * @param string $domain + * * @return bool */ public function isIgnoredDomain($domain) @@ -173,7 +181,8 @@ public function getIgnoredDomains() } /** - * @param $domain + * @param string $domain + * * @return bool */ public function hasDomain($domain) @@ -213,6 +222,14 @@ public function getDefaultOutputFormat() return $this->defaultOutputFormat; } + /** + * @return bool + */ + public function shouldUseIcuMessageFormat() + { + return $this->useIcuMessageFormat; + } + /** * @return mixed */ diff --git a/Translation/ConfigBuilder.php b/Translation/ConfigBuilder.php index 9839bd24..35c4d828 100644 --- a/Translation/ConfigBuilder.php +++ b/Translation/ConfigBuilder.php @@ -1,5 +1,7 @@ * @@ -33,12 +35,12 @@ final class ConfigBuilder /** * @var array */ - private $ignoredDomains = array(); + private $ignoredDomains = []; /** * @var array */ - private $domains = array(); + private $domains = []; /** * @var string @@ -50,25 +52,30 @@ final class ConfigBuilder */ private $defaultOutputFormat = 'xlf'; + /** + * @var bool + */ + private $useIcuMessageFormat = false; + /** * @var array */ - private $scanDirs = array(); + private $scanDirs = []; /** * @var array */ - private $excludedDirs = array('Tests'); + private $excludedDirs = ['Tests']; /** * @var array */ - private $excludedNames = array('*Test.php', '*TestCase.php'); + private $excludedNames = ['*Test.php', '*TestCase.php']; /** * @var array */ - private $enabledExtractors = array(); + private $enabledExtractors = []; /** * @var bool @@ -78,12 +85,14 @@ final class ConfigBuilder /** * @var array */ - private $loadResources = array(); + private $loadResources = []; /** - * @static * @param Config $config + * * @return ConfigBuilder + * + * @static */ public static function fromConfig(Config $config) { @@ -94,6 +103,7 @@ public static function fromConfig(Config $config) $builder->setDomains($config->getDomains()); $builder->setOutputFormat($config->getOutputFormat()); $builder->setDefaultOutputFormat($config->getDefaultOutputFormat()); + $builder->setUseIcuMessageFormat($config->shouldUseIcuMessageFormat()); $builder->setScanDirs($config->getScanDirs()); $builder->setExcludedDirs($config->getExcludedDirs()); $builder->setExcludedNames($config->getExcludedNames()); @@ -111,6 +121,7 @@ public static function fromConfig(Config $config) * - you haven't forced a format * * @param string $format + * * @return $this */ public function setDefaultOutputFormat($format) @@ -128,6 +139,7 @@ public function setDefaultOutputFormat($format) * another format to be deleted. * * @param string $format + * * @return $this */ public function setOutputFormat($format) @@ -137,6 +149,23 @@ public function setOutputFormat($format) return $this; } + /** + * Defines whether or not the ICU message format should be used. + * + * If enabled, translation files will be suffixed with +intl-icu, e.g.: + * message+intl-icu.en.xlf + * + * @param bool $useIcuMessageFormat + * + * @return $this + */ + public function setUseIcuMessageFormat($useIcuMessageFormat) + { + $this->useIcuMessageFormat = $useIcuMessageFormat; + + return $this; + } + /** * Sets ignored domains. * @@ -144,6 +173,7 @@ public function setOutputFormat($format) * appear in the change set calculated by getChangeSet(). * * @param array $domains an array of the form array('domain' => true, 'another_domain' => true) + * * @return $this */ public function setIgnoredDomains(array $domains) @@ -155,6 +185,7 @@ public function setIgnoredDomains(array $domains) /** * @param string $domain + * * @return $this */ public function addIgnoredDomain($domain) @@ -166,6 +197,7 @@ public function addIgnoredDomain($domain) /** * @param array $domains + * * @return $this */ public function setDomains(array $domains) @@ -177,6 +209,7 @@ public function setDomains(array $domains) /** * @param string $domain + * * @return $this */ public function addDomain($domain) @@ -188,6 +221,7 @@ public function addDomain($domain) /** * @param string $locale + * * @return $this */ public function setLocale($locale) @@ -199,6 +233,7 @@ public function setLocale($locale) /** * @param string $dir + * * @return $this */ public function setTranslationsDir($dir) @@ -210,6 +245,7 @@ public function setTranslationsDir($dir) /** * @param array $dirs + * * @return $this */ public function setScanDirs(array $dirs) @@ -221,6 +257,7 @@ public function setScanDirs(array $dirs) /** * @param array $dirs + * * @return $this */ public function setExcludedDirs(array $dirs) @@ -232,6 +269,7 @@ public function setExcludedDirs(array $dirs) /** * @param array $names + * * @return $this */ public function setExcludedNames(array $names) @@ -243,6 +281,7 @@ public function setExcludedNames(array $names) /** * @param array $aliases + * * @return $this */ public function setEnabledExtractors(array $aliases) @@ -254,6 +293,7 @@ public function setEnabledExtractors(array $aliases) /** * @param string $alias + * * @return $this */ public function enableExtractor($alias) @@ -265,6 +305,7 @@ public function enableExtractor($alias) /** * @param string $alias + * * @return $this */ public function disableExtractor($alias) @@ -276,6 +317,7 @@ public function disableExtractor($alias) /** * @param bool $value + * * @return $this */ public function setKeepOldTranslations($value) @@ -297,6 +339,7 @@ public function getConfig() $this->domains, $this->outputFormat, $this->defaultOutputFormat, + $this->useIcuMessageFormat, $this->scanDirs, $this->excludedDirs, $this->excludedNames, @@ -308,6 +351,7 @@ public function getConfig() /** * @param array $loadResources + * * @return $this */ public function setLoadResources(array $loadResources) diff --git a/Translation/ConfigFactory.php b/Translation/ConfigFactory.php index 6bce0a40..74b02e77 100644 --- a/Translation/ConfigFactory.php +++ b/Translation/ConfigFactory.php @@ -1,5 +1,7 @@ * @@ -28,10 +30,9 @@ class ConfigFactory private $builders; /** - * ConfigFactory constructor. * @param array $builders ConfigBuilder */ - public function __construct(array $builders = array()) + public function __construct(array $builders = []) { $this->builders = $builders; } @@ -45,8 +46,10 @@ public function getNames() } /** - * @param $name + * @param string $name + * * @return ConfigBuilder + * * @throws InvalidArgumentException */ public function getBuilder($name) @@ -59,8 +62,9 @@ public function getBuilder($name) } /** - * @param $name - * @param $locale + * @param string $name + * @param string $locale + * * @return Config */ public function getConfig($name, $locale) diff --git a/Translation/Dumper/ArrayStructureDumper.php b/Translation/Dumper/ArrayStructureDumper.php index 172ab865..ef6300f1 100644 --- a/Translation/Dumper/ArrayStructureDumper.php +++ b/Translation/Dumper/ArrayStructureDumper.php @@ -1,5 +1,7 @@ * @@ -28,9 +30,6 @@ abstract class ArrayStructureDumper implements DumperInterface */ private $prettyPrint = true; - /** - * @param $bool - */ public function setPrettyPrint($bool) { $this->prettyPrint = (bool) $bool; @@ -39,6 +38,7 @@ public function setPrettyPrint($bool) /** * @param MessageCatalogue $catalogue * @param string $domain + * * @return string */ public function dump(MessageCatalogue $catalogue, $domain = 'messages') @@ -46,7 +46,7 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') $structure = $catalogue->getDomain($domain)->all(); if ($this->prettyPrint) { - $tmpStructure = array(); + $tmpStructure = []; foreach ($structure as $id => $message) { $pointer = &$tmpStructure; @@ -57,14 +57,14 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') // are before sub-paths, e.g. // array_keys($structure) = array('foo.bar', 'foo.bar.baz') // but NOT: array_keys($structure) = array('foo.bar.baz', 'foo.bar') - for ($i=0, $c=count($parts); $i<$c; $i++) { - if ($i+1 === $c) { + for ($i = 0, $c = count($parts); $i < $c; $i++) { + if ($i + 1 === $c) { $pointer[$parts[$i]] = $message; break; } if (!isset($pointer[$parts[$i]])) { - $pointer[$parts[$i]] = array(); + $pointer[$parts[$i]] = []; } if ($pointer[$parts[$i]] instanceof Message) { @@ -86,6 +86,7 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') /** * @param array $structure + * * @return string */ abstract protected function dumpStructure(array $structure); diff --git a/Translation/Dumper/DumperInterface.php b/Translation/Dumper/DumperInterface.php index b7dbf4e8..11a839fd 100644 --- a/Translation/Dumper/DumperInterface.php +++ b/Translation/Dumper/DumperInterface.php @@ -1,5 +1,7 @@ * @@ -35,6 +37,7 @@ interface DumperInterface * * @param MessageCatalogue $catalogue * @param string $domain + * * @return string */ public function dump(MessageCatalogue $catalogue, $domain = 'messages'); diff --git a/Translation/Dumper/PhpDumper.php b/Translation/Dumper/PhpDumper.php index 20846e10..ef9dc784 100644 --- a/Translation/Dumper/PhpDumper.php +++ b/Translation/Dumper/PhpDumper.php @@ -1,5 +1,7 @@ * @@ -28,9 +30,6 @@ class PhpDumper extends ArrayStructureDumper */ private $writer; - /** - * PhpDumper constructor. - */ public function __construct() { $this->writer = new Writer(); @@ -38,6 +37,7 @@ public function __construct() /** * @param array $structure + * * @return string */ protected function dumpStructure(array $structure) @@ -46,8 +46,7 @@ protected function dumpStructure(array $structure) ->reset() ->writeln('writeln('return array(') - ->indent() - ; + ->indent(); $this->dumpStructureRecursively($structure); @@ -71,10 +70,10 @@ private function dumpStructureRecursively(array $structure) } if ($desc) { - $this->writer->writeln('// Desc: '.$desc); + $this->writer->writeln('// Desc: ' . $desc); } if ($meaning) { - $this->writer->writeln('// Meaning: '.$meaning); + $this->writer->writeln('// Meaning: ' . $meaning); } } elseif (!$isFirst) { $this->writer->write("\n"); @@ -82,10 +81,10 @@ private function dumpStructureRecursively(array $structure) $isFirst = false; $precededByMessage = $isMessage; - $this->writer->write(var_export($k, true).' => '); + $this->writer->write(var_export($k, true) . ' => '); if ($isMessage) { - $this->writer->write(var_export($v->getLocaleString(), true).','); + $this->writer->write(var_export($v->getLocaleString(), true) . ','); if ($v->isNew()) { $this->writer->write(' // FIXME'); @@ -98,15 +97,13 @@ private function dumpStructureRecursively(array $structure) $this->writer ->write("array(\n") - ->indent() - ; + ->indent(); $this->dumpStructureRecursively($v); $this->writer ->outdent() - ->writeln('),') - ; + ->writeln('),'); } } } diff --git a/Translation/Dumper/SymfonyDumperAdapter.php b/Translation/Dumper/SymfonyDumperAdapter.php index 3789d6c1..6591d6bd 100644 --- a/Translation/Dumper/SymfonyDumperAdapter.php +++ b/Translation/Dumper/SymfonyDumperAdapter.php @@ -1,5 +1,7 @@ * @@ -19,10 +21,10 @@ namespace JMS\TranslationBundle\Translation\Dumper; use JMS\TranslationBundle\Exception\RuntimeException; +use JMS\TranslationBundle\Model\MessageCatalogue; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Translation\MessageCatalogue as SymfonyCatalogue; use Symfony\Component\Translation\Dumper\DumperInterface as SymfonyDumper; -use JMS\TranslationBundle\Model\MessageCatalogue; +use Symfony\Component\Translation\MessageCatalogue as SymfonyCatalogue; /** * Adapter for Symfony's dumpers. @@ -46,11 +48,6 @@ class SymfonyDumperAdapter implements DumperInterface */ private $format; - /** - * SymfonyDumperAdapter constructor. - * @param SymfonyDumper $dumper - * @param $format string - */ public function __construct(SymfonyDumper $dumper, $format) { $this->dumper = $dumper; @@ -60,7 +57,9 @@ public function __construct(SymfonyDumper $dumper, $format) /** * @param MessageCatalogue $catalogue * @param string $domain + * * @return string + * * @throws RuntimeException */ public function dump(MessageCatalogue $catalogue, $domain = 'messages') @@ -69,21 +68,19 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') foreach ($catalogue->getDomain($domain)->all() as $id => $message) { $symfonyCatalogue->add( - array($id => $message->getLocaleString()), + [$id => $message->getLocaleString()], $domain ); } - $tmpPath = sys_get_temp_dir().'/'.uniqid('translation', false); + $tmpPath = sys_get_temp_dir() . '/' . uniqid('translation', false); if (!is_dir($tmpPath) && false === @mkdir($tmpPath, 0777, true)) { throw new RuntimeException(sprintf('Could not create temporary directory "%s".', $tmpPath)); } - $this->dumper->dump($symfonyCatalogue, array( - 'path' => $tmpPath, - )); + $this->dumper->dump($symfonyCatalogue, ['path' => $tmpPath]); - if (!is_file($tmpFile = $tmpPath.'/'.$domain.'.'.$catalogue->getLocale().'.'.$this->format)) { + if (!is_file($tmpFile = $tmpPath . '/' . $domain . '.' . $catalogue->getLocale() . '.' . $this->format)) { throw new RuntimeException(sprintf('Could not find dumped translation file "%s".', $tmpFile)); } diff --git a/Translation/Dumper/XliffDumper.php b/Translation/Dumper/XliffDumper.php index 36d0f718..ad950a08 100644 --- a/Translation/Dumper/XliffDumper.php +++ b/Translation/Dumper/XliffDumper.php @@ -1,5 +1,7 @@ * @@ -18,18 +20,19 @@ namespace JMS\TranslationBundle\Translation\Dumper; -use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\JMSTranslationBundle; -use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message\XliffMessage; +use JMS\TranslationBundle\Model\MessageCatalogue; /** * XLIFF dumper. * * This dumper uses version 1.2 of the specification. * - * @see http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html * @author Johannes M. Schmitt + * + * @see http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html */ class XliffDumper implements DumperInterface { @@ -53,33 +56,21 @@ class XliffDumper implements DumperInterface */ private $addReferencePosition = true; - /** - * @param $bool - */ public function setAddDate($bool) { $this->addDate = (bool) $bool; } - /** - * @param $lang - */ public function setSourceLanguage($lang) { $this->sourceLanguage = $lang; } - /** - * @param $bool - */ public function setAddReference($bool) { $this->addReference = $bool; } - /** - * @param $bool - */ public function setAddReferencePosition($bool) { $this->addReferencePosition = $bool; @@ -88,6 +79,7 @@ public function setAddReferencePosition($bool) /** * @param MessageCatalogue $catalogue * @param MessageCatalogue|string $domain + * * @return string */ public function dump(MessageCatalogue $catalogue, $domain = 'messages') @@ -108,7 +100,7 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') } $file->setAttribute('source-language', $this->sourceLanguage); - $file->setAttribute('target-language', $catalogue->getLocale()); + $file->setAttribute('target-language', (string) $catalogue->getLocale()); $file->setAttribute('datatype', 'plaintext'); $file->setAttribute('original', 'not.available'); @@ -119,7 +111,6 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') $tool->setAttribute('tool-name', 'JMSTranslationBundle'); $tool->setAttribute('tool-version', JMSTranslationBundle::VERSION); - $header->appendChild($note = $doc->createElement('note')); $note->appendChild($doc->createTextNode('The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.')); @@ -127,8 +118,8 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') foreach ($catalogue->getDomain($domain)->all() as $id => $message) { $body->appendChild($unit = $doc->createElement('trans-unit')); - $unit->setAttribute('id', hash('sha1', $id)); - $unit->setAttribute('resname', $id); + $unit->setAttribute('id', hash('sha1', (string) $id)); + $unit->setAttribute('resname', (string) $id); if ($message instanceof XliffMessage && $message->isApproved()) { $unit->setAttribute('approved', 'yes'); } @@ -157,14 +148,14 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') if ($message instanceof XliffMessage) { if ($message->hasState()) { - $target->setAttribute('state', $message->getState()); + $target->setAttribute('state', (string) $message->getState()); } - + if ($message->hasNotes()) { foreach ($message->getNotes() as $note) { $noteNode = $unit->appendChild($doc->createElement('note', $note['text'])); if (isset($note['from'])) { - $noteNode->setAttribute('from', $note['from']); + $noteNode->setAttribute('from', $note['from']); } } } @@ -182,11 +173,11 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') if ($this->addReferencePosition) { if ($source->getLine()) { - $refFile->setAttribute('line', $source->getLine()); + $refFile->setAttribute('line', (string) $source->getLine()); } if ($source->getColumn()) { - $refFile->setAttribute('column', $source->getColumn()); + $refFile->setAttribute('column', (string) $source->getColumn()); } } @@ -199,7 +190,7 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') } if ($meaning = $message->getMeaning()) { - $unit->setAttribute('extradata', 'Meaning: '.$meaning); + $unit->setAttribute('extradata', 'Meaning: ' . $meaning); } } @@ -211,11 +202,12 @@ public function dump(MessageCatalogue $catalogue, $domain = 'messages') * If the reference position are not used, the reference file will be write once * * @param array $sources - * @return FileSource + * + * @return array */ protected function getSortedSources(array $sources) { - $indexedSources = array(); + $indexedSources = []; foreach ($sources as $source) { if ($source instanceof FileSource) { $index = $source->getPath(); diff --git a/Translation/Dumper/YamlDumper.php b/Translation/Dumper/YamlDumper.php index 8f93ce40..a67b782c 100644 --- a/Translation/Dumper/YamlDumper.php +++ b/Translation/Dumper/YamlDumper.php @@ -1,5 +1,7 @@ * @@ -18,8 +20,8 @@ namespace JMS\TranslationBundle\Translation\Dumper; -use JMS\TranslationBundle\Util\Writer; use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Util\Writer; use Symfony\Component\Yaml\Inline; class YamlDumper extends ArrayStructureDumper @@ -29,9 +31,6 @@ class YamlDumper extends ArrayStructureDumper */ private $writer; - /** - * YamlDumper constructor. - */ public function __construct() { $this->writer = new Writer(); @@ -39,6 +38,7 @@ public function __construct() /** * @param array $structure + * * @return string */ protected function dumpStructure(array $structure) @@ -66,11 +66,11 @@ private function dumpStructureRecursively(array $structure) } if ($desc) { - $desc = str_replace(array("\r\n", "\n", "\r", "\t"), array('\r\n', '\n', '\r', '\t'), $desc); - $this->writer->writeln('# Desc: '.$desc); + $desc = str_replace(["\r\n", "\n", "\r", "\t"], ['\r\n', '\n', '\r', '\t'], $desc); + $this->writer->writeln('# Desc: ' . $desc); } if ($meaning) { - $this->writer->writeln('# Meaning: '.$meaning); + $this->writer->writeln('# Meaning: ' . $meaning); } } elseif (!$isFirst) { $this->writer->write("\n"); @@ -78,10 +78,10 @@ private function dumpStructureRecursively(array $structure) $isFirst = false; $precededByMessage = $isMessage; - $this->writer->write(Inline::dump($k).':'); + $this->writer->write(Inline::dump($k) . ':'); if ($isMessage) { - $this->writer->write(' '.Inline::dump($v->getLocaleString())); + $this->writer->write(' ' . Inline::dump($v->getLocaleString())); if ($v->isNew()) { $this->writer->write(' # FIXME'); diff --git a/Translation/Extractor/File/AuthenticationMessagesExtractor.php b/Translation/Extractor/File/AuthenticationMessagesExtractor.php index aca6eb66..1eaff0e5 100644 --- a/Translation/Extractor/File/AuthenticationMessagesExtractor.php +++ b/Translation/Extractor/File/AuthenticationMessagesExtractor.php @@ -1,5 +1,7 @@ * @@ -18,20 +20,22 @@ namespace JMS\TranslationBundle\Translation\Extractor\File; -use JMS\TranslationBundle\Exception\RuntimeException; -use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Annotation\Meaning; +use Doctrine\Common\Annotations\DocParser; use JMS\TranslationBundle\Annotation\Desc; use JMS\TranslationBundle\Annotation\Ignore; +use JMS\TranslationBundle\Annotation\Meaning; +use JMS\TranslationBundle\Exception\RuntimeException; +use JMS\TranslationBundle\Logger\LoggerAwareInterface; +use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; -use Doctrine\Common\Annotations\DocParser; -use JMS\TranslationBundle\Logger\LoggerAwareInterface; use JMS\TranslationBundle\Translation\FileSourceFactory; use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; use Psr\Log\LoggerInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Twig\Node\Node as TwigNode; class AuthenticationMessagesExtractor implements LoggerAwareInterface, FileVisitorInterface, NodeVisitor { @@ -85,11 +89,6 @@ class AuthenticationMessagesExtractor implements LoggerAwareInterface, FileVisit */ private $logger; - /** - * AuthenticationMessagesExtractor constructor. - * @param DocParser $parser - * @param FileSourceFactory $fileSourceFactory - */ public function __construct(DocParser $parser, FileSourceFactory $fileSourceFactory) { $this->docParser = $parser; @@ -98,17 +97,11 @@ public function __construct(DocParser $parser, FileSourceFactory $fileSourceFact $this->traverser->addVisitor($this); } - /** - * @param LoggerInterface $logger - */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; } - /** - * @param $domain - */ public function setDomain($domain) { $this->domain = $domain; @@ -116,28 +109,31 @@ public function setDomain($domain) /** * @param Node $node + * * @return void */ public function enterNode(Node $node) { if ($node instanceof Node\Stmt\Namespace_) { if (isset($node->name)) { - $this->namespace = implode('\\', $node->name->parts); + $this->namespace = property_exists($node->name, 'parts') ? implode('\\', $node->name->parts) : $node->name->name; } return; } if ($node instanceof Node\Stmt\Class_) { - $name = '' === $this->namespace ? $node->name : $this->namespace.'\\'.$node->name; + $name = '' === $this->namespace ? (string) $node->name : $this->namespace . '\\' . $node->name; if (!class_exists($name)) { return; } $ref = new \ReflectionClass($name); - if (!$ref->isSubclassOf('Symfony\Component\Security\Core\Exception\AuthenticationException') - && $ref->name !== 'Symfony\Component\Security\Core\Exception\AuthenticationException') { + if ( + !$ref->isSubclassOf(AuthenticationException::class) + && $ref->name !== 'Symfony\Component\Security\Core\Exception\AuthenticationException' + ) { return; } @@ -154,7 +150,7 @@ public function enterNode(Node $node) } if ($node instanceof Node\Stmt\ClassMethod) { - if ('getmessagekey' === strtolower($node->name)) { + if ('getmessagekey' === strtolower((string) $node->name)) { $this->inGetMessageKey = true; } @@ -172,7 +168,7 @@ public function enterNode(Node $node) $ignore = false; $desc = $meaning = null; if ($docComment = $node->getDocComment()) { - foreach ($this->docParser->parse($docComment->getText(), 'file '.$this->file.' near line '.$node->getLine()) as $annot) { + foreach ($this->docParser->parse($docComment->getText(), 'file ' . $this->file . ' near line ' . $node->getLine()) as $annot) { if ($annot instanceof Ignore) { $ignore = true; } elseif ($annot instanceof Desc) { @@ -201,8 +197,7 @@ public function enterNode(Node $node) $message = Message::create($node->expr->value, $this->domain) ->setDesc($desc) ->setMeaning($meaning) - ->addSource($this->fileSourceFactory->create($this->file, $node->expr->getLine())) - ; + ->addSource($this->fileSourceFactory->create($this->file, $node->expr->getLine())); $this->catalogue->add($message); } @@ -222,7 +217,8 @@ public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, ar /** * @param Node $node - * @return false|null|Node|\PhpParser\Node[]|void + * + * @return false|Node|Node[]|void|null */ public function leaveNode(Node $node) { @@ -241,7 +237,8 @@ public function leaveNode(Node $node) /** * @param array $nodes - * @return null|\PhpParser\Node[]|void + * + * @return Node[]|void|null */ public function beforeTraverse(array $nodes) { @@ -249,26 +246,18 @@ public function beforeTraverse(array $nodes) /** * @param array $nodes - * @return null|\PhpParser\Node[]|void + * + * @return Node[]|void|null */ public function afterTraverse(array $nodes) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - */ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - * @param \Twig_Node $ast - */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) { } } diff --git a/Translation/Extractor/File/DefaultPhpFileExtractor.php b/Translation/Extractor/File/DefaultPhpFileExtractor.php index f129c92f..4d9287c3 100644 --- a/Translation/Extractor/File/DefaultPhpFileExtractor.php +++ b/Translation/Extractor/File/DefaultPhpFileExtractor.php @@ -1,5 +1,7 @@ * @@ -18,22 +20,23 @@ namespace JMS\TranslationBundle\Translation\Extractor\File; -use JMS\TranslationBundle\Exception\RuntimeException; use Doctrine\Common\Annotations\DocParser; -use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Annotation\Meaning; use JMS\TranslationBundle\Annotation\Desc; use JMS\TranslationBundle\Annotation\Ignore; -use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; -use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Annotation\Meaning; +use JMS\TranslationBundle\Exception\RuntimeException; use JMS\TranslationBundle\Logger\LoggerAwareInterface; +use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; use JMS\TranslationBundle\Translation\FileSourceFactory; use PhpParser\Comment\Doc; use PhpParser\Node; +use PhpParser\Node\Scalar\String_; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; -use PhpParser\Node\Scalar\String_; use Psr\Log\LoggerInterface; +use Twig\Node\Node as TwigNode; /** * This parser can extract translation information from PHP files. @@ -48,7 +51,7 @@ class DefaultPhpFileExtractor implements LoggerAwareInterface, FileVisitorInterf * @var FileSourceFactory */ private $fileSourceFactory; - + /** * @var NodeTraverser */ @@ -84,16 +87,11 @@ class DefaultPhpFileExtractor implements LoggerAwareInterface, FileVisitorInterf * * @var array method => position of the "domain" parameter */ - protected $methodsToExtractFrom = array( + protected $methodsToExtractFrom = [ 'trans' => 2, 'transchoice' => 3, - ); + ]; - /** - * DefaultPhpFileExtractor constructor. - * @param DocParser $docParser - * @param FileSourceFactory $fileSourceFactory - */ public function __construct(DocParser $docParser, FileSourceFactory $fileSourceFactory) { $this->docParser = $docParser; @@ -102,9 +100,6 @@ public function __construct(DocParser $docParser, FileSourceFactory $fileSourceF $this->traverser->addVisitor($this); } - /** - * @param LoggerInterface $logger - */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; @@ -112,6 +107,7 @@ public function setLogger(LoggerInterface $logger) /** * @param Node $node + * * @return void */ public function enterNode(Node $node) @@ -120,9 +116,12 @@ public function enterNode(Node $node) if ($node instanceof Node\Expr\MethodCall) { $methodCallNodeName = $node->name instanceof Node\Identifier ? $node->name->name : $node->name; } - if (!is_string($methodCallNodeName) - || !in_array(strtolower($methodCallNodeName), array_map('strtolower', array_keys($this->methodsToExtractFrom)))) { + if ( + !is_string($methodCallNodeName) + || !in_array(strtolower($methodCallNodeName), array_map('strtolower', array_keys($this->methodsToExtractFrom))) + ) { $this->previousNode = $node; + return; } @@ -132,7 +131,7 @@ public function enterNode(Node $node) if ($docComment instanceof Doc) { $docComment = $docComment->getText(); } - foreach ($this->docParser->parse($docComment, 'file '.$this->file.' near line '.$node->getLine()) as $annot) { + foreach ($this->docParser->parse($docComment, 'file ' . $this->file . ' near line ' . $node->getLine()) as $annot) { if ($annot instanceof Ignore) { $ignore = true; } elseif ($annot instanceof Desc) { @@ -152,6 +151,7 @@ public function enterNode(Node $node) if ($this->logger) { $this->logger->error($message); + return; } @@ -159,25 +159,45 @@ public function enterNode(Node $node) } $id = $node->args[0]->value->value; - $index = $this->methodsToExtractFrom[strtolower($methodCallNodeName)]; - if (isset($node->args[$index])) { - if (!$node->args[$index]->value instanceof String_) { + $domainArg = null; + + if (isset($node->args[$index]) && $node->args[$index] instanceof Node\Arg && null === $node->args[$index]->name) { + $domainArg = $node->args[$index]; + } else { + foreach ($node->args as $arg) { + if (!$arg instanceof Node\Arg) { + continue; + } + + if (null !== $arg->name && 'domain' === $arg->name->name) { + $domainArg = $arg; + + break; + } + } + } + + if (null !== $domainArg) { + if ($domainArg->value instanceof Node\Expr\ConstFetch && 'null' === (string) $domainArg->value->name) { + $domain = 'messages'; + } elseif ($domainArg->value instanceof String_) { + $domain = $domainArg->value->value; + } else { if ($ignore) { return; } - $message = sprintf('Can only extract the translation domain from a scalar string, but got "%s". Please refactor your code to make it extractable, or add the doc comment /** @Ignore */ to this code element (in %s on line %d).', get_class($node->args[$index]->value), $this->file, $node->args[$index]->value->getLine()); + $message = sprintf('Can only extract the translation domain from a scalar string, but got "%s". Please refactor your code to make it extractable, or add the doc comment /** @Ignore */ to this code element (in %s on line %d).', get_class($domainArg->value), $this->file, $domainArg->value->getLine()); if ($this->logger) { $this->logger->error($message); + return; } throw new RuntimeException($message); } - - $domain = $node->args[$index]->value->value; } else { $domain = 'messages'; } @@ -203,6 +223,7 @@ public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, ar /** * @param array $nodes + * * @return void */ public function beforeTraverse(array $nodes) @@ -211,6 +232,7 @@ public function beforeTraverse(array $nodes) /** * @param Node $node + * * @return void */ public function leaveNode(Node $node) @@ -219,32 +241,25 @@ public function leaveNode(Node $node) /** * @param array $nodes + * * @return void */ public function afterTraverse(array $nodes) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - */ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - * @param \Twig_Node $ast - */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) { } /** * @param Node $node - * @return null|string + * + * @return string|null */ private function getDocCommentForNode(Node $node) { @@ -262,6 +277,7 @@ private function getDocCommentForNode(Node $node) return $comment->getText(); } elseif (null !== $this->previousNode && $this->previousNode->getDocComment() !== null) { $comment = $this->previousNode->getDocComment(); + return is_object($comment) ? $comment->getText() : $comment; } diff --git a/Translation/Extractor/File/FormExtractor.php b/Translation/Extractor/File/FormExtractor.php index a3715527..e7f8aed3 100644 --- a/Translation/Extractor/File/FormExtractor.php +++ b/Translation/Extractor/File/FormExtractor.php @@ -1,5 +1,7 @@ * @@ -18,22 +20,24 @@ namespace JMS\TranslationBundle\Translation\Extractor\File; -use JMS\TranslationBundle\Exception\RuntimeException; -use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Annotation\Meaning; +use Doctrine\Common\Annotations\DocParser; use JMS\TranslationBundle\Annotation\Desc; use JMS\TranslationBundle\Annotation\Ignore; -use Doctrine\Common\Annotations\DocParser; +use JMS\TranslationBundle\Annotation\Meaning; +use JMS\TranslationBundle\Exception\RuntimeException; +use JMS\TranslationBundle\Logger\LoggerAwareInterface; +use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Model\SourceInterface; use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; -use JMS\TranslationBundle\Logger\LoggerAwareInterface; use JMS\TranslationBundle\Translation\FileSourceFactory; use PhpParser\Comment\Doc; use PhpParser\Node; +use PhpParser\Node\Expr\ArrayItem; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; use Psr\Log\LoggerInterface; -use Symfony\Component\HttpKernel\Kernel; +use Twig\Node\Node as TwigNode; class FormExtractor implements FileVisitorInterface, LoggerAwareInterface, NodeVisitor { @@ -73,7 +77,7 @@ class FormExtractor implements FileVisitorInterface, LoggerAwareInterface, NodeV private $defaultDomain; /** - * @var string + * @var array */ private $defaultDomainMessages; @@ -82,11 +86,6 @@ class FormExtractor implements FileVisitorInterface, LoggerAwareInterface, NodeV */ private $attrNode; - /** - * FormExtractor constructor. - * @param DocParser $docParser - * @param FileSourceFactory $fileSourceFactory - */ public function __construct(DocParser $docParser, FileSourceFactory $fileSourceFactory) { $this->docParser = $docParser; @@ -97,13 +96,14 @@ public function __construct(DocParser $docParser, FileSourceFactory $fileSourceF /** * @param Node $node + * * @return null */ public function enterNode(Node $node) { if ($node instanceof Node\Stmt\Class_) { $this->defaultDomain = null; - $this->defaultDomainMessages = array(); + $this->defaultDomainMessages = []; } if ($node instanceof Node\Expr\MethodCall) { @@ -115,6 +115,7 @@ public function enterNode(Node $node) $name = strtolower($nodeName); if ('setdefaults' === $name || 'replacedefaults' === $name || 'setdefault' === $name) { $this->parseDefaultsCall($node); + return; } } @@ -133,7 +134,7 @@ public function enterNode(Node $node) // look for options containing a message foreach ($node->items as $item) { - if (!$item->key instanceof Node\Scalar\String_) { + if (!is_object($item) || !$item->key instanceof Node\Scalar\String_) { continue; } @@ -166,6 +167,12 @@ public function enterNode(Node $node) } $this->parseItem($item, $domain); break; + case 'constraints': + if ($this->parseConstraintNode($item, 'validators')) { + continue 2; + } + $this->parseItem($item, $domain); + break; } } } @@ -173,17 +180,14 @@ public function enterNode(Node $node) /** * @param Node $node - * @return null|string + * + * @return string|null */ public function getDomain(Node $node) { return $this->getDomainValueForKey('translation_domain', $node); } - /** - * @param Node $node - * @return null|string - */ public function getChoiceDomain(Node $node) { return $this->getDomainValueForKey('choice_translation_domain', $node); @@ -194,12 +198,12 @@ private function getDomainValueForKey($key, Node $node) $domain = null; foreach ($node->items as $item) { - if (!$item->key instanceof Node\Scalar\String_) { + if (!is_object($item) || !$item->key instanceof Node\Scalar\String_) { continue; } if ($key === $item->key->value) { - if ($item->value instanceof Node\Expr\ConstFetch && $item->value->name->parts[0] === 'false') { + if ($item->value instanceof Node\Expr\ConstFetch && $item->value->name === 'false') { $domain = false; break; } @@ -220,15 +224,17 @@ private function getDomainValueForKey($key, Node $node) * Returning true means either that regardless of whether * parsing has occurred or not, the enterNode function should move on to the next node item. * + * @internal + * * @param Node $item - * @param $domain + * @param string $domain + * * @return bool - * @internal */ protected function parseEmptyValueNode(Node $item, $domain) { // Skip empty_value when false - if ($item->value instanceof Node\Expr\ConstFetch && $item->value->name instanceof Node\Name && 'false' === $item->value->name->parts[0]) { + if ($item->value instanceof Node\Expr\ConstFetch && $item->value->name instanceof Node\Name && 'false' === (property_exists($item->value->name, 'parts') ? $item->value->name->parts[0] : $item->value->name->getFirst())) { return true; } @@ -250,11 +256,13 @@ protected function parseEmptyValueNode(Node $item, $domain) * Returning true means either that regardless of whether * parsing has occurred or not, the enterNode function should move on to the next node item. * + * @internal + * * @param Node $item * @param Node $node - * @param $domain + * @param string $domain + * * @return bool - * @internal */ protected function parseChoiceNode(Node $item, Node $node, $domain) { @@ -263,23 +271,11 @@ protected function parseChoiceNode(Node $item, Node $node, $domain) return true; } - //Checking for the choice_as_values in the same form item - $choicesAsValues = false; - foreach ($node->items as $choiceItem) { - if ($choiceItem->key !== null && 'choices_as_values' === $choiceItem->key->value) { - $choicesAsValues = ($choiceItem->value->name->parts[0] === 'true'); - } - } - - foreach ($item->value->items as $subItem) { - // If we have a choice as value that differ from the Symfony default strategy - // we should invert the key and the value - if (Kernel::VERSION_ID < 30000 && $choicesAsValues === true || Kernel::VERSION_ID >= 30000) { - $newItem = clone $subItem; - $newItem->key = $subItem->value; - $newItem->value = $subItem->key; - $subItem = $newItem; - } + foreach ($item->value->items as $index => $subItem) { + $newItem = clone $subItem; + $newItem->key = $subItem->value; + $newItem->value = $subItem->key ?? new Node\Scalar\LNumber($index); + $subItem = $newItem; $this->parseItem($subItem, $domain); } @@ -292,10 +288,12 @@ protected function parseChoiceNode(Node $item, Node $node, $domain) * Returning true means either that regardless of whether * parsing has occurred or not, the enterNode function should move on to the next node item. * + * @internal + * * @param Node $item - * @param $domain + * @param string $domain + * * @return bool - * @internal */ protected function parseAttrNode(Node $item, $domain) { @@ -304,10 +302,10 @@ protected function parseAttrNode(Node $item, $domain) } foreach ($item->value->items as $sitem) { - if ('placeholder' == $sitem->key->value) { + if ('placeholder' === $sitem->key->value) { $this->parseItem($sitem, $domain); } - if ('title' == $sitem->key->value) { + if ('title' === $sitem->key->value) { $this->parseItem($sitem, $domain); } } @@ -315,21 +313,65 @@ protected function parseAttrNode(Node $item, $domain) return true; } - /** - * @param Node $node + * This parses any Node of type constraints. + * + * Returning true means either that regardless of whether + * parsing has occurred or not, the enterNode function should move on to the next node item. + * + * @internal + * + * @param Node $item + * @param string $domain + * + * @return bool */ + protected function parseConstraintNode(Node $item, $domain) + { + if (!$item->value instanceof Node\Expr\Array_) { + return true; + } + + foreach ($item->value->items as $subItem) { + if ( + !$subItem->value instanceof Node\Expr\New_ + || !$subItem->value->args + || !property_exists($subItem->value->args[0]->value, 'items') + ) { + continue; + } + + foreach ($subItem->value->args[0]->value->items as $messageItem) { + if (!$messageItem->key instanceof Node\Scalar\String_) { + continue; + } + if (strtolower(substr($messageItem->key->value, -7)) !== 'message') { + continue; + } + $this->parseItem($messageItem, $domain); + } + } + + return true; + } + private function parseDefaultsCall(Node $node) { - static $returningMethods = array( - 'setdefaults' => true, 'replacedefaults' => true, 'setoptional' => true, 'setrequired' => true, - 'setallowedvalues' => true, 'addallowedvalues' => true, 'setallowedtypes' => true, - 'addallowedtypes' => true, 'setfilters' => true - ); + static $returningMethods = [ + 'setdefaults' => true, + 'replacedefaults' => true, + 'setoptional' => true, + 'setrequired' => true, + 'setallowedvalues' => true, + 'addallowedvalues' => true, + 'setallowedtypes' => true, + 'addallowedtypes' => true, + 'setfilters' => true, + ]; $var = $node->var; while ($var instanceof Node\Expr\MethodCall) { - if (!isset($returningMethods[strtolower($var->name)])) { + if (!isset($returningMethods[strtolower((string) $var->name)])) { return; } @@ -345,12 +387,14 @@ private function parseDefaultsCall(Node $node) return; } - if (isset($node->args[1]) + if ( + isset($node->args[1]) && $node->args[0]->value instanceof Node\Scalar\String_ && $node->args[1]->value instanceof Node\Scalar\String_ && 'translation_domain' === $node->args[0]->value->value ) { $this->defaultDomain = $node->args[1]->value->value; + return; } @@ -362,7 +406,7 @@ private function parseDefaultsCall(Node $node) // check if a translation_domain is set as a default option $domain = null; foreach ($node->args[0]->value->items as $item) { - if (!$item->key instanceof Node\Scalar\String_) { + if (!is_object($item) || !$item->key instanceof Node\Scalar\String_) { continue; } @@ -377,7 +421,7 @@ private function parseDefaultsCall(Node $node) } /** - * @param $item + * @param ArrayItem $item * @param null $domain */ private function parseItem($item, $domain = null) @@ -394,13 +438,17 @@ private function parseItem($item, $domain = null) $docComment = $item->value->getDocComment(); } + if (!$docComment) { + $docComment = $item->getDocComment(); + } + $docComment = is_object($docComment) ? $docComment->getText() : null; if ($docComment) { if ($docComment instanceof Doc) { $docComment = $docComment->getText(); } - foreach ($this->docParser->parse($docComment, 'file '.$this->file.' near line '.$item->value->getLine()) as $annot) { + foreach ($this->docParser->parse($docComment, 'file ' . $this->file . ' near line ' . $item->value->getLine()) as $annot) { if ($annot instanceof Ignore) { $ignore = true; } elseif ($annot instanceof Desc) { @@ -412,9 +460,9 @@ private function parseItem($item, $domain = null) } // check if the value is explicitly set to false => e.g. for FormField that should be rendered without label - $ignore = $ignore || !$item->value instanceof Node\Scalar\String_ || $item->value->value == false; + $ignore = $ignore || !$item->value instanceof Node\Scalar\String_ || $item->value->value === false; - if (!$item->value instanceof Node\Scalar\String_ && !$item->value instanceof Node\Scalar\LNumber) { + if (!$item->value instanceof Node\Scalar\String_ && !$item->value instanceof Node\Scalar\LNumber && !$item->value instanceof Node\Scalar\Int_) { if ($ignore) { return; } @@ -438,12 +486,12 @@ private function parseItem($item, $domain = null) $id = $item->value->value; if (null === $domain) { - $this->defaultDomainMessages[] = array( + $this->defaultDomainMessages[] = [ 'id' => $id, 'source' => $source, 'desc' => $desc, - 'meaning' => $meaning - ); + 'meaning' => $meaning, + ]; } else { $this->addToCatalogue($id, $source, $domain, $desc, $meaning); } @@ -451,10 +499,10 @@ private function parseItem($item, $domain = null) /** * @param string $id - * @param string $source - * @param null|string $domain - * @param null|string $desc - * @param null|string $meaning + * @param SourceInterface $source + * @param string|null $domain + * @param string|null $desc + * @param string|null $meaning */ private function addToCatalogue($id, $source, $domain = null, $desc = null, $meaning = null) { @@ -497,7 +545,8 @@ public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, ar /** * @param Node $node - * @return null|\PhpParser\Node[]|void + * + * @return Node[]|void|null */ public function leaveNode(Node $node) { @@ -505,7 +554,8 @@ public function leaveNode(Node $node) /** * @param array $nodes - * @return null|\PhpParser\Node[]|void + * + * @return Node[]|void|null */ public function beforeTraverse(array $nodes) { @@ -513,32 +563,21 @@ public function beforeTraverse(array $nodes) /** * @param array $nodes - * @return null|\PhpParser\Node[]|void + * + * @return Node[]|void|null */ public function afterTraverse(array $nodes) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - */ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - * @param \Twig_Node $ast - */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) { } - /** - * @param LoggerInterface $logger - */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; diff --git a/Translation/Extractor/File/TranslationContainerExtractor.php b/Translation/Extractor/File/TranslationContainerExtractor.php index 796dfb88..5e565656 100644 --- a/Translation/Extractor/File/TranslationContainerExtractor.php +++ b/Translation/Extractor/File/TranslationContainerExtractor.php @@ -1,5 +1,7 @@ * @@ -25,6 +27,7 @@ use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; +use Twig\Node\Node as TwigNode; /** * Extracts translations from designated translation containers. @@ -41,11 +44,6 @@ class TranslationContainerExtractor implements FileVisitorInterface, NodeVisitor */ private $traverser; - /** - * @var string - */ - private $file; - /** * @var MessageCatalogue */ @@ -59,11 +57,8 @@ class TranslationContainerExtractor implements FileVisitorInterface, NodeVisitor /** * @var array */ - private $useStatements = array(); + private $useStatements = []; - /** - * TranslationContainerExtractor constructor. - */ public function __construct() { $this->traverser = new NodeTraverser(); @@ -72,22 +67,23 @@ public function __construct() /** * @param Node $node - * @return null|Node|void + * + * @return Node|void|null */ public function enterNode(Node $node) { if ($node instanceof Node\Stmt\Namespace_) { if (isset($node->name)) { - $this->namespace = implode('\\', $node->name->parts); + $this->namespace = property_exists($node->name, 'parts') ? implode('\\', $node->name->parts) : $node->name->name; } - $this->useStatements = array(); + $this->useStatements = []; return; } if ($node instanceof Node\Stmt\UseUse) { $nodeAliasName = is_string($node->alias) ? $node->alias : $node->getAlias()->name; - $this->useStatements[$nodeAliasName] = implode('\\', $node->name->parts); + $this->useStatements[$nodeAliasName] = property_exists($node->name, 'parts') ? implode('\\', $node->name->parts) : $node->name->name; return; } @@ -98,7 +94,7 @@ public function enterNode(Node $node) $isContainer = false; foreach ($node->implements as $interface) { - $name = implode('\\', $interface->parts); + $name = property_exists($interface, 'parts') ? implode('\\', $interface->parts) : $interface->name; if (isset($this->useStatements[$name])) { $name = $this->useStatements[$name]; } @@ -113,14 +109,14 @@ public function enterNode(Node $node) return; } - $messages = call_user_func(array($this->namespace.'\\'.$node->name, 'getTranslationMessages')); + $messages = call_user_func([$this->namespace . '\\' . $node->name, 'getTranslationMessages']); if (!is_array($messages)) { - throw new RuntimeException(sprintf('%s::getTranslationMessages() was expected to return an array of messages, but got %s.', $this->namespace.'\\'.$node->name, gettype($messages))); + throw new RuntimeException(sprintf('%s::getTranslationMessages() was expected to return an array of messages, but got %s.', $this->namespace . '\\' . $node->name, gettype($messages))); } foreach ($messages as $message) { if (!$message instanceof Message) { - throw new RuntimeException(sprintf('%s::getTranslationMessages() was expected to return an array of messages, but got an array which contains an item of type %s.', $this->namespace.'\\'.$node->name, gettype($message))); + throw new RuntimeException(sprintf('%s::getTranslationMessages() was expected to return an array of messages, but got an array which contains an item of type %s.', $this->namespace . '\\' . $node->name, gettype($message))); } $this->catalogue->add($message); @@ -134,13 +130,13 @@ public function enterNode(Node $node) */ public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast) { - $this->file = $file; $this->catalogue = $catalogue; $this->traverser->traverse($ast); } /** * @param array $nodes + * * @return void */ public function beforeTraverse(array $nodes) @@ -149,6 +145,7 @@ public function beforeTraverse(array $nodes) /** * @param Node $node + * * @return void */ public function leaveNode(Node $node) @@ -157,26 +154,18 @@ public function leaveNode(Node $node) /** * @param array $nodes + * * @return void */ public function afterTraverse(array $nodes) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - */ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - * @param \Twig_Node $ast - */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) { } } diff --git a/Translation/Extractor/File/TwigFileExtractor.php b/Translation/Extractor/File/TwigFileExtractor.php index d1ced5bf..7f21c8b7 100644 --- a/Translation/Extractor/File/TwigFileExtractor.php +++ b/Translation/Extractor/File/TwigFileExtractor.php @@ -1,5 +1,7 @@ * @@ -19,13 +21,19 @@ namespace JMS\TranslationBundle\Translation\Extractor\File; use JMS\TranslationBundle\Exception\RuntimeException; -use JMS\TranslationBundle\Translation\FileSourceFactory; -use Symfony\Bridge\Twig\Node\TransNode; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; - -class TwigFileExtractor extends \Twig_BaseNodeVisitor implements FileVisitorInterface +use JMS\TranslationBundle\Translation\FileSourceFactory; +use Symfony\Bridge\Twig\Node\TransNode; +use Twig\Environment; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Expression\FilterExpression; +use Twig\Node\Node; +use Twig\NodeTraverser; +use Twig\NodeVisitor\AbstractNodeVisitor; + +class TwigFileExtractor extends AbstractNodeVisitor implements FileVisitorInterface { /** * @var FileSourceFactory @@ -43,32 +51,25 @@ class TwigFileExtractor extends \Twig_BaseNodeVisitor implements FileVisitorInte private $catalogue; /** - * @var \Twig_NodeTraverser + * @var NodeTraverser */ private $traverser; /** * @var array */ - private $stack = array(); + private $stack = []; - /** - * TwigFileExtractor constructor. - * @param \Twig_Environment $env - * @param FileSourceFactory $fileSourceFactory - */ - public function __construct(\Twig_Environment $env, FileSourceFactory $fileSourceFactory) + public function __construct(Environment $env, FileSourceFactory $fileSourceFactory) { $this->fileSourceFactory = $fileSourceFactory; - $this->traverser = new \Twig_NodeTraverser($env, array($this)); + $this->traverser = new NodeTraverser($env, [$this]); } /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node + * @return Node */ - protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) + protected function doEnterNode(Node $node, Environment $env) { $this->stack[] = $node; @@ -83,25 +84,27 @@ protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) $message = new Message($id, $domain); $message->addSource($this->fileSourceFactory->create($this->file, $node->getTemplateLine())); $this->catalogue->add($message); - } elseif ($node instanceof \Twig_Node_Expression_Filter) { + } elseif ($node instanceof FilterExpression) { $name = $node->getNode('filter')->getAttribute('value'); if ('trans' === $name || 'transchoice' === $name) { $idNode = $node->getNode('node'); - if (!$idNode instanceof \Twig_Node_Expression_Constant) { + if (!$idNode instanceof ConstantExpression) { return $node; + // FIXME: see below // throw new \RuntimeException(sprintf('Cannot infer translation id from node "%s". Please refactor to only translate constants.', get_class($idNode))); } $id = $idNode->getAttribute('value'); - $index = 'trans' === $name ? 1 : 2; - $domain = 'messages'; - $arguments = $node->getNode('arguments'); - if ($arguments->hasNode($index)) { - $argument = $arguments->getNode($index); - if (!$argument instanceof \Twig_Node_Expression_Constant) { + $index = $name === 'trans' ? 1 : 2; + $domain = 'messages'; + $arguments = iterator_to_array($node->getNode('arguments')); + if (isset($arguments[$index])) { + $argument = $arguments[$index]; + if (! $argument instanceof ConstantExpression) { return $node; + // FIXME: Throw exception if there is some way for the user to turn this off // on a case-by-case basis, similar to @Ignore in PHP } @@ -112,24 +115,24 @@ protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) $message = new Message($id, $domain); $message->addSource($this->fileSourceFactory->create($this->file, $node->getTemplateLine())); - for ($i=count($this->stack)-2; $i>=0; $i-=1) { - if (!$this->stack[$i] instanceof \Twig_Node_Expression_Filter) { + for ($i = count($this->stack) - 2; $i >= 0; $i -= 1) { + if (!$this->stack[$i] instanceof FilterExpression) { break; } $name = $this->stack[$i]->getNode('filter')->getAttribute('value'); - if ('desc' === $name || 'meaning' === $name) { - $arguments = $this->stack[$i]->getNode('arguments'); - if (!$arguments->hasNode(0)) { + if ($name === 'desc' || $name === 'meaning') { + $arguments = iterator_to_array($this->stack[$i]->getNode('arguments')); + if (! isset($arguments[0])) { throw new RuntimeException(sprintf('The "%s" filter requires exactly one argument, the description text.', $name)); } - $text = $arguments->getNode(0); - if (!$text instanceof \Twig_Node_Expression_Constant) { + $text = $arguments[0]; + if (! $text instanceof ConstantExpression) { throw new RuntimeException(sprintf('The first argument of the "%s" filter must be a constant expression, such as a string.', $name)); } - $message->{'set'.$name}($text->getAttribute('value')); + $message->{'set' . $name}($text->getAttribute('value')); } elseif ('trans' === $name) { break; } @@ -150,12 +153,7 @@ public function getPriority() return 0; } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - * @param \Twig_Node $ast - */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, Node $ast) { $this->file = $file; $this->catalogue = $catalogue; @@ -167,10 +165,8 @@ public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \ * If the current Twig Node has embedded templates, we want to travese these templates * in the same manner as we do the main twig template to ensure all translations are * caught. - * - * @param \Twig_Node $node */ - private function traverseEmbeddedTemplates(\Twig_Node $node) + private function traverseEmbeddedTemplates(Node $node) { $templates = $node->getAttribute('embedded_templates'); @@ -183,21 +179,15 @@ private function traverseEmbeddedTemplates(\Twig_Node $node) } /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node + * @return Node */ - protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) + protected function doLeaveNode(Node $node, Environment $env) { array_pop($this->stack); return $node; } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - */ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { } diff --git a/Translation/Extractor/File/ValidationContextExtractor.php b/Translation/Extractor/File/ValidationContextExtractor.php index 7c244cf0..826283b0 100644 --- a/Translation/Extractor/File/ValidationContextExtractor.php +++ b/Translation/Extractor/File/ValidationContextExtractor.php @@ -1,5 +1,7 @@ * @@ -28,12 +30,8 @@ use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; use SplFileInfo; +use Twig\Node\Node as TwigNode; -/** - * Class ValidationContextExtractor - * - * Extracts - */ class ValidationContextExtractor implements FileVisitorInterface, NodeVisitor { /** @@ -74,11 +72,6 @@ class ValidationContextExtractor implements FileVisitorInterface, NodeVisitor private $source; private $fileSourceFactory; - /** - * ValidationContextExtractor constructor. - * - * @param FileSourceFactory $fileSourceFactory - */ public function __construct(FileSourceFactory $fileSourceFactory) { $this->fileSourceFactory = $fileSourceFactory; @@ -111,7 +104,7 @@ public function visitPhpFile(SplFileInfo $file, MessageCatalogue $catalogue, arr /** * {@inheritdoc} */ - public function visitTwigFile(SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) { } @@ -135,7 +128,8 @@ public function enterNode(Node $node) if ($node instanceof Node\Stmt\Use_) { foreach ($node->uses as $use) { - $this->aliases[$use->alias] = (string) $use->name; + $parts = explode('\\', (string) $use->name); + $this->aliases[end($parts)] = (string) $use->name; } return; @@ -149,7 +143,7 @@ public function enterNode(Node $node) $param1 = $params[0]; $paramClass = $this->resolveAlias((string) $param1->type); if (is_subclass_of($paramClass, '\Symfony\Component\Validator\Context\ExecutionContextInterface')) { - $this->contextVariable = $param1->name; + $this->contextVariable = $param1->getType(); } return; @@ -160,9 +154,6 @@ public function enterNode(Node $node) } } - /** - * @param Node\Expr\MethodCall $node - */ private function parseMethodCall(Node\Expr\MethodCall $node) { if (!$this->contextVariable) { @@ -173,7 +164,7 @@ private function parseMethodCall(Node\Expr\MethodCall $node) $this->parseMethodCall($node->var); } - if ($node->name === 'buildViolation') { + if ((string) $node->name === 'buildViolation') { $this->id = null; $this->domain = null; @@ -184,15 +175,15 @@ private function parseMethodCall(Node\Expr\MethodCall $node) $this->source = $this->fileSourceFactory->create($this->file, $arg1->value->getLine()); } } - } elseif ($node->name === 'setTranslationDomain') { + } elseif ((string) $node->name === 'setTranslationDomain') { if ($node->args) { $arg1 = $node->args[0]; if ($arg1->value instanceof Node\Scalar\String_) { $this->domain = $arg1->value->value; } } - } elseif ($node->name === 'addViolation') { - if ($this->id and $this->source) { + } elseif ((string) $node->name === 'addViolation') { + if ($this->id && $this->source) { $this->messages[] = [ 'id' => $this->id, 'source' => $this->source, @@ -244,13 +235,8 @@ private function addToCatalogue($id, SourceInterface $source, $domain = null) $this->catalogue->add($message); } - /** - * @param $class - * - * @return string - */ - private function resolveAlias($class) + private function resolveAlias(string $class): string { - return isset($this->aliases[$class]) ? $this->aliases[$class] : $class; + return $this->aliases[$class] ?: $class; } } diff --git a/Translation/Extractor/File/ValidationExtractor.php b/Translation/Extractor/File/ValidationExtractor.php index 1996f6d6..4fc01f99 100644 --- a/Translation/Extractor/File/ValidationExtractor.php +++ b/Translation/Extractor/File/ValidationExtractor.php @@ -1,5 +1,7 @@ * @@ -18,15 +20,16 @@ namespace JMS\TranslationBundle\Translation\Extractor\File; +use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; use Symfony\Component\Validator\MetadataFactoryInterface as LegacyMetadataFactoryInterface; -use JMS\TranslationBundle\Model\Message; -use JMS\TranslationBundle\Model\MessageCatalogue; -use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface; +use Twig\Node\Node as TwigNode; /** * Extracts translations validation constraints. @@ -45,11 +48,6 @@ class ValidationExtractor implements FileVisitorInterface, NodeVisitor */ private $traverser; - /** - * @var \SplFileInfo - */ - private $file; - /** * @var MessageCatalogue */ @@ -60,18 +58,16 @@ class ValidationExtractor implements FileVisitorInterface, NodeVisitor */ private $namespace = ''; - /** - * ValidationExtractor constructor. - * @param $metadataFactory - */ public function __construct($metadataFactory) { - if (! ( + if ( + ! ( $metadataFactory instanceof MetadataFactoryInterface || $metadataFactory instanceof LegacyMetadataFactoryInterface || $metadataFactory instanceof ClassMetadataFactoryInterface - )) { - throw new \InvalidArgumentException(sprintf('%s expects an instance of MetadataFactoryInterface or ClassMetadataFactoryInterface', get_class($this))); + ) + ) { + throw new \InvalidArgumentException(sprintf('%s expects an instance of MetadataFactoryInterface or ClassMetadataFactoryInterface', static::class)); } $this->metadataFactory = $metadataFactory; @@ -81,13 +77,14 @@ public function __construct($metadataFactory) /** * @param Node $node + * * @return void */ public function enterNode(Node $node) { if ($node instanceof Node\Stmt\Namespace_) { if (isset($node->name)) { - $this->namespace = implode('\\', $node->name->parts); + $this->namespace = property_exists($node->name, 'parts') ? implode('\\', $node->name->parts) : $node->name->name; } return; @@ -97,13 +94,13 @@ public function enterNode(Node $node) return; } - $name = '' === $this->namespace ? $node->name : $this->namespace.'\\'.$node->name; + $name = '' === $this->namespace ? (string) $node->name : $this->namespace . '\\' . $node->name; if (!class_exists($name)) { return; } - $metadata = ($this->metadataFactory instanceof ClassMetadataFactoryInterface)? $this->metadataFactory->getClassMetadata($name) : $this->metadataFactory->getMetadataFor($name); + $metadata = $this->metadataFactory instanceof ClassMetadataFactoryInterface ? $this->metadataFactory->getClassMetadata($name) : $this->metadataFactory->getMetadataFor($name); if (!$metadata->hasConstraints() && !count($metadata->getConstrainedProperties())) { return; } @@ -123,7 +120,6 @@ public function enterNode(Node $node) */ public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast) { - $this->file = $file; $this->namespace = ''; $this->catalogue = $catalogue; $this->traverser->traverse($ast); @@ -131,6 +127,7 @@ public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, ar /** * @param array $nodes + * * @return void */ public function beforeTraverse(array $nodes) @@ -139,6 +136,7 @@ public function beforeTraverse(array $nodes) /** * @param Node $node + * * @return void */ public function leaveNode(Node $node) @@ -147,26 +145,18 @@ public function leaveNode(Node $node) /** * @param array $nodes + * * @return void */ public function afterTraverse(array $nodes) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - */ public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { } - /** - * @param \SplFileInfo $file - * @param MessageCatalogue $catalogue - * @param \Twig_Node $ast - */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, TwigNode $ast) { } @@ -178,7 +168,7 @@ private function extractFromConstraints(array $constraints) foreach ($constraints as $constraint) { $ref = new \ReflectionClass($constraint); $defaultValues = $ref->getDefaultProperties(); - + $defaultParameters = null !== $ref->getConstructor() ? $ref->getConstructor()->getParameters() : []; $properties = $ref->getProperties(); foreach ($properties as $property) { @@ -187,9 +177,18 @@ private function extractFromConstraints(array $constraints) // If the property ends with 'Message' if (strtolower(substr($propName, -1 * strlen('Message'))) === 'message') { // If it is different from the default value - if ($defaultValues[$propName] !== $constraint->{$propName}) { + if (array_key_exists($propName, $defaultValues) && $defaultValues[$propName] !== $constraint->{$propName}) { $message = new Message($constraint->{$propName}, 'validators'); $this->catalogue->add($message); + } elseif (method_exists($property, 'isPromoted') && $property->isPromoted()) { + foreach ($defaultParameters as $defaultParameter) { + if ($defaultParameter->getName() === $propName && $defaultParameter->isDefaultValueAvailable() && $defaultParameter->getDefaultValue() !== $constraint->{$propName}) { + $message = new Message($constraint->{$propName}, 'validators'); + $this->catalogue->add($message); + + break; + } + } } } } diff --git a/Translation/Extractor/FileExtractor.php b/Translation/Extractor/FileExtractor.php index 6321b34b..b7a6c576 100644 --- a/Translation/Extractor/FileExtractor.php +++ b/Translation/Extractor/FileExtractor.php @@ -1,5 +1,7 @@ * @@ -18,18 +20,22 @@ namespace JMS\TranslationBundle\Translation\Extractor; -use JMS\TranslationBundle\Twig\DefaultApplyingNodeVisitor; use JMS\TranslationBundle\Exception\InvalidArgumentException; +use JMS\TranslationBundle\Logger\LoggerAwareInterface; +use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\ExtractorInterface; +use JMS\TranslationBundle\Twig\DefaultApplyingNodeVisitor; +use JMS\TranslationBundle\Twig\RemovingNodeVisitor; use PhpParser\Error; use PhpParser\Lexer; use PhpParser\Parser; use PhpParser\ParserFactory; use Psr\Log\LoggerInterface; -use JMS\TranslationBundle\Logger\LoggerAwareInterface; -use JMS\TranslationBundle\Twig\RemovingNodeVisitor; -use JMS\TranslationBundle\Translation\ExtractorInterface; -use JMS\TranslationBundle\Model\MessageCatalogue; use Symfony\Component\Finder\Finder; +use Twig\Environment; +use Twig\Loader\ArrayLoader; +use Twig\NodeVisitor\NodeVisitorInterface; +use Twig\Source; /** * File-based extractor. @@ -39,7 +45,7 @@ class FileExtractor implements ExtractorInterface, LoggerAwareInterface { /** - * @var \Twig_Environment + * @var Environment */ private $twig; @@ -64,24 +70,24 @@ class FileExtractor implements ExtractorInterface, LoggerAwareInterface private $directory; /** - * @var RemovingNodeVisitor|\Twig_NodeVisitorInterface + * @var RemovingNodeVisitor|NodeVisitorInterface */ private $removingTwigVisitor; /** - * @var DefaultApplyingNodeVisitor|RemovingNodeVisitor|\Twig_NodeVisitorInterface + * @var DefaultApplyingNodeVisitor|RemovingNodeVisitor|NodeVisitorInterface */ private $defaultApplyingTwigVisitor; /** * @var array */ - private $excludedNames = array(); + private $excludedNames = []; /** * @var array */ - private $excludedDirs = array(); + private $excludedDirs = []; /** * @var LoggerInterface @@ -89,20 +95,19 @@ class FileExtractor implements ExtractorInterface, LoggerAwareInterface private $logger; /** - * FileExtractor constructor. - * @param \Twig_Environment $twig + * @param Environment $twig * @param LoggerInterface $logger * @param array $visitors */ - public function __construct(\Twig_Environment $twig, LoggerInterface $logger, array $visitors) + public function __construct(Environment $twig, LoggerInterface $logger, array $visitors) { $this->twig = $twig; $this->visitors = $visitors; $this->setLogger($logger); $lexer = new Lexer(); - if (class_exists('PhpParser\ParserFactory')) { + if (class_exists(ParserFactory::class)) { $factory = new ParserFactory(); - $this->phpParser = $factory->create(ParserFactory::PREFER_PHP7, $lexer); + $this->phpParser = \method_exists($factory, 'create') ? $factory->create(ParserFactory::PREFER_PHP7, $lexer) : $factory->createForNewestSupportedVersion(); } else { $this->phpParser = new Parser($lexer); } @@ -119,13 +124,10 @@ public function __construct(\Twig_Environment $twig, LoggerInterface $logger, ar public function reset() { - $this->excludedNames = array(); - $this->excludedDirs = array(); + $this->excludedNames = []; + $this->excludedDirs = []; } - /** - * @param LoggerInterface $logger - */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; @@ -139,9 +141,6 @@ public function setLogger(LoggerInterface $logger) } } - /** - * @param $directory - */ public function setDirectory($directory) { if (!is_dir($directory)) { @@ -177,6 +176,7 @@ public function setPattern(array $pattern) /** * @return MessageCatalogue + * * @throws \Exception */ public function extract() @@ -203,22 +203,22 @@ public function extract() } $curTwigLoader = $this->twig->getLoader(); - $this->twig->setLoader(new \Twig_Loader_Array(array())); + $this->twig->setLoader(new ArrayLoader([])); try { $catalogue = new MessageCatalogue(); foreach ($finder as $file) { $visitingMethod = 'visitFile'; - $visitingArgs = array($file, $catalogue); + $visitingArgs = [$file, $catalogue]; $this->logger->debug(sprintf('Parsing file "%s"', $file)); - if (false !== $pos = strrpos($file, '.')) { - $extension = substr($file, $pos + 1); + if (false !== $pos = strrpos((string) $file, '.')) { + $extension = substr((string) $file, $pos + 1); if ('php' === $extension) { try { - $ast = $this->phpParser->parse(file_get_contents($file)); + $ast = $this->phpParser->parse(file_get_contents((string) $file)); } catch (Error $ex) { throw new \RuntimeException(sprintf('Could not parse "%s": %s', $file, $ex->getMessage()), $ex->getCode(), $ex); } @@ -227,12 +227,12 @@ public function extract() $visitingArgs[] = $ast; } elseif ('twig' === $extension) { $visitingMethod = 'visitTwigFile'; - $visitingArgs[] = $this->twig->parse($this->twig->tokenize(new \Twig_Source(file_get_contents($file), (string) $file))); + $visitingArgs[] = $this->twig->parse($this->twig->tokenize(new Source(file_get_contents((string) $file), (string) $file))); } } foreach ($this->visitors as $visitor) { - call_user_func_array(array($visitor, $visitingMethod), $visitingArgs); + call_user_func_array([$visitor, $visitingMethod], $visitingArgs); } } @@ -248,7 +248,7 @@ public function extract() } return $catalogue; - } catch (\Exception $ex) { + } catch (\Throwable $ex) { if (null !== $curTwigLoader) { $this->twig->setLoader($curTwigLoader); } diff --git a/Translation/Extractor/FileVisitorInterface.php b/Translation/Extractor/FileVisitorInterface.php index 08f1de83..21e324aa 100644 --- a/Translation/Extractor/FileVisitorInterface.php +++ b/Translation/Extractor/FileVisitorInterface.php @@ -1,5 +1,7 @@ * @@ -19,6 +21,7 @@ namespace JMS\TranslationBundle\Translation\Extractor; use JMS\TranslationBundle\Model\MessageCatalogue; +use Twig\Node\Node; /** * File Visitor Interface. @@ -55,7 +58,7 @@ public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, ar * * @param \SplFileInfo $file * @param MessageCatalogue $catalogue - * @param \Twig_Node $ast + * @param Node $ast */ - public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast); + public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, Node $ast); } diff --git a/Translation/ExtractorInterface.php b/Translation/ExtractorInterface.php index 390e621f..c7afa30f 100644 --- a/Translation/ExtractorInterface.php +++ b/Translation/ExtractorInterface.php @@ -1,5 +1,7 @@ * diff --git a/Translation/ExtractorManager.php b/Translation/ExtractorManager.php index e8e253cb..82af80f3 100644 --- a/Translation/ExtractorManager.php +++ b/Translation/ExtractorManager.php @@ -1,5 +1,7 @@ * @@ -19,17 +21,17 @@ namespace JMS\TranslationBundle\Translation; use JMS\TranslationBundle\Exception\InvalidArgumentException; -use Psr\Log\LoggerInterface; +use JMS\TranslationBundle\Logger\LoggerAwareInterface; use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Extractor\FileExtractor; -use JMS\TranslationBundle\Logger\LoggerAwareInterface; +use Psr\Log\LoggerInterface; class ExtractorManager implements ExtractorInterface { private $fileExtractor; private $customExtractors; - private $directories = array(); - private $enabledExtractors = array(); + private $directories = []; + private $enabledExtractors = []; private $logger; /** @@ -37,7 +39,7 @@ class ExtractorManager implements ExtractorInterface * @param LoggerInterface $logger * @param array $customExtractors */ - public function __construct(FileExtractor $extractor, LoggerInterface $logger, array $customExtractors = array()) + public function __construct(FileExtractor $extractor, LoggerInterface $logger, array $customExtractors = []) { $this->fileExtractor = $extractor; $this->customExtractors = $customExtractors; @@ -46,14 +48,11 @@ public function __construct(FileExtractor $extractor, LoggerInterface $logger, a public function reset() { - $this->directories = array(); - $this->enabledExtractors = array(); + $this->directories = []; + $this->enabledExtractors = []; $this->fileExtractor->reset(); } - /** - * @param LoggerInterface $logger - */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; @@ -73,16 +72,17 @@ public function setLogger(LoggerInterface $logger) */ public function setDirectories(array $directories) { - $this->directories = array(); - + $this->directories = []; + foreach ($directories as $dir) { $this->addDirectory($dir); } } /** - * @param $directory - * @throws \JMS\TranslationBundle\Exception\InvalidArgumentException + * @param string $directory + * + * @throws InvalidArgumentException */ public function addDirectory($directory) { @@ -111,7 +111,8 @@ public function setExcludedNames(array $names) /** * @param array $aliases - * @throws \JMS\TranslationBundle\Exception\InvalidArgumentException + * + * @throws InvalidArgumentException */ public function setEnabledExtractors(array $aliases) { @@ -125,7 +126,7 @@ public function setEnabledExtractors(array $aliases) } /** - * @return \JMS\TranslationBundle\Model\MessageCatalogue + * @return MessageCatalogue */ public function extract() { diff --git a/Translation/FileSourceFactory.php b/Translation/FileSourceFactory.php index adcd6982..ad7a252a 100644 --- a/Translation/FileSourceFactory.php +++ b/Translation/FileSourceFactory.php @@ -1,5 +1,7 @@ * @@ -23,26 +25,32 @@ class FileSourceFactory { /** + * @deprecated Will be removed in 2.0. Use $baseDir instead. + * * @var string */ protected $kernelRoot; /** - * FileSourceFactory constructor. - * + * @var string + */ + protected $baseDir; + + /** * @param string $kernelRoot */ - public function __construct($kernelRoot) + public function __construct($kernelRoot, ?string $baseDir = null) { $this->kernelRoot = $kernelRoot; + $this->baseDir = $baseDir ?? $kernelRoot; } /** * Generate a new FileSource with a relative path. * * @param \SplFileInfo $file - * @param null|int $line - * @param null|int $column + * @param int|null $line + * @param int|null $column * * @return FileSource */ @@ -58,15 +66,15 @@ public function create(\SplFileInfo $file, $line = null, $column = null) */ private function getRelativePath($path) { - if (0 === strpos($path, $this->kernelRoot)) { - return substr($path, strlen($this->kernelRoot)); + if (0 === strpos($path, $this->baseDir)) { + return substr($path, strlen($this->baseDir)); } $relativePath = $ds = DIRECTORY_SEPARATOR; - $rootArray = explode($ds, $this->kernelRoot); + $rootArray = explode($ds, $this->baseDir); $pathArray = explode($ds, $path); - // Take the first directory in the kernelRoot tree + // Take the first directory in the baseDir tree foreach ($rootArray as $rootCurrentDirectory) { // Take the first directory from the path tree $pathCurrentDirectory = array_shift($pathArray); @@ -74,16 +82,16 @@ private function getRelativePath($path) // If they are not equal if ($pathCurrentDirectory !== $rootCurrentDirectory) { // Prepend $relativePath with "/.." - $relativePath = $ds.'..'.$relativePath; + $relativePath = $ds . '..' . $relativePath; if ($pathCurrentDirectory) { // Append the current directory - $relativePath .= $pathCurrentDirectory.$ds; + $relativePath .= $pathCurrentDirectory . $ds; } } } // Add the rest of the $pathArray on the relative directory - return rtrim($relativePath.implode($ds, $pathArray), '/'); + return rtrim($relativePath . implode($ds, $pathArray), '/'); } } diff --git a/Translation/FileWriter.php b/Translation/FileWriter.php index c9ef006e..690fa3e6 100644 --- a/Translation/FileWriter.php +++ b/Translation/FileWriter.php @@ -1,5 +1,7 @@ * @@ -37,28 +39,30 @@ class FileWriter /** * @param array $dumpers */ - public function __construct(array $dumpers = array()) + public function __construct(array $dumpers = []) { $this->dumpers = $dumpers; } /** - * @param \JMS\TranslationBundle\Model\MessageCatalogue $catalogue + * @param MessageCatalogue $catalogue * @param string $domain * @param string $filePath * @param string $format - * @throws \JMS\TranslationBundle\Exception\InvalidArgumentException + * + * @throws InvalidArgumentException */ public function write(MessageCatalogue $catalogue, $domain, $filePath, $format) { if (!isset($this->dumpers[$format])) { $allowedFormats = array_keys($this->dumpers); - $allowedFormatsString = join(',', $allowedFormats); + $allowedFormatsString = implode(',', $allowedFormats); + throw new InvalidArgumentException(sprintf('The format "%s" is not supported. Allowed formats:%s', $format, $allowedFormatsString)); } // sort messages before dumping - $catalogue->getDomain($domain)->sort(function ($a, $b) { + $catalogue->getDomain($domain)->sort(static function ($a, $b) { return strcmp($a->getId(), $b->getId()); }); diff --git a/Translation/Loader/LoaderInterface.php b/Translation/Loader/LoaderInterface.php index c02c2f51..6446b7e6 100644 --- a/Translation/Loader/LoaderInterface.php +++ b/Translation/Loader/LoaderInterface.php @@ -1,5 +1,7 @@ * @@ -37,6 +39,7 @@ interface LoaderInterface * @param mixed $resource * @param string $locale * @param string $domain + * * @return MessageCatalogue */ public function load($resource, $locale, $domain = 'messages'); diff --git a/Translation/Loader/Symfony/XliffLoader.php b/Translation/Loader/Symfony/XliffLoader.php index 668953c1..3440a301 100644 --- a/Translation/Loader/Symfony/XliffLoader.php +++ b/Translation/Loader/Symfony/XliffLoader.php @@ -1,5 +1,7 @@ * @@ -20,8 +22,9 @@ use JMS\TranslationBundle\Exception\RuntimeException; use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\TranslatorBagInterface; /** * XLIFF loader. @@ -32,15 +35,13 @@ * * @author Johannes M. Schmitt */ -class XliffLoader implements LoaderInterface +// phpcs:ignore +class XliffLoaderInternal { - /** - * {@inheritdoc} - */ - public function load($resource, $locale, $domain = 'messages') + protected function loadInternal($resource, $locale, $domain = 'messages') { $previous = libxml_use_internal_errors(true); - if (false === $xml = simplexml_load_file($resource)) { + if (false === $xml = simplexml_load_file((string) $resource)) { libxml_use_internal_errors($previous); $error = libxml_get_last_error(); @@ -53,14 +54,36 @@ public function load($resource, $locale, $domain = 'messages') $catalogue = new MessageCatalogue($locale); foreach ($xml->xpath('//xliff:trans-unit') as $translation) { - $id = ($resName = (string) $translation->attributes()->resname) - ? $resName : (string) $translation->source; + $resName = (string) $translation->attributes()->resname; + $id = $resName ?: (string) $translation->source; $catalogue->set($id, (string) $translation->target, $domain); } - $catalogue->addResource(new FileResource($resource)); + $catalogue->addResource(new FileResource((string) $resource)); return $catalogue; } } + +$isSf6 = method_exists(TranslatorBagInterface::class, 'getCatalogues'); + +if ($isSf6) { + // phpcs:ignore + class XliffLoader extends XliffLoaderInternal implements LoaderInterface + { + public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + return $this->loadInternal($resource, $locale, $domain); + } + } +} else { + // phpcs:ignore + class XliffLoader extends XliffLoaderInternal implements LoaderInterface + { + public function load($resource, $locale, $domain = 'messages') + { + return $this->loadInternal($resource, $locale, $domain); + } + } +} diff --git a/Translation/Loader/SymfonyLoaderAdapter.php b/Translation/Loader/SymfonyLoaderAdapter.php index 198482cb..7832f83b 100644 --- a/Translation/Loader/SymfonyLoaderAdapter.php +++ b/Translation/Loader/SymfonyLoaderAdapter.php @@ -1,5 +1,7 @@ * @@ -18,8 +20,8 @@ namespace JMS\TranslationBundle\Translation\Loader; -use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Model\MessageCatalogue; use Symfony\Component\Translation\Loader\LoaderInterface as SymfonyLoader; /** @@ -36,10 +38,6 @@ class SymfonyLoaderAdapter implements LoaderInterface */ private $loader; - /** - * SymfonyLoaderAdapter constructor. - * @param SymfonyLoader $loader - */ public function __construct(SymfonyLoader $loader) { $this->loader = $loader; @@ -52,6 +50,7 @@ public function __construct(SymfonyLoader $loader) * @param mixed $resource * @param string $locale * @param string $domain + * * @return MessageCatalogue */ public function load($resource, $locale, $domain = 'messages') diff --git a/Translation/Loader/XliffLoader.php b/Translation/Loader/XliffLoader.php index 307aceed..129c328a 100644 --- a/Translation/Loader/XliffLoader.php +++ b/Translation/Loader/XliffLoader.php @@ -1,5 +1,7 @@ * @@ -19,9 +21,9 @@ namespace JMS\TranslationBundle\Translation\Loader; use JMS\TranslationBundle\Exception\RuntimeException; -use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Model\FileSource; use JMS\TranslationBundle\Model\Message\XliffMessage as Message; +use JMS\TranslationBundle\Model\MessageCatalogue; class XliffLoader implements LoaderInterface { @@ -29,19 +31,23 @@ class XliffLoader implements LoaderInterface * @param mixed $resource * @param string $locale * @param string $domain + * * @return MessageCatalogue */ public function load($resource, $locale, $domain = 'messages') { - $previous = libxml_use_internal_errors(true); - if (false === $doc = simplexml_load_file($resource)) { - libxml_use_internal_errors($previous); + $previousErrors = libxml_use_internal_errors(true); + $previousEntities = $this->libxmlDisableEntityLoader(false); + if (false === $doc = simplexml_load_file((string) $resource)) { + libxml_use_internal_errors($previousErrors); + $this->libxmlDisableEntityLoader($previousEntities); $libxmlError = libxml_get_last_error(); throw new RuntimeException(sprintf('Could not load XML-file "%s": %s', $resource, $libxmlError->message)); } - libxml_use_internal_errors($previous); + libxml_use_internal_errors($previousErrors); + $this->libxmlDisableEntityLoader($previousEntities); $doc->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2'); $doc->registerXPathNamespace('jms', 'urn:jms:translation'); @@ -51,25 +57,24 @@ public function load($resource, $locale, $domain = 'messages') $catalogue = new MessageCatalogue(); $catalogue->setLocale($locale); - /** @var \SimpleXMLElement $trans */ foreach ($doc->xpath('//xliff:trans-unit') as $trans) { - $id = ($resName = (string) $trans->attributes()->resname) - ? $resName : (string) $trans->source; + \assert($trans instanceof \SimpleXMLElement); + $resName = (string) $trans->attributes()->resname; + $id = $resName ?: (string) $trans->source; - /** @var Message $m */ $m = Message::create($id, $domain) ->setDesc((string) $trans->source) - ->setLocaleString((string) $trans->target) - ; + ->setLocaleString((string) $trans->target); + \assert($m instanceof Message); - $m->setApproved($trans['approved']=='yes'); + $m->setApproved((string) $trans['approved'] === 'yes'); if (isset($trans->target['state'])) { $m->setState((string) $trans->target['state']); } // Create closure - $addNoteToMessage = function(Message $m, $note) { + $addNoteToMessage = static function (Message $m, $note) { $m->addNote((string) $note, isset($note['from']) ? ((string) $note['from']) : null); }; @@ -93,8 +98,8 @@ public function load($resource, $locale, $domain = 'messages') $column = (string) $file->attributes()->column; $m->addSource(new FileSource( (string) $file, - $line ? (integer) $line : null, - $column ? (integer) $column : null + $line ? (int) $line : null, + $column ? (int) $column : null )); } } @@ -114,4 +119,17 @@ public function load($resource, $locale, $domain = 'messages') return $catalogue; } + + /** + * Use libxml_disable_entity_loader only if it's not deprecated + */ + private function libxmlDisableEntityLoader(bool $disable): bool + { + if (PHP_VERSION_ID >= 80000) { + return true; + } + + // phpcs:ignore + return libxml_disable_entity_loader($disable); + } } diff --git a/Translation/LoaderManager.php b/Translation/LoaderManager.php index 1f357482..a8e072a9 100644 --- a/Translation/LoaderManager.php +++ b/Translation/LoaderManager.php @@ -1,5 +1,7 @@ * @@ -20,6 +22,7 @@ use JMS\TranslationBundle\Exception\InvalidArgumentException; use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\Loader\LoaderInterface; use JMS\TranslationBundle\Util\FileUtils; class LoaderManager @@ -35,10 +38,11 @@ public function __construct(array $loaders) } /** - * @param $file - * @param $format - * @param $locale + * @param mixed $file + * @param string $format + * @param string $locale * @param string $domain + * * @return mixed */ public function loadFile($file, $format, $locale, $domain = 'messages') @@ -47,9 +51,10 @@ public function loadFile($file, $format, $locale, $domain = 'messages') } /** - * @param $dir - * @param $targetLocale - * @return \JMS\TranslationBundle\Model\MessageCatalogue + * @param string $dir + * @param string $targetLocale + * + * @return MessageCatalogue */ public function loadFromDirectory($dir, $targetLocale) { @@ -64,7 +69,7 @@ public function loadFromDirectory($dir, $targetLocale) continue; } - list($format, $file) = $data; + [$format, $file] = $data; $catalogue->merge($this->getLoader($format)->load($file, $locale, $domain)); } @@ -74,10 +79,11 @@ public function loadFromDirectory($dir, $targetLocale) } /** - * @param $format - * @return mixed + * @param string $format + * + * @return LoaderInterface + * * @throws \InvalidArgumentException - * @return \JMS\TranslationBundle\Translation\Loader\LoaderInterface */ protected function getLoader($format) { diff --git a/Translation/TranslationContainerInterface.php b/Translation/TranslationContainerInterface.php index c6ea4bf2..52ad08f6 100644 --- a/Translation/TranslationContainerInterface.php +++ b/Translation/TranslationContainerInterface.php @@ -1,5 +1,7 @@ * @@ -18,6 +20,8 @@ namespace JMS\TranslationBundle\Translation; +use JMS\TranslationBundle\Model\Message; + /** * Translation Container Interface. * diff --git a/Translation/Updater.php b/Translation/Updater.php index 9f79d6e6..a95cbf8e 100644 --- a/Translation/Updater.php +++ b/Translation/Updater.php @@ -1,5 +1,7 @@ * @@ -18,11 +20,11 @@ namespace JMS\TranslationBundle\Translation; -use JMS\TranslationBundle\Translation\Comparison\ChangeSet; -use JMS\TranslationBundle\Util\FileUtils; use JMS\TranslationBundle\Exception\RuntimeException; use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\Comparison\CatalogueComparator; +use JMS\TranslationBundle\Translation\Comparison\ChangeSet; +use JMS\TranslationBundle\Util\FileUtils; use Psr\Log\LoggerInterface; use Symfony\Component\Finder\Finder; @@ -72,12 +74,6 @@ class Updater */ private $writer; - /** - * @param LoaderManager $loader - * @param ExtractorManager $extractor - * @param LoggerInterface $logger - * @param FileWriter $writer - */ public function __construct(LoaderManager $loader, ExtractorManager $extractor, LoggerInterface $logger, FileWriter $writer) { $this->loader = $loader; @@ -86,9 +82,6 @@ public function __construct(LoaderManager $loader, ExtractorManager $extractor, $this->writer = $writer; } - /** - * @param LoggerInterface $logger - */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; @@ -97,6 +90,7 @@ public function setLogger(LoggerInterface $logger) /** * @param Config $config + * * @return ChangeSet */ public function getChangeSet(Config $config) @@ -124,8 +118,7 @@ public function updateTranslation($file, $format, $domain, $locale, $id, $trans) $catalogue ->get($id, $domain) ->setLocaleString($trans) - ->setNew(false) - ; + ->setNew(false); $this->writer->write($catalogue, $domain, $file, $format); } @@ -155,30 +148,60 @@ public function process(Config $config) $format = $this->detectOutputFormat($name); // delete translation files of other formats - foreach (Finder::create()->name('/^'.$name.'\.'.$this->config->getLocale().'\.[^\.]+$/')->in($this->config->getTranslationsDir())->depth('< 1')->files() as $file) { - if ('.'.$format === substr($file, -1 * strlen('.'.$format))) { + $translationFileRegex = sprintf( + '/^%s%s\.%s\.[^\.]+$/', + $name, + $this->config->shouldUseIcuMessageFormat() ? '+intl-icu' : '', + $this->config->getLocale() + ); + foreach (Finder::create()->name($translationFileRegex)->in($this->config->getTranslationsDir())->depth('< 1')->files() as $file) { + if ('.' . $format === substr((string) $file, -1 * strlen('.' . $format))) { continue; } $this->logger->info(sprintf('Deleting translation file "%s".', $file)); - if (false === @unlink($file)) { + if (false === @unlink((string) $file)) { throw new RuntimeException(sprintf('Could not delete the translation file "%s".', $file)); } } - $outputFile = $this->config->getTranslationsDir().'/'.$name.'.'.$this->config->getLocale().'.'.$format; + $outputFile = sprintf( + '%s/%s%s.%s.%s', + $this->config->getTranslationsDir(), + $name, + $this->config->shouldUseIcuMessageFormat() ? '+intl-icu' : '', + $this->config->getLocale(), + $format + ); $this->logger->info(sprintf('Writing translation file "%s".', $outputFile)); $this->writer->write($this->scannedCatalogue, $name, $outputFile, $format); } + + // Remove file if all translations removed + $endOfFile = sprintf('.%s.%s', $this->config->getLocale(), $format); + $translationFilesRegex = sprintf('/%s$/', $endOfFile); + foreach (Finder::create()->name($translationFilesRegex)->in($this->config->getTranslationsDir())->files() as $file) { + $domainName = str_replace($endOfFile, '', $file->getFilename()); + if ($this->scannedCatalogue->hasDomain($domainName)) { + continue; + } + + $this->logger->info(sprintf('Deleting translation file "%s".', $file)); + if (false === unlink((string) $file)) { + throw new RuntimeException(sprintf('Could not delete the translation file "%s".', $file)); + } + } } /** * Detects the most suitable output format to use. * - * @param $currentDomain - * @return string * @internal param string $domain + * + * @param string $currentDomain + * + * @return string */ private function detectOutputFormat($currentDomain) { @@ -190,7 +213,7 @@ private function detectOutputFormat($currentDomain) $otherDomainFormat = $localeFormat = $otherLocaleFormat = null; foreach (FileUtils::findTranslationFiles($this->config->getTranslationsDir()) as $domain => $locales) { foreach ($locales as $locale => $fileData) { - list($format, ) = $fileData; + [$format] = $fileData; if ($currentDomain !== $domain) { $otherDomainFormat = $format; @@ -221,14 +244,11 @@ private function detectOutputFormat($currentDomain) return $this->config->getDefaultOutputFormat(); } - /** - * @param Config $config - */ private function setConfig(Config $config) { $this->config = $config; - $this->logger->info(sprintf("Loading catalogues from \"%s\"", $config->getTranslationsDir())); + $this->logger->info(sprintf('Loading catalogues from "%s"', $config->getTranslationsDir())); $this->existingCatalogue = new MessageCatalogue(); // load external resources, so current translations can be reused in the final translation @@ -240,7 +260,8 @@ private function setConfig(Config $config) } $this->existingCatalogue->merge($this->loader->loadFromDirectory( - $config->getTranslationsDir(), $config->getLocale() + $config->getTranslationsDir(), + $config->getLocale() )); $this->extractor->reset(); @@ -249,7 +270,7 @@ private function setConfig(Config $config) $this->extractor->setExcludedNames($config->getExcludedNames()); $this->extractor->setEnabledExtractors($config->getEnabledExtractors()); - $this->logger->info("Extracting translation keys"); + $this->logger->info('Extracting translation keys'); $this->scannedCatalogue = $this->extractor->extract(); $this->scannedCatalogue->setLocale($config->getLocale()); diff --git a/Twig/DefaultApplyingNodeVisitor.php b/Twig/DefaultApplyingNodeVisitor.php index 162746df..b389d72f 100644 --- a/Twig/DefaultApplyingNodeVisitor.php +++ b/Twig/DefaultApplyingNodeVisitor.php @@ -1,5 +1,7 @@ * @@ -19,6 +21,15 @@ namespace JMS\TranslationBundle\Twig; use JMS\TranslationBundle\Exception\RuntimeException; +use JMS\TranslationBundle\Twig\Node\Transchoice; +use Twig\Environment; +use Twig\Node\Expression\ArrayExpression; +use Twig\Node\Expression\Binary\EqualBinary; +use Twig\Node\Expression\ConditionalExpression; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Expression\FilterExpression; +use Twig\Node\Node; +use Twig\NodeVisitor\AbstractNodeVisitor; /** * Applies the value of the "desc" filter if the "trans" filter has no @@ -28,88 +39,90 @@ * * @author Johannes M. Schmitt */ -class DefaultApplyingNodeVisitor extends \Twig_BaseNodeVisitor +class DefaultApplyingNodeVisitor extends AbstractNodeVisitor { /** * @var bool */ private $enabled = true; - /** - * @param $bool - */ public function setEnabled($bool) { $this->enabled = (bool) $bool; } /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node + * @return Node */ - public function doEnterNode(\Twig_Node $node, \Twig_Environment $env) + public function doEnterNode(Node $node, Environment $env) { if (!$this->enabled) { return $node; } - if ($node instanceof \Twig_Node_Expression_Filter - && 'desc' === $node->getNode('filter')->getAttribute('value')) { + if ( + $node instanceof FilterExpression + && 'desc' === $node->getNode('filter')->getAttribute('value') + ) { $transNode = $node->getNode('node'); - while ($transNode instanceof \Twig_Node_Expression_Filter + while ( + $transNode instanceof FilterExpression && 'trans' !== $transNode->getNode('filter')->getAttribute('value') - && 'transchoice' !== $transNode->getNode('filter')->getAttribute('value')) { + && 'transchoice' !== $transNode->getNode('filter')->getAttribute('value') + ) { $transNode = $transNode->getNode('node'); } - if (!$transNode instanceof \Twig_Node_Expression_Filter) { - throw new RuntimeException(sprintf('The "desc" filter must be applied after a "trans", or "transchoice" filter.')); + if (!$transNode instanceof FilterExpression) { + throw new RuntimeException(sprintf('The "desc" filter in "%s" line %d must be applied after a "trans", or "transchoice" filter.', $node->getTemplateName(), $node->getTemplateLine())); } $wrappingNode = $node->getNode('node'); - $testNode = clone $wrappingNode; - $defaultNode = $node->getNode('arguments')->getNode(0); + + $testNode = clone $wrappingNode; + $arguments = iterator_to_array($node->getNode('arguments')); + $defaultNode = $arguments[0]; // if the |transchoice filter is used, delegate the call to the TranslationExtension // so that we can catch a possible exception when the default translation has not yet // been extracted if ('transchoice' === $transNode->getNode('filter')->getAttribute('value')) { - $transchoiceArguments = new \Twig_Node_Expression_Array(array(), $transNode->getTemplateLine()); + $transchoiceArguments = new ArrayExpression([], $transNode->getTemplateLine()); $transchoiceArguments->addElement($wrappingNode->getNode('node')); $transchoiceArguments->addElement($defaultNode); foreach ($wrappingNode->getNode('arguments') as $arg) { $transchoiceArguments->addElement($arg); } - $transchoiceNode = new Node\Transchoice($transchoiceArguments, $transNode->getTemplateLine()); + $transchoiceNode = new Transchoice($transchoiceArguments, $transNode->getTemplateLine()); $node->setNode('node', $transchoiceNode); return $node; } + $wrappingNodeArguments = iterator_to_array($wrappingNode->getNode('arguments')); + // if the |trans filter has replacements parameters // (e.g. |trans({'%foo%': 'bar'})) - if ($wrappingNode->getNode('arguments')->hasNode(0)) { + if (isset($wrappingNodeArguments[0])) { $lineno = $wrappingNode->getTemplateLine(); // remove the replacements from the test node - $testNode->setNode('arguments', clone $testNode->getNode('arguments')); - $testNode->getNode('arguments')->setNode(0, new \Twig_Node_Expression_Array(array(), $lineno)); + $testNodeArguments = iterator_to_array($testNode->getNode('arguments')); + $testNodeArguments[0] = new ArrayExpression([], $lineno); + $testNode->setNode('arguments', new Node($testNodeArguments)); // wrap the default node in a |replace filter - $defaultNode = new \Twig_Node_Expression_Filter( - clone $node->getNode('arguments')->getNode(0), - new \Twig_Node_Expression_Constant('replace', $lineno), - new \Twig_Node(array( - clone $wrappingNode->getNode('arguments')->getNode(0) - )), + $defaultNode = new FilterExpression( + $arguments[0], + new ConstantExpression('replace', $lineno), + new Node([$wrappingNodeArguments[0]]), $lineno ); } - $condition = new \Twig_Node_Expression_Conditional( - new \Twig_Node_Expression_Binary_Equal($testNode, $transNode->getNode('node'), $wrappingNode->getTemplateLine()), + $condition = new ConditionalExpression( + new EqualBinary($testNode, $transNode->getNode('node'), $wrappingNode->getTemplateLine()), $defaultNode, clone $wrappingNode, $wrappingNode->getTemplateLine() @@ -121,11 +134,9 @@ public function doEnterNode(\Twig_Node $node, \Twig_Environment $env) } /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node + * @return Node */ - public function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) + public function doLeaveNode(Node $node, Environment $env) { return $node; } diff --git a/Twig/Node/Transchoice.php b/Twig/Node/Transchoice.php index ec3f4d0a..b83b5cf0 100644 --- a/Twig/Node/Transchoice.php +++ b/Twig/Node/Transchoice.php @@ -1,5 +1,7 @@ * @@ -18,19 +20,24 @@ namespace JMS\TranslationBundle\Twig\Node; -class Transchoice extends \Twig_Node_Expression +use JMS\TranslationBundle\Twig\TranslationExtension; +use Twig\Compiler; +use Twig\Node\Expression\AbstractExpression; +use Twig\Node\Expression\ArrayExpression; + +class Transchoice extends AbstractExpression { - public function __construct(\Twig_Node_Expression_Array $arguments, $lineno) + public function __construct(ArrayExpression $arguments, $lineno) { - parent::__construct(array('arguments' => $arguments), array(), $lineno); + parent::__construct(['arguments' => $arguments], [], $lineno); } - public function compile(\Twig_Compiler $compiler) + public function compile(Compiler $compiler) { $compiler->raw( sprintf( '$this->env->getExtension(\'%s\')->%s(', - 'JMS\TranslationBundle\Twig\TranslationExtension', + TranslationExtension::class, 'transchoiceWithDefault' ) ); diff --git a/Twig/NormalizingNodeVisitor.php b/Twig/NormalizingNodeVisitor.php index 7c1ee619..f4f6fee0 100644 --- a/Twig/NormalizingNodeVisitor.php +++ b/Twig/NormalizingNodeVisitor.php @@ -1,5 +1,7 @@ * @@ -18,6 +20,12 @@ namespace JMS\TranslationBundle\Twig; +use Twig\Environment; +use Twig\Node\Expression\Binary\ConcatBinary; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Node; +use Twig\NodeVisitor\AbstractNodeVisitor; + /** * Performs equivalence transformations on the AST to ensure that * subsequent visitors do not need to be aware of different syntaxes. @@ -26,29 +34,27 @@ * * @author Johannes M. Schmitt */ -class NormalizingNodeVisitor extends \Twig_BaseNodeVisitor +class NormalizingNodeVisitor extends AbstractNodeVisitor { /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node + * @return Node */ - protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) + protected function doEnterNode(Node $node, Environment $env) { return $node; } /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node_Expression_Constant|\Twig_Node + * @return ConstantExpression|Node */ - protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) + protected function doLeaveNode(Node $node, Environment $env) { - if ($node instanceof \Twig_Node_Expression_Binary_Concat - && ($left = $node->getNode('left')) instanceof \Twig_Node_Expression_Constant - && ($right = $node->getNode('right')) instanceof \Twig_Node_Expression_Constant) { - return new \Twig_Node_Expression_Constant($left->getAttribute('value').$right->getAttribute('value'), $left->getTemplateLine()); + if ( + $node instanceof ConcatBinary + && ($left = $node->getNode('left')) instanceof ConstantExpression + && ($right = $node->getNode('right')) instanceof ConstantExpression + ) { + return new ConstantExpression($left->getAttribute('value') . $right->getAttribute('value'), $left->getTemplateLine()); } return $node; diff --git a/Twig/RemovingNodeVisitor.php b/Twig/RemovingNodeVisitor.php index 095862d0..c5a8d4e2 100644 --- a/Twig/RemovingNodeVisitor.php +++ b/Twig/RemovingNodeVisitor.php @@ -1,5 +1,7 @@ * @@ -18,34 +20,34 @@ namespace JMS\TranslationBundle\Twig; +use Twig\Environment; +use Twig\Node\Expression\FilterExpression; +use Twig\Node\Node; +use Twig\NodeVisitor\AbstractNodeVisitor; + /** * Removes translation metadata filters from the AST. * * @author Johannes M. Schmitt */ -class RemovingNodeVisitor extends \Twig_BaseNodeVisitor +class RemovingNodeVisitor extends AbstractNodeVisitor { /** * @var bool */ private $enabled = true; - /** - * @param $bool - */ public function setEnabled($bool) { $this->enabled = (bool) $bool; } /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node + * @return Node */ - protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) + protected function doEnterNode(Node $node, Environment $env) { - if ($this->enabled && $node instanceof \Twig_Node_Expression_Filter) { + if ($this->enabled && $node instanceof FilterExpression) { $name = $node->getNode('filter')->getAttribute('value'); if ('desc' === $name || 'meaning' === $name) { @@ -57,11 +59,9 @@ protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) } /** - * @param \Twig_Node $node - * @param \Twig_Environment $env - * @return \Twig_Node + * @return Node */ - protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) + protected function doLeaveNode(Node $node, Environment $env) { return $node; } diff --git a/Twig/TranslationExtension.php b/Twig/TranslationExtension.php index cca06b21..20ac34d1 100644 --- a/Twig/TranslationExtension.php +++ b/Twig/TranslationExtension.php @@ -1,5 +1,7 @@ * @@ -23,12 +25,15 @@ * * @author Johannes M. Schmitt */ -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; +use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; -class TranslationExtension extends \Twig_Extension +class TranslationExtension extends AbstractExtension { /** - * @var TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface */ private $translator; @@ -38,12 +43,20 @@ class TranslationExtension extends \Twig_Extension private $debug; /** - * TranslationExtension constructor. - * @param TranslatorInterface $translator + * @param TranslatorInterface|LegacyTranslatorInterface $translator * @param bool $debug */ - public function __construct(TranslatorInterface $translator, $debug = false) + public function __construct($translator, $debug = false) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \InvalidArgumentException(sprintf( + 'Argument 1 must be an instance of %s or %s, instance of %s given', + TranslatorInterface::class, + LegacyTranslatorInterface::class, + get_class($translator) + )); + } + $this->translator = $translator; $this->debug = $debug; } @@ -53,10 +66,10 @@ public function __construct(TranslatorInterface $translator, $debug = false) */ public function getNodeVisitors() { - $visitors = array( + $visitors = [ new NormalizingNodeVisitor(), new RemovingNodeVisitor(), - ); + ]; if ($this->debug) { $visitors[] = new DefaultApplyingNodeVisitor(); @@ -70,10 +83,10 @@ public function getNodeVisitors() */ public function getFilters() { - return array( - new \Twig_SimpleFilter('desc', array($this, 'desc')), - new \Twig_SimpleFilter('meaning', array($this, 'meaning')), - ); + return [ + new TwigFilter('desc', [$this, 'desc']), + new TwigFilter('meaning', [$this, 'meaning']), + ]; } /** @@ -81,30 +94,36 @@ public function getFilters() * @param string $defaultMessage * @param int $count * @param array $arguments - * @param null|string $domain - * @param null|string $locale + * @param string|null $domain + * @param string|null $locale + * * @return string */ - public function transchoiceWithDefault($message, $defaultMessage, $count, array $arguments = array(), $domain = null, $locale = null) + public function transchoiceWithDefault($message, $defaultMessage, $count, array $arguments = [], $domain = null, $locale = null) { if (null === $domain) { $domain = 'messages'; } - // If < sf2.6 - if (!method_exists($this->translator, 'getCatalogue')) { - return $this->transchoiceWithDefaultLegacy($message, $defaultMessage, $count, $arguments, $domain, $locale); + if (false === $this->translator->getCatalogue($locale)->defines($message, $domain)) { + return $this->doTransChoice($defaultMessage, $count, array_merge(['%count%' => $count], $arguments), $domain, $locale); } - if (false == $this->translator->getCatalogue($locale)->defines($message, $domain)) { - return $this->translator->transChoice($defaultMessage, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + return $this->doTransChoice($message, $count, array_merge(['%count%' => $count], $arguments), $domain, $locale); + } + + private function doTransChoice($message, $count, array $arguments, $domain, $locale) + { + if ($this->translator instanceof LegacyTranslatorInterface) { + return $this->translator->transChoice($message, $count, array_merge(['%count%' => $count], $arguments), $domain, $locale); } - return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + return $this->translator->trans($message, array_merge(['%count%' => $count], $arguments), $domain, $locale); } /** - * @param $v + * @param mixed $v + * * @return mixed */ public function desc($v) @@ -113,7 +132,8 @@ public function desc($v) } /** - * @param $v + * @param mixed $v + * * @return mixed */ public function meaning($v) @@ -128,30 +148,4 @@ public function getName() { return 'jms_translation'; } - - /** - * This function exists to support Symfony 2.3 - * - * @param string $message - * @param string $defaultMessage - * @param int $count - * @param array $arguments - * @param string $domain - * @param string $locale - * - * @return string - */ - private function transchoiceWithDefaultLegacy($message, $defaultMessage, $count, array $arguments, $domain, $locale) - { - try { - $translatedMessage = $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); - - if ($translatedMessage !== $message) { - return $translatedMessage; - } - } catch (\InvalidArgumentException $e) { - } - - return $this->translator->transChoice($defaultMessage, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); - } } diff --git a/Util/FileUtils.php b/Util/FileUtils.php index 641f0bd9..8eb385bd 100644 --- a/Util/FileUtils.php +++ b/Util/FileUtils.php @@ -1,5 +1,7 @@ * @@ -35,22 +37,28 @@ abstract class FileUtils * ) * ) * - * @throws \RuntimeException - * * @return array + * + * @throws \RuntimeException */ public static function findTranslationFiles($directory) { - $files = array(); + $files = []; foreach (Finder::create()->in($directory)->depth('< 1')->files() as $file) { - if (!preg_match('/^([^\.]+)\.([^\.]+)\.([^\.]+)$/', basename($file), $match)) { + $isTranslationFile = preg_match( + '/^(?P[^\.]+?)(?P\+intl-icu)?\.(?P[^\.]+)\.(?P[^\.]+)$/', + basename((string) $file), + $match + ); + if (!$isTranslationFile) { continue; } - $files[$match[1]][$match[2]] = array( - $match[3], - $file - ); + $files[$match['domain']][$match['locale']] = [ + $match['format'], + $file, + !empty($match['icu']), + ]; } uksort($files, 'strcasecmp'); diff --git a/Util/Writer.php b/Util/Writer.php index 173a2334..07cff962 100644 --- a/Util/Writer.php +++ b/Util/Writer.php @@ -1,5 +1,7 @@ * @@ -52,7 +54,7 @@ class Writer /** * @var array */ - private $changes = array(); + private $changes = []; /** * @return $this @@ -79,12 +81,13 @@ public function outdent() } /** - * @param $content + * @param string $content + * * @return $this */ public function writeln($content) { - $this->write($content."\n"); + $this->write($content . "\n"); return $this; } @@ -92,12 +95,13 @@ public function writeln($content) public function revert() { $change = array_pop($this->changes); - $this->changeCount -=1 ; + $this->changeCount -= 1; $this->content = substr($this->content, 0, -1 * strlen($change)); } /** - * @param $content + * @param string $content + * * @return $this */ public function write($content) @@ -106,16 +110,18 @@ public function write($content) $addition = ''; $lines = explode("\n", $content); - for ($i=0, $c=count($lines); $i<$c; $i++) { - if ($this->indentationLevel > 0 + for ($i = 0, $c = count($lines); $i < $c; $i++) { + if ( + $this->indentationLevel > 0 && !empty($lines[$i]) - && ((empty($addition) && "\n" === substr($this->content, -1)) || "\n" === substr($addition, -1))) { + && ((empty($addition) && "\n" === substr($this->content, -1)) || "\n" === substr($addition, -1)) + ) { $addition .= str_repeat(' ', $this->indentationLevel * $this->indentationSpaces); } $addition .= $lines[$i]; - if ($i+1 < $c) { + if ($i + 1 < $c) { $addition .= "\n"; } } @@ -129,6 +135,7 @@ public function write($content) /** * @param bool $preserveNewLines + * * @return $this */ public function rtrim($preserveNewLines = true) diff --git a/composer.json b/composer.json index c3777eab..f65d4aee 100644 --- a/composer.json +++ b/composer.json @@ -1,44 +1,81 @@ { - "name": "jms/translation-bundle", - "description": "Puts the Symfony Translation Component on steroids", - "keywords": ["translation", "multilanguage", "extract", "webinterface", "ui", "i18n", "extraction", "interface"], - "homepage": "http://jmsyst.com/bundles/JMSTranslationBundle", - "type": "symfony-bundle", - "license": "Apache-2.0", + "name": "jms/translation-bundle", + "type": "symfony-bundle", + "description": "Puts the Symfony Translation Component on steroids", + "keywords": [ + "translation", + "multilanguage", + "extract", + "webinterface", + "ui", + "i18n", + "extraction", + "interface" + ], + "homepage": "http://jmsyst.com/bundles/JMSTranslationBundle", + "license": "Apache-2.0", "authors": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" } ], - "minimum-stability": "stable", "require": { - "php": "^5.3.3 || ^7.0", - "twig/twig": "^1.27 || ^2.0", - "symfony/framework-bundle": "^2.3 || ^3.0 || ^4.0", - "nikic/php-parser": "^1.4 || ^2.0 || ^3.0 || ^4.0", - "symfony/console": "^2.3 || ^3.0 || ^4.0", - "symfony/validator": "^2.3 || ^3.0 || ^4.0" + "php": "^7.4 || ^8.0", + "nikic/php-parser": "^4.9 || ^5", + "symfony/console": "^4.3 || ^5.4 || ^6.0", + "symfony/expression-language": "^4.3 || ^5.4 || ^6.0", + "symfony/framework-bundle": "^4.3 || ^5.4 || ^6.0", + "symfony/config": "^4.3 || ^5.4 || ^6.2", + "symfony/translation": "^4.3 || ^5.4 || ^6.0", + "symfony/translation-contracts": "^1.1 || ^2.0 || ^3.0", + "symfony/validator": "^4.3 || ^5.4 || ^6.0", + "twig/twig": "^1.42.4 || ^2.12.5 || ^3.0", + "psr/log": "^1.0 || ^2.0" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0", - "psr/log": "^1.0", - "symfony/symfony": "^2.3 || ^3.0 || ^4.0", - "symfony/expression-language": "^2.6 || ^3.0 || ^4.0", - "sensio/framework-extra-bundle": "^2.3 || ^3.0 || ^4.0", - "matthiasnoback/symfony-dependency-injection-test": "^1.2", - "nyholm/nsa": "^1.0.1" - }, - "autoload": { - "psr-0": { "JMS\\TranslationBundle": "" } + "doctrine/annotations": "^1.11", + "doctrine/coding-standard": "^8.2.1", + "matthiasnoback/symfony-dependency-injection-test": "^4.1", + "nyholm/nsa": "^1.0.1", + "symfony/phpunit-bridge": "^4.4 || ^5.4 || ^6.4", + "sensio/framework-extra-bundle": "^6.2.4", + "symfony/asset": "^4.4 || ^5.4 || ^6.4", + "symfony/browser-kit": "^4.4 || ^5.4 || ^6.4", + "symfony/css-selector": "^4.4 || ^5.4 || ^6.4", + "symfony/filesystem": "^4.4 || ^5.4 || ^6.4", + "symfony/form": "^4.4 || ^5.4 || ^6.4", + "symfony/security-csrf": "^4.4 || ^5.4 || ^6.4", + "symfony/templating": "^4.4 || ^5.4 || ^6.4", + "symfony/property-access": "^4.4 || ^5.4 || ^6.4", + "symfony/routing": "^4.4.15 || ^5.4 || ^6.4", + "symfony/twig-bundle": "^4.4 || ^5.4 || ^6.4", + "symfony/flex": "^1.19 || ^2.0" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "symfony/flex": true, + "dealerdirect/phpcodesniffer-composer-installer": true + } }, - "target-dir": "JMS/TranslationBundle", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.x-dev" + }, + "symfony": { + "allow-contrib": true + } + }, + "autoload": { + "psr-4": { + "JMS\\TranslationBundle\\": "" + } + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" } } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 00000000..c1ca707b --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + Annotation/ + Command/ + Controller/ + DependencyInjection/ + Exception/ + Logger/ + Model/ + Tests/ + Translation/ + Twig/ + Util/ + vendor/* + messages\.en\.php + .+\.html\.php$ + Tests/Translation/Dumper/php/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ab61930c..712c2c61 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,10 +8,9 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" bootstrap="Tests/bootstrap.php" > - + ./Tests