DEV Community

Cover image for Android XML Resources: Automated Translations with doloc
Daniel Schreiber
Daniel Schreiber

Posted on

Android XML Resources: Automated Translations with doloc

Developing multilingual apps efficiently can be a real challenge - especially when working with various frameworks and tools. This is where doloc comes into play: a lightweight solution for automated translations that integrates seamlessly into your existing workflows. The focus is on great translations and no need for a glossary, as all texts automatically serve implicitly as a glossary.

Today, we’re excited to introduce our latest integration for Android!

In this article, we’ll show you how to use doloc to automate translations in your Android project and save valuable time. It takes you only two steps before you can extract and translate your texts automatically!

Android: Automating Translations with doloc

  1. Set up Andoroid Localization with the Andorid documentation.
  2. Add doloc to your workflow by adding the following 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")
}
Enter fullscreen mode Exit fullscreen mode

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")
}
Enter fullscreen mode Exit fullscreen mode
  • 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 in your doloc account.

Now run gradle update-i18n to translate all new texts into all target languages!

When should the skript be run by the developer?

This script should be run 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. (Furthermore, this simplifies the CI/CD pipeline and prevents unnecessary commits for translations only.)

How to handle 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.

More Info

To find more info and details to integrate doloc into your Andorid workflow follow our Guide: https://doloc.io/getting-started/frameworks/android/.

Also we provide detailed information on configuration of specific options in the URL in our Android XML Resources guide.

Summary

By using the Android integration of doloc you have the following advantages:

  • Easy integration into the development workflow
  • Instant translations of texts via API
  • Consistent style without a dictionary
  • Reduced translator workload
  • Accelerated time to market
  • And, of course: Great translations!

Leave a comment or send us a message in case you need help or you have questions!

P.S. This also works for many other formats and frameworks!
Be sure to check out our guides for Angular and FormatJS/react-intl.

Sentry blog image

The countdown to March 31 is on.

Make the switch from app center suck less with Sentry.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay