176def fetch_traits(engine_traits: EngineTraits) -> None:
177 """Fetch languages and regions from Mullvad-Leta"""
178
179 def extract_table_data(table):
180 for row in table.xpath(".//tr")[2:]:
181 cells = row.xpath(".//td | .//th")
182 if len(cells) > 1:
183 cell0 = cells[0].text_content().strip()
184 cell1 = cells[1].text_content().strip()
185 yield [cell0, cell1]
186
187
188
190
191
192
193 resp = http_get(f"{search_url}/documentation")
194 if not isinstance(resp, Response):
195 print("ERROR: failed to get response from mullvad-leta. Are you connected to the VPN?")
196 return
197 if not resp.ok:
198 print("ERROR: response from mullvad-leta is not OK. Are you connected to the VPN?")
199 return
200
201 dom = html.fromstring(resp.text)
202
203
204
205
206
207
208 tables = eval_xpath_list(dom.body, "//table")
209 if tables is None or len(tables) <= 0:
210 print("ERROR: could not find any tables. Was the page updated?")
211
212 language_table = tables[3]
213 lang_map = {
214 "zh-hant": "zh_Hans",
215 "zh-hans": "zh_Hant",
216 "jp": "ja",
217 }
218
219 for language, code in extract_table_data(language_table):
220
221 locale_tag = lang_map.get(code, code).replace("-", "_")
222 try:
223 locale = babel.Locale.parse(locale_tag)
224 except babel.UnknownLocaleError:
225 print(f"ERROR: Mullvad-Leta language {language} ({code}) is unknown by babel")
226 continue
227
228 sxng_tag = language_tag(locale)
229 engine_traits.languages[sxng_tag] = code
230
231 country_table = tables[2]
232 country_map = {
233 "cn": "zh-CN",
234 "hk": "zh-HK",
235 "jp": "ja-JP",
236 "my": "ms-MY",
237 "tw": "zh-TW",
238 "uk": "en-GB",
239 "us": "en-US",
240 }
241
242 for country, code in extract_table_data(country_table):
243
244 sxng_tag = country_map.get(code)
245 if sxng_tag:
246 engine_traits.regions[sxng_tag] = code
247 continue
248
249 try:
250 locale = babel.Locale.parse(f"{code.lower()}_{code.upper()}")
251 except babel.UnknownLocaleError:
252 locale = None
253
254 if locale:
255 engine_traits.regions[region_tag(locale)] = code
256 continue
257
258 official_locales = get_official_locales(code, engine_traits.languages.keys(), regional=True)
259 if not official_locales:
260 print(f"ERROR: Mullvad-Leta country '{code}' ({country}) could not be mapped as expected.")
261 continue
262
263 for locale in official_locales:
264 engine_traits.regions[region_tag(locale)] = code