cmake, translation: Use native Qt TS file as source for translations on Transifex #34808

pull hebasto wants to merge 5 commits into bitcoin:master from hebasto:260311-qt-ts-source changing 6 files +1743 −8751
  1. hebasto commented at 5:44 pm on March 11, 2026: member

    In Bitcoin Core v22.0, we switched from Qt TS to XLIFF translation source file to provide more context, specifically developer notes, to translators on Transifex. That was very useful for translators back then, even though it required some extra complexity on our side.

    Since then, Transifex has enabled support for developer notes in Qt TS files as well.

    Therefore, I believe we should thank XLIFF for its service and retire it.

    In addition to switching back to Qt TS, this PR introduces a few tweaks to the lupdate command (see the corresponding commit messages).

    To summarize, this PR brings the following benefits:

    1. Removal of obsolete code from the build system.

    2. Minimal diffs during translation updates. For a recent example, see https://github.com/bitcoin-core/gui/pull/931. One can also apply the changes from bitcoin/bitcoin#34301 and run cmake -B build --fresh -DBUILD_GUI=ON && cmake --build build -t translate to observe the new minimal diff.

    3. More stable string hashes on Transifex. They no longer include string ids, which makes this PR an alternative to #33270.

    As a potential drawback, we are tying ourselves back to Qt’s proprietary translation file format.

    I’ve created an experimental resource on Transifex based on this branch: https://app.transifex.com/bitcoin/bitcoin/experimental-do-not-translate. Reviewers can use it to observe Transifex’s support for the various features on the following messages:

    • # 11 - Developer Notes
    • # 144 - Plurals
    • # 510 - A disambiguation string (provided as a second argument to the tr() function) added to the string context.
  2. hebasto added this to the milestone 32.0 on Mar 11, 2026
  3. hebasto added the label GUI on Mar 11, 2026
  4. hebasto added the label Build system on Mar 11, 2026
  5. DrahtBot commented at 5:44 pm on March 11, 2026: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK l0rinc

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

  6. hebasto commented at 5:46 pm on March 11, 2026: member

    Ping:

  7. hebasto renamed this:
    cmake, translation: Use native Qt TS file as source for transaltions on Transifex
    cmake, translation: Use native Qt TS file as source for translations on Transifex
    on Mar 11, 2026
  8. fanquake commented at 12:44 pm on March 12, 2026: member
    cc @l0rinc given this is an alternative to #33270.
  9. in .tx/config:6 in a7cde0b8ce outdated
    0@@ -1,7 +1,7 @@
    1 [main]
    2 host = https://www.transifex.com
    3 
    4-[o:bitcoin:p:bitcoin:r:qt-translation-031x]
    5-file_filter = src/qt/locale/bitcoin_<lang>.xlf
    6-source_file = src/qt/locale/bitcoin_en.xlf
    7+[o:bitcoin:p:bitcoin:r:qt-translation-032x]
    8+file_filter = src/qt/locale/bitcoin_<lang>.ts
    9+source_file = src/qt/locale/bitcoin_en.ts
    



    hebasto commented at 12:22 pm on March 16, 2026:
    Thanks!. Updated.
  10. in src/qt/CMakeLists.txt:309 in 127b49fe58 outdated
    305@@ -306,7 +306,6 @@ else()
    306   add_custom_target(translate COMMAND ${CMAKE_COMMAND}
    307     -D "PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}"
    308     -D "COPYRIGHT_HOLDERS=${COPYRIGHT_HOLDERS}"
    309-    -D "LCONVERT_EXECUTABLE=$<TARGET_FILE:Qt6::lconvert>"
    


    l0rinc commented at 2:01 pm on March 12, 2026:

    hebasto commented at 12:22 pm on March 16, 2026:
    Thanks! Removed.

    hebasto commented at 12:54 pm on March 16, 2026:
    For some reason, this change effectively disables the qttranslations submodule, so I’ll leave the patch as is for now. In any case, lconvert consists of just a single source file.
  11. in share/qt/translate.cmake:119 in 127b49fe58 outdated
    109@@ -111,20 +110,3 @@ execute_process(
    110     -ts ${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.ts
    111   COMMAND_ERROR_IS_FATAL ANY
    112 )
    113-
    114-execute_process(
    115-  COMMAND ${LCONVERT_EXECUTABLE}
    116-    -drop-translations
    117-    -o ${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.xlf
    118-    -i ${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.ts
    


    l0rinc commented at 2:30 pm on March 12, 2026:

    127b49f cmake, translation: Remove TS to XLIFF conversion:

    For context, while we can reliably generate this file now, it still makes sense to check it in under version control so that Transifex can simply select it as a source without having to recreate it during build


    hebasto commented at 12:24 pm on March 16, 2026:
    Yes, the translation source file should be checked in to enable the Transifex Autoupdate feature.
  12. in share/qt/translate.cmake:105 in e613d0c6a6 outdated
    101@@ -102,6 +102,7 @@ extract_strings("${PROJECT_SOURCE_DIR}/src/qt/bitcoinstrings.cpp"
    102 execute_process(
    103   COMMAND ${LUPDATE_EXECUTABLE}
    104     -no-obsolete
    105+    -sort-messages
    


    l0rinc commented at 2:35 pm on March 12, 2026:

    e613d0c cmake, translation: Sort messages within contexts alphabetically:

    Tested the sorting by doing it manually via Python, resulting in the exact same output 👍

     0#!/usr/bin/env python3
     1# Copyright (c) 2026 The Bitcoin Core developers
     2# Distributed under the MIT software license, see the accompanying
     3# file COPYING or http://www.opensource.org/licenses/mit-license.php.
     4
     5import html
     6import pathlib
     7import re
     8import sys
     9
    10
    11CONTEXT_RE = re.compile(r"(?s)<context>\n.*?\n</context>")
    12MESSAGE_RE = re.compile(r"(?s)    <message(?: [^>]*)?>\n.*?</message>\n?")
    13SOURCE_RE = re.compile(r"(?s)<source>(.*?)</source>")
    14COMMENT_RE = re.compile(r"(?s)<comment>(.*?)</comment>")
    15
    16
    17def message_sort_key(message: str) -> tuple[str, str, str]:
    18    source = SOURCE_RE.search(message)
    19    comment = COMMENT_RE.search(message)
    20    return (
    21        html.unescape(source.group(1) if source else ""),
    22        html.unescape(comment.group(1) if comment else ""),
    23        message,
    24    )
    25
    26
    27def sort_context(match: re.Match[str]) -> str:
    28    context = match.group(0)
    29    if not context.endswith("</context>"):
    30        raise ValueError("Unexpected context terminator")
    31    body = context.removeprefix("<context>\n").removesuffix("</context>")
    32    name = re.match(r"(?s)    <name>.*?</name>\n", body)
    33    if not name:
    34        raise ValueError("Missing context name")
    35    remainder = body[name.end():]
    36    messages = MESSAGE_RE.findall(remainder)
    37    if "".join(messages) != remainder:
    38        raise ValueError("Unexpected context layout")
    39    return "<context>\n" + name.group(0) + "".join(sorted(messages, key=message_sort_key)) + "</context>"
    40
    41
    42def main() -> int:
    43    if len(sys.argv) != 2:
    44        raise SystemExit("Usage: sort_messages.py path/to/file.ts")
    45
    46    ts_path = pathlib.Path(sys.argv[1])
    47    original = ts_path.read_text(encoding="utf-8")
    48    sorted_text = CONTEXT_RE.sub(sort_context, original)
    49    if not sorted_text.endswith("\n"):
    50        sorted_text += "\n"
    51    ts_path.write_text(sorted_text, encoding="utf-8")
    52    return 0
    53
    54
    55if __name__ == "__main__":
    56    raise SystemExit(main())
    
  13. in share/qt/translate.cmake:108 in 890b80d7f1 outdated
    104@@ -105,6 +105,7 @@ execute_process(
    105     -sort-messages
    106     -I ${PROJECT_SOURCE_DIR}/src
    107     -locations none
    108+    -target-language en
    


    l0rinc commented at 2:36 pm on March 12, 2026:

    890b80d cmake, translation: Specify English as target language explicitly:

    what’s wrong with en_US? We can’t really have language-neutral English spelling. Aren’t we closer to US spelling?


    hebasto commented at 12:35 pm on March 16, 2026:
    There is a consensus among our international language coordinators (French, German, Spanish) that there is little value in supporting territory-specific variants. Because of this, we chose to gradually phase them out. I don’t believe English is an exception here, and we do not accept requests to support en_US or en_GB translations. Therefore, for the sake of consistency, I would prefer not to use the en_US locale for the source file.
  14. in share/qt/translate.cmake:106 in ae7cb3785f outdated
    102@@ -103,7 +103,7 @@ execute_process(
    103   COMMAND ${LUPDATE_EXECUTABLE}
    104     -no-obsolete
    105     -I ${PROJECT_SOURCE_DIR}/src
    106-    -locations relative
    107+    -locations none
    


    l0rinc commented at 2:45 pm on March 12, 2026:

    ae7cb37 cmake, translation: Skip source locations in TS files:

    Was wondering if we could just remove it instead. No, the default is absolute:

    0 lupdate -help | grep -A3 '\-locations'
    1    -locations {absolute|relative|none}
    2           Specify/override how source code references are saved in TS files.
    3           Guessed from existing TS files if not specified.
    4           Default is absolute for new files.
    
  15. in src/qt/locale/bitcoin_en.ts:6 in ae7cb3785f outdated
    3@@ -4,855 +4,679 @@
    4 <context>
    5     <name>AddressBookPage</name>
    6     <message>
    


    l0rinc commented at 2:50 pm on March 12, 2026:

    ae7cb37 cmake, translation: Skip source locations in TS files:

    It could help review if we simplified the change to a scripted diff that achieves the same as the original translation command, i.e.

    0-BEGIN VERIFY SCRIPT-
    1sed -Ei 's/(-locations) relative/\1 none/' share/qt/translate.cmake
    2sed -i '/^        <location /d' src/qt/locale/bitcoin_en.ts
    3-END VERIFY SCRIPT-
    

    hebasto commented at 2:29 pm on March 16, 2026:
    Since another commit also modifies the lupdate option, I’d prefer to keep it consistent how other changes are done, namely, by using cmake --build build -t translate.

    l0rinc commented at 2:49 pm on March 16, 2026:
    We can still do that (run the translation task manually), and also add the scripted diff to the commit so that the CI verifies it. I have already done that locally, so I’m fine with it as-is, but other reviewers may find this useful.
  16. in .tx/config:4 in a7cde0b8ce outdated
    0@@ -1,7 +1,7 @@
    1 [main]
    2 host = https://www.transifex.com
    3 
    4-[o:bitcoin:p:bitcoin:r:qt-translation-031x]
    5-file_filter = src/qt/locale/bitcoin_<lang>.xlf
    6-source_file = src/qt/locale/bitcoin_en.xlf
    7+[o:bitcoin:p:bitcoin:r:qt-translation-032x]
    


    l0rinc commented at 2:57 pm on March 12, 2026:

    a7cde0b translation: Switch to Qt TS source file:

    Can you please mention the reason for the version bump in the commit?


    hebasto commented at 12:36 pm on March 16, 2026:
    Thanks! Done.
  17. l0rinc commented at 3:04 pm on March 12, 2026: contributor

    Concept ACK

    The switch back to native Qt TS will kind of tie us more to Qt’s file format, but we can easily switch if that ever becomes a problem. The lupdate tweaks should make future bitcoin_en.ts updates much less noisy.

    Left a few more notes about doc/release-process.md updates, the script diff, and lconvert leftovers.

  18. translation: Switch to Qt TS source file
    Additionally, the slug was updated for the next release.
    8c30055458
  19. hebasto force-pushed on Mar 16, 2026
  20. hebasto marked this as a draft on Mar 16, 2026
  21. cmake, translation: Remove TS to XLIFF conversion
    Drop all code related to converting Qt TS files to XLIFF, enabling the
    build system to handle TS source file directly.
    4f553bd0da
  22. cmake, translation: Skip source locations in TS files
    Source locations do not contribute to the string's Context on Transifex
    (used for hash calculation) and only trigger unnecessary diffs in
    version control.
    
    While the `filename` attribute of the `<location>` element is rendered
    as "Occurrences" on Transifex, Qt's lupdate tool does not seem to set
    this attribute consistently.
    312ab8ab0a
  23. cmake, translation: Sort messages within contexts alphabetically
    This is done in addition to the default sorting of the contexts
    themselves, further minimizing unnecessary diffs in version control.
    4097d6d968
  24. cmake, translation: Specify English as target language explicitly
    Otherwise, "en_US" might be used.
    a434d66025
  25. hebasto force-pushed on Mar 16, 2026
  26. hebasto marked this as ready for review on Mar 16, 2026
  27. DrahtBot added the label CI failed on Mar 16, 2026
  28. hebasto commented at 2:30 pm on March 16, 2026: member

    @l0rinc

    Thank you for the review! Your feedback has been addressed.

  29. DrahtBot removed the label CI failed on Mar 16, 2026
  30. l0rinc commented at 2:52 pm on March 16, 2026: contributor

    Code review ACK a434d66025095764cf58c5a049cf4414f53ee52f

    Tested the xml sorting and location removal locally via scripted diff and python script.

  31. achow101 requested review from achow101 on Mar 17, 2026


hebasto DrahtBot fanquake l0rinc


achow101

Labels
GUI Build system

Milestone
32.0


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-03-24 03:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me