Skip to content

Commit

Permalink
EZP-29932: As a Developer I want list of Languages based on User's br…
Browse files Browse the repository at this point in the history
…owser settings (ezsystems#2507)

* EZP-29932: As a Developer I want list of Languages based on User's browser settings

* EZP-29932: proper kernel docblocks

* EZP-29925: Extract UserLanguagePreferenceProviderInterface

* EZP-29932: Add fallback for getName and getDescription when mainLanguageCode is not provided
  • Loading branch information
ViniTou authored and Łukasz Serwatka committed Dec 21, 2018
1 parent e130e8c commit 8fa8663
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 2 deletions.
46 changes: 46 additions & 0 deletions eZ/Bundle/EzPublishCoreBundle/Resources/config/locale.yml
Expand Up @@ -44,6 +44,47 @@ parameters:
tur-TR: tr_TR
ukr-UA: uk_UA

ezpublish.locale.browser_map:
au: ['eng-AU']
be: ['fre-BE']
br: ['por-BR']
ca: ['eng-CA']
cn: ['chi-CN']
cz: ['cze-CZ']
de: ['ger-DE']
dk: ['dan-DK']
en: ['eng-GB', 'eng-US']
en-us: ['eng-US']
es: ['esl-ES']
fi: ['fin-FI']
fr: ['fre-FR']
gb: ['eng-GB']
gr: ['ell-GR']
hk: ['chi-HK']
hr: ['cro-HR']
hu: ['hun-HU']
id: ['ind-ID']
il: ['heb-IL']
in: ['hin-IN']
it: ['ita-IT']
jp: ['jpn-JP']
kr: ['kor-KR']
mx: ['esl-MX']
mz: ['por-MZ']
nl: ['dut-NL']
no: ['nor-NO']
nz: ['eng-NZ']
pl: ['pol-PL']
pt: ['por-PT']
rs: ['srp-RS']
ru: ['rus-RU']
sa: ['ara-SA']
se: ['swe-SE']
sk: ['slk-SK']
tr: ['tur-TR']
tw: ['chi-TW']
ua: ['ukr-UA']

ezpublish.locale.converter.class: eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverter

services:
Expand All @@ -57,3 +98,8 @@ services:
arguments: ["@request_stack", "%kernel.default_locale%", "@?router"]
tags:
- { name: kernel.event_subscriber }

eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider:
autowire: true
arguments:
$languageCodesMap: '%ezpublish.locale.browser_map%'
@@ -0,0 +1,89 @@
<?php

/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace eZ\Publish\Core\MVC\Symfony\Locale\Tests;

use eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\HeaderBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Yaml\Yaml;

class UserLanguagePreferenceProviderTest extends TestCase
{
/**
* @var \eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProviderInterface
*/
private $userLanguagePreferenceProvider;

/**
* @var \PHPUnit\Framework\MockObject\MockObject|\Symfony\Component\HttpFoundation\RequestStack
*/
private $requestStackMock;

public function setUp()
{
$this->requestStackMock = $this->createMock(RequestStack::class);

$this->userLanguagePreferenceProvider = new UserLanguagePreferenceProvider(
$this->requestStackMock,
$this->getLanguageCodesMap()
);
}

/**
* @dataProvider providerForTestGetPreferredLanguages
*
* @param array $userLanguages
* @param array $expectedEzLanguageCodes
*/
public function testGetPreferredLanguages(array $userLanguages, array $expectedEzLanguageCodes)
{
$request = new Request();
$request->headers = new HeaderBag(
[
'Accept-Language' => implode(', ', $userLanguages),
]
);
$this
->requestStackMock
->expects($this->once())
->method('getCurrentRequest')
->willReturn($request);

self::assertEquals(
$expectedEzLanguageCodes,
$this->userLanguagePreferenceProvider->getPreferredLanguages()
);
}

/**
* @see testGetPreferredLanguages
*
* @return array
*/
public function providerForTestGetPreferredLanguages(): array
{
return [
[['pl'], ['pol-PL']],
[['pol-PL'], ['pol-PL']],
[['fr'], ['fre-FR']],
[['en'], ['eng-GB', 'eng-US']],
];
}

private function getLanguageCodesMap(): array
{
$config = Yaml::parseFile(
realpath(dirname(__DIR__, 6) . '/Bundle/EzPublishCoreBundle/Resources/config/locale.yml')
);

return $config['parameters']['ezpublish.locale.browser_map'];
}
}
@@ -0,0 +1,70 @@
<?php

/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace eZ\Publish\Core\MVC\Symfony\Locale;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

class UserLanguagePreferenceProvider implements UserLanguagePreferenceProviderInterface
{
/**
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
private $requestStack;

/**
* @var array
*/
private $languageCodesMap;

/**
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* @param array $languageCodesMap
*/
public function __construct(RequestStack $requestStack, array $languageCodesMap)
{
$this->requestStack = $requestStack;
$this->languageCodesMap = $languageCodesMap;
}

public function getPreferredLocales(Request $request = null): array
{
$request = $request ?? $this->requestStack->getCurrentRequest();
$preferredLocales = $request->headers->get('Accept-Language') ?? '';
$preferredLocales = array_reduce(
explode(',', $preferredLocales),
function (array $result, string $languageWithQuality) {
[$language, $quality] = array_merge(explode(';q=', $languageWithQuality), [1]);
$result[$language] = (float) $quality;

return $result;
},
[]
);
arsort($preferredLocales);

return array_keys($preferredLocales);
}

public function getPreferredLanguages(): array
{
$languageCodes = [];
foreach ($this->getPreferredLocales() as $locale) {
$locale = strtolower($locale);
if (isset($this->languageCodesMap[$locale])) {
$languageCodes = array_merge($languageCodes, $this->languageCodesMap[$locale]);
} elseif (preg_match('/^([a-z]{3})-([a-z]{2})$/', $locale, $matches)) {
// if the given locale is already in the eZ format
$languageCodes[] = strtolower($matches[1]) . '-' . strtoupper($matches[2]);
}
}

return array_unique($languageCodes);
}
}
@@ -0,0 +1,33 @@
<?php

/**
* @copyright Copyright (C) eZ Systems AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace eZ\Publish\Core\MVC\Symfony\Locale;

use Symfony\Component\HttpFoundation\Request;

/**
* Provides list of user-preferred languages.
*/
interface UserLanguagePreferenceProviderInterface
{
/**
* Return a list of user's browser preferred locales directly from Accept-Language header.
*
* @param \Symfony\Component\HttpFoundation\Request request to retrieve information from, use current if null
*
* @return string[]
*/
public function getPreferredLocales(Request $request = null): array;

/**
* List of eZ Language codes.
*
* @return string[]
*/
public function getPreferredLanguages(): array;
}
Expand Up @@ -43,6 +43,8 @@ public function getDescription($languageCode = null)
}
}

return $this->descriptions[$this->mainLanguageCode];
return isset($this->descriptions[$this->mainLanguageCode])
? $this->descriptions[$this->mainLanguageCode]
: reset($this->descriptions);
}
}
4 changes: 3 additions & 1 deletion eZ/Publish/Core/Repository/Values/MultiLanguageNameTrait.php
Expand Up @@ -43,6 +43,8 @@ public function getName($languageCode = null)
}
}

return $this->names[$this->mainLanguageCode];
return isset($this->names[$this->mainLanguageCode])
? $this->names[$this->mainLanguageCode]
: reset($this->names);
}
}

0 comments on commit 8fa8663

Please sign in to comment.