Skip to content

Commit

Permalink
locale
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Jan 9, 2024
1 parent f1fbaa8 commit afafcc6
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 19 deletions.
7 changes: 4 additions & 3 deletions src/Latte/Engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Engine
private ?Policy $policy = null;
private bool $sandboxed = false;
private ?string $phpBinary = null;
private ?string $locale = null;
private ?Locale $locale = null;


public function __construct()
Expand Down Expand Up @@ -552,12 +552,13 @@ public function isStrictParsing(): bool

public function setLocale(?string $locale): static
{
$this->locale = $locale;
$this->locale = $locale ? new Locale($locale) : null;
return $this;
}


public function getLocale(): ?string
/** @internal */
public function getLocale(): ?Locale
{
return $this->locale;
}
Expand Down
2 changes: 2 additions & 0 deletions src/Latte/Essential/CoreExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ public function getFilters(): array
? [$this->filters, 'lower']
: fn() => throw new RuntimeException('Filter |lower requires mbstring extension.'),
'number' => [$this->filters, 'number'],
'numberInWords' => [$this->filters, 'numberInWords'],
'orderNumber' => [$this->filters, 'orderNumber'],
'padLeft' => [$this->filters, 'padLeft'],
'padRight' => [$this->filters, 'padRight'],
'query' => [$this->filters, 'query'],
Expand Down
85 changes: 69 additions & 16 deletions src/Latte/Essential/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,11 @@ public static function date(string|int|\DateTimeInterface|\DateInterval|null $ti
* Local date/time formatting.
* https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
*/
public static function localDate(string|int|\DateTimeInterface|null $time, string $format = 'date'): ?string
public function localDate(string|int|\DateTimeInterface|null $time, string $format = 'date'): ?string
{
if (!class_exists(\IntlDateFormatter::class)) {
throw new Latte\RuntimeException("Filters |localDate and |localTime requires 'intl' extension.");
$locale = $this->engine->getLocale();
if (!$locale) {
throw new Latte\RuntimeException('Filters |localDate and |localTime require the locale to be set using Engine::setLocale()');
} elseif ($time == null) { // intentionally ==
return null;
} elseif (is_numeric($time)) {
Expand All @@ -229,27 +230,78 @@ public static function localDate(string|int|\DateTimeInterface|null $time, strin
'full' => [\IntlDateFormatter::FULL, \IntlDateFormatter::NONE],
default => $format,
};
$res = $locale->getDateFormatter()->format($time);
$res = \IntlDateFormatter::formatObject($time, $format);
$res = preg_replace('~(\d\.) ~', "\$1\u{a0}", $res);
return $res;
}


/**
* Formats a number with grouped thousands and optionally decimal digits.
* Formats a number with grouped thousands and optionally decimal digits according to locale.
*/
public function number(
float $num,
float $number,
int $decimals = 0,
?string $decimalSeparator = '.',
?string $thousandsSeparator = ',',
string $decimalSeparator = '.',
string $thousandsSeparator = ',',
): string
{
if ($locale = $this->engine->getLocale()) {
$formatter = clone $locale->getNumberFormatter();
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $decimals);
return $formatter->format($number);
}

return number_format($number, $decimals, $decimalSeparator, $thousandsSeparator);
}


/**
* Prints the order number according to locale.
*/
public function orderNumber(float $number): string
{
$locale = $this->engine->getLocale();
if (!$locale) {
throw new Latte\RuntimeException('Filter |orderNumber requires the locale to be set using Engine::setLocale()');
}

return number_format($num, $decimals, $decimalSeparator, $thousandsSeparator);
$formatter = new \NumberFormatter($locale->getNumberFormatter()->getLocale(), \NumberFormatter::ORDINAL);
return $formatter->format($number);
}


/**
* Prints the number in words according to locale.
*/
public function numberInWords(float $number): string
{
$locale = $this->engine->getLocale();
if (!$locale) {
throw new Latte\RuntimeException('Filter |numberInWords requires the locale to be set using Engine::setLocale()');
}

$formatter = new \NumberFormatter($locale->getNumberFormatter()->getLocale(), \NumberFormatter::SPELLOUT);
return $formatter->format($number);
}


/**
* Format the currency value according to locale.
*/
public function money(
float $amount,
?string $currency = null,
): string
{
$locale = $this->engine->getLocale();
if (!$locale) {
throw new Latte\RuntimeException('Filter |money requires the locale to be set using Engine::setLocale()');
}
return $currency === null
? $locale->getMoneyFormatter()->format($amount, \NumberFormatter::TYPE_DEFAULT)
: $locale->getMoneyFormatter()->formatCurrency($amount, $currency);
}


Expand Down Expand Up @@ -510,22 +562,23 @@ public static function batch(iterable $list, int $length, $rest = null): \Genera
/**
* Sorts elements using the comparison function and preserves the key association.
*/
public static function sort(
public function sort(
iterable $iterable,
?\Closure $comparison = null,
bool $byKey = false,
string|int|\Closure|null $by = null,
): iterable
{
if (!class_exists(\Collator::class)) {
throw new Latte\RuntimeException("Sort filter requires the 'intl' extension to be installed.");
if ($comparison) {
} elseif ($locale = $this->engine->getLocale()) {
$collator = $locale->getCollator();
$comparison = fn($a, $b) => is_string($a) && is_string($b)
? $collator->compare($a, $b)
: $a <=> $b;
} else {
$comparison = fn($a, $b) => $a <=> $b;
}

$collator = new \Collator($locale);
$comparison ??= fn($a, $b) => is_string($a) && is_string($b)
? $collator->compare($a, $b)
: $a <=> $b;

$comparison = match (true) {
$by === null => $comparison,
$by instanceof \Closure => fn($a, $b) => $comparison($by($a), $by($b)),
Expand Down
54 changes: 54 additions & 0 deletions src/Latte/Locale.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

/**
* This file is part of the Latte (https://latte.nette.org)
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Latte;


final class Locale
{
// currency: $fmt->setTextAttribute(NumberFormatter::CURRENCY_CODE, 'EUR');

private \Collator $collator;
private \NumberFormatter $numberFormatter;
private \NumberFormatter $moneyFormatter;
private \IntlDateFormatter $dateFormatter;


public function __construct(
private string $locale,
) {
if (!extension_loaded('intl')) {
throw new RuntimeException("Locate requires the 'intl' extension to be installed.");
}
}


public function getCollator(): \Collator
{
return $this->collator ??= new \Collator($this->locale);
}


public function getNumberFormatter(): \NumberFormatter
{
return $this->numberFormatter ??= new \NumberFormatter($this->locale, \NumberFormatter::DECIMAL);
}


public function getMoneyFormatter(): \NumberFormatter
{
return $this->moneyFormatter ??= new \NumberFormatter($this->locale, \NumberFormatter::CURRENCY);
}


public function getDateFormatter(): \IntlDateFormatter
{
return $this->dateFormatter ??= new \IntlDateFormatter($this->locale, \IntlDateFormatter::FULL, \IntlDateFormatter::FULL);
}
}

0 comments on commit afafcc6

Please sign in to comment.