Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b1525a3

Browse files
committed
[HttpFoundation] Similar locale selection
Allow the nearest locale to be selected instead the default one.
1 parent a3686fb commit b1525a3

File tree

2 files changed

+79
-45
lines changed

2 files changed

+79
-45
lines changed

src/Symfony/Component/HttpFoundation/Request.php

+49-26
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,7 @@ public function getPreferredFormat(?string $default = 'html'): ?string
15271527
public function getPreferredLanguage(array $locales = null): ?string
15281528
{
15291529
$preferredLanguages = $this->getLanguages();
1530+
$locales = array_map([$this, 'formatLocale'], $locales ?? []);
15301531

15311532
if (empty($locales)) {
15321533
return $preferredLanguages[0] ?? null;
@@ -1536,20 +1537,35 @@ public function getPreferredLanguage(array $locales = null): ?string
15361537
return $locales[0];
15371538
}
15381539

1539-
$extendedPreferredLanguages = [];
1540+
//First, we try to find an exact match
1541+
foreach ($preferredLanguages as $language) {
1542+
if (\in_array($language, $locales, true)) {
1543+
return $language;
1544+
}
1545+
}
1546+
1547+
//Second, we try to find a locale match
1548+
$preferredLocales = [];
15401549
foreach ($preferredLanguages as $language) {
1541-
$extendedPreferredLanguages[] = $language;
15421550
if (false !== $position = strpos($language, '_')) {
1543-
$superLanguage = substr($language, 0, $position);
1544-
if (!\in_array($superLanguage, $preferredLanguages)) {
1545-
$extendedPreferredLanguages[] = $superLanguage;
1551+
$locale = substr($language, 0, $position);
1552+
if (!\in_array($locale, $preferredLanguages)) {
1553+
$preferredLocales[] = $locale;
15461554
}
1555+
} else {
1556+
$preferredLocales[] = $language;
15471557
}
15481558
}
15491559

1550-
$preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
1560+
foreach ($preferredLocales as $preferredLocale) {
1561+
foreach ($locales as $locale) {
1562+
if (str_starts_with($locale, $preferredLocale)) {
1563+
return $locale;
1564+
}
1565+
}
1566+
}
15511567

1552-
return $preferredLanguages[0] ?? $locales[0];
1568+
return $locales[0];
15531569
}
15541570

15551571
/**
@@ -1567,32 +1583,39 @@ public function getLanguages(): array
15671583
$this->languages = [];
15681584
foreach ($languages as $acceptHeaderItem) {
15691585
$lang = $acceptHeaderItem->getValue();
1570-
if (str_contains($lang, '-')) {
1571-
$codes = explode('-', $lang);
1572-
if ('i' === $codes[0]) {
1573-
// Language not listed in ISO 639 that are not variants
1574-
// of any listed language, which can be registered with the
1575-
// i-prefix, such as i-cherokee
1576-
if (\count($codes) > 1) {
1577-
$lang = $codes[1];
1578-
}
1579-
} else {
1580-
for ($i = 0, $max = \count($codes); $i < $max; ++$i) {
1581-
if (0 === $i) {
1582-
$lang = strtolower($codes[0]);
1583-
} else {
1584-
$lang .= '_'.strtoupper($codes[$i]);
1585-
}
1586-
}
1587-
}
1588-
}
1586+
$lang = $this->formatLocale($lang);
15891587

15901588
$this->languages[] = $lang;
15911589
}
15921590

15931591
return $this->languages;
15941592
}
15951593

1594+
private static function formatLocale(string $locale): string
1595+
{
1596+
if (str_contains($locale, '-')) {
1597+
$codes = explode('-', $locale);
1598+
if ('i' === $codes[0]) {
1599+
// Language not listed in ISO 639 that are not variants
1600+
// of any listed language, which can be registered with the
1601+
// i-prefix, such as i-cherokee
1602+
if (\count($codes) > 1) {
1603+
$locale = $codes[1];
1604+
}
1605+
} else {
1606+
for ($i = 0, $max = \count($codes); $i < $max; ++$i) {
1607+
if (0 === $i) {
1608+
$locale = strtolower($codes[0]);
1609+
} else {
1610+
$locale .= '_'.strtoupper($codes[$i]);
1611+
}
1612+
}
1613+
}
1614+
}
1615+
1616+
return $locale;
1617+
}
1618+
15961619
/**
15971620
* Gets a list of charsets acceptable by the client browser in preferable order.
15981621
*

src/Symfony/Component/HttpFoundation/Tests/RequestTest.php

+30-19
Original file line numberDiff line numberDiff line change
@@ -1502,27 +1502,38 @@ public function testGetPreferredLanguage()
15021502
{
15031503
$request = new Request();
15041504
$this->assertNull($request->getPreferredLanguage());
1505-
$this->assertNull($request->getPreferredLanguage([]));
1506-
$this->assertEquals('fr', $request->getPreferredLanguage(['fr']));
1507-
$this->assertEquals('fr', $request->getPreferredLanguage(['fr', 'en']));
1508-
$this->assertEquals('en', $request->getPreferredLanguage(['en', 'fr']));
1509-
$this->assertEquals('fr-ch', $request->getPreferredLanguage(['fr-ch', 'fr-fr']));
1510-
1511-
$request = new Request();
1512-
$request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
1513-
$this->assertEquals('en', $request->getPreferredLanguage(['en', 'en-us']));
1514-
1515-
$request = new Request();
1516-
$request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
1517-
$this->assertEquals('en', $request->getPreferredLanguage(['fr', 'en']));
1518-
1519-
$request = new Request();
1520-
$request->headers->set('Accept-language', 'zh, en-us; q=0.8');
1521-
$this->assertEquals('en', $request->getPreferredLanguage(['fr', 'en']));
1505+
}
15221506

1507+
/**
1508+
* @dataProvider providePreferredLanguage
1509+
*/
1510+
public function testPreferredLanguageWithLocales(?string $expectedLocale, ?string $acceptLanguage, array $locales)
1511+
{
15231512
$request = new Request();
1524-
$request->headers->set('Accept-language', 'zh, en-us; q=0.8, fr-fr; q=0.6, fr; q=0.5');
1525-
$this->assertEquals('en', $request->getPreferredLanguage(['fr', 'en']));
1513+
if ($acceptLanguage) {
1514+
$request->headers->set('Accept-language', $acceptLanguage);
1515+
}
1516+
$this->assertSame($expectedLocale, $request->getPreferredLanguage($locales));
1517+
}
1518+
1519+
public static function providePreferredLanguage(): iterable
1520+
{
1521+
yield '"es_PA" is selected as no supported locale is set' => ['es_PA', 'es-pa, en-us; q=0.8, en; q=0.6', []];
1522+
yield 'No supported locales' => [null, null, []];
1523+
yield '"fr" selected as first choice when no header is present' => ['fr', null, ['fr', 'en']];
1524+
yield '"en" selected as first choice when no header is present' => ['en', null, ['en', 'fr']];
1525+
yield '"fr_CH" selected as first choice when no header is present' => ['fr_CH', null, ['fr-ch', 'fr-fr']];
1526+
yield '"en_US" is selected as an exact match is found (1)' => ['en_US', 'zh, en-us; q=0.8, en; q=0.6', ['en', 'en-us']];
1527+
yield '"en_US" is selected as an exact match is found (2)' => ['en_US', 'ja-JP,fr_CA;q=0.7,fr;q=0.5,en_US;q=0.3', ['en_US', 'fr_FR']];
1528+
yield '"en" is selected as an exact match is found' => ['en', 'zh, en-us; q=0.8, en; q=0.6', ['fr', 'en']];
1529+
yield '"fr" is selected as an exact match is found' => ['fr', 'zh, en-us; q=0.8, fr-fr; q=0.6, fr; q=0.5', ['fr', 'en']];
1530+
yield '"en" is selected as "en-us" is a similar dialect' => ['en', 'zh, en-us; q=0.8', ['fr', 'en']];
1531+
yield '"fr_FR" is selected as "fr_CA" is a similar dialect (1)' => ['fr_FR', 'ja-JP,fr_CA;q=0.7,fr;q=0.5', ['en_US', 'fr_FR']];
1532+
yield '"fr_FR" is selected as "fr_CA" is a similar dialect (2)' => ['fr_FR', 'ja-JP,fr_CA;q=0.7', ['en_US', 'fr_FR']];
1533+
yield '"fr_FR" is selected as "fr" is a similar dialect' => ['fr_FR', 'ja-JP,fr;q=0.5', ['en_US', 'fr_FR']];
1534+
yield '"fr_FR" is selected as "fr_CA" is a similar dialect and has a greater "q" compared to "en_US" (2)' => ['fr_FR', 'ja-JP,fr_CA;q=0.7,ru-ru;q=0.3', ['en_US', 'fr_FR']];
1535+
yield '"en_US" is selected it is an exact match' => ['en_US', 'ja-JP,fr;q=0.5,en_US;q=0.3', ['en_US', 'fr_FR']];
1536+
yield '"fr_FR" is selected as "fr_CA" is a similar dialect and has a greater "q" compared to "en"' => ['fr_FR', 'ja-JP,fr_CA;q=0.7,en;q=0.5', ['en_US', 'fr_FR']];
15261537
}
15271538

15281539
public function testIsXmlHttpRequest()

0 commit comments

Comments
 (0)