Building a translation service
Brightspot's Translation Service API provides a standardized interface for translating content. To integrate with a third-party translation provider, implement a custom translation service that communicates with external APIs.
Since each integration can be quite different depending on the third-party service, this guide may not cover all details needed for a specific integration.
Step 1: Define the translation service
Create a class that extends TranslationService:
1@Recordable.DisplayName("Pig Latin")2public class PigLatinTranslationService extends TranslationService {34@Override5public Set<Locale> getSupportedLocales(Locale sourceLocale) {6return Set.of();7}89@Override10public List<TranslationLog> translate(TranslationContext context) {11return List.of();12}13}
The @DisplayName annotation changes how the service appears in the Brightspot UI.
Step 2: Add configuration fields (optional)
Add fields to your service class for user-configurable options. These fields appear when users select your service during translation:
1@Recordable.DisplayName("Pig Latin")2public class PigLatinTranslationServiceWithFields extends TranslationService {34@Recordable.Values({ "a", "e", "i", "o", "u" })5private Set<String> excludedVowels;67@Override8public Set<Locale> getSupportedLocales(Locale sourceLocale) {9return Set.of();10}1112@Override13public List<TranslationLog> translate(TranslationContext context) {14return List.of();15}16}
Fields use standard Brightspot data modeling concepts, including annotations for UI elements and validation.
Step 3: Provide supported locales (optional)
Implement getSupportedLocales(Locale sourceLocale) to define which target locales your service supports. Users only see supported locales when selecting targets.
1@Override2public Set<Locale> getSupportedLocales(Locale sourceLocale) {3if (sourceLocale.getLanguage().equals("en")) {4return Stream.of(Locale.forLanguageTag("la"), Locale.US)5.collect(Collectors.toSet());6}7return null;8}
The list can be hard-coded or retrieved dynamically (e.g., via API call to the translation service).
Step 4: Provide default locales (optional)
Implement getDefaultLocales() to pre-select target locales for users:
1@Override2public Set<Locale> getDefaultLocales() {3return Collections.singleton(Locale.forLanguageTag("la"));4}
Step 5: Control target locale selection (optional)
Some services don't allow one-off target locale selection (e.g., when targets are defined by a project). Use allowTargetLocaleSelection() with getDefaultLocales() to show a read-only list:
1@Override2public boolean allowTargetLocaleSelection() {3return false;4}
Step 6: Control multiple locale selection (optional)
Some translation services only support translating to one locale at a time. Override allowMultipleTargetLocales() to restrict selection:
1@Override2public boolean allowMultipleTargetLocales() {3return false; // User can only select one target locale4}
When this returns false:
- The locale selection UI shows radio buttons instead of checkboxes
- Users must submit separate translation requests for each locale
- Useful for services with per-request locale limitations
The default is true, allowing multiple locales per request.
Step 7: Provide a custom data converter (optional)
To customize how content is transformed for your service, implement a custom TranslationDataConverter and return it from getDataConverter().
The default converter handles most use cases. Only implement a custom converter when you need special data handling. See Translation Data Converter for implementation details.
Step 8: Implement the translation process
Implement the translate method to initiate translation. This method varies significantly depending on whether your service is synchronous or asynchronous.
Translation context
The translate method receives a TranslationContext with:
- Site from which translation was requested
- User who requested translation
- Source locale of the content
- Target locales requested
TranslationContextItemobjects for each piece of content, containing:- Source content and draft/overlay (if applicable)
- Translation data (produced by
TranslationDataConverter) - Translation service settings
- Raw source data
Translation logs
The translate method must create and save TranslationLog instances. Create one log per target locale per content item. Each log tracks:
| Field | Description |
|---|---|
| Service | The translation service used |
| Source | ID of the source content |
| Draft/Overlay | IDs if translating a revision |
| Locale | Target locale |
| Site | Site where translation was requested |
| User | User who requested translation |
| Translation Date | When translation was requested |
| Status | SUCCESS, SUCCESS_WITH_ERRORS, PENDING, FAILURE, or CANCELED |
| Content Hash | Hash of source content |
| Source Data | Data that was translated |
| Translated Data | Data received from service |
| Translated Content | ID of created content in Brightspot |
Completing Translation
For synchronous services, call TranslationService#completeTranslation within the translate method.
For asynchronous services:
- Create logs with status
PENDING - Implement a mechanism to receive completed translations (webhooks, polling task, etc.)
- Call
completeTranslationwhen data is received
Complete Example
Here's a complete implementation of a Pig Latin translation service:
1import java.util.ArrayList;2import java.util.Collections;3import java.util.Date;4import java.util.HashSet;5import java.util.List;6import java.util.Locale;7import java.util.Set;8import java.util.stream.Collectors;9import java.util.stream.Stream;1011import com.psddev.dari.db.Recordable;12import com.psddev.translation.TranslationContext;13import com.psddev.translation.TranslationContextItem;14import com.psddev.translation.TranslationData;15import com.psddev.translation.TranslationLog;16import com.psddev.translation.TranslationService;1718@Recordable.DisplayName("Pig Latin")19public class PigLatinTranslationService extends TranslationService {2021private static final Set<Character> VOWELS = new HashSet<>();2223static {24VOWELS.add('a');25VOWELS.add('e');26VOWELS.add('i');27VOWELS.add('o');28VOWELS.add('u');29}3031@Recordable.Values({ "a", "e", "i", "o", "u" })32private Set<String> excludedVowels;3334@Override35public Set<Locale> getSupportedLocales(Locale sourceLocale) {36if (sourceLocale.getLanguage().equals("en")) {37return Stream.of(Locale.forLanguageTag("la"), Locale.US)38.collect(Collectors.toSet());39}40return null;41}4243@Override44public Set<Locale> getDefaultLocales() {45return Collections.singleton(Locale.forLanguageTag("la"));46}4748@Override49public boolean allowTargetLocaleSelection() {50return false;51}5253@Override54public List<TranslationLog> translate(TranslationContext context) {55List<TranslationLog> logs = new ArrayList<>();5657for (TranslationContextItem contextItem : context.getItems()) {58for (Locale targetLocale : context.getTargetLocales()) {59// Create and configure the translation log60TranslationLog log = new TranslationLog();61log.setService(this);62log.setSource(contextItem.getSource());63log.setDraft(contextItem.getDraft());64log.setOverlay(contextItem.getOverlay());65log.setLocale(targetLocale);66log.setSite(context.getSite());67log.setUser(context.getUser());68log.setTranslationDate(new Date());69log.setContentHash(contextItem.getTranslationData().hashCode());70log.setSourceData(contextItem.getSourceData());7172// Perform the translation73TranslationData output = translateData(contextItem.getTranslationData());7475// Complete the translation (creates content, sets status)76completeTranslation(log, output);77logs.add(log);78}79}80return logs;81}8283private TranslationData translateData(TranslationData data) {84// Translation logic here - convert each field value to Pig Latin85// ...86return data;87}8889private String translateString(String input) {90// Pig Latin conversion logic91// ...92return input;93}94}
See Also
- Completion Actions — Customize what happens after translation
- Translation Actions — Add custom UI elements