Skip to content

Gradle plugin to auto. translate your Android project (strings.xml + Fastlane) into any language using external Translation providers

License

Notifications You must be signed in to change notification settings

Crustack/auto-translation-gradle-plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Auto Translation Plugin

Gradle Plugin Portal Version

Gradle plugin to automatically translate your Android project (strings.xml + Fastlane metadata) into any language using external services like DeepL, Google Cloud Translation, Azure AI Translator, LibreTranslate, or OpenAI.

Features

  • Detect missing translations for any existing or new language your project's strings.xml files
  • Automatically translate missing entries via configured Translation Provider
  • Support for Android quantity strings (plurals)
  • Correctly preserve special formatting characters and HTML tags in strings.xml
  • Auto-sorts translations by their name attribute
  • Translate Fastlane metadata text files

Supported Translation Providers

Setup

In build.gradle.kts:

plugins {
    id("io.github.philkes.auto-translation") version "1.0.0"
}

// Minimal example: translate strings.xml for all present language folders using DeepL
autoTranslate {
    provider = deepL {
        authKey = "YOUR_AUTH_KEY"
        // Alternatively get it via an environment variable:
        // authKey = System.getenv("DEEPL_API_KEY")
    }
}

Run:

./gradlew autoTranslate

Configuration

Shown values are defaults or placeholders.

autoTranslate {
    // Language of the source strings (values/strings.xml)
    sourceLanguage = "en-US" // default

    // Explicit target languages (ISO codes like "de", "fr", "pt-BR").
    // By default targetLanguages are auto-detected from existing values-* folders.
    targetLanguages = listOf(/* ... */)

    // When auto-detecting targets, you can exclude some languages
    excludeLanguages = listOf()  // default

    // Configure strings.xml translation
    translateStringsXml {
        enabled = true // default
        // Path to the folder containing the `values/strings.xml` and `values-{targetLanguage}` folders.
        resDirectory = project.layout.projectDirectory.dir("src/main/res") // default
    }

    // Configure Fastlane metadata translation
    translateFastlane {
        enabled = false // default
        // Path to fastlane metadata root folder, containing `{targetLanguage` folders
        // with description, changelogs txt files
        metadataDirectory = project.rootProject.layout.projectDirectory.dir("fastlane/metadata/android") // default
        // Language of the source fastlane documentation
        sourceLanguage = "en-US"  // Defaults to autoTranslate.sourceLanguage
        // Explicit target languages for Fastlane
        targetLanguages = setOf("de-DE") // Defaults to autoTranslate.targetLanguages
    }
    
    provider = // Choose ONE of the providers from below section "Providers"
}

Providers

DeepL

import io.github.philkes.auto.translation.plugin.provider.deepl.DeepLTextTranslationOptions
//...
provider = deepL {
    // Authentication Key for DeepL API
    authKey = "YOUR_API_KEY"
    // (Optional) Overwrite DeepL specific settings for the translations
    // See https://github.com/DeepLcom/deepl-java?tab=readme-ov-file#text-translation-options
    options = DeepLTextTranslationOptions()
        .setPreserveFormatting(true)
}

Google

import io.github.philkes.auto.translation.plugin.provider.google.GoogleTranslateOptions
//...
provider = google {
    // Configure Google TranslateOptions, must set at least credential
    options = GoogleTranslateOptions() 
                // Google offers many different authentication methods, e.g. api key:
                .setCredentials(ApiKeyCredentials.create("API_KEY"))
                // E.g. set Google Project Id for quota and billing purposes
                .setQuotaProjectId("XYZ")
    // (Optional) Specify model (e.g., "nmt" or "base")
    model = "base" // default
}

Azure

import io.github.philkes.auto.translation.plugin.provider.azure.AzureTextTranslationClientBuilder
//...
provider = azure {
    // Configure Azure TextTranslationClientBuilder, must set at least credential
    options =
        AzureTextTranslationClientBuilder() 
            // Azure offers different authentication methods, e.g. api key:
            .credential("YOUR_API_KEY")
            // (Optional) E.g. set Azure Resource Id and region used to authorize requests
            .resourceId("XYZ").region("Region")
}

LibreTranslate

provider = libreTranslate {
    // (Optional) Base URL should be the server root (the plugin uses the /translate endpoint)
    // Use your own instance or the public one
    baseUrl = "https://libretranslate.com" // default
    // (Optional) API key if your instance requires it
    apiKey = "YOUR_API_KEY"
}

OpenAI

Open AI does not offer a direct translation API like the other providers.

Instead, we leverage System-Messages and Structured-Outputs to get translated texts from OpenAI's completion API. Since there are other providers that are OpenAI compatible, you could also use this e.g. with Ollama

import io.github.philkes.auto.translation.plugin.provider.OpenAIOkHttpClientBuilder
//...
provider = openAI {
    // Configure OpenAI OpenAIOkHttpClient, must set at least apiKey or credential
    options = OpenAIOkHttpClientBuilder
        .apiKey("YOUR_API_KEY")
        // (Optional) E.g. change the base url, any OpenAi-Compatible API should work
        .baseUrl("https://api.openai.com")

    // (Optional) overwrite used model
    model = "gpt-4o-mini" // default

    // (Optional) overwrite the system message used to guide the assistant
    systemMessage = """
            You're a professional translator for software projects, especially Android apps.
            You are given text in a specified source language, and should translate it in the most suitable way to the specified target language.
            The given texts can contain XML/HTML tags, as well as special string formatting placeholders like '%1$s',
             do not translate them and keep them at the position they were originally by preserving the format of the text.
            The input is a JSON object that contains:
             - the texts' source language ('srcLang'),
             - the desired target languages ('targetLangs'),
             - the list of texts that should be translated.
        """ // default
}

Github Action

If you want to integrate the plugin into a Github Action Workflow:

  1. Add the plugin as described in Setup
  2. Add the github-action/add-missing-translations.yaml to your repo's .github/workflows folder
  3. (Optional): Customize the on triggers, commit_message in the add-missing-translation.yaml if needed
  4. To not expose your API Key/Credentials for your configured API, add Github Secrets for them. For example, if you use DeepL with an API Key, create a secret called "AUTO_TRANSLATE_API_KEY"
  5. In your build.gradle.kts, set your API credentials to be read from an environment variable, e.g. for DeepL:
    autoTranslate {
        //...
        provider = deepL {
            apiKey = System.getenv("AUTO_TRANSLATE_API_KEY")
        }    
    }
  6. In your .github/workflows/add-missing-translation.yaml make sure the environment variables are properly set:
      #...
      jobs:
        auto-translate:
          #...
          steps:
            #...
            - name: Run Gradle autoTranslate
              env:
                AUTO_TRANSLATE_API_KEY: ${{ secrets.AUTO_TRANSLATE_API_KEY }}
              run: ./gradlew autoTranslate
  7. Now when you push new changes to your strings.xml or fastlane files, the plugin will be automatically run and the changes commited by Github Actions

Fastlane specifics

  • The Fastlane metadata root contains locale-named subfolders (e.g., en-US, de-DE).
  • All .txt files inside these folders (recursively) are translated.
  • The entire content of each .txt file is sent as a single string (no line splitting).
  • When targets are autodetected, you can exclude some via excludeLanguages on the main task.

Plurals

Android supports quantity strings (plurals). For every <item> quantity in the sourceLanguage's strings.xml, a translation is requested and then written as a single <plurals> block with ordered quantities.

Run on build (optional)

If you want the autoTranslate to run automatically on every build, in build.gradle.kts:

preBuild.dependsOn("autoTranslate")

Pre-commit hook (optional)

If you want autoTranslate to run automatically before any git commit:

  1. Copy the pre-commit folder to the root of your project
  2. In build.gradle:
    tasks.register('installLocalGitHooks', Copy) {
        def scriptsDir = new File(rootProject.rootDir, 'scripts/')
        def hooksDir = new File(rootProject.rootDir, '.git/hooks')
        from(scriptsDir) {
            include 'pre-commit', 'pre-commit.bat'
        }
        into { hooksDir }
        inputs.files(file("${scriptsDir}/pre-commit"), file("${scriptsDir}/pre-commit.bat"))
        outputs.dir(hooksDir)
        fileMode 0775
    }
    preBuild.dependsOn installLocalGitHooks
  3. Whenever you create a git commit and there are changes in src/main/res/values/strings.xml, translations will be kept up to date.

About

Gradle plugin to auto. translate your Android project (strings.xml + Fastlane) into any language using external Translation providers

Topics

Resources

License

Stars

Watchers

Forks

Languages