
Translate structured content from Java with the PolyLingo SDK
By Robert
Translate structured content from Java with the PolyLingo SDK
The PolyLingo Java SDK is now available on Maven Central. It covers the full PolyLingo API: synchronous translation, batch requests, async jobs with polling, and all utility endpoints. It requires Java 11 or later, uses the standard library HTTP client, and has exactly one runtime dependency: Jackson for JSON.
Installation
Maven
<dependency>
<groupId>com.usepolylingo</groupId>
<artifactId>polylingo</artifactId>
<version>0.1.0</version>
</dependency>
Gradle
implementation 'com.usepolylingo:polylingo:0.1.0'
No additional HTTP client dependency required. The SDK uses java.net.http.HttpClient from the Java 11 standard library.
Setting up the client
import com.usepolylingo.polylingo.PolyLingo;
PolyLingo client = PolyLingo.builder()
.apiKey(System.getenv("POLYLINGO_API_KEY"))
.build();
Two optional builder parameters are available:
PolyLingo client = PolyLingo.builder()
.apiKey(System.getenv("POLYLINGO_API_KEY"))
.baseUrl("https://api.usepolylingo.com/v1") // default, override for self-hosted instances
.timeout(Duration.ofSeconds(30)) // default is 120 seconds
.build();
Keep your API key in an environment variable. Never hardcode it or commit it to version control.
The SDK uses the builder pattern throughout. No positional constructors, no required field order. All optional fields have sensible defaults so you only configure what you actually need to change.
Translating content
Single request
import com.usepolylingo.polylingo.types.TranslateParams;
import com.usepolylingo.polylingo.types.TranslateResult;
TranslateResult result = client.translate(
TranslateParams.builder()
.content("Hello, world!")
.targets(List.of("es", "fr", "de"))
.build()
);
result.getTranslations().forEach((lang, text) ->
System.out.println(lang + ": " + text)
);
// es: ¡Hola, mundo!
// fr: Bonjour le monde !
// de: Hallo Welt!
The TranslateParams builder accepts content and targets as required fields. Optional parameters include format (plain, markdown, json, or html — auto-detected if omitted), source as a language hint, and model as either standard (default) or advanced.
Format preservation works the same as the rest of the PolyLingo API. For json content, only string values are translated and keys, nesting, and non-string types are left untouched. For markdown, headings stay headings and code blocks are left verbatim. For html, tags and attributes are preserved and only text nodes are translated.
Translating a JSON locale file
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.file.Files;
import java.nio.file.Path;
ObjectMapper mapper = new ObjectMapper();
String sourceJson = Files.readString(Path.of("messages/en.json"));
TranslateResult result = client.translate(
TranslateParams.builder()
.content(sourceJson)
.format("json")
.targets(List.of("fr", "de", "ja"))
.build()
);
result.getTranslations().forEach((locale, translated) -> {
try {
Object parsed = mapper.readValue(translated, Object.class);
String pretty = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(parsed);
Files.writeString(Path.of("messages/" + locale + ".json"), pretty);
System.out.println("Wrote messages/" + locale + ".json");
} catch (Exception e) {
throw new RuntimeException(e);
}
});
One request handles all three locales. Keys are identical to the source in every output file.
Batch requests
Send up to 100 content items in a single request, each with its own id and optional format:
import com.usepolylingo.polylingo.types.BatchParams;
import com.usepolylingo.polylingo.types.BatchItem;
import com.usepolylingo.polylingo.types.BatchResult;
BatchResult result = client.batch(
BatchParams.builder()
.targets(List.of("es", "ja"))
.addItem(BatchItem.builder().id("title").content("Welcome").build())
.addItem(BatchItem.builder().id("body").content("Get started today.").build())
.build()
);
for (BatchItemResult item : result.getResults()) {
System.out.println(item.getId() + ": " + item.getTranslations());
}
All items share the same targets list. The id you assign is preserved in the response so you can map results back to your source data without relying on order.
Async jobs
For long documents or large translation workloads the jobs API accepts a request, returns immediately with a job ID, and lets you poll for the result. The SDK handles this two ways.
One call that polls until done
import com.usepolylingo.polylingo.types.JobsTranslateParams;
TranslateResult result = client.jobs().translate(
JobsTranslateParams.builder()
.content(longArticleText)
.targets(List.of("fr", "de", "ja", "zh"))
.pollInterval(Duration.ofSeconds(3))
.timeout(Duration.ofMinutes(10))
.onProgress(queuePosition ->
System.out.println("Queue position: " + queuePosition))
.build()
);
result.getTranslations().forEach((lang, text) ->
System.out.println(lang + ": " + text.substring(0, 100) + "...")
);
The onProgress callback fires on each poll with the current queue position as an integer, or null if the API does not return one. Use it to log progress or update a UI.
Enqueue and poll manually
import com.usepolylingo.polylingo.types.CreateJobParams;
import com.usepolylingo.polylingo.types.Job;
Job job = client.jobs().create(
CreateJobParams.builder()
.content("Translate this.")
.targets(List.of("es"))
.build()
);
// Poll yourself
Job status = client.jobs().get(job.getJobId());
System.out.println(status.getStatus()); // pending / processing / completed / failed
Utility endpoints
// Check API health (no API key required)
HealthResponse health = client.health();
System.out.println(health.getStatus()); // "ok"
// List supported languages (no API key required)
LanguagesResponse langs = client.languages();
langs.getLanguages().forEach(l ->
System.out.println(l.getCode() + " — " + l.getName())
);
// Check token usage for the current billing month
UsageResponse usage = client.usage();
System.out.println("Tokens used: " + usage.getUsage().getTokensUsed());
System.out.println("Tokens remaining: " + usage.getUsage().getTokensRemaining());
Error handling
All exceptions are unchecked. No forced throws declarations, no checked exception chains to unwind:
import com.usepolylingo.polylingo.errors.*;
try {
TranslateResult result = client.translate(
TranslateParams.builder()
.content("# Hello")
.targets(List.of("es", "fr"))
.format("markdown")
.build()
);
} catch (AuthException e) {
// HTTP 401 — invalid, missing, or revoked API key
System.out.println("Auth failed: " + e.getError());
} catch (RateLimitException e) {
// HTTP 429 — per-minute limit reached
e.getRetryAfter().ifPresent(s ->
System.out.println("Retry after: " + s + "s")
);
} catch (JobFailedException e) {
// Async job reached a failed terminal status or polling timed out
System.out.println("Job " + e.getJobId() + " failed: " + e.getError());
} catch (PolyLingoException e) {
// All other API errors
System.out.println(e.getStatus() + ": " + e.getMessage());
}
RateLimitException.getRetryAfter() returns an Optional<Integer> — present with the number of seconds to wait if the API includes that value, empty otherwise. JobFailedException.getJobId() gives you the ID of the failed job for logging or retry logic.
Design notes
A few decisions worth knowing about if you are evaluating the SDK:
Synchronous API. Every method blocks and returns a result directly. No Future, no CompletableFuture, no reactive streams. The jobs polling is the SDK's async story for heavy workloads — submit the job, the SDK polls on your thread and calls your onProgress callback as the queue position updates.
Builder pattern throughout. Every params object uses a builder. No positional constructors means no guessing at argument order, no accidental field swaps, and clear code at the call site.
One runtime dependency. Jackson for JSON serialization. Everything else is standard library. No OkHttp, no Apache HttpClient, no Guava.
Java 11+. Uses java.net.http.HttpClient introduced in Java 11. If your project targets Java 8 or earlier the SDK is not compatible.
Quick reference
| Method | Endpoint | Auth required |
|---|---|---|
client.health() | GET /health | No |
client.languages() | GET /languages | No |
client.translate(...) | POST /translate | Yes |
client.batch(...) | POST /translate/batch | Yes |
client.usage() | GET /usage | Yes |
client.jobs().create(...) | POST /jobs | Yes |
client.jobs().get(jobId) | GET /jobs/:id | Yes |
client.jobs().translate(...) | POST /jobs + polling | Yes |
Get started
The SDK is on Maven Central at central.sonatype.com/artifact/com.usepolylingo/polylingo. Source code and Javadoc are at github.com/UsePolyLingo/polylingo-Java. Full API documentation is at usepolylingo.com/docs/sdk/java.
The free tier includes 50,000 tokens per month. No credit card required.
<dependency>
<groupId>com.usepolylingo</groupId>
<artifactId>polylingo</artifactId>
<version>0.1.0</version>
</dependency>