Client-Side Translator

Operations

Overview

The Client-Side Translator, codenamed Crest, is an alternative publishing mode. Instead of operating in proxy mode, the system generates a JavaScript stub that needs to be referenced in the site, and it will translate the page in real time using a dictionary downloaded from the cloud service. Language choice is persisted in the browser’s Local Storage, enabling automatic translation of any page in the site instantly on landing.

Setup

Content is collected and translated the same way as normal. Once publishing is needed, content is exported by selecting the Client-side translation file format, then publishing the latest export (or the one selected for production) from the Previous Exports screen and clicking the context menu.

The translation loader script can be inserted with a one-liner script element, which is available from the Global Settings screen of the Publish section in the sidebar. The website owner needs to insert this script element into pages requiring translation.

../_images/crest.pngCopy from the Dashboard and paste into the head

Once complete, the translations can be requested by adding a query parameter to the URL, with the name __ptLanguage and the chosen locale as the value (for example https://example.com/path/to/page?__ptLanguage=ja-JP).

Integrators’ Guide

Elements

Crest is controlled by the loader script, inserted into every page requiring translation. The script element should be inserted as high in the head as possible in order to begin translation at the earliest possible point.The loader script has a number of query parameters that may be used to manipulate its operation. Any number of these can be combined to customize the loader’s behaviour from the default settings (existence of said defaults also means none of these parameters are mandatory to supply).

  • languageParameter: This parameter can be used to change the language selector key from its default of __ptLanguage.
  • storageParameter: This parameter can be used to change the LocalStorage key used to store a previous selection from its default of ptLanguage.
  • noXDefault: if set to true, suppress placing an x-default link element in the head if a translated language is loaded. This may have SEO implications!
  • rewriteUrl: if set to true, use history.replaceState to rewrite the URL shown to the user so that it always displays the selected language.
  • scriptUrlIsBase: if set to true, the loader will search for the translator script based on its own URL. CAUTION: This is not supported under Internet Explorer!
  • disableSelector: if set to true, the stub will not inject its own language selector in the sidebar. In this case, it is up to the website to provide links to the various language versions.

Language selection is possible via the sidebar inserted on the right by default, or custom a elements that manipulate the value of the __ptLanguage query parameter. Note that once a language is selected, the choice is persisted into the browser Local Storage, so further links need not be annotated with the query to maintain translation.

On selecting a language, the loader script will insert a new script element referencing the exported dictionary. It downloads the translations necessary for display and the translator algorithm that processes the available DOM to replace content with the available translations.The translator will also attach a MutationObserver to the document being displayed that allows it to react to DOM manipulation or newly-appearing elements in real time.

Interop

Events

In order to provide a seamless user experience, Crest exposes a number of events at key points in the process that allow the containing page to react to the translation process and take action to enhance the experience. The following events are dispatched at various points:

  • crestDictionaryLoadingStart: Dispatched when a language is selected and download of the corresponding dictionary begins. As the dictionary can be sizeable, this event can be used to display a notification to the visitor advising them that the language is about to change.
  • crestDictionaryLoadingEnd: Dispatched on completion of the dictionary download. Firing this event means translations are available, and they will be applied to the DOM momentarily. If a notification was displayed on download start, it should be removed on this event.
  • crestDocumentTranslationStart: Dispatched when the initial translation of the document begins. Firing this event means translations are currently being applied to the entire page, and the displayed language is about to change. In case translation takes significant time, the user may benefit from an overlay or other message notifying them of the process and that the displayed language will change soon.
  • crestDocumentTranslationEnd: Dispatched when the initial translation of the DOM is complete, and all available content has been replaced. At this point, the page is translated to the best of Crest’s abilities, and if an overlay or notification was displayed, it should be removed.
  • crestMutationTranslationStart: Dispatched when a mutation of the DOM is detected by the attached observer and translation of the new/changed elements begins. This event is unique in that it includes a payload, an array of the MutationRecords that are being processed. These include information about the element name, DOM path, and other data that may be used by the page to react to changes.
  • crestMutationTranslationEnd: Dispatched when the mutation observer completes its run and designates all mutated elements translated. If a notification was displayed on the preceding event, it should be removed now.

In addition, the crestStub event can be used to detect changes in the loader configuration in real-time.

Example
<script type="application/javascript">
    document.addEventListener("crestDocumentTranslationStart", () => console.log("Document translation started"));
    document.addEventListener("crestDocumentTranslationEnd", () => console.log("Document translation ended"));
    // e.detail.targets contains the array of MutationRecord objects that are being processed in the current run. For more information, see https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord
    document.addEventListener("crestMutationTranslationStart", (e) => console.log(`Mutation translation started. Mutated records: ${e.detail.targets}`));
    document.addEventListener("crestMutationTranslationEnd", () => console.log("Mutation translation ended"));
    document.addEventListener("crestDictionaryLoadingStart", () => console.log("Dictionary download started"));
    document.addEventListener("crestDictionaryLoadingEnd", () => console.log("Dictionary download ended"));

    document.addEventListener("crestStub", (event) => {
        let changes = event.detail.changes; // List of changes in the state.
        let selectedLanguage = event.detail.state.selectedLanguage; // The currently selected language.
    });

    
    console.log("Event listeners ready...");
</script>

Language selector

Language selectors are crucial for localised websites to ensure that the translations are prominently featured on the website. Crest is compatible with two types of language selector - a dropdown or a sidebar.

Custom Dropdown

The loader exposes its current configuration in the local window, by setting a read-only copy of it as window.crestStub. Developers can use it to generate custom language selectors and get vital information about the current state of the translation engine.

Structure:

  • config: Contains configurations for Crest’s operation.
    • crestConfig: Specific configurations for Crest’s behavior.
      • passive: (Boolean) Determines if Crest operates in a passive mode.
      • scriptInjections: (Array) Scripts to be injected dynamically.
      • languageSelectedByPath: (Boolean) Defines if language should be determined by the URL path.
      • selectorDisabled: (Boolean) Determines if the language selector is disabled.
    • restrictions: Specifies restrictions on paths and translations.
      • externalizedPathPrefixes: (Array) Paths that should be externalized.
      • pathPrefixMask: (Array) Masks for path prefixes.
      • externalizedQueryGlobs: (Array) Glob patterns for externalizing query parameters.
      • translationPathPrefixes: (Array) Prefixes for translated paths.
    • sheet: (String) URL of the icon sheet used for flags.
    • flagWidth: (Number) Width of an individual flag icon.
    • flagHeight: (Number) Height of an individual flag icon.
    • flagPixelRatio: (Number) Pixel ratio for the flag icons.
    • atlasWidth: (Number) Total width of the flag icon atlas.
    • languageParameter: (String) The query parameter used to indicate language selection.
    • appendTo: (String) DOM selector where the language selector should be appended.
    • deployed: (Boolean) Indicates if the translation has been deployed.
    • storageKey: (String) Key used for storing selected language in local storage.
    • disableSelector: (Boolean) Determines if the language selector is disabled.
  • languages: (Array) Contains a list of available languages for translation.
    • country: (String) The country for the specific language.
    • language: (String) The name of the language.
    • direction: (String) Text direction (ltr or rtl).
    • deployPath: (String) URL where the translated version is deployed.
    • flag: (Object) Contains the x and y positions of the flag in the icon sheet.
    • targetLanguage: (String) The target language code.
    • uri: (String) URL for the translation script of the respective language.
    • displayName: (String) Display name for the language.
    • published: (Boolean) Indicates if the language translation is published.

Usage:

Developers can use the window.crestStub object to create custom language selectors. By iterating over the languages array, one can generate language options dynamically. The current selected language can be obtained from the config.crestConfig.languageSelectedByPath or by checking the URL (when languageSelectedByPath is true).

Example:

const languages = window.crestStub.languages;
languages.forEach(lang => {
  console.log(lang.displayName); // Output the display name of each language
});