Login Account

Android

Developing a (native) Android app?

Follow these steps to integrate doloc into your workflow and make sure your app is translated with great quality.

Internationalization in Android

If you are not already familiar with Android localization, you can check the Android documentation for the standard setup to translate your app into different languages.

Add doloc to your workflow

For convenience, you can add doloc to your Gradle scripts. Use Groovy or Kotlin DSL depending on your project setup.

Groovy DSL (build.gradle)

import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.nio.file.Files
import java.nio.file.Path

// other existing plugins/tasks..

static def translate(String targetLang, Path sourceFile, Path targetFile) {
  def boundary = "Boundary-${System.currentTimeMillis()}"
  HttpClient.newHttpClient().send(
    HttpRequest.newBuilder().uri(URI.create("https://api.doloc.io?targetLang=$targetLang"))
      .header("Content-Type", "multipart/form-data; boundary=$boundary")
      .header("Authorization", "Bearer ${System.getenv("API_TOKEN")}")
      .POST(
        HttpRequest.BodyPublishers.ofString(
          "--$boundary\r\n" +
          "Content-Disposition: form-data; name=\"source\"; filename=\"${sourceFile.fileName}\"\r\n" +
          "Content-Type: application/octet-stream\r\n\r\n" +
          "${Files.readString(sourceFile)}\r\n" +
          "--$boundary\r\n" +
          "Content-Disposition: form-data; name=\"target\"; filename=\"${targetFile.fileName}\"\r\n" +
          "Content-Type: application/octet-stream\r\n\r\n" +
          "${Files.readString(targetFile)}\r\n" +
          "--$boundary--\r\n"
        )
      )
      .build(), HttpResponse.BodyHandlers.ofFile(targetFile)
  )
}

tasks.register("doloc-fr") {
  doLast {
    translate(
      "fr",
      project.file("app/src/main/res/values/strings.xml").toPath(),
      project.file("app/src/main/res/values-fr/strings.xml").toPath()
    )
  }
}

tasks.register("doloc-es") {
  doLast {
    translate(
      "es",
      project.file("app/src/main/res/values/strings.xml").toPath(),
      project.file("app/src/main/res/values-es/strings.xml").toPath()
    )
  }
}

tasks.register("update-i18n") {
  dependsOn("doloc-fr", "doloc-es")
}

Kotlin DSL (build.gradle.kts)

import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.nio.file.Files
import java.nio.file.Path

// other existing plugins/tasks..

fun translate(targetLang: String, sourceFile: Path, targetFile: Path) {
  val boundary = "Boundary-${System.currentTimeMillis()}"
  HttpClient.newHttpClient().send(
    HttpRequest.newBuilder().uri(URI.create("https://api.doloc.io?targetLang=$targetLang"))
      .header("Content-Type", "multipart/form-data; boundary=$boundary")
      .header("Authorization", "Bearer ${System.getenv("API_TOKEN")}")
      .POST(
        HttpRequest.BodyPublishers.ofString(
          "--$boundary\r\n" +
          "Content-Disposition: form-data; name=\"source\"; filename=\"${sourceFile.fileName}\"\r\n" +
          "Content-Type: application/octet-stream\r\n\r\n" +
          "${Files.readString(sourceFile)}\r\n" +
          "--$boundary\r\n" +
          "Content-Disposition: form-data; name=\"target\"; filename=\"${targetFile.fileName}\"\r\n" +
          "Content-Type: application/octet-stream\r\n\r\n" +
          "${Files.readString(targetFile)}\r\n" +
          "--$boundary--\r\n"
        )
      )
      .build(), HttpResponse.BodyHandlers.ofFile(targetFile)
  )
}

tasks.register("doloc-fr") {
  doLast {
    translate(
      "fr",
      project.file("app/src/main/res/values/strings.xml").toPath(),
      project.file("app/src/main/res/values-fr/strings.xml").toPath()
    )
  }
}

tasks.register("doloc-es") {
  doLast {
    translate(
      "es",
      project.file("app/src/main/res/values/strings.xml").toPath(),
      project.file("app/src/main/res/values-es/strings.xml").toPath()
    )
  }
}

tasks.register("update-i18n") {
  dependsOn("doloc-fr", "doloc-es")
}

Make sure to update the translation file paths and to replace ${System.getenv("API_TOKEN")} with your API token or set a corresponding environment variable. The API token can be found or created in your doloc account.

With this setup, you can run gradle update-i18n translate all new texts into all target languages! 🎉

Running the script

Generally, this script should be run by the developer after adding or updating translations - and not on the CI/CD pipeline. This has the advantage that the code always stays in sync with the translations and that the developer can check the translations before committing and/or merging them.

Of course, you can also run the script in the CI/CD pipeline - for a detailed discussion, see When to Run doloc?.

Handling updated texts

When changing texts in the source file, one should remove the corresponding texts in the target file to ensure that the translations are updated. Alternatively, one can manually update the translations in the target file, of course.

Storing the API token

Your API tokens generally should not be shared publicly.

Having said that, if your repository is private, it is acceptable to store the API token in your repository. The worst-case scenario is that others might use your quota, but never access or change any of your data. Of course, you can always revoke the token and create a new one if needed.

Configuration

The default configurations of doloc works well for Android XML resources and covers most use cases.

For more advanced configurations, doloc can be configured by passing options in the URL - see Options for general information and Android XML Resources for specific options.

Example

In our Android demo, you can see how doloc is integrated into a Android project. There, both the local and CI workflows are set up and ready to use.