Coverage for src / rendercv / schema / models / locale / english_locale.py: 100%
29 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-25 13:49 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-25 13:49 +0000
1import functools
2from typing import Annotated, Literal
4import annotated_types as at
5import pydantic
7from ..base import BaseModelWithoutExtraKeys
10class Phrases(BaseModelWithoutExtraKeys):
11 degree_with_area: str = pydantic.Field(
12 default="DEGREE in AREA",
13 description=(
14 "Template for combining degree and area in education entries."
15 " Available placeholders: DEGREE, AREA."
16 " The default value is `DEGREE in AREA`."
17 ),
18 )
21class EnglishLocale(BaseModelWithoutExtraKeys):
22 language: Literal["english"] = pydantic.Field(
23 default="english",
24 description="The language for your CV. The default value is `english`.",
25 )
26 last_updated: str = pydantic.Field(
27 default="Last updated in",
28 description=(
29 'Translation of "Last updated in". The default value is `Last updated in`.'
30 ),
31 )
32 month: str = pydantic.Field(
33 default="month",
34 description='Translation of "month" (singular). The default value is `month`.',
35 )
36 months: str = pydantic.Field(
37 default="months",
38 description='Translation of "months" (plural). The default value is `months`.',
39 )
40 year: str = pydantic.Field(
41 default="year",
42 description='Translation of "year" (singular). The default value is `year`.',
43 )
44 years: str = pydantic.Field(
45 default="years",
46 description='Translation of "years" (plural). The default value is `years`.',
47 )
48 present: str = pydantic.Field(
49 default="present",
50 description=(
51 'Translation of "present" for ongoing dates. The default value is'
52 " `present`."
53 ),
54 )
55 phrases: Phrases = pydantic.Field(
56 default_factory=Phrases,
57 description="Locale-specific phrases used in entry templates as placeholders.",
58 )
59 # From https://web.library.yale.edu/cataloging/months
60 month_abbreviations: Annotated[list[str], at.Len(min_length=12, max_length=12)] = (
61 pydantic.Field(
62 default=[
63 "Jan",
64 "Feb",
65 "Mar",
66 "Apr",
67 "May",
68 "June",
69 "July",
70 "Aug",
71 "Sept",
72 "Oct",
73 "Nov",
74 "Dec",
75 ],
76 description="Month abbreviations (Jan-Dec).",
77 )
78 )
79 month_names: Annotated[list[str], at.Len(min_length=12, max_length=12)] = (
80 pydantic.Field(
81 default=[
82 "January",
83 "February",
84 "March",
85 "April",
86 "May",
87 "June",
88 "July",
89 "August",
90 "September",
91 "October",
92 "November",
93 "December",
94 ],
95 description="Full month names (January-December).",
96 )
97 )
99 @functools.cached_property
100 def language_iso_639_1(self) -> str:
101 """Get ISO 639-1 two-letter language code for locale.
103 Why:
104 Typst's text element requires ISO 639-1/2/3 language codes for the
105 lang parameter. This enables proper hyphenation, smart quotes, and
106 accessibility (screen readers use it for voice selection). HTML export
107 also uses it for lang attribute.
109 Returns:
110 Two-letter ISO 639-1 language code for Typst and HTML.
111 """
112 return {
113 "danish": "da",
114 "dutch": "nl",
115 "english": "en",
116 "french": "fr",
117 "german": "de",
118 "hindi": "hi",
119 "italian": "it",
120 "indonesian": "id",
121 "japanese": "ja",
122 "korean": "ko",
123 "indonesia": "id",
124 "mandarin_chinese": "zh",
125 "norwegian_bokmål": "nb",
126 "norwegian_nynorsk": "nn",
127 "portuguese": "pt",
128 "russian": "ru",
129 "spanish": "es",
130 "turkish": "tr",
131 "arabic": "ar",
132 "hebrew": "he",
133 "persian": "fa",
134 "vietnamese": "vi",
135 "hungarian": "hu",
136 }[self.language]
138 @functools.cached_property
139 def flag_emoji(self) -> str:
140 """Get flag emoji for the locale's primary country.
142 Why:
143 Flag emojis are displayed next to locale names in the UI. Deriving
144 flags here keeps the mapping in one place alongside the ISO language
145 codes.
147 Returns:
148 Flag emoji string (e.g., "🇬🇧" for English).
149 """
150 country = {
151 "arabic": "SA",
152 "danish": "DK",
153 "dutch": "NL",
154 "english": "GB",
155 "french": "FR",
156 "german": "DE",
157 "hebrew": "IL",
158 "hindi": "IN",
159 "hungarian": "HU",
160 "indonesian": "ID",
161 "italian": "IT",
162 "japanese": "JP",
163 "korean": "KR",
164 "mandarin_chinese": "CN",
165 "norwegian_bokmål": "NO",
166 "norwegian_nynorsk": "NO",
167 "persian": "IR",
168 "portuguese": "PT",
169 "russian": "RU",
170 "spanish": "ES",
171 "turkish": "TR",
172 "vietnamese": "VN",
173 }[self.language]
174 return "".join(chr(0x1F1E6 + ord(c) - ord("A")) for c in country)
176 @functools.cached_property
177 def is_rtl(self) -> bool:
178 """Check if language uses right-to-left text direction.
180 Returns:
181 True if language is RTL (Arabic, Hebrew, Persian, Urdu, etc.)
182 """
183 rtl_languages = {"arabic", "hebrew", "persian"}
184 return self.language in rtl_languages