Freelancing for Pale Blue

Looking for flexible work opportunities that fit your schedule?


Setting up a translation system for your Android and iOS app

Android Mar 28, 2025

When developing an Android app, managing translations is straightforward: all strings are stored in strings.xml. But when you need to synchronize translations with an iOS counterpart (or even a web or desktop application), things become more complex.

Choosing a translation management system

We decided to use Weblate, one of the most popular open-source translation management systems. It offers both a hosted version and a self-hosting option.

For small-to-medium projects, self-hosting with Docker is a viable choice, providing full control over the translation workflow.

Integrating with the Android and iOS apps

To streamline the translation update process, we created a script that fetches the latest translations from Weblate and places them in the appropriate directories for Android and iOS. This script can be run manually whenever translations need updating or integrated into the build system for automation.

Translation Update Script

#!/bin/bash

# Show usage if no format is provided
if [ $# -eq 0 ]; then
    echo "Usage: $0 <android|ios>"
    exit 1
fi

# Get format from first argument
FORMAT=$1

# Validate format
if [[ "$FORMAT" != "android" && "$FORMAT" != "ios" ]]; then
    echo "Error: Format must be either 'android' or 'ios'"
    exit 1
fi

# Default values
HOST="http://YOUR-WEBLATE-INSTANCE.com"
PROJECT_ID="PROJECT_ID"
COMPONENT_ID="mobile"

# Get list of available languages
echo "Fetching available languages..."
LANGUAGES_URL="$HOST/api/components/$PROJECT_ID/$COMPONENT_ID/translations/"
LANGUAGES=$(curl -s "$LANGUAGES_URL" | grep -o '"language_code":"[^"]*"' | cut -d'"' -f4)

if [[ -z "$LANGUAGES" ]]; then
    echo "No languages found or error in API response"
    exit 1
fi

# Download translations for each language
echo "Downloading translations..."
for lang in $LANGUAGES; do
    if [ "$FORMAT" = "android" ]; then
        OUTPUT_DIR="androidApp/src/main/res"
        # Convert language code for Android (e.g., zh-CN to zh-rCN)
        android_lang=$(echo "$lang" | sed 's/-\([A-Z]\)/-r\1/g')
        if [ "$lang" = "en" ]; then
             dir_structure="$OUTPUT_DIR/values"
         else
             dir_structure="$OUTPUT_DIR/values-$android_lang"
         fi
        mkdir -p dir_structure
        filename="strings.xml"
        file_format="aresource"
    else
        OUTPUT_DIR="iosApp/Paid/Resources/Localization"
        dir_structure="$OUTPUT_DIR/$lang.lproj"
        filename="Localizable.strings"
        file_format="strings"
    fi

    mkdir -p "$dir_structure"
    url="$HOST/api/translations/$PROJECT_ID/$COMPONENT_ID/$lang/file/?format=$file_format"

    if curl -s "$url" -o "$dir_structure/$filename"; then
        echo "✓ Downloaded $lang"
    else
        echo "✗ Failed to download $lang"
    fi
done

echo "Done! Translations saved in $OUTPUT_DIR"

update_translations.sh (Linux and MacOS)

Automating translation updates in Android builds

If your app receives frequent translation updates, you can integrate the script into the build process to ensure you always have the latest translations before each build.

Add the following task to your Gradle build script:

val updateWeblateTranslations by tasks.registering(Exec::class) {
    workingDir = file("..")
    commandLine("bash", "updateTranslations.sh", "android")
}

tasks.preBuild.dependsOn(updateWeblateTranslations)

build.gradle

With this setup, your translations will always be updated whenever you build your Android app.

Conclusion

By leveraging Weblate and creating a custom synchronization script, we've developed a robust, scalable translation management system that works seamlessly across Android and iOS.

The key is automation: reducing manual steps, ensuring consistency, and making multilingual support almost effortless.

Happy coding!

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.