Skip to content

Commit

Permalink
Merge pull request #2587 from kylekatarnls/fix/mongolian-declension-f…
Browse files Browse the repository at this point in the history
…or-sequences

Allow grammar rule customization for unit declensions
  • Loading branch information
kylekatarnls authored Apr 13, 2022
2 parents 9e4a286 + 634327c commit e06036c
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 14 deletions.
43 changes: 32 additions & 11 deletions src/Carbon/CarbonInterval.php
Original file line number Diff line number Diff line change
Expand Up @@ -1620,18 +1620,23 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options
$unit = $short ? 's' : 'second';
$isFuture = $this->invert === 1;
$transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
$declensionMode = null;

/** @var \Symfony\Component\Translation\Translator $translator */
$translator = $this->getLocalTranslator();

$handleDeclensions = function ($unit, $count) use ($interpolations, $transId, $translator, $altNumbers, $absolute) {
$handleDeclensions = function ($unit, $count, $index = 0, $parts = 1) use ($interpolations, $transId, $translator, $altNumbers, $absolute, &$declensionMode) {
if (!$absolute) {
// Some languages have special pluralization for past and future tense.
$key = $unit.'_'.$transId;
$result = $this->translate($key, $interpolations, $count, $translator, $altNumbers);
$declensionMode = $declensionMode ?? $this->translate($transId.'_mode');

if ($result !== $key) {
return $result;
if ($this->needsDeclension($declensionMode, $index, $parts)) {
// Some languages have special pluralization for past and future tense.
$key = $unit.'_'.$transId;
$result = $this->translate($key, $interpolations, $count, $translator, $altNumbers);

if ($result !== $key) {
return $result;
}
}
}

Expand Down Expand Up @@ -1696,25 +1701,25 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options
}
}

$transChoice = function ($short, $unitData) use ($absolute, $handleDeclensions, $translator, $aUnit, $altNumbers, $interpolations) {
$transChoice = function ($short, $unitData, $index, $parts) use ($absolute, $handleDeclensions, $translator, $aUnit, $altNumbers, $interpolations) {
$count = $unitData['value'];

if ($short) {
$result = $handleDeclensions($unitData['unitShort'], $count);
$result = $handleDeclensions($unitData['unitShort'], $count, $index, $parts);

if ($result !== null) {
return $result;
}
} elseif ($aUnit) {
$result = $handleDeclensions('a_'.$unitData['unit'], $count);
$result = $handleDeclensions('a_'.$unitData['unit'], $count, $index, $parts);

if ($result !== null) {
return $result;
}
}

if (!$absolute) {
return $handleDeclensions($unitData['unit'], $count);
return $handleDeclensions($unitData['unit'], $count, $index, $parts);
}

return $this->translate($unitData['unit'], $interpolations, $count, $translator, $altNumbers);
Expand All @@ -1726,7 +1731,7 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options
if ($diffIntervalData['value'] > 0) {
$unit = $short ? $diffIntervalData['unitShort'] : $diffIntervalData['unit'];
$count = $diffIntervalData['value'];
$interval[] = $transChoice($short, $diffIntervalData);
$interval[] = [$short, $diffIntervalData];
} elseif ($options & CarbonInterface::SEQUENTIAL_PARTS_ONLY && \count($interval) > 0) {
break;
}
Expand All @@ -1744,6 +1749,12 @@ public function forHumans($syntax = null, $short = false, $parts = -1, $options
}
}

$actualParts = \count($interval);

foreach ($interval as $index => &$item) {
$item = $transChoice($item[0], $item[1], $index, $actualParts);
}

if (\count($interval) === 0) {
if ($relativeToNow && $options & CarbonInterface::JUST_NOW) {
$key = 'diff_now';
Expand Down Expand Up @@ -2752,4 +2763,14 @@ public function ceil($precision = 1)
{
return $this->round($precision, 'ceil');
}

private function needsDeclension(string $mode, int $index, int $parts): bool
{
switch ($mode) {
case 'last':
return $index === $parts - 1;
default:
return true;
}
}
}
5 changes: 4 additions & 1 deletion src/Carbon/Lang/mn.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
'second' => ':count секунд',
's' => ':countс',

'ago_mode' => 'last',
'ago' => ':time өмнө',
'year_ago' => ':count жилийн',
'y_ago' => ':count жилийн',
Expand All @@ -57,6 +58,7 @@
'minute_ago' => ':count минутын',
'second_ago' => ':count секундын',

'from_now_mode' => 'last',
'from_now' => 'одоогоос :time',
'year_from_now' => ':count жилийн дараа',
'y_from_now' => ':count жилийн дараа',
Expand All @@ -68,7 +70,7 @@
'minute_from_now' => ':count минутын дараа',
'second_from_now' => ':count секундын дараа',

// Does it required to make translation for before, after as follows? hmm, I think we've made it with ago and from now keywords already. Anyway, I've included it just in case of undesired action...
'after_mode' => 'last',
'after' => ':time дараа',
'year_after' => ':count жилийн',
'y_after' => ':count жилийн',
Expand All @@ -80,6 +82,7 @@
'minute_after' => ':count минутын',
'second_after' => ':count секундын',

'before_mode' => 'last',
'before' => ':time өмнө',
'year_before' => ':count жилийн',
'y_before' => ':count жилийн',
Expand Down
24 changes: 24 additions & 0 deletions tests/Carbon/LocalizationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -914,4 +914,28 @@ public function testAustriaGermanJanuary()
Carbon::parse('2020-02-15')->locale('de')->monthName
);
}

public function testDeclensionModes()
{
$this->assertSame(
'2 жил 3 сар 1 өдөр 1с өмнө',
Carbon::now()
->subYears(2)
->subMonths(3)
->subDay()
->subSecond()
->locale('mn')
->diffForHumans(null, null, true, 4)
);
$this->assertSame(
'2 жил 3 сар 1 өдөр 1 секундын өмнө',
Carbon::now()
->subYears(2)
->subMonths(3)
->subDay()
->subSecond()
->locale('mn')
->diffForHumans(null, null, false, 4)
);
}
}
24 changes: 24 additions & 0 deletions tests/CarbonImmutable/LocalizationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -864,4 +864,28 @@ public function testPolishDeclensions()
$this->assertSame('za minutę', $minute->diffForHumans(['aUnit' => true]));
$this->assertSame('za sekundę', $second->translate('from_now', ['time' => 'sekunda']));
}

public function testDeclensionModes()
{
$this->assertSame(
'2 жил 3 сар 1 өдөр 1с өмнө',
Carbon::now()
->subYears(2)
->subMonths(3)
->subDay()
->subSecond()
->locale('mn')
->diffForHumans(null, null, true, 4)
);
$this->assertSame(
'2 жил 3 сар 1 өдөр 1 секундын өмнө',
Carbon::now()
->subYears(2)
->subMonths(3)
->subDay()
->subSecond()
->locale('mn')
->diffForHumans(null, null, false, 4)
);
}
}
2 changes: 1 addition & 1 deletion tests/Localization/MnMnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class MnMnTest extends LocalizationTestCase
// Carbon::now()->subMonths(5)->diffForHumans(null, null, true, 4)
'5 сарын өмнө',
// Carbon::now()->subYears(2)->subMonths(3)->subDay()->subSecond()->diffForHumans(null, null, true, 4)
'2 жилийн 3 сарын 1 хоногийн 1с өмнө',
'2 жил 3 сар 1 өдөр 1с өмнө',
// Carbon::now()->addWeek()->addHours(10)->diffForHumans(null, true, false, 2)
'1 долоо хоног 10 цаг',
// Carbon::now()->addWeek()->addDays(6)->diffForHumans(null, true, false, 2)
Expand Down
2 changes: 1 addition & 1 deletion tests/Localization/MnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class MnTest extends LocalizationTestCase
// Carbon::now()->subMonths(5)->diffForHumans(null, null, true, 4)
'5 сарын өмнө',
// Carbon::now()->subYears(2)->subMonths(3)->subDay()->subSecond()->diffForHumans(null, null, true, 4)
'2 жилийн 3 сарын 1 хоногийн 1с өмнө',
'2 жил 3 сар 1 өдөр 1с өмнө',
// Carbon::now()->addWeek()->addHours(10)->diffForHumans(null, true, false, 2)
'1 долоо хоног 10 цаг',
// Carbon::now()->addWeek()->addDays(6)->diffForHumans(null, true, false, 2)
Expand Down

0 comments on commit e06036c

Please sign in to comment.