translate.cmake raw
1 # Copyright (c) 2025-present 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
5 cmake_minimum_required(VERSION 3.22)
6
7 set(input_variables
8 PROJECT_SOURCE_DIR
9 COPYRIGHT_HOLDERS
10 LCONVERT_EXECUTABLE
11 LUPDATE_EXECUTABLE
12 XGETTEXT_EXECUTABLE
13 )
14
15 foreach(var IN LISTS input_variables)
16 if(NOT DEFINED ${var})
17 message(FATAL_ERROR "Variable '${var}' is not defined!")
18 endif()
19 endforeach()
20
21 # Extract _("...") strings for translation and convert to Qt stringdefs so that
22 # they can be picked up by Qt linguist.
23 function(extract_strings output)
24 execute_process(
25 COMMAND ${XGETTEXT_EXECUTABLE}
26 --output=bitcoinstrings.po
27 --no-location
28 --from-code=utf-8
29 --keyword=_
30 ${ARGN}
31 COMMAND_ERROR_IS_FATAL ANY
32 )
33
34 file(STRINGS "bitcoinstrings.po" text ENCODING "UTF-8")
35
36 set(messages "${COPYRIGHT_HOLDERS}")
37 foreach(line IN LISTS text)
38 if(line MATCHES "^msgid \"(.*)\"$")
39 set(msgid "${CMAKE_MATCH_1}")
40 elseif(line MATCHES "^\"(.*)\"$")
41 string(APPEND msgid "${CMAKE_MATCH_1}")
42 elseif(line MATCHES "^msgstr .*$" AND NOT msgid STREQUAL "")
43 # CMake uses ';' as a list separator.
44 # We need to temporarily replace that in order to keep strings intact.
45 string(REPLACE ";" "<cmake-semicolon>" msgid "${msgid}")
46 list(APPEND messages "${msgid}")
47 endif()
48 endforeach()
49
50 set(content [[// Automatically generated by translate.cmake
51
52 #include <QtGlobal>
53
54 #ifdef __GNUC__
55 #define UNUSED __attribute__((unused))
56 #else
57 #define UNUSED
58 #endif
59
60 static const char UNUSED *bitcoin_strings[] = {
61 ]])
62
63 set(prefix "QT_TRANSLATE_NOOP(\"bitcoin-core\", \"")
64 set(suffix "\"),\n")
65
66 list(SORT messages)
67 list(JOIN messages "${suffix}${prefix}" messages_str)
68 string(APPEND content "${prefix}${messages_str}${suffix}")
69 string(APPEND content "};\n")
70 string(REPLACE "<cmake-semicolon>" ";" content "${content}")
71
72 file(WRITE "${output}" "${content}")
73 endfunction()
74
75 file(GLOB_RECURSE translatable_sources
76 "${PROJECT_SOURCE_DIR}/src/*.h"
77 "${PROJECT_SOURCE_DIR}/src/*.cpp"
78 "${PROJECT_SOURCE_DIR}/src/*.mm"
79 )
80
81 file(GLOB_RECURSE qt_translatable_sources
82 "${PROJECT_SOURCE_DIR}/src/qt/*.h"
83 "${PROJECT_SOURCE_DIR}/src/qt/*.cpp"
84 "${PROJECT_SOURCE_DIR}/src/qt/*.mm"
85 )
86
87 file(GLOB ui_files
88 "${PROJECT_SOURCE_DIR}/src/qt/forms/*.ui"
89 )
90
91 set(subtrees crc32c crypto/ctaes leveldb minisketch secp256k1)
92 set(exclude_dirs bench compat crypto support test univalue)
93 foreach(directory IN LISTS subtrees exclude_dirs)
94 list(FILTER translatable_sources
95 EXCLUDE REGEX "${PROJECT_SOURCE_DIR}/src/${directory}/.*"
96 )
97 endforeach()
98
99 extract_strings("${PROJECT_SOURCE_DIR}/src/qt/bitcoinstrings.cpp"
100 ${translatable_sources}
101 )
102
103 execute_process(
104 COMMAND ${LUPDATE_EXECUTABLE}
105 -no-obsolete
106 -I ${PROJECT_SOURCE_DIR}/src
107 -locations relative
108 ${ui_files}
109 ${qt_translatable_sources}
110 ${PROJECT_SOURCE_DIR}/src/qt/bitcoinstrings.cpp
111 -ts ${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.ts
112 COMMAND_ERROR_IS_FATAL ANY
113 )
114
115 execute_process(
116 COMMAND ${LCONVERT_EXECUTABLE}
117 -drop-translations
118 -o ${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.xlf
119 -i ${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.ts
120 COMMAND_ERROR_IS_FATAL ANY
121 )
122
123 file(READ "${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.xlf" bitcoin_en)
124 string(REPLACE "source-language=\"en\" target-language=\"en\""
125 "source-language=\"en\"" bitcoin_en "${bitcoin_en}"
126 )
127 string(REGEX REPLACE " *<target xml:space=\"preserve\"></target>\n"
128 "" bitcoin_en "${bitcoin_en}"
129 )
130 file(WRITE "${PROJECT_SOURCE_DIR}/src/qt/locale/bitcoin_en.xlf" "${bitcoin_en}")
131