1 """Helper methods for language selection in Home Assistant."""
3 from __future__
import annotations
5 from collections.abc
import Iterable
6 from dataclasses
import dataclass
13 SEPARATOR_RE = re.compile(
r"[-_]")
26 country: str |
None =
None,
27 code: str |
None =
None,
29 """Yield an ordered list of regions for a language based on country/code hints.
31 Regions should be checked for support in the returned order if no other
32 information is available.
34 if country
is not None:
41 elif language ==
"zh":
49 yield language.upper()
52 def is_region(language: str, region: str |
None) -> bool:
53 """Return true if region is not known to be a script/code instead."""
55 return region !=
"419"
58 return region !=
"Latn"
61 return region
not in (
"Hans",
"Hant")
67 """Return true if two languages are considered the same."""
72 if tuple(sorted([lang_1, lang_2]))
in SAME_LANGUAGES:
80 """Language with optional region and script/code."""
84 code: str |
None =
None
87 """Fix casing of language/region."""
91 if self.
regionregion
is not None:
96 self, dialect: Dialect, country: str |
None =
None
97 ) -> tuple[float, float]:
98 """Return score for match with another dialect where higher is better.
100 Score < 0 indicates a failure to match.
106 is_exact_language = self.
languagelanguage == dialect.language
108 if (self.
regionregion
is None)
and (dialect.region
is None):
111 return (2
if is_exact_language
else 1, 0)
113 if (self.
regionregion
is not None)
and (dialect.region
is not None):
114 if self.
regionregion == dialect.region:
119 1
if is_exact_language
else 0,
136 if self.
regionregion
is not None:
137 region_idx = pref_regions.index(self.
regionregion)
138 elif dialect.region
is not None:
139 region_idx = pref_regions.index(dialect.region)
143 return (1 + (len(pref_regions) - region_idx), 0)
153 """Parse language tag into language/region/code."""
154 parts = SEPARATOR_RE.split(tag, maxsplit=1)
156 region: str |
None =
None
157 code: str |
None =
None
160 region_or_code = parts[1]
163 region = region_or_code
166 code = region_or_code
176 target: str, supported: Iterable[str], country: str |
None =
None
178 """Return a sorted list of matching language tags based on a target tag and country hint."""
179 if target == MATCH_ALL:
180 return list(supported)
182 target_dialect = Dialect.parse(target)
188 dialect := Dialect.parse(tag),
189 target_dialect.score(dialect, country=country),
194 key=operator.itemgetter(1),
199 return [tag
for _dialect, score, tag
in scored
if score[0] >= 0]
202 def intersect(languages_1: set[str], languages_2: set[str]) -> set[str]:
203 """Intersect two sets of languages using is_match for aliases."""
205 for lang_1
in languages_1:
206 for lang_2
in languages_2:
208 languages.add(lang_1)
tuple[float, float] score(self, Dialect dialect, str|None country=None)
bool is_region(str language, str|None region)
set[str] intersect(set[str] languages_1, set[str] languages_2)
Iterable[str] preferred_regions(str language, str|None country=None, str|None code=None)
list[str] matches(str target, Iterable[str] supported, str|None country=None)
bool is_language_match(str lang_1, str lang_2)