Marco Ricci commited on 2025-01-13 14:50:03
Zeige 1 geänderte Dateien mit 58 Einfügungen und 19 Löschungen.
The debug translations object and the `.po` file writer now properly support translation strings with the filename portion trimmed. Effectively, these are separate translation strings, and as such they need separate cache entries and separate `.po` entries. To fully support this, the translatable string itself now knows how to trim the filename replacement field, which the debug translations object now honors. The `.po` writer still needs the enum name to construct the message ID, but now it takes an optional transformed string to use instead of the enum value, if given. One additional minor fix to the debug translator: some of the emitted translated messages interpolate other translated messages (such as metavars). Thus when interpolating arguments, if the argument is a translated string, then stringify it before interpolation. This avoids printing the `repr` of the inner translated string, which is otherwise very common for debug translation output.
... | ... |
@@ -107,9 +107,11 @@ class DebugTranslations(gettext.NullTranslations): |
107 | 107 |
for enum_class in MSG_TEMPLATE_CLASSES: |
108 | 108 |
for member in enum_class.__members__.values(): |
109 | 109 |
value = cast('TranslatableString', member.value) |
110 |
- singular = value.singular |
|
111 |
- plural = value.plural |
|
112 |
- context = value.l10n_context |
|
110 |
+ value2 = value.maybe_without_filename() |
|
111 |
+ for v in {value, value2}: |
|
112 |
+ singular = v.singular |
|
113 |
+ plural = v.plural |
|
114 |
+ context = v.l10n_context |
|
113 | 115 |
cache.setdefault((context, singular), member) |
114 | 116 |
if plural: |
115 | 117 |
cache.setdefault((context, plural), member) |
... | ... |
@@ -204,6 +206,26 @@ class TranslatableString(NamedTuple): |
204 | 206 |
translator_comments: str |
205 | 207 |
flags: frozenset[str] |
206 | 208 |
|
209 |
+ def maybe_without_filename(self) -> Self: |
|
210 |
+ """Return a new translatable string without the "filename" field. |
|
211 |
+ |
|
212 |
+ Only acts upon translatable strings containing the exact |
|
213 |
+ contents `": {filename!r}"`. The specified part will be |
|
214 |
+ removed. This is correct usage in English for messages like |
|
215 |
+ `"Cannot open file: {error!s}: {filename!r}."`, but not |
|
216 |
+ necessarily in other languages. |
|
217 |
+ |
|
218 |
+ """ |
|
219 |
+ filename_str = ': {filename!r}' |
|
220 |
+ ret = self |
|
221 |
+ a, sep1, b = self.singular.partition(filename_str) |
|
222 |
+ c, sep2, d = self.plural.partition(filename_str) |
|
223 |
+ if sep1: |
|
224 |
+ ret = ret._replace(singular=(a + b)) |
|
225 |
+ if sep2: |
|
226 |
+ ret = ret._replace(plural=(c + d)) |
|
227 |
+ return ret |
|
228 |
+ |
|
207 | 229 |
|
208 | 230 |
def _prepare_translatable( |
209 | 231 |
msg: str, |
... | ... |
@@ -300,27 +322,33 @@ class TranslatedString: |
300 | 322 |
template = translation.pgettext(context, template) |
301 | 323 |
else: # pragma: no cover |
302 | 324 |
template = translation.gettext(template) |
303 |
- self._rendered = template.format(**self.kwargs) |
|
325 |
+ kwargs = { |
|
326 |
+ k: str(v) if isinstance(v, TranslatedString) else v |
|
327 |
+ for k, v in self.kwargs.items() |
|
328 |
+ } |
|
329 |
+ self._rendered = template.format(**kwargs) |
|
304 | 330 |
return self._rendered |
305 | 331 |
|
306 | 332 |
def maybe_without_filename(self) -> Self: |
333 |
+ """Return a new string without the "filename" field. |
|
334 |
+ |
|
335 |
+ Only acts upon translated strings containing the exact contents |
|
336 |
+ `": {filename!r}"`. The specified part will be removed. This |
|
337 |
+ acts upon the string *before* translation, i.e., the string |
|
338 |
+ without the filename will be used as a translation base. |
|
339 |
+ |
|
340 |
+ """ |
|
341 |
+ new_template = ( |
|
342 |
+ self.template.maybe_without_filename() |
|
343 |
+ if not isinstance(self.template, str) |
|
344 |
+ else self.template |
|
345 |
+ ) |
|
307 | 346 |
if ( |
308 |
- not isinstance(self.template, str) |
|
347 |
+ not isinstance(new_template, str) |
|
309 | 348 |
and self.kwargs.get('filename') is None |
310 |
- and ': {filename!r}' in self.template.singular |
|
349 |
+ and new_template != self.template |
|
311 | 350 |
): |
312 |
- singular = ''.join( |
|
313 |
- self.template.singular.split(': {filename!r}', 1) |
|
314 |
- ) |
|
315 |
- plural = ( |
|
316 |
- ''.join(self.template.plural.split(': {filename!r}', 1)) |
|
317 |
- if self.template.plural |
|
318 |
- else self.template.plural |
|
319 |
- ) |
|
320 |
- return self.__class__( |
|
321 |
- self.template._replace(singular=singular, plural=plural), |
|
322 |
- self.kwargs, |
|
323 |
- ) |
|
351 |
+ return self.__class__(new_template, self.kwargs) |
|
324 | 352 |
return self |
325 | 353 |
|
326 | 354 |
|
... | ... |
@@ -1878,11 +1906,21 @@ def _write_po_file( # noqa: C901 |
1878 | 1906 |
subdict.items(), |
1879 | 1907 |
key=lambda kv: str(kv[1]), |
1880 | 1908 |
): |
1909 |
+ value = cast('TranslatableString', enum_value.value) |
|
1910 |
+ value2 = value.maybe_without_filename() |
|
1881 | 1911 |
fileobj.writelines( |
1882 | 1912 |
_format_po_entry( |
1883 | 1913 |
enum_value, is_debug_translation=not is_template |
1884 | 1914 |
) |
1885 | 1915 |
) |
1916 |
+ if value != value2: |
|
1917 |
+ fileobj.writelines( |
|
1918 |
+ _format_po_entry( |
|
1919 |
+ enum_value, |
|
1920 |
+ is_debug_translation=not is_template, |
|
1921 |
+ transformed_string=value2, |
|
1922 |
+ ) |
|
1923 |
+ ) |
|
1886 | 1924 |
|
1887 | 1925 |
|
1888 | 1926 |
def _format_po_info( |
... | ... |
@@ -1921,9 +1959,10 @@ def _format_po_entry( |
1921 | 1959 |
/, |
1922 | 1960 |
*, |
1923 | 1961 |
is_debug_translation: bool = False, |
1962 |
+ transformed_string: TranslatableString | None = None, |
|
1924 | 1963 |
) -> tuple[str, ...]: # pragma: no cover |
1925 | 1964 |
ret: list[str] = ['\n'] |
1926 |
- ts = enum_value.value |
|
1965 |
+ ts = transformed_string or cast('TranslatableString', enum_value.value) |
|
1927 | 1966 |
if ts.translator_comments: |
1928 | 1967 |
comments = ts.translator_comments.splitlines(False) # noqa: FBT003 |
1929 | 1968 |
comments.extend(['', f'Message-ID: {enum_value}']) |
1930 | 1969 |