Open cmb69 opened 9 years ago
A potential solution has been worked out in a PM thread in the forum. A rough outline:
The actual main language has to be created as second language. All requests to the installation folder are redirected according to the sent Accept-Language
header field (defaulting to the main language) using the 300 Multiple Choices
status code. Since some browsers (most notably Chrome) ignore the Location
, a manual language selection is presented (perhaps in a minimal template). This requires the languagemenu to be adjusted (to avoid duplicate links to the main language), and also perhaps further adjustments. The language selection also sets apprialte hreflang="x-default"
links.
The following code may serve as base for the actual implementation:
<?php
if ($sl === $cf['language']['default']) {
$temp = XH_secondLanguages();
array_unshift($temp, 'en'); // explicitly add "default" language
$temp = new LanguagePicker($temp);
$temp = $temp->pickLanguage($_SERVER['HTTP_ACCEPT_LANGUAGE']);
$temp = CMSIMPLE_URL . "$temp/";
if ($_SERVER['QUERY_STRING'] !== '') {
$temp .= "?{$_SERVER[QUERY_STRING]}";
}
header("Location: $temp", true, 302);
exit;
}
/**
* see <https://tools.ietf.org/html/rfc7231#section-5.3.5>
*/
class LanguagePicker
{
/**
* @var array
*/
private $availableLanguages;
public function __construct(array $availableLanguages)
{
$this->availableLanguages = $availableLanguages;
}
/**
* @param string $acceptLanguage
* @return string
*/
public function pickLanguage($acceptLanguage)
{
$acceptedLanguages = $this->parseAcceptLanguageHeader($acceptLanguage);
foreach ($acceptedLanguages as $language) {
if ($language === '*') {
return $this->availableLanguages[0];
} elseif (in_array($language, $this->availableLanguages) !== false) {
return $language;
}
}
return $this->availableLanguages[0];
}
/**
* @param string $acceptLanguage
* @return array
*/
private function parseAcceptLanguageHeader($acceptLanguage)
{
$languages = array();
$languageRanges = explode(',', $acceptLanguage);
foreach ($languageRanges as $languageRange) {
$parts = explode(';', trim($languageRange));
if (count($parts) === 1) {
$language = $parts[0];
$weight = 1.0;
} else {
list($language, $weight) = $parts;
list(, $weight) = explode('=', $weight);
$weight = (float) $weight;
}
$parts = explode('-', $language);
$language = trim($parts[0]);
$languages[] = compact('language', 'weight');
}
$this->sortLanguages($languages);
foreach ($languages as &$language) {
$language = $language['language'];
}
$languages = array_unique($languages);
return $languages;
}
/**
* Can't use PHP sort functions, since we require a stable sort.
*
* @return void
*/
private function sortLanguages(array &$array)
{
for ($i = 1; $i < count($array); $i++) {
$current = $array[$i];
$j = $i;
while ($j > 0 && $array[$j-1]['weight'] < $current['weight']) {
$array[$j] = $array[$j-1];
$j--;
}
$array[$j] = $current;
}
}
}
FTR: ext/intl offers Locale::acceptFromHttp which might be interesting.
The language of the visitor can be detected by sniffing the Accept-Language header. It seems to be reasonable to automatically select the language based on this information. Some kind of overriding should be possible, though.
See http://www.cmsimpleforum.com/viewtopic.php?f=5&t=5905&p=34437#p34437 and http://forum.cmsimple-xh.dk/?f=1&t=754.