From bd4efad22bc75df08bdfc166f79de619ad55c9a7 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Mon, 9 Nov 2020 13:28:22 +0100 Subject: [PATCH] added event to manage javascript translations (#2104) --- .../ThemeJavascriptTranslationsEvent.php | 64 +++++++++++++++++++ src/Twig/EventExtensions.php | 1 + src/Twig/Runtime/ThemeEventExtension.php | 10 +++ templates/base.html.twig | 26 +------- .../ThemeJavascriptTranslationsEventTest.php | 40 ++++++++++++ tests/Twig/EventExtensionsTest.php | 2 +- .../Twig/Runtime/ThemeEventExtensionTest.php | 9 ++- 7 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 src/Event/ThemeJavascriptTranslationsEvent.php create mode 100644 tests/Event/ThemeJavascriptTranslationsEventTest.php diff --git a/src/Event/ThemeJavascriptTranslationsEvent.php b/src/Event/ThemeJavascriptTranslationsEvent.php new file mode 100644 index 00000000..ee055ef6 --- /dev/null +++ b/src/Event/ThemeJavascriptTranslationsEvent.php @@ -0,0 +1,64 @@ +> + */ + private $translations = [ + 'confirm' => ['confirm', 'messages'], + 'cancel' => ['cancel', 'messages'], + 'today' => ['daterangepicker.today', 'daterangepicker'], + 'yesterday' => ['daterangepicker.yesterday', 'daterangepicker'], + 'lastWeek' => ['daterangepicker.lastWeek', 'daterangepicker'], + 'thisWeek' => ['daterangepicker.thisWeek', 'daterangepicker'], + 'lastMonth' => ['daterangepicker.lastMonth', 'daterangepicker'], + 'thisMonth' => ['daterangepicker.thisMonth', 'daterangepicker'], + 'lastYear' => ['daterangepicker.lastYear', 'daterangepicker'], + 'thisYear' => ['daterangepicker.thisYear', 'daterangepicker'], + 'customRange' => ['daterangepicker.customRange', 'daterangepicker'], + 'timesheet.start.success' => ['timesheet.start.success', 'flashmessages'], + 'timesheet.start.error' => ['timesheet.start.error', 'flashmessages'], + 'timesheet.start.exceeded_limit' => ['timesheet.start.exceeded_limit', 'flashmessages'], + 'timesheet.stop.success' => ['timesheet.stop.success', 'flashmessages'], + 'timesheet.stop.error' => ['timesheet.stop.error', 'flashmessages'], + 'action.update.success' => ['action.update.success', 'flashmessages'], + 'action.update.error' => ['action.update.error', 'flashmessages'], + 'action.delete.success' => ['action.delete.success', 'flashmessages'], + 'action.delete.error' => ['action.delete.error', 'flashmessages'], + 'confirm.delete' => ['confirm.delete', 'messages'], + 'delete' => ['action.delete', 'messages'], + 'login.required' => ['login_required', 'messages'], + ]; + + public function getTranslations(): array + { + return $this->translations; + } + + public function setTranslation(string $key, string $translationKey, string $translationDomain = 'messages'): ThemeJavascriptTranslationsEvent + { + $this->translations[$key] = [$translationKey, $translationDomain]; + + return $this; + } +} diff --git a/src/Twig/EventExtensions.php b/src/Twig/EventExtensions.php index 9c04df8d..7c2e52e0 100644 --- a/src/Twig/EventExtensions.php +++ b/src/Twig/EventExtensions.php @@ -22,6 +22,7 @@ class EventExtensions extends AbstractExtension { return [ new TwigFunction('trigger', [ThemeEventExtension::class, 'trigger']), + new TwigFunction('javascript_translations', [ThemeEventExtension::class, 'getJavascriptTranslations']), ]; } } diff --git a/src/Twig/Runtime/ThemeEventExtension.php b/src/Twig/Runtime/ThemeEventExtension.php index e824726d..e3a80b18 100644 --- a/src/Twig/Runtime/ThemeEventExtension.php +++ b/src/Twig/Runtime/ThemeEventExtension.php @@ -10,6 +10,7 @@ namespace App\Twig\Runtime; use App\Event\ThemeEvent; +use App\Event\ThemeJavascriptTranslationsEvent; use App\Security\CurrentUser; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Twig\Extension\RuntimeExtensionInterface; @@ -46,4 +47,13 @@ final class ThemeEventExtension implements RuntimeExtensionInterface return $themeEvent; } + + public function getJavascriptTranslations(): array + { + $event = new ThemeJavascriptTranslationsEvent(); + + $this->eventDispatcher->dispatch($event); + + return $event->getTranslations(); + } } diff --git a/templates/base.html.twig b/templates/base.html.twig index f5f3bbbd..945056fa 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -239,29 +239,9 @@ defaultColor: '{{ constant('App\\Constants::DEFAULT_COLOR') }}', }, { - 'confirm': '{{ 'confirm'|trans }}', - 'cancel': '{{ 'cancel'|trans }}', - 'today': '{{ 'daterangepicker.today'|trans({}, 'daterangepicker')|escape('js') }}', - 'yesterday': '{{ 'daterangepicker.yesterday'|trans({}, 'daterangepicker')|escape('js') }}', - 'lastWeek': '{{ 'daterangepicker.lastWeek'|trans({}, 'daterangepicker')|escape('js') }}', - 'thisWeek': '{{ 'daterangepicker.thisWeek'|trans({}, 'daterangepicker')|escape('js') }}', - 'lastMonth': '{{ 'daterangepicker.lastMonth'|trans({}, 'daterangepicker')|escape('js') }}', - 'thisMonth': '{{ 'daterangepicker.thisMonth'|trans({}, 'daterangepicker')|escape('js') }}', - 'lastYear': '{{ 'daterangepicker.lastYear'|trans({}, 'daterangepicker')|escape('js') }}', - 'thisYear': '{{ 'daterangepicker.thisYear'|trans({}, 'daterangepicker')|escape('js') }}', - 'customRange': '{{ 'daterangepicker.customRange'|trans({}, 'daterangepicker')|escape('js') }}', - 'timesheet.start.success': '{{ 'timesheet.start.success'|trans({}, 'flashmessages')|escape('js') }}', - 'timesheet.start.error': '{{ 'timesheet.start.error'|trans({}, 'flashmessages')|escape('js') }}', - 'timesheet.start.exceeded_limit': '{{ 'timesheet.start.exceeded_limit'|trans({}, 'flashmessages')|escape('js') }}', - 'timesheet.stop.success': '{{ 'timesheet.stop.success'|trans({}, 'flashmessages')|escape('js') }}', - 'timesheet.stop.error': '{{ 'timesheet.stop.error'|trans({}, 'flashmessages')|escape('js') }}', - 'action.update.success': '{{ 'action.update.success'|trans({}, 'flashmessages')|escape('js') }}', - 'action.update.error': '{{ 'action.update.error'|trans({}, 'flashmessages')|escape('js') }}', - 'action.delete.success': '{{ 'action.delete.success'|trans({}, 'flashmessages')|escape('js') }}', - 'action.delete.error': '{{ 'action.delete.error'|trans({}, 'flashmessages')|escape('js') }}', - 'confirm.delete': '{{ 'confirm.delete'|trans|escape('js') }}', - 'delete': '{{ 'action.delete'|trans|escape('js') }}', - 'login.required': '{{ 'login_required'|trans|escape('js') }}' + {% for key, translation in javascript_translations() -%} + '{{ key }}': '{{ translation.0|trans({}, translation.1)|escape('js') }}', + {%- endfor %} } ); window.kimai = loader.getKimai(); diff --git a/tests/Event/ThemeJavascriptTranslationsEventTest.php b/tests/Event/ThemeJavascriptTranslationsEventTest.php new file mode 100644 index 00000000..c87aaedc --- /dev/null +++ b/tests/Event/ThemeJavascriptTranslationsEventTest.php @@ -0,0 +1,40 @@ +assertCount(23, $sut->getTranslations()); + } + + public function testGetterAndSetter() + { + $sut = new ThemeJavascriptTranslationsEvent(); + $sut->setTranslation('foo', 'bar'); + $sut->setTranslation('hello', 'world', 'testing'); + + $result = $sut->getTranslations(); + self::assertCount(25, $result); + self::assertArrayHasKey('foo', $result); + self::assertEquals(['bar', 'messages'], $result['foo']); + self::assertArrayHasKey('hello', $result); + self::assertEquals(['world', 'testing'], $result['hello']); + } +} diff --git a/tests/Twig/EventExtensionsTest.php b/tests/Twig/EventExtensionsTest.php index b45928ae..eefcb36f 100644 --- a/tests/Twig/EventExtensionsTest.php +++ b/tests/Twig/EventExtensionsTest.php @@ -25,7 +25,7 @@ class EventExtensionsTest extends TestCase public function testGetFunctions() { - $functions = ['trigger']; + $functions = ['trigger', 'javascript_translations']; $sut = $this->getSut(); $twigFunctions = $sut->getFunctions(); self::assertCount(\count($functions), $twigFunctions); diff --git a/tests/Twig/Runtime/ThemeEventExtensionTest.php b/tests/Twig/Runtime/ThemeEventExtensionTest.php index 8f8e70df..41a1e1b2 100644 --- a/tests/Twig/Runtime/ThemeEventExtensionTest.php +++ b/tests/Twig/Runtime/ThemeEventExtensionTest.php @@ -24,7 +24,7 @@ class ThemeEventExtensionTest extends TestCase protected function getSut(bool $hasListener = true): ThemeEventExtension { $dispatcher = $this->createMock(EventDispatcherInterface::class); - $dispatcher->expects($this->once())->method('hasListeners')->willReturn($hasListener); + $dispatcher->method('hasListeners')->willReturn($hasListener); $dispatcher->expects($hasListener ? $this->once() : $this->never())->method('dispatch'); $user = (new CurrentUserFactory($this))->create(new User()); @@ -45,4 +45,11 @@ class ThemeEventExtensionTest extends TestCase $event = $sut->trigger('foo', []); self::assertInstanceOf(ThemeEvent::class, $event); } + + public function testJavascriptTranslations() + { + $sut = $this->getSut(); + $values = $sut->getJavascriptTranslations(); + self::assertCount(23, $values); + } }