Skip to main content

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.

Note

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")
2
public class PigLatinTranslationService extends TranslationService {
3
4
@Override
5
public Set<Locale> getSupportedLocales(Locale sourceLocale) {
6
return Set.of();
7
}
8
9
@Override
10
public List<TranslationLog> translate(TranslationContext context) {
11
return 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")
2
public class PigLatinTranslationServiceWithFields extends TranslationService {
3
4
@Recordable.Values({ "a", "e", "i", "o", "u" })
5
private Set<String> excludedVowels;
6
7
@Override
8
public Set<Locale> getSupportedLocales(Locale sourceLocale) {
9
return Set.of();
10
}
11
12
@Override
13
public List<TranslationLog> translate(TranslationContext context) {
14
return 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
@Override
2
public Set<Locale> getSupportedLocales(Locale sourceLocale) {
3
if (sourceLocale.getLanguage().equals("en")) {
4
return Stream.of(Locale.forLanguageTag("la"), Locale.US)
5
.collect(Collectors.toSet());
6
}
7
return 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
@Override
2
public Set<Locale> getDefaultLocales() {
3
return 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
@Override
2
public boolean allowTargetLocaleSelection() {
3
return 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
@Override
2
public boolean allowMultipleTargetLocales() {
3
return false; // User can only select one target locale
4
}

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
  • TranslationContextItem objects 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:

FieldDescription
ServiceThe translation service used
SourceID of the source content
Draft/OverlayIDs if translating a revision
LocaleTarget locale
SiteSite where translation was requested
UserUser who requested translation
Translation DateWhen translation was requested
StatusSUCCESS, SUCCESS_WITH_ERRORS, PENDING, FAILURE, or CANCELED
Content HashHash of source content
Source DataData that was translated
Translated DataData received from service
Translated ContentID of created content in Brightspot

Completing Translation

For synchronous services, call TranslationService#completeTranslation within the translate method.

For asynchronous services:

  1. Create logs with status PENDING
  2. Implement a mechanism to receive completed translations (webhooks, polling task, etc.)
  3. Call completeTranslation when data is received

Complete Example

Here's a complete implementation of a Pig Latin translation service:

1
import java.util.ArrayList;
2
import java.util.Collections;
3
import java.util.Date;
4
import java.util.HashSet;
5
import java.util.List;
6
import java.util.Locale;
7
import java.util.Set;
8
import java.util.stream.Collectors;
9
import java.util.stream.Stream;
10
11
import com.psddev.dari.db.Recordable;
12
import com.psddev.translation.TranslationContext;
13
import com.psddev.translation.TranslationContextItem;
14
import com.psddev.translation.TranslationData;
15
import com.psddev.translation.TranslationLog;
16
import com.psddev.translation.TranslationService;
17
18
@Recordable.DisplayName("Pig Latin")
19
public class PigLatinTranslationService extends TranslationService {
20
21
private static final Set<Character> VOWELS = new HashSet<>();
22
23
static {
24
VOWELS.add('a');
25
VOWELS.add('e');
26
VOWELS.add('i');
27
VOWELS.add('o');
28
VOWELS.add('u');
29
}
30
31
@Recordable.Values({ "a", "e", "i", "o", "u" })
32
private Set<String> excludedVowels;
33
34
@Override
35
public Set<Locale> getSupportedLocales(Locale sourceLocale) {
36
if (sourceLocale.getLanguage().equals("en")) {
37
return Stream.of(Locale.forLanguageTag("la"), Locale.US)
38
.collect(Collectors.toSet());
39
}
40
return null;
41
}
42
43
@Override
44
public Set<Locale> getDefaultLocales() {
45
return Collections.singleton(Locale.forLanguageTag("la"));
46
}
47
48
@Override
49
public boolean allowTargetLocaleSelection() {
50
return false;
51
}
52
53
@Override
54
public List<TranslationLog> translate(TranslationContext context) {
55
List<TranslationLog> logs = new ArrayList<>();
56
57
for (TranslationContextItem contextItem : context.getItems()) {
58
for (Locale targetLocale : context.getTargetLocales()) {
59
// Create and configure the translation log
60
TranslationLog log = new TranslationLog();
61
log.setService(this);
62
log.setSource(contextItem.getSource());
63
log.setDraft(contextItem.getDraft());
64
log.setOverlay(contextItem.getOverlay());
65
log.setLocale(targetLocale);
66
log.setSite(context.getSite());
67
log.setUser(context.getUser());
68
log.setTranslationDate(new Date());
69
log.setContentHash(contextItem.getTranslationData().hashCode());
70
log.setSourceData(contextItem.getSourceData());
71
72
// Perform the translation
73
TranslationData output = translateData(contextItem.getTranslationData());
74
75
// Complete the translation (creates content, sets status)
76
completeTranslation(log, output);
77
logs.add(log);
78
}
79
}
80
return logs;
81
}
82
83
private TranslationData translateData(TranslationData data) {
84
// Translation logic here - convert each field value to Pig Latin
85
// ...
86
return data;
87
}
88
89
private String translateString(String input) {
90
// Pig Latin conversion logic
91
// ...
92
return input;
93
}
94
}

See Also