Setting up a translation system for your Android and iOS app
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!