Skip to content

Commit

Permalink
Merge pull request #1457 from NatLibFi/issue1453-guessLanguage-tests
Browse files Browse the repository at this point in the history
Fixes and unit tests for UI language detection
  • Loading branch information
osma authored May 25, 2023
2 parents a6f1795 + 9817e23 commit f74bc3c
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 16 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"twig/extensions": "1.5.*",
"twbs/bootstrap": "5.1.*",
"twitter/typeahead.js": "v0.11.*",
"willdurand/negotiation": "3.0.*",
"willdurand/negotiation": "3.1.*",
"vakata/jstree": "3.3.*",
"punic/punic": "3.5.1",
"ml/json-ld": "1.*",
Expand Down
13 changes: 8 additions & 5 deletions controller/WebController.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,16 @@ public function __construct($model)
/**
* Guess the language of the user. Return a language string that is one
* of the supported languages defined in the $LANGUAGES setting, e.g. "fi".
* @param Request $request HTTP request
* @param string $vocid identifier for the vocabulary eg. 'yso'.
* @return string returns the language choice as a numeric string value
*/
public function guessLanguage($vocid = null)
public function guessLanguage($request, $vocid = null)
{
// 1. select language based on SKOSMOS_LANGUAGE cookie
if (filter_input(INPUT_COOKIE, 'SKOSMOS_LANGUAGE', FILTER_SANITIZE_FULL_SPECIAL_CHARS)) {
return filter_input(INPUT_COOKIE, 'SKOSMOS_LANGUAGE', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$languageCookie = $request->getCookie('SKOSMOS_LANGUAGE');
if ($languageCookie) {
return $languageCookie;
}

// 2. if vocabulary given, select based on the default language of the vocabulary
Expand All @@ -101,9 +103,10 @@ public function guessLanguage($vocid = null)
$this->negotiator = new \Negotiation\LanguageNegotiator();
$langcodes = array_keys($this->languages);
// using a random language from the configured UI languages when there is no accept language header set
$acceptLanguage = filter_input(INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ? filter_input(INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE', FILTER_SANITIZE_FULL_SPECIAL_CHARS) : $langcodes[0];
$acceptLanguage = $request->getServerConstant('HTTP_ACCEPT_LANGUAGE') ? $request->getServerConstant('HTTP_ACCEPT_LANGUAGE') : $langcodes[0];

$bestLang = $this->negotiator->getBest($acceptLanguage, $langcodes);
if (isset($bestLang) && in_array($bestLang, $langcodes)) {
if (isset($bestLang) && in_array($bestLang->getValue(), $langcodes)) {
return $bestLang->getValue();
}

Expand Down
4 changes: 2 additions & 2 deletions dockerfiles/config/config-docker-compose.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
[ rdfs:label "da" ; rdf:value "da_DK.utf8" ]
[ rdfs:label "de" ; rdf:value "de_DE.utf8" ]
[ rdfs:label "en" ; rdf:value "en_GB.utf8" ]
[ rdfs:label "en_US" ; rdf:value "en_US.utf8" ]
[ rdfs:label "en-US" ; rdf:value "en_US.utf8" ]
[ rdfs:label "es" ; rdf:value "es_ES.utf8" ]
[ rdfs:label "fa" ; rdf:value "fa_IR.utf8" ]
[ rdfs:label "fi" ; rdf:value "fi_FI.utf8" ]
Expand All @@ -48,7 +48,7 @@
[ rdfs:label "nn" ; rdf:value "nn_NO.utf8" ]
[ rdfs:label "pl" ; rdf:value "pl_PL.utf8" ]
[ rdfs:label "pt" ; rdf:value "pt_PT.utf8" ]
[ rdfs:label "pt_BR" ; rdf:value "pt_BR.utf8" ]
[ rdfs:label "pt-BR" ; rdf:value "pt_BR.utf8" ]
[ rdfs:label "ru" ; rdf:value "ru_RU.utf8" ]
[ rdfs:label "sv" ; rdf:value "sv_SE.utf8" ]
[ rdfs:label "zh" ; rdf:value "zh_CN.utf8" ]
Expand Down
4 changes: 2 additions & 2 deletions dockerfiles/config/config-docker.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
[ rdfs:label "da" ; rdf:value "da_DK.utf8" ]
[ rdfs:label "de" ; rdf:value "de_DE.utf8" ]
[ rdfs:label "en" ; rdf:value "en_GB.utf8" ]
[ rdfs:label "en_US" ; rdf:value "en_US.utf8" ]
[ rdfs:label "en-US" ; rdf:value "en_US.utf8" ]
[ rdfs:label "es" ; rdf:value "es_ES.utf8" ]
[ rdfs:label "fa" ; rdf:value "fa_IR.utf8" ]
[ rdfs:label "fi" ; rdf:value "fi_FI.utf8" ]
Expand All @@ -48,7 +48,7 @@
[ rdfs:label "nn" ; rdf:value "nn_NO.utf8" ]
[ rdfs:label "pl" ; rdf:value "pl_PL.utf8" ]
[ rdfs:label "pt" ; rdf:value "pt_PT.utf8" ]
[ rdfs:label "pt_BR" ; rdf:value "pt_BR.utf8" ]
[ rdfs:label "pt-BR" ; rdf:value "pt_BR.utf8" ]
[ rdfs:label "ru" ; rdf:value "ru_RU.utf8" ]
[ rdfs:label "sv" ; rdf:value "sv_SE.utf8" ]
[ rdfs:label "zh" ; rdf:value "zh_CN.utf8" ]
Expand Down
8 changes: 4 additions & 4 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
if (sizeof($parts) <= 2) {
// if language code missing, redirect to guessed language
// in any case, redirect to <lang>/
$lang = sizeof($parts) == 2 && $parts[1] !== '' ? $parts[1] : $controller->guessLanguage();
$lang = sizeof($parts) == 2 && $parts[1] !== '' ? $parts[1] : $controller->guessLanguage($request);
header("Location: " . $lang . "/");
} else {
if (array_key_exists($parts[1], $config->getLanguages())) { // global pages
Expand All @@ -50,12 +50,12 @@
try {
$request->setVocab($parts[1]);
} catch (Exception | ValueError $e) {
$request->setLang($controller->guessLanguage());
$request->setLang($controller->guessLanguage($request));
$controller->invokeGenericErrorPage($request);
return;
}
if (sizeof($parts) == 3) { // language code missing
$lang = $controller->guessLanguage();
$lang = $controller->guessLanguage($request);
$newurl = $controller->getBaseHref() . $vocab . "/" . $lang . "/";
header("Location: " . $newurl);
} else {
Expand Down Expand Up @@ -97,7 +97,7 @@
$controller->invokeGenericErrorPage($request);
}
} else { // language code missing, redirect to some language version
$lang = $controller->guessLanguage($vocab);
$lang = $controller->guessLanguage($request, $vocab);
$newurl = $controller->getBaseHref() . $vocab . "/" . $lang . "/" . implode('/', array_slice($parts, 2));
$qs = $request->getServerConstant('QUERY_STRING');
if ($qs) {
Expand Down
24 changes: 24 additions & 0 deletions model/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Request
private $queryParams;
private $queryParamsPOST;
private $serverConstants;
private $cookies;

/**
* Initializes the Request Object
Expand Down Expand Up @@ -45,6 +46,13 @@ public function __construct($model)
foreach (filter_input_array(INPUT_SERVER) ?: [] as $key => $val) {
$this->serverConstants[$key] = $val;
}

// Store cookies in a local array, so we can mock them in tests.
// We do not apply any filters at this point.
$this->cookies = [];
foreach (filter_input_array(INPUT_COOKIE) ?: [] as $key => $val) {
$this->cookies[$key] = $val;
}
}

/**
Expand All @@ -67,6 +75,16 @@ public function setServerConstant($paramName, $value)
$this->serverConstants[$paramName] = $value;
}

/**
* Set a cookie to mock it in tests.
* @param string $paramName parameter name
* @param string $value parameter value
*/
public function setCookie($paramName, $value)
{
$this->cookies[$paramName] = $value;
}

/**
* Return the requested GET query parameter as a string. Backslashes are stripped for security reasons.
* @param string $paramName parameter name
Expand Down Expand Up @@ -110,6 +128,12 @@ public function getServerConstant($paramName)
return filter_var($this->serverConstants[$paramName], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
}

public function getCookie($paramName)
{
if (!isset($this->cookies[$paramName])) return null;
return filter_var($this->cookies[$paramName], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
}

public function getLang()
{
return $this->lang;
Expand Down
2 changes: 1 addition & 1 deletion tests/GlobalConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function testGetBaseHref()

public function testGetLanguages()
{
$this->assertEquals(array('en' => 'en_GB.utf8'), $this->config->getLanguages());
$this->assertEquals(array('en' => 'en_GB.utf8', 'fi' => 'fi_FI.utf8', 'fr' => 'fr_FR.utf8'), $this->config->getLanguages());
}

public function testGetSearchResultsSize()
Expand Down
36 changes: 36 additions & 0 deletions tests/WebControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,40 @@ public function testFormatChangeList() {
$expected = array ('hurr durr' => array ('uri' => 'http://www.skosmos.skos/changes/d3', 'prefLabel' => 'Hurr Durr', 'date' => DateTime::__set_state(array('date' => '2010-02-12 10:26:39.000000', 'timezone_type' => 3, 'timezone' => 'UTC')), 'datestring' => 'Feb 12, 2010'), 'second date' => array ('uri' => 'http://www.skosmos.skos/changes/d2', 'prefLabel' => 'Second date', 'date' => DateTime::__set_state(array('date' => '2010-02-12 15:26:39.000000', 'timezone_type' => 3, 'timezone' => 'UTC')), 'datestring' => 'Feb 12, 2010'));
$this->assertEquals($expected, $months['February 2010']);
}

public function testGuessLanguageFirstInConfig() {
$request = new Request($this->model);
$guessedLanguage = $this->webController->guessLanguage($request);
$this->assertEquals($guessedLanguage, 'en');
}

public function testGuessLanguageCookie() {
$request = new Request($this->model);
$request->setCookie('SKOSMOS_LANGUAGE', 'fr');
$guessedLanguage = $this->webController->guessLanguage($request);
$this->assertEquals($guessedLanguage, 'fr');
}

public function testGuessLanguageVocabDefault() {
$request = new Request($this->model);
$guessedLanguage = $this->webController->guessLanguage($request, 'groups');
$this->assertEquals($guessedLanguage, 'fi');
}

public function testGuessLanguageAcceptLanguageSimple() {
$request = new Request($this->model);
$request->setServerConstant('HTTP_ACCEPT_LANGUAGE', 'fr');
$guessedLanguage = $this->webController->guessLanguage($request);
$this->assertEquals($guessedLanguage, 'fr');
}

public function testGuessLanguageAcceptLanguageBestMatch() {
$request = new Request($this->model);
$request->setServerConstant('HTTP_ACCEPT_LANGUAGE', 'sv, de;q=0.9, fi;q=0.8, fr;q=0.5');
$guessedLanguage = $this->webController->guessLanguage($request);
// configured/available languages are en, fi, fr
// the best matching language for the given Accept-Language is fi
$this->assertEquals($guessedLanguage, 'fi');
}

}
4 changes: 3 additions & 1 deletion tests/testconfig.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
# customize the base element. Set this if the automatic base url detection doesn't work. For example setups behind a proxy.
skosmos:baseHref "http://tests.localhost/Skosmos/" ;
# interface languages available, and the corresponding system locales
skosmos:languages ( [ rdfs:label "en" ; rdf:value "en_GB.utf8" ] ) ;
skosmos:languages ( [ rdfs:label "en" ; rdf:value "en_GB.utf8" ]
[ rdfs:label "fi" ; rdf:value "fi_FI.utf8" ]
[ rdfs:label "fr" ; rdf:value "fr_FR.utf8" ] ) ;
# how many results (maximum) to load at a time on the search results page
skosmos:searchResultsSize 5 ;
# how many items (maximum) to retrieve in transitive property queries
Expand Down

0 comments on commit f74bc3c

Please sign in to comment.