├── .gitattributes ├── README.md ├── bin └── translala ├── box.json ├── build └── translala.phar ├── composer.json ├── composer.lock └── src └── Translala ├── App ├── Command │ ├── AbstractCommand.php │ ├── CommonCommand.php │ ├── DeadCommand.php │ ├── HealthCommand.php │ ├── StatsCommand.php │ └── TranslateCommand.php ├── Loader │ ├── LoaderInterface.php │ └── ProjectLoader.php └── Templates │ ├── common.html.twig │ ├── dead.html.twig │ ├── layout.html.twig │ └── stats.html.twig ├── Domain ├── Exception │ └── HealthException.php ├── Model │ ├── CommandContext.php │ ├── CommonTranslation.php │ ├── ConfigFile.php │ ├── ConfigFileInterface.php │ ├── LocaleStats.php │ ├── Project.php │ ├── ProjectInterface.php │ ├── Translation.php │ ├── TranslationFile.php │ ├── TranslationFileInterface.php │ └── TranslationInterface.php ├── Parser │ └── ParserInterface.php └── Processor │ ├── CommonProcessor.php │ └── HealthProcessor.php └── Infra ├── Application └── Application.php ├── Job ├── CommonJob.php ├── DeadJob.php ├── StatsJob.php └── TranslateJob.php └── Parser ├── AbstractFileParser.php ├── JsonFileParser.php ├── PhpFileParser.php └── YamlFileParser.php /.gitattributes: -------------------------------------------------------------------------------- 1 | *.phar linguist-language=Php 2 | build/translala.phar linguist-vendored=false -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Translala 2 | ================== 3 | 4 | Your new toolbox to manage your (Symfony/Laravel/Other) projects translations. 5 | It provide commands to translate your missing translations, detect commons translations, report translations stats and detect dead translations. 6 | 7 | It can also be integrated in your CI process. 8 | 9 | ![](http://www.updemia.com/static/e/b/xl/58a2f6843b6f1.png) 10 | 11 | Availables commands: 12 | - Stats command 13 | - Common command 14 | - Dead command 15 | - Health command (CI Process) 16 | - Translation command 17 | 18 | Availables Files format: 19 | - Yaml 20 | - Php 21 | - Json 22 | 23 | ## Install 24 | Grab the translala.phar and move it to your bin. 25 | 26 | ``` bash 27 | $ git clone git@github.com:Translala/translala.git && cp translala/build/translala.phar /usr/local/bin/translala 28 | ``` 29 | 30 | ## Configure your project 31 | Here is what a complete `.translation.yml` file look like. You can add many path for each of your translation path. 32 | The translala report are dumped in your export path. 33 | 34 | ``` yml 35 | master_language: fr 36 | languages: 37 | - en 38 | - fr 39 | - da 40 | 41 | paths: 42 | - ./app/Resources/translations/ 43 | - ./resources/lang/en/ 44 | - ./src/AcmeBundle/Resources/translations/ 45 | 46 | export_path: /tmp 47 | 48 | api: 49 | google: my_very_secret_api_key 50 | 51 | project_path: ./ 52 | 53 | 54 | health: 55 | fr: 100 56 | en: 95 57 | da: 70 58 | ``` 59 | 60 | `master_language` is the locale of reference for your project. 61 | 62 | `languages` list of locales your project have to manage. 63 | 64 | `paths` are filepaths where your translations file are stored. 65 | 66 | `export_path` is the directory where Translala dump your reports 67 | 68 | ## Stats command 69 | `translala translala:project:stats --config ./app/Resources/translations/.translation.yml` 70 | 71 | The stats command report project stats about translations achievements. 72 | ![](http://www.updemia.com/static/e/a/xl/5887cc5f5c697.png) 73 | 74 | ## Common command 75 | `translala translala:project:common --config ./app/Resources/translations/.translation.yml` 76 | 77 | Do you have a different translation key for each "save" buttons of your form? With many locales, it can have a significant cost. 78 | ![](http://www.updemia.com/static/e/a/xl/5887cc9c35428.png) 79 | 80 | ## Dead command 81 | `translala translala:project:dead --config ./app/Resources/translations/.translation.yml` 82 | 83 | This search each translations keys in your project to help you find dead key. 84 | ![](https://i.imgflip.com/11z8lt.jpg) 85 | 86 | ## Health command 87 | `translala translala:health:status --config ./app/Resources/translations/.translation.yml` 88 | 89 | Check percentage of missing translation for each locale. Return an exit code if check fail. Made for CI process. 90 | 91 | ## Translation command 92 | `translala translala:project:translate --config ./app/Resources/translations/.translation.yml` 93 | 94 | Using php-translation it translate missing or empty keys for each locale in each file. 95 | 96 | ## Contribution 97 | Feel free to remove array manipulation, fix typo and create new file parser with a PR ;) -------------------------------------------------------------------------------- /bin/translala: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); -------------------------------------------------------------------------------- /box.json: -------------------------------------------------------------------------------- 1 | { 2 | "algorithm": "SHA1", 3 | "alias": "translala.phar", 4 | "banner": "", 5 | "chmod": "0755", 6 | "compactors": [ 7 | "Herrera\\Box\\Compactor\\Json", 8 | "Herrera\\Box\\Compactor\\Php" 9 | ], 10 | "directories": "src", 11 | "extract": true, 12 | "files": [ 13 | "vendor/autoload.php", 14 | "vendor/composer/installed.json" 15 | ], 16 | "finder": [ 17 | { 18 | "exclude": [ 19 | "tests", 20 | "Tests" 21 | ], 22 | "in": [ 23 | "vendor", 24 | "vendor/guzzlehttp", 25 | "vendor/php-http", 26 | "vendor/php-translation", 27 | "vendor/psr", 28 | "vendor/symfony" 29 | ], 30 | "name": "*.php" 31 | } 32 | ], 33 | "git-version": "package_version", 34 | "main": "bin/translala", 35 | "output": "build/translala.phar", 36 | "stub": true 37 | } -------------------------------------------------------------------------------- /build/translala.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScullWM/translala/8319ebeb5f368acdc3d09d3a955c946e09ecbe4a/build/translala.phar -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "translala/translala", 3 | "type": "library", 4 | "require": { 5 | "php": "^5.5|^7.0", 6 | "symfony/console": "^2.7|^3.0", 7 | "symfony/process": "^2.7|^3.0", 8 | "symfony/finder": "^2.7|^3.0", 9 | "symfony/filesystem": "^2.7|^3.0", 10 | "symfony/yaml": "^2.7|^3.0", 11 | "symfony/event-dispatcher": "^2.7|^3.0", 12 | "symfony/property-access": "^2.7|^3.0", 13 | "php-http/guzzle6-adapter": "^1.1", 14 | "php-translation/translator": "^0.1.0", 15 | "php-http/discovery": "^1.1", 16 | "php-http/message": "^1.4", 17 | "twig/twig": "^1.31" 18 | }, 19 | "bin": [ 20 | "bin/tanslala" 21 | ], 22 | "config": { 23 | "bin-dir": "bin" 24 | }, 25 | "autoload": { 26 | "psr-4": {"Translala\\": "src/Translala"} 27 | }, 28 | "extra": { 29 | "branch-alias": { 30 | "dev-master": "1.0-dev" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "a726c54effd76e08da69587dce3d9bb8", 8 | "packages": [ 9 | { 10 | "name": "clue/stream-filter", 11 | "version": "v1.3.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/clue/php-stream-filter.git", 15 | "reference": "e3bf9415da163d9ad6701dccb407ed501ae69785" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/clue/php-stream-filter/zipball/e3bf9415da163d9ad6701dccb407ed501ae69785", 20 | "reference": "e3bf9415da163d9ad6701dccb407ed501ae69785", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3" 25 | }, 26 | "type": "library", 27 | "autoload": { 28 | "psr-4": { 29 | "Clue\\StreamFilter\\": "src/" 30 | }, 31 | "files": [ 32 | "src/functions.php" 33 | ] 34 | }, 35 | "notification-url": "https://packagist.org/downloads/", 36 | "license": [ 37 | "MIT" 38 | ], 39 | "authors": [ 40 | { 41 | "name": "Christian Lück", 42 | "email": "christian@lueck.tv" 43 | } 44 | ], 45 | "description": "A simple and modern approach to stream filtering in PHP", 46 | "homepage": "https://github.com/clue/php-stream-filter", 47 | "keywords": [ 48 | "bucket brigade", 49 | "callback", 50 | "filter", 51 | "php_user_filter", 52 | "stream", 53 | "stream_filter_append", 54 | "stream_filter_register" 55 | ], 56 | "time": "2015-11-08T23:41:30+00:00" 57 | }, 58 | { 59 | "name": "guzzlehttp/guzzle", 60 | "version": "6.2.2", 61 | "source": { 62 | "type": "git", 63 | "url": "https://github.com/guzzle/guzzle.git", 64 | "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" 65 | }, 66 | "dist": { 67 | "type": "zip", 68 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", 69 | "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", 70 | "shasum": "" 71 | }, 72 | "require": { 73 | "guzzlehttp/promises": "^1.0", 74 | "guzzlehttp/psr7": "^1.3.1", 75 | "php": ">=5.5" 76 | }, 77 | "require-dev": { 78 | "ext-curl": "*", 79 | "phpunit/phpunit": "^4.0", 80 | "psr/log": "^1.0" 81 | }, 82 | "type": "library", 83 | "extra": { 84 | "branch-alias": { 85 | "dev-master": "6.2-dev" 86 | } 87 | }, 88 | "autoload": { 89 | "files": [ 90 | "src/functions_include.php" 91 | ], 92 | "psr-4": { 93 | "GuzzleHttp\\": "src/" 94 | } 95 | }, 96 | "notification-url": "https://packagist.org/downloads/", 97 | "license": [ 98 | "MIT" 99 | ], 100 | "authors": [ 101 | { 102 | "name": "Michael Dowling", 103 | "email": "mtdowling@gmail.com", 104 | "homepage": "https://github.com/mtdowling" 105 | } 106 | ], 107 | "description": "Guzzle is a PHP HTTP client library", 108 | "homepage": "http://guzzlephp.org/", 109 | "keywords": [ 110 | "client", 111 | "curl", 112 | "framework", 113 | "http", 114 | "http client", 115 | "rest", 116 | "web service" 117 | ], 118 | "time": "2016-10-08T15:01:37+00:00" 119 | }, 120 | { 121 | "name": "guzzlehttp/promises", 122 | "version": "v1.3.1", 123 | "source": { 124 | "type": "git", 125 | "url": "https://github.com/guzzle/promises.git", 126 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" 127 | }, 128 | "dist": { 129 | "type": "zip", 130 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", 131 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", 132 | "shasum": "" 133 | }, 134 | "require": { 135 | "php": ">=5.5.0" 136 | }, 137 | "require-dev": { 138 | "phpunit/phpunit": "^4.0" 139 | }, 140 | "type": "library", 141 | "extra": { 142 | "branch-alias": { 143 | "dev-master": "1.4-dev" 144 | } 145 | }, 146 | "autoload": { 147 | "psr-4": { 148 | "GuzzleHttp\\Promise\\": "src/" 149 | }, 150 | "files": [ 151 | "src/functions_include.php" 152 | ] 153 | }, 154 | "notification-url": "https://packagist.org/downloads/", 155 | "license": [ 156 | "MIT" 157 | ], 158 | "authors": [ 159 | { 160 | "name": "Michael Dowling", 161 | "email": "mtdowling@gmail.com", 162 | "homepage": "https://github.com/mtdowling" 163 | } 164 | ], 165 | "description": "Guzzle promises library", 166 | "keywords": [ 167 | "promise" 168 | ], 169 | "time": "2016-12-20T10:07:11+00:00" 170 | }, 171 | { 172 | "name": "guzzlehttp/psr7", 173 | "version": "1.3.1", 174 | "source": { 175 | "type": "git", 176 | "url": "https://github.com/guzzle/psr7.git", 177 | "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" 178 | }, 179 | "dist": { 180 | "type": "zip", 181 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", 182 | "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", 183 | "shasum": "" 184 | }, 185 | "require": { 186 | "php": ">=5.4.0", 187 | "psr/http-message": "~1.0" 188 | }, 189 | "provide": { 190 | "psr/http-message-implementation": "1.0" 191 | }, 192 | "require-dev": { 193 | "phpunit/phpunit": "~4.0" 194 | }, 195 | "type": "library", 196 | "extra": { 197 | "branch-alias": { 198 | "dev-master": "1.4-dev" 199 | } 200 | }, 201 | "autoload": { 202 | "psr-4": { 203 | "GuzzleHttp\\Psr7\\": "src/" 204 | }, 205 | "files": [ 206 | "src/functions_include.php" 207 | ] 208 | }, 209 | "notification-url": "https://packagist.org/downloads/", 210 | "license": [ 211 | "MIT" 212 | ], 213 | "authors": [ 214 | { 215 | "name": "Michael Dowling", 216 | "email": "mtdowling@gmail.com", 217 | "homepage": "https://github.com/mtdowling" 218 | } 219 | ], 220 | "description": "PSR-7 message implementation", 221 | "keywords": [ 222 | "http", 223 | "message", 224 | "stream", 225 | "uri" 226 | ], 227 | "time": "2016-06-24T23:00:38+00:00" 228 | }, 229 | { 230 | "name": "php-http/discovery", 231 | "version": "v1.1.1", 232 | "source": { 233 | "type": "git", 234 | "url": "https://github.com/php-http/discovery.git", 235 | "reference": "47fc36bd73ab615b55c31f4134e6bae70f1c04c1" 236 | }, 237 | "dist": { 238 | "type": "zip", 239 | "url": "https://api.github.com/repos/php-http/discovery/zipball/47fc36bd73ab615b55c31f4134e6bae70f1c04c1", 240 | "reference": "47fc36bd73ab615b55c31f4134e6bae70f1c04c1", 241 | "shasum": "" 242 | }, 243 | "require": { 244 | "php": "^5.5 || ^7.0" 245 | }, 246 | "require-dev": { 247 | "henrikbjorn/phpspec-code-coverage": "^2.0.2", 248 | "php-http/httplug": "^1.0", 249 | "php-http/message-factory": "^1.0", 250 | "phpspec/phpspec": "^2.4", 251 | "puli/composer-plugin": "1.0.0-beta10" 252 | }, 253 | "suggest": { 254 | "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories", 255 | "puli/composer-plugin": "Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details." 256 | }, 257 | "type": "library", 258 | "extra": { 259 | "branch-alias": { 260 | "dev-master": "1.2-dev" 261 | } 262 | }, 263 | "autoload": { 264 | "psr-4": { 265 | "Http\\Discovery\\": "src/" 266 | } 267 | }, 268 | "notification-url": "https://packagist.org/downloads/", 269 | "license": [ 270 | "MIT" 271 | ], 272 | "authors": [ 273 | { 274 | "name": "Márk Sági-Kazár", 275 | "email": "mark.sagikazar@gmail.com" 276 | } 277 | ], 278 | "description": "Finds installed HTTPlug implementations and PSR-7 message factories", 279 | "homepage": "http://php-http.org", 280 | "keywords": [ 281 | "adapter", 282 | "client", 283 | "discovery", 284 | "factory", 285 | "http", 286 | "message", 287 | "psr7" 288 | ], 289 | "time": "2016-11-27T12:21:25+00:00" 290 | }, 291 | { 292 | "name": "php-http/guzzle6-adapter", 293 | "version": "v1.1.1", 294 | "source": { 295 | "type": "git", 296 | "url": "https://github.com/php-http/guzzle6-adapter.git", 297 | "reference": "a56941f9dc6110409cfcddc91546ee97039277ab" 298 | }, 299 | "dist": { 300 | "type": "zip", 301 | "url": "https://api.github.com/repos/php-http/guzzle6-adapter/zipball/a56941f9dc6110409cfcddc91546ee97039277ab", 302 | "reference": "a56941f9dc6110409cfcddc91546ee97039277ab", 303 | "shasum": "" 304 | }, 305 | "require": { 306 | "guzzlehttp/guzzle": "^6.0", 307 | "php": ">=5.5.0", 308 | "php-http/httplug": "^1.0" 309 | }, 310 | "provide": { 311 | "php-http/async-client-implementation": "1.0", 312 | "php-http/client-implementation": "1.0" 313 | }, 314 | "require-dev": { 315 | "ext-curl": "*", 316 | "php-http/adapter-integration-tests": "^0.4" 317 | }, 318 | "type": "library", 319 | "extra": { 320 | "branch-alias": { 321 | "dev-master": "1.2-dev" 322 | } 323 | }, 324 | "autoload": { 325 | "psr-4": { 326 | "Http\\Adapter\\Guzzle6\\": "src/" 327 | } 328 | }, 329 | "notification-url": "https://packagist.org/downloads/", 330 | "license": [ 331 | "MIT" 332 | ], 333 | "authors": [ 334 | { 335 | "name": "Márk Sági-Kazár", 336 | "email": "mark.sagikazar@gmail.com" 337 | }, 338 | { 339 | "name": "David de Boer", 340 | "email": "david@ddeboer.nl" 341 | } 342 | ], 343 | "description": "Guzzle 6 HTTP Adapter", 344 | "homepage": "http://httplug.io", 345 | "keywords": [ 346 | "Guzzle", 347 | "http" 348 | ], 349 | "time": "2016-05-10T06:13:32+00:00" 350 | }, 351 | { 352 | "name": "php-http/httplug", 353 | "version": "v1.1.0", 354 | "source": { 355 | "type": "git", 356 | "url": "https://github.com/php-http/httplug.git", 357 | "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018" 358 | }, 359 | "dist": { 360 | "type": "zip", 361 | "url": "https://api.github.com/repos/php-http/httplug/zipball/1c6381726c18579c4ca2ef1ec1498fdae8bdf018", 362 | "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018", 363 | "shasum": "" 364 | }, 365 | "require": { 366 | "php": ">=5.4", 367 | "php-http/promise": "^1.0", 368 | "psr/http-message": "^1.0" 369 | }, 370 | "require-dev": { 371 | "henrikbjorn/phpspec-code-coverage": "^1.0", 372 | "phpspec/phpspec": "^2.4" 373 | }, 374 | "type": "library", 375 | "extra": { 376 | "branch-alias": { 377 | "dev-master": "1.1-dev" 378 | } 379 | }, 380 | "autoload": { 381 | "psr-4": { 382 | "Http\\Client\\": "src/" 383 | } 384 | }, 385 | "notification-url": "https://packagist.org/downloads/", 386 | "license": [ 387 | "MIT" 388 | ], 389 | "authors": [ 390 | { 391 | "name": "Eric GELOEN", 392 | "email": "geloen.eric@gmail.com" 393 | }, 394 | { 395 | "name": "Márk Sági-Kazár", 396 | "email": "mark.sagikazar@gmail.com" 397 | } 398 | ], 399 | "description": "HTTPlug, the HTTP client abstraction for PHP", 400 | "homepage": "http://httplug.io", 401 | "keywords": [ 402 | "client", 403 | "http" 404 | ], 405 | "time": "2016-08-31T08:30:17+00:00" 406 | }, 407 | { 408 | "name": "php-http/message", 409 | "version": "v1.4.1", 410 | "source": { 411 | "type": "git", 412 | "url": "https://github.com/php-http/message.git", 413 | "reference": "bfd895a4e753bdde99bed64d75b999e0c9fa6147" 414 | }, 415 | "dist": { 416 | "type": "zip", 417 | "url": "https://api.github.com/repos/php-http/message/zipball/bfd895a4e753bdde99bed64d75b999e0c9fa6147", 418 | "reference": "bfd895a4e753bdde99bed64d75b999e0c9fa6147", 419 | "shasum": "" 420 | }, 421 | "require": { 422 | "clue/stream-filter": "^1.3", 423 | "php": ">=5.4", 424 | "php-http/message-factory": "^1.0.2", 425 | "psr/http-message": "^1.0" 426 | }, 427 | "require-dev": { 428 | "akeneo/phpspec-skip-example-extension": "^1.0", 429 | "coduo/phpspec-data-provider-extension": "^1.0", 430 | "ext-zlib": "*", 431 | "guzzlehttp/psr7": "^1.0", 432 | "henrikbjorn/phpspec-code-coverage": "^1.0", 433 | "phpspec/phpspec": "^2.4", 434 | "slim/slim": "^3.0", 435 | "zendframework/zend-diactoros": "^1.0" 436 | }, 437 | "suggest": { 438 | "ext-zlib": "Used with compressor/decompressor streams", 439 | "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", 440 | "slim/slim": "Used with Slim Framework PSR-7 implementation", 441 | "zendframework/zend-diactoros": "Used with Diactoros Factories" 442 | }, 443 | "type": "library", 444 | "extra": { 445 | "branch-alias": { 446 | "dev-master": "1.5-dev" 447 | } 448 | }, 449 | "autoload": { 450 | "psr-4": { 451 | "Http\\Message\\": "src/" 452 | }, 453 | "files": [ 454 | "src/filters.php" 455 | ] 456 | }, 457 | "notification-url": "https://packagist.org/downloads/", 458 | "license": [ 459 | "MIT" 460 | ], 461 | "authors": [ 462 | { 463 | "name": "Márk Sági-Kazár", 464 | "email": "mark.sagikazar@gmail.com" 465 | } 466 | ], 467 | "description": "HTTP Message related tools", 468 | "homepage": "http://php-http.org", 469 | "keywords": [ 470 | "http", 471 | "message", 472 | "psr-7" 473 | ], 474 | "time": "2016-12-16T21:09:21+00:00" 475 | }, 476 | { 477 | "name": "php-http/message-factory", 478 | "version": "v1.0.2", 479 | "source": { 480 | "type": "git", 481 | "url": "https://github.com/php-http/message-factory.git", 482 | "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" 483 | }, 484 | "dist": { 485 | "type": "zip", 486 | "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", 487 | "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", 488 | "shasum": "" 489 | }, 490 | "require": { 491 | "php": ">=5.4", 492 | "psr/http-message": "^1.0" 493 | }, 494 | "type": "library", 495 | "extra": { 496 | "branch-alias": { 497 | "dev-master": "1.0-dev" 498 | } 499 | }, 500 | "autoload": { 501 | "psr-4": { 502 | "Http\\Message\\": "src/" 503 | } 504 | }, 505 | "notification-url": "https://packagist.org/downloads/", 506 | "license": [ 507 | "MIT" 508 | ], 509 | "authors": [ 510 | { 511 | "name": "Márk Sági-Kazár", 512 | "email": "mark.sagikazar@gmail.com" 513 | } 514 | ], 515 | "description": "Factory interfaces for PSR-7 HTTP Message", 516 | "homepage": "http://php-http.org", 517 | "keywords": [ 518 | "factory", 519 | "http", 520 | "message", 521 | "stream", 522 | "uri" 523 | ], 524 | "time": "2015-12-19T14:08:53+00:00" 525 | }, 526 | { 527 | "name": "php-http/promise", 528 | "version": "v1.0.0", 529 | "source": { 530 | "type": "git", 531 | "url": "https://github.com/php-http/promise.git", 532 | "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980" 533 | }, 534 | "dist": { 535 | "type": "zip", 536 | "url": "https://api.github.com/repos/php-http/promise/zipball/dc494cdc9d7160b9a09bd5573272195242ce7980", 537 | "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980", 538 | "shasum": "" 539 | }, 540 | "require-dev": { 541 | "henrikbjorn/phpspec-code-coverage": "^1.0", 542 | "phpspec/phpspec": "^2.4" 543 | }, 544 | "type": "library", 545 | "extra": { 546 | "branch-alias": { 547 | "dev-master": "1.1-dev" 548 | } 549 | }, 550 | "autoload": { 551 | "psr-4": { 552 | "Http\\Promise\\": "src/" 553 | } 554 | }, 555 | "notification-url": "https://packagist.org/downloads/", 556 | "license": [ 557 | "MIT" 558 | ], 559 | "authors": [ 560 | { 561 | "name": "Márk Sági-Kazár", 562 | "email": "mark.sagikazar@gmail.com" 563 | }, 564 | { 565 | "name": "Joel Wurtz", 566 | "email": "joel.wurtz@gmail.com" 567 | } 568 | ], 569 | "description": "Promise used for asynchronous HTTP requests", 570 | "homepage": "http://httplug.io", 571 | "keywords": [ 572 | "promise" 573 | ], 574 | "time": "2016-01-26T13:27:02+00:00" 575 | }, 576 | { 577 | "name": "php-translation/translator", 578 | "version": "0.1.0", 579 | "source": { 580 | "type": "git", 581 | "url": "https://github.com/php-translation/translator.git", 582 | "reference": "036711160406d94d414e2dfa12a94a0dc36c2322" 583 | }, 584 | "dist": { 585 | "type": "zip", 586 | "url": "https://api.github.com/repos/php-translation/translator/zipball/036711160406d94d414e2dfa12a94a0dc36c2322", 587 | "reference": "036711160406d94d414e2dfa12a94a0dc36c2322", 588 | "shasum": "" 589 | }, 590 | "require": { 591 | "php": "^5.5 || ^7.0", 592 | "php-http/client-implementation": "^1.0", 593 | "php-http/httplug": "^1.0", 594 | "psr/log": "~1.0" 595 | }, 596 | "require-dev": { 597 | "nyholm/nsa": "^1.0", 598 | "php-http/curl-client": "^1.0", 599 | "phpunit/phpunit": "^4.5 || ^5.4" 600 | }, 601 | "type": "library", 602 | "autoload": { 603 | "psr-4": { 604 | "Translation\\Translator\\": "src/" 605 | } 606 | }, 607 | "notification-url": "https://packagist.org/downloads/", 608 | "license": [ 609 | "MIT" 610 | ], 611 | "authors": [ 612 | { 613 | "name": "Tobias Nyholm", 614 | "email": "tobias.nyholm@gmail.com" 615 | } 616 | ], 617 | "description": "Translator services", 618 | "time": "2016-12-05T10:06:43+00:00" 619 | }, 620 | { 621 | "name": "psr/http-message", 622 | "version": "1.0.1", 623 | "source": { 624 | "type": "git", 625 | "url": "https://github.com/php-fig/http-message.git", 626 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 627 | }, 628 | "dist": { 629 | "type": "zip", 630 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 631 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 632 | "shasum": "" 633 | }, 634 | "require": { 635 | "php": ">=5.3.0" 636 | }, 637 | "type": "library", 638 | "extra": { 639 | "branch-alias": { 640 | "dev-master": "1.0.x-dev" 641 | } 642 | }, 643 | "autoload": { 644 | "psr-4": { 645 | "Psr\\Http\\Message\\": "src/" 646 | } 647 | }, 648 | "notification-url": "https://packagist.org/downloads/", 649 | "license": [ 650 | "MIT" 651 | ], 652 | "authors": [ 653 | { 654 | "name": "PHP-FIG", 655 | "homepage": "http://www.php-fig.org/" 656 | } 657 | ], 658 | "description": "Common interface for HTTP messages", 659 | "homepage": "https://github.com/php-fig/http-message", 660 | "keywords": [ 661 | "http", 662 | "http-message", 663 | "psr", 664 | "psr-7", 665 | "request", 666 | "response" 667 | ], 668 | "time": "2016-08-06T14:39:51+00:00" 669 | }, 670 | { 671 | "name": "psr/log", 672 | "version": "1.0.2", 673 | "source": { 674 | "type": "git", 675 | "url": "https://github.com/php-fig/log.git", 676 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" 677 | }, 678 | "dist": { 679 | "type": "zip", 680 | "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 681 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 682 | "shasum": "" 683 | }, 684 | "require": { 685 | "php": ">=5.3.0" 686 | }, 687 | "type": "library", 688 | "extra": { 689 | "branch-alias": { 690 | "dev-master": "1.0.x-dev" 691 | } 692 | }, 693 | "autoload": { 694 | "psr-4": { 695 | "Psr\\Log\\": "Psr/Log/" 696 | } 697 | }, 698 | "notification-url": "https://packagist.org/downloads/", 699 | "license": [ 700 | "MIT" 701 | ], 702 | "authors": [ 703 | { 704 | "name": "PHP-FIG", 705 | "homepage": "http://www.php-fig.org/" 706 | } 707 | ], 708 | "description": "Common interface for logging libraries", 709 | "homepage": "https://github.com/php-fig/log", 710 | "keywords": [ 711 | "log", 712 | "psr", 713 | "psr-3" 714 | ], 715 | "time": "2016-10-10T12:19:37+00:00" 716 | }, 717 | { 718 | "name": "symfony/console", 719 | "version": "v3.0.4", 720 | "source": { 721 | "type": "git", 722 | "url": "https://github.com/symfony/console.git", 723 | "reference": "6b1175135bc2a74c08a28d89761272de8beed8cd" 724 | }, 725 | "dist": { 726 | "type": "zip", 727 | "url": "https://api.github.com/repos/symfony/console/zipball/6b1175135bc2a74c08a28d89761272de8beed8cd", 728 | "reference": "6b1175135bc2a74c08a28d89761272de8beed8cd", 729 | "shasum": "" 730 | }, 731 | "require": { 732 | "php": ">=5.5.9", 733 | "symfony/polyfill-mbstring": "~1.0" 734 | }, 735 | "require-dev": { 736 | "psr/log": "~1.0", 737 | "symfony/event-dispatcher": "~2.8|~3.0", 738 | "symfony/process": "~2.8|~3.0" 739 | }, 740 | "suggest": { 741 | "psr/log": "For using the console logger", 742 | "symfony/event-dispatcher": "", 743 | "symfony/process": "" 744 | }, 745 | "type": "library", 746 | "extra": { 747 | "branch-alias": { 748 | "dev-master": "3.0-dev" 749 | } 750 | }, 751 | "autoload": { 752 | "psr-4": { 753 | "Symfony\\Component\\Console\\": "" 754 | }, 755 | "exclude-from-classmap": [ 756 | "/Tests/" 757 | ] 758 | }, 759 | "notification-url": "https://packagist.org/downloads/", 760 | "license": [ 761 | "MIT" 762 | ], 763 | "authors": [ 764 | { 765 | "name": "Fabien Potencier", 766 | "email": "fabien@symfony.com" 767 | }, 768 | { 769 | "name": "Symfony Community", 770 | "homepage": "https://symfony.com/contributors" 771 | } 772 | ], 773 | "description": "Symfony Console Component", 774 | "homepage": "https://symfony.com", 775 | "time": "2016-03-16T17:00:50+00:00" 776 | }, 777 | { 778 | "name": "symfony/event-dispatcher", 779 | "version": "v3.0.4", 780 | "source": { 781 | "type": "git", 782 | "url": "https://github.com/symfony/event-dispatcher.git", 783 | "reference": "9002dcf018d884d294b1ef20a6f968efc1128f39" 784 | }, 785 | "dist": { 786 | "type": "zip", 787 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9002dcf018d884d294b1ef20a6f968efc1128f39", 788 | "reference": "9002dcf018d884d294b1ef20a6f968efc1128f39", 789 | "shasum": "" 790 | }, 791 | "require": { 792 | "php": ">=5.5.9" 793 | }, 794 | "require-dev": { 795 | "psr/log": "~1.0", 796 | "symfony/config": "~2.8|~3.0", 797 | "symfony/dependency-injection": "~2.8|~3.0", 798 | "symfony/expression-language": "~2.8|~3.0", 799 | "symfony/stopwatch": "~2.8|~3.0" 800 | }, 801 | "suggest": { 802 | "symfony/dependency-injection": "", 803 | "symfony/http-kernel": "" 804 | }, 805 | "type": "library", 806 | "extra": { 807 | "branch-alias": { 808 | "dev-master": "3.0-dev" 809 | } 810 | }, 811 | "autoload": { 812 | "psr-4": { 813 | "Symfony\\Component\\EventDispatcher\\": "" 814 | }, 815 | "exclude-from-classmap": [ 816 | "/Tests/" 817 | ] 818 | }, 819 | "notification-url": "https://packagist.org/downloads/", 820 | "license": [ 821 | "MIT" 822 | ], 823 | "authors": [ 824 | { 825 | "name": "Fabien Potencier", 826 | "email": "fabien@symfony.com" 827 | }, 828 | { 829 | "name": "Symfony Community", 830 | "homepage": "https://symfony.com/contributors" 831 | } 832 | ], 833 | "description": "Symfony EventDispatcher Component", 834 | "homepage": "https://symfony.com", 835 | "time": "2016-03-10T10:34:12+00:00" 836 | }, 837 | { 838 | "name": "symfony/filesystem", 839 | "version": "v3.0.4", 840 | "source": { 841 | "type": "git", 842 | "url": "https://github.com/symfony/filesystem.git", 843 | "reference": "f82499a459dcade2ea56df94cc58b19c8bde3d20" 844 | }, 845 | "dist": { 846 | "type": "zip", 847 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/f82499a459dcade2ea56df94cc58b19c8bde3d20", 848 | "reference": "f82499a459dcade2ea56df94cc58b19c8bde3d20", 849 | "shasum": "" 850 | }, 851 | "require": { 852 | "php": ">=5.5.9" 853 | }, 854 | "type": "library", 855 | "extra": { 856 | "branch-alias": { 857 | "dev-master": "3.0-dev" 858 | } 859 | }, 860 | "autoload": { 861 | "psr-4": { 862 | "Symfony\\Component\\Filesystem\\": "" 863 | }, 864 | "exclude-from-classmap": [ 865 | "/Tests/" 866 | ] 867 | }, 868 | "notification-url": "https://packagist.org/downloads/", 869 | "license": [ 870 | "MIT" 871 | ], 872 | "authors": [ 873 | { 874 | "name": "Fabien Potencier", 875 | "email": "fabien@symfony.com" 876 | }, 877 | { 878 | "name": "Symfony Community", 879 | "homepage": "https://symfony.com/contributors" 880 | } 881 | ], 882 | "description": "Symfony Filesystem Component", 883 | "homepage": "https://symfony.com", 884 | "time": "2016-03-27T10:24:39+00:00" 885 | }, 886 | { 887 | "name": "symfony/finder", 888 | "version": "v3.0.4", 889 | "source": { 890 | "type": "git", 891 | "url": "https://github.com/symfony/finder.git", 892 | "reference": "c54e407b35bc098916704e9fd090da21da4c4f52" 893 | }, 894 | "dist": { 895 | "type": "zip", 896 | "url": "https://api.github.com/repos/symfony/finder/zipball/c54e407b35bc098916704e9fd090da21da4c4f52", 897 | "reference": "c54e407b35bc098916704e9fd090da21da4c4f52", 898 | "shasum": "" 899 | }, 900 | "require": { 901 | "php": ">=5.5.9" 902 | }, 903 | "type": "library", 904 | "extra": { 905 | "branch-alias": { 906 | "dev-master": "3.0-dev" 907 | } 908 | }, 909 | "autoload": { 910 | "psr-4": { 911 | "Symfony\\Component\\Finder\\": "" 912 | }, 913 | "exclude-from-classmap": [ 914 | "/Tests/" 915 | ] 916 | }, 917 | "notification-url": "https://packagist.org/downloads/", 918 | "license": [ 919 | "MIT" 920 | ], 921 | "authors": [ 922 | { 923 | "name": "Fabien Potencier", 924 | "email": "fabien@symfony.com" 925 | }, 926 | { 927 | "name": "Symfony Community", 928 | "homepage": "https://symfony.com/contributors" 929 | } 930 | ], 931 | "description": "Symfony Finder Component", 932 | "homepage": "https://symfony.com", 933 | "time": "2016-03-10T11:13:05+00:00" 934 | }, 935 | { 936 | "name": "symfony/polyfill-mbstring", 937 | "version": "v1.1.1", 938 | "source": { 939 | "type": "git", 940 | "url": "https://github.com/symfony/polyfill-mbstring.git", 941 | "reference": "1289d16209491b584839022f29257ad859b8532d" 942 | }, 943 | "dist": { 944 | "type": "zip", 945 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d", 946 | "reference": "1289d16209491b584839022f29257ad859b8532d", 947 | "shasum": "" 948 | }, 949 | "require": { 950 | "php": ">=5.3.3" 951 | }, 952 | "suggest": { 953 | "ext-mbstring": "For best performance" 954 | }, 955 | "type": "library", 956 | "extra": { 957 | "branch-alias": { 958 | "dev-master": "1.1-dev" 959 | } 960 | }, 961 | "autoload": { 962 | "psr-4": { 963 | "Symfony\\Polyfill\\Mbstring\\": "" 964 | }, 965 | "files": [ 966 | "bootstrap.php" 967 | ] 968 | }, 969 | "notification-url": "https://packagist.org/downloads/", 970 | "license": [ 971 | "MIT" 972 | ], 973 | "authors": [ 974 | { 975 | "name": "Nicolas Grekas", 976 | "email": "p@tchwork.com" 977 | }, 978 | { 979 | "name": "Symfony Community", 980 | "homepage": "https://symfony.com/contributors" 981 | } 982 | ], 983 | "description": "Symfony polyfill for the Mbstring extension", 984 | "homepage": "https://symfony.com", 985 | "keywords": [ 986 | "compatibility", 987 | "mbstring", 988 | "polyfill", 989 | "portable", 990 | "shim" 991 | ], 992 | "time": "2016-01-20T09:13:37+00:00" 993 | }, 994 | { 995 | "name": "symfony/process", 996 | "version": "v3.0.4", 997 | "source": { 998 | "type": "git", 999 | "url": "https://github.com/symfony/process.git", 1000 | "reference": "e6f1f98bbd355d209a992bfff45e7edfbd4a0776" 1001 | }, 1002 | "dist": { 1003 | "type": "zip", 1004 | "url": "https://api.github.com/repos/symfony/process/zipball/e6f1f98bbd355d209a992bfff45e7edfbd4a0776", 1005 | "reference": "e6f1f98bbd355d209a992bfff45e7edfbd4a0776", 1006 | "shasum": "" 1007 | }, 1008 | "require": { 1009 | "php": ">=5.5.9" 1010 | }, 1011 | "type": "library", 1012 | "extra": { 1013 | "branch-alias": { 1014 | "dev-master": "3.0-dev" 1015 | } 1016 | }, 1017 | "autoload": { 1018 | "psr-4": { 1019 | "Symfony\\Component\\Process\\": "" 1020 | }, 1021 | "exclude-from-classmap": [ 1022 | "/Tests/" 1023 | ] 1024 | }, 1025 | "notification-url": "https://packagist.org/downloads/", 1026 | "license": [ 1027 | "MIT" 1028 | ], 1029 | "authors": [ 1030 | { 1031 | "name": "Fabien Potencier", 1032 | "email": "fabien@symfony.com" 1033 | }, 1034 | { 1035 | "name": "Symfony Community", 1036 | "homepage": "https://symfony.com/contributors" 1037 | } 1038 | ], 1039 | "description": "Symfony Process Component", 1040 | "homepage": "https://symfony.com", 1041 | "time": "2016-03-30T10:41:14+00:00" 1042 | }, 1043 | { 1044 | "name": "symfony/property-access", 1045 | "version": "v3.0.4", 1046 | "source": { 1047 | "type": "git", 1048 | "url": "https://github.com/symfony/property-access.git", 1049 | "reference": "475e661a0b90f0e6942a7d2fdf4e0dbf8104fef3" 1050 | }, 1051 | "dist": { 1052 | "type": "zip", 1053 | "url": "https://api.github.com/repos/symfony/property-access/zipball/475e661a0b90f0e6942a7d2fdf4e0dbf8104fef3", 1054 | "reference": "475e661a0b90f0e6942a7d2fdf4e0dbf8104fef3", 1055 | "shasum": "" 1056 | }, 1057 | "require": { 1058 | "php": ">=5.5.9" 1059 | }, 1060 | "type": "library", 1061 | "extra": { 1062 | "branch-alias": { 1063 | "dev-master": "3.0-dev" 1064 | } 1065 | }, 1066 | "autoload": { 1067 | "psr-4": { 1068 | "Symfony\\Component\\PropertyAccess\\": "" 1069 | }, 1070 | "exclude-from-classmap": [ 1071 | "/Tests/" 1072 | ] 1073 | }, 1074 | "notification-url": "https://packagist.org/downloads/", 1075 | "license": [ 1076 | "MIT" 1077 | ], 1078 | "authors": [ 1079 | { 1080 | "name": "Fabien Potencier", 1081 | "email": "fabien@symfony.com" 1082 | }, 1083 | { 1084 | "name": "Symfony Community", 1085 | "homepage": "https://symfony.com/contributors" 1086 | } 1087 | ], 1088 | "description": "Symfony PropertyAccess Component", 1089 | "homepage": "https://symfony.com", 1090 | "keywords": [ 1091 | "access", 1092 | "array", 1093 | "extraction", 1094 | "index", 1095 | "injection", 1096 | "object", 1097 | "property", 1098 | "property path", 1099 | "reflection" 1100 | ], 1101 | "time": "2016-03-23T13:23:25+00:00" 1102 | }, 1103 | { 1104 | "name": "symfony/yaml", 1105 | "version": "v3.0.4", 1106 | "source": { 1107 | "type": "git", 1108 | "url": "https://github.com/symfony/yaml.git", 1109 | "reference": "0047c8366744a16de7516622c5b7355336afae96" 1110 | }, 1111 | "dist": { 1112 | "type": "zip", 1113 | "url": "https://api.github.com/repos/symfony/yaml/zipball/0047c8366744a16de7516622c5b7355336afae96", 1114 | "reference": "0047c8366744a16de7516622c5b7355336afae96", 1115 | "shasum": "" 1116 | }, 1117 | "require": { 1118 | "php": ">=5.5.9" 1119 | }, 1120 | "type": "library", 1121 | "extra": { 1122 | "branch-alias": { 1123 | "dev-master": "3.0-dev" 1124 | } 1125 | }, 1126 | "autoload": { 1127 | "psr-4": { 1128 | "Symfony\\Component\\Yaml\\": "" 1129 | }, 1130 | "exclude-from-classmap": [ 1131 | "/Tests/" 1132 | ] 1133 | }, 1134 | "notification-url": "https://packagist.org/downloads/", 1135 | "license": [ 1136 | "MIT" 1137 | ], 1138 | "authors": [ 1139 | { 1140 | "name": "Fabien Potencier", 1141 | "email": "fabien@symfony.com" 1142 | }, 1143 | { 1144 | "name": "Symfony Community", 1145 | "homepage": "https://symfony.com/contributors" 1146 | } 1147 | ], 1148 | "description": "Symfony Yaml Component", 1149 | "homepage": "https://symfony.com", 1150 | "time": "2016-03-04T07:55:57+00:00" 1151 | }, 1152 | { 1153 | "name": "twig/twig", 1154 | "version": "v1.31.0", 1155 | "source": { 1156 | "type": "git", 1157 | "url": "https://github.com/twigphp/Twig.git", 1158 | "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7" 1159 | }, 1160 | "dist": { 1161 | "type": "zip", 1162 | "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddc9e3e20ee9c0b6908f401ac8353635b750eca7", 1163 | "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7", 1164 | "shasum": "" 1165 | }, 1166 | "require": { 1167 | "php": ">=5.2.7" 1168 | }, 1169 | "require-dev": { 1170 | "symfony/debug": "~2.7", 1171 | "symfony/phpunit-bridge": "~3.2" 1172 | }, 1173 | "type": "library", 1174 | "extra": { 1175 | "branch-alias": { 1176 | "dev-master": "1.31-dev" 1177 | } 1178 | }, 1179 | "autoload": { 1180 | "psr-0": { 1181 | "Twig_": "lib/" 1182 | } 1183 | }, 1184 | "notification-url": "https://packagist.org/downloads/", 1185 | "license": [ 1186 | "BSD-3-Clause" 1187 | ], 1188 | "authors": [ 1189 | { 1190 | "name": "Fabien Potencier", 1191 | "email": "fabien@symfony.com", 1192 | "homepage": "http://fabien.potencier.org", 1193 | "role": "Lead Developer" 1194 | }, 1195 | { 1196 | "name": "Armin Ronacher", 1197 | "email": "armin.ronacher@active-4.com", 1198 | "role": "Project Founder" 1199 | }, 1200 | { 1201 | "name": "Twig Team", 1202 | "homepage": "http://twig.sensiolabs.org/contributors", 1203 | "role": "Contributors" 1204 | } 1205 | ], 1206 | "description": "Twig, the flexible, fast, and secure template language for PHP", 1207 | "homepage": "http://twig.sensiolabs.org", 1208 | "keywords": [ 1209 | "templating" 1210 | ], 1211 | "time": "2017-01-11T19:36:15+00:00" 1212 | } 1213 | ], 1214 | "packages-dev": [], 1215 | "aliases": [], 1216 | "minimum-stability": "stable", 1217 | "stability-flags": [], 1218 | "prefer-stable": false, 1219 | "prefer-lowest": false, 1220 | "platform": { 1221 | "php": "^5.5|^7.0" 1222 | }, 1223 | "platform-dev": [] 1224 | } 1225 | -------------------------------------------------------------------------------- /src/Translala/App/Command/AbstractCommand.php: -------------------------------------------------------------------------------- 1 | addOption( 14 | '--config', 15 | '-c', 16 | InputOption::VALUE_OPTIONAL, 17 | "Config file directory", 18 | getcwd() 19 | ) 20 | ->addOption( 21 | '--domain', 22 | '-d', 23 | InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 24 | "Desired domains", 25 | [] 26 | ) 27 | ->addOption( 28 | '--language', 29 | '-l', 30 | InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 31 | "Desired languages", 32 | [] 33 | ) 34 | ; 35 | } 36 | } -------------------------------------------------------------------------------- /src/Translala/App/Command/CommonCommand.php: -------------------------------------------------------------------------------- 1 | setName('translala:project:common') 20 | ->setDescription('Display common translations') 21 | ; 22 | } 23 | 24 | protected function execute(InputInterface $input, OutputInterface $output) 25 | { 26 | $projectLoader = new ProjectLoader($input->getOption('config')); 27 | $projectLoader->load(new CommandContext($input->getOption('domain'), $input->getOption('language'))); 28 | 29 | $output->writeln('√ Launching Common Command'); 30 | 31 | $job = new CommonJob($projectLoader->getTranslationFiles(), $projectLoader->getConfig()); 32 | $stats = $job->process(); 33 | $output->writeln('√ Done'); 34 | 35 | $projectLoader->render('common.html.twig', ['listing' => $stats->getTranslationsListing()]); 36 | $output->writeln('Report generated in common.html'); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Translala/App/Command/DeadCommand.php: -------------------------------------------------------------------------------- 1 | setName('translala:project:dead') 20 | ->setDescription('I see dead translations') 21 | ; 22 | } 23 | 24 | protected function execute(InputInterface $input, OutputInterface $output) 25 | { 26 | $projectLoader = new ProjectLoader($input->getOption('config')); 27 | $projectLoader->load(new CommandContext($input->getOption('domain'), $input->getOption('language'))); 28 | 29 | $output->writeln('√ Launching Dead Command'); 30 | 31 | $job = new DeadJob($projectLoader->getTranslationFiles(), $projectLoader->getConfig()); 32 | $deads = $job->process(); 33 | $output->writeln('√ Done'); 34 | 35 | $projectLoader->render('dead.html.twig', ['deads' => $deads]); 36 | $output->writeln('Report generated in dead.html'); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Translala/App/Command/HealthCommand.php: -------------------------------------------------------------------------------- 1 | setName('translala:status:health') 22 | ->setDescription('Check if translations are ok') 23 | ; 24 | } 25 | 26 | protected function execute(InputInterface $input, OutputInterface $output) 27 | { 28 | $projectLoader = new ProjectLoader($input->getOption('config')); 29 | $projectLoader->load(new CommandContext($input->getOption('domain'), $input->getOption('language'))); 30 | 31 | $configFileData = $projectLoader->getConfig()->getData(); 32 | 33 | if (!isset($configFileData['health'])) { 34 | throw new \Exception("Undefined data for health cmd"); 35 | } 36 | 37 | $job = new StatsJob($projectLoader->getTranslationFiles(), $projectLoader->getConfig()); 38 | $statsPerLocale = $job->process(); 39 | 40 | $healthProcessor = new HealthProcessor($configFileData['health']); 41 | 42 | try { 43 | $healthProcessor->process($statsPerLocale); 44 | } catch (HealthException $e) { 45 | $output->writeln('Error: ' . $e->getMessage()); 46 | exit(255); 47 | } 48 | 49 | exit(0); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Translala/App/Command/StatsCommand.php: -------------------------------------------------------------------------------- 1 | setName('translala:project:stats') 20 | ->setDescription('Display stats translations') 21 | ; 22 | } 23 | 24 | protected function execute(InputInterface $input, OutputInterface $output) 25 | { 26 | $projectLoader = new ProjectLoader($input->getOption('config')); 27 | $projectLoader->load(new CommandContext($input->getOption('domain'), $input->getOption('language'))); 28 | 29 | $output->writeln('√ Launching Stats Command'); 30 | 31 | $job = new StatsJob($projectLoader->getTranslationFiles(), $projectLoader->getConfig()); 32 | $statsLocale = $job->process(); 33 | $output->writeln('√ Done'); 34 | 35 | $projectLoader->render('stats.html.twig', ['locales' => $statsLocale]); 36 | $output->writeln('Report generated in stats.html'); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Translala/App/Command/TranslateCommand.php: -------------------------------------------------------------------------------- 1 | setName('translala:project:translate') 20 | ->setDescription('Guess missing translations') 21 | ; 22 | } 23 | 24 | protected function execute(InputInterface $input, OutputInterface $output) 25 | { 26 | $projectLoader = new ProjectLoader($input->getOption('config')); 27 | $projectLoader->load(new CommandContext($input->getOption('domain'), $input->getOption('language'))); 28 | 29 | $output->writeln('√ Launching Translate Command'); 30 | 31 | $job = new TranslateJob($projectLoader->getTranslationFiles(), $projectLoader->getConfig()); 32 | $job->process(); 33 | 34 | $output->writeln('√ Done'); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Translala/App/Loader/LoaderInterface.php: -------------------------------------------------------------------------------- 1 | configFile = new ConfigFile($filepath); 30 | } 31 | 32 | /** 33 | * @param CommandContext $context 34 | */ 35 | public function load(CommandContext $context) 36 | { 37 | $translationPaths = $this->configFile->getTranslationPaths(); 38 | 39 | if ($directoryManagement = $this->configFile->getDirectoryManagement()) { 40 | $regex = $this->configFile->getMasterLocale() . DIRECTORY_SEPARATOR . '*.{yml,php,json}'; 41 | } else { 42 | $regex = '*.' . $this->configFile->getMasterLocale() . '.{yml,php,json}'; 43 | } 44 | 45 | foreach ($translationPaths as $path) { 46 | foreach (glob($path . $regex, GLOB_BRACE) as $translationFile) { 47 | $translationFileModel = new TranslationFile($translationFile, $this->getFileParser($translationFile), $this->configFile->getMasterLocale()); 48 | $translationFileModel->parse(); 49 | $this->translationsFiles[] = $translationFileModel; 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * @param string $translationFilePath 56 | * @return FileParserInterface 57 | */ 58 | private function getFileParser($translationFilePath) 59 | { 60 | $fileInfo = pathinfo($translationFilePath); 61 | switch ($fileInfo['extension']) { 62 | case 'yml': 63 | $fileParser = new YamlFileParser($translationFilePath); 64 | break; 65 | 66 | case 'php': 67 | $fileParser = new PhpFileParser($translationFilePath); 68 | break; 69 | 70 | case 'json': 71 | $fileParser = new JsonFileParser($translationFilePath); 72 | break; 73 | } 74 | 75 | return $fileParser; 76 | } 77 | 78 | /** 79 | * @return array 80 | */ 81 | public function getTranslationFiles() 82 | { 83 | return $this->translationsFiles; 84 | } 85 | 86 | /** 87 | * @return ConfigFileInterface 88 | */ 89 | public function getConfig() 90 | { 91 | return $this->configFile; 92 | } 93 | 94 | /** 95 | * @param string $viewPath 96 | * @param array $datas 97 | */ 98 | public function render($viewPath, array $datas) 99 | { 100 | \Twig_Autoloader::register(); 101 | $loader = new \Twig_Loader_Filesystem(__DIR__.'/../Templates'); 102 | $twig = new \Twig_Environment($loader, array('cache' => false)); 103 | 104 | $initDatas = [ 105 | 'config' => $this->configFile, 106 | ]; 107 | 108 | $exportPath = str_replace('.twig', '', $this->configFile->getExportPath() . DIRECTORY_SEPARATOR . $viewPath); 109 | file_put_contents($exportPath, $twig->render($viewPath, array_merge($initDatas, $datas))); 110 | } 111 | } -------------------------------------------------------------------------------- /src/Translala/App/Templates/common.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html.twig' %} 2 | 3 | {% block content %} 4 |
5 |

Common translations

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% for item in listing %} 17 | 18 | 19 | 20 | 25 | 26 | {% endfor %} 27 | 28 |
RepeatTranslationKeys
{{ item.counter }}{{ item.translation }} 21 | {% for ref in item.references %} 22 | {{ ref }}
23 | {% endfor %} 24 |
29 |
30 | {% endblock %} -------------------------------------------------------------------------------- /src/Translala/App/Templates/dead.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html.twig' %} 2 | 3 | {% block content %} 4 |
5 |

Dead translations

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for key in deads %} 15 | 16 | 17 | 18 | {% endfor %} 19 | 20 |
Keys
{{ key }}
21 |
22 | {% endblock %} -------------------------------------------------------------------------------- /src/Translala/App/Templates/layout.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Translala 10 | 11 | 12 | 13 | 14 | 23 | 24 | 28 | 29 | 30 | 52 | 53 |
54 | {% block content %}{% endblock %} 55 |
56 | 58 | 59 | 60 | 61 | 65 | {% block javascript %}{% endblock %} 66 | 67 | -------------------------------------------------------------------------------- /src/Translala/App/Templates/stats.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html.twig' %} 2 | 3 | {% block content %} 4 |
5 |

Translations Stats

6 | 7 | {% for locale in locales %} 8 |
9 |

{{ locale.locale|upper }} Translations

10 |
11 | 12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% for filepath, nbre in locale.geStatsPerFilepath() %} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {% endfor %} 31 |
FileTotalMissing
{{ filepath }}{{ nbre.total }} 0 %} class="danger"{% endif %}>{{ nbre.missing }}
32 |
33 |
34 | {% endfor %} 35 |
36 | 37 | 46 | {% endblock %} 47 | 48 | 49 | {% block javascript %} 50 | 64 | {% endblock %} -------------------------------------------------------------------------------- /src/Translala/Domain/Exception/HealthException.php: -------------------------------------------------------------------------------- 1 | domain = $domain; 24 | $this->locale = $locale; 25 | } 26 | 27 | /** 28 | * Gets the value of domain. 29 | * 30 | * @return string 31 | */ 32 | public function getDomain() 33 | { 34 | return $this->domain; 35 | } 36 | 37 | /** 38 | * Gets the value of locale. 39 | * 40 | * @return string 41 | */ 42 | public function getLocale() 43 | { 44 | return $this->locale; 45 | } 46 | } -------------------------------------------------------------------------------- /src/Translala/Domain/Model/CommonTranslation.php: -------------------------------------------------------------------------------- 1 | hash = $hash; 33 | $this->counter = 0; 34 | $this->keysReferences = []; 35 | $this->translation = $translation; 36 | } 37 | 38 | /** 39 | * @param string $reference 40 | * @return this 41 | */ 42 | public function increment($reference) 43 | { 44 | $this->counter++; 45 | $this->keysReferences[] = $reference; 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * @return int 52 | */ 53 | public function getCounter() 54 | { 55 | return $this->counter; 56 | } 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getReferences() 62 | { 63 | return $this->keysReferences; 64 | } 65 | 66 | /** 67 | * @return string 68 | */ 69 | public function getTranslation() 70 | { 71 | return $this->translation; 72 | } 73 | } -------------------------------------------------------------------------------- /src/Translala/Domain/Model/ConfigFile.php: -------------------------------------------------------------------------------- 1 | filepath = $filepath; 26 | 27 | $yamlParser = new Parser(); 28 | $this->data = $yamlParser->parse(file_get_contents($filepath)); 29 | } 30 | 31 | /** 32 | * @return string 33 | */ 34 | public function getFilepath() 35 | { 36 | return $this->filepath; 37 | } 38 | 39 | /** 40 | * @return array 41 | */ 42 | public function getData() 43 | { 44 | return $this->data; 45 | } 46 | 47 | /** 48 | * @return array 49 | */ 50 | public function getTranslationPaths() 51 | { 52 | return $this->data[ConfigFileInterface::PATHS_KEY]; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getMasterLocale() 59 | { 60 | return $this->data[ConfigFileInterface::MASTER_LOCALE_KEY]; 61 | } 62 | 63 | /** 64 | * @return array 65 | */ 66 | public function getLanguages() 67 | { 68 | return $this->data[ConfigFileInterface::LANGUAGES_KEY]; 69 | } 70 | 71 | /** 72 | * @return string 73 | */ 74 | public function getProjectPath() 75 | { 76 | return $this->data[ConfigFileInterface::PROJECT_PATH]; 77 | } 78 | 79 | /** 80 | * @return bool 81 | */ 82 | public function getDirectoryManagement() 83 | { 84 | if (isset($this->data[ConfigFileInterface::PROJECT_DIRECTORY_MANAGEMENT])) { 85 | return $this->data[ConfigFileInterface::PROJECT_DIRECTORY_MANAGEMENT]; 86 | } 87 | 88 | return false; 89 | } 90 | 91 | /** 92 | * @return array 93 | */ 94 | public function getOthersLanguages() 95 | { 96 | $allLanguages = $this->getLanguages(); 97 | if(($key = array_search($this->getMasterLocale(), $allLanguages)) !== false) { 98 | unset($allLanguages[$key]); 99 | } 100 | 101 | return $allLanguages; 102 | } 103 | 104 | /** 105 | * @return string 106 | */ 107 | public function getExportPath() 108 | { 109 | return $this->data[ConfigFileInterface::EXPORT_PATH_KEY]; 110 | } 111 | 112 | /** 113 | * @return array 114 | */ 115 | public function getApiKey() 116 | { 117 | return $this->data[ConfigFileInterface::API_KEY]; 118 | } 119 | } -------------------------------------------------------------------------------- /src/Translala/Domain/Model/ConfigFileInterface.php: -------------------------------------------------------------------------------- 1 | locale = $locale; 33 | } 34 | 35 | /** 36 | * @return string 37 | */ 38 | public function getLocale() 39 | { 40 | return $this->locale; 41 | } 42 | 43 | /** 44 | * @return string 45 | */ 46 | public function getFilespath() 47 | { 48 | return $this->filespath; 49 | } 50 | 51 | /** 52 | * @param string $filespath the filespath 53 | * 54 | * @return this 55 | */ 56 | public function addFilespath($filespath) 57 | { 58 | $this->filespath[] = $filespath; 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * @return this 65 | */ 66 | public function setTotalTranslationsPerFilepath($filepath, $total) 67 | { 68 | $this->totalTranslationsPerFilepath[$filepath] = $total; 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * @return this 75 | */ 76 | public function setMissingTranslationsPerFilepath($filepath, $total) 77 | { 78 | $this->missingTranslationsPerFilepath[$filepath] = $total; 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * @return integer 85 | */ 86 | public function getTotalTranslations() 87 | { 88 | return array_sum($this->totalTranslationsPerFilepath); 89 | } 90 | 91 | /** 92 | * @return integer 93 | */ 94 | public function getTotalMissingTranslations() 95 | { 96 | return array_sum($this->missingTranslationsPerFilepath); 97 | } 98 | 99 | /** 100 | * @return array 101 | */ 102 | public function geStatsPerFilepath() 103 | { 104 | $statsPerFile = []; 105 | foreach ($this->totalTranslationsPerFilepath as $filepath => $value) { 106 | $missing = (isset($this->missingTranslationsPerFilepath[$filepath])) ? $this->missingTranslationsPerFilepath[$filepath] : $value; 107 | $statsPerFile[$filepath] = ['total' => $value, 'missing' => $missing]; 108 | } 109 | 110 | return $statsPerFile; 111 | } 112 | } -------------------------------------------------------------------------------- /src/Translala/Domain/Model/Project.php: -------------------------------------------------------------------------------- 1 | locales; 13 | } 14 | 15 | public function getConfigFile() 16 | { 17 | return $this->configFile; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Translala/Domain/Model/ProjectInterface.php: -------------------------------------------------------------------------------- 1 | key = $key; 36 | $this->value = $value; 37 | $this->language = $language; 38 | $this->domain = $domain; 39 | } 40 | 41 | /** 42 | * Gets the value of key. 43 | * 44 | * @return string 45 | */ 46 | public function getKey() 47 | { 48 | return $this->key; 49 | } 50 | 51 | /** 52 | * Gets the value of value. 53 | * 54 | * @return string 55 | */ 56 | public function getValue() 57 | { 58 | return $this->value; 59 | } 60 | 61 | /** 62 | * Gets the value of language. 63 | * 64 | * @return string 65 | */ 66 | public function getLanguage() 67 | { 68 | return $this->language; 69 | } 70 | 71 | /** 72 | * Gets the value of domain. 73 | * 74 | * @return string 75 | */ 76 | public function getDomain() 77 | { 78 | return $this->domain; 79 | } 80 | 81 | /** 82 | * @return array 83 | */ 84 | public function getKeypath() 85 | { 86 | $array = (!empty($this->getValue())) ? $this->getValue() : '~'; 87 | foreach (array_reverse(explode('.', $this->key)) as $arr) { 88 | $array = [$arr => $array]; 89 | } 90 | 91 | return $array; 92 | } 93 | 94 | /** 95 | * @return boolean 96 | */ 97 | public function isEmpty() 98 | { 99 | return empty($this->getValue()); 100 | } 101 | 102 | /** 103 | * @param string $value 104 | */ 105 | public function setValue($value) 106 | { 107 | $this->value = $value; 108 | 109 | return $this; 110 | } 111 | } -------------------------------------------------------------------------------- /src/Translala/Domain/Model/TranslationFile.php: -------------------------------------------------------------------------------- 1 | path = $path; 39 | $this->fileParser = $fileParser; 40 | $this->currentLocale = $currentLocale; 41 | } 42 | 43 | /** 44 | * @param string $locale 45 | * @return string 46 | */ 47 | private function getPathForLocale($locale) 48 | { 49 | $distantLocale = sprintf($this->fileParser->getStrategyName(), $locale); 50 | $currentLocale = sprintf($this->fileParser->getStrategyName(), $this->currentLocale); 51 | 52 | return str_replace($currentLocale, $distantLocale, $this->path); 53 | } 54 | 55 | /** 56 | * @param string $locale 57 | * @return boolean 58 | */ 59 | public function isMissingForLocale($locale) 60 | { 61 | return (bool) file_exists($this->getPathForLocale($locale)); 62 | } 63 | 64 | /** 65 | * @return null 66 | */ 67 | public function parse() 68 | { 69 | $this->translations = $this->fileParser->parse($this->getPath()); 70 | } 71 | 72 | /** 73 | * @return string 74 | */ 75 | public function getPath() 76 | { 77 | return $this->path; 78 | } 79 | 80 | /** 81 | * @return array 82 | */ 83 | public function getTranslations() 84 | { 85 | return $this->translations; 86 | } 87 | 88 | /** 89 | * @param string $locale 90 | * @return TranslationFileInterface 91 | */ 92 | public function getTranslationFileForLocale($locale) 93 | { 94 | $parserFqcn = get_class($this->fileParser); 95 | $newParser = new $parserFqcn($this->getPathForLocale($locale)); 96 | 97 | return new TranslationFile($this->getPathForLocale($locale), $newParser, $locale); 98 | } 99 | 100 | /** 101 | * @param string $key 102 | * @return boolean 103 | */ 104 | public function isTranslationEmpty($key) 105 | { 106 | if (!isset($this->translations[$key])) { 107 | return true; 108 | } 109 | 110 | return (bool) (empty($this->translations[$key]->getValue())); 111 | } 112 | 113 | /** 114 | * Dump translation in new file 115 | * 116 | * @return null 117 | */ 118 | public function dump() 119 | { 120 | $datas = []; 121 | foreach ($this->translations as $translation) { 122 | $datas = array_merge_recursive($translation->getKeypath(), $datas); 123 | } 124 | 125 | $this->fileParser->dump($datas, $this->getPath()); 126 | } 127 | 128 | /** 129 | * @param TranslationInterface $translation 130 | * @return this 131 | */ 132 | public function updateTranslation(TranslationInterface $translation) 133 | { 134 | $this->translations[$translation->getKey()] = $translation; 135 | 136 | return $this; 137 | } 138 | } -------------------------------------------------------------------------------- /src/Translala/Domain/Model/TranslationFileInterface.php: -------------------------------------------------------------------------------- 1 | translationsCounter, function($b, $a) { return strcmp($a->getCounter(), $b->getCounter()); }); 21 | 22 | return $this->translationsCounter; 23 | } 24 | 25 | /** 26 | * @param TranslationInterface $translation 27 | */ 28 | public function process(TranslationInterface $translation) 29 | { 30 | $translationKey = md5($this->cannonicalize($translation->getValue())); 31 | 32 | if (!isset($this->translationsCounter[$translationKey])) { 33 | $this->translationsCounter[$translationKey] = new CommonTranslation($translationKey, $translation->getValue()); 34 | } 35 | 36 | $this->translationsCounter[$translationKey]->increment($translation->getKey()); 37 | } 38 | 39 | /** 40 | * @param string $key 41 | * @return string 42 | */ 43 | private function cannonicalize($key) 44 | { 45 | return strtolower(trim($key)); 46 | } 47 | } -------------------------------------------------------------------------------- /src/Translala/Domain/Processor/HealthProcessor.php: -------------------------------------------------------------------------------- 1 | healthGoalsPerLocale = $healthGoalsPerLocale; 17 | } 18 | 19 | /** 20 | * @param StatsJob $statsJob 21 | * @return array 22 | */ 23 | public function process(array $statsPerLocale = []) 24 | { 25 | $missingTranslationPercentagePerLocale = []; 26 | 27 | foreach ($statsPerLocale as $locale) { 28 | $percentage = (($locale->getTotalTranslations()-$locale->getTotalMissingTranslations())/$locale->getTotalTranslations())*100; 29 | 30 | if (isset($this->healthGoalsPerLocale[$locale->getLocale()]) && $percentage < $this->healthGoalsPerLocale[$locale->getLocale()]) { 31 | throw new HealthException( 32 | sprintf("Health fail for locale %s (Get %s while require at minial %s)", $locale->getLocale(), $percentage, $this->healthGoalsPerLocale[$locale->getLocale()]) 33 | ); 34 | 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Translala/Infra/Application/Application.php: -------------------------------------------------------------------------------- 1 | translationsFiles = $translationsFiles; 33 | $this->configFile = $configFile; 34 | } 35 | 36 | /** 37 | * @return CommonProcessor 38 | */ 39 | public function process() 40 | { 41 | $commonProcessor = new CommonProcessor(); 42 | foreach ($this->translationsFiles as $translationFile) { 43 | array_map([$commonProcessor, 'process'], $translationFile->getTranslations()); 44 | } 45 | 46 | return $commonProcessor; 47 | } 48 | } -------------------------------------------------------------------------------- /src/Translala/Infra/Job/DeadJob.php: -------------------------------------------------------------------------------- 1 | translationsFiles = $translationsFiles; 35 | $this->configFile = $configFile; 36 | } 37 | 38 | public function process() 39 | { 40 | foreach ($this->translationsFiles as $translationFile) { 41 | array_map([$this, 'checkDeadTranslations'], $translationFile->getTranslations()); 42 | } 43 | 44 | return $this->missingTranslations; 45 | } 46 | 47 | /** 48 | * @param TranslationInterface $translation 49 | */ 50 | protected function checkDeadTranslations(TranslationInterface $translation) 51 | { 52 | $process = new Process('grep -r "' . $translation->getKey() . '" ' . $this->configFile->getProjectPath()); 53 | $process->run(); 54 | 55 | if (!strstr($process->getOutput(), $translation->getKey())) { 56 | $this->missingTranslations[] = $translation->getKey(); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Translala/Infra/Job/StatsJob.php: -------------------------------------------------------------------------------- 1 | translationsFiles = $translationsFiles; 28 | $this->configFile = $configFile; 29 | } 30 | 31 | /** 32 | * @return LocaleStats[] 33 | */ 34 | public function process() 35 | { 36 | $masterLocale = $this->configFile->getMasterLocale(); 37 | $statsPerLocale = []; 38 | foreach ($this->configFile->getLanguages() as $locale) { 39 | $statsPerLocale[$locale] = new LocaleStats($locale); 40 | } 41 | 42 | foreach ($this->translationsFiles as $translationFile) { 43 | foreach ($this->configFile->getLanguages() as $locale) { 44 | $this->statsPerFile($translationFile, $statsPerLocale[$locale]); 45 | } 46 | } 47 | 48 | return $statsPerLocale; 49 | } 50 | 51 | /** 52 | * @param TranslationFileInterface $translationFile 53 | * @param string $locale 54 | */ 55 | private function statsPerFile(TranslationFileInterface $translationFile, LocaleStats $localeStats) 56 | { 57 | $toTranslateFile = $translationFile->getTranslationFileForLocale($localeStats->getLocale()); 58 | $toTranslateFile->parse(); 59 | 60 | $localeStats->addFilespath($toTranslateFile->getPath()); 61 | 62 | $totalTranslation = $missingTranslation = 0; 63 | foreach ($translationFile->getTranslations() as $translation) { 64 | if ($toTranslateFile->isTranslationEmpty($translation->getKey())) { 65 | $missingTranslation++; 66 | } 67 | $totalTranslation++; 68 | } 69 | 70 | $localeStats->setTotalTranslationsPerFilepath($toTranslateFile->getPath(), $totalTranslation); 71 | $localeStats->setMissingTranslationsPerFilepath($toTranslateFile->getPath(), $missingTranslation); 72 | } 73 | } -------------------------------------------------------------------------------- /src/Translala/Infra/Job/TranslateJob.php: -------------------------------------------------------------------------------- 1 | translationsFiles = $translationsFiles; 33 | $this->configFile = $configFile; 34 | } 35 | 36 | public function process() 37 | { 38 | $apiKeys = $this->configFile->getApiKey(); 39 | $this->checkKey($apiKeys); 40 | 41 | $this->translationClient = new Translator(); 42 | $this->translationClient->addTranslatorService(new GoogleTranslator($apiKeys['google'])); 43 | $masterLocale = $this->configFile->getMasterLocale(); 44 | 45 | foreach ($this->translationsFiles as $translationFile) { 46 | foreach ($this->configFile->getOthersLanguages() as $locale) { 47 | $this->translateFile($translationFile, $locale); 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * @param TranslationFileInterface $translationFile 54 | * @param string $locale 55 | */ 56 | private function translateFile(TranslationFileInterface $translationFile, $locale) 57 | { 58 | $countUpdatedTranslation = 0; 59 | 60 | $toTranslateFile = $translationFile->getTranslationFileForLocale($locale); 61 | foreach ($translationFile->getTranslations() as $translation) { 62 | if ($toTranslateFile->isTranslationEmpty($translation->getKey())) { 63 | 64 | $translatedValue = $this->translationClient->translate($translation->getValue(), $translation->getLanguage(), $locale); 65 | $updatedTranslation = clone($translation); 66 | $updatedTranslation->setValue($translatedValue); 67 | 68 | $toTranslateFile->updateTranslation($updatedTranslation); 69 | $countUpdatedTranslation++; 70 | } 71 | } 72 | 73 | if ($countUpdatedTranslation > 0) { 74 | $toTranslateFile->dump(); 75 | } 76 | } 77 | 78 | private function checkKey($apiKeys) 79 | { 80 | if (empty($apiKeys['google'])) { 81 | throw new \Exception(sprintf("GoogleTranslator api key is missing: %s", $apiKeys['google'])); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/Translala/Infra/Parser/AbstractFileParser.php: -------------------------------------------------------------------------------- 1 | filepath = $filepath; 45 | $exploded = explode('.', $filepath); 46 | 47 | $this->domain = $exploded[0]; 48 | $this->language = $exploded[1]; 49 | $this->extension = $exploded[2]; 50 | } 51 | 52 | /** 53 | * @param array $datas 54 | */ 55 | protected function loadTranslationData($datas, $prefix = null, $iteration = 0) 56 | { 57 | foreach ($datas as $key => $value) { 58 | if (is_array($value)) { 59 | $tmpPrefix = trim($prefix . '.' . $key, '.'); 60 | $iteration++; 61 | $this->loadTranslationData($value, $tmpPrefix, $iteration); 62 | } else { 63 | $tmpPrefix = $prefix . '.' . $key; 64 | 65 | $this->translations[$tmpPrefix] = new Translation($tmpPrefix, $value, $this->language, $this->domain); 66 | } 67 | if ($iteration == 0) { 68 | $prefix = ''; 69 | $iteration = 0; 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * @return array 76 | */ 77 | public function getTranslations() 78 | { 79 | return $this->translations; 80 | } 81 | } -------------------------------------------------------------------------------- /src/Translala/Infra/Parser/JsonFileParser.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function parse($filepath = null) 21 | { 22 | $filepath = ($filepath) ? $filepath : $this->filepath; 23 | 24 | if (file_exists($filepath)) { 25 | $datas = json_decode($filepath); 26 | $this->loadTranslationData($datas); 27 | } else { 28 | $this->translations = []; 29 | } 30 | 31 | return $this->translations; 32 | } 33 | 34 | /** 35 | * @param array $datas 36 | * @param string $path 37 | */ 38 | public function dump(array $datas, $path) 39 | { 40 | file_put_contents($path, json_encode($datas)); 41 | } 42 | } -------------------------------------------------------------------------------- /src/Translala/Infra/Parser/PhpFileParser.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function parse($filepath = null) 21 | { 22 | $filepath = ($filepath) ? $filepath : $this->filepath; 23 | 24 | if (file_exists($filepath)) { 25 | $datas = require($filepath); 26 | $this->loadTranslationData($datas); 27 | } else { 28 | $this->translations = []; 29 | } 30 | 31 | return $this->translations; 32 | } 33 | 34 | /** 35 | * @param array $datas 36 | * @param string $path 37 | */ 38 | public function dump(array $datas, $path) 39 | { 40 | $phpContent = " 20 | */ 21 | public function parse($filepath = null) 22 | { 23 | $filepath = ($filepath) ? $filepath : $this->filepath; 24 | 25 | $yamlParser = new Yaml(); 26 | 27 | if (file_exists($filepath)) { 28 | $datas = $yamlParser->parse(file_get_contents($filepath)); 29 | $this->loadTranslationData($datas); 30 | } else { 31 | $this->translations = []; 32 | } 33 | 34 | return $this->translations; 35 | } 36 | 37 | /** 38 | * @param array $datas 39 | * @param string $path 40 | */ 41 | public function dump(array $datas, $path) 42 | { 43 | $yamlParser = new Yaml(); 44 | $yamlContent = $yamlParser->dump($datas, 100); 45 | 46 | file_put_contents($path, $yamlContent); 47 | } 48 | } --------------------------------------------------------------------------------