Added in: release-2026-11
Introduction
Document Translation Flows let a project control how a document is translated into another language. When a user picks a language in the translation card of the editor, the server creates a new document in the same language group with the target locale. Without a translation flow, the server duplicates the source document. With a translation flow, a registered translate function decides the title, metadata and content of the translation, for example by calling a machine translation service.
Translations require the translation workflow of the multi-language setup. See the Enable Multi-Language Content guide for the language configuration and the li-language metadata property.
Register a server function
You can register translate functions using the liServer.registerTranslateFunctions method, which accepts an array of objects containing a unique handle and a translate function.
When the function is executed it is provided with the document being translated, the projectConfig which the document belongs to, the userId of the user which triggered the translation, the sourceLocale and targetLocale, and a context object which is defined within the project config later.
The function can return the following values: title, metadata, metadataSource, translations, content. All of them are optional. If metadata is not returned, the translation receives the metadata of the source document without the properties whose plugin is marked with removeDuringDefaultCopy. For all other values that are not returned, the value of the source document is used. Returning nothing duplicates the source document, which is what the built-in default function does. A translate function cannot change the content type.
Content types with a displayTitlePattern build the title that users see from metadata, for example from metadata.title. For those content types, return the translated title in metadata. The returned title is only displayed for content types without a displayTitlePattern.
When a user edits content that the editor extracts into metadata, for example the headline into the title, the editor updates that metadata property again. If your function returns a metadata value that intentionally differs from the content, mark it as manual in metadataSource so the editor keeps it. A returned metadataSource replaces the one of the source document, so extend it rather than returning only your change: metadataSource: {...document.metadataSource, title: 'manual'}.
The language metadata property of the translation is always set by the server. It keeps the groupId of the source document and receives the target locale, regardless of what the function returns for that property.
liServer.registerTranslateFunctions([
{
handle: 'machineTranslation',
async translate({document, projectConfig, userId, context, sourceLocale, targetLocale}) {
// Returned metadata replaces the source metadata, so skip the properties
// that a default copy removes as well
const metadata = {}
for (const {handle, plugin, value} of [...document.metadata, ...document.systemMetadata]) {
if (!plugin.removeDuringDefaultCopy) metadata[handle] = value
}
const options = {from: sourceLocale, to: targetLocale, glossary: context.glossary}
metadata.title = await translateText(metadata.title, options)
return {
title: metadata.title,
metadata,
content: await translateContent(document.content, options)
}
}
}
])
context of a translation flow is part of editorSettings, which the editor loads for every user. Do not put credentials into it. Keep API keys in the server configuration or in environment variables and read them inside the translate function.The source document’s inbox is copied to the translation when it is created. Inbox entries that the content type’s inbox config does not permit are skipped.
Add document translation flow to projectConfig
A translation flow needs a unique handle and a reference to the handle of the translate function. An optional context object is passed to the translate function, which is useful when the same function serves several flows.
{
// ...
editorSettings: {
// ...
documentTranslationFlows: [
{
handle: 'deepl',
translateFunction: 'machineTranslation',
context: {glossary: 'news'}
}
]
}
}
A translation flow has no labels, dialog or paramsSchema, because the translation card only asks for the target language.
Enable translation flow in contentTypeConfig
Finally, you need to enable the translation flow for each content type that should use it. To do this you provide a reference to the translation flow you defined in the project config by using the handle as the value for the useDocumentTranslationFlow property.
{
handle: 'regular',
// ...
documentTranslationFlows: [
{
useDocumentTranslationFlow: 'deepl'
}
]
}
A content type can reference one translation flow. The referenced flow must exist in editorSettings.documentTranslationFlows, otherwise the project config is rejected. Content types without documentTranslationFlows use the built-in default function, which duplicates the document.
Behavior
- A translation is linked to its source through
systemdata.translationSource.documentIdis the source document anddocumentTranslationFlowis the handle of the translate function that ran, which isliDefaultTranslateFunctionwithout a translation flow. Both can be used as filters in the document search, andtranslationSource.documentIdalso in the publication search. - The info panel of a translation shows the source under “Translation of”, and the info panel of the source lists its translations in the copies section.
- The server rejects a translation with a
409status when the target locale is the locale of the source document or already exists in the language group. - The user needs the create permission for the content type. It is checked before the translate function runs.
- Creating a translation emits a
document.createevent.