mirror of https://github.com/streamlink/streamlink
160 lines
5.1 KiB
Python
160 lines
5.1 KiB
Python
import locale
|
|
import logging
|
|
from typing import Optional
|
|
|
|
from pycountry import countries, languages # type: ignore[import]
|
|
|
|
|
|
DEFAULT_LANGUAGE = "en"
|
|
DEFAULT_COUNTRY = "US"
|
|
DEFAULT_LANGUAGE_CODE = f"{DEFAULT_LANGUAGE}_{DEFAULT_COUNTRY}"
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class Country:
|
|
def __init__(self, alpha2, alpha3, numeric, name, official_name=None):
|
|
self.alpha2 = alpha2
|
|
self.alpha3 = alpha3
|
|
self.numeric = numeric
|
|
self.name = name
|
|
self.official_name = official_name
|
|
|
|
@classmethod
|
|
def get(cls, country):
|
|
try:
|
|
c = countries.lookup(country)
|
|
return Country(
|
|
c.alpha_2,
|
|
c.alpha_3,
|
|
c.numeric,
|
|
c.name,
|
|
getattr(c, "official_name", c.name),
|
|
)
|
|
except LookupError as err:
|
|
raise LookupError(f"Invalid country code: {country}") from err
|
|
|
|
def __eq__(self, other):
|
|
return (
|
|
(self.alpha2 and self.alpha2 == other.alpha2)
|
|
or (self.alpha3 and self.alpha3 == other.alpha3)
|
|
or (self.numeric and self.numeric == other.numeric)
|
|
)
|
|
|
|
def __str__(self):
|
|
return "Country({0!r}, {1!r}, {2!r}, {3!r}, official_name={4!r})".format(
|
|
self.alpha2,
|
|
self.alpha3,
|
|
self.numeric,
|
|
self.name,
|
|
self.official_name,
|
|
)
|
|
|
|
|
|
class Language:
|
|
def __init__(self, alpha2, alpha3, name, bibliographic=None):
|
|
self.alpha2 = alpha2
|
|
self.alpha3 = alpha3
|
|
self.name = name
|
|
self.bibliographic = bibliographic
|
|
|
|
@classmethod
|
|
def get(cls, language):
|
|
try:
|
|
lang = (
|
|
languages.get(alpha_2=language)
|
|
or languages.get(alpha_3=language)
|
|
or languages.get(bibliographic=language)
|
|
or languages.get(name=language)
|
|
)
|
|
if not lang:
|
|
raise KeyError(language)
|
|
return Language(
|
|
# some languages don't have an alpha_2 code
|
|
getattr(lang, "alpha_2", ""),
|
|
lang.alpha_3,
|
|
lang.name,
|
|
getattr(lang, "bibliographic", ""),
|
|
)
|
|
except LookupError as err:
|
|
raise LookupError(f"Invalid language code: {language}") from err
|
|
|
|
def __eq__(self, other):
|
|
return (
|
|
(self.alpha2 and self.alpha2 == other.alpha2)
|
|
or (self.alpha3 and self.alpha3 == other.alpha3)
|
|
or (self.bibliographic and self.bibliographic == other.bibliographic)
|
|
)
|
|
|
|
def __str__(self):
|
|
return "Language({0!r}, {1!r}, {2!r}, bibliographic={3!r})".format(
|
|
self.alpha2,
|
|
self.alpha3,
|
|
self.name,
|
|
self.bibliographic,
|
|
)
|
|
|
|
|
|
class Localization:
|
|
def __init__(self, language_code=None):
|
|
self._language_code = None
|
|
self.country = None
|
|
self.language = None
|
|
self.explicit = bool(language_code)
|
|
self._set_language_code(language_code)
|
|
|
|
@property
|
|
def language_code(self):
|
|
return self._language_code
|
|
|
|
@language_code.setter
|
|
def language_code(self, language_code):
|
|
self._set_language_code(language_code)
|
|
|
|
def _parse_locale_code(self, language_code):
|
|
parts = language_code.split("_", 1)
|
|
if len(parts) != 2 or len(parts[0]) != 2 or len(parts[1]) != 2:
|
|
raise LookupError(f"Invalid language code: {language_code}")
|
|
return self.get_language(parts[0]), self.get_country(parts[1])
|
|
|
|
def _set_language_code(self, language_code):
|
|
is_system_locale = language_code is None
|
|
if is_system_locale:
|
|
try:
|
|
language_code, _ = locale.getlocale()
|
|
except ValueError:
|
|
language_code = None
|
|
if language_code is None or language_code == "C":
|
|
# cannot be determined
|
|
language_code = DEFAULT_LANGUAGE_CODE
|
|
|
|
try:
|
|
self.language, self.country = self._parse_locale_code(language_code)
|
|
self._language_code = language_code
|
|
except LookupError:
|
|
if not is_system_locale:
|
|
raise
|
|
# If the system locale returns an invalid code, use the default
|
|
self.language = self.get_language(DEFAULT_LANGUAGE)
|
|
self.country = self.get_country(DEFAULT_COUNTRY)
|
|
self._language_code = DEFAULT_LANGUAGE_CODE
|
|
log.debug(f"Language code: {self._language_code}")
|
|
|
|
def equivalent(self, language: Optional[str] = None, country: Optional[str] = None) -> bool:
|
|
try:
|
|
return (
|
|
(not language or self.language == self.get_language(language))
|
|
and (not country or self.country == self.get_country(country))
|
|
)
|
|
except LookupError:
|
|
# if an unknown language/country code is given, they cannot be equivalent
|
|
return False
|
|
|
|
@classmethod
|
|
def get_country(cls, country):
|
|
return Country.get(country)
|
|
|
|
@classmethod
|
|
def get_language(cls, language):
|
|
return Language.get(language)
|