From cb99130c1e8929f356441d1cc6328d6f9a8d1d73 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 15:08:46 +0200 Subject: [PATCH 1/9] Created translator and used inside one of the commands --- .env.dist | 1 + composer.json | 3 +- config/autoload/translator.global.php | 8 ++ module/CLI/config/translator.config.php | 14 ++++ module/CLI/lang/es.mo | Bin 0 -> 1238 bytes module/CLI/lang/es.po | 40 ++++++++++ .../src/Command/GenerateShortcodeCommand.php | 46 +++++++---- module/Common/config/services.config.php | 6 ++ module/Common/config/templates.config.php | 12 +++ .../Common/src/Factory/TranslatorFactory.php | 30 +++++++ .../Twig/Extension/TranslatorExtension.php | 75 ++++++++++++++++++ 11 files changed, 219 insertions(+), 16 deletions(-) create mode 100644 config/autoload/translator.global.php create mode 100644 module/CLI/config/translator.config.php create mode 100644 module/CLI/lang/es.mo create mode 100644 module/CLI/lang/es.po create mode 100644 module/Common/config/templates.config.php create mode 100644 module/Common/src/Factory/TranslatorFactory.php create mode 100644 module/Common/src/Twig/Extension/TranslatorExtension.php diff --git a/.env.dist b/.env.dist index d6271d57..726635ba 100644 --- a/.env.dist +++ b/.env.dist @@ -3,6 +3,7 @@ APP_ENV= SHORTENED_URL_SCHEMA= SHORTENED_URL_HOSTNAME= SHORTCODE_CHARS= +DEFAULT_LOCALE= # Database DB_USER= diff --git a/composer.json b/composer.json index 0211ed05..b9be6c6d 100644 --- a/composer.json +++ b/composer.json @@ -21,10 +21,11 @@ "zendframework/zend-servicemanager": "^3.0", "zendframework/zend-paginator": "^2.6", "zendframework/zend-config": "^2.6", + "zendframework/zend-i18n": "^2.7", "mtymek/expressive-config-manager": "^0.4", + "acelaya/zsm-annotated-services": "^0.2.0", "doctrine/orm": "^2.5", "guzzlehttp/guzzle": "^6.2", - "acelaya/zsm-annotated-services": "^0.2.0", "symfony/console": "^3.0" }, "require-dev": { diff --git a/config/autoload/translator.global.php b/config/autoload/translator.global.php new file mode 100644 index 00000000..e3730927 --- /dev/null +++ b/config/autoload/translator.global.php @@ -0,0 +1,8 @@ + [ + 'locale' => getenv('DEFAULT_LOCALE') ?: 'en', + ], + +]; diff --git a/module/CLI/config/translator.config.php b/module/CLI/config/translator.config.php new file mode 100644 index 00000000..ae120db3 --- /dev/null +++ b/module/CLI/config/translator.config.php @@ -0,0 +1,14 @@ + [ + 'translation_file_patterns' => [ + [ + 'type' => 'gettext', + 'base_dir' => __DIR__ . '/../lang', + 'pattern' => '%s.mo', + ], + ], + ], + +]; diff --git a/module/CLI/lang/es.mo b/module/CLI/lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..08f58bd96006efffab0495d0cc27f145a5b7d370 GIT binary patch literal 1238 zcmaJ=O=}cE5FIssu%ZV=ym+V$2#U=x;4VE~Z_$phq_m@+(S4b6TF3WXtM0MM zh@NLM$;w=+X`lvDE?iB+XKbw0DyEGrm+B2QqeiX$xDzfJ77mH@Sd>T6E2G>ifZk=Fd`3EwAr>Xsa_e{`KdXoUgvKps?SsN z=G<6dLl-9DrOH`Mm9>%lLmupCliHTF4rdu&49|_;sx6Pmu`o&2hNG|%lQqe)sb)-x zR$H=L3-)#&p*xu};Tx`}O@e&Hw-a literal 0 HcmV?d00001 diff --git a/module/CLI/lang/es.po b/module/CLI/lang/es.po new file mode 100644 index 00000000..896f2965 --- /dev/null +++ b/module/CLI/lang/es.po @@ -0,0 +1,40 @@ +msgid "" +msgstr "" +"Project-Id-Version: Shlink 1.0\n" +"POT-Creation-Date: 2016-07-21 15:05+0200\n" +"PO-Revision-Date: 2016-07-21 15:08+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es_ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.7.1\n" +"X-Poedit-Basepath: ..\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-KeywordsList: translate;translatePlural\n" +"X-Poedit-SearchPath-0: src\n" +"X-Poedit-SearchPath-1: config\n" + +msgid "Generates a shortcode for provided URL and returns the short URL" +msgstr "Genera un código corto para la URL proporcionada y devuelve la URL acortada" + +msgid "The long URL to parse" +msgstr "La URL larga a procesar" + +msgid "A long URL was not provided. Which URL do you want to shorten?:" +msgstr "No se ha proporcionado una URL larga. ¿Qué URL deseas acortar?" + +msgid "A URL was not provided!" +msgstr "¡No se ha proporcionado una URL!" + +msgid "Processed URL:" +msgstr "URL procesada:" + +msgid "Generated URL:" +msgstr "URL generada:" + +#, php-format +msgid "Provided URL \"%s\" is invalid. Try with a different one." +msgstr "La URL proporcionada \"%s\" e inválida. Prueba con una diferente." diff --git a/module/CLI/src/Command/GenerateShortcodeCommand.php b/module/CLI/src/Command/GenerateShortcodeCommand.php index 91cbd228..0a0af9eb 100644 --- a/module/CLI/src/Command/GenerateShortcodeCommand.php +++ b/module/CLI/src/Command/GenerateShortcodeCommand.php @@ -12,6 +12,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\Question; use Zend\Diactoros\Uri; +use Zend\I18n\Translator\TranslatorInterface; class GenerateShortcodeCommand extends Command { @@ -23,26 +24,37 @@ class GenerateShortcodeCommand extends Command * @var array */ private $domainConfig; + /** + * @var TranslatorInterface + */ + private $translator; /** * GenerateShortcodeCommand constructor. * @param UrlShortenerInterface|UrlShortener $urlShortener + * @param TranslatorInterface $translator * @param array $domainConfig * - * @Inject({UrlShortener::class, "config.url_shortener.domain"}) + * @Inject({UrlShortener::class, "translator", "config.url_shortener.domain"}) */ - public function __construct(UrlShortenerInterface $urlShortener, array $domainConfig) - { - parent::__construct(null); + public function __construct( + UrlShortenerInterface $urlShortener, + TranslatorInterface $translator, + array $domainConfig + ) { $this->urlShortener = $urlShortener; + $this->translator = $translator; $this->domainConfig = $domainConfig; + parent::__construct(null); } public function configure() { $this->setName('shortcode:generate') - ->setDescription('Generates a shortcode for provided URL and returns the short URL') - ->addArgument('longUrl', InputArgument::REQUIRED, 'The long URL to parse'); + ->setDescription( + $this->translator->translate('Generates a shortcode for provided URL and returns the short URL') + ) + ->addArgument('longUrl', InputArgument::REQUIRED, $this->translator->translate('The long URL to parse')); } public function interact(InputInterface $input, OutputInterface $output) @@ -54,9 +66,10 @@ class GenerateShortcodeCommand extends Command /** @var QuestionHelper $helper */ $helper = $this->getHelper('question'); - $question = new Question( - 'A long URL was not provided. Which URL do you want to shorten?: ' - ); + $question = new Question(sprintf( + '%s ', + $this->translator->translate('A long URL was not provided. Which URL do you want to shorten?:') + )); $longUrl = $helper->ask($input, $output, $question); if (! empty($longUrl)) { @@ -70,7 +83,7 @@ class GenerateShortcodeCommand extends Command try { if (! isset($longUrl)) { - $output->writeln('A URL was not provided!'); + $output->writeln(sprintf('%s', $this->translator->translate('A URL was not provided!'))); return; } @@ -80,13 +93,16 @@ class GenerateShortcodeCommand extends Command ->withHost($this->domainConfig['hostname']); $output->writeln([ - sprintf('Processed URL %s', $longUrl), - sprintf('Generated URL %s', $shortUrl), + sprintf('%s %s', $this->translator->translate('Processed URL:'), $longUrl), + sprintf('%s %s', $this->translator->translate('Generated URL:'), $shortUrl), ]); } catch (InvalidUrlException $e) { - $output->writeln( - sprintf('Provided URL "%s" is invalid. Try with a different one.', $longUrl) - ); + $output->writeln(sprintf( + '' . $this->translator->translate( + 'Provided URL "%s" is invalid. Try with a different one.' + ) . '', + $longUrl + )); } } } diff --git a/module/Common/config/services.config.php b/module/Common/config/services.config.php index 838b7896..1423b543 100644 --- a/module/Common/config/services.config.php +++ b/module/Common/config/services.config.php @@ -4,7 +4,10 @@ use Doctrine\Common\Cache\Cache; use Doctrine\ORM\EntityManager; use Shlinkio\Shlink\Common\Factory\CacheFactory; use Shlinkio\Shlink\Common\Factory\EntityManagerFactory; +use Shlinkio\Shlink\Common\Factory\TranslatorFactory; use Shlinkio\Shlink\Common\Service\IpLocationResolver; +use Shlinkio\Shlink\Common\Twig\Extension\TranslatorExtension; +use Zend\I18n\Translator\Translator; use Zend\ServiceManager\Factory\InvokableFactory; return [ @@ -15,10 +18,13 @@ return [ GuzzleHttp\Client::class => InvokableFactory::class, Cache::class => CacheFactory::class, IpLocationResolver::class => AnnotatedFactory::class, + Translator::class => TranslatorFactory::class, + TranslatorExtension::class => AnnotatedFactory::class, ], 'aliases' => [ 'em' => EntityManager::class, 'httpClient' => GuzzleHttp\Client::class, + 'translator' => Translator::class, AnnotatedFactory::CACHE_SERVICE => Cache::class, ], ], diff --git a/module/Common/config/templates.config.php b/module/Common/config/templates.config.php new file mode 100644 index 00000000..903c1e8c --- /dev/null +++ b/module/Common/config/templates.config.php @@ -0,0 +1,12 @@ + [ + 'extensions' => [ + TranslatorExtension::class, + ], + ], + +]; diff --git a/module/Common/src/Factory/TranslatorFactory.php b/module/Common/src/Factory/TranslatorFactory.php new file mode 100644 index 00000000..e41ba2fc --- /dev/null +++ b/module/Common/src/Factory/TranslatorFactory.php @@ -0,0 +1,30 @@ +get('config'); + return Translator::factory(isset($config['translator']) ? $config['translator'] : []); + } +} diff --git a/module/Common/src/Twig/Extension/TranslatorExtension.php b/module/Common/src/Twig/Extension/TranslatorExtension.php new file mode 100644 index 00000000..48ee3f11 --- /dev/null +++ b/module/Common/src/Twig/Extension/TranslatorExtension.php @@ -0,0 +1,75 @@ +translator = $translator; + } + + /** + * Returns the name of the extension. + * + * @return string The extension name + */ + public function getName() + { + return __CLASS__; + } + + public function getFunctions() + { + return [ + new \Twig_SimpleFunction('translate', [$this, 'translate']), + new \Twig_SimpleFunction('translate_plural', [$this, 'translatePlural']), + ]; + } + + /** + * Translate a message. + * + * @param string $message + * @param string $textDomain + * @param string $locale + * @return string + */ + public function translate($message, $textDomain = 'default', $locale = null) + { + return $this->translator->translate($message, $textDomain, $locale); + } + + /** + * Translate a plural message. + * + * @param string $singular + * @param string $plural + * @param int $number + * @param string $textDomain + * @param string|null $locale + * @return string + */ + public function translatePlural( + $singular, + $plural, + $number, + $textDomain = 'default', + $locale = null + ) { + $this->translator->translatePlural($singular, $plural, $number, $textDomain, $locale); + } +} From 545fe7da70462404bfc6c2ff10c95c818dd693ff Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 15:35:52 +0200 Subject: [PATCH 2/9] Added translator and translations to GetVisitsCommand --- module/CLI/lang/es.mo | Bin 1238 -> 2199 bytes module/CLI/lang/es.po | 31 ++++++++++++++- module/CLI/src/Command/GetVisitsCommand.php | 42 +++++++++++++------- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/module/CLI/lang/es.mo b/module/CLI/lang/es.mo index 08f58bd96006efffab0495d0cc27f145a5b7d370..2dcd57bcde8e004d9f701961ff46710a00bf1717 100644 GIT binary patch literal 2199 zcmbtU%WEV>9In;%HNI9s1o808g5qO)oEdQwI)rS@Ll-+8Li(d--p2Bz@ z<7Ww;mK?8#n^)0)GNx zOFRc7j{^zV1vY`f-Y39oz%PI}Dt-or*nb7y0{#gM@m_rx19%e{_CE!}jglkqBCOdj z*n1M=5sb&`)k{E7m3PBZuoZrw5szYo*uoECm3b3uw71ixv2>)3r=c|?m2$d(_1x&e zxtmRTFH=cY?xlt%roese$s2N+v7Ys6jQiy`B_`#2=ua5tj@(65I1VIvZpJPUAE?~3 zrIB*VyDMbb7gj4cXmmaypBcxbd5j}IlbTqkluFP4k-_BY34`;}`if&C5WU5kEk4o^ zc08Lpl2xE(oCn6vQdp)(!jH;VM3Hfw6EKLLH3>U+!iQ;@`101}7u;o1j+7qBT%}Fw z+X;=8&tNQ72M25!={B01Vu!=b0uPO6T1!(4R!l>FDw`>Lsd5;qk_T3}c__Uyn$9?U zS`E>Mue16i{X=OTm#e3TXk2zd%{mTMu9{%LUhFv}QV!r}i1Y(ad~`jH-ev1xHm2Py zSNcP0H(QO~c0XFTT>2A*RL9h5wXa64#i-M!_8W0$>D5-J6%Ix_9IP3A?zz`H)w!;8 zKI&VkT`s+`F*UkU4+=OF^;sUy&0}JBdvmvOqeuEK{FY`AI~5A*M_-N zdYO_eq{6T4^|zv>({sTbDiLjJGy?iErlo!58}CPT-vmE;hEwGc^ip%N*~VVA5WOiK z52epyYBn3ays$ElwooD$lOC2+x6)ar>T~5KO$#g3etG$nVAm8j;q}V1m@3z1^zZOw zY;5Yf%6Y_6Cx(}2UzN!@I+ixcdhjS}#pG;q?p8a-+tvqa&=7ZyZsO@PWioXC(AWeu zl6cXDmZ6~+#J}%TF6}@z>CO+gis?7y6G+)HG)X4V6KPj#f}yR+r^n!?WO|&cfq@^^ zo7wdPRl$(-si_u~th%#OeBqx<8YI9G>;aN%qQD$sD^O7bZ9C$y% zJFfPtRHhX*4nuuP?(TWlO9kQ}K+_{A5Ngx21@FsH^0KOsp;Cn)LEBcrQ?a2eCyAOK zYbYMXNQ#X)4{Fh=_JblJuPM}wPx4>I!ui>vp6w@5`&zaOw6KoCip{&PrGcoBd8k(K a#*x{_gN7!}m0YX^ed6Fm;RU^RwxZ}BpVR3L-{}@3`{`GJh_!IX7Wa+>67O(i%ovb?7(DTI$4)R qjmf}bb12Jg#>ooot0jCA6+(l26mk-a(i0UD6$*;-lT(Wmix>dt?Ib<` diff --git a/module/CLI/lang/es.po b/module/CLI/lang/es.po index 896f2965..595211c2 100644 --- a/module/CLI/lang/es.po +++ b/module/CLI/lang/es.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: Shlink 1.0\n" -"POT-Creation-Date: 2016-07-21 15:05+0200\n" -"PO-Revision-Date: 2016-07-21 15:08+0200\n" +"POT-Creation-Date: 2016-07-21 15:28+0200\n" +"PO-Revision-Date: 2016-07-21 15:32+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: es_ES\n" @@ -38,3 +38,30 @@ msgstr "URL generada:" #, php-format msgid "Provided URL \"%s\" is invalid. Try with a different one." msgstr "La URL proporcionada \"%s\" e inválida. Prueba con una diferente." + +msgid "Returns the detailed visits information for provided short code" +msgstr "Devuelve la información detallada de visitas para el código corto proporcionado" + +msgid "The short code which visits we want to get" +msgstr "El código corto del cual queremos obtener las visitas" + +msgid "Allows to filter visits, returning only those older than start date" +msgstr "Permite filtrar las visitas, devolviendo sólo aquellas más antiguas que startDate" + +msgid "Allows to filter visits, returning only those newer than end date" +msgstr "Permite filtrar las visitas, devolviendo sólo aquellas más nuevas que endDate" + +msgid "A short code was not provided. Which short code do you want to use?:" +msgstr "No se prporcionó un código corto. ¿Qué código corto deseas usar?" + +msgid "Referer" +msgstr "Origen" + +msgid "Date" +msgstr "Fecha" + +msgid "Remote Address" +msgstr "Dirección remota" + +msgid "User agent" +msgstr "Agente de usuario" diff --git a/module/CLI/src/Command/GetVisitsCommand.php b/module/CLI/src/Command/GetVisitsCommand.php index e7221e5e..c25ba27a 100644 --- a/module/CLI/src/Command/GetVisitsCommand.php +++ b/module/CLI/src/Command/GetVisitsCommand.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\Question; +use Zend\I18n\Translator\TranslatorInterface; class GetVisitsCommand extends Command { @@ -20,35 +21,47 @@ class GetVisitsCommand extends Command * @var VisitsTrackerInterface */ private $visitsTracker; + /** + * @var TranslatorInterface + */ + private $translator; /** * GetVisitsCommand constructor. * @param VisitsTrackerInterface|VisitsTracker $visitsTracker + * @param TranslatorInterface $translator * - * @Inject({VisitsTracker::class}) + * @Inject({VisitsTracker::class, "translator"}) */ - public function __construct(VisitsTrackerInterface $visitsTracker) + public function __construct(VisitsTrackerInterface $visitsTracker, TranslatorInterface $translator) { - parent::__construct(null); $this->visitsTracker = $visitsTracker; + $this->translator = $translator; + parent::__construct(null); } public function configure() { $this->setName('shortcode:visits') - ->setDescription('Returns the detailed visits information for provided short code') - ->addArgument('shortCode', InputArgument::REQUIRED, 'The short code which visits we want to get') + ->setDescription( + $this->translator->translate('Returns the detailed visits information for provided short code') + ) + ->addArgument( + 'shortCode', + InputArgument::REQUIRED, + $this->translator->translate('The short code which visits we want to get') + ) ->addOption( 'startDate', 's', InputOption::VALUE_OPTIONAL, - 'Allows to filter visits, returning only those older than start date' + $this->translator->translate('Allows to filter visits, returning only those older than start date') ) ->addOption( 'endDate', 'e', InputOption::VALUE_OPTIONAL, - 'Allows to filter visits, returning only those newer than end date' + $this->translator->translate('Allows to filter visits, returning only those newer than end date') ); } @@ -61,9 +74,10 @@ class GetVisitsCommand extends Command /** @var QuestionHelper $helper */ $helper = $this->getHelper('question'); - $question = new Question( - 'A short code was not provided. Which short code do you want to use?: ' - ); + $question = new Question(sprintf( + '%s ', + $this->translator->translate('A short code was not provided. Which short code do you want to use?:') + )); $shortCode = $helper->ask($input, $output, $question); if (! empty($shortCode)) { @@ -80,10 +94,10 @@ class GetVisitsCommand extends Command $visits = $this->visitsTracker->info($shortCode, new DateRange($startDate, $endDate)); $table = new Table($output); $table->setHeaders([ - 'Referer', - 'Date', - 'Remote Address', - 'User agent', + $this->translator->translate('Referer'), + $this->translator->translate('Date'), + $this->translator->translate('Remote Address'), + $this->translator->translate('User agent'), ]); foreach ($visits as $row) { From 8e51b51cae5d3f7dc8e66c1dad2609da4bf83d97 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 15:43:19 +0200 Subject: [PATCH 3/9] Added translator and translations to ListShortcodesCommand --- module/CLI/lang/es.mo | Bin 2199 -> 2749 bytes module/CLI/lang/es.po | 43 +++++++++++++++--- .../CLI/src/Command/ListShortcodesCommand.php | 36 ++++++++++----- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/module/CLI/lang/es.mo b/module/CLI/lang/es.mo index 2dcd57bcde8e004d9f701961ff46710a00bf1717..002ecddd3c899feefce8fafe3ebbccf18725531c 100644 GIT binary patch delta 1004 zcmX|;O-K}B7{_0Cx75B&%RbEL4Ky_f68t!}nQ-qz0 zAu@;{ymlz|CUsgm*1d}mc?vpojfj5F?C1l}`P{NA8kv0XX$p*z} zk^bWJk{!&gO0q&}Cf0$oMRiOrnEWR(8II+s!?8KKfv7WnGj?{GMF1WtsC4X}pqqFIIJ8+D; zRWbU$81|hx zqdhCDll;t=aw8X$3x#vJk>UDW;d5>{8{Z1a_W4vVHr~oeYRcG^?9`4)mDp~h?k8tj z?+%F;QsTg|BB`IMp+Xp2kwTdcc7(0Aew>_W)E##rNaKMj(<5}NCfx0%tyCTbMWFFa y)%bCM$N-VeafXu7wtFKt8k^(=GS9L|rrA}RTb^zCtCqu-EZx}jVz;_mTm1+4iNRX{ delta 456 zcmX}nJxc>Y5P;#iz1x_?uS5ufSZEW2h)4vr5l@X`5!7HS$RUCJ0I{)16Cv6zVy7sU zX(U*QV55y_;SUf6I}3X&-#LQ|bI;E0-OSEi?lfC}8wgH>l_iFWD`JqC@OZGAsQD2t z;02cO0~axGM3%9J0k*IYuQ9~?PVV_4v*a1$ zh}d|e lUV^e)vY{!?xrWV~>0-NOABshortUrlService = $shortUrlService; + $this->translator = $translator; + parent::__construct(null); } public function configure() { $this->setName('shortcode:list') - ->setDescription('List all short URLs') + ->setDescription($this->translator->translate('List all short URLs')) ->addOption( 'page', 'p', InputOption::VALUE_OPTIONAL, - sprintf('The first page to list (%s items per page)', PaginableRepositoryAdapter::ITEMS_PER_PAGE), + sprintf( + $this->translator->translate('The first page to list (%s items per page)'), + PaginableRepositoryAdapter::ITEMS_PER_PAGE + ), 1 ); } @@ -59,10 +69,10 @@ class ListShortcodesCommand extends Command $page++; $table = new Table($output); $table->setHeaders([ - 'Short code', - 'Original URL', - 'Date created', - 'Visits count', + $this->translator->translate('Short code'), + $this->translator->translate('Original URL'), + $this->translator->translate('Date created'), + $this->translator->translate('Visits count'), ]); foreach ($result as $row) { @@ -72,10 +82,14 @@ class ListShortcodesCommand extends Command if ($this->isLastPage($result)) { $continue = false; - $output->writeln('You have reached last page'); + $output->writeln( + sprintf('%s', $this->translator->translate('You have reached last page')) + ); } else { $continue = $helper->ask($input, $output, new ConfirmationQuestion( - sprintf('Continue with page %s? (y/N) ', $page), + sprintf('' . $this->translator->translate( + 'Continue with page' + ) . ' %s? (y/N) ', $page), false )); } From 6a05265a4846c14c706488f82cd5aebd8daca1a1 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 15:50:27 +0200 Subject: [PATCH 4/9] Added translator and translations to ProcessVisitsCommand --- module/CLI/lang/es.mo | Bin 2749 -> 3165 bytes module/CLI/lang/es.po | 20 +++++++++- .../CLI/src/Command/ProcessVisitsCommand.php | 35 +++++++++++++----- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/module/CLI/lang/es.mo b/module/CLI/lang/es.mo index 002ecddd3c899feefce8fafe3ebbccf18725531c..8c24a7b497614ef7e8e972d2c9326a246ca0d746 100644 GIT binary patch delta 1050 zcmYk)Pe>I(9Ki8;K3_iVm1U{V)EtFM!~O_j8T~Od;X%-oqEnac?dDt9cH_-bWQ34` zoq||`qDy(|;LSrBbn;RVbSgVk6hYT6UW$UgzumDmFNYw@?rM-R&DhEn*_;SNs}X%swHDie7#A+k}So<*S0-L^Y7L5Dv=tFjR zR8}}fUv-Te8hx$m)SP4|H}JJ$}pIHA7ogsS{+gHm}ZUw2?a7QTE#`Xb zx+mP4Q>nNsOMbGMJ7d|FcKjsYHw~-(U=grtiyr~^XpNkcbB zbEJ>d&isd-!f&p8*QVy5F~|NRGj4B~vOVAUwu_cEYxV;vYp!t-NczlE@X0)ec1visitService = $visitService; $this->ipLocationResolver = $ipLocationResolver; + $this->translator = $translator; + parent::__construct(null); } public function configure() { $this->setName('visit:process') - ->setDescription('Processes visits where location is not set already'); + ->setDescription( + $this->translator->translate('Processes visits where location is not set yet') + ); } public function execute(InputInterface $input, OutputInterface $output) @@ -51,9 +63,11 @@ class ProcessVisitsCommand extends Command foreach ($visits as $visit) { $ipAddr = $visit->getRemoteAddr(); - $output->write(sprintf('Processing IP %s', $ipAddr)); + $output->write(sprintf('%s %s', $this->translator->translate('Processing IP'), $ipAddr)); if ($ipAddr === self::LOCALHOST) { - $output->writeln(' (Ignored localhost address)'); + $output->writeln( + sprintf(' (%s)', $this->translator->translate('Ignored localhost address')) + ); continue; } @@ -63,12 +77,15 @@ class ProcessVisitsCommand extends Command $location->exchangeArray($result); $visit->setVisitLocation($location); $this->visitService->saveVisit($visit); - $output->writeln(sprintf(' (Address located at "%s")', $location->getCityName())); + $output->writeln(sprintf( + ' (' . $this->translator->translate('Address located at "%s"') . ')', + $location->getCityName() + )); } catch (WrongIpException $e) { continue; } } - $output->writeln('Finished processing all IPs'); + $output->writeln($this->translator->translate('Finished processing all IPs')); } } From 73a35a8f449bc3d0974f86f50d0b0e15d030c31e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 16:01:16 +0200 Subject: [PATCH 5/9] Added translator and translations to ResolveUrlCommand --- module/CLI/lang/es.mo | Bin 3165 -> 3826 bytes module/CLI/lang/es.po | 25 ++++++++++- module/CLI/src/Command/ResolveUrlCommand.php | 41 +++++++++++++------ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/module/CLI/lang/es.mo b/module/CLI/lang/es.mo index 8c24a7b497614ef7e8e972d2c9326a246ca0d746..5d5830b3db3fb3b331f55692446c7766ffeddf52 100644 GIT binary patch delta 1205 zcmZ9~Uq}=|9Ki9p(=+w@W0rsVqfKTK2boqHNR*Wdf%HcS3PRYt@mx4}Y;W(h2&5pW z*A@#3A*l$Wx1)z%dJ%-^p?d2@A)=_ai15XizQ0XF8gA#aJ9{(p`~7AY>VGv%EmoIK zD_S$xPOhUdr8@9(oQ~F3qEsEW<8eHVeRvmp@Waacno^}s@Vp;Sp~Y$}B0n`xw;tc& z4*Y~0m6}q&x#?!0B%#zc?7=NKgcazc#v3RL%peoh3v9*LxBe2V?} z6?^d@lN)dn&*FU?#BW&7{;Dgf6hW&&l#*RSi7<J##2+Qb z9Y1G^OYvv1RKN8y`ochempWrbJ!11-n%>vBQR@eK*rttc+M8<%AC}&0J?Gnkn>OkH zU+PgOryWnbUct$_(flzd2vdnm4Fe{~`(925qef>}cGFkPsLM7^IFZPN^NHv2j>DlX zYl(Ms79W&-iN$Pqy}Z5ZYTh-z$q|EBFn-_`ZTWJnCF}SZCyF;`*jF2m_kr)Et@d0m zllL4+BpgcCl}2%!%Hpx!tR9{#rrnHXF+Z?++_&S_A9k%L^W=XzaE)hl-jn1B-byla uvt(n#+T>VUk16C$wm=fm`al>uZ32I8Hit5E?H=LH~~kb{4Xk z1^xq8R@fkqvL)GgEPQ|0O@4Ly+;i^l-t#+;x9DvoaTj)P3Qc)TKoB!uySRo7uT{ZOi1 zp)_J2{dtVy2@c^Ej$mb)NCX#=e~Llffo-h9L)5}AaS)$S^BbtLnE7RZ2VLWUYz zAD2)+JjX7)MrxBtx=0q5;xIO%<}G6rZlE67nbW^V&Ch2NEvy>LupJXDSn_E9LzDEZ zG<1t~)mkw-{uf%X7RzGnW6N>g=uxsUc0oGC+;->@glL&Gy=xjQro!Gb?vj1^bdY;u zABSBr&wf*vvXczCCoHq;*)VbMn7Q+gn_=I&E7oEjeU+|QYtourlShortener = $urlShortener; + $this->translator = $translator; + parent::__construct(null); } public function configure() { $this->setName('shortcode:parse') - ->setDescription('Returns the long URL behind a short code') - ->addArgument('shortCode', InputArgument::REQUIRED, 'The short code to parse'); + ->setDescription($this->translator->translate('Returns the long URL behind a short code')) + ->addArgument( + 'shortCode', + InputArgument::REQUIRED, + $this->translator->translate('The short code to parse') + ); } public function interact(InputInterface $input, OutputInterface $output) @@ -47,9 +58,10 @@ class ResolveUrlCommand extends Command /** @var QuestionHelper $helper */ $helper = $this->getHelper('question'); - $question = new Question( - 'A short code was not provided. Which short code do you want to parse?: ' - ); + $question = new Question(sprintf( + '%s ', + $this->translator->translate('A short code was not provided. Which short code do you want to parse?:') + )); $shortCode = $helper->ask($input, $output, $question); if (! empty($shortCode)) { @@ -64,15 +76,18 @@ class ResolveUrlCommand extends Command try { $longUrl = $this->urlShortener->shortCodeToUrl($shortCode); if (! isset($longUrl)) { - $output->writeln(sprintf('No URL found for short code "%s"', $shortCode)); + $output->writeln(sprintf( + '' . $this->translator->translate('No URL found for short code "%s"') . '', + $shortCode + )); return; } - $output->writeln(sprintf('Long URL %s', $longUrl)); + $output->writeln(sprintf('%s %s', $this->translator->translate('Long URL:'), $longUrl)); } catch (InvalidShortCodeException $e) { - $output->writeln( - sprintf('Provided short code "%s" has an invalid format.', $shortCode) - ); + $output->writeln(sprintf('' . $this->translator->translate( + 'Provided short code "%s" has an invalid format.' + ) . '', $shortCode)); } } } From 06868f782b74f41630ffa15c3c4d8bf4448c0cdd Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 16:20:27 +0200 Subject: [PATCH 6/9] Created middleware for rest that reads the language from the Accept-Language header --- data/docs/rest.md | 6 ++ .../config/middleware-pipeline.config.php | 1 + module/Rest/config/services.config.php | 1 + module/Rest/config/translator.config.php | 14 ++++ .../Rest/src/Middleware/LocaleMiddleware.php | 82 +++++++++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 module/Rest/config/translator.config.php create mode 100644 module/Rest/src/Middleware/LocaleMiddleware.php diff --git a/data/docs/rest.md b/data/docs/rest.md index 09147518..35f18e2a 100644 --- a/data/docs/rest.md +++ b/data/docs/rest.md @@ -15,6 +15,12 @@ Statuses: [TODO] +## Language + +In order to set the application language, you have to pass it by using the Accept-Language header. + +If not provided or provided language is not supported, english (en_US) will be used. + ## Endpoints #### Authenticate diff --git a/module/Rest/config/middleware-pipeline.config.php b/module/Rest/config/middleware-pipeline.config.php index 78c20c38..aa9889f1 100644 --- a/module/Rest/config/middleware-pipeline.config.php +++ b/module/Rest/config/middleware-pipeline.config.php @@ -8,6 +8,7 @@ return [ 'path' => '/rest', 'middleware' => [ Middleware\CheckAuthenticationMiddleware::class, + Middleware\LocaleMiddleware::class, Middleware\CrossDomainMiddleware::class, ], 'priority' => 5, diff --git a/module/Rest/config/services.config.php b/module/Rest/config/services.config.php index aff4cc96..093e6e41 100644 --- a/module/Rest/config/services.config.php +++ b/module/Rest/config/services.config.php @@ -19,6 +19,7 @@ return [ Middleware\CrossDomainMiddleware::class => InvokableFactory::class, Middleware\CheckAuthenticationMiddleware::class => AnnotatedFactory::class, + Middleware\LocaleMiddleware::class => AnnotatedFactory::class, ], ], diff --git a/module/Rest/config/translator.config.php b/module/Rest/config/translator.config.php new file mode 100644 index 00000000..ae120db3 --- /dev/null +++ b/module/Rest/config/translator.config.php @@ -0,0 +1,14 @@ + [ + 'translation_file_patterns' => [ + [ + 'type' => 'gettext', + 'base_dir' => __DIR__ . '/../lang', + 'pattern' => '%s.mo', + ], + ], + ], + +]; diff --git a/module/Rest/src/Middleware/LocaleMiddleware.php b/module/Rest/src/Middleware/LocaleMiddleware.php new file mode 100644 index 00000000..f3a4ddde --- /dev/null +++ b/module/Rest/src/Middleware/LocaleMiddleware.php @@ -0,0 +1,82 @@ +translator = $translator; + } + + /** + * Process an incoming request and/or response. + * + * Accepts a server-side request and a response instance, and does + * something with them. + * + * If the response is not complete and/or further processing would not + * interfere with the work done in the middleware, or if the middleware + * wants to delegate to another process, it can use the `$out` callable + * if present. + * + * If the middleware does not return a value, execution of the current + * request is considered complete, and the response instance provided will + * be considered the response to return. + * + * Alternately, the middleware may return a response instance. + * + * Often, middleware will `return $out();`, with the assumption that a + * later middleware will return a response. + * + * @param Request $request + * @param Response $response + * @param null|callable $out + * @return null|Response + */ + public function __invoke(Request $request, Response $response, callable $out = null) + { + if (! $request->hasHeader('Accept-Language')) { + return $out($request, $response); + } + + $locale = $request->getHeaderLine('Accept-Language'); + $this->translator->setLocale($this->normalizeLocale($locale)); + return $out($request, $response); + } + + /** + * @param string $locale + * @return string + */ + protected function normalizeLocale($locale) + { + $parts = explode('_', $locale); + if (count($parts) > 1) { + return $parts[0]; + } + + $parts = explode('-', $locale); + if (count($parts) > 1) { + return $parts[0]; + } + + return $locale; + } +} From e42469b0908c9650f02988a5bb7a33e46ddb3fa3 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 16:41:16 +0200 Subject: [PATCH 7/9] Added translations for error messages returned by the REST API --- module/Rest/lang/es.mo | Bin 0 -> 1754 bytes module/Rest/lang/es.po | 60 ++++++++++++++++++ .../src/Action/AuthenticateMiddleware.php | 15 +++-- .../src/Action/CreateShortcodeMiddleware.php | 25 ++++++-- .../Rest/src/Action/GetVisitsMiddleware.php | 15 +++-- .../src/Action/ListShortcodesMiddleware.php | 13 +++- .../Rest/src/Action/ResolveUrlMiddleware.php | 20 ++++-- .../CheckAuthenticationMiddleware.php | 17 +++-- module/Rest/src/Service/RestTokenService.php | 2 +- module/Rest/src/Util/RestUtils.php | 2 +- 10 files changed, 141 insertions(+), 28 deletions(-) create mode 100644 module/Rest/lang/es.mo create mode 100644 module/Rest/lang/es.po diff --git a/module/Rest/lang/es.mo b/module/Rest/lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..2a8d4431a081c2eee3abe6973ebe7cb2bac0ce20 GIT binary patch literal 1754 zcmZ`(L5m|r6fSkuHJ}F%3xen~BDhHRBw3jmd&og&XJs(68)h=Ho}{MxB`LR4)mBw! zHuwt!4_^EMUIY&+vf#;E4*meaqu@W#f8bZ$Njl8#HoSc4>Q~?U-m7}=?^myVC-Agj zufuZK%dp>J;rSPK1NI+`VAr1!;v3*i;G4kTfP28dfHCluXN7nh*ayA_l)xVFE8suy z`5E{I-oL$8`~3n8vHk?^hV|!#xC{IP_#yDd3qo{&_kls**T7rAAAq0}&%v(4u)TUf zFR-=B=f!$SytG1W0V$oI4(L)k(#BI^&0J-iiDNyNxyqtA zNtqgt*+L;1&2h7@;-d{AO;InY*AyY-O!}w0H_^no=7$BRo-<-=A=RX5X)Q|rnJK|L zXXL)F16>%%YOfEWT@|XmK7)1$_yxG4<1G4=twTu@I-BN7KcH^hX$|jc9PWHNDlR?#+XS;LMaZ<-=N8LbbFF{U^K#&EN*gdE~Mx3?FR%)gia& zSlVULNYT{Y8mdbICrJA%FtrR1UAHV zy!;itW@x_r9t~{V(y}K_@{uNklk<$9aJy@cm`)kfKvrRO$#ckIG;!ts zIKzS%JAabIc{2{S#A80BGK0t~Ojyq)K3#B%bFCV@5zjdNkYAHLW)Zv)BV9hq)Fc$Z zqIE)hi$PVMdPZzl4*pGEGNxngmp^JRb3@1@2&(E>C5l;4jRMSpqpLx-IqQ(*6|N&T zu7-gLXRxW@;|xLOl|$WrestTokenService = $restTokenService; + $this->translator = $translator; } /** @@ -40,7 +47,7 @@ class AuthenticateMiddleware extends AbstractRestMiddleware if (! isset($authData['username'], $authData['password'])) { return new JsonResponse([ 'error' => RestUtils::INVALID_ARGUMENT_ERROR, - 'message' => 'You have to provide both "username" and "password"' + 'message' => $this->translator->translate('You have to provide both "username" and "password"'), ], 400); } @@ -50,7 +57,7 @@ class AuthenticateMiddleware extends AbstractRestMiddleware } catch (AuthenticationException $e) { return new JsonResponse([ 'error' => RestUtils::getRestErrorCodeFromException($e), - 'message' => 'Invalid username and/or password', + 'message' => $this->translator->translate('Invalid username and/or password'), ], 401); } } diff --git a/module/Rest/src/Action/CreateShortcodeMiddleware.php b/module/Rest/src/Action/CreateShortcodeMiddleware.php index fdb0ade3..fe3074fa 100644 --- a/module/Rest/src/Action/CreateShortcodeMiddleware.php +++ b/module/Rest/src/Action/CreateShortcodeMiddleware.php @@ -10,6 +10,7 @@ use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; use Shlinkio\Shlink\Rest\Util\RestUtils; use Zend\Diactoros\Response\JsonResponse; use Zend\Diactoros\Uri; +use Zend\I18n\Translator\TranslatorInterface; class CreateShortcodeMiddleware extends AbstractRestMiddleware { @@ -21,18 +22,27 @@ class CreateShortcodeMiddleware extends AbstractRestMiddleware * @var array */ private $domainConfig; + /** + * @var TranslatorInterface + */ + private $translator; /** * GenerateShortcodeMiddleware constructor. * * @param UrlShortenerInterface|UrlShortener $urlShortener + * @param TranslatorInterface $translator * @param array $domainConfig * - * @Inject({UrlShortener::class, "config.url_shortener.domain"}) + * @Inject({UrlShortener::class, "translator", "config.url_shortener.domain"}) */ - public function __construct(UrlShortenerInterface $urlShortener, array $domainConfig) - { + public function __construct( + UrlShortenerInterface $urlShortener, + TranslatorInterface $translator, + array $domainConfig + ) { $this->urlShortener = $urlShortener; + $this->translator = $translator; $this->domainConfig = $domainConfig; } @@ -48,7 +58,7 @@ class CreateShortcodeMiddleware extends AbstractRestMiddleware if (! isset($postData['longUrl'])) { return new JsonResponse([ 'error' => RestUtils::INVALID_ARGUMENT_ERROR, - 'message' => 'A URL was not provided', + 'message' => $this->translator->translate('A URL was not provided'), ], 400); } $longUrl = $postData['longUrl']; @@ -67,12 +77,15 @@ class CreateShortcodeMiddleware extends AbstractRestMiddleware } catch (InvalidUrlException $e) { return new JsonResponse([ 'error' => RestUtils::getRestErrorCodeFromException($e), - 'message' => sprintf('Provided URL "%s" is invalid. Try with a different one.', $longUrl), + 'message' => sprintf( + $this->translator->translate('Provided URL "%s" is invalid. Try with a different one.'), + $longUrl + ), ], 400); } catch (\Exception $e) { return new JsonResponse([ 'error' => RestUtils::UNKNOWN_ERROR, - 'message' => 'Unexpected error occured', + 'message' => $this->translator->translate('Unexpected error occurred'), ], 500); } } diff --git a/module/Rest/src/Action/GetVisitsMiddleware.php b/module/Rest/src/Action/GetVisitsMiddleware.php index 3e5bfbaf..75765389 100644 --- a/module/Rest/src/Action/GetVisitsMiddleware.php +++ b/module/Rest/src/Action/GetVisitsMiddleware.php @@ -10,6 +10,7 @@ use Shlinkio\Shlink\Core\Service\VisitsTracker; use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface; use Shlinkio\Shlink\Rest\Util\RestUtils; use Zend\Diactoros\Response\JsonResponse; +use Zend\I18n\Translator\TranslatorInterface; class GetVisitsMiddleware extends AbstractRestMiddleware { @@ -17,16 +18,22 @@ class GetVisitsMiddleware extends AbstractRestMiddleware * @var VisitsTrackerInterface */ private $visitsTracker; + /** + * @var TranslatorInterface + */ + private $translator; /** * GetVisitsMiddleware constructor. * @param VisitsTrackerInterface|VisitsTracker $visitsTracker + * @param TranslatorInterface $translator * - * @Inject({VisitsTracker::class}) + * @Inject({VisitsTracker::class, "translator"}) */ - public function __construct(VisitsTrackerInterface $visitsTracker) + public function __construct(VisitsTrackerInterface $visitsTracker, TranslatorInterface $translator) { $this->visitsTracker = $visitsTracker; + $this->translator = $translator; } /** @@ -52,12 +59,12 @@ class GetVisitsMiddleware extends AbstractRestMiddleware } catch (InvalidArgumentException $e) { return new JsonResponse([ 'error' => RestUtils::getRestErrorCodeFromException($e), - 'message' => sprintf('Provided short code "%s" is invalid', $shortCode), + 'message' => sprintf($this->translator->translate('Provided short code "%s" is invalid'), $shortCode), ], 400); } catch (\Exception $e) { return new JsonResponse([ 'error' => RestUtils::UNKNOWN_ERROR, - 'message' => 'Unexpected error occured', + 'message' => $this->translator->translate('Unexpected error occurred'), ], 500); } } diff --git a/module/Rest/src/Action/ListShortcodesMiddleware.php b/module/Rest/src/Action/ListShortcodesMiddleware.php index 4361ab89..99b1c718 100644 --- a/module/Rest/src/Action/ListShortcodesMiddleware.php +++ b/module/Rest/src/Action/ListShortcodesMiddleware.php @@ -9,6 +9,7 @@ use Shlinkio\Shlink\Core\Service\ShortUrlService; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; use Shlinkio\Shlink\Rest\Util\RestUtils; use Zend\Diactoros\Response\JsonResponse; +use Zend\I18n\Translator\TranslatorInterface; class ListShortcodesMiddleware extends AbstractRestMiddleware { @@ -18,16 +19,22 @@ class ListShortcodesMiddleware extends AbstractRestMiddleware * @var ShortUrlServiceInterface */ private $shortUrlService; + /** + * @var TranslatorInterface + */ + private $translator; /** * ListShortcodesMiddleware constructor. * @param ShortUrlServiceInterface|ShortUrlService $shortUrlService + * @param TranslatorInterface $translator * - * @Inject({ShortUrlService::class}) + * @Inject({ShortUrlService::class, "translator"}) */ - public function __construct(ShortUrlServiceInterface $shortUrlService) + public function __construct(ShortUrlServiceInterface $shortUrlService, TranslatorInterface $translator) { $this->shortUrlService = $shortUrlService; + $this->translator = $translator; } /** @@ -45,7 +52,7 @@ class ListShortcodesMiddleware extends AbstractRestMiddleware } catch (\Exception $e) { return new JsonResponse([ 'error' => RestUtils::UNKNOWN_ERROR, - 'message' => 'Unexpected error occured', + 'message' => $this->translator->translate('Unexpected error occurred'), ], 500); } } diff --git a/module/Rest/src/Action/ResolveUrlMiddleware.php b/module/Rest/src/Action/ResolveUrlMiddleware.php index 92a1e5c2..db7e5827 100644 --- a/module/Rest/src/Action/ResolveUrlMiddleware.php +++ b/module/Rest/src/Action/ResolveUrlMiddleware.php @@ -9,6 +9,7 @@ use Shlinkio\Shlink\Core\Service\UrlShortener; use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; use Shlinkio\Shlink\Rest\Util\RestUtils; use Zend\Diactoros\Response\JsonResponse; +use Zend\I18n\Translator\TranslatorInterface; class ResolveUrlMiddleware extends AbstractRestMiddleware { @@ -16,16 +17,22 @@ class ResolveUrlMiddleware extends AbstractRestMiddleware * @var UrlShortenerInterface */ private $urlShortener; + /** + * @var TranslatorInterface + */ + private $translator; /** * ResolveUrlMiddleware constructor. * @param UrlShortenerInterface|UrlShortener $urlShortener + * @param TranslatorInterface $translator * - * @Inject({UrlShortener::class}) + * @Inject({UrlShortener::class, "translator"}) */ - public function __construct(UrlShortenerInterface $urlShortener) + public function __construct(UrlShortenerInterface $urlShortener, TranslatorInterface $translator) { $this->urlShortener = $urlShortener; + $this->translator = $translator; } /** @@ -43,7 +50,7 @@ class ResolveUrlMiddleware extends AbstractRestMiddleware if (! isset($longUrl)) { return new JsonResponse([ 'error' => RestUtils::INVALID_ARGUMENT_ERROR, - 'message' => sprintf('No URL found for shortcode "%s"', $shortCode), + 'message' => sprintf($this->translator->translate('No URL found for shortcode "%s"'), $shortCode), ], 400); } @@ -53,12 +60,15 @@ class ResolveUrlMiddleware extends AbstractRestMiddleware } catch (InvalidShortCodeException $e) { return new JsonResponse([ 'error' => RestUtils::getRestErrorCodeFromException($e), - 'message' => sprintf('Provided short code "%s" has an invalid format', $shortCode), + 'message' => sprintf( + $this->translator->translate('Provided short code "%s" has an invalid format'), + $shortCode + ), ], 400); } catch (\Exception $e) { return new JsonResponse([ 'error' => RestUtils::UNKNOWN_ERROR, - 'message' => 'Unexpected error occured', + 'message' => $this->translator->translate('Unexpected error occurred'), ], 500); } } diff --git a/module/Rest/src/Middleware/CheckAuthenticationMiddleware.php b/module/Rest/src/Middleware/CheckAuthenticationMiddleware.php index 9120e2c6..f05c1b78 100644 --- a/module/Rest/src/Middleware/CheckAuthenticationMiddleware.php +++ b/module/Rest/src/Middleware/CheckAuthenticationMiddleware.php @@ -10,6 +10,7 @@ use Shlinkio\Shlink\Rest\Service\RestTokenServiceInterface; use Shlinkio\Shlink\Rest\Util\RestUtils; use Zend\Diactoros\Response\JsonResponse; use Zend\Expressive\Router\RouteResult; +use Zend\I18n\Translator\TranslatorInterface; use Zend\Stratigility\MiddlewareInterface; class CheckAuthenticationMiddleware implements MiddlewareInterface @@ -20,16 +21,22 @@ class CheckAuthenticationMiddleware implements MiddlewareInterface * @var RestTokenServiceInterface */ private $restTokenService; + /** + * @var TranslatorInterface + */ + private $translator; /** * CheckAuthenticationMiddleware constructor. * @param RestTokenServiceInterface|RestTokenService $restTokenService + * @param TranslatorInterface $translator * - * @Inject({RestTokenService::class}) + * @Inject({RestTokenService::class, "translator"}) */ - public function __construct(RestTokenServiceInterface $restTokenService) + public function __construct(RestTokenServiceInterface $restTokenService, TranslatorInterface $translator) { $this->restTokenService = $restTokenService; + $this->translator = $translator; } /** @@ -93,8 +100,10 @@ class CheckAuthenticationMiddleware implements MiddlewareInterface return new JsonResponse([ 'error' => RestUtils::INVALID_AUTH_TOKEN_ERROR, 'message' => sprintf( - 'Missing or invalid auth token provided. Perform a new authentication request and send provided token ' - . 'on every new request on the "%s" header', + $this->translator->translate( + 'Missing or invalid auth token provided. Perform a new authentication request and send provided ' + . 'token on every new request on the "%s" header' + ), self::AUTH_TOKEN_HEADER ), ], 401); diff --git a/module/Rest/src/Service/RestTokenService.php b/module/Rest/src/Service/RestTokenService.php index c452af2a..b9dd4a9d 100644 --- a/module/Rest/src/Service/RestTokenService.php +++ b/module/Rest/src/Service/RestTokenService.php @@ -21,8 +21,8 @@ class RestTokenService implements RestTokenServiceInterface /** * ShortUrlService constructor. * @param EntityManagerInterface $em - * * @param array $restConfig + * * @Inject({"em", "config.rest"}) */ public function __construct(EntityManagerInterface $em, array $restConfig) diff --git a/module/Rest/src/Util/RestUtils.php b/module/Rest/src/Util/RestUtils.php index 82082dcd..8e50a1cf 100644 --- a/module/Rest/src/Util/RestUtils.php +++ b/module/Rest/src/Util/RestUtils.php @@ -21,7 +21,7 @@ class RestUtils return self::INVALID_SHORTCODE_ERROR; case $e instanceof Core\InvalidUrlException: return self::INVALID_URL_ERROR; - case $e instanceof Core\InvalidArgumentException: + case $e instanceof Common\InvalidArgumentException: return self::INVALID_ARGUMENT_ERROR; case $e instanceof Rest\AuthenticationException: return self::INVALID_CREDENTIALS_ERROR; From fb9c7f8eecc30f5e20d394e500a6ab73d15dda48 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 16:54:00 +0200 Subject: [PATCH 8/9] Used twig extension to load translations on twig templates --- module/Core/config/translator.config.php | 14 +++++++ module/Core/lang/es.mo | Bin 0 -> 1036 bytes module/Core/lang/es.po | 35 ++++++++++++++++++ .../Core/templates/core/error/404.html.twig | 8 ++-- .../Core/templates/core/error/error.html.twig | 8 ++-- 5 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 module/Core/config/translator.config.php create mode 100644 module/Core/lang/es.mo create mode 100644 module/Core/lang/es.po diff --git a/module/Core/config/translator.config.php b/module/Core/config/translator.config.php new file mode 100644 index 00000000..ae120db3 --- /dev/null +++ b/module/Core/config/translator.config.php @@ -0,0 +1,14 @@ + [ + 'translation_file_patterns' => [ + [ + 'type' => 'gettext', + 'base_dir' => __DIR__ . '/../lang', + 'pattern' => '%s.mo', + ], + ], + ], + +]; diff --git a/module/Core/lang/es.mo b/module/Core/lang/es.mo new file mode 100644 index 0000000000000000000000000000000000000000..d34bb83b2b79815b5e36833dd99a0c223c0b994a GIT binary patch literal 1036 zcmZuw!A=xG5N#DTS>r*@YKnsqBQx%7B9PhaL2*%Vkqs>9#h7NM*cqAa-gNhXB>sjU zzzq*3Cf+={U*W|c@Zib2uV)ZAP??w4JzaTKQ~l=G^wcMTxCYz?dcY0fBjDp0=mMXC ztH2*%7WfO?1nyoE;tqHYJbhV+Rq!hIZ?6b(9efP-`Cq}ez(2tR{tZTIRTorgd-gE} zK&lF))v+e`m~3g74s}VnN{TY&lw?tm%b1c(8ksmYc8(5mmyy!MADodiE>+@6>2j?? zv98BQ25hV+R?HuRVrRHu}ZK+8DE<2 zS^uyx+JvHS^m|TRu#yJbXanV9+RTbv?NJmq8~yb`uw=OEBX}kq$JA;@^FebVXhjsw z$B&y2o2{l#3^sT__W~E^MRi_JS{DqARJM>#o0u9ssfMK-axh>yI=7FB?Yr(~V{K)v zd)Aw8YDwc;6l4y^=*MATkH&?}RlAy}W!KpnEC-8cdA{5(o1lw1q`4Z#w78SI#+%?d zD>ii>ea&g^@X?FmLKxw!ei1yCmdDa%F@<5HUzA1`!Ll|Z81sa|$w70q-g zdeA--*wm#-cnLR+&SI)tJC%RQhX>lEwwGHR;^wQtaQkG%b;bW%$M5Pw=j2(MB @@ -10,8 +10,8 @@ {% endblock %} {% block content %} -

Oops!

+

{{ translate('Oops!') }}


-

This short URL doesn't seem to be valid.

-

Make sure you included all the characters, with no extra punctuation.

+

{{ translate('This short URL doesn\'t seem to be valid.') }}

+

{{ translate('Make sure you included all the characters, with no extra punctuation.') }}

{% endblock %} diff --git a/module/Core/templates/core/error/error.html.twig b/module/Core/templates/core/error/error.html.twig index c93b3b7e..7426a33a 100644 --- a/module/Core/templates/core/error/error.html.twig +++ b/module/Core/templates/core/error/error.html.twig @@ -10,11 +10,11 @@ {% endblock %} {% block content %} -

Oops!

+

{{ translate('Oops!') }}


-

We encountered a {{ status }} {{ reason }} error.

+

{{ translate('We encountered a %s %s error.') | format(status, reason) }}

{% if status == 404 %} -

This short URL doesn't seem to be valid.

-

Make sure you included all the characters, with no extra punctuation.

+

{{ translate('This short URL doesn\'t seem to be valid.') }}

+

{{ translate('Make sure you included all the characters, with no extra punctuation.') }}

{% endif %} {% endblock %} From cd5bbcd60a638425b3d173b60513684f06be6093 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 21 Jul 2016 16:59:27 +0200 Subject: [PATCH 9/9] Reused middleware to check Accept-Language header on any HTTP related middleware --- .../Common/config/middleware-pipeline.config.php | 14 ++++++++++++++ module/Common/config/services.config.php | 2 ++ .../src/Middleware/LocaleMiddleware.php | 2 +- module/Rest/config/middleware-pipeline.config.php | 1 - module/Rest/config/services.config.php | 1 - 5 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 module/Common/config/middleware-pipeline.config.php rename module/{Rest => Common}/src/Middleware/LocaleMiddleware.php (98%) diff --git a/module/Common/config/middleware-pipeline.config.php b/module/Common/config/middleware-pipeline.config.php new file mode 100644 index 00000000..361621c6 --- /dev/null +++ b/module/Common/config/middleware-pipeline.config.php @@ -0,0 +1,14 @@ + [ + 'pre-routing' => [ + 'middleware' => [ + Middleware\LocaleMiddleware::class, + ], + 'priority' => 5, + ], + ], +]; diff --git a/module/Common/config/services.config.php b/module/Common/config/services.config.php index 1423b543..42d9dbc5 100644 --- a/module/Common/config/services.config.php +++ b/module/Common/config/services.config.php @@ -5,6 +5,7 @@ use Doctrine\ORM\EntityManager; use Shlinkio\Shlink\Common\Factory\CacheFactory; use Shlinkio\Shlink\Common\Factory\EntityManagerFactory; use Shlinkio\Shlink\Common\Factory\TranslatorFactory; +use Shlinkio\Shlink\Common\Middleware\LocaleMiddleware; use Shlinkio\Shlink\Common\Service\IpLocationResolver; use Shlinkio\Shlink\Common\Twig\Extension\TranslatorExtension; use Zend\I18n\Translator\Translator; @@ -20,6 +21,7 @@ return [ IpLocationResolver::class => AnnotatedFactory::class, Translator::class => TranslatorFactory::class, TranslatorExtension::class => AnnotatedFactory::class, + LocaleMiddleware::class => AnnotatedFactory::class, ], 'aliases' => [ 'em' => EntityManager::class, diff --git a/module/Rest/src/Middleware/LocaleMiddleware.php b/module/Common/src/Middleware/LocaleMiddleware.php similarity index 98% rename from module/Rest/src/Middleware/LocaleMiddleware.php rename to module/Common/src/Middleware/LocaleMiddleware.php index f3a4ddde..20f796ff 100644 --- a/module/Rest/src/Middleware/LocaleMiddleware.php +++ b/module/Common/src/Middleware/LocaleMiddleware.php @@ -1,5 +1,5 @@ '/rest', 'middleware' => [ Middleware\CheckAuthenticationMiddleware::class, - Middleware\LocaleMiddleware::class, Middleware\CrossDomainMiddleware::class, ], 'priority' => 5, diff --git a/module/Rest/config/services.config.php b/module/Rest/config/services.config.php index 093e6e41..aff4cc96 100644 --- a/module/Rest/config/services.config.php +++ b/module/Rest/config/services.config.php @@ -19,7 +19,6 @@ return [ Middleware\CrossDomainMiddleware::class => InvokableFactory::class, Middleware\CheckAuthenticationMiddleware::class => AnnotatedFactory::class, - Middleware\LocaleMiddleware::class => AnnotatedFactory::class, ], ],