Preliminary support for Accept-Language: header

In the absence of the "userlang" query parameter in the URL, the value
of the "Accept-Language" header is used. However, it is assumed that
"Accept-Language" specifies a single language (rather than a comma
separated list of languages possibly weighted with quality values).

Example:

Accept-Language: fr
// should work

Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5
// The requested language will be considered to be
// "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5".
// The i18n code will fail to find resources for such a language
// and will use the default "en" instead.
This commit is contained in:
Veloman Yunkan 2022-04-09 13:32:34 +04:00 committed by Matthieu Gautier
parent 9987fbd488
commit 927c12574a
2 changed files with 82 additions and 2 deletions

View File

@ -195,7 +195,15 @@ std::string RequestContext::get_query() const {
std::string RequestContext::get_user_language() const
{
return get_optional_param<std::string>("userlang", "en");
try {
return get_argument("userlang");
} catch(const std::out_of_range&) {}
try {
return get_header("Accept-Language");
} catch(const std::out_of_range&) {}
return "en";
}
}

View File

@ -57,7 +57,6 @@ std::string removeEOLWhitespaceMarkers(const std::string& s)
return std::regex_replace(s, pattern, "");
}
class ZimFileServer
{
public: // types
@ -879,6 +878,79 @@ TEST_F(ServerTest, 500)
EXPECT_EQ(r->body, expectedBody);
}
TEST_F(ServerTest, UserLanguageControl)
{
struct TestData
{
const std::string url;
const std::string acceptLanguageHeader;
const std::string expectedH1;
operator TestContext() const
{
return TestContext{
{"url", url},
{"acceptLanguageHeader", acceptLanguageHeader},
};
}
};
const TestData testData[] = {
{
/*url*/ "/ROOT/zimfile/invalid-article",
/*Accept-Language:*/ "",
/* expected <h1> */ "Not Found"
},
{
/*url*/ "/ROOT/zimfile/invalid-article?userlang=en",
/*Accept-Language:*/ "",
/* expected <h1> */ "Not Found"
},
{
/*url*/ "/ROOT/zimfile/invalid-article?userlang=hy",
/*Accept-Language:*/ "",
/* expected <h1> */ "Սխալ հասցե"
},
{
/*url*/ "/ROOT/zimfile/invalid-article",
/*Accept-Language:*/ "*",
/* expected <h1> */ "Not Found"
},
{
/*url*/ "/ROOT/zimfile/invalid-article",
/*Accept-Language:*/ "hy",
/* expected <h1> */ "Սխալ հասցե"
},
{
// userlang query parameter takes precedence over Accept-Language
/*url*/ "/ROOT/zimfile/invalid-article?userlang=en",
/*Accept-Language:*/ "hy",
/* expected <h1> */ "Not Found"
},
{
// The value of the Accept-Language header is not currently parsed.
// In case of a comma separated list of languages (optionally weighted
// with quality values) the default (en) language is used instead.
/*url*/ "/ROOT/zimfile/invalid-article",
/*Accept-Language:*/ "hy;q=0.9, en;q=0.2",
/* expected <h1> */ "Not Found"
},
};
const std::regex h1Regex("<h1>(.+)</h1>");
for ( const auto& t : testData ) {
std::smatch h1Match;
Headers headers;
if ( !t.acceptLanguageHeader.empty() ) {
headers.insert({"Accept-Language", t.acceptLanguageHeader});
}
const auto r = zfs1_->GET(t.url.c_str(), headers);
std::regex_search(r->body, h1Match, h1Regex);
const std::string h1(h1Match[1]);
EXPECT_EQ(h1, t.expectedH1) << t;
}
}
TEST_F(ServerTest, RandomPageRedirectsToAnExistingArticle)
{
auto g = zfs1_->GET("/ROOT/random?content=zimfile");