Skip to content

Commit 3c815e2

Browse files
committed
feat: Add getBestLocaleMatch()
1 parent 75ab337 commit 3c815e2

File tree

1 file changed

+62
-2
lines changed

1 file changed

+62
-2
lines changed

system/HTTP/Negotiate.php

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,16 @@ public function encoding(array $supported = []): string
123123
* types the application says it supports, and the types requested
124124
* by the client.
125125
*
126-
* If no match is found, the first, highest-ranking client requested
126+
* If loose locale negotiation is enabled and no match is found, the first, highest-ranking client requested
127127
* type is returned.
128128
*/
129129
public function language(array $supported): string
130130
{
131-
return $this->getBestMatch($supported, $this->request->getHeaderLine('accept-language'), false, false, config(Feature::class)->simpleNegotiateLocale);
131+
if (config(Feature::class)->looseLocaleNegotiation) {
132+
return $this->getBestMatch($supported, $this->request->getHeaderLine('accept-language'), false, false, true);
133+
}
134+
135+
return $this->getBestLocaleMatch($supported, $this->request->getHeaderLine('accept-language'));
132136
}
133137

134138
// --------------------------------------------------------------------
@@ -190,6 +194,62 @@ protected function getBestMatch(
190194
return $strictMatch ? '' : $supported[0];
191195
}
192196

197+
/**
198+
* Strict locale search, including territories (en-*)
199+
*
200+
* @param list<string> $supported App-supported values
201+
* @param ?string $header Compatible 'Accept-Language' header string
202+
*/
203+
protected function getBestLocaleMatch(array $supported, ?string $header): string
204+
{
205+
if ($supported === []) {
206+
throw HTTPException::forEmptySupportedNegotiations();
207+
}
208+
209+
if ($header === null || $header === '') {
210+
return $supported[0];
211+
}
212+
213+
$acceptable = $this->parseHeader($header);
214+
$fallbackLocales = [];
215+
216+
foreach ($acceptable as $accept) {
217+
// if acceptable quality is zero, skip it.
218+
if ($accept['q'] === 0.0) {
219+
continue;
220+
}
221+
222+
// if acceptable value is "anything", return the first available
223+
if ($accept['value'] === '*' || $accept['value'] === '*/*') {
224+
return $supported[0];
225+
}
226+
227+
// look for exact match
228+
if (in_array($accept['value'], $supported, true)) {
229+
return $accept['value'];
230+
}
231+
232+
// set a fallback locale
233+
$fallbackLocales[] = strtok($accept['value'], '-');
234+
}
235+
236+
foreach ($fallbackLocales as $fallbackLocale) {
237+
// look for exact match
238+
if (in_array($fallbackLocale, $supported, true)) {
239+
return $fallbackLocale;
240+
}
241+
242+
// look for locale variants match
243+
foreach ($supported as $locale) {
244+
if (str_starts_with($locale, $fallbackLocale . '-')) {
245+
return $locale;
246+
}
247+
}
248+
}
249+
250+
return $supported[0];
251+
}
252+
193253
/**
194254
* Parses an Accept* header into it's multiple values.
195255
*

0 commit comments

Comments
 (0)