Preview Provider
Introduction
Preview apps offer the possibility to use external preview providers within the Trados Enterprise platform.
The type of preview generated is known as "Dynamic Preview" as it can dynamically adjust depending on choices the user might make in the browser, for instance, selecting different preview sections or views from the rendered preview. The extension acts as a webserver which implements custom endpoints to serve the content to the browser. This content can be anything which the browser is able to parse and display accordingly.
To build a new preview app we recommend to start by using the provided app blueprints.
Preview App Extension
A preview app needs to define at least one preview extension within its descriptor. For example:
{
...
"extensions": [
{
"extensionPointId": "lc.previewprovider",
"id": "string",
"name": "string",
"description": "string",
"extensionPointVersion": "string",
"configuration": {
"endpoints": {
"lc.preview.startpreview": "string",
"lc.preview.refreshpreview": "string",
"lc.preview.updatesegment": "string",
"lc.preview.endpreview": "string"
},
"supportedFileTypes": "html",
"requiredInputFiles": {
"bilingualDocument": true,
"previewPackageTemplate": true,
"nativeFile": true,
"nativeAnnotatedFile": true
},
"outputType": "previewPackage"
}
}
]
...
}
id- unique extension identifier provided by the app Developer.name- provide a friendly and unique name. It might be shown to the end user, and may be useful to help the user distinguish between multiple extensions.extensionPointVersion- the version of the extension point that is implemented in the Extension.extensionPointId- the extension point identifier corresponding to this extensionType: lc.previewprovider.description- the preview extension descriptionconfiguration- the extension configuration.endpoints- the required endpoints for the preview extension, should be relative to yourbaseUrl.lc.previewprovider.startpreview- the endpoint used to start the preview generation. For ex:/start-preview.lc.previewprovider.refreshpreview- the endpoint used to refresh the existing preview session. For ex:refresh-preview.lc.previewprovider.updatesegment- the endpoint used to update a segment based on a BCM document fragment. For ex:update-segment.lc.previewprovider.endpreview- the endpoint used to clean up any state associated with a given preview session. For ex:end-preview.
supportedFileTypes- A list of FileTypeDefinitionIds which this app may be associated withrequiredInputFiles- provides details about which types of input files this extension will require for generating a preview.bilingualDocument- request an endpoint for downloading a BCM document.previewPackageTemplate- request an endpoint for downloading a preview Package Template.nativeFile- request an endpoint for downloading a native file.nativeAnnotatedFile- request an endpoint for downloading a native annotated file.
outputType- Indicates which type of output is generated by the extension. For v1, this can be "previewPackage" for package-based previews or "previewUrl" for dynamic URL-based previews.
Preview App Endpoints
StartPreview
The startPreview endpoint initiates a background preview generation session
and returns after creating the background worker which generates the actual preview. The result is sent via calling the URL set in the callbackUrl field. An important field is the previewSessionId which indicates the session this preview is associated with.
If the app needs to store any state, it should associate it with this identifier. The very first call for a given session should always be startPreview. If other endpoints are called before this, they should return an error.
If the app is generating a previewPackage or standalone HTML result, it should be uploaded via the URL defined in the previewResultFileUrl field.
The resulting resource identifier returned from this call should be set in the previewResult field of the callback response.
For dynamic previews (outputType: "previewUrl"), the preview content is served by the app via one or more endpoints the app implements. The content can be any format, as long as the browser hosting the preview can parse and display that content. This includes HTML, PDF, streamed video etc. The resulting URL returned from this call should be set in the previewResult field of the callback response.
Example:
POST https://your-app.com/start-preview
- The request is as follows:
{
"previewSessionId": "string",
"fileInformation": {
"name": "string",
"sourceLanguage": "string",
"targetLanguage": "string"
},
"inputFiles": {
"bilingualDocumentUrl": "string",
"previewPackageTemplateUrl": "string",
"nativeFileUrl": "string",
"nativeAnnotatedFileUrl": "string"
},
"previewResultFileUrl": "string",
"callbackUrl": "string",
"scope": "source"
}
- The response after setting up the background worker, would look like this:
{
"errorCode": "string",
"message": "string",
"details": [
{
"name": "string",
"code": "string",
"value": "string"
}
]
}
- The final result, which is sent on the callback, is shown below:
{
"previewSessionId": "string",
"previewResult": "string",
"errors": [
{
"errorCode": "string",
"message": "string",
"details": [
{
"name": "string",
"code": "string",
"value": "string"
}
]
}
]
}
Endpoint usage:
This endpoint is used to initiate a preview session on the app. When this endpoint is called, the app should generate the initial preview and associate it (internally, if any state is kept) - with the previewSessionId.
This should be the first endpoint called for any given session - if another endpoint is called for a given session ID before this one, an error should be returned.
Please refer to the endpoint's documentation for further details.
RefreshPreview
The refreshPreview endpoint refreshes an existing preview. The mechanism for this in entirely up to the Extension implementation in that it might keep state from the initial preview generation from startPreview in order to speed up the refresh.
The request and responses (REST and Callback) are identical to the startPreview endpoint.
Example:
POST https://your-app.com/refresh-preview
Endpoint usage: This endpoint is called when a preview refresh is requested by the user. Any state the app holds from the initial preview generation may be used to speed up the refresh.
Please refer to the endpoint's documentation for further details.
UpdateSegment
The updateSegment endpoint accepts a list of BCM document fragments along with their associated segment IDs. The Extension will generate and return an HTML rendered fragment for each BCM document fragment sent in this manner.
Example:
POST https://your-app.com/update-segment
- The request is as follows:
{
"previewSessionId": "string",
"segments": [
{
"segmentId": "string",
"fragment": {}
}
],
"fileInformation": {
"name": "string",
"sourceLanguage": "string",
"targetLanguage": "string"
},
"scope": "source"
}
previewSessionId- unique ID of current preview session.segments- an array of segments with the following details:segmentId- ID of segment being rendered.fragment- BCM document JSON fragment.
fileInformation- provides information relating to the file for which the rendered HTML is being generated.scope- indicates whether the generation is happening for source or target content.The response would look like this:
{
"previewSessionId": "string",
"renderedTranslations": [
{
"html": "string",
"segmentId": "string",
"errorCode": "string"
}
]
}
previewSessionId- unique ID of current preview session.renderedTranslations- an array of rendered translations with the following details:html- rendered HTML content for this segment.segmentId- ID of this segment.errorCode- Error code for this segment (App/Extension specific).
Endpoint usage: This endpoint is called when the user is updating one or more segments in the Editor. For each segment, the extension will render an equivalent HTML fragment based on the input BCM document fragment.
Please refer to the endpoint's documentation for further details.
EndPreview
The endPreview endpoint is called when a given preview session associated with the previewSessionId is finished. This is called so that the app may perform any required cleanup.
Example:
POST https://your-app.com/end-preview
{
"previewSessionId": "string"
}
Endpoint usage: Called to indicate a preview session has terminated. Any required cleanup should be performed by the app.
Please refer to the endpoint's documentation for further details.
Javascript Communication Protocol
The Online Editor Container communicates with the Preview Viewer iframe using the Javascript postMessage mechanism. The API is detailed below:
"use strict";
/* Communication Protocol between the RWS Online Editor and the preview loaded into it.
*
* Based on passing global events back and forth (events on the window object).
* Used for:
*
* - navigation from one segment to the other (either happening on the editor and
* getting propagated to the preview, or happening on the preview - by clicking
* a segment - and getting propagated to the editor)
*
* - segment updates: when the translation for a segment changes, the segment
* update flow will yield some piece of string (usually html content) that
* will be sent to the preview currently being loaded in the editor
*
* - showing messages (could not navigate to the segment currently selected
* in editor / cannot update the translation for certain segment)
*/
// IE compatibility
Element.prototype.remove = function () {
this.parentElement.removeChild(this);
}
if (!window.sdlPreviewScriptIncludeGuard) {
// when the preview package gets inlined into a single html file, the script can
// get included multiple times; we use this guard to only subscribe to the events
// once
window.sdlPreviewScriptIncludeGuard = true;
//state
var selectedSegmentId = "";
var segmentIdToSigCache = {};
var initialized = false;
// names of the events
var PREVIEW_SEGMENT_SELECTED = "Preview:segmentSelected";
var PREVIEW_SHOW_MESSAGE = "Preview:showMessage";
var UE_SEGMENT_SELECTED = "UE:segmentSelected";
var UE_UPDATE_SEGMENT = "UE:updateSegment";
document.addEventListener("DOMContentLoaded", function (event) {
/* preparations can be done here */
});
window.addEventListener(UE_SEGMENT_SELECTED, function (event) {
// event.detail will contain the id of the segment we need to navigate to
// the segment with this id can be highlighted and scrolled into view
// if the segment cannot be highlighted on the preview:
reportError("CannotNavigateToSegment");
});
window.addEventListener(UE_UPDATE_SEGMENT, function(event) {
// the id of the segment being updated
var segmentId = event.detail.segmentId;
// the string (html snippet) returned by the segment updater
var segmentSnippet = event.detail.snippet;
/* the contents of the corresponding page element can be changed
* here according to the snippet
*/
// if the update cannot be done:
reportError("PreviewCannotUpdate");
});
}
function reportError(type) {
// types currently being used are "CannotNavigateToSegment" and "PreviewCannotUpdate"
// TBD if we can support custom messages being sent in the detail attribute here
window.dispatchEvent(new CustomEvent(
PREVIEW_SHOW_MESSAGE,
{ detail: type }
));
}
function somethingWasClicked() {
// can be handler of onclick on page elements corresponding to segments
var previousSelection = "...";
var segmentId = "...";
if (segmentId !== previousSelection) {
window.dispatchEvent(new CustomEvent(
PREVIEW_SEGMENT_SELECTED,
{ detail: segmentId } // this informs the editor which segment it should navigate to
));
}
}
For dynamic previews, a more comprehensive Javascript API is available that handles communication via postMessage between the iframe and the Online Editor. This includes:
- Navigation between segments
- Segment updates with translation changes
- Error messaging
- Scroll synchronization for side-by-side preview
- Segment highlighting and selection
Session Management and Tenant Separation
It is up to the app to manage any session state internally and have an expiry mechanism for this. Also, any data which is tenant specific needs to be kept separate from any other data and the app also needs to manage tenant separation concerns internally.
Input Files
The preview provider works with various types of input files:
- PreviewPackageTemplate - a zip package containing content used in preview generation (XML, HTML, images, CSS, XSLT, Javascript etc.)
- Native File - the native format being translated or previewed (Word, Excel, XML documents etc.)
- Annotated Native File - a native file annotated with special characters encoding segment identifiers and boundaries
Dynamic Preview Concepts (for outputType: "previewUrl")
When implementing dynamic previews:
- The app acts as a web server implementing custom endpoints
- TLS (Transport Layer Security) must be enabled on all custom endpoints
- Authorization via hard-to-guess access tokens with TTL support
- Content can be dynamically updated based on user selections
- Support for different renderings or views of the same BCM document content
The URL format for dynamic preview endpoints:
https://my.extension/custom-endpoint?sessionId={sessionId}&tenant={tenantId}&accessToken={hard-to-guess-access-token}&page={pageId}