@@ -113,7 +113,7 @@ def locale2str(value: Locale) -> str:
113113
114114def best_match (accept_languages , available_locales ) -> Locale :
115115 """
116- Takes an Accept-Languages string (from header or request query params)
116+ Takes an Accept-Languages sorted list (from header or request query params)
117117 and finds the best matching locale from a list of available locales.
118118
119119 This function provides a framework-independent alternative to the
@@ -131,12 +131,12 @@ def best_match(accept_languages, available_locales) -> Locale:
131131 or unknown locale is ignored. However, if no
132132 `available_locales` are specified, a `LocaleError` is raised.
133133
134- :param accept_languages: A Locale or string with one or more languages.
134+ :param accept_languages: A Locale or list of one or more languages.
135135 This can be as simple as "de" for example,
136136 but it's also possible to include a territory
137137 (e.g. "en-US" or "fr_BE") or even a complex
138- string with quality values, e.g.
139- "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5" .
138+ list sorted by quality values, e.g.
139+ [ "fr-CH", "fr", "en", "de", "*"] .
140140 :param available_locales: A list containing the available locales.
141141 For example, a pygeoapi provider might only
142142 support ["de", "en"].
@@ -170,49 +170,12 @@ def get_match(locale_, available_locales_):
170170
171171 if isinstance (accept_languages , Locale ):
172172 # If a Babel Locale was used as input, transform back into a string
173- accept_languages = locale2str (accept_languages )
173+ accept_languages = [ locale2str (accept_languages )]
174174
175- if not isinstance (accept_languages , str ):
175+ if not isinstance (accept_languages , list ):
176176 # If `accept_languages` is not a string, ignore it
177177 LOGGER .debug (f"ignoring invalid accept-languages '{ accept_languages } '" )
178- accept_languages = ''
179-
180- tags = accept_languages .split (',' )
181- num_tags = len (tags )
182- req_locales = {}
183- for i , lang in enumerate (tags ):
184- q_raw = None
185- q_out = None
186- if not lang :
187- continue
188-
189- # Check if complex (i.e. with quality weights)
190- try :
191- lang , q_raw = (v .strip () for v in lang .split (';' ))
192- except ValueError :
193- # Tuple unpacking failed: tag is not complex (or too complex :))
194- pass
195-
196- # Validate locale tag
197- loc = str2locale (lang , True )
198- if not loc :
199- LOGGER .debug (f"ignoring invalid accept-language '{ lang } '" )
200- continue
201-
202- # Validate quality weight (e.g. "q=0.7")
203- if q_raw :
204- try :
205- q_out = float ([v .strip () for v in q_raw .split ('=' )][1 ])
206- except (ValueError , IndexError ):
207- # Tuple unpacking failed: not a valid q tag
208- pass
209-
210- # If there's no actual q, set one based on the language order
211- if not q_out :
212- q_out = num_tags - i
213-
214- # Store locale
215- req_locales [q_out ] = loc
178+ accept_languages = []
216179
217180 # Process supported locales
218181 prv_locales = OrderedDict ()
@@ -221,7 +184,11 @@ def get_match(locale_, available_locales_):
221184 prv_locales .setdefault (loc .language , []).append (loc .territory )
222185
223186 # Return best match from accepted languages
224- for _ , loc in sorted (req_locales .items (), reverse = True ):
187+ for lang in accept_languages :
188+ loc = str2locale (lang , True )
189+ if not loc :
190+ LOGGER .debug (f"ignoring invalid accept-language '{ lang } '" )
191+ continue
225192 match = get_match (loc , prv_locales )
226193 if match :
227194 LOGGER .debug (f"'{ match } ' matches requested '{ accept_languages } '" )
@@ -281,7 +248,7 @@ def translate(value, language: Union[Locale, str]):
281248 return value
282249
283250 # Find best language match and return value by its key
284- out_locale = best_match (language , loc_items .keys ())
251+ out_locale = best_match ([ language ] , loc_items .keys ())
285252 return value [loc_items [out_locale ]]
286253
287254
@@ -340,42 +307,6 @@ def _translate_dict(obj, level: int = 0):
340307 return result
341308
342309
343- def locale_from_headers (headers ) -> str :
344- """
345- Gets a valid Locale from a request headers dictionary.
346- Supported are complex strings (e.g. "fr-CH, fr;q=0.9, en;q=0.8"),
347- web locales (e.g. "en-US") or basic language tags (e.g. "en").
348- A value of `None` is returned if the locale was not found or invalid.
349-
350- :param headers: Mapping of request headers.
351-
352- :returns: locale string or None
353- """
354-
355- lang = {k .lower (): v for k , v in headers .items ()}.get ('accept-language' )
356- if lang :
357- LOGGER .debug (f"Got locale '{ lang } ' from 'Accept-Language' header" )
358- return lang
359-
360-
361- def locale_from_params (params ) -> str :
362- """
363- Gets a valid Locale from a request query parameters dictionary.
364- Supported are complex strings (e.g. "fr-CH, fr;q=0.9, en;q=0.8"),
365- web locales (e.g. "en-US") or basic language tags (e.g. "en").
366- A value of `None` is returned if the locale was not found or invalid.
367-
368- :param params: Mapping of request query parameters.
369-
370- :returns: locale string or None
371- """
372-
373- lang = params .get (QUERY_PARAM )
374- if lang :
375- LOGGER .debug (f"Got locale '{ lang } ' from query parameter '{ QUERY_PARAM } '" ) # noqa
376- return lang
377-
378-
379310def set_response_language (headers : dict , * locale_ : Locale ):
380311 """
381312 Sets the Content-Language on the given HTTP response headers dict.
0 commit comments