added event to manage javascript translations (#2104)
This commit is contained in:
64
src/Event/ThemeJavascriptTranslationsEvent.php
Normal file
64
src/Event/ThemeJavascriptTranslationsEvent.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
final class ThemeJavascriptTranslationsEvent extends Event
|
||||
{
|
||||
/**
|
||||
* Contains default translations for all views.
|
||||
*
|
||||
* For usage see "templates/base.html.twig" in block "javascripts".
|
||||
*
|
||||
* Structure
|
||||
* ---------
|
||||
* Javascript key: [Translation key, Translation domain]
|
||||
*
|
||||
* @var array<string, array<int, string>>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ class EventExtensions extends AbstractExtension
|
||||
{
|
||||
return [
|
||||
new TwigFunction('trigger', [ThemeEventExtension::class, 'trigger']),
|
||||
new TwigFunction('javascript_translations', [ThemeEventExtension::class, 'getJavascriptTranslations']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
40
tests/Event/ThemeJavascriptTranslationsEventTest.php
Normal file
40
tests/Event/ThemeJavascriptTranslationsEventTest.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Event;
|
||||
|
||||
use App\Event\ThemeJavascriptTranslationsEvent;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Event\ThemeJavascriptTranslationsEvent
|
||||
*/
|
||||
class ThemeJavascriptTranslationsEventTest extends TestCase
|
||||
{
|
||||
public function testDefaultValues()
|
||||
{
|
||||
$sut = new ThemeJavascriptTranslationsEvent();
|
||||
|
||||
$this->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']);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user