Check for translation errors when generating placeholder translations
Check for common errors like trailing whitespace in a string marked for translation. The filename and line number are displayed in the terminal output, along with a description of the problem and possible solutions. If translation errors are found then the Linux CI build will fail, but the Windows and macOS builds are allowed to proceed. Other kinds of error (e.g. Python exceptions) will cause all builds to fail.
This commit is contained in:
parent
96addbf260
commit
a0a6a662d2
2
.github/workflows/ci_macos_mu4.yml
vendored
2
.github/workflows/ci_macos_mu4.yml
vendored
|
@ -170,7 +170,7 @@ jobs:
|
|||
- name: Generate placeholder.ts files
|
||||
if: env.DO_PLACEHOLDER_TRANSLATIONS == 'true'
|
||||
run: |
|
||||
python3 ./tools/translations/placeholder_translations.py
|
||||
python3 ./tools/translations/placeholder_translations.py --warn-only
|
||||
- name: Generate .qm files
|
||||
if: env.DO_BUILD == 'true'
|
||||
run: |
|
||||
|
|
2
.github/workflows/ci_windows_mu4.yml
vendored
2
.github/workflows/ci_windows_mu4.yml
vendored
|
@ -157,7 +157,7 @@ jobs:
|
|||
if: env.DO_PLACEHOLDER_TRANSLATIONS == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
python3 -X utf8 ./tools/translations/placeholder_translations.py
|
||||
python3 -X utf8 ./tools/translations/placeholder_translations.py --warn-only
|
||||
- name: Generate .qm files
|
||||
if: env.DO_BUILD == 'true'
|
||||
shell: bash
|
||||
|
|
|
@ -3385,7 +3385,8 @@ String Note::accessibleInfo() const
|
|||
int on = _playEvents[0].ontime();
|
||||
int off = _playEvents[0].offtime();
|
||||
if (on != 0 || off != NoteEvent::NOTE_LENGTH) {
|
||||
onofftime = mtrc("engraving", " (on %1‰ off %2‰)").arg(on, off);
|
||||
//: Note-on and note-off times relative to note duration, expressed in thousandths (per mille)
|
||||
onofftime = u" " + mtrc("engraving", "(on %1‰ off %2‰)").arg(on, off);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -277,7 +277,7 @@ bool SaveProjectScenario::warnBeforeSavingToExistingPubliclyVisibleCloudProject(
|
|||
IInteractive::Result result = interactive()->warning(
|
||||
trc("project/save", "Publish changes online?"),
|
||||
trc("project/save", "Your saved changes will be publicly visible. We will also "
|
||||
"need to generate a new MP3 for public playback. "),
|
||||
"need to generate a new MP3 for public playback."),
|
||||
buttons, int(IInteractive::Button::Ok));
|
||||
|
||||
return result.standardButton() == IInteractive::Button::Ok;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# 5. Compile & run MuseScore
|
||||
# 6. In Preferences > General, set language to «Placeholder translations»
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import io
|
||||
import os
|
||||
|
@ -20,21 +21,41 @@ import re
|
|||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate fake translations for testing purposes.')
|
||||
parser.add_argument('--warn-only', action='store_true',
|
||||
help='exit with zero status even when translation errors are detected')
|
||||
args = parser.parse_args()
|
||||
|
||||
exit_status = 0
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
def tr_error(message_element, description, resolution, hint=''):
|
||||
location = message_element.find('location')
|
||||
filename = location.get('filename')
|
||||
filename = os.path.relpath(os.path.join('share/locale', filename))
|
||||
line_num = location.get('line')
|
||||
eprint(f'Translation error at line {line_num} in file {filename}:')
|
||||
eprint(f' Problem: {description}')
|
||||
eprint(f' Solution: {resolution}')
|
||||
if hint:
|
||||
eprint(f' Hint: {hint}')
|
||||
|
||||
superscript_numbers = '¹²³⁴⁵⁶⁷⁸⁹'
|
||||
|
||||
os.chdir(sys.path[0] + '/../..') # make all paths relative to repository root
|
||||
|
||||
source_lang = 'en'
|
||||
target_lang = 'en@placeholder' # Substring before '@' must correspond to a real
|
||||
# locale for plurals to work. You must provide as
|
||||
# locale for plurals to work. We must provide as
|
||||
# many plural forms as are required by the locale.
|
||||
|
||||
source_lang_ts = source_lang + '.ts'
|
||||
target_lang_ts = target_lang + '.ts'
|
||||
|
||||
eprint(f'{sys.argv[0]}: Generating fake translations for testing purposes.')
|
||||
|
||||
for source_file in glob.glob('share/locale/*_' + source_lang_ts):
|
||||
target_file = source_file[:-len(source_lang_ts)] + target_lang_ts
|
||||
|
||||
|
@ -50,9 +71,44 @@ for source_file in glob.glob('share/locale/*_' + source_lang_ts):
|
|||
translation = message.find('translation')
|
||||
plurals = translation.findall('numerusform')
|
||||
|
||||
bytes = source.findall('byte')
|
||||
if bytes:
|
||||
values = ', '.join([ f"'{b.get('value')}'" for b in bytes ])
|
||||
if len(bytes) == 1:
|
||||
tr_error(message, f'Translated string contains illegal byte: {values}.',
|
||||
'Remove the illegal byte or provide it untranslated.')
|
||||
else:
|
||||
tr_error(message, f'Translated string contains illegal bytes: {values}.',
|
||||
'Remove the illegal bytes or provide them untranslated.')
|
||||
exit_status = 1
|
||||
continue
|
||||
|
||||
# use the source as basis for the translation
|
||||
tr_txt = source.text
|
||||
|
||||
if not tr_txt:
|
||||
# Sadly, this test only works for empty strings in QML files. If a translated
|
||||
# string is empty in a C++ file then lupdate doesn't include it in the TS file.
|
||||
tr_error(message, 'Translated string is empty.',
|
||||
'Provide a non-empty string or use "" untranslated if it really needs to be empty.')
|
||||
exit_status = 1
|
||||
continue
|
||||
|
||||
tr_stripped = tr_txt.strip()
|
||||
|
||||
if not tr_stripped:
|
||||
tr_error(message, 'Translated string only contains whitespace characters.',
|
||||
'Include non-whitespace characters or provide the whitepace as untranslated text.')
|
||||
exit_status = 1
|
||||
continue
|
||||
|
||||
if tr_txt != tr_stripped:
|
||||
tr_error(message, 'Translated string contains leading and/or trailing whitespace.',
|
||||
'Remove the whitepace or provide it separately as untranslated text.',
|
||||
'Use .arg() and %1 tags if you need to insert text or numbers into a translated string.')
|
||||
exit_status = 1
|
||||
continue
|
||||
|
||||
# identify QString arg() markers '%1', '%2', etc. as their
|
||||
# arguments will require separate translation
|
||||
tr_txt = re.sub(r'%([1-9]+)', r'⌜%\1⌝', tr_txt)
|
||||
|
@ -67,11 +123,11 @@ for source_file in glob.glob('share/locale/*_' + source_lang_ts):
|
|||
translation.text = 'ᵗʳ' + tr_txt
|
||||
|
||||
eprint("Writing " + target_file)
|
||||
# Tweak ElementTree's XML formatting to match that of Qt's lrelease. The aim is to
|
||||
# minimise the diff between source_file and target_file.
|
||||
# Tweak ElementTree's XML formatting to match that of Qt's lupdate. The aim
|
||||
# is to minimise the diff between source_file and target_file.
|
||||
for el in root.findall('.//'):
|
||||
if el.text:
|
||||
# lrelease XML-escapes more characters than ElementTree, but ElementTree
|
||||
# lupdate XML-escapes more characters than ElementTree, but ElementTree
|
||||
# won't allow us to write the & escape character so use \r instead.
|
||||
el.text = el.text.replace('\'', '\rapos;').replace('"', '\rquot;')
|
||||
|
||||
|
@ -89,3 +145,11 @@ for source_file in glob.glob('share/locale/*_' + source_lang_ts):
|
|||
.replace('" />', '"/>') # remove space after final attribute in opening tags
|
||||
.replace('\r', '&') # use the proper escape character in ' and "
|
||||
)
|
||||
|
||||
if exit_status == 0:
|
||||
eprint(f'{sys.argv[0]}: Success!')
|
||||
elif args.warn_only:
|
||||
eprint(f'{sys.argv[0]}: Success! Some errors were ignored because of the --warn-only option.')
|
||||
else:
|
||||
eprint(f'{sys.argv[0]}: Failed! Translation errors were detected.')
|
||||
raise SystemExit(exit_status)
|
||||
|
|
Loading…
Reference in a new issue