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