Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions I18N.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ There are excellent manuals detailing the translation process for [developers](h
### Update base translations file
1. Update the base TS files (`qml_base.ts` and `qml_en.ts`, the latter serving only for the purpose of providing plural forms for English):
```bash
$ cd scripts/translationScripts
$ python update-en-ts.py
$ make update-translations
```
2. The updated TS files are created in the `ui/i18n/` directory
3. Ensure the updated base files land in master

The resulting binary QM files are also produced by simply running `make` via `make compile-translations` subtarget

### Update translations
1. Create pull request with exported translations using lokalise.com, see [docs](https://docs.lokalise.com/en/articles/1684090-github)

Expand Down
35 changes: 22 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ BUILD_SYSTEM_DIR := vendor/nimbus-build-system
check-pkg-target-macos \
check-pkg-target-windows \
clean \
update-translations \
compile-translations \
deps \
nim_status_client \
Expand All @@ -39,11 +40,11 @@ BUILD_SYSTEM_DIR := vendor/nimbus-build-system
status-keycard-go \
statusq-sanity-checker \
run-statusq-sanity-checker \
statusq-tests \
run-statusq-tests \
storybook-build \
run-storybook \
run-storybook-tests \
statusq-tests \
run-statusq-tests \
storybook-build \
run-storybook \
run-storybook-tests \
update

ifeq ($(NIM_PARAMS),)
Expand Down Expand Up @@ -532,18 +533,26 @@ $(UI_RESOURCES): $(UI_SOURCES) | check-qt-dir

rcc: $(UI_RESOURCES)

TS_SOURCES := $(shell find ui/i18n -iname '*.ts') # ui/i18n/qml_*.ts
QM_BINARIES := $(shell find ui/i18n -iname "*.ts" | sed 's/\.ts/\.qm/' | sed 's/ui/bin/') # bin/i18n/qml_*.qm
TS_SOURCE_DIR := ui/i18n
TS_BUILD_DIR := $(TS_SOURCE_DIR)/build

$(QM_BINARIES): TS_FILE = $(shell echo $@ | sed 's/\.qm/\.ts/' | sed 's/bin/ui/')
$(QM_BINARIES): $(TS_SOURCES) | check-qt-dir
mkdir -p bin/i18n
lrelease -removeidentical $(TS_FILE) -qm $@ $(HANDLE_OUTPUT)
log-update-translations:
echo -e "\033[92mUpdating:\033[39m translations"

update-translations: | log-update-translations
cmake -S $(TS_SOURCE_DIR) -B $(TS_BUILD_DIR) -Wno-dev $(HANDLE_OUTPUT)
cmake --build $(TS_BUILD_DIR) --target update_application_translations $(HANDLE_OUTPUT)
# + cd scripts/translationScripts && ./fixup-base-ts-for-lokalise.py $(HANDLE_OUTPUT)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a manual step (for now) as it requires Python; no change here. We probably don't want or need this to be run everywhere (mac, Windows), can be done only on CI


log-compile-translations:
echo -e "\033[92mCompiling:\033[39m translations"

compile-translations: | log-compile-translations $(QM_BINARIES)
compile-translations: | update-translations log-compile-translations
cmake -S $(TS_SOURCE_DIR) -B $(TS_BUILD_DIR) -Wno-dev $(HANDLE_OUTPUT)
cmake --build $(TS_BUILD_DIR) --target compile_application_translations $(HANDLE_OUTPUT)

clean-translations:
rm -rf $(TS_BUILD_DIR)

# used to override the default number of kdf iterations for sqlcipher
KDF_ITERATIONS ?= 0
Expand Down Expand Up @@ -839,7 +848,7 @@ zip-windows: check-pkg-target-windows $(STATUS_CLIENT_7Z)
clean-destdir:
rm -rf bin/*

clean: | clean-common clean-destdir statusq-clean status-go-clean dotherside-clean storybook-clean
clean: | clean-common clean-destdir statusq-clean status-go-clean dotherside-clean storybook-clean clean-translations
rm -rf node_modules bottles/* pkg/* tmp/* $(STATUSKEYCARDGO)
+ $(MAKE) -C vendor/QR-Code-generator/c/ --no-print-directory clean

Expand Down
77 changes: 77 additions & 0 deletions scripts/translationScripts/fixup-base-ts-for-lokalise.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/python

import xml.etree.ElementTree as ET
import sys

def main():
# Relative paths from project root
base_file = "../../ui/i18n/qml_base_en.ts"
plural_file = "../../ui/i18n/qml_en.ts"
output_file = "../../ui/i18n/qml_base_lokalise_en.ts"

# Parse the base TS file
try:
base_tree = ET.parse(base_file)
base_root = base_tree.getroot()
except ET.ParseError as e:
print(f"Error parsing {base_file}: {e}", file=sys.stderr)
sys.exit(1)

# Parse the plural TS file
try:
plural_tree = ET.parse(plural_file)
plural_root = plural_tree.getroot()
except ET.ParseError as e:
print(f"Error parsing {plural_file}: {e}", file=sys.stderr)
sys.exit(1)

# Create a dictionary for quick lookup of plural translations by source
plural_lookup = {}
for context in plural_root.findall('context'):
for message in context.findall('message'):
source = message.find('source')
if source is not None and source.text:
plural_lookup[source.text] = message.find('translation')

# Process each message in the base file
for context in base_root.findall('context'):
for message in context.findall('message'):
numerus = message.get('numerus')
source = message.find('source')
translation = message.find('translation')

if numerus == 'yes' and source is not None and source.text in plural_lookup:
# Copy translation from plural file
plural_translation = plural_lookup[source.text]
if plural_translation is not None:
# Clear existing translation content
translation.clear()
# Copy attributes and subelements
for attr, value in plural_translation.attrib.items():
translation.set(attr, value)
for child in plural_translation:
translation.append(child)
# Remove unfinished if present
if 'type' in translation.attrib and translation.attrib['type'] == 'unfinished':
del translation.attrib['type']
else:
# For non-numerus or unmatched, set translation to source
if translation is not None and source is not None:
translation.text = source.text
# Remove unfinished
if 'type' in translation.attrib and translation.attrib['type'] == 'unfinished':
del translation.attrib['type']

# Fixup the "language" attribute
base_root.set('language', 'en')

# Write the modified XML to output file
try:
base_tree.write(output_file, encoding='utf-8', xml_declaration=True)
print(f"Successfully transformed {base_file} to {output_file}")
except Exception as e:
print(f"Error writing to {output_file}: {e}", file=sys.stderr)
sys.exit(1)

if __name__ == "__main__":
main()
39 changes: 0 additions & 39 deletions scripts/translationScripts/update-en-ts.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ type

let localeDescriptionTable* = {
"ar": Description(name: "Arabic", native: "العربية", flag: "", state: State.Alpha),
"bn": Description(name: "Bengali", native: "বাংলা", flag: "X", state: State.Alpha),
"bn": Description(name: "Bengali", native: "বাংলা", flag: "X", state: State.Alpha),
"cs": Description(name: "Czech", native: "čeština", flag: "🇨🇿", state: State.Alpha),
Copy link
Member Author

@caybro caybro Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this should be done on the fly, and not hardcoded here. Will be tackled in a separate issue #18330

"de": Description(name: "German", native: "Deutsch", flag: "🇩🇪", state: State.Alpha),
"el": Description(name: "Greek", native: "Ελληνικά", flag: "🇬🇷", state: State.Alpha),
"en": Description(name: "English", native: "English", flag: "🏴󠁧󠁢󠁥󠁮󠁧󠁿", state: State.Stable),
"en": Description(name: "English", native: "English", flag: "🇬🇧", state: State.Stable),
"en_US": Description(name: "English (United States)", native: "English (United States)", flag: "🇺🇸", state: State.Alpha),
"es": Description(name: "Spanish", native: "Español", flag: "🇪🇸", state: State.Alpha),
"es_419": Description(name: "Spanish (Latin America)", native: "Español (Latinoamerica)", flag: "", state: State.Alpha),
Expand Down
49 changes: 49 additions & 0 deletions ui/i18n/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.19)

project(status-app-i18n) # dummy project to silence warnings, TBD at the toplevel

find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)

#qt6_standard_project_setup(I18N_TRANSLATED_LANGUAGES cs)

set(QT_NO_MISSING_CATALOG_LANGUAGE_WARNING ON)

file(GLOB_RECURSE
COLLECTED_SOURCE_FILES
${CMAKE_SOURCE_DIR}/../*.qml
)

qt6_add_lupdate(
LUPDATE_TARGET update_application_translations # name of the cmake target
OPTIONS -locations none -no-obsolete -source-language en # options passed to 'lupdate'
TS_FILES
${CMAKE_SOURCE_DIR}/qml_base_en.ts #empty base file, for manual translations
${CMAKE_SOURCE_DIR}/qml_cs.ts
PLURALS_TS_FILE
${CMAKE_SOURCE_DIR}/qml_en.ts
SOURCES ${COLLECTED_SOURCE_FILES}
)

qt6_add_lrelease(
LRELEASE_TARGET compile_application_translations # name of the cmake target
OPTIONS -removeidentical -compress -nounfinished # options passed to 'lrelease'
TS_FILES
${CMAKE_SOURCE_DIR}/qml_en.ts
${CMAKE_SOURCE_DIR}/qml_cs.ts
QM_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../../bin/i18n
MERGE_QT_TRANSLATIONS
QT_TRANSLATION_CATALOGS qtbase qtmultimedia qtwebengine
)

# Ideally at the toplevel it could be done sth like this in one pass/target:
#qt6_add_translations(
# TS_FILE_BASE qml_
# SOURCES ${COLLECTED_SOURCE_FILES}
# LUPDATE_TARGET update_application_translations
# LUPDATE_OPTIONS "-locations none -no-obsolete"
# LRELEASE_TARGET compile_application_translations
# LRELEASE_OPTIONS "-removeidentical -compress -nounfinished"
# QM_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/../../bin/i18n
# MERGE_QT_TRANSLATIONS
#)
Loading