forked from jxmorris12/language_tool_python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlanguage_tag.py
More file actions
125 lines (102 loc) Β· 4.02 KB
/
Copy pathlanguage_tag.py
File metadata and controls
125 lines (102 loc) Β· 4.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""LanguageTool language tag normalization module."""
import logging
import re
from functools import total_ordering
from typing import Any, Iterable
logger = logging.getLogger(__name__)
@total_ordering
class LanguageTag:
"""
A class to represent and normalize language tags.
:param tag: The language tag.
:type tag: str
:param languages: An iterable of supported language tags.
:type languages: Iterable[str]
"""
tag: str
"""The language tag to be normalized."""
languages: Iterable[str]
"""An iterable of supported language tags."""
normalized_tag: str
"""The normalized language tag."""
_LANGUAGE_RE = re.compile(r"^([a-z]{2,3})(?:[_-]([a-z]{2}))?$", re.I)
"""A regular expression to match language tags."""
def __init__(self, tag: str, languages: Iterable[str]) -> None:
"""
Initialize a LanguageTag instance.
"""
self.tag = tag
self.languages = languages
self.normalized_tag = self._normalize(tag)
def __eq__(self, other: Any) -> bool:
"""
Compare this LanguageTag object with another for equality.
:param other: The other object to compare with.
:type other: Any
:return: True if the normalized tags are equal, False otherwise.
:rtype: bool
"""
return self.normalized_tag == self._normalize(other)
def __lt__(self, other: Any) -> bool:
"""
Compare this object with another for less-than ordering.
:param other: The object to compare with.
:type other: Any
:return: True if this object is less than the other, False otherwise.
:rtype: bool
"""
return str(self) < self._normalize(other)
def __str__(self) -> str:
"""
Returns the string representation of the object.
:return: The normalized tag as a string.
:rtype: str
"""
return self.normalized_tag
def __repr__(self) -> str:
"""
Return a string representation of the LanguageTag instance.
:return: A string in the format '<LanguageTag "language_tag_string">'
:rtype: str
"""
return f'<LanguageTag "{str(self)}">'
def _normalize(self, tag: str) -> str:
"""
Normalize a language tag to a standard format.
:param tag: The language tag to normalize.
:type tag: str
:raises ValueError: If the tag is empty or unsupported.
:return: The normalized language tag.
:rtype: str
"""
logger.debug("Normalizing language tag: %r", tag)
if not tag:
err = "empty language tag"
raise ValueError(err)
languages = {
language.lower().replace("-", "_"): language for language in self.languages
}
logger.debug("Available languages: %s", list(languages.keys()))
# If POSIX, default to English variants
if tag.lower() in {"c", "posix"} or tag.lower().startswith("c."):
logger.debug("Detected POSIX/C locale for tag %r", tag)
for candidate in ("en_us", "en_gb", "en"):
if candidate in languages:
logger.debug("Using POSIX fallback language %r", candidate)
return languages[candidate]
err = f"unsupported language (no default for POSIX locale): {tag!r}"
raise ValueError(err)
try:
return languages[tag.lower().replace("-", "_")]
except KeyError as e:
logger.debug("Tag %r not found directly, attempting regex match", tag)
try:
match = self._LANGUAGE_RE.match(tag)
if match is None:
err = "tag does not match pattern"
raise AttributeError(err) from e
logger.debug("Regex match groups: %s", match.groups())
return languages[match.group(1).lower()]
except (KeyError, AttributeError) as e:
err = f"unsupported language: {tag!r}"
raise ValueError(err) from e