From 1a50635a8f4c59160817e254234f4fba5db8ff44 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 20 Oct 2026 16:05:39 +0200 Subject: [PATCH] chore: remove transifex from the translation workflow --- .env.example | 1 - .tx/config | 8 - AGENTS.md | 1 + Justfile | 6 - README.md | 42 +-- journeys/shop/gift-card-category-titles.xml | 2 +- scripts/pull-translations.sh | 286 -------------------- scripts/push-translations.sh | 102 ------- 8 files changed, 5 insertions(+), 443 deletions(-) delete mode 100755 .tx/config delete mode 100755 scripts/pull-translations.sh delete mode 100755 scripts/push-translations.sh diff --git a/.env.example b/.env.example index 78d23fa491..f4018fdffa 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,6 @@ # GITHUB_ACTOR= # GITHUB_TOKEN= -# TX_TOKEN= # KEYSTORE_FILE= # KEYSTORE_PASSWORD= diff --git a/.tx/config b/.tx/config deleted file mode 100755 index a698677d01..0000000000 --- a/.tx/config +++ /dev/null @@ -1,8 +0,0 @@ -[main] -host = https://app.transifex.com - -[o:synonym:p:bitkit:r:stringsxml] -file_filter = app/src/main/res/values-/strings.xml -source_file = app/src/main/res/values/strings.xml -source_lang = en -type = ANDROID diff --git a/AGENTS.md b/AGENTS.md index cbc1061023..3bf01fb6c2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -351,6 +351,7 @@ suspend fun getData(): Result = withContext(Dispatchers.IO) { - ALWAYS add business logic to repository layer via methods returning `Result` and use it in ViewModels - ALWAYS order upstream architectural data flow this way: `UI -> ViewModel -> Repository -> RUST` and vice versa for downstream - ALWAYS add new localizable string resources in alphabetical order in `strings.xml` +- ALWAYS add or update the translation in every `values-*/strings.xml` in the same PR when adding or changing an English string; translations live in this repository, not in Transifex - NEVER add string resources for strings used only in dev settings screens and previews and never localize acronyms - ALWAYS use template in `.github/pull_request_template.md` for PR descriptions - ALWAYS reference test files in PR descriptions/QA Notes by bare file name only (e.g. `HwWalletRepoTest.kt`), NEVER the full path; only when two referenced test files share the same name, prefix the shortest leading path segment(s) that disambiguate them (e.g. `repositories/FooTest.kt` vs `viewmodels/FooTest.kt`) diff --git a/Justfile b/Justfile index 77e5806acc..47ffc68fac 100644 --- a/Justfile +++ b/Justfile @@ -24,9 +24,6 @@ list: "lint" \ "lint baseline" \ "format" \ - "translations pull" \ - "translations push source" \ - "translations push all" \ "e2e [network|no geo|TASK] [TASK]" \ "changelog [all|next|hotfix]" \ "clean" @@ -193,9 +190,6 @@ lint target="": format: {{ gradle }} detekt --auto-correct --rerun-tasks -translations action value="": - {{ if action == "pull" { "./scripts/pull-translations.sh" } else if action == "push" { if value == "source" { "tx push --source" } else if value == "all" { "./scripts/push-translations.sh" } else { error("usage: just translations pull|push source|push all") } } else { error("usage: just translations pull|push source|push all") } }} - e2e mode="" value="" task="assembleDevRelease": {{ if mode == "" { "E2E=true " + gradle + " " + task } else if mode == "network" { "E2E=true E2E_BACKEND=network " + gradle + " " + if value == "" { task } else { value } } else if mode == "no" { if value == "geo" { "GEO=false E2E=true " + gradle + " " + task } else { error("usage: just e2e no geo [TASK]") } } else { "E2E=true " + gradle + " " + mode } }} diff --git a/README.md b/README.md index 29b0f0d82c..2bc5e0bd68 100644 --- a/README.md +++ b/README.md @@ -60,9 +60,9 @@ Install `just` ([more options](https://github.com/casey/just#packages)): Set up local env: - `just init` -- Uncomment only the values you need in the fresh `.env` file, such as `GITHUB_ACTOR`, `GITHUB_TOKEN`, `TX_TOKEN`, and E2E build settings. +- Uncomment only the values you need in the fresh `.env` file, such as `GITHUB_ACTOR`, `GITHUB_TOKEN`, and E2E build settings. -Run `just list` to see available commands. The common ones are `just init`, `just compile`, `just run`, `just build`, `just release`, `just test`, `just lint`, and `just translations pull`. `just run` prefers a physical device and falls back to an emulator. +Run `just list` to see available commands. The common ones are `just init`, `just compile`, `just run`, `just build`, `just release`, `just test`, and `just lint`. `just run` prefers a physical device and falls back to an emulator. ### Trezor Bridge In Android Studio @@ -122,43 +122,7 @@ just test android # run ## Localization -### Pulling Translations - -To pull the latest translations from Transifex: - -1. **Install Transifex CLI** (if not already installed): - - Follow the installation instructions: [Transifex CLI Installation](https://developers.transifex.com/docs/cli) - -2. **Authenticate with Transifex** (if not already configured): - - Set the `TX_TOKEN` environment variable with your API token: - ```sh - export TX_TOKEN="YOUR_API_TOKEN_HERE" - ``` - - You can get your API token from [Transifex account settings](https://www.transifex.com/user/settings/api/) - - Add it to `~/.zshrc` or other shell rc file to persist across sessions - -3. **Pull translations**: - ```sh - just translations pull - ``` - -### Pushing Source Strings - -When you add or modify translation keys in the EN source file, push them to Transifex: - -```sh -just translations push source -``` - -To intentionally round-trip local source and translation files back to Transifex, use `just translations push all`. - -### Translation Workflow - -1. **Add/modify strings** in `app/src/main/res/values/strings.xml` -2. **Push to Transifex:** `just translations push source` -3. **Translators** work on translations in Transifex -4. **Pull translations:** `just translations pull` -5. **Commit** the updated translation files +Translations live in this repository, in `app/src/main/res/values-*/strings.xml`. A PR that adds or changes an English string in `app/src/main/res/values/strings.xml` updates every shipped locale in the same PR. ## Build diff --git a/journeys/shop/gift-card-category-titles.xml b/journeys/shop/gift-card-category-titles.xml index f94b01fd5c..5a67c88ecb 100644 --- a/journeys/shop/gift-card-category-titles.xml +++ b/journeys/shop/gift-card-category-titles.xml @@ -11,7 +11,7 @@ an `other__shop__categories__*` key yet, so every row reads English in every shipped locale either way. A pseudolocale cannot close the gap: `androidResources.localeFilters` (`app/build.gradle.kts:276`) keeps only the 16 shipped locales, so `en-XA` resources are - stripped. Once the Transifex pull lands the German strings, replace the German step below with + stripped. Once the German strings land, replace the German step below with assertions on concrete translated titles ("Food Delivery" and "Health & Beauty" rows plus the web view top bar) and drop the English fallback allowance; that is what makes this journey discriminate. "VoIP" is not translatable and stays "VoIP" in every locale. diff --git a/scripts/pull-translations.sh b/scripts/pull-translations.sh deleted file mode 100755 index a89cba258e..0000000000 --- a/scripts/pull-translations.sh +++ /dev/null @@ -1,286 +0,0 @@ -#!/bin/bash - -# Script to pull translations from Transifex and clean up empty files/directories - -set -e - -# Validate script is run from project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -RES_DIR="$PROJECT_ROOT/app/src/main/res" - -if [ ! -d "$RES_DIR" ]; then - echo "Error: Resource directory not found: $RES_DIR" - echo "Please run this script from the project root directory." - exit 1 -fi - -if [ ! -f "$PROJECT_ROOT/.tx/config" ]; then - echo "Error: Transifex config not found: $PROJECT_ROOT/.tx/config" - echo "Please ensure Transifex is configured for this project." - exit 1 -fi - -# Helper function to rename or merge directories -rename_or_merge() { - local src="$1" - local dst="$2" - local src_name=$(basename "$src") - local dst_name=$(basename "$dst") - local merge_errors=0 - - if [ ! -d "$src" ]; then - echo " Warning: Source directory does not exist: $src_name" - return 1 - fi - - if [ ! -d "$dst" ]; then - echo " Renaming: $src_name -> $dst_name" - if mv "$src" "$dst"; then - return 0 - else - echo " Error: Failed to rename $src_name to $dst_name" - return 1 - fi - else - echo " Merging: $src_name -> $dst_name" - while IFS= read -r -d '' item; do - if ! mv "$item" "$dst/" 2>/dev/null; then - echo " Warning: Failed to move $(basename "$item") from $src_name" - merge_errors=$((merge_errors + 1)) - fi - done < <(find "$src" -mindepth 1 -maxdepth 1 -print0 2>/dev/null) - - # Only remove source directory if merge was successful - if [ "$merge_errors" -eq 0 ]; then - if rmdir "$src" 2>/dev/null; then - return 0 - else - echo " Warning: Could not remove empty directory: $src_name" - return 1 - fi - else - echo " Error: Some files could not be merged from $src_name" - return 1 - fi - fi -} - -# Validate XML file is well-formed before processing -validate_xml() { - local file="$1" - if command -v python3 &> /dev/null; then - python3 - "$file" <<'PY' -import sys -import xml.etree.ElementTree as ET - -try: - ET.parse(sys.argv[1]) -except Exception as e: - print(e) - raise SystemExit(1) -PY - return $? - fi - - # Fallback validation: check for opening and closing resources tags - if ! grep -q '/dev/null || ! grep -q '' "$file" 2>/dev/null; then - echo "missing resources root" - return 1 - fi - - return 0 -} - -echo "Pulling translations from Transifex..." - -# Check if tx command is available -if ! command -v tx &> /dev/null; then - echo "Error: Transifex CLI (tx) is not installed or not in PATH" - echo "Please install it: https://developers.transifex.com/docs/cli" - exit 1 -fi - -# Check if TX_TOKEN is set -if [ -z "$TX_TOKEN" ]; then - echo "Error: TX_TOKEN environment variable is not set" - echo "Please set it with your Transifex API token: export TX_TOKEN=\"your-token\"" - exit 1 -fi - -# Run tx pull and check for errors -set +e # Temporarily disable exit on error to check tx pull status -tx pull -a -TX_EXIT_CODE=$? -set -e # Re-enable exit on error - -if [ "$TX_EXIT_CODE" -ne 0 ]; then - echo "Error: Transifex pull failed with exit code $TX_EXIT_CODE" - echo "Please check your Transifex configuration and authentication." - exit 1 -fi - -echo "" -echo "Renaming and cleaning up directories..." - -RENAMED_COUNT=0 -REMOVED_COUNT=0 - -# Process all values-* directories -while IFS= read -r dir; do - dir_name=$(basename "$dir") - dir_path=$(dirname "$dir") - - case "$dir_name" in - # Keep these as-is (direct 1:1 mapping from Transifex or already in correct format) - values-ar|values-b+es+419|values-ca|values-cs|values-de|values-el|values-es|values-es-rES|values-fr|values-it|values-nl|values-pl|values-pt|values-pt-rBR|values-ru) - ;; - # RENAME Transifex locales to Android folder names - values-es_419) - echo " Renaming: $dir_name -> values-b+es+419" - rm -rf "$dir_path/values-b+es+419" 2>/dev/null - mv "$dir" "$dir_path/values-b+es+419" - RENAMED_COUNT=$((RENAMED_COUNT + 1)) - ;; - values-es_ES) - echo " Renaming: $dir_name -> values-es-rES" - rm -rf "$dir_path/values-es-rES" 2>/dev/null - mv "$dir" "$dir_path/values-es-rES" - RENAMED_COUNT=$((RENAMED_COUNT + 1)) - ;; - values-pt_BR) - echo " Renaming: $dir_name -> values-pt-rBR" - rm -rf "$dir_path/values-pt-rBR" 2>/dev/null - mv "$dir" "$dir_path/values-pt-rBR" - RENAMED_COUNT=$((RENAMED_COUNT + 1)) - ;; - # DELETE 0% or near-0% translation locales - values-arb|values-pt_PT|values-ja|values-ko|values-no|values-fa|values-ro|values-uk|values-yo) - echo " Removing: $dir_name (0% translations)" - rm -rf "$dir" - REMOVED_COUNT=$((REMOVED_COUNT + 1)) - ;; - # Handle any other BCP 47 formats - values-b+*) - new_name=$(echo "$dir_name" | sed 's/values-b+\([a-z][a-z]*\)+\([A-Z0-9][A-Z0-9]*\)/values-\1_\2/') - if [ "$new_name" != "$dir_name" ]; then - echo " Renaming: $dir_name -> $new_name" - mv "$dir" "$dir_path/$new_name" - RENAMED_COUNT=$((RENAMED_COUNT + 1)) - fi - ;; - esac -done < <(find "$RES_DIR" -type d -name "values-*" 2>/dev/null | sort) - -echo " Renamed $RENAMED_COUNT directories" -echo " Removed $REMOVED_COUNT directories" - -echo "" -echo "Normalizing XML formatting..." - -# Normalize XML -MARKUP_FIXED_COUNT=0 -NORMALIZED_COUNT=0 -XML_ERROR_COUNT=0 -while IFS= read -r file; do - if sed \ - -e 's# "$file.markup.tmp"; then - if cmp -s "$file" "$file.markup.tmp"; then - rm -f "$file.markup.tmp" - elif mv "$file.markup.tmp" "$file"; then - MARKUP_FIXED_COUNT=$((MARKUP_FIXED_COUNT + 1)) - else - echo " Warning: Failed to fix markup in $file" - rm -f "$file.markup.tmp" - fi - else - echo " Warning: Failed to scan markup in $file" - rm -f "$file.markup.tmp" - fi - - # Validate XML before processing - if ! validation_error=$(validate_xml "$file" 2>&1); then - echo " Error: $file is malformed XML: $validation_error" - XML_ERROR_COUNT=$((XML_ERROR_COUNT + 1)) - continue - fi - - if awk ' - { - gsub(/[[:space:]]+$/, "") - if ($0 ~ /^[[:space:]]*<\/resources>[[:space:]]*$/) { - print "" - saw_resources = 1 - next - } - if (saw_resources && $0 == "") next - if (saw_resources && $0 != "") saw_resources = 0 - print - } - ' "$file" > "$file.tmp"; then - if mv "$file.tmp" "$file" 2>/dev/null; then - NORMALIZED_COUNT=$((NORMALIZED_COUNT + 1)) - else - echo " Warning: Failed to replace $file" - rm -f "$file.tmp" - fi - else - echo " Warning: Failed to normalize $file" - rm -f "$file.tmp" - fi -done < <(find "$RES_DIR" -type f -path "*/values-*/strings.xml" 2>/dev/null) - -echo " Fixed markup in $MARKUP_FIXED_COUNT files" -echo " Normalized $NORMALIZED_COUNT files" - -if [ "$XML_ERROR_COUNT" -ne 0 ]; then - echo "Error: Found $XML_ERROR_COUNT malformed XML file(s) after pull" - exit 1 -fi - -echo "" -echo "Cleaning up empty files and directories..." - -EMPTY_COUNT=0 -DELETED_DIRS=0 -declare -a dirs_to_check - -# Delete empty files and collect their directories -set +e # Allow commands to fail for empty file detection -while IFS= read -r file; do - line_count=$(wc -l < "$file" 2>/dev/null | tr -d ' ' || echo "0") - string_count=$(grep -c '/dev/null | wc -l | tr -d ' ') - set -e - if [ "$file_count" -eq 0 ]; then - echo " Deleting empty directory: $dir" - if rmdir "$dir" 2>/dev/null; then - DELETED_DIRS=$((DELETED_DIRS + 1)) - fi - fi -done - -echo "" -echo "Complete!" -echo " Renamed: $RENAMED_COUNT, Removed: $REMOVED_COUNT" -echo " Normalized: $NORMALIZED_COUNT" -echo " Deleted files: $EMPTY_COUNT, Deleted dirs: $DELETED_DIRS" -echo "Pull complete!" diff --git a/scripts/push-translations.sh b/scripts/push-translations.sh deleted file mode 100755 index fee8b71088..0000000000 --- a/scripts/push-translations.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash - -# Script to push translations to Transifex -# Handles directory naming conversion between Android format and Transifex format - -set -e - -# Validate script is run from project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -RES_DIR="$PROJECT_ROOT/app/src/main/res" - -if [ ! -d "$RES_DIR" ]; then - echo "Error: Resource directory not found: $RES_DIR" - echo "Please run this script from the project root directory." - exit 1 -fi - -if [ ! -f "$PROJECT_ROOT/.tx/config" ]; then - echo "Error: Transifex config not found: $PROJECT_ROOT/.tx/config" - echo "Please ensure Transifex is configured for this project." - exit 1 -fi - -# Check if tx command is available -if ! command -v tx &> /dev/null; then - echo "Error: Transifex CLI (tx) is not installed or not in PATH" - echo "Please install it: https://developers.transifex.com/docs/cli" - exit 1 -fi - -# Check if TX_TOKEN is set -if [ -z "$TX_TOKEN" ]; then - echo "Error: TX_TOKEN environment variable is not set" - echo "Please set it with your Transifex API token: export TX_TOKEN=\"your-token\"" - exit 1 -fi - -# Track renamed directories for cleanup -RENAMED_ES_ES=false -RENAMED_PT_BR=false -RENAMED_ES_419=false - -# Cleanup function to restore Android directory names -cleanup() { - echo "" - echo "Restoring Android directory names..." - - if [ "$RENAMED_ES_ES" = true ] && [ -d "$RES_DIR/values-es_ES" ]; then - echo " Restoring: values-es_ES -> values-es-rES" - mv "$RES_DIR/values-es_ES" "$RES_DIR/values-es-rES" - fi - - if [ "$RENAMED_PT_BR" = true ] && [ -d "$RES_DIR/values-pt_BR" ]; then - echo " Restoring: values-pt_BR -> values-pt-rBR" - mv "$RES_DIR/values-pt_BR" "$RES_DIR/values-pt-rBR" - fi - - if [ "$RENAMED_ES_419" = true ] && [ -d "$RES_DIR/values-es_419" ]; then - echo " Restoring: values-es_419 -> values-b+es+419" - mv "$RES_DIR/values-es_419" "$RES_DIR/values-b+es+419" - fi - - echo "Cleanup complete." -} - -# Set trap to ensure cleanup runs even if script fails -trap cleanup EXIT - -echo "Pushing translations to Transifex..." -echo "" - -# Step 1: Rename Android directories to Transifex format -echo "Converting directory names to Transifex format..." - -if [ -d "$RES_DIR/values-es-rES" ]; then - echo " Renaming: values-es-rES -> values-es_ES" - mv "$RES_DIR/values-es-rES" "$RES_DIR/values-es_ES" - RENAMED_ES_ES=true -fi - -if [ -d "$RES_DIR/values-pt-rBR" ]; then - echo " Renaming: values-pt-rBR -> values-pt_BR" - mv "$RES_DIR/values-pt-rBR" "$RES_DIR/values-pt_BR" - RENAMED_PT_BR=true -fi - -if [ -d "$RES_DIR/values-b+es+419" ]; then - echo " Renaming: values-b+es+419 -> values-es_419" - mv "$RES_DIR/values-b+es+419" "$RES_DIR/values-es_419" - RENAMED_ES_419=true -fi - -echo "" -echo "Running tx push..." - -# Step 2: Push source and translations to Transifex -cd "$PROJECT_ROOT" -tx push --source --translation --all --skip --force - -echo "" -echo "Push complete!"