Skip to content

Commit eba9436

Browse files
committed
Skip interface
1 parent 8b437a8 commit eba9436

File tree

3 files changed

+220
-245
lines changed

3 files changed

+220
-245
lines changed

src/BrowserLocale.php

Lines changed: 210 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,44 @@
22

33
namespace CodeZero\BrowserLocale;
44

5-
interface BrowserLocale
5+
class BrowserLocale
66
{
7+
/**
8+
* Array of all preferred locales that are
9+
* configured in a visitor's browser.
10+
*
11+
* @var array
12+
*/
13+
protected $locales = [];
14+
15+
/**
16+
* Create a new BrowserLocale instance.
17+
*
18+
* @param string $httpAcceptLanguages
19+
*/
20+
public function __construct($httpAcceptLanguages = null)
21+
{
22+
// $_SERVER["HTTP_ACCEPT_LANGUAGE"] will return a comma separated list of language codes.
23+
// Each language code MAY have a "relative quality factor" attached ("nl;q=0.8") which
24+
// determines the order of preference. For example: "nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4"
25+
if ($httpAcceptLanguages === null && isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])) {
26+
$this->parseAcceptLanguages($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
27+
}
28+
29+
if ($httpAcceptLanguages) {
30+
$this->parseAcceptLanguages($httpAcceptLanguages);
31+
}
32+
}
33+
734
/**
835
* Get the most preferred locale.
936
*
1037
* @return Locale|null
1138
*/
12-
public function getLocale();
39+
public function getLocale()
40+
{
41+
return isset($this->locales[0]) ? $this->locales[0] : null;
42+
}
1343

1444
/**
1545
* Get an array of Locale objects in descending order of preference.
@@ -21,5 +51,182 @@ public function getLocale();
2151
*
2252
* @return array
2353
*/
24-
public function getLocales($propertyFilter = null);
54+
public function getLocales($propertyFilter = null)
55+
{
56+
if ($propertyFilter === null) {
57+
return $this->locales;
58+
}
59+
60+
return $this->filterLocaleInfo($propertyFilter);
61+
}
62+
63+
/**
64+
* Parse all HTTP Accept Languages.
65+
*
66+
* @param string $httpAcceptLanguages
67+
*
68+
* @return void
69+
*/
70+
protected function parseAcceptLanguages($httpAcceptLanguages)
71+
{
72+
foreach ($this->splitAcceptLanguages($httpAcceptLanguages) as $httpAcceptLanguage) {
73+
if ($locale = $this->parseAcceptLanguage($httpAcceptLanguage)) {
74+
$this->locales[] = $locale;
75+
}
76+
}
77+
78+
$this->sortLocales();
79+
}
80+
81+
/**
82+
* Extract and save information from a HTTP Accept Language.
83+
*
84+
* @param string $httpAcceptLanguage
85+
*
86+
* @return Locale|null
87+
*/
88+
protected function parseAcceptLanguage($httpAcceptLanguage)
89+
{
90+
$parts = $this->splitAcceptLanguage($httpAcceptLanguage);
91+
92+
$locale = isset($parts[0]) ? $parts[0] : null;
93+
$weight = isset($parts[1]) ? $parts[1] : null;
94+
95+
$localeInstance = null;
96+
97+
if ($locale !== null) {
98+
$localeInstance = new Locale();
99+
$localeInstance->locale = $locale;
100+
$localeInstance->language = $this->getLanguage($locale);
101+
$localeInstance->country = $this->getCountry($locale);
102+
$localeInstance->weight = $this->getWeight($weight);
103+
}
104+
105+
return $localeInstance;
106+
}
107+
108+
/**
109+
* Convert a comma separated list to an array.
110+
*
111+
* Example: ["en", "en-US;q=0.8"]
112+
*
113+
* @param string $httpAcceptLanguages
114+
*
115+
* @return array
116+
*/
117+
protected function splitAcceptLanguages($httpAcceptLanguages)
118+
{
119+
return explode(',', $httpAcceptLanguages) ?: [];
120+
}
121+
122+
/**
123+
* Split a language code and the relative quality factor by semicolon.
124+
*
125+
* Example: ["en"] or ["en-US"] or ["en-US", "q=0.8"]
126+
*
127+
* @param string $httpAcceptLanguage
128+
*
129+
* @return array
130+
*/
131+
protected function splitAcceptLanguage($httpAcceptLanguage)
132+
{
133+
return explode(';', trim($httpAcceptLanguage)) ?: [];
134+
}
135+
136+
/**
137+
* Get the 2-letter language code from the locale.
138+
*
139+
* Example: "en"
140+
*
141+
* @param string $locale
142+
*
143+
* @return string
144+
*/
145+
protected function getLanguage($locale)
146+
{
147+
return strtolower(substr($locale, 0, 2));
148+
}
149+
150+
/**
151+
* Get the 2-letter country code from the locale.
152+
*
153+
* Example: "US"
154+
*
155+
* @param string $locale
156+
*
157+
* @return string
158+
*/
159+
protected function getCountry($locale)
160+
{
161+
if (($divider = strpos($locale, '-')) === false){
162+
return '';
163+
}
164+
165+
return strtoupper(substr($locale, $divider + 1, 2));
166+
}
167+
168+
/**
169+
* Parse the relative quality factor and return its value.
170+
*
171+
* Example: 1.0 or 0.8
172+
*
173+
* @param string $q
174+
*
175+
* @return float
176+
*/
177+
protected function getWeight($q)
178+
{
179+
$weight = 1.0;
180+
$parts = explode('=', $q);
181+
182+
if (isset($parts[1])) {
183+
$weight = ((float) $parts[1]);
184+
}
185+
186+
return $weight;
187+
}
188+
189+
/**
190+
* Sort the array of locales in descending order of preference.
191+
*
192+
* @return void
193+
*/
194+
protected function sortLocales()
195+
{
196+
usort($this->locales, function ($a, $b) {
197+
if ($a->weight === $b->weight) {
198+
return 0;
199+
}
200+
201+
return ($a->weight > $b->weight) ? -1 : 1;
202+
});
203+
}
204+
205+
/**
206+
* Get a flattened array of locale information,
207+
* containing only the requested property values.
208+
*
209+
* @param string $property
210+
*
211+
* @return array
212+
*/
213+
protected function filterLocaleInfo($property)
214+
{
215+
$filters = ['locale', 'language', 'country', 'weight'];
216+
$locales = $this->locales;
217+
218+
if ( ! in_array($property, $filters)) {
219+
return $locales;
220+
}
221+
222+
$filtered = [];
223+
224+
foreach ($locales as $locale) {
225+
if ($locale->$property && ! in_array($locale->$property, $filtered)) {
226+
$filtered[] = $locale->$property;
227+
}
228+
}
229+
230+
return $filtered;
231+
}
25232
}

0 commit comments

Comments
 (0)