invoices: unified money, number and date formats and fully respect configured language (#1814)
This commit is contained in:
@@ -10,6 +10,8 @@ Perform EACH version specific task between your version and the new one, otherwi
|
|||||||
|
|
||||||
## [1.10](https://github.com/kevinpapst/kimai2/releases/tag/1.10)
|
## [1.10](https://github.com/kevinpapst/kimai2/releases/tag/1.10)
|
||||||
|
|
||||||
|
**New database tables and fields were created, don't forget to [run the updater](https://www.kimai.org/documentation/updates.html).**
|
||||||
|
|
||||||
- Invoice renderer `CSV` was removed
|
- Invoice renderer `CSV` was removed
|
||||||
- Sessions are now stored in the database (all users have to re-login after upgrade)
|
- Sessions are now stored in the database (all users have to re-login after upgrade)
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ use Symfony\Component\HttpKernel\Event\RequestEvent;
|
|||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
|
||||||
class TimezoneSubscriber implements EventSubscriberInterface
|
class UserEnvironmentSubscriber implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var TokenStorageInterface
|
* @var TokenStorageInterface
|
||||||
*/
|
*/
|
||||||
protected $storage;
|
private $storage;
|
||||||
|
|
||||||
public function __construct(TokenStorageInterface $tokenStorage)
|
public function __construct(TokenStorageInterface $tokenStorage)
|
||||||
{
|
{
|
||||||
@@ -30,11 +30,11 @@ class TimezoneSubscriber implements EventSubscriberInterface
|
|||||||
public static function getSubscribedEvents(): array
|
public static function getSubscribedEvents(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
KernelEvents::REQUEST => ['setTimezone', 100],
|
KernelEvents::REQUEST => ['prepareEnvironment', 100],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTimezone(RequestEvent $event)
|
public function prepareEnvironment(RequestEvent $event)
|
||||||
{
|
{
|
||||||
if (null === $this->storage->getToken()) {
|
if (null === $this->storage->getToken()) {
|
||||||
return;
|
return;
|
||||||
@@ -44,6 +44,7 @@ class TimezoneSubscriber implements EventSubscriberInterface
|
|||||||
|
|
||||||
if ($user instanceof User) {
|
if ($user instanceof User) {
|
||||||
date_default_timezone_set($user->getTimezone());
|
date_default_timezone_set($user->getTimezone());
|
||||||
|
\Locale::setDefault($user->getLocale());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,7 @@
|
|||||||
namespace App\Invoice;
|
namespace App\Invoice;
|
||||||
|
|
||||||
use App\Twig\DateExtensions;
|
use App\Twig\DateExtensions;
|
||||||
use App\Twig\Extensions;
|
use App\Twig\LocaleExtensions;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
||||||
|
|
||||||
final class DefaultInvoiceFormatter implements InvoiceFormatter
|
final class DefaultInvoiceFormatter implements InvoiceFormatter
|
||||||
{
|
{
|
||||||
@@ -19,25 +18,13 @@ final class DefaultInvoiceFormatter implements InvoiceFormatter
|
|||||||
* @var DateExtensions
|
* @var DateExtensions
|
||||||
*/
|
*/
|
||||||
private $dateExtension;
|
private $dateExtension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Extensions
|
* @var LocaleExtensions
|
||||||
*/
|
*/
|
||||||
private $extension;
|
private $extension;
|
||||||
|
|
||||||
/**
|
public function __construct(DateExtensions $dateExtension, LocaleExtensions $extensions)
|
||||||
* @var TranslatorInterface
|
|
||||||
*/
|
|
||||||
private $translator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param TranslatorInterface $translator
|
|
||||||
* @param DateExtensions $dateExtension
|
|
||||||
* @param Extensions $extensions
|
|
||||||
*/
|
|
||||||
public function __construct(TranslatorInterface $translator, DateExtensions $dateExtension, Extensions $extensions)
|
|
||||||
{
|
{
|
||||||
$this->translator = $translator;
|
|
||||||
$this->dateExtension = $dateExtension;
|
$this->dateExtension = $dateExtension;
|
||||||
$this->extension = $extensions;
|
$this->extension = $extensions;
|
||||||
}
|
}
|
||||||
@@ -60,23 +47,24 @@ final class DefaultInvoiceFormatter implements InvoiceFormatter
|
|||||||
return $this->dateExtension->time($date);
|
return $this->dateExtension->time($date);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $amount
|
|
||||||
* @param string $currency
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getFormattedMoney($amount, $currency)
|
|
||||||
{
|
|
||||||
return $this->extension->money($amount, $currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \DateTime $date
|
* @param \DateTime $date
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getFormattedMonthName(\DateTime $date)
|
public function getFormattedMonthName(\DateTime $date)
|
||||||
{
|
{
|
||||||
return $this->translator->trans($this->dateExtension->monthName($date));
|
return $this->dateExtension->monthName($date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float|int $amount
|
||||||
|
* @param string|null $currency
|
||||||
|
* @param bool $withCurrency
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFormattedMoney($amount, ?string $currency, bool $withCurrency = true)
|
||||||
|
{
|
||||||
|
return $this->extension->money($amount, $currency, $withCurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -64,13 +64,13 @@ class InvoiceItemDefaultHydrator implements InvoiceItemHydrator
|
|||||||
'entry.type' => $item->getType(),
|
'entry.type' => $item->getType(),
|
||||||
'entry.category' => $item->getCategory(),
|
'entry.category' => $item->getCategory(),
|
||||||
'entry.rate' => $formatter->getFormattedMoney($appliedRate, $currency),
|
'entry.rate' => $formatter->getFormattedMoney($appliedRate, $currency),
|
||||||
'entry.rate_nc' => $formatter->getFormattedMoney($appliedRate, null),
|
'entry.rate_nc' => $formatter->getFormattedMoney($appliedRate, $currency, false),
|
||||||
'entry.rate_plain' => $appliedRate,
|
'entry.rate_plain' => $appliedRate,
|
||||||
'entry.rate_internal' => $formatter->getFormattedMoney($internalRate, $currency),
|
'entry.rate_internal' => $formatter->getFormattedMoney($internalRate, $currency),
|
||||||
'entry.rate_internal_nc' => $formatter->getFormattedMoney($internalRate, null),
|
'entry.rate_internal_nc' => $formatter->getFormattedMoney($internalRate, $currency, false),
|
||||||
'entry.rate_internal_plain' => $internalRate,
|
'entry.rate_internal_plain' => $internalRate,
|
||||||
'entry.total' => $formatter->getFormattedMoney($rate, $currency),
|
'entry.total' => $formatter->getFormattedMoney($rate, $currency),
|
||||||
'entry.total_nc' => $formatter->getFormattedMoney($rate, null),
|
'entry.total_nc' => $formatter->getFormattedMoney($rate, $currency, false),
|
||||||
'entry.total_plain' => $rate,
|
'entry.total_plain' => $rate,
|
||||||
'entry.currency' => $currency,
|
'entry.currency' => $currency,
|
||||||
'entry.duration' => $item->getDuration(),
|
'entry.duration' => $item->getDuration(),
|
||||||
|
|||||||
@@ -31,15 +31,15 @@ class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
|||||||
'invoice.currency_symbol' => $formatter->getCurrencySymbol($currency),
|
'invoice.currency_symbol' => $formatter->getCurrencySymbol($currency),
|
||||||
'invoice.vat' => $model->getCalculator()->getVat(),
|
'invoice.vat' => $model->getCalculator()->getVat(),
|
||||||
'invoice.tax' => $formatter->getFormattedMoney($tax, $currency),
|
'invoice.tax' => $formatter->getFormattedMoney($tax, $currency),
|
||||||
'invoice.tax_nc' => $formatter->getFormattedMoney($tax, null),
|
'invoice.tax_nc' => $formatter->getFormattedMoney($tax, $currency, false),
|
||||||
'invoice.tax_plain' => $tax,
|
'invoice.tax_plain' => $tax,
|
||||||
'invoice.total_time' => $formatter->getFormattedDuration($model->getCalculator()->getTimeWorked()),
|
'invoice.total_time' => $formatter->getFormattedDuration($model->getCalculator()->getTimeWorked()),
|
||||||
'invoice.duration_decimal' => $formatter->getFormattedDecimalDuration($model->getCalculator()->getTimeWorked()),
|
'invoice.duration_decimal' => $formatter->getFormattedDecimalDuration($model->getCalculator()->getTimeWorked()),
|
||||||
'invoice.total' => $formatter->getFormattedMoney($total, $currency),
|
'invoice.total' => $formatter->getFormattedMoney($total, $currency),
|
||||||
'invoice.total_nc' => $formatter->getFormattedMoney($total, null),
|
'invoice.total_nc' => $formatter->getFormattedMoney($total, $currency, false),
|
||||||
'invoice.total_plain' => $total,
|
'invoice.total_plain' => $total,
|
||||||
'invoice.subtotal' => $formatter->getFormattedMoney($subtotal, $currency),
|
'invoice.subtotal' => $formatter->getFormattedMoney($subtotal, $currency),
|
||||||
'invoice.subtotal_nc' => $formatter->getFormattedMoney($subtotal, null),
|
'invoice.subtotal_nc' => $formatter->getFormattedMoney($subtotal, $currency, false),
|
||||||
'invoice.subtotal_plain' => $subtotal,
|
'invoice.subtotal_plain' => $subtotal,
|
||||||
|
|
||||||
'template.name' => $model->getTemplate()->getName(),
|
'template.name' => $model->getTemplate()->getName(),
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class InvoiceModelProjectHydrator implements InvoiceModelHydrator
|
|||||||
$prefix . 'end_date' => null !== $project->getEnd() ? $formatter->getFormattedDateTime($project->getEnd()) : '',
|
$prefix . 'end_date' => null !== $project->getEnd() ? $formatter->getFormattedDateTime($project->getEnd()) : '',
|
||||||
$prefix . 'order_date' => null !== $project->getOrderDate() ? $formatter->getFormattedDateTime($project->getOrderDate()) : '',
|
$prefix . 'order_date' => null !== $project->getOrderDate() ? $formatter->getFormattedDateTime($project->getOrderDate()) : '',
|
||||||
$prefix . 'budget_money' => $formatter->getFormattedMoney($project->getBudget(), $currency),
|
$prefix . 'budget_money' => $formatter->getFormattedMoney($project->getBudget(), $currency),
|
||||||
$prefix . 'budget_money_nc' => $formatter->getFormattedMoney($project->getBudget(), null),
|
$prefix . 'budget_money_nc' => $formatter->getFormattedMoney($project->getBudget(), $currency, false),
|
||||||
$prefix . 'budget_money_plain' => $project->getBudget(),
|
$prefix . 'budget_money_plain' => $project->getBudget(),
|
||||||
$prefix . 'budget_time' => $project->getTimeBudget(),
|
$prefix . 'budget_time' => $project->getTimeBudget(),
|
||||||
$prefix . 'budget_time_decimal' => $formatter->getFormattedDecimalDuration($project->getTimeBudget()),
|
$prefix . 'budget_time_decimal' => $formatter->getFormattedDecimalDuration($project->getTimeBudget()),
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ interface InvoiceFormatter
|
|||||||
/**
|
/**
|
||||||
* @param int|float $amount
|
* @param int|float $amount
|
||||||
* @param string|null $currency
|
* @param string|null $currency
|
||||||
* @return mixed
|
* @param bool $withCurrency
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getFormattedMoney($amount, $currency);
|
public function getFormattedMoney($amount, ?string $currency, bool $withCurrency = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \DateTime $date
|
* @param \DateTime $date
|
||||||
|
|||||||
74
src/Invoice/Renderer/AbstractTwigRenderer.php
Normal file
74
src/Invoice/Renderer/AbstractTwigRenderer.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?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\Invoice\Renderer;
|
||||||
|
|
||||||
|
use App\Entity\InvoiceDocument;
|
||||||
|
use App\Invoice\InvoiceModel;
|
||||||
|
use App\Invoice\RendererInterface;
|
||||||
|
use App\Twig\DateExtensions;
|
||||||
|
use App\Twig\LocaleExtensions;
|
||||||
|
use Symfony\Bridge\Twig\Extension\TranslationExtension;
|
||||||
|
use Symfony\Contracts\Translation\LocaleAwareInterface;
|
||||||
|
use Twig\Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
abstract class AbstractTwigRenderer implements RendererInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Environment
|
||||||
|
*/
|
||||||
|
private $twig;
|
||||||
|
|
||||||
|
public function __construct(Environment $twig)
|
||||||
|
{
|
||||||
|
$this->twig = $twig;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderTwigTemplate(InvoiceDocument $document, InvoiceModel $model): string
|
||||||
|
{
|
||||||
|
$previousLocale = $this->changeTwigLocale($this->twig, $model->getTemplate()->getLanguage());
|
||||||
|
|
||||||
|
$content = $this->twig->render('@invoice/' . basename($document->getFilename()), [
|
||||||
|
'model' => $model
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->changeTwigLocale($this->twig, $previousLocale);
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function changeTwigLocale(Environment $twig, ?string $locale = null): ?string
|
||||||
|
{
|
||||||
|
// for invoices that don't have a language configured (using request locale)
|
||||||
|
if (null === $locale) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var TranslationExtension $extension */
|
||||||
|
$extension = $twig->getExtension(TranslationExtension::class);
|
||||||
|
/** @var LocaleAwareInterface $translator */
|
||||||
|
$translator = $extension->getTranslator();
|
||||||
|
$previousLocale = $translator->getLocale();
|
||||||
|
|
||||||
|
$translator->setLocale($locale);
|
||||||
|
|
||||||
|
/** @var LocaleExtensions $extension */
|
||||||
|
$extension = $twig->getExtension(LocaleExtensions::class);
|
||||||
|
$extension->setLocale($locale);
|
||||||
|
|
||||||
|
/** @var DateExtensions $extension */
|
||||||
|
$extension = $twig->getExtension(DateExtensions::class);
|
||||||
|
$extension->setLocale($locale);
|
||||||
|
|
||||||
|
return $previousLocale;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,23 +12,11 @@ namespace App\Invoice\Renderer;
|
|||||||
use App\Entity\InvoiceDocument;
|
use App\Entity\InvoiceDocument;
|
||||||
use App\Invoice\InvoiceFilename;
|
use App\Invoice\InvoiceFilename;
|
||||||
use App\Invoice\InvoiceModel;
|
use App\Invoice\InvoiceModel;
|
||||||
use App\Invoice\RendererInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Twig\Environment;
|
|
||||||
|
|
||||||
final class JsonRenderer implements RendererInterface
|
final class JsonRenderer extends AbstractTwigRenderer
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Environment
|
|
||||||
*/
|
|
||||||
private $twig;
|
|
||||||
|
|
||||||
public function __construct(Environment $twig)
|
|
||||||
{
|
|
||||||
$this->twig = $twig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(InvoiceDocument $document): bool
|
public function supports(InvoiceDocument $document): bool
|
||||||
{
|
{
|
||||||
return stripos($document->getFilename(), '.json.twig') !== false;
|
return stripos($document->getFilename(), '.json.twig') !== false;
|
||||||
@@ -36,9 +24,7 @@ final class JsonRenderer implements RendererInterface
|
|||||||
|
|
||||||
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
||||||
{
|
{
|
||||||
$content = $this->twig->render('@invoice/' . basename($document->getFilename()), [
|
$content = $this->renderTwigTemplate($document, $model);
|
||||||
'model' => $model
|
|
||||||
]);
|
|
||||||
$filename = (string) new InvoiceFilename($model);
|
$filename = (string) new InvoiceFilename($model);
|
||||||
|
|
||||||
$response = new Response($content);
|
$response = new Response($content);
|
||||||
|
|||||||
@@ -12,18 +12,13 @@ namespace App\Invoice\Renderer;
|
|||||||
use App\Entity\InvoiceDocument;
|
use App\Entity\InvoiceDocument;
|
||||||
use App\Invoice\InvoiceFilename;
|
use App\Invoice\InvoiceFilename;
|
||||||
use App\Invoice\InvoiceModel;
|
use App\Invoice\InvoiceModel;
|
||||||
use App\Invoice\RendererInterface;
|
|
||||||
use App\Utils\HtmlToPdfConverter;
|
use App\Utils\HtmlToPdfConverter;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Twig\Environment;
|
use Twig\Environment;
|
||||||
|
|
||||||
final class PdfRenderer implements RendererInterface
|
final class PdfRenderer extends AbstractTwigRenderer
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Environment
|
|
||||||
*/
|
|
||||||
private $twig;
|
|
||||||
/**
|
/**
|
||||||
* @var HtmlToPdfConverter
|
* @var HtmlToPdfConverter
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +26,7 @@ final class PdfRenderer implements RendererInterface
|
|||||||
|
|
||||||
public function __construct(Environment $twig, HtmlToPdfConverter $converter)
|
public function __construct(Environment $twig, HtmlToPdfConverter $converter)
|
||||||
{
|
{
|
||||||
$this->twig = $twig;
|
parent::__construct($twig);
|
||||||
$this->converter = $converter;
|
$this->converter = $converter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,9 +37,7 @@ final class PdfRenderer implements RendererInterface
|
|||||||
|
|
||||||
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
||||||
{
|
{
|
||||||
$content = $this->twig->render('@invoice/' . basename($document->getFilename()), [
|
$content = $this->renderTwigTemplate($document, $model);
|
||||||
'model' => $model
|
|
||||||
]);
|
|
||||||
|
|
||||||
$content = $this->converter->convertToPdf($content, [
|
$content = $this->converter->convertToPdf($content, [
|
||||||
'setAutoTopMargin' => 'pad',
|
'setAutoTopMargin' => 'pad',
|
||||||
|
|||||||
@@ -12,23 +12,11 @@ namespace App\Invoice\Renderer;
|
|||||||
use App\Entity\InvoiceDocument;
|
use App\Entity\InvoiceDocument;
|
||||||
use App\Invoice\InvoiceFilename;
|
use App\Invoice\InvoiceFilename;
|
||||||
use App\Invoice\InvoiceModel;
|
use App\Invoice\InvoiceModel;
|
||||||
use App\Invoice\RendererInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Twig\Environment;
|
|
||||||
|
|
||||||
final class TextRenderer implements RendererInterface
|
final class TextRenderer extends AbstractTwigRenderer
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Environment
|
|
||||||
*/
|
|
||||||
private $twig;
|
|
||||||
|
|
||||||
public function __construct(Environment $twig)
|
|
||||||
{
|
|
||||||
$this->twig = $twig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(InvoiceDocument $document): bool
|
public function supports(InvoiceDocument $document): bool
|
||||||
{
|
{
|
||||||
return stripos($document->getFilename(), '.txt.twig') !== false;
|
return stripos($document->getFilename(), '.txt.twig') !== false;
|
||||||
@@ -36,9 +24,7 @@ final class TextRenderer implements RendererInterface
|
|||||||
|
|
||||||
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
||||||
{
|
{
|
||||||
$content = $this->twig->render('@invoice/' . basename($document->getFilename()), [
|
$content = $this->renderTwigTemplate($document, $model);
|
||||||
'model' => $model
|
|
||||||
]);
|
|
||||||
$filename = (string) new InvoiceFilename($model);
|
$filename = (string) new InvoiceFilename($model);
|
||||||
|
|
||||||
$response = new Response($content);
|
$response = new Response($content);
|
||||||
|
|||||||
@@ -11,22 +11,10 @@ namespace App\Invoice\Renderer;
|
|||||||
|
|
||||||
use App\Entity\InvoiceDocument;
|
use App\Entity\InvoiceDocument;
|
||||||
use App\Invoice\InvoiceModel;
|
use App\Invoice\InvoiceModel;
|
||||||
use App\Invoice\RendererInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Twig\Environment;
|
|
||||||
|
|
||||||
final class TwigRenderer implements RendererInterface
|
final class TwigRenderer extends AbstractTwigRenderer
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Environment
|
|
||||||
*/
|
|
||||||
private $twig;
|
|
||||||
|
|
||||||
public function __construct(Environment $twig)
|
|
||||||
{
|
|
||||||
$this->twig = $twig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(InvoiceDocument $document): bool
|
public function supports(InvoiceDocument $document): bool
|
||||||
{
|
{
|
||||||
return stripos($document->getFilename(), '.html.twig') !== false;
|
return stripos($document->getFilename(), '.html.twig') !== false;
|
||||||
@@ -34,9 +22,7 @@ final class TwigRenderer implements RendererInterface
|
|||||||
|
|
||||||
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
||||||
{
|
{
|
||||||
$content = $this->twig->render('@invoice/' . basename($document->getFilename()), [
|
$content = $this->renderTwigTemplate($document, $model);
|
||||||
'model' => $model
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = new Response();
|
$response = new Response();
|
||||||
$response->setContent($content);
|
$response->setContent($content);
|
||||||
|
|||||||
@@ -12,23 +12,11 @@ namespace App\Invoice\Renderer;
|
|||||||
use App\Entity\InvoiceDocument;
|
use App\Entity\InvoiceDocument;
|
||||||
use App\Invoice\InvoiceFilename;
|
use App\Invoice\InvoiceFilename;
|
||||||
use App\Invoice\InvoiceModel;
|
use App\Invoice\InvoiceModel;
|
||||||
use App\Invoice\RendererInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Twig\Environment;
|
|
||||||
|
|
||||||
final class XmlRenderer implements RendererInterface
|
final class XmlRenderer extends AbstractTwigRenderer
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Environment
|
|
||||||
*/
|
|
||||||
private $twig;
|
|
||||||
|
|
||||||
public function __construct(Environment $twig)
|
|
||||||
{
|
|
||||||
$this->twig = $twig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(InvoiceDocument $document): bool
|
public function supports(InvoiceDocument $document): bool
|
||||||
{
|
{
|
||||||
return stripos($document->getFilename(), '.xml.twig') !== false;
|
return stripos($document->getFilename(), '.xml.twig') !== false;
|
||||||
@@ -36,9 +24,7 @@ final class XmlRenderer implements RendererInterface
|
|||||||
|
|
||||||
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
public function render(InvoiceDocument $document, InvoiceModel $model): Response
|
||||||
{
|
{
|
||||||
$content = $this->twig->render('@invoice/' . basename($document->getFilename()), [
|
$content = $this->renderTwigTemplate($document, $model);
|
||||||
'model' => $model
|
|
||||||
]);
|
|
||||||
$filename = (string) new InvoiceFilename($model);
|
$filename = (string) new InvoiceFilename($model);
|
||||||
|
|
||||||
$response = new Response($content);
|
$response = new Response($content);
|
||||||
|
|||||||
@@ -9,8 +9,11 @@
|
|||||||
|
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
use App\Utils\LocaleSettings;
|
use App\Configuration\LanguageFormattings;
|
||||||
|
use App\Constants;
|
||||||
|
use App\Utils\LocaleFormats;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFilter;
|
use Twig\TwigFilter;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
@@ -21,9 +24,9 @@ use Twig\TwigFunction;
|
|||||||
class DateExtensions extends AbstractExtension
|
class DateExtensions extends AbstractExtension
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var LocaleSettings|null
|
* @var LocaleFormats|null
|
||||||
*/
|
*/
|
||||||
protected $localeSettings = null;
|
protected $localeFormats = null;
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
@@ -44,13 +47,26 @@ class DateExtensions extends AbstractExtension
|
|||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected $isTwentyFourHour = null;
|
protected $isTwentyFourHour = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param LocaleSettings $localeSettings
|
* @var string
|
||||||
*/
|
*/
|
||||||
public function __construct(LocaleSettings $localeSettings)
|
private $locale;
|
||||||
|
/**
|
||||||
|
* @var LanguageFormattings
|
||||||
|
*/
|
||||||
|
private $formats;
|
||||||
|
|
||||||
|
public function __construct(RequestStack $requestStack, LanguageFormattings $formats)
|
||||||
{
|
{
|
||||||
$this->localeSettings = $localeSettings;
|
$locale = Constants::DEFAULT_LOCALE;
|
||||||
|
|
||||||
|
// request is null in a console command
|
||||||
|
if (null !== $requestStack->getMasterRequest()) {
|
||||||
|
$locale = $requestStack->getMasterRequest()->getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->formats = $formats;
|
||||||
|
$this->setLocale($locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,6 +76,7 @@ class DateExtensions extends AbstractExtension
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new TwigFilter('month_name', [$this, 'monthName']),
|
new TwigFilter('month_name', [$this, 'monthName']),
|
||||||
|
new TwigFilter('day_name', [$this, 'dayName']),
|
||||||
new TwigFilter('date_short', [$this, 'dateShort']),
|
new TwigFilter('date_short', [$this, 'dateShort']),
|
||||||
new TwigFilter('date_time', [$this, 'dateTime']),
|
new TwigFilter('date_time', [$this, 'dateTime']),
|
||||||
new TwigFilter('date_full', [$this, 'dateTimeFull']),
|
new TwigFilter('date_full', [$this, 'dateTimeFull']),
|
||||||
@@ -79,15 +96,25 @@ class DateExtensions extends AbstractExtension
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to switch the locale used for all twig filter and functions.
|
||||||
|
*
|
||||||
|
* @param string $locale
|
||||||
|
*/
|
||||||
|
public function setLocale(string $locale)
|
||||||
|
{
|
||||||
|
$this->locale = $locale;
|
||||||
|
$this->localeFormats = new LocaleFormats($this->formats, $locale);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DateTime|string $date
|
* @param DateTime|string $date
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function dateShort($date)
|
public function dateShort($date)
|
||||||
{
|
{
|
||||||
if (null === $this->dateFormat) {
|
if (null === $this->dateFormat) {
|
||||||
$this->dateFormat = $this->localeSettings->getDateFormat();
|
$this->dateFormat = $this->localeFormats->getDateFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$date instanceof DateTime) {
|
if (!$date instanceof DateTime) {
|
||||||
@@ -98,18 +125,17 @@ class DateExtensions extends AbstractExtension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return date_format($date, $this->dateFormat);
|
return $date->format($this->dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DateTime|string $date
|
* @param DateTime|string $date
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function dateTime($date)
|
public function dateTime($date)
|
||||||
{
|
{
|
||||||
if (null === $this->dateTimeFormat) {
|
if (null === $this->dateTimeFormat) {
|
||||||
$this->dateTimeFormat = $this->localeSettings->getDateTimeFormat();
|
$this->dateTimeFormat = $this->localeFormats->getDateTimeFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$date instanceof DateTime) {
|
if (!$date instanceof DateTime) {
|
||||||
@@ -125,14 +151,12 @@ class DateExtensions extends AbstractExtension
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DateTime|string $date
|
* @param DateTime|string $date
|
||||||
* @param bool $userTimezone
|
|
||||||
* @return bool|false|string
|
* @return bool|false|string
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function dateTimeFull($date, bool $userTimezone = true)
|
public function dateTimeFull($date)
|
||||||
{
|
{
|
||||||
if (null === $this->dateTimeTypeFormat) {
|
if (null === $this->dateTimeTypeFormat) {
|
||||||
$this->dateTimeTypeFormat = $this->localeSettings->getDateTimeTypeFormat();
|
$this->dateTimeTypeFormat = $this->localeFormats->getDateTimeTypeFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$date instanceof DateTime) {
|
if (!$date instanceof DateTime) {
|
||||||
@@ -143,17 +167,11 @@ class DateExtensions extends AbstractExtension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$timezone = date_default_timezone_get();
|
|
||||||
|
|
||||||
if (!$userTimezone) {
|
|
||||||
$timezone = $date->getTimezone()->getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
$formatter = new \IntlDateFormatter(
|
$formatter = new \IntlDateFormatter(
|
||||||
$this->localeSettings->getLocale(),
|
$this->locale,
|
||||||
\IntlDateFormatter::MEDIUM,
|
\IntlDateFormatter::MEDIUM,
|
||||||
\IntlDateFormatter::MEDIUM,
|
\IntlDateFormatter::MEDIUM,
|
||||||
$timezone,
|
date_default_timezone_get(),
|
||||||
\IntlDateFormatter::GREGORIAN,
|
\IntlDateFormatter::GREGORIAN,
|
||||||
$this->dateTimeTypeFormat
|
$this->dateTimeTypeFormat
|
||||||
);
|
);
|
||||||
@@ -177,34 +195,52 @@ class DateExtensions extends AbstractExtension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return date_format($date, $format);
|
return $date->format($format);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DateTime|string $date
|
* @param DateTime|string $date
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function time($date)
|
public function time($date)
|
||||||
{
|
{
|
||||||
if (null === $this->timeFormat) {
|
if (null === $this->timeFormat) {
|
||||||
$this->timeFormat = $this->localeSettings->getTimeFormat();
|
$this->timeFormat = $this->localeFormats->getTimeFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$date instanceof DateTime) {
|
if (!$date instanceof DateTime) {
|
||||||
$date = new DateTime($date);
|
$date = new DateTime($date);
|
||||||
}
|
}
|
||||||
|
|
||||||
return date_format($date, $this->timeFormat);
|
return $date->format($this->timeFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function monthName(\DateTime $dateTime): string
|
||||||
* @param \DateTime $date
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function monthName(\DateTime $date)
|
|
||||||
{
|
{
|
||||||
return 'month.' . $date->format('n');
|
$formatter = new \IntlDateFormatter(
|
||||||
|
$this->locale,
|
||||||
|
\IntlDateFormatter::FULL,
|
||||||
|
\IntlDateFormatter::FULL,
|
||||||
|
$dateTime->getTimezone()->getName(),
|
||||||
|
\IntlDateFormatter::GREGORIAN,
|
||||||
|
'LLLL'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $formatter->format($dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dayName(\DateTime $dateTime): string
|
||||||
|
{
|
||||||
|
$formatter = new \IntlDateFormatter(
|
||||||
|
$this->locale,
|
||||||
|
\IntlDateFormatter::FULL,
|
||||||
|
\IntlDateFormatter::FULL,
|
||||||
|
$dateTime->getTimezone()->getName(),
|
||||||
|
\IntlDateFormatter::GREGORIAN,
|
||||||
|
'EEEE'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $formatter->format($dateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,7 +251,7 @@ class DateExtensions extends AbstractExtension
|
|||||||
public function hour24($twentyFour, $twelveHour)
|
public function hour24($twentyFour, $twelveHour)
|
||||||
{
|
{
|
||||||
if (null === $this->isTwentyFourHour) {
|
if (null === $this->isTwentyFourHour) {
|
||||||
$this->isTwentyFourHour = $this->localeSettings->isTwentyFourHours();
|
$this->isTwentyFourHour = $this->localeFormats->isTwentyFourHours();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true === $this->isTwentyFourHour) {
|
if (true === $this->isTwentyFourHour) {
|
||||||
@@ -230,6 +266,6 @@ class DateExtensions extends AbstractExtension
|
|||||||
*/
|
*/
|
||||||
public function getDurationFormat()
|
public function getDurationFormat()
|
||||||
{
|
{
|
||||||
return $this->localeSettings->getDurationFormat();
|
return $this->localeFormats->getDurationFormat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,6 @@
|
|||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
use App\Constants;
|
use App\Constants;
|
||||||
use App\Entity\Timesheet;
|
|
||||||
use App\Utils\Duration;
|
|
||||||
use App\Utils\LocaleSettings;
|
|
||||||
use NumberFormatter;
|
|
||||||
use Symfony\Component\Intl\Countries;
|
|
||||||
use Symfony\Component\Intl\Currencies;
|
|
||||||
use Symfony\Component\Intl\Languages;
|
|
||||||
use Symfony\Component\Intl\Locales;
|
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFilter;
|
use Twig\TwigFilter;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
@@ -27,49 +19,12 @@ use Twig\TwigFunction;
|
|||||||
*/
|
*/
|
||||||
class Extensions extends AbstractExtension
|
class Extensions extends AbstractExtension
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var LocaleSettings
|
|
||||||
*/
|
|
||||||
protected $localeSettings;
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $locale;
|
|
||||||
/**
|
|
||||||
* @var Duration
|
|
||||||
*/
|
|
||||||
protected $durationFormatter;
|
|
||||||
/**
|
|
||||||
* @var NumberFormatter
|
|
||||||
*/
|
|
||||||
protected $numberFormatter;
|
|
||||||
/**
|
|
||||||
* @var NumberFormatter
|
|
||||||
*/
|
|
||||||
protected $moneyFormatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param LocaleSettings $localeSettings
|
|
||||||
*/
|
|
||||||
public function __construct(LocaleSettings $localeSettings)
|
|
||||||
{
|
|
||||||
$this->localeSettings = $localeSettings;
|
|
||||||
$this->durationFormatter = new Duration();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getFilters()
|
public function getFilters()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new TwigFilter('duration', [$this, 'duration']),
|
|
||||||
new TwigFilter('duration_decimal', [$this, 'durationDecimal']),
|
|
||||||
new TwigFilter('money', [$this, 'money']),
|
|
||||||
new TwigFilter('currency', [$this, 'currency']),
|
|
||||||
new TwigFilter('country', [$this, 'country']),
|
|
||||||
new TwigFilter('language', [$this, 'language']),
|
|
||||||
new TwigFilter('amount', [$this, 'amount']),
|
|
||||||
new TwigFilter('docu_link', [$this, 'documentationLink']),
|
new TwigFilter('docu_link', [$this, 'documentationLink']),
|
||||||
new TwigFilter('multiline_indent', [$this, 'multilineIndent']),
|
new TwigFilter('multiline_indent', [$this, 'multilineIndent']),
|
||||||
];
|
];
|
||||||
@@ -81,7 +36,6 @@ class Extensions extends AbstractExtension
|
|||||||
public function getFunctions()
|
public function getFunctions()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new TwigFunction('locales', [$this, 'getLocales']),
|
|
||||||
new TwigFunction('class_name', [$this, 'getClassName']),
|
new TwigFunction('class_name', [$this, 'getClassName']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -120,115 +74,6 @@ class Extensions extends AbstractExtension
|
|||||||
return implode(PHP_EOL, $parts);
|
return implode(PHP_EOL, $parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms seconds into a duration string.
|
|
||||||
*
|
|
||||||
* @param int|Timesheet $duration
|
|
||||||
* @param bool $decimal
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function duration($duration, $decimal = false)
|
|
||||||
{
|
|
||||||
if ($decimal) {
|
|
||||||
return $this->durationDecimal($duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
$duration = $this->getSecondsForDuration($duration);
|
|
||||||
$format = $this->localeSettings->getDurationFormat();
|
|
||||||
|
|
||||||
return $this->formatDuration($duration, $format);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms seconds into a decimal formatted duration string.
|
|
||||||
*
|
|
||||||
* @param int|Timesheet $duration
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function durationDecimal($duration)
|
|
||||||
{
|
|
||||||
$duration = $this->getSecondsForDuration($duration);
|
|
||||||
|
|
||||||
return $this->getNumberFormatter()->format(number_format($duration / 3600, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string|float $amount
|
|
||||||
* @return bool|false|string
|
|
||||||
*/
|
|
||||||
public function amount($amount)
|
|
||||||
{
|
|
||||||
return $this->getNumberFormatter()->format($amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getSecondsForDuration($duration): int
|
|
||||||
{
|
|
||||||
if (null === $duration) {
|
|
||||||
$duration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($duration instanceof Timesheet) {
|
|
||||||
if (null === $duration->getEnd()) {
|
|
||||||
$duration = time() - $duration->getBegin()->getTimestamp();
|
|
||||||
} else {
|
|
||||||
$duration = $duration->getDuration();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) $duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function formatDuration(int $seconds, string $format): string
|
|
||||||
{
|
|
||||||
if ($seconds < 0) {
|
|
||||||
return '?';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->durationFormatter->format($seconds, $format);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $currency
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function currency($currency)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Currencies::getSymbol(strtoupper($currency));
|
|
||||||
} catch (\Exception $ex) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return $currency;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $language
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function language($language)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Languages::getName(strtolower($language), $this->locale);
|
|
||||||
} catch (\Exception $ex) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return $language;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $country
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function country($country)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Countries::getName(strtoupper($country));
|
|
||||||
} catch (\Exception $ex) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return $country;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $url
|
* @param string $url
|
||||||
* @return string
|
* @return string
|
||||||
@@ -237,62 +82,4 @@ class Extensions extends AbstractExtension
|
|||||||
{
|
{
|
||||||
return Constants::HOMEPAGE . '/documentation/' . $url;
|
return Constants::HOMEPAGE . '/documentation/' . $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function initLocale()
|
|
||||||
{
|
|
||||||
$locale = $this->localeSettings->getLocale();
|
|
||||||
|
|
||||||
if ($this->locale === $locale) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->locale = $locale;
|
|
||||||
$this->numberFormatter = new NumberFormatter($locale, NumberFormatter::DECIMAL);
|
|
||||||
$this->moneyFormatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getNumberFormatter(): NumberFormatter
|
|
||||||
{
|
|
||||||
$this->initLocale();
|
|
||||||
|
|
||||||
return $this->numberFormatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getMoneyFormatter(): NumberFormatter
|
|
||||||
{
|
|
||||||
$this->initLocale();
|
|
||||||
|
|
||||||
return $this->moneyFormatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param float $amount
|
|
||||||
* @param string $currency
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function money($amount, $currency = null)
|
|
||||||
{
|
|
||||||
if (null !== $currency) {
|
|
||||||
return $this->getMoneyFormatter()->formatCurrency($amount, $currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->getNumberFormatter()->format($amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes the list of codes of the locales (languages) enabled in the
|
|
||||||
* application and returns an array with the name of each locale written
|
|
||||||
* in its own language (e.g. English, Français, Español, etc.)
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getLocales()
|
|
||||||
{
|
|
||||||
$locales = [];
|
|
||||||
foreach ($this->localeSettings->getAvailableLanguages() as $locale) {
|
|
||||||
$locales[] = ['code' => $locale, 'name' => Locales::getName($locale, $locale)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $locales;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
219
src/Twig/LocaleExtensions.php
Normal file
219
src/Twig/LocaleExtensions.php
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
<?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\Twig;
|
||||||
|
|
||||||
|
use App\Configuration\LanguageFormattings;
|
||||||
|
use App\Constants;
|
||||||
|
use App\Entity\Timesheet;
|
||||||
|
use App\Utils\Duration;
|
||||||
|
use App\Utils\LocaleFormats;
|
||||||
|
use App\Utils\LocaleHelper;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\Intl\Languages;
|
||||||
|
use Symfony\Component\Intl\Locales;
|
||||||
|
use Twig\Extension\AbstractExtension;
|
||||||
|
use Twig\TwigFilter;
|
||||||
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locale specific Twig extensions
|
||||||
|
*/
|
||||||
|
final class LocaleExtensions extends AbstractExtension
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var LocaleFormats
|
||||||
|
*/
|
||||||
|
private $localeFormats;
|
||||||
|
/**
|
||||||
|
* @var Duration
|
||||||
|
*/
|
||||||
|
private $durationFormatter;
|
||||||
|
/**
|
||||||
|
* @var LocaleHelper
|
||||||
|
*/
|
||||||
|
private $helper;
|
||||||
|
/**
|
||||||
|
* @var LanguageFormattings
|
||||||
|
*/
|
||||||
|
private $formats;
|
||||||
|
|
||||||
|
public function __construct(RequestStack $requestStack, LanguageFormattings $formats)
|
||||||
|
{
|
||||||
|
$locale = Constants::DEFAULT_LOCALE;
|
||||||
|
|
||||||
|
// request is null in a console command
|
||||||
|
if (null !== $requestStack->getMasterRequest()) {
|
||||||
|
$locale = $requestStack->getMasterRequest()->getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->durationFormatter = new Duration();
|
||||||
|
$this->formats = $formats;
|
||||||
|
$this->setLocale($locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFilters()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFilter('duration', [$this, 'duration']),
|
||||||
|
new TwigFilter('duration_decimal', [$this, 'durationDecimal']),
|
||||||
|
new TwigFilter('money', [$this, 'money']),
|
||||||
|
new TwigFilter('currency', [$this, 'currency']),
|
||||||
|
new TwigFilter('country', [$this, 'country']),
|
||||||
|
new TwigFilter('language', [$this, 'language']),
|
||||||
|
new TwigFilter('amount', [$this, 'amount']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFunctions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFunction('locales', [$this, 'getLocales']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows to switch the locale used for all twig filter and functions.
|
||||||
|
*
|
||||||
|
* @param string $locale
|
||||||
|
*/
|
||||||
|
public function setLocale(string $locale)
|
||||||
|
{
|
||||||
|
$this->helper = new LocaleHelper($locale);
|
||||||
|
$this->localeFormats = new LocaleFormats($this->formats, $locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms seconds into a duration string.
|
||||||
|
*
|
||||||
|
* @param int|Timesheet $duration
|
||||||
|
* @param bool $decimal
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function duration($duration, $decimal = false)
|
||||||
|
{
|
||||||
|
if ($decimal) {
|
||||||
|
return $this->durationDecimal($duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
$seconds = $this->getSecondsForDuration($duration);
|
||||||
|
$format = $this->localeFormats->getDurationFormat();
|
||||||
|
|
||||||
|
return $this->formatDuration($seconds, $format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms seconds into a decimal formatted duration string.
|
||||||
|
*
|
||||||
|
* @param int|Timesheet $duration
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function durationDecimal($duration)
|
||||||
|
{
|
||||||
|
$seconds = $this->getSecondsForDuration($duration);
|
||||||
|
|
||||||
|
return $this->helper->durationDecimal($seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getSecondsForDuration($duration): int
|
||||||
|
{
|
||||||
|
if (null === $duration) {
|
||||||
|
$duration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($duration instanceof Timesheet) {
|
||||||
|
if (null === $duration->getEnd()) {
|
||||||
|
$duration = time() - $duration->getBegin()->getTimestamp();
|
||||||
|
} else {
|
||||||
|
$duration = $duration->getDuration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatDuration(int $seconds, string $format): string
|
||||||
|
{
|
||||||
|
if ($seconds < 0) {
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->durationFormatter->format($seconds, $format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|float $amount
|
||||||
|
* @return bool|false|string
|
||||||
|
*/
|
||||||
|
public function amount($amount)
|
||||||
|
{
|
||||||
|
return $this->helper->amount($amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $currency
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function currency($currency)
|
||||||
|
{
|
||||||
|
return $this->helper->currency($currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $language
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function language($language)
|
||||||
|
{
|
||||||
|
return $this->helper->language($language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $country
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function country($country)
|
||||||
|
{
|
||||||
|
return $this->helper->country($country);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float $amount
|
||||||
|
* @param string|null $currency
|
||||||
|
* @param bool $withCurrency
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function money($amount, ?string $currency = null, bool $withCurrency = true)
|
||||||
|
{
|
||||||
|
return $this->helper->money($amount, $currency, $withCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes the list of codes of the locales (languages) enabled in the
|
||||||
|
* application and returns an array with the name of each locale written
|
||||||
|
* in its own language (e.g. English, Français, Español, etc.)
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getLocales()
|
||||||
|
{
|
||||||
|
$locales = [];
|
||||||
|
foreach ($this->localeFormats->getAvailableLanguages() as $locale) {
|
||||||
|
$locales[] = ['code' => $locale, 'name' => Locales::getName($locale, $locale)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $locales;
|
||||||
|
}
|
||||||
|
}
|
||||||
144
src/Utils/LocaleFormats.php
Normal file
144
src/Utils/LocaleFormats.php
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?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\Utils;
|
||||||
|
|
||||||
|
use App\Configuration\LanguageFormattings;
|
||||||
|
use App\Constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this class, when you want information about formats for a locale.
|
||||||
|
*/
|
||||||
|
class LocaleFormats
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var LanguageFormattings
|
||||||
|
*/
|
||||||
|
private $formats;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $locale = Constants::DEFAULT_LOCALE;
|
||||||
|
|
||||||
|
public function __construct(LanguageFormattings $formats, string $locale)
|
||||||
|
{
|
||||||
|
$this->formats = $formats;
|
||||||
|
$this->locale = $locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with all available locale/language codes.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getAvailableLanguages(): array
|
||||||
|
{
|
||||||
|
return $this->formats->getAvailableLanguages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current locale used by the user in this request.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLocale(): string
|
||||||
|
{
|
||||||
|
return $this->locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the format which is used by the form component to handle date values.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDateTypeFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getDateTypeFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the format which is used by the Javascript component to handle date values.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDatePickerFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getDatePickerFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the format which is used by the form component to handle datetime values.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDateTimeTypeFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getDateTimeTypeFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the format which is used by the Javascript component to handle datetime values.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDateTimePickerFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getDateTimePickerFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale specific date format, which should be used in combination with the twig filter "|date".
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDateFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getDateFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale specific time format, which should be used in combination with the twig filter "|time".
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTimeFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getTimeFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale specific datetime format, which should be used in combination with the twig filter "|date".
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDateTimeFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getDateTimeFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the format used in the "|duration" twig filter to display a Timesheet duration.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDurationFormat(): string
|
||||||
|
{
|
||||||
|
return $this->formats->getDurationFormat($this->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this locale uses the 24 hour format.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isTwentyFourHours(): bool
|
||||||
|
{
|
||||||
|
return $this->formats->isTwentyFourHours($this->getLocale());
|
||||||
|
}
|
||||||
|
}
|
||||||
151
src/Utils/LocaleHelper.php
Normal file
151
src/Utils/LocaleHelper.php
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<?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\Utils;
|
||||||
|
|
||||||
|
use NumberFormatter;
|
||||||
|
use Symfony\Component\Intl\Countries;
|
||||||
|
use Symfony\Component\Intl\Currencies;
|
||||||
|
use Symfony\Component\Intl\Languages;
|
||||||
|
|
||||||
|
final class LocaleHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $locale;
|
||||||
|
/**
|
||||||
|
* @var NumberFormatter
|
||||||
|
*/
|
||||||
|
private $numberFormatter;
|
||||||
|
/**
|
||||||
|
* @var NumberFormatter
|
||||||
|
*/
|
||||||
|
private $moneyFormatter;
|
||||||
|
/**
|
||||||
|
* @var NumberFormatter
|
||||||
|
*/
|
||||||
|
private $moneyFormatterNoCurrency;
|
||||||
|
|
||||||
|
public function __construct(string $locale)
|
||||||
|
{
|
||||||
|
$this->locale = $locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms seconds into a decimal formatted duration string.
|
||||||
|
*
|
||||||
|
* @param int $seconds
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function durationDecimal(int $seconds)
|
||||||
|
{
|
||||||
|
return $this->getNumberFormatter()->format(number_format($seconds / 3600, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|float $amount
|
||||||
|
* @return bool|false|string
|
||||||
|
*/
|
||||||
|
public function amount($amount)
|
||||||
|
{
|
||||||
|
return $this->getNumberFormatter()->format($amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $currency
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function currency($currency)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Currencies::getSymbol(strtoupper($currency), $this->locale);
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return $currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $language
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function language(string $language)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Languages::getName(strtolower($language), $this->locale);
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return $language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $country
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function country(string $country)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Countries::getName(strtoupper($country), $this->locale);
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return $country;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|float $amount
|
||||||
|
* @param string|null $currency
|
||||||
|
* @param bool $withCurrency
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function money($amount, ?string $currency = null, bool $withCurrency = true)
|
||||||
|
{
|
||||||
|
if (null === $currency) {
|
||||||
|
$withCurrency = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getMoneyFormatter($withCurrency)->formatCurrency($amount, $currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getNumberFormatter(): NumberFormatter
|
||||||
|
{
|
||||||
|
if (null === $this->numberFormatter) {
|
||||||
|
$this->numberFormatter = new NumberFormatter($this->locale, NumberFormatter::DECIMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->numberFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMoneyFormatter(bool $withCurrency = true): NumberFormatter
|
||||||
|
{
|
||||||
|
if (null === $this->moneyFormatter) {
|
||||||
|
$this->moneyFormatter = new NumberFormatter($this->locale, NumberFormatter::CURRENCY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($withCurrency) {
|
||||||
|
return $this->moneyFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $this->moneyFormatterNoCurrency) {
|
||||||
|
// if anyone knows a better way of achieving this, please let me know!
|
||||||
|
$this->moneyFormatterNoCurrency = new NumberFormatter($this->locale, NumberFormatter::CURRENCY);
|
||||||
|
$pattern = $this->moneyFormatterNoCurrency->getPattern();
|
||||||
|
$pattern = str_replace('¤ ', '¤', $pattern);
|
||||||
|
$pattern = str_replace(' ¤', '¤', $pattern);
|
||||||
|
$this->moneyFormatterNoCurrency->setPattern($pattern);
|
||||||
|
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_SYMBOL, '');
|
||||||
|
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_CODE, '');
|
||||||
|
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::INTL_CURRENCY_SYMBOL, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->moneyFormatterNoCurrency;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,133 +16,15 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||||||
/**
|
/**
|
||||||
* Use this class, when you want information about formats for the "current request locale".
|
* Use this class, when you want information about formats for the "current request locale".
|
||||||
*/
|
*/
|
||||||
final class LocaleSettings
|
final class LocaleSettings extends LocaleFormats
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var LanguageFormattings
|
|
||||||
*/
|
|
||||||
private $formats;
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $locale = Constants::DEFAULT_LOCALE;
|
|
||||||
|
|
||||||
public function __construct(RequestStack $requestStack, LanguageFormattings $formats)
|
public function __construct(RequestStack $requestStack, LanguageFormattings $formats)
|
||||||
{
|
{
|
||||||
|
$locale = Constants::DEFAULT_LOCALE;
|
||||||
// request is null in a console command
|
// request is null in a console command
|
||||||
if (null !== $requestStack->getMasterRequest()) {
|
if (null !== $requestStack->getMasterRequest()) {
|
||||||
$this->locale = $requestStack->getMasterRequest()->getLocale();
|
$locale = $requestStack->getMasterRequest()->getLocale();
|
||||||
}
|
}
|
||||||
$this->formats = $formats;
|
parent::__construct($formats, $locale);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array with all available locale/language codes.
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
public function getAvailableLanguages(): array
|
|
||||||
{
|
|
||||||
return $this->formats->getAvailableLanguages();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current locale used by the user in this request.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLocale(): string
|
|
||||||
{
|
|
||||||
return $this->locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format which is used by the form component to handle date values.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDateTypeFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getDateTypeFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format which is used by the Javascript component to handle date values.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDatePickerFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getDatePickerFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format which is used by the form component to handle datetime values.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDateTimeTypeFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getDateTimeTypeFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format which is used by the Javascript component to handle datetime values.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDateTimePickerFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getDateTimePickerFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the locale specific date format, which should be used in combination with the twig filter "|date".
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDateFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getDateFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the locale specific time format, which should be used in combination with the twig filter "|time".
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getTimeFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getTimeFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the locale specific datetime format, which should be used in combination with the twig filter "|date".
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDateTimeFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getDateTimeFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the format used in the "|duration" twig filter to display a Timesheet duration.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getDurationFormat(): string
|
|
||||||
{
|
|
||||||
return $this->formats->getDurationFormat($this->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this locale uses the 24 hour format.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isTwentyFourHours(): bool
|
|
||||||
{
|
|
||||||
return $this->formats->isTwentyFourHours($this->getLocale());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
{% if customer.timezone is not empty %}
|
{% if customer.timezone is not empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.timezone'|trans }}</th>
|
<th>{{ 'label.timezone'|trans }}</th>
|
||||||
<td><span data-toggle="tooltip" data-placement="top" title="{{ customer.timezone }}">{{ now|date_full(false) }}</span></td>
|
<td><span data-toggle="tooltip" data-placement="top" title="{{ customer.timezone }}">{{ now|date_time }}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if customer.currency is not empty %}
|
{% if customer.currency is not empty %}
|
||||||
|
|||||||
@@ -3,17 +3,18 @@
|
|||||||
{% block box_title %}{{ 'label.budget'|trans }}{% endblock %}
|
{% block box_title %}{{ 'label.budget'|trans }}{% endblock %}
|
||||||
{% block box_attributes %}id="budget_box"{% endblock %}
|
{% block box_attributes %}id="budget_box"{% endblock %}
|
||||||
{% block box_body %}
|
{% block box_body %}
|
||||||
|
{% set currency = customer.currency %}
|
||||||
|
|
||||||
{% set params = {
|
{% set params = {
|
||||||
'%activity%': '<strong>' ~ stats.activityAmount ~ '</strong>',
|
'%activity%': '<strong>' ~ stats.activityAmount ~ '</strong>',
|
||||||
'%project%': '<strong>' ~ stats.projectAmount ~ '</strong>',
|
'%project%': '<strong>' ~ stats.projectAmount ~ '</strong>',
|
||||||
'%customer%': '<strong>' ~ customer.name ~ '</strong>',
|
'%customer%': '<strong>' ~ customer.name ~ '</strong>',
|
||||||
'%records%': '<strong>' ~ stats.recordAmount ~ '</strong>',
|
'%records%': '<strong>' ~ stats.recordAmount ~ '</strong>',
|
||||||
'%duration%': '<strong>' ~ stats.recordDuration|duration ~ '</strong>',
|
'%duration%': '<strong>' ~ stats.recordDuration|duration ~ '</strong>',
|
||||||
'%rate%': '<strong>' ~ stats.recordRate|money ~ '</strong>'
|
'%rate%': '<strong>' ~ stats.recordRate|money(currency) ~ '</strong>',
|
||||||
|
'%internal_rate%': '<strong>' ~ stats.recordInternalRate|money(currency) ~ '</strong>'
|
||||||
} %}
|
} %}
|
||||||
|
|
||||||
{% set currency = customer.currency %}
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{{ 'admin_customer.short_stats'|trans(params)|raw }}
|
{{ 'admin_customer.short_stats'|trans(params)|raw }}
|
||||||
{{ 'label.rate_internal'|trans }}: {{ stats.recordInternalRate|money(currency) }}.
|
{{ 'label.rate_internal'|trans }}: {{ stats.recordInternalRate|money(currency) }}.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
{{ model.template.title }}
|
{{ model.template.title }}
|
||||||
</td>
|
</td>
|
||||||
<td class="date">
|
<td class="date">
|
||||||
{{ 'label.date'|trans({}, 'messages', language) }}: {{ model.invoiceDate|date_short }}
|
{{ 'label.date'|trans }}: {{ model.invoiceDate|date_short }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -32,10 +32,10 @@
|
|||||||
<table class="footer">
|
<table class="footer">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<strong>{{ 'label.contact'|trans({}, 'messages', language) }}</strong>:
|
<strong>{{ 'label.contact'|trans }}</strong>:
|
||||||
{{ model.template.contact|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
{{ model.template.contact|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||||
<br>
|
<br>
|
||||||
<strong>{{ 'label.invoice_bank_account'|trans({}, 'messages', language) }}</strong>:
|
<strong>{{ 'label.invoice_bank_account'|trans }}</strong>:
|
||||||
{{ model.template.paymentDetails|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
{{ model.template.paymentDetails|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||||
</td>
|
</td>
|
||||||
<td align="right">
|
<td align="right">
|
||||||
@@ -50,33 +50,33 @@ mpdf-->
|
|||||||
<table class="addresses">
|
<table class="addresses">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="60%">
|
<td width="60%">
|
||||||
{{ 'invoice.to'|trans({}, 'messages', language) }}
|
{{ 'invoice.to'|trans }}
|
||||||
<br>
|
<br>
|
||||||
<strong>{{ model.customer.company|default(model.customer.name) }}</strong>
|
<strong>{{ model.customer.company|default(model.customer.name) }}</strong>
|
||||||
<br>
|
<br>
|
||||||
{{ model.customer.address|nl2br }}
|
{{ model.customer.address|nl2br }}
|
||||||
{% if model.customer.vatId is not empty %}
|
{% if model.customer.vatId is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.vat_id'|trans({}, 'messages', language) }}: {{ model.customer.vatId }}
|
{{ 'label.vat_id'|trans }}: {{ model.customer.vatId }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if model.customer.number is not empty %}
|
{% if model.customer.number is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.number'|trans({}, 'messages', language) }}: {{ model.customer.number }}
|
{{ 'label.number'|trans }}: {{ model.customer.number }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.orderNumber'|trans({}, 'messages', language) }}: {{ model.query.project.orderNumber }}
|
{{ 'label.orderNumber'|trans }}: {{ model.query.project.orderNumber }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ 'invoice.from'|trans({}, 'messages', language) }}
|
{{ 'invoice.from'|trans }}
|
||||||
<br>
|
<br>
|
||||||
<strong>{{ model.template.company }}</strong>
|
<strong>{{ model.template.company }}</strong>
|
||||||
<br>
|
<br>
|
||||||
{{ model.template.address|trim|nl2br }}
|
{{ model.template.address|trim|nl2br }}
|
||||||
{% if model.template.vatId is not empty %}
|
{% if model.template.vatId is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.vat_id'|trans({}, 'messages', language) }}:
|
{{ 'label.vat_id'|trans }}:
|
||||||
{{ model.template.vatId }}
|
{{ model.template.vatId }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
@@ -84,22 +84,22 @@ mpdf-->
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ 'invoice.number'|trans({}, 'messages', language) }}:</strong>
|
<strong>{{ 'invoice.number'|trans }}:</strong>
|
||||||
{{ model.invoiceNumber }}
|
{{ model.invoiceNumber }}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<strong>{{ 'invoice.due_days'|trans({}, 'messages', language) }}:</strong>
|
<strong>{{ 'invoice.due_days'|trans }}:</strong>
|
||||||
{{ model.dueDate|date_short }}
|
{{ model.dueDate|date_short }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<table class="items">
|
<table class="items">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="first">{{ 'label.date'|trans({}, 'messages', language) }}</th>
|
<th class="first">{{ 'label.date'|trans }}</th>
|
||||||
<th>{{ 'label.description'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.description'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.unit_price'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.unit_price'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.amount'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.amount'|trans }}</th>
|
||||||
<th class="last text-right">{{ 'label.total_rate'|trans({}, 'messages', language) }}</th>
|
<th class="last text-right">{{ 'label.total_rate'|trans }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -129,19 +129,19 @@ mpdf-->
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-right">
|
<td colspan="4" class="text-right">
|
||||||
{{ 'invoice.subtotal'|trans({}, 'messages', language) }}
|
{{ 'invoice.subtotal'|trans }}
|
||||||
</td>
|
</td>
|
||||||
<td class="last text-right">{{ model.calculator.subtotal|money(currency) }}</td>
|
<td class="last text-right">{{ model.calculator.subtotal|money(currency) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-right">
|
<td colspan="4" class="text-right">
|
||||||
{{ 'invoice.tax'|trans({}, 'messages', language) }} ({{ model.calculator.vat }}%)
|
{{ 'invoice.tax'|trans }} ({{ model.calculator.vat }}%)
|
||||||
</td>
|
</td>
|
||||||
<td class="last text-right">{{ model.calculator.tax|money(currency) }}</td>
|
<td class="last text-right">{{ model.calculator.tax|money(currency) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-right text-nowrap">
|
<td colspan="4" class="text-right text-nowrap">
|
||||||
<strong>{{ 'invoice.total'|trans({}, 'messages', language) }}</strong>
|
<strong>{{ 'invoice.total'|trans }}</strong>
|
||||||
</td>
|
</td>
|
||||||
<td class="last text-right">
|
<td class="last text-right">
|
||||||
<strong>{{ model.calculator.total|money(currency) }}</strong>
|
<strong>{{ model.calculator.total|money(currency) }}</strong>
|
||||||
|
|||||||
@@ -1,48 +1,46 @@
|
|||||||
{% extends 'invoice/layout.html.twig' %}
|
{% extends 'invoice/layout.html.twig' %}
|
||||||
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
|
|
||||||
{% set language = model.template.language|default(fallback) %}
|
|
||||||
{% set isDecimal = model.template.decimalDuration|default(false) %}
|
|
||||||
{% set currency = model.currency %}
|
|
||||||
|
|
||||||
{% block invoice %}
|
{% block invoice %}
|
||||||
|
{% set isDecimal = model.template.decimalDuration|default(false) %}
|
||||||
|
{% set currency = model.currency %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<h2 class="page-header">
|
<h2 class="page-header">
|
||||||
<span contenteditable="true">{{ model.template.title }}</span>
|
<span contenteditable="true">{{ model.template.title }}</span>
|
||||||
<small class="pull-right">{{ 'label.date'|trans({}, 'messages', language) }}: {{ model.invoiceDate|date_short }}</small>
|
<small class="pull-right">{{ 'label.date'|trans }}: {{ model.invoiceDate|date_short }}</small>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
{{ 'invoice.to'|trans({}, 'messages', language) }}
|
{{ 'invoice.to'|trans }}
|
||||||
<address contenteditable="true">
|
<address contenteditable="true">
|
||||||
<strong>{{ model.customer.company|default(model.customer.name) }}</strong><br>
|
<strong>{{ model.customer.company|default(model.customer.name) }}</strong><br>
|
||||||
{{ model.customer.address|nl2br }}
|
{{ model.customer.address|nl2br }}
|
||||||
{% if model.customer.vatId is not empty %}
|
{% if model.customer.vatId is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.vat_id'|trans({}, 'messages', language) }}: {{ model.customer.vatId }}
|
{{ 'label.vat_id'|trans }}: {{ model.customer.vatId }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if model.customer.number is not empty %}
|
{% if model.customer.number is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.number'|trans({}, 'messages', language) }}: {{ model.customer.number }}
|
{{ 'label.number'|trans }}: {{ model.customer.number }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.orderNumber'|trans({}, 'messages', language) }}: {{ model.query.project.orderNumber }}
|
{{ 'label.orderNumber'|trans }}: {{ model.query.project.orderNumber }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</address>
|
</address>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-2"></div>
|
<div class="col-sm-2"></div>
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
{{ 'invoice.from'|trans({}, 'messages', language) }}
|
{{ 'invoice.from'|trans }}
|
||||||
<address contenteditable="true">
|
<address contenteditable="true">
|
||||||
<strong>{{ model.template.company }}</strong><br>
|
<strong>{{ model.template.company }}</strong><br>
|
||||||
{{ model.template.address|trim|nl2br }}
|
{{ model.template.address|trim|nl2br }}
|
||||||
{% if model.template.vatId is not empty %}
|
{% if model.template.vatId is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.vat_id'|trans({}, 'messages', language) }}:
|
{{ 'label.vat_id'|trans }}:
|
||||||
{{ model.template.vatId }}
|
{{ model.template.vatId }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</address>
|
</address>
|
||||||
@@ -52,11 +50,11 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
<p contenteditable="true">
|
<p contenteditable="true">
|
||||||
<strong>{{ 'invoice.number'|trans({}, 'messages', language) }}:</strong>
|
<strong>{{ 'invoice.number'|trans }}:</strong>
|
||||||
{{ model.invoiceNumber }}
|
{{ model.invoiceNumber }}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<strong>{{ 'invoice.due_days'|trans({}, 'messages', language) }}:</strong>
|
<strong>{{ 'invoice.due_days'|trans }}:</strong>
|
||||||
{{ model.dueDate|date_short }}
|
{{ model.dueDate|date_short }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,11 +66,11 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.date'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.date'|trans }}</th>
|
||||||
<th>{{ 'label.description'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.description'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.unit_price'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.unit_price'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.amount'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.amount'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.total_rate'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.total_rate'|trans }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -102,19 +100,19 @@
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-right">
|
<td colspan="4" class="text-right">
|
||||||
{{ 'invoice.subtotal'|trans({}, 'messages', language) }}
|
{{ 'invoice.subtotal'|trans }}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">{{ model.calculator.subtotal|money(currency) }}</td>
|
<td class="text-right">{{ model.calculator.subtotal|money(currency) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-right">
|
<td colspan="4" class="text-right">
|
||||||
{{ 'invoice.tax'|trans({}, 'messages', language) }} ({{ model.calculator.vat }}%)
|
{{ 'invoice.tax'|trans }} ({{ model.calculator.vat }}%)
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">{{ model.calculator.tax|money(currency) }}</td>
|
<td class="text-right">{{ model.calculator.tax|money(currency) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-right text-nowrap">
|
<td colspan="4" class="text-right text-nowrap">
|
||||||
<strong>{{ 'invoice.total'|trans({}, 'messages', language) }}</strong>
|
<strong>{{ 'invoice.total'|trans }}</strong>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<strong>{{ model.calculator.total|money(currency) }}</strong>
|
<strong>{{ model.calculator.total|money(currency) }}</strong>
|
||||||
@@ -137,12 +135,11 @@
|
|||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ 'label.address'|trans({}, 'messages', language) }}</strong>: {{ model.template.company }} – {{ model.template.address|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
<strong>{{ 'label.address'|trans }}</strong>: {{ model.template.company }} – {{ model.template.address|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||||
<br>
|
<br>
|
||||||
<strong>{{ 'label.invoice_bank_account'|trans({}, 'messages', language) }}</strong>: {{ model.template.paymentDetails|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
<strong>{{ 'label.invoice_bank_account'|trans }}</strong>: {{ model.template.paymentDetails|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||||
<br>
|
<br>
|
||||||
<strong>{{ 'label.contact'|trans({}, 'messages', language) }}</strong>: {{ model.template.contact|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
<strong>{{ 'label.contact'|trans }}</strong>: {{ model.template.contact|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
{% import "macros/widgets.html.twig" as widgets %}
|
{% import "macros/widgets.html.twig" as widgets %}
|
||||||
{% extends 'invoice/layout.html.twig' %}
|
{% extends 'invoice/layout.html.twig' %}
|
||||||
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
|
|
||||||
{% set language = model.template.language|default(fallback) %}
|
|
||||||
{% set isDecimal = model.template.decimalDuration|default(false) %}
|
|
||||||
{% set currency = model.currency %}
|
|
||||||
|
|
||||||
{% block invoice %}
|
{% block invoice %}
|
||||||
|
{% set isDecimal = model.template.decimalDuration|default(false) %}
|
||||||
|
{% set currency = model.currency %}
|
||||||
<div class="row" id="freelancer-invoice">
|
<div class="row" id="freelancer-invoice">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<header>
|
<header>
|
||||||
@@ -14,33 +12,33 @@
|
|||||||
</address>
|
</address>
|
||||||
</header>
|
</header>
|
||||||
<article class="address">
|
<article class="address">
|
||||||
<h1>{{ 'invoice.to'|trans({}, 'messages', language) }}</h1>
|
<h1>{{ 'invoice.to'|trans }}</h1>
|
||||||
<address>
|
<address>
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ model.customer.company|default(model.customer.name) }}</strong><br>
|
<strong>{{ model.customer.company|default(model.customer.name) }}</strong><br>
|
||||||
{{ model.customer.address|nl2br }}
|
{{ model.customer.address|nl2br }}
|
||||||
{% if model.customer.vatId is not empty %}
|
{% if model.customer.vatId is not empty %}
|
||||||
<br>
|
<br>
|
||||||
{{ 'label.vat_id'|trans({}, 'messages', language) }}: {{ model.customer.vatId }}
|
{{ 'label.vat_id'|trans }}: {{ model.customer.vatId }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
</address>
|
</address>
|
||||||
<table class="meta">
|
<table class="meta">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.date'|trans({}, 'messages', language) }}:</th>
|
<th>{{ 'label.date'|trans }}:</th>
|
||||||
<td contenteditable="true">{{ model.invoiceDate|date_short }}</td>
|
<td contenteditable="true">{{ model.invoiceDate|date_short }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'invoice.service_date'|trans({}, 'messages', language) }}:</th>
|
<th>{{ 'invoice.service_date'|trans }}:</th>
|
||||||
<td><span contenteditable="true">{{ model.query.end|month_name|trans({}, 'messages', language) }} {{ model.query.end|date('Y') }}</span></td>
|
<td><span contenteditable="true">{{ model.query.end|month_name }} {{ model.query.end|date('Y') }}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'invoice.number'|trans({}, 'messages', language) }}:</th>
|
<th>{{ 'invoice.number'|trans }}:</th>
|
||||||
<td>{{ model.invoiceNumber }}</td>
|
<td>{{ model.invoiceNumber }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.orderNumber'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.orderNumber'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{{ model.query.project.orderNumber }}
|
{{ model.query.project.orderNumber }}
|
||||||
</td>
|
</td>
|
||||||
@@ -48,7 +46,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if model.template.vatId is not empty %}
|
{% if model.template.vatId is not empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.vat_id'|trans({}, 'messages', language) }}:</th>
|
<th>{{ 'label.vat_id'|trans }}:</th>
|
||||||
<td>
|
<td>
|
||||||
{{ model.template.vatId }}
|
{{ model.template.vatId }}
|
||||||
</td>
|
</td>
|
||||||
@@ -61,10 +59,10 @@
|
|||||||
<table class="inventory">
|
<table class="inventory">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.description'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.description'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.unit_price'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.unit_price'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.amount'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.amount'|trans }}</th>
|
||||||
<th class="text-right">{{ 'label.total_rate'|trans({}, 'messages', language) }}</th>
|
<th class="text-right">{{ 'label.total_rate'|trans }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -94,15 +92,15 @@
|
|||||||
</table>
|
</table>
|
||||||
<table class="balance">
|
<table class="balance">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'invoice.subtotal'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'invoice.subtotal'|trans }}</th>
|
||||||
<td>{{ model.calculator.subtotal|money(currency) }}</td>
|
<td>{{ model.calculator.subtotal|money(currency) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'invoice.tax'|trans({}, 'messages', language) }} ({{ model.calculator.vat }}%)</th>
|
<th>{{ 'invoice.tax'|trans }} ({{ model.calculator.vat }}%)</th>
|
||||||
<td>{{ model.calculator.tax|money(currency) }}</td>
|
<td>{{ model.calculator.tax|money(currency) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="total">{{ 'invoice.total'|trans({}, 'messages', language) }}</th>
|
<th class="total">{{ 'invoice.total'|trans }}</th>
|
||||||
<td class="total">{{ model.calculator.total|money(currency) }}</td>
|
<td class="total">{{ model.calculator.total|money(currency) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -118,7 +116,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-4" contenteditable="true">
|
<div class="col-sm-4" contenteditable="true">
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ 'label.address'|trans({}, 'messages', language) }}</strong>
|
<strong>{{ 'label.address'|trans }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{ model.template.company }}<br>
|
{{ model.template.company }}<br>
|
||||||
@@ -127,7 +125,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 text-center" contenteditable="true">
|
<div class="col-sm-4 text-center" contenteditable="true">
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ 'label.invoice_bank_account'|trans({}, 'messages', language) }}</strong>
|
<strong>{{ 'label.invoice_bank_account'|trans }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{ model.template.paymentDetails|nl2br }}
|
{{ model.template.paymentDetails|nl2br }}
|
||||||
@@ -135,7 +133,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 text-right" contenteditable="true">
|
<div class="col-sm-4 text-right" contenteditable="true">
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ 'label.contact'|trans({}, 'messages', language) }}</strong>
|
<strong>{{ 'label.contact'|trans }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{ model.template.contact|nl2br }}
|
{{ model.template.contact|nl2br }}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
{% import "macros/widgets.html.twig" as widgets %}
|
{% import "macros/widgets.html.twig" as widgets %}
|
||||||
{% extends 'invoice/layout.html.twig' %}
|
{% extends 'invoice/layout.html.twig' %}
|
||||||
{% set fallback = app.request is not null ? app.request.locale : 'en' %}
|
|
||||||
{% set language = model.template.language|default(fallback) %}
|
|
||||||
{% set isDecimal = model.template.decimalDuration|default(false) %}
|
|
||||||
|
|
||||||
{% block invoice %}
|
{% block invoice %}
|
||||||
|
{% set isDecimal = model.template.decimalDuration|default(false) %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<h2 class="page-header">
|
<h2 class="page-header">
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<table class="table no-border table-condensed">
|
<table class="table no-border table-condensed">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'invoice.from'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'invoice.from'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{% if model.query.user is not empty %}
|
{% if model.query.user is not empty %}
|
||||||
{{ widgets.username(model.query.user) }}
|
{{ widgets.username(model.query.user) }}
|
||||||
@@ -27,17 +25,17 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.date'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.date'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{% if model.query.begin|date('m') != model.query.end|date('m') or model.query.begin|date('Y') != model.query.end|date('Y') %}
|
{% if model.query.begin|date('m') != model.query.end|date('m') or model.query.begin|date('Y') != model.query.end|date('Y') %}
|
||||||
{{ model.query.begin|date_short }} - {{ model.query.end|date_short }}
|
{{ model.query.begin|date_short }} - {{ model.query.end|date_short }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ model.query.end|month_name|trans({}, 'messages', language) }} {{ model.query.end|date('Y') }}
|
{{ model.query.end|month_name }} {{ model.query.end|date('Y') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.customer'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.customer'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{% if model.customer.number is not empty %}[{{ model.customer.number }}]{% endif %}
|
{% if model.customer.number is not empty %}[{{ model.customer.number }}]{% endif %}
|
||||||
{{ model.customer.name }}{% if model.customer.contact is not empty %} / {{ model.customer.contact }}{% endif %}
|
{{ model.customer.name }}{% if model.customer.contact is not empty %} / {{ model.customer.contact }}{% endif %}
|
||||||
@@ -45,7 +43,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.orderNumber'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.orderNumber'|trans }}</th>
|
||||||
<td contenteditable="true">
|
<td contenteditable="true">
|
||||||
{{ model.query.project.orderNumber }}
|
{{ model.query.project.orderNumber }}
|
||||||
</td>
|
</td>
|
||||||
@@ -60,12 +58,12 @@
|
|||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'label.date'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.date'|trans }}</th>
|
||||||
{% if model.query.user is empty %}
|
{% if model.query.user is empty %}
|
||||||
<th>{{ 'label.user'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.user'|trans }}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th>{{ 'label.activity'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.activity'|trans }}</th>
|
||||||
<th>{{ 'label.hours'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'label.hours'|trans }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -92,7 +90,7 @@
|
|||||||
{% if model.query.user is empty %}
|
{% if model.query.user is empty %}
|
||||||
<th></th>
|
<th></th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th>{{ 'invoice.total_working_time'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'invoice.total_working_time'|trans }}</th>
|
||||||
<th class="text-nowrap">{{ model.calculator.timeWorked|duration(isDecimal) }}</th>
|
<th class="text-nowrap">{{ model.calculator.timeWorked|duration(isDecimal) }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
@@ -103,7 +101,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
{% if model.template.paymentTerms is not empty %}
|
{% if model.template.paymentTerms is not empty %}
|
||||||
<p class="lead">{{ 'label.payment_terms'|trans({}, 'messages', language) }}</p>
|
<p class="lead">{{ 'label.payment_terms'|trans }}</p>
|
||||||
|
|
||||||
<p class="text-muted well well-sm no-shadow" contenteditable="true" style="margin-bottom: 100px">
|
<p class="text-muted well well-sm no-shadow" contenteditable="true" style="margin-bottom: 100px">
|
||||||
{{ model.template.paymentTerms|trim|nl2br }}
|
{{ model.template.paymentTerms|trim|nl2br }}
|
||||||
@@ -114,10 +112,10 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="padding-bottom: 60px">{{ 'invoice.signature_user'|trans({}, 'messages', language) }}</th>
|
<th style="padding-bottom: 60px">{{ 'invoice.signature_user'|trans }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'invoice.signature_customer'|trans({}, 'messages', language) }}</th>
|
<th>{{ 'invoice.signature_customer'|trans }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
{% if seeOwnRate %}
|
{% if seeOwnRate %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'stats.amountMonth'|trans }}</th>
|
<th>{{ 'stats.amountMonth'|trans }}</th>
|
||||||
|
{# TODO which currency shall we use here? #}
|
||||||
<td class="text-nowrap pull-right">{{ stats.amountThisMonth|money }}</td>
|
<td class="text-nowrap pull-right">{{ stats.amountThisMonth|money }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
{% if seeOwnRate %}
|
{% if seeOwnRate %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ 'stats.amountTotal'|trans }}</th>
|
<th>{{ 'stats.amountTotal'|trans }}</th>
|
||||||
|
{# TODO which currency shall we use here? #}
|
||||||
<td class="text-nowrap pull-right">{{ stats.amountTotal|money }}</td>
|
<td class="text-nowrap pull-right">{{ stats.amountTotal|money }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -103,73 +105,3 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{# -------------------------------- UNUSED FOR NOW -------------------------------- #}
|
|
||||||
{% macro profile_list_unused(user, items) %}
|
|
||||||
{% import "@AdminLTE/Macros/default.html.twig" as macro %}
|
|
||||||
{% import "macros/widgets.html.twig" as widgets %}
|
|
||||||
|
|
||||||
<div class="box box-widget widget-user-2">
|
|
||||||
<!-- Add the bg color to the header using any of the bg-* classes -->
|
|
||||||
<div class="widget-user-header bg-green">
|
|
||||||
<div class="widget-user-image">
|
|
||||||
{{ macro.avatar(user.avatar, user.username) }}
|
|
||||||
</div>
|
|
||||||
<!-- /.widget-user-image -->
|
|
||||||
<h3 class="widget-user-username">{{ widgets.username(user) }}</h3>
|
|
||||||
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
|
||||||
</div>
|
|
||||||
<div class="box-footer no-padding">
|
|
||||||
<ul class="nav nav-stacked">
|
|
||||||
{% for entry in items %}
|
|
||||||
<li><a href="{{ entry.url }}">{{ entry.title|trans }} <span class="pull-right badge bg-{{ entry.color }}">{{ entry.value }}</span></a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{# -------------------------------- UNUSED FOR NOW -------------------------------- #}
|
|
||||||
{% macro profile_box_unused(user, stats) %}
|
|
||||||
{% import "@AdminLTE/Macros/default.html.twig" as macro %}
|
|
||||||
{% import "macros/widgets.html.twig" as widgets %}
|
|
||||||
|
|
||||||
<div class="box box-widget widget-user">
|
|
||||||
<div class="widget-user-header bg-green">
|
|
||||||
<h3 class="widget-user-username">{{ widgets.username(user) }}</h3>
|
|
||||||
<h5 class="widget-user-desc">{{ user.title }}</h5>
|
|
||||||
</div>
|
|
||||||
<div class="widget-user-image">
|
|
||||||
{{ macro.avatar(user.avatar, user.username) }}
|
|
||||||
</div>
|
|
||||||
<div class="box-footer">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6 border-right">
|
|
||||||
<div class="description-block">
|
|
||||||
<h5 class="description-header">{{ stats.durationTotal|duration }}</h5>
|
|
||||||
<span class="description-text">{{ 'stats.durationTotal'|trans }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 border-right">
|
|
||||||
<div class="description-block">
|
|
||||||
<h5 class="description-header">{{ stats.amountTotal|money }}</h5>
|
|
||||||
<span class="description-text">{{ 'stats.amountTotal'|trans }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 border-right">
|
|
||||||
<div class="description-block">
|
|
||||||
<h5 class="description-header">{{ stats.durationThisMonth|duration }}</h5>
|
|
||||||
<span class="description-text">{{ 'stats.durationMonth'|trans }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 border-right">
|
|
||||||
<div class="description-block">
|
|
||||||
<h5 class="description-header">{{ stats.amountThisMonth|money }}</h5>
|
|
||||||
<span class="description-text">{{ 'stats.amountMonth'|trans }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<div class="col-sm-3 col-xs-6">
|
<div class="col-sm-3 col-xs-6">
|
||||||
<div class="description-block border-right">
|
<div class="description-block border-right">
|
||||||
<h5 class="description-header">{{ data.month|duration }}</h5>
|
<h5 class="description-header">{{ data.month|duration }}</h5>
|
||||||
<span class="description-text">{{ 'stats.workingTimeMonth'|trans({'%month%': data.begin|month_name|trans, '%year%': data.begin|date_format('Y')}) }}</span>
|
<span class="description-text">{{ 'stats.workingTimeMonth'|trans({'%month%': data.begin|month_name, '%year%': data.begin|date_format('Y')}) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-3 col-xs-6">
|
<div class="col-sm-3 col-xs-6">
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ class TimesheetTest extends TestCase
|
|||||||
public function testDefaultValues()
|
public function testDefaultValues()
|
||||||
{
|
{
|
||||||
$sut = new Timesheet();
|
$sut = new Timesheet();
|
||||||
|
self::assertEquals('timesheet', $sut->getType());
|
||||||
|
self::assertEquals('work', $sut->getCategory());
|
||||||
self::assertNull($sut->getId());
|
self::assertNull($sut->getId());
|
||||||
self::assertNull($sut->getBegin());
|
self::assertNull($sut->getBegin());
|
||||||
self::assertNull($sut->getEnd());
|
self::assertNull($sut->getEnd());
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ use App\Export\ExportRendererInterface;
|
|||||||
use App\Export\RendererInterface;
|
use App\Export\RendererInterface;
|
||||||
use App\Repository\Query\TimesheetQuery;
|
use App\Repository\Query\TimesheetQuery;
|
||||||
use App\Twig\DateExtensions;
|
use App\Twig\DateExtensions;
|
||||||
use App\Utils\LocaleSettings;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
@@ -60,10 +59,8 @@ abstract class AbstractRendererTest extends KernelTestCase
|
|||||||
$request->setLocale('en');
|
$request->setLocale('en');
|
||||||
$requestStack->push($request);
|
$requestStack->push($request);
|
||||||
|
|
||||||
$localeSettings = new LocaleSettings($requestStack, new LanguageFormattings($languages));
|
|
||||||
|
|
||||||
$translator = $this->createMock(TranslatorInterface::class);
|
$translator = $this->createMock(TranslatorInterface::class);
|
||||||
$dateExtension = new DateExtensions($localeSettings);
|
$dateExtension = new DateExtensions($requestStack, new LanguageFormattings($languages));
|
||||||
|
|
||||||
$dispatcher = new EventDispatcher();
|
$dispatcher = new EventDispatcher();
|
||||||
$dispatcher->addSubscriber(new MetaFieldColumnSubscriber());
|
$dispatcher->addSubscriber(new MetaFieldColumnSubscriber());
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ use App\Event\TimesheetMetaDisplayEvent;
|
|||||||
use App\Export\TimesheetExportInterface;
|
use App\Export\TimesheetExportInterface;
|
||||||
use App\Repository\Query\TimesheetQuery;
|
use App\Repository\Query\TimesheetQuery;
|
||||||
use App\Twig\DateExtensions;
|
use App\Twig\DateExtensions;
|
||||||
use App\Utils\LocaleSettings;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
@@ -59,10 +58,8 @@ abstract class AbstractRendererTest extends KernelTestCase
|
|||||||
$request->setLocale('en');
|
$request->setLocale('en');
|
||||||
$requestStack->push($request);
|
$requestStack->push($request);
|
||||||
|
|
||||||
$localeSettings = new LocaleSettings($requestStack, new LanguageFormattings($languages));
|
|
||||||
|
|
||||||
$translator = $this->getMockBuilder(TranslatorInterface::class)->getMock();
|
$translator = $this->getMockBuilder(TranslatorInterface::class)->getMock();
|
||||||
$dateExtension = new DateExtensions($localeSettings);
|
$dateExtension = new DateExtensions($requestStack, new LanguageFormattings($languages));
|
||||||
|
|
||||||
$dispatcher = new EventDispatcher();
|
$dispatcher = new EventDispatcher();
|
||||||
$dispatcher->addSubscriber(new MetaFieldColumnSubscriber());
|
$dispatcher->addSubscriber(new MetaFieldColumnSubscriber());
|
||||||
|
|||||||
@@ -32,17 +32,22 @@ class DebugFormatter implements InvoiceFormatter
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $amount
|
* @param int|float $amount
|
||||||
* @param string|null $currency
|
* @param string|null $currency
|
||||||
* @return mixed
|
* @param bool $withCurrency
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getFormattedMoney($amount, $currency)
|
public function getFormattedMoney($amount, ?string $currency, bool $withCurrency = true)
|
||||||
{
|
{
|
||||||
if (null !== $currency) {
|
if (null === $currency) {
|
||||||
|
$withCurrency = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($withCurrency) {
|
||||||
return $amount . ' ' . $currency;
|
return $amount . ' ' . $currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $amount;
|
return (string) $amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Twig\Environment;
|
|||||||
use Twig\Loader\FilesystemLoader;
|
use Twig\Loader\FilesystemLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @covers \App\Invoice\Renderer\AbstractTwigRenderer
|
||||||
* @covers \App\Invoice\Renderer\JsonRenderer
|
* @covers \App\Invoice\Renderer\JsonRenderer
|
||||||
* @group integration
|
* @group integration
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use Twig\Environment;
|
|||||||
use Twig\Loader\FilesystemLoader;
|
use Twig\Loader\FilesystemLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @covers \App\Invoice\Renderer\AbstractTwigRenderer
|
||||||
* @covers \App\Invoice\Renderer\PdfRenderer
|
* @covers \App\Invoice\Renderer\PdfRenderer
|
||||||
* @group integration
|
* @group integration
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -30,11 +30,9 @@ use App\Invoice\NumberGenerator\DateNumberGenerator;
|
|||||||
use App\Invoice\Renderer\AbstractRenderer;
|
use App\Invoice\Renderer\AbstractRenderer;
|
||||||
use App\Repository\Query\InvoiceQuery;
|
use App\Repository\Query\InvoiceQuery;
|
||||||
use App\Twig\DateExtensions;
|
use App\Twig\DateExtensions;
|
||||||
use App\Twig\Extensions;
|
use App\Twig\LocaleExtensions;
|
||||||
use App\Utils\LocaleSettings;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
||||||
|
|
||||||
trait RendererTestTrait
|
trait RendererTestTrait
|
||||||
{
|
{
|
||||||
@@ -87,13 +85,12 @@ trait RendererTestTrait
|
|||||||
$request->setLocale('en');
|
$request->setLocale('en');
|
||||||
$requestStack->push($request);
|
$requestStack->push($request);
|
||||||
|
|
||||||
$localeSettings = new LocaleSettings($requestStack, new LanguageFormattings($languages));
|
$formattings = new LanguageFormattings($languages);
|
||||||
|
|
||||||
$translator = $this->getMockBuilder(TranslatorInterface::class)->getMock();
|
$dateExtension = new DateExtensions($requestStack, $formattings);
|
||||||
$dateExtension = new DateExtensions($localeSettings);
|
$extensions = new LocaleExtensions($requestStack, $formattings);
|
||||||
$extensions = new Extensions($localeSettings);
|
|
||||||
|
|
||||||
return new DefaultInvoiceFormatter($translator, $dateExtension, $extensions);
|
return new DefaultInvoiceFormatter($dateExtension, $extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getInvoiceModel(): InvoiceModel
|
protected function getInvoiceModel(): InvoiceModel
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Twig\Environment;
|
|||||||
use Twig\Loader\FilesystemLoader;
|
use Twig\Loader\FilesystemLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @covers \App\Invoice\Renderer\AbstractTwigRenderer
|
||||||
* @covers \App\Invoice\Renderer\TextRenderer
|
* @covers \App\Invoice\Renderer\TextRenderer
|
||||||
* @group integration
|
* @group integration
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Twig\Environment;
|
|||||||
use Twig\Loader\FilesystemLoader;
|
use Twig\Loader\FilesystemLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @covers \App\Invoice\Renderer\AbstractTwigRenderer
|
||||||
* @covers \App\Invoice\Renderer\TwigRenderer
|
* @covers \App\Invoice\Renderer\TwigRenderer
|
||||||
* @group integration
|
* @group integration
|
||||||
*/
|
*/
|
||||||
@@ -56,6 +57,7 @@ class TwigRendererTest extends KernelTestCase
|
|||||||
$sut = new TwigRenderer($twig);
|
$sut = new TwigRenderer($twig);
|
||||||
|
|
||||||
$model = $this->getInvoiceModel();
|
$model = $this->getInvoiceModel();
|
||||||
|
$model->getTemplate()->setLanguage('de');
|
||||||
|
|
||||||
$document = $this->getInvoiceDocument('timesheet.html.twig');
|
$document = $this->getInvoiceDocument('timesheet.html.twig');
|
||||||
$response = $sut->render($document, $model);
|
$response = $sut->render($document, $model);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Twig\Environment;
|
|||||||
use Twig\Loader\FilesystemLoader;
|
use Twig\Loader\FilesystemLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @covers \App\Invoice\Renderer\AbstractTwigRenderer
|
||||||
* @covers \App\Invoice\Renderer\XmlRenderer
|
* @covers \App\Invoice\Renderer\XmlRenderer
|
||||||
* @group integration
|
* @group integration
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ namespace App\Tests\Twig;
|
|||||||
|
|
||||||
use App\Configuration\LanguageFormattings;
|
use App\Configuration\LanguageFormattings;
|
||||||
use App\Twig\DateExtensions;
|
use App\Twig\DateExtensions;
|
||||||
use App\Utils\LocaleSettings;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
@@ -35,14 +34,12 @@ class DateExtensionsTest extends TestCase
|
|||||||
$requestStack = new RequestStack();
|
$requestStack = new RequestStack();
|
||||||
$requestStack->push($request);
|
$requestStack->push($request);
|
||||||
|
|
||||||
$localeSettings = new LocaleSettings($requestStack, new LanguageFormattings($dateSettings));
|
return new DateExtensions($requestStack, new LanguageFormattings($dateSettings));
|
||||||
|
|
||||||
return new DateExtensions($localeSettings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFilters()
|
public function testGetFilters()
|
||||||
{
|
{
|
||||||
$filters = ['month_name', 'date_short', 'date_time', 'date_full', 'date_format', 'time', 'hour24'];
|
$filters = ['month_name', 'day_name', 'date_short', 'date_time', 'date_full', 'date_format', 'time', 'hour24'];
|
||||||
$sut = $this->getSut('de', []);
|
$sut = $this->getSut('de', []);
|
||||||
$twigFilters = $sut->getFilters();
|
$twigFilters = $sut->getFilters();
|
||||||
$this->assertCount(\count($filters), $twigFilters);
|
$this->assertCount(\count($filters), $twigFilters);
|
||||||
@@ -121,22 +118,39 @@ class DateExtensionsTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \DateTime $date
|
* @dataProvider getDayNameTestData
|
||||||
* @param string $result
|
|
||||||
* @dataProvider getMonthData
|
|
||||||
*/
|
*/
|
||||||
public function testMonthName(\DateTime $date, $result)
|
public function testDayName(string $locale, string $date, string $expectedName)
|
||||||
{
|
{
|
||||||
$sut = $this->getSut('en', []);
|
$sut = $this->getSut($locale, []);
|
||||||
$this->assertEquals($result, $sut->monthName($date));
|
self::assertEquals($expectedName, $sut->dayName(new \DateTime($date)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMonthData()
|
public function getDayNameTestData()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[new \DateTime('January 2016'), 'month.1'],
|
['de', '2020-07-09 12:00:00', 'Donnerstag'],
|
||||||
[new \DateTime('2016-06-23'), 'month.6'],
|
['en', '2020-07-09 12:00:00', 'Thursday']
|
||||||
[new \DateTime('2016-12-23'), 'month.12'],
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getMonthNameTestData
|
||||||
|
*/
|
||||||
|
public function testMonthName(string $locale, string $date, string $expectedName)
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($locale, []);
|
||||||
|
self::assertEquals($expectedName, $sut->monthName(new \DateTime($date)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMonthNameTestData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['de', '2020-07-09 23:59:59', 'Juli'],
|
||||||
|
['en', '2020-07-09 23:59:59', 'July'],
|
||||||
|
['de', 'January 2016', 'Januar'],
|
||||||
|
['en', 'January 2016', 'January'],
|
||||||
|
['en', '2016-12-23', 'December'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,9 @@
|
|||||||
|
|
||||||
namespace App\Tests\Twig;
|
namespace App\Tests\Twig;
|
||||||
|
|
||||||
use App\Configuration\LanguageFormattings;
|
|
||||||
use App\Entity\Timesheet;
|
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Twig\Extensions;
|
use App\Twig\Extensions;
|
||||||
use App\Utils\LocaleSettings;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
|
||||||
use Symfony\Component\Intl\Util\IntlTestHelper;
|
|
||||||
use Twig\TwigFilter;
|
use Twig\TwigFilter;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
@@ -26,32 +20,15 @@ use Twig\TwigFunction;
|
|||||||
*/
|
*/
|
||||||
class ExtensionsTest extends TestCase
|
class ExtensionsTest extends TestCase
|
||||||
{
|
{
|
||||||
private $localeEn = ['en' => ['date' => 'Y-m-d', 'duration' => '%h:%m h']];
|
protected function getSut(): Extensions
|
||||||
private $localeDe = ['de' => ['date' => 'd.m.Y', 'duration' => '%h:%m h']];
|
|
||||||
private $localeRu = ['ru' => ['date' => 'd.m.Y', 'duration' => '%h:%m h']];
|
|
||||||
private $localeFake = ['XX' => ['date' => 'd.m.Y', 'duration' => '%h - %m - %s Zeit']];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $locales
|
|
||||||
* @param string $locale
|
|
||||||
* @return Extensions
|
|
||||||
*/
|
|
||||||
protected function getSut($locales, $locale = 'en')
|
|
||||||
{
|
{
|
||||||
$request = new Request();
|
return new Extensions();
|
||||||
$request->setLocale($locale);
|
|
||||||
$requestStack = new RequestStack();
|
|
||||||
$requestStack->push($request);
|
|
||||||
|
|
||||||
$localeSettings = new LocaleSettings($requestStack, new LanguageFormattings($locales));
|
|
||||||
|
|
||||||
return new Extensions($localeSettings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetFilters()
|
public function testGetFilters()
|
||||||
{
|
{
|
||||||
$filters = ['duration', 'duration_decimal', 'money', 'currency', 'country', 'language', 'amount', 'docu_link', 'multiline_indent'];
|
$filters = ['docu_link', 'multiline_indent'];
|
||||||
$sut = $this->getSut($this->localeDe);
|
$sut = $this->getSut();
|
||||||
$twigFilters = $sut->getFilters();
|
$twigFilters = $sut->getFilters();
|
||||||
$this->assertCount(\count($filters), $twigFilters);
|
$this->assertCount(\count($filters), $twigFilters);
|
||||||
$i = 0;
|
$i = 0;
|
||||||
@@ -64,8 +41,8 @@ class ExtensionsTest extends TestCase
|
|||||||
|
|
||||||
public function testGetFunctions()
|
public function testGetFunctions()
|
||||||
{
|
{
|
||||||
$functions = ['locales', 'class_name'];
|
$functions = ['class_name'];
|
||||||
$sut = $this->getSut($this->localeDe);
|
$sut = $this->getSut();
|
||||||
$twigFunctions = $sut->getFunctions();
|
$twigFunctions = $sut->getFunctions();
|
||||||
$this->assertCount(\count($functions), $twigFunctions);
|
$this->assertCount(\count($functions), $twigFunctions);
|
||||||
$i = 0;
|
$i = 0;
|
||||||
@@ -76,216 +53,6 @@ class ExtensionsTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLocales()
|
|
||||||
{
|
|
||||||
$locales = [
|
|
||||||
['code' => 'en', 'name' => 'English'],
|
|
||||||
['code' => 'de', 'name' => 'Deutsch'],
|
|
||||||
['code' => 'ru', 'name' => 'русский'],
|
|
||||||
];
|
|
||||||
|
|
||||||
$appLocales = array_merge($this->localeEn, $this->localeDe, $this->localeRu);
|
|
||||||
$sut = $this->getSut($appLocales);
|
|
||||||
$this->assertEquals($locales, $sut->getLocales());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCurrency()
|
|
||||||
{
|
|
||||||
$symbols = [
|
|
||||||
'EUR' => '€',
|
|
||||||
'USD' => '$',
|
|
||||||
'RUB' => 'RUB',
|
|
||||||
'rub' => 'RUB',
|
|
||||||
123 => 123,
|
|
||||||
];
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn);
|
|
||||||
foreach ($symbols as $name => $symbol) {
|
|
||||||
$this->assertEquals($symbol, $sut->currency($name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCountry()
|
|
||||||
{
|
|
||||||
$countries = [
|
|
||||||
'DE' => 'Germany',
|
|
||||||
'RU' => 'Russia',
|
|
||||||
'ES' => 'Spain',
|
|
||||||
'es' => 'Spain',
|
|
||||||
'12' => '12',
|
|
||||||
];
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn);
|
|
||||||
foreach ($countries as $locale => $name) {
|
|
||||||
$this->assertEquals($name, $sut->country($locale));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testLanguage()
|
|
||||||
{
|
|
||||||
$languages = [
|
|
||||||
'de' => 'German',
|
|
||||||
'ru' => 'Russian',
|
|
||||||
'es' => 'Spanish',
|
|
||||||
'ES' => 'Spanish',
|
|
||||||
'12' => '12',
|
|
||||||
];
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn);
|
|
||||||
foreach ($languages as $locale => $name) {
|
|
||||||
$this->assertEquals($name, $sut->language($locale));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMoneyNull()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut($this->localeEn, 'en');
|
|
||||||
$this->assertEquals('123.75', $sut->money(123.75));
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn, 'de');
|
|
||||||
$this->assertEquals('123.234,755', $sut->money(123234.7554));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider getMoneyData
|
|
||||||
*/
|
|
||||||
public function testMoney($result, $amount, $currency, $locale)
|
|
||||||
{
|
|
||||||
$sut = $this->getSut($this->localeEn, $locale);
|
|
||||||
$this->assertEquals($result, $sut->money($amount, $currency));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMoneyData()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['0,00 €', null, 'EUR', 'de'],
|
|
||||||
['2.345,00 €', 2345, 'EUR', 'de'],
|
|
||||||
['€2,345.00', 2345, 'EUR', 'en'],
|
|
||||||
['€2,345.01', 2345.009, 'EUR', 'en'],
|
|
||||||
['2.345,01 €', 2345.009, 'EUR', 'de'],
|
|
||||||
['$13.75', 13.75, 'USD', 'en'],
|
|
||||||
['13,75 $', 13.75, 'USD', 'de'],
|
|
||||||
['13,75 RUB', 13.75, 'RUB', 'de'],
|
|
||||||
['14 ¥', 13.75, 'JPY', 'de'],
|
|
||||||
['13 933 ¥', 13933.49, 'JPY', 'ru'],
|
|
||||||
['1.234.567,89 $', 1234567.891234567890000, 'USD', 'de'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider getAmountData
|
|
||||||
*/
|
|
||||||
public function testAmount($result, $amount, $locale)
|
|
||||||
{
|
|
||||||
$sut = $this->getSut($this->localeEn, $locale);
|
|
||||||
$this->assertEquals($result, $sut->amount($amount));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAmountData()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['0', null, 'de'],
|
|
||||||
['2.345,01', 2345.01, 'de'],
|
|
||||||
['2.345', 2345, 'de'],
|
|
||||||
['2,345', 2345, 'en'],
|
|
||||||
['2,345.009', 2345.009, 'en'],
|
|
||||||
['2.345,009', 2345.009, 'de'],
|
|
||||||
['13.75', 13.75, 'en'],
|
|
||||||
['13,75', 13.75, 'de'],
|
|
||||||
['13 933,49', 13933.49, 'ru'],
|
|
||||||
['1.234.567,891', 1234567.891234567890000, 'de'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider getMoneyData62_1
|
|
||||||
*/
|
|
||||||
public function testMoney62_1($result, $amount, $currency, $locale)
|
|
||||||
{
|
|
||||||
IntlTestHelper::requireFullIntl($this, '62.1');
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn, $locale);
|
|
||||||
$this->assertEquals($result, $sut->money($amount, $currency));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMoneyData62_1()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['RUB 13.50', 13.50, 'RUB', 'en'],
|
|
||||||
['13,75 ₽', 13.75, 'RUB', 'ru'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDuration()
|
|
||||||
{
|
|
||||||
$record = $this->getTimesheet(9437);
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn);
|
|
||||||
$this->assertEquals('02:37 h', $sut->duration($record->getDuration()));
|
|
||||||
$this->assertEquals('2.62', $sut->duration($record->getDuration(), true));
|
|
||||||
|
|
||||||
// test Timesheet object
|
|
||||||
$this->assertEquals('02:37 h', $sut->duration($record));
|
|
||||||
$this->assertEquals('2.62', $sut->duration($record, true));
|
|
||||||
|
|
||||||
// test extended format
|
|
||||||
$sut = $this->getSut($this->localeFake, 'XX');
|
|
||||||
$this->assertEquals('02 - 37 - 17 Zeit', $sut->duration($record->getDuration()));
|
|
||||||
|
|
||||||
// test negative duration
|
|
||||||
$sut = $this->getSut($this->localeEn, 'en');
|
|
||||||
$this->assertEquals('?', $sut->duration('-1'));
|
|
||||||
|
|
||||||
// test zero duration
|
|
||||||
$sut = $this->getSut($this->localeEn, 'en');
|
|
||||||
$this->assertEquals('00:00 h', $sut->duration('0'));
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn, 'en');
|
|
||||||
|
|
||||||
$this->assertEquals('00:00 h', $sut->duration(null));
|
|
||||||
$this->assertEquals('0', $sut->duration(null, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDurationDecimal()
|
|
||||||
{
|
|
||||||
$record = $this->getTimesheet(9437);
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn);
|
|
||||||
$this->assertEquals('2.62', $sut->durationDecimal($record->getDuration()));
|
|
||||||
|
|
||||||
// test Timesheet object
|
|
||||||
$this->assertEquals('2.62', $sut->durationDecimal($record));
|
|
||||||
|
|
||||||
// test extended format
|
|
||||||
$sut = $this->getSut($this->localeDe, 'de');
|
|
||||||
$this->assertEquals('2,62', $sut->durationDecimal($record->getDuration()));
|
|
||||||
|
|
||||||
// test negative duration
|
|
||||||
$sut = $this->getSut($this->localeEn, 'en');
|
|
||||||
$this->assertEquals('0', $sut->durationDecimal('-1'));
|
|
||||||
|
|
||||||
// test zero duration
|
|
||||||
$sut = $this->getSut($this->localeEn, 'en');
|
|
||||||
$this->assertEquals('0', $sut->durationDecimal('0'));
|
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn, 'en');
|
|
||||||
|
|
||||||
$this->assertEquals('0', $sut->durationDecimal(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getTimesheet($seconds)
|
|
||||||
{
|
|
||||||
$begin = new \DateTime();
|
|
||||||
$end = clone $begin;
|
|
||||||
$end->setTimestamp($begin->getTimestamp() + $seconds);
|
|
||||||
$record = new Timesheet();
|
|
||||||
$record->setBegin($begin);
|
|
||||||
$record->setEnd($end);
|
|
||||||
$record->setDuration($seconds);
|
|
||||||
|
|
||||||
return $record;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDocuLink()
|
public function testDocuLink()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
@@ -295,7 +62,7 @@ class ExtensionsTest extends TestCase
|
|||||||
'' => 'https://www.kimai.org/documentation/',
|
'' => 'https://www.kimai.org/documentation/',
|
||||||
];
|
];
|
||||||
|
|
||||||
$sut = $this->getSut($this->localeEn);
|
$sut = $this->getSut();
|
||||||
foreach ($data as $input => $expected) {
|
foreach ($data as $input => $expected) {
|
||||||
$result = $sut->documentationLink($input);
|
$result = $sut->documentationLink($input);
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
@@ -304,7 +71,7 @@ class ExtensionsTest extends TestCase
|
|||||||
|
|
||||||
public function testGetClassName()
|
public function testGetClassName()
|
||||||
{
|
{
|
||||||
$sut = $this->getSut($this->localeEn);
|
$sut = $this->getSut();
|
||||||
$this->assertEquals('DateTime', $sut->getClassName(new \DateTime()));
|
$this->assertEquals('DateTime', $sut->getClassName(new \DateTime()));
|
||||||
$this->assertEquals('stdClass', $sut->getClassName(new \stdClass()));
|
$this->assertEquals('stdClass', $sut->getClassName(new \stdClass()));
|
||||||
$this->assertNull($sut->getClassName(''));
|
$this->assertNull($sut->getClassName(''));
|
||||||
@@ -346,7 +113,7 @@ sdfsdf' . PHP_EOL . "\n" .
|
|||||||
*/
|
*/
|
||||||
public function testMultilineIndent($indent, $string, $expected)
|
public function testMultilineIndent($indent, $string, $expected)
|
||||||
{
|
{
|
||||||
$sut = $this->getSut($this->localeEn);
|
$sut = $this->getSut();
|
||||||
self::assertEquals(implode("\n", $expected), $sut->multilineIndent($string, $indent));
|
self::assertEquals(implode("\n", $expected), $sut->multilineIndent($string, $indent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
321
tests/Twig/LocaleExtensionsTest.php
Normal file
321
tests/Twig/LocaleExtensionsTest.php
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
<?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\Twig;
|
||||||
|
|
||||||
|
use App\Configuration\LanguageFormattings;
|
||||||
|
use App\Entity\Timesheet;
|
||||||
|
use App\Twig\LocaleExtensions;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\Intl\Util\IntlTestHelper;
|
||||||
|
use Twig\TwigFilter;
|
||||||
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Twig\LocaleExtensions
|
||||||
|
*/
|
||||||
|
class LocaleExtensionsTest extends TestCase
|
||||||
|
{
|
||||||
|
private $localeEn = ['en' => ['date' => 'Y-m-d', 'duration' => '%h:%m h']];
|
||||||
|
private $localeDe = ['de' => ['date' => 'd.m.Y', 'duration' => '%h:%m h']];
|
||||||
|
private $localeRu = ['ru' => ['date' => 'd.m.Y', 'duration' => '%h:%m h']];
|
||||||
|
private $localeFake = ['XX' => ['date' => 'd.m.Y', 'duration' => '%h - %m - %s Zeit']];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $locales
|
||||||
|
* @param string $locale
|
||||||
|
* @return LocaleExtensions
|
||||||
|
*/
|
||||||
|
protected function getSut($locales, $locale = 'en')
|
||||||
|
{
|
||||||
|
$request = new Request();
|
||||||
|
$request->setLocale($locale);
|
||||||
|
$requestStack = new RequestStack();
|
||||||
|
$requestStack->push($request);
|
||||||
|
|
||||||
|
return new LocaleExtensions($requestStack, new LanguageFormattings($locales));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetFilters()
|
||||||
|
{
|
||||||
|
$filters = ['duration', 'duration_decimal', 'money', 'currency', 'country', 'language', 'amount'];
|
||||||
|
$sut = $this->getSut($this->localeDe);
|
||||||
|
$twigFilters = $sut->getFilters();
|
||||||
|
$this->assertCount(\count($filters), $twigFilters);
|
||||||
|
$i = 0;
|
||||||
|
/** @var TwigFilter $filter */
|
||||||
|
foreach ($twigFilters as $filter) {
|
||||||
|
$this->assertInstanceOf(TwigFilter::class, $filter);
|
||||||
|
$this->assertEquals($filters[$i++], $filter->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetFunctions()
|
||||||
|
{
|
||||||
|
$functions = ['locales'];
|
||||||
|
$sut = $this->getSut($this->localeDe);
|
||||||
|
$twigFunctions = $sut->getFunctions();
|
||||||
|
$this->assertCount(\count($functions), $twigFunctions);
|
||||||
|
$i = 0;
|
||||||
|
/** @var TwigFunction $filter */
|
||||||
|
foreach ($twigFunctions as $filter) {
|
||||||
|
$this->assertInstanceOf(TwigFunction::class, $filter);
|
||||||
|
$this->assertEquals($functions[$i++], $filter->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLocales()
|
||||||
|
{
|
||||||
|
$locales = [
|
||||||
|
['code' => 'en', 'name' => 'English'],
|
||||||
|
['code' => 'de', 'name' => 'Deutsch'],
|
||||||
|
['code' => 'ru', 'name' => 'русский'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$appLocales = array_merge($this->localeEn, $this->localeDe, $this->localeRu);
|
||||||
|
$sut = $this->getSut($appLocales);
|
||||||
|
$this->assertEquals($locales, $sut->getLocales());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCurrency()
|
||||||
|
{
|
||||||
|
$symbols = [
|
||||||
|
'EUR' => '€',
|
||||||
|
'USD' => '$',
|
||||||
|
'RUB' => 'RUB',
|
||||||
|
'rub' => 'RUB',
|
||||||
|
123 => 123,
|
||||||
|
];
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn);
|
||||||
|
foreach ($symbols as $name => $symbol) {
|
||||||
|
$this->assertEquals($symbol, $sut->currency($name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCountry()
|
||||||
|
{
|
||||||
|
$countries = [
|
||||||
|
'DE' => 'Germany',
|
||||||
|
'RU' => 'Russia',
|
||||||
|
'ES' => 'Spain',
|
||||||
|
'es' => 'Spain',
|
||||||
|
'12' => '12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn);
|
||||||
|
foreach ($countries as $locale => $name) {
|
||||||
|
$this->assertEquals($name, $sut->country($locale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLanguage()
|
||||||
|
{
|
||||||
|
$languages = [
|
||||||
|
'de' => 'German',
|
||||||
|
'ru' => 'Russian',
|
||||||
|
'es' => 'Spanish',
|
||||||
|
'ES' => 'Spanish',
|
||||||
|
'12' => '12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn);
|
||||||
|
foreach ($languages as $locale => $name) {
|
||||||
|
$this->assertEquals($name, $sut->language($locale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMoneyWithoutCurrency()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($this->localeEn, 'en');
|
||||||
|
$this->assertEquals('123.75', $sut->money(123.75));
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn, 'de');
|
||||||
|
$this->assertEquals('123.234,76', $sut->money(123234.7554, null, true));
|
||||||
|
$this->assertEquals('123.234,76', $sut->money(123234.7554, null, false));
|
||||||
|
$this->assertEquals('123.234,76', $sut->money(123234.7554, 'EUR', false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getMoneyNoCurrencyData
|
||||||
|
*/
|
||||||
|
public function testMoneyNoCurrency($result, $amount, $currency, $locale)
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($this->localeEn, $locale);
|
||||||
|
$this->assertEquals($result, $sut->money($amount, $currency, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMoneyNoCurrencyData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['0,00', null, 'EUR', 'de'],
|
||||||
|
['2.345,00', 2345, 'EUR', 'de'],
|
||||||
|
['2,345.00', 2345, 'EUR', 'en'],
|
||||||
|
['2,345.01', 2345.009, 'EUR', 'en'],
|
||||||
|
['2.345,01', 2345.009, 'EUR', 'de'],
|
||||||
|
['13.75', 13.75, 'USD', 'en'],
|
||||||
|
['13,75', 13.75, 'USD', 'de'],
|
||||||
|
['13,75', 13.75, 'RUB', 'de'],
|
||||||
|
['13,75', 13.75, 'JPY', 'de'],
|
||||||
|
['13 933,49', 13933.49, 'JPY', 'ru'],
|
||||||
|
['13,75', 13.75, 'CNY', 'de'],
|
||||||
|
['13.933,00', 13933, 'CNY', 'de'],
|
||||||
|
['13 933,00', 13933, 'CNY', 'ru'],
|
||||||
|
['13,933.00', 13933, 'CNY', 'en'],
|
||||||
|
['13,933.00', 13933, 'CNY', 'zh_CN'],
|
||||||
|
['1.234.567,89', 1234567.891234567890000, 'USD', 'de'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getMoneyData
|
||||||
|
*/
|
||||||
|
public function testMoney($result, $amount, $currency, $locale)
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($this->localeEn, $locale);
|
||||||
|
$this->assertEquals($result, $sut->money($amount, $currency));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMoneyData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['0,00 €', null, 'EUR', 'de'],
|
||||||
|
['2.345,00 €', 2345, 'EUR', 'de'],
|
||||||
|
['€2,345.00', 2345, 'EUR', 'en'],
|
||||||
|
['€2,345.01', 2345.009, 'EUR', 'en'],
|
||||||
|
['2.345,01 €', 2345.009, 'EUR', 'de'],
|
||||||
|
['$13.75', 13.75, 'USD', 'en'],
|
||||||
|
['13,75 $', 13.75, 'USD', 'de'],
|
||||||
|
['13,75 RUB', 13.75, 'RUB', 'de'],
|
||||||
|
['14 ¥', 13.75, 'JPY', 'de'],
|
||||||
|
['13 933 ¥', 13933.49, 'JPY', 'ru'],
|
||||||
|
['13,75 CN¥', 13.75, 'CNY', 'de'],
|
||||||
|
['13.933,00 CN¥', 13933, 'CNY', 'de'],
|
||||||
|
['13 933,00 CN¥', 13933, 'CNY', 'ru'],
|
||||||
|
['CN¥13,933.00', 13933, 'CNY', 'en'],
|
||||||
|
['1.234.567,89 $', 1234567.891234567890000, 'USD', 'de'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getAmountData
|
||||||
|
*/
|
||||||
|
public function testAmount($result, $amount, $locale)
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($this->localeEn, $locale);
|
||||||
|
$this->assertEquals($result, $sut->amount($amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAmountData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['0', null, 'de'],
|
||||||
|
['2.345,01', 2345.01, 'de'],
|
||||||
|
['2.345', 2345, 'de'],
|
||||||
|
['2,345', 2345, 'en'],
|
||||||
|
['2,345.009', 2345.009, 'en'],
|
||||||
|
['2.345,009', 2345.009, 'de'],
|
||||||
|
['13.75', 13.75, 'en'],
|
||||||
|
['13,75', 13.75, 'de'],
|
||||||
|
['13 933,49', 13933.49, 'ru'],
|
||||||
|
['1.234.567,891', 1234567.891234567890000, 'de'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getMoneyData62_1
|
||||||
|
*/
|
||||||
|
public function testMoney62_1($result, $amount, $currency, $locale)
|
||||||
|
{
|
||||||
|
IntlTestHelper::requireFullIntl($this, '62.1');
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn, $locale);
|
||||||
|
$this->assertEquals($result, $sut->money($amount, $currency));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMoneyData62_1()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['RUB 13.50', 13.50, 'RUB', 'en'],
|
||||||
|
['13,75 ₽', 13.75, 'RUB', 'ru'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDuration()
|
||||||
|
{
|
||||||
|
$record = $this->getTimesheet(9437);
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn);
|
||||||
|
$this->assertEquals('02:37 h', $sut->duration($record->getDuration()));
|
||||||
|
$this->assertEquals('2.62', $sut->duration($record->getDuration(), true));
|
||||||
|
|
||||||
|
// test Timesheet object
|
||||||
|
$this->assertEquals('02:37 h', $sut->duration($record));
|
||||||
|
$this->assertEquals('2.62', $sut->duration($record, true));
|
||||||
|
|
||||||
|
// test extended format
|
||||||
|
$sut = $this->getSut($this->localeFake, 'XX');
|
||||||
|
$this->assertEquals('02 - 37 - 17 Zeit', $sut->duration($record->getDuration()));
|
||||||
|
|
||||||
|
// test negative duration
|
||||||
|
$sut = $this->getSut($this->localeEn, 'en');
|
||||||
|
$this->assertEquals('?', $sut->duration('-1'));
|
||||||
|
|
||||||
|
// test zero duration
|
||||||
|
$sut = $this->getSut($this->localeEn, 'en');
|
||||||
|
$this->assertEquals('00:00 h', $sut->duration('0'));
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn, 'en');
|
||||||
|
|
||||||
|
$this->assertEquals('00:00 h', $sut->duration(null));
|
||||||
|
$this->assertEquals('0', $sut->duration(null, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDurationDecimal()
|
||||||
|
{
|
||||||
|
$record = $this->getTimesheet(9437);
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn);
|
||||||
|
$this->assertEquals('2.62', $sut->durationDecimal($record->getDuration()));
|
||||||
|
|
||||||
|
// test Timesheet object
|
||||||
|
$this->assertEquals('2.62', $sut->durationDecimal($record));
|
||||||
|
|
||||||
|
// test extended format
|
||||||
|
$sut = $this->getSut($this->localeDe, 'de');
|
||||||
|
$this->assertEquals('2,62', $sut->durationDecimal($record->getDuration()));
|
||||||
|
|
||||||
|
// test negative duration
|
||||||
|
$sut = $this->getSut($this->localeEn, 'en');
|
||||||
|
$this->assertEquals('0', $sut->durationDecimal('-1'));
|
||||||
|
|
||||||
|
// test zero duration
|
||||||
|
$sut = $this->getSut($this->localeEn, 'en');
|
||||||
|
$this->assertEquals('0', $sut->durationDecimal('0'));
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn, 'en');
|
||||||
|
|
||||||
|
$this->assertEquals('0', $sut->durationDecimal(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTimesheet($seconds)
|
||||||
|
{
|
||||||
|
$begin = new \DateTime();
|
||||||
|
$end = clone $begin;
|
||||||
|
$end->setTimestamp($begin->getTimestamp() + $seconds);
|
||||||
|
$record = new Timesheet();
|
||||||
|
$record->setBegin($begin);
|
||||||
|
$record->setEnd($end);
|
||||||
|
$record->setDuration($seconds);
|
||||||
|
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
}
|
||||||
191
tests/Utils/LocaleFormatsTest.php
Normal file
191
tests/Utils/LocaleFormatsTest.php
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
<?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\Utils;
|
||||||
|
|
||||||
|
use App\Configuration\LanguageFormattings;
|
||||||
|
use App\Utils\LocaleFormats;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Utils\LocaleFormats
|
||||||
|
* @covers \App\Configuration\LanguageFormattings
|
||||||
|
*/
|
||||||
|
class LocaleFormatsTest extends TestCase
|
||||||
|
{
|
||||||
|
protected function getSut(string $locale, array $settings)
|
||||||
|
{
|
||||||
|
return new LocaleFormats(new LanguageFormattings($settings), $locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDefaultSettings()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'de' => [
|
||||||
|
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||||
|
'date_type' => 'dd.MM.yyyy',
|
||||||
|
'date' => 'd.m.Y',
|
||||||
|
'date_time' => 'd.m. H:i',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
'time' => 'H:i',
|
||||||
|
'24_hours' => true,
|
||||||
|
],
|
||||||
|
'en' => [
|
||||||
|
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||||
|
'date_type' => 'yyyy-MM-dd',
|
||||||
|
'date' => 'Y-m-d',
|
||||||
|
'date_time' => 'm-d H:i',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
'time' => 'H:i:s',
|
||||||
|
'24_hours' => false,
|
||||||
|
],
|
||||||
|
'pt_BR' => [
|
||||||
|
'date_time_type' => 'dd-MM-yyyy HH:mm',
|
||||||
|
'date_type' => 'dd-MM-yyyy',
|
||||||
|
'date' => 'd-m-Y',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
],
|
||||||
|
'it' => [
|
||||||
|
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||||
|
'date_type' => 'dd.MM.yyyy',
|
||||||
|
'date' => 'd.m.Y',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
],
|
||||||
|
'fr' => [
|
||||||
|
'date_time_type' => 'dd/MM/yyyy HH:mm',
|
||||||
|
'date_type' => 'dd/MM/yyyy',
|
||||||
|
'date' => 'd/m/Y',
|
||||||
|
'duration' => '%h h %m',
|
||||||
|
],
|
||||||
|
'es' => [
|
||||||
|
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||||
|
'date_type' => 'dd.MM.yyyy',
|
||||||
|
'date' => 'd.m.Y',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
],
|
||||||
|
'ru' => [
|
||||||
|
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
||||||
|
'date_type' => 'dd.MM.yyyy',
|
||||||
|
'date' => 'd.m.Y',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
],
|
||||||
|
'ar' => [
|
||||||
|
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||||
|
'date_type' => 'yyyy-MM-dd',
|
||||||
|
'date' => 'Y-m-d',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
],
|
||||||
|
'hu' => [
|
||||||
|
'date_time_type' => 'yyyy.MM.dd HH:mm',
|
||||||
|
'date_type' => 'yyyy.MM.dd',
|
||||||
|
'date' => 'Y.m.d.',
|
||||||
|
'duration' => '%h:%m h',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetLocale()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', []);
|
||||||
|
$this->assertEquals('en', $sut->getLocale());
|
||||||
|
$sut = $this->getSut('ar', []);
|
||||||
|
$this->assertEquals('ar', $sut->getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAvailableLanguages()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', []);
|
||||||
|
$this->assertEquals([], $sut->getAvailableLanguages());
|
||||||
|
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals(['de', 'en', 'pt_BR', 'it', 'fr', 'es', 'ru', 'ar', 'hu'], $sut->getAvailableLanguages());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidLocaleWithDefaultLocale()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
|
||||||
|
$sut = $this->getSut('en', []);
|
||||||
|
$sut->getDateFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidLocaleWithGivenLocale()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage('Unknown locale given: xx');
|
||||||
|
|
||||||
|
$sut = $this->getSut('xx', $this->getDefaultSettings());
|
||||||
|
$sut->getDateFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDurationFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('%h:%m h', $sut->getDurationFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDateFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('de', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('d.m.Y', $sut->getDateFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDateTimeFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('m-d H:i', $sut->getDateTimeFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDateTypeFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('de', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('dd.MM.yyyy', $sut->getDateTypeFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDatePickerFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('YYYY-MM-DD', $sut->getDatePickerFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDateTimeTypeFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('yyyy-MM-dd HH:mm', $sut->getDateTimeTypeFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetDateTimePickerFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('YYYY-MM-DD HH:mm', $sut->getDateTimePickerFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIs24Hours()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertFalse($sut->isTwentyFourHours());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTimeFormat()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', $this->getDefaultSettings());
|
||||||
|
$this->assertEquals('H:i:s', $sut->getTimeFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnknownSetting()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage('Unknown setting for locale en: date_time_type');
|
||||||
|
|
||||||
|
$sut = $this->getSut('en', ['en' => [
|
||||||
|
'xxx' => 'dd.MM.yyyy HH:mm',
|
||||||
|
]]);
|
||||||
|
$sut->getDateTimePickerFormat();
|
||||||
|
}
|
||||||
|
}
|
||||||
223
tests/Utils/LocaleHelperTest.php
Normal file
223
tests/Utils/LocaleHelperTest.php
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<?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\Utils;
|
||||||
|
|
||||||
|
use App\Entity\Timesheet;
|
||||||
|
use App\Utils\LocaleHelper;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Intl\Util\IntlTestHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \App\Utils\LocaleHelper
|
||||||
|
*/
|
||||||
|
class LocaleHelperTest extends TestCase
|
||||||
|
{
|
||||||
|
protected function getSut(string $locale): LocaleHelper
|
||||||
|
{
|
||||||
|
return new LocaleHelper($locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCurrency()
|
||||||
|
{
|
||||||
|
$symbols = [
|
||||||
|
'EUR' => '€',
|
||||||
|
'USD' => '$',
|
||||||
|
'RUB' => 'RUB',
|
||||||
|
'rub' => 'RUB',
|
||||||
|
123 => 123,
|
||||||
|
];
|
||||||
|
|
||||||
|
$sut = $this->getSut('en');
|
||||||
|
foreach ($symbols as $name => $symbol) {
|
||||||
|
$this->assertEquals($symbol, $sut->currency($name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCountry()
|
||||||
|
{
|
||||||
|
$countries = [
|
||||||
|
'DE' => 'Germany',
|
||||||
|
'RU' => 'Russia',
|
||||||
|
'ES' => 'Spain',
|
||||||
|
'es' => 'Spain',
|
||||||
|
'12' => '12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$sut = $this->getSut('en');
|
||||||
|
foreach ($countries as $locale => $name) {
|
||||||
|
$this->assertEquals($name, $sut->country($locale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLanguage()
|
||||||
|
{
|
||||||
|
$languages = [
|
||||||
|
'de' => 'German',
|
||||||
|
'ru' => 'Russian',
|
||||||
|
'es' => 'Spanish',
|
||||||
|
'ES' => 'Spanish',
|
||||||
|
'12' => '12',
|
||||||
|
];
|
||||||
|
|
||||||
|
$sut = $this->getSut('en');
|
||||||
|
foreach ($languages as $locale => $name) {
|
||||||
|
$this->assertEquals($name, $sut->language($locale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMoneyWithoutCurrency()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en');
|
||||||
|
$this->assertEquals('123.75', $sut->money(123.75));
|
||||||
|
|
||||||
|
$sut = $this->getSut('de');
|
||||||
|
$this->assertEquals('123.234,76', $sut->money(123234.7554, null, true));
|
||||||
|
$this->assertEquals('123.234,76', $sut->money(123234.7554, null, false));
|
||||||
|
$this->assertEquals('123.234,76', $sut->money(123234.7554, 'EUR', false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getMoneyNoCurrencyData
|
||||||
|
*/
|
||||||
|
public function testMoneyNoCurrency($result, $amount, $currency, $locale)
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($locale);
|
||||||
|
$this->assertEquals($result, $sut->money($amount, $currency, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMoneyNoCurrencyData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['0,00', null, 'EUR', 'de'],
|
||||||
|
['2.345,00', 2345, 'EUR', 'de'],
|
||||||
|
['2,345.00', 2345, 'EUR', 'en'],
|
||||||
|
['2,345.01', 2345.009, 'EUR', 'en'],
|
||||||
|
['2.345,01', 2345.009, 'EUR', 'de'],
|
||||||
|
['13.75', 13.75, 'USD', 'en'],
|
||||||
|
['13,75', 13.75, 'USD', 'de'],
|
||||||
|
['13,75', 13.75, 'RUB', 'de'],
|
||||||
|
['13,75', 13.75, 'JPY', 'de'],
|
||||||
|
['13 933,49', 13933.49, 'JPY', 'ru'],
|
||||||
|
['13,75', 13.75, 'CNY', 'de'],
|
||||||
|
['13.933,00', 13933, 'CNY', 'de'],
|
||||||
|
['13 933,00', 13933, 'CNY', 'ru'],
|
||||||
|
['13,933.00', 13933, 'CNY', 'en'],
|
||||||
|
['13,933.00', 13933, 'CNY', 'zh_CN'],
|
||||||
|
['1.234.567,89', 1234567.891234567890000, 'USD', 'de'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getMoneyData
|
||||||
|
*/
|
||||||
|
public function testMoney($result, $amount, $currency, $locale)
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($locale);
|
||||||
|
$this->assertEquals($result, $sut->money($amount, $currency));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMoneyData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['0,00 €', null, 'EUR', 'de'],
|
||||||
|
['2.345,00 €', 2345, 'EUR', 'de'],
|
||||||
|
['€2,345.00', 2345, 'EUR', 'en'],
|
||||||
|
['€2,345.01', 2345.009, 'EUR', 'en'],
|
||||||
|
['2.345,01 €', 2345.009, 'EUR', 'de'],
|
||||||
|
['$13.75', 13.75, 'USD', 'en'],
|
||||||
|
['13,75 $', 13.75, 'USD', 'de'],
|
||||||
|
['13,75 RUB', 13.75, 'RUB', 'de'],
|
||||||
|
['14 ¥', 13.75, 'JPY', 'de'],
|
||||||
|
['13 933 ¥', 13933.49, 'JPY', 'ru'],
|
||||||
|
['13,75 CN¥', 13.75, 'CNY', 'de'],
|
||||||
|
['13.933,00 CN¥', 13933, 'CNY', 'de'],
|
||||||
|
['13 933,00 CN¥', 13933, 'CNY', 'ru'],
|
||||||
|
['CN¥13,933.00', 13933, 'CNY', 'en'],
|
||||||
|
['1.234.567,89 $', 1234567.891234567890000, 'USD', 'de'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getAmountData
|
||||||
|
*/
|
||||||
|
public function testAmount($result, $amount, $locale)
|
||||||
|
{
|
||||||
|
$sut = $this->getSut($locale);
|
||||||
|
$this->assertEquals($result, $sut->amount($amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAmountData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['0', null, 'de'],
|
||||||
|
['2.345,01', 2345.01, 'de'],
|
||||||
|
['2.345', 2345, 'de'],
|
||||||
|
['2,345', 2345, 'en'],
|
||||||
|
['2,345.009', 2345.009, 'en'],
|
||||||
|
['2.345,009', 2345.009, 'de'],
|
||||||
|
['13.75', 13.75, 'en'],
|
||||||
|
['13,75', 13.75, 'de'],
|
||||||
|
['13 933,49', 13933.49, 'ru'],
|
||||||
|
['1.234.567,891', 1234567.891234567890000, 'de'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getMoneyData62_1
|
||||||
|
*/
|
||||||
|
public function testMoney62_1($result, $amount, $currency, $locale)
|
||||||
|
{
|
||||||
|
IntlTestHelper::requireFullIntl($this, '62.1');
|
||||||
|
|
||||||
|
$sut = $this->getSut($locale);
|
||||||
|
$this->assertEquals($result, $sut->money($amount, $currency));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMoneyData62_1()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['RUB 13.50', 13.50, 'RUB', 'en'],
|
||||||
|
['13,75 ₽', 13.75, 'RUB', 'ru'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDurationDecimal()
|
||||||
|
{
|
||||||
|
$record = $this->getTimesheet(9437);
|
||||||
|
|
||||||
|
$sut = $this->getSut('en');
|
||||||
|
$this->assertEquals('2.62', $sut->durationDecimal($record->getDuration()));
|
||||||
|
|
||||||
|
// test extended format
|
||||||
|
$sut = $this->getSut('de');
|
||||||
|
$this->assertEquals('2,62', $sut->durationDecimal($record->getDuration()));
|
||||||
|
|
||||||
|
// test negative duration
|
||||||
|
$sut = $this->getSut('en');
|
||||||
|
$this->assertEquals('0', $sut->durationDecimal('-1'));
|
||||||
|
|
||||||
|
// test zero duration
|
||||||
|
$sut = $this->getSut('en');
|
||||||
|
$this->assertEquals('0', $sut->durationDecimal('0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTimesheet($seconds)
|
||||||
|
{
|
||||||
|
$begin = new \DateTime();
|
||||||
|
$end = clone $begin;
|
||||||
|
$end->setTimestamp($begin->getTimestamp() + $seconds);
|
||||||
|
$record = new Timesheet();
|
||||||
|
$record->setBegin($begin);
|
||||||
|
$record->setEnd($end);
|
||||||
|
$record->setDuration($seconds);
|
||||||
|
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ namespace App\Tests\Utils;
|
|||||||
|
|
||||||
use App\Configuration\LanguageFormattings;
|
use App\Configuration\LanguageFormattings;
|
||||||
use App\Utils\LocaleSettings;
|
use App\Utils\LocaleSettings;
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
@@ -19,185 +18,15 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||||||
* @covers \App\Utils\LocaleSettings
|
* @covers \App\Utils\LocaleSettings
|
||||||
* @covers \App\Configuration\LanguageFormattings
|
* @covers \App\Configuration\LanguageFormattings
|
||||||
*/
|
*/
|
||||||
class LocaleSettingsTest extends TestCase
|
class LocaleSettingsTest extends LocaleFormatsTest
|
||||||
{
|
{
|
||||||
protected function getRequestStack(string $locale)
|
protected function getSut(string $locale, array $settings)
|
||||||
{
|
{
|
||||||
$request = new Request();
|
$request = new Request();
|
||||||
$request->setLocale($locale);
|
$request->setLocale($locale);
|
||||||
$requestStack = new RequestStack();
|
$requestStack = new RequestStack();
|
||||||
$requestStack->push($request);
|
$requestStack->push($request);
|
||||||
|
|
||||||
return $requestStack;
|
return new LocaleSettings($requestStack, new LanguageFormattings($settings));
|
||||||
}
|
|
||||||
|
|
||||||
protected function getSut(string $locale, array $settings)
|
|
||||||
{
|
|
||||||
return new LocaleSettings($this->getRequestStack($locale), new LanguageFormattings($settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getDefaultSettings()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'de' => [
|
|
||||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
|
||||||
'date_type' => 'dd.MM.yyyy',
|
|
||||||
'date' => 'd.m.Y',
|
|
||||||
'date_time' => 'd.m. H:i',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
'time' => 'H:i',
|
|
||||||
'24_hours' => true,
|
|
||||||
],
|
|
||||||
'en' => [
|
|
||||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
|
||||||
'date_type' => 'yyyy-MM-dd',
|
|
||||||
'date' => 'Y-m-d',
|
|
||||||
'date_time' => 'm-d H:i',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
'time' => 'H:i:s',
|
|
||||||
'24_hours' => false,
|
|
||||||
],
|
|
||||||
'pt_BR' => [
|
|
||||||
'date_time_type' => 'dd-MM-yyyy HH:mm',
|
|
||||||
'date_type' => 'dd-MM-yyyy',
|
|
||||||
'date' => 'd-m-Y',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
],
|
|
||||||
'it' => [
|
|
||||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
|
||||||
'date_type' => 'dd.MM.yyyy',
|
|
||||||
'date' => 'd.m.Y',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
],
|
|
||||||
'fr' => [
|
|
||||||
'date_time_type' => 'dd/MM/yyyy HH:mm',
|
|
||||||
'date_type' => 'dd/MM/yyyy',
|
|
||||||
'date' => 'd/m/Y',
|
|
||||||
'duration' => '%h h %m',
|
|
||||||
],
|
|
||||||
'es' => [
|
|
||||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
|
||||||
'date_type' => 'dd.MM.yyyy',
|
|
||||||
'date' => 'd.m.Y',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
],
|
|
||||||
'ru' => [
|
|
||||||
'date_time_type' => 'dd.MM.yyyy HH:mm',
|
|
||||||
'date_type' => 'dd.MM.yyyy',
|
|
||||||
'date' => 'd.m.Y',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
],
|
|
||||||
'ar' => [
|
|
||||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
|
||||||
'date_type' => 'yyyy-MM-dd',
|
|
||||||
'date' => 'Y-m-d',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
],
|
|
||||||
'hu' => [
|
|
||||||
'date_time_type' => 'yyyy.MM.dd HH:mm',
|
|
||||||
'date_type' => 'yyyy.MM.dd',
|
|
||||||
'date' => 'Y.m.d.',
|
|
||||||
'duration' => '%h:%m h',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetLocale()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', []);
|
|
||||||
$this->assertEquals('en', $sut->getLocale());
|
|
||||||
$sut = $this->getSut('ar', []);
|
|
||||||
$this->assertEquals('ar', $sut->getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetAvailableLanguages()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', []);
|
|
||||||
$this->assertEquals([], $sut->getAvailableLanguages());
|
|
||||||
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals(['de', 'en', 'pt_BR', 'it', 'fr', 'es', 'ru', 'ar', 'hu'], $sut->getAvailableLanguages());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInvalidLocaleWithDefaultLocale()
|
|
||||||
{
|
|
||||||
$this->expectException(\InvalidArgumentException::class);
|
|
||||||
|
|
||||||
$sut = $this->getSut('en', []);
|
|
||||||
$sut->getDateFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInvalidLocaleWithGivenLocale()
|
|
||||||
{
|
|
||||||
$this->expectException(\InvalidArgumentException::class);
|
|
||||||
$this->expectExceptionMessage('Unknown locale given: xx');
|
|
||||||
|
|
||||||
$sut = $this->getSut('xx', $this->getDefaultSettings());
|
|
||||||
$sut->getDateFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDurationFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('%h:%m h', $sut->getDurationFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDateFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('de', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('d.m.Y', $sut->getDateFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDateTimeFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('m-d H:i', $sut->getDateTimeFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDateTypeFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('de', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('dd.MM.yyyy', $sut->getDateTypeFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDatePickerFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('YYYY-MM-DD', $sut->getDatePickerFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDateTimeTypeFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('yyyy-MM-dd HH:mm', $sut->getDateTimeTypeFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetDateTimePickerFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('YYYY-MM-DD HH:mm', $sut->getDateTimePickerFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testIs24Hours()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertFalse($sut->isTwentyFourHours());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetTimeFormat()
|
|
||||||
{
|
|
||||||
$sut = $this->getSut('en', $this->getDefaultSettings());
|
|
||||||
$this->assertEquals('H:i:s', $sut->getTimeFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUnknownSetting()
|
|
||||||
{
|
|
||||||
$this->expectException(\InvalidArgumentException::class);
|
|
||||||
$this->expectExceptionMessage('Unknown setting for locale en: date_time_type');
|
|
||||||
|
|
||||||
$sut = $this->getSut('en', ['en' => [
|
|
||||||
'xxx' => 'dd.MM.yyyy HH:mm',
|
|
||||||
]]);
|
|
||||||
$sut->getDateTimePickerFormat();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -650,58 +650,6 @@
|
|||||||
<target>حساب البنك</target>
|
<target>حساب البنك</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>يناير</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>فبراير</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>آذار</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>نيسان</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>مايو</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>يونيو</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>يوليو</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>أغسطس</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>سبتمبر</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>أكتوبر</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>نوفمبر</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>ديسمبر</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -897,58 +897,6 @@
|
|||||||
<target>Vytvořeno %date% s %kimai%</target>
|
<target>Vytvořeno %date% s %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Leden</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Únor</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Březen</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Doben</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Květen</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Červen</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Červenec</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Srpen</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Září</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Říjen</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Listopad</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Prosinec</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -945,58 +945,6 @@
|
|||||||
<target>Oprettet %date% med %kimai%</target>
|
<target>Oprettet %date% med %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Januar</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Februar</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Marts</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>April</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Maj</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Juni</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Juli</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>August</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>September</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Oktober</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>November</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>December</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -1045,58 +1045,6 @@
|
|||||||
<target>Erstellt %date% mit %kimai%</target>
|
<target>Erstellt %date% mit %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Januar</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Februar</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>März</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>April</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Mai</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Juni</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Juli</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>August</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>September</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Oktober</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>November</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Dezember</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -1045,58 +1045,6 @@
|
|||||||
<target>Created %date% with %kimai%</target>
|
<target>Created %date% with %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>January</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>February</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>March</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>April</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>May</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>June</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>July</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>August</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>September</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>October</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>November</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>December</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -1009,58 +1009,6 @@
|
|||||||
<target>Kreita je la %date% per %kimai%</target>
|
<target>Kreita je la %date% per %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Januaro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Februaro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Marto</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Aprilo</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Majo</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Junio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Julio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Aŭgusto</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Septembro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Octobro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Novembro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Decembro</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -913,58 +913,6 @@
|
|||||||
<target>Creado el %date% con %kimai%</target>
|
<target>Creado el %date% con %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Enero</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Febrero</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Marzo</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Abril</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Mayo</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Junio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Julio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Agosto</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Septiembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Octubre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Noviembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Diciembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -953,58 +953,6 @@
|
|||||||
<target>%date%-n sortua %kimai%-ren bitartez</target>
|
<target>%date%-n sortua %kimai%-ren bitartez</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Urtarrila</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Otsaila</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Martxoa</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Apirila</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Maiatza</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Ekaina</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Uztaila</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Abuztua</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Iraila</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Urria</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Azaroa</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Abendua</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -837,58 +837,6 @@
|
|||||||
<target>Généré le %date% avec %kimai%</target>
|
<target>Généré le %date% avec %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Janvier</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Février</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Mars</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Avril</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Mai</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Juin</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Juillet</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Août</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Septembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Octobre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Novembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Decembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -943,54 +943,6 @@
|
|||||||
<source>export.date_copyright</source>
|
<source>export.date_copyright</source>
|
||||||
<target>נוצר ב %date%</target>
|
<target>נוצר ב %date%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="TzrTkzA" resname="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>ינואר</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Ybbwj3q" resname="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>פברואר</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="ZUWOMta" resname="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>מרץ</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="r7dXujQ" resname="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>אפריל</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5U8dGjy" resname="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>מאי</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="S__KfXn" resname="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>יוני</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="c6TKQ2p" resname="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>יולי</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="js3tNpC" resname="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>אוגוסט</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="2thDj5n" resname="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>ספטמבר</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="HshzwEV" resname="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>אוקטובר</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Qi4.VFn" resname="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>נובמבר</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="LXtp3N0" resname="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>דצמבר</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="OxdYMR3" resname="active.entries">
|
<trans-unit id="OxdYMR3" resname="active.entries">
|
||||||
<source>active.entries</source>
|
<source>active.entries</source>
|
||||||
<target>מדדי זמני הפעילות שלך</target>
|
<target>מדדי זמני הפעילות שלך</target>
|
||||||
|
|||||||
@@ -837,58 +837,6 @@
|
|||||||
<target>Készült: %date% A %kimai% segítségével</target>
|
<target>Készült: %date% A %kimai% segítségével</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Január</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Február</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Március</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Április</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Május</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Június</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Július</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Augusztus</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Szeptember</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Október</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>November</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>December</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -985,58 +985,6 @@
|
|||||||
<target>Creato %date% con %kimai%</target>
|
<target>Creato %date% con %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Gennaio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Febbraio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Marzo</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Aprile</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Maggio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Giugno</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Luglio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Agosto</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Settembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Ottobre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Novembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Dicembre</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -837,58 +837,6 @@
|
|||||||
<target>%kimai% で %date% に作成されました</target>
|
<target>%kimai% で %date% に作成されました</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>1 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>2 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>3 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>4 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>5 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>6 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>7 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>8 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>9 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>10 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>11 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>12 月</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -857,58 +857,6 @@
|
|||||||
<target state="translated">생성 %date% 의 %kimai%</target>
|
<target state="translated">생성 %date% 의 %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target state="translated">1월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target state="translated">2월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target state="translated">3월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target state="translated">4월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target state="translated">5월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target state="translated">6월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target state="translated">7월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target state="translated">8월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target state="translated">9월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target state="translated">10월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target state="translated">11월</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target state="translated">12월</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -913,58 +913,6 @@
|
|||||||
<target>Gemaakt op %date% met %kimai%</target>
|
<target>Gemaakt op %date% met %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Januari</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Februari</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Maart</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>April</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Mei</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Juni</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Juli</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Augustus</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>September</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Oktober</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>November</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>December</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -973,58 +973,6 @@
|
|||||||
<target>Utworzono %date% za pomocą %kimai%</target>
|
<target>Utworzono %date% za pomocą %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>styczeń</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Luty</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Marzec</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Kwiecień</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Maj</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Czerwiec</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Lipiec</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Sierpień</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Wrzesień</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Pażdziernik</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Listopad</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Grudzień</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -837,58 +837,6 @@
|
|||||||
<target>Criado %date% com %kimai%</target>
|
<target>Criado %date% com %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Janeiro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Fevereiro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Março</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Abril</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Maio</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Junho</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Julho</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Agosto</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Setembro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Outubro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Novembro</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Dezembro</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -907,54 +907,7 @@
|
|||||||
<source>export.date_copyright</source>
|
<source>export.date_copyright</source>
|
||||||
<target>Creat în %date% cu %kimai%</target>
|
<target>Creat în %date% cu %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="TzrTkzA" resname="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Ianuarie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Ybbwj3q" resname="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Februarie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="ZUWOMta" resname="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Martie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="r7dXujQ" resname="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Aprilie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5U8dGjy" resname="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Mai</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="S__KfXn" resname="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Iunie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="c6TKQ2p" resname="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Iulie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="js3tNpC" resname="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>August</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="2thDj5n" resname="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Septembrie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="HshzwEV" resname="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Octombrie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="Qi4.VFn" resname="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Noiembrie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="LXtp3N0" resname="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Decembrie</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="OxdYMR3" resname="active.entries">
|
<trans-unit id="OxdYMR3" resname="active.entries">
|
||||||
<source>active.entries</source>
|
<source>active.entries</source>
|
||||||
<target>Înregistrările tale active</target>
|
<target>Înregistrările tale active</target>
|
||||||
|
|||||||
@@ -657,58 +657,6 @@
|
|||||||
<target>Номер заказа</target>
|
<target>Номер заказа</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Январь</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Февраль</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Март</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Апрель</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Май</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Июнь</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Июль</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Август</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Сентябрь</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Октябрь</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Ноябрь</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Декабрь</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -990,58 +990,6 @@
|
|||||||
<target>Vytvorené %date%</target>
|
<target>Vytvorené %date%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Január</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Február</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Marec</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Apríl</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Máj</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Jún</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Júl</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>August</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>September</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Október</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>November</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>December</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -832,58 +832,6 @@
|
|||||||
<target state="translated">Skapad %date% med %kimai%</target>
|
<target state="translated">Skapad %date% med %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target state="translated">Januari</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target state="translated">Februari</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target state="translated">Mars</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target state="translated">April</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target state="translated">Maj</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target state="translated">Juni</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target state="translated">Juli</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target state="translated">Augusti</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>September</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target state="translated">Oktober</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>November</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>December</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -913,58 +913,6 @@
|
|||||||
<target state="translated">%date% günü %kimai% ile oluşturuldu.</target>
|
<target state="translated">%date% günü %kimai% ile oluşturuldu.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target state="translated">Ocak</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target state="translated">Şubat</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target state="translated">Mart</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target state="translated">Nisan</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target state="translated">Mayıs</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target state="translated">Haziran</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target state="translated">Temmuz</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target state="translated">Ağustos</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target state="translated">Eylül</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target state="translated">Ekim</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target state="translated">Kasım</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target state="translated">Aralık</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -1025,58 +1025,6 @@
|
|||||||
<target>Tạo %date% với %kimai%</target>
|
<target>Tạo %date% với %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>Tháng 1</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>Tháng 2</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>Tháng 3</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>Tháng 4</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>Tháng 5</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>Tháng 6</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>Tháng 7</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>Tháng 8</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>Tháng 9</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>Tháng 10</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>Tháng 11</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>Tháng 12</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -944,58 +944,6 @@
|
|||||||
<target>创建了 %date% 与 %kimai%</target>
|
<target>创建了 %date% 与 %kimai%</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
|
||||||
<!--
|
|
||||||
Month names
|
|
||||||
-->
|
|
||||||
<trans-unit id="month.1">
|
|
||||||
<source>month.1</source>
|
|
||||||
<target>一月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.2">
|
|
||||||
<source>month.2</source>
|
|
||||||
<target>二月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.3">
|
|
||||||
<source>month.3</source>
|
|
||||||
<target>三月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.4">
|
|
||||||
<source>month.4</source>
|
|
||||||
<target>四月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.5">
|
|
||||||
<source>month.5</source>
|
|
||||||
<target>五月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.6">
|
|
||||||
<source>month.6</source>
|
|
||||||
<target>六月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.7">
|
|
||||||
<source>month.7</source>
|
|
||||||
<target>七月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.8">
|
|
||||||
<source>month.8</source>
|
|
||||||
<target>八月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.9">
|
|
||||||
<source>month.9</source>
|
|
||||||
<target>九月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.10">
|
|
||||||
<source>month.10</source>
|
|
||||||
<target>十月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.11">
|
|
||||||
<source>month.11</source>
|
|
||||||
<target>十一月</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="month.12">
|
|
||||||
<source>month.12</source>
|
|
||||||
<target>十二月</target>
|
|
||||||
</trans-unit>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Navbar - recent entries and activities
|
Navbar - recent entries and activities
|
||||||
-->
|
-->
|
||||||
|
|||||||
Reference in New Issue
Block a user