2
2
3
3
namespace CodeZero \BrowserLocale ;
4
4
5
- interface BrowserLocale
5
+ class BrowserLocale
6
6
{
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
+
7
34
/**
8
35
* Get the most preferred locale.
9
36
*
10
37
* @return Locale|null
11
38
*/
12
- public function getLocale ();
39
+ public function getLocale ()
40
+ {
41
+ return isset ($ this ->locales [0 ]) ? $ this ->locales [0 ] : null ;
42
+ }
13
43
14
44
/**
15
45
* Get an array of Locale objects in descending order of preference.
@@ -21,5 +51,182 @@ public function getLocale();
21
51
*
22
52
* @return array
23
53
*/
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
+ }
25
232
}
0 commit comments