Update PO files.
[tails.git] / refresh-translations
blob439b79ddb8162dae2a36583c2bd0ae3c3352c120
1 #! /bin/bash
3 set -e
4 set -u
6 PERL_PROGS="/usr/local/bin/tails-security-check \
7 /usr/local/lib/tails-htp-notify-user \
8 $(
9 find \
10 config/chroot_local-includes/usr/src/iuk/bin \
11 -type f |
12 perl -p -E 's{^config/chroot_local-includes}{}'
13 ) \
15 find \
16 config/chroot_local-includes/usr/src/iuk/lib \
17 config/chroot_local-includes/usr/src/perl5lib/lib \
18 -type f -name '*.pm' |
19 perl -p -E 's{^config/chroot_local-includes}{}'
21 PYTHON_PROGS="\
22 /usr/local/bin/electrum \
23 /usr/local/bin/replace-su-with-sudo \
24 /usr/local/bin/tails-about \
25 /usr/local/bin/tails-additional-software-config \
26 /usr/local/bin/tails-screen-locker \
27 /usr/local/bin/tails-upgrade-frontend-wrapper \
28 /usr/lib/python3/dist-packages/additional_software.py \
29 /usr/lib/python3/dist-packages/tails_installer/creator.py \
30 /usr/lib/python3/dist-packages/tails_installer/gui.py \
31 /usr/lib/python3/dist-packages/tails_installer/passphrase_dialog.py \
32 /usr/lib/python3/dist-packages/tails_installer/source.py \
33 /usr/lib/python3/dist-packages/tails_installer/utils.py \
34 /usr/lib/python3/dist-packages/tailsgreeter/settings/persistence.py \
35 /usr/lib/python3/dist-packages/tailsgreeter/ui/add_settings_dialog.py \
36 /usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py \
37 /usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py \
38 /usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py \
39 /usr/lib/python3/dist-packages/tca/config.py \
40 /usr/lib/python3/dist-packages/tca/ui/main_window.py \
41 /usr/lib/python3/dist-packages/tps/configuration/binding.py \
42 /usr/lib/python3/dist-packages/tps/device.py \
43 /usr/lib/python3/dist-packages/tps_frontend/application.py \
44 /usr/lib/python3/dist-packages/tps_frontend/change_passphrase_dialog.py \
45 /usr/lib/python3/dist-packages/tps_frontend/error_dialog.py \
46 /usr/lib/python3/dist-packages/tps_frontend/feature.py \
47 /usr/lib/python3/dist-packages/tps_frontend/passphrase_strength_hint.py \
48 /usr/lib/python3/dist-packages/tps_frontend/views/features_view.py \
49 /usr/lib/python3/dist-packages/tps_frontend/window.py \
50 /usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py \
51 /usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume_list.py \
52 /usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume_manager.py \
53 /usr/lib/python3/dist-packages/whisperBack/exceptions.py \
54 /usr/lib/python3/dist-packages/whisperBack/gui.py \
55 /usr/lib/python3/dist-packages/whisperBack/whisperback.py \
56 /usr/local/lib/additional-software/asp-handle-package-changes \
57 /usr/local/lib/additional-software/asp-install \
58 /usr/local/lib/tails-additional-software-notify \
59 /usr/local/lib/tails-virt-notify-user"
60 SHELL_PROGS=" \
61 /usr/local/bin/keepassxc \
62 /usr/local/bin/tca \
63 /usr/local/lib/tails-spoof-mac \
64 /usr/local/lib/tails-report-disk-ioerrors \
65 /usr/local/lib/tails-report-disk-resize-errors \
66 /usr/local/bin/thunderbird \
67 /usr/bin/tor-browser \
68 /usr/local/sbin/unsafe-browser"
69 JAVASCRIPT_PROGS=" \
70 /usr/share/gnome-shell/extensions/status-menu-helper@tails.boum.org/extension.js \
71 /usr/share/gnome-shell/extensions/torstatus@tails.boum.org/extension.js \
73 GLADE_UI="\
74 /usr/share/tails/additional-software/configuration-window.ui \
77 LOCALE_BASEDIR=config/chroot_local-includes/usr/share/locale
79 ### External libraries
81 . config/chroot_local-includes/usr/local/lib/tails-shell-library/common.sh
82 . config/chroot_local-includes/usr/local/lib/tails-shell-library/po.sh
84 ### Functions
86 normalize_pot() {
87 sed --regexp-extended \
88 -i 's@^"Content-Type: text/plain; charset=CHARSET\\n"@"Content-Type: text/plain; charset=UTF-8\\n"@' \
89 "${@}"
92 prog_potfile() {
93 prog=$1
95 progpath="config/chroot_local-includes$prog"
96 case $prog in
97 /usr/share/gnome-shell/extensions/status-menu-helper@tails.boum.org/extension.js)
98 domain=status-menu-helper-extension.js
100 /usr/share/gnome-shell/extensions/torstatus@tails.boum.org/extension.js)
101 domain=torstatus-extension.js
103 /usr/lib/python3/dist-packages/tps/*)
104 domain="tps-$(basename "$prog")"
106 /usr/lib/python3/dist-packages/tps_frontend/*)
107 domain="tps-frontend-$(basename "$prog")"
109 /usr/lib/python3/dist-packages/unlock_veracrypt_volumes/*)
110 domain="unlock-veracrypt-volumes-$(basename "$prog")"
112 /usr/lib/python3/dist-packages/tailsgreeter/*)
113 domain="greeter-$(basename "$prog")"
115 /usr/lib/python*/dist-packages/tails_installer/*)
116 domain="installer-$(basename "$prog")"
118 /usr/lib/python*/dist-packages/tca/*)
119 domain="tca-$(basename "$prog")"
121 /usr/local/bin/tca)
122 domain="tca-bin"
124 /usr/lib/python3/dist-packages/whisperBack/*)
125 domain="whisperBack-$(basename "$prog")"
128 domain=$(basename "$prog")
130 esac
131 echo "tmp/pot/${domain}.pot"
134 create_pot() {
135 prog=$1
136 proglang=$2
137 progpath="config/chroot_local-includes${prog}"
138 if [ -e "${progpath}" ]; then
139 pot="$(prog_potfile "${prog}")"
140 mkdir -p "$(dirname "${pot}")"
141 case "$proglang" in
142 Perl)
143 # XXX: once we can assume libintl-perl 1.28+,
144 # generate this list of options with:
145 # perl -MLocale::TextDomain -E 'print Locale::TextDomain->options'
146 xgettext_args='--keyword'
147 xgettext_args="$xgettext_args --keyword=__"
148 xgettext_args="$xgettext_args --keyword='\$__'"
149 xgettext_args="$xgettext_args --keyword=__x"
150 xgettext_args="$xgettext_args --keyword=__n:1,2"
151 xgettext_args="$xgettext_args --keyword=__nx:1,2"
152 xgettext_args="$xgettext_args --keyword=__xn:1,2"
153 xgettext_args="$xgettext_args --keyword=__p:1c,2"
154 xgettext_args="$xgettext_args --keyword=__np:1c,2,3"
155 xgettext_args="$xgettext_args --keyword=__npx:1c,2,3"
156 xgettext_args="$xgettext_args --keyword=N__"
157 xgettext_args="$xgettext_args --keyword=N__n:1,2"
158 xgettext_args="$xgettext_args --keyword=N__p:1c,2"
159 xgettext_args="$xgettext_args --keyword=N__np:1c,2,3"
160 xgettext_args="$xgettext_args --keyword=%__'"
163 xgettext_args=
165 esac
166 # shellcheck disable=SC2086
167 xgettext --language="${proglang}" --from-code=UTF-8 \
168 $xgettext_args \
169 --add-comments="Translators:" -o "${pot}" "${progpath}"
170 if [ -f "${pot}" ]; then
171 normalize_pot "${pot}"
173 else
174 echo "error: We are supposed to create a POT file for '${prog}'" \
175 "but '${progpath}' does not exist"
176 exit 1
180 po_file() {
181 locale=$1
183 echo "po/${locale}.po"
186 mo_file() {
187 locale=$1
189 echo "${LOCALE_BASEDIR}/${locale}/LC_MESSAGES/tails.mo"
192 refresh_mo() {
193 for locale in "$@"; do
194 po=$(po_file "$locale")
195 mo=$(mo_file "$locale")
196 mkdir -p "$(dirname "$mo")"
197 if ! msgfmt --check -o "${mo}" "${po}"; then
198 echo "Errors for locale '${locale}': restoring old file"
199 if [ -n "$(git ls-files "${po}")" ]; then
200 git restore "${po}"
201 msgfmt --check -o "${mo}" "${po}"
202 else
203 rm "${po}"
206 done
209 no_left_out_files() {
210 (cd po && intltool-update --maintain)
211 [ ! -e po/missing ] || return 1
212 return 0
215 intltool_update_pot() {
217 cd po
218 cp -a tails.pot tails.pot.orig
219 intltool-update --pot --gettext-package=tails
220 normalize_pot tails.pot tails.pot.orig
222 if [ "${FORCE:-}" = yes ]; then
223 echo "Force-updating 'tails.pot'."
224 rm tails.pot.orig
225 elif diff_without_pot_creation_date_and_comments -q tails.pot.orig tails.pot; then
226 echo "Only header or comment changes in tails.pot: keeping the old one"
227 mv tails.pot.orig tails.pot
228 else
229 echo "Real changes in tails.pot: switching to the updated one"
230 rm tails.pot.orig
235 intltool_merge_desktop() {
236 extract_from_file_between_markers po/POTFILES.in \
237 '^# Files updated by intltool-merge --desktop-style' '^$' |
238 while read -r infile; do
239 intltool-merge --quiet --desktop-style --utf8 \
240 po "$infile" "${infile%.in}"
241 done
244 intltool_merge_xml() {
245 extract_from_file_between_markers po/POTFILES.in \
246 '^# Files updated by intltool-merge --xml-style' '^$' |
247 while read -r infile; do
248 intltool-merge --quiet --xml-style --utf8 \
249 po "$infile" "${infile%.in}"
250 done
253 ### Main
255 FORCE=no
256 KEEP_TMP_POT=no
257 while [ -n "${1:-}" ]; do
258 case "${1:-}" in
259 '--force')
260 FORCE=yes
262 '--keep-tmp-pot')
263 KEEP_TMP_POT=yes
266 echo "Unknown option: ${1}"
267 exit 1
269 esac
270 shift
271 done
273 # Schedule clean up
274 # shellcheck disable=SC2064
275 trap "rm -fr po/*.new po/*.orig ; [ '$KEEP_TMP_POT' = yes ] || rm -fr tmp/pot" EXIT
276 trap "echo refresh-translations: failed" ERR
278 # Update POT files
279 mkdir -p tmp/pot
280 for prog in $PERL_PROGS; do create_pot "$prog" Perl; done
281 for prog in $PYTHON_PROGS; do create_pot "$prog" Python; done
282 for prog in $SHELL_PROGS; do create_pot "$prog" Shell; done
283 for prog in $JAVASCRIPT_PROGS; do create_pot "$prog" JavaScript; done
284 for prog in $GLADE_UI; do create_pot "$prog" Glade; done
285 intltool_update_pot
287 # If left out files are detected, intltool-update --maintain writes
288 # them to po/missing.
289 if ! no_left_out_files; then
290 echo "E: These files should be listed in POTFILES.in or POTFILES.skip:" >&2
291 cat po/missing
292 exit 3
295 # Update PO files
296 # shellcheck disable=SC2046
297 intltool_update_po $(po_languages)
299 # Update files that are actually used at runtime
300 # shellcheck disable=SC2046
301 refresh_mo $(po_languages)
302 intltool_merge_desktop
303 intltool_merge_xml