From 29c309d23adac30165a71c4dff51b038bb145755 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 22 Jul 2025 18:53:27 +0530 Subject: [PATCH] Restore formatter function help texts to Transifex We now use the form processed for easy translation. Fixes #2117352 [Translatable raw strings cause spurious newlines on Transifex](https://bugs.launchpad.net/calibre/+bug/2117352) --- setup/translations.py | 26 +++++++++++++++++++++++- src/calibre/utils/formatter_functions.py | 14 +++++++------ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/setup/translations.py b/setup/translations.py index c946b32c10..385552d57d 100644 --- a/setup/translations.py +++ b/setup/translations.py @@ -8,6 +8,7 @@ __docformat__ = 'restructuredtext en' import errno import glob import hashlib +import inspect import json import os import re @@ -28,6 +29,20 @@ from setup.iso_codes import iso_data from setup.parallel_build import batched_parallel_jobs +def serialize_msgid(text): + '''Serialize a string in the format used by msgid in GNU POT files.''' + if not text: + return 'msgid ""\n' + # Escape backslashes and quotes + escaped = text.replace('\\', r'\\').replace('"', r'\"') + ans = ['msgid ""'] + lines = escaped.splitlines() + for line in lines: + trailer = '"' if line is lines[-1] else r'\n"' + ans.append(f'"{line}{trailer}') + return '\n'.join(ans) + + def qt_sources(): qtdir = os.environ.get('QT_SRC', '/usr/src/qt6/qtbase') j = partial(os.path.join, qtdir) @@ -71,6 +86,15 @@ class POT(Command): # {{{ ans.append(self.a(self.j(root, name))) return ans + def get_ffml_docs(self): + from calibre.utils.formatter_functions import _formatter_builtins as b + ans = [] + for ff in b: + lnum = inspect.getsourcelines(ff.__doc__getter__)[1] + text = ff.__doc__getter__().msgid + ans.append(f'#: src/calibre/utils/formatter_function.py:{lnum}\n' + serialize_msgid(text) + '\nmsgstr ""\n\n') + return ''.join(ans) + def get_tweaks_docs(self): path = self.a(self.j(self.SRC, '..', 'resources', 'default_tweaks.py')) with open(path, 'rb') as f: @@ -218,12 +242,12 @@ class POT(Command): # {{{ '--from-code=UTF-8', '--sort-by-file', '--omit-header', '--no-wrap', '-kQT_TRANSLATE_NOOP:2', '-ktr', '-ktranslate:2', ] + qt_inputs) - with open(out.name, 'rb') as f: src = f.read().decode('utf-8') os.remove(out.name) src = pot_header + '\n' + src src += '\n\n' + self.get_tweaks_docs() + src += '\n\n' + self.get_ffml_docs() bdir = os.path.join(self.TRANSLATIONS, __appname__) if not os.path.exists(bdir): os.makedirs(bdir) diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index b48400c3fb..d35e9e98ac 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -60,20 +60,20 @@ URL_FUNCTIONS = _('URL functions') # Class and method to save an untranslated copy of translated strings class TranslatedStringWithRaw(str): - def __new__(cls, raw_english, raw_other, formatted_english, formatted_other): + def __new__(cls, raw_english, raw_other, formatted_english, formatted_other, msgid): instance = super().__new__(cls, formatted_other) instance.raw_english = raw_english instance.raw_other = raw_other instance.formatted_english = formatted_english instance.formatted_other = formatted_other + instance.msgid = msgid instance.did_format = False return instance def format(self, *args, **kw): formatted_english = self.raw_english.format(*args, **kw) formatted_other = self.raw_other.format(*args, **kw) - v = TranslatedStringWithRaw(self.raw_english, self.raw_other, - formatted_english, formatted_other) + v = TranslatedStringWithRaw(self.raw_english, self.raw_other, formatted_english, formatted_other, self.msgid) v.saved_args = args v.saved_kwargs = kw v.did_format = True @@ -87,9 +87,11 @@ class TranslatedStringWithRaw(str): def translate_ffml(txt): from calibre.utils.ffml_processor import FFMLProcessor - lookup = FFMLProcessor().document_to_transifex(txt, '', safe=True) - translated = xlated(lookup) - return TranslatedStringWithRaw(txt, translated, txt, translated) + msgid = FFMLProcessor().document_to_transifex(txt, '', safe=True).strip() + translated = xlated(msgid) + if translated == msgid: + translated = txt + return TranslatedStringWithRaw(txt, translated, txt, translated, msgid) class StoredObjectType(Enum):