Technical requirements: Content exchange hosted connector

This document describes the technical requirements to build a content exchange connector, that is:

  • an app
  • that will be installed and hosted on Lokalise
  • and integrates with a third party content platform.

When you build a hosted connector, Lokalise will take care of the user interface and handle two user flows: the app setup and the content exchange. Therefore, you will only focus on implementing the following endpoints for each user flow:

  • App setup user flow: Install the app on Lokalise and connect it to the content platform.
    • /auth: Initiate the user authentication process, either with an API key or (coming soon) an OAuth flow.
    • /auth/refresh: Renew an expired token.
    • /env: Get all locales, default locale and hashmap with fields and labels for cache items.
    • /cache: Get translatable item identifiers.
    • /cache/items: Get translatable items using the identifiers.
  • Content exchange user flow: Get translatable content into Lokalise, and send it back to the content platform once it’s translated.
    • /translate: Get translatable content from the content platform.
    • /publish: Transfer translated content to the content platform.

Find the API spec below.

App setup: install and connect to the content platform

  • This flow is initiated when the user clicks on "Install"

    • Lokalise checks the type of authorization for this connector: OAuth 2.0 or API token.
    • If it’s through API token, Lokalise presents a UI where the user can enter the API token. Lokalise sends a request to the connector /auth endpoint to validate the API key; the connector will use a third party system to validate the API key. On success, it returns the API key to Lokalise, and Lokalise saves it.
    • (Coming soon) If it’s through OAuth, then Lokalise orchestrates the end user authentication and authorization: Lokalise sends the callback URL to the connector though the /auth endpoint, and receives the redirect URL in response. Lokalise will save the OAuth access token for you.
    • Lokalise will provide the auth credentials (either API key or OAuth access token) in all requests to the connector.
      • Lokalise handles the refreshing of expired OAuth access tokens. Whenever an OAuth access token needs to be refreshed, Lokalise will send a request to /auth/refresh connector endpoint providing the refresh token; Lokalise will then get a new OAuth access token in the response.
  • Lokalise shows a UI to configure the app

    • Lokalise sends a request to /env to learn what languages and translatable item fields are available on the third party system. The result of this call will indicate which languages will be presented to the users, and which columns will show on Lokalise UI.
    • Lokalise orchestrates the user flow to select setup options, and saves the configuration for the app.
  • Lokalise gets translatable content

    • Lokalise keeps a cache with the items representing content that can be imported from the third party system. At this point, Lokalise uses /cache and /cache/items connector endpoints to collect the translatable items list.
    • Lokalise will inform of the progress until all the metadata for translatable items is transferred. At that point, the user will see a list of translatable items on the UI.

Content exchange

Once the content platform has been connected to Lokalise, users can start managing the translations. They'll see the list of translatable items with the following statuses:

Status

Definition

Not translated

Items that haven't been transferred to Lokalise for translation yet.

Translation in progress

Item-language pairs that have been transferred to Lokalise, and for which the translation is not reviewed yet.

Translation completed

The item-language pairs that are reviewed and not yet transferred to the third party system.

Published

The item-language pairs that have been reviewed and transferred to the third party system.

Note: The items shown on the list depend on the latest cached items. The user can refresh the list at any point in time.

This list shows the items that are available for translation on the third party content platform. User actions available depend on item status.

  • Items with "Not translated" status

    • User can select which items to transfer to Lokalise for translation.
      • Lokalise sends a /translate request to the connector, in order to initiate the process of sending content from the third party to Lokalise for translation.
      • The connector returns the source content to Lokalise; Lokalise deals with key management and determines the translation status of each key, taking into account all the target languages. If there’s any issue, the UI will show it.
      • At this point, the user can open the Lokalise editor to see all the keys that need to be translated.
  • Items with "Translation in progress" status

    • The items were transferred to Lokalise and are currently in progress, meaning that they are nor yet fully translated and marked as reviewed.
  • Items with "Translation completed" status

    • These items are translated and marked as reviewed, and not yet published. The user can select items to be transferred to the third party system.
    • Lokalise gathers the content and sends a /publish request to the connector. The connector will publish the content in the third party system. If the result is successful, Lokalise will change the item status to published.
  • Items with "Published" status

    • This list shows the items that have already been transferred to the 3rd party system, and the source hasn’t changed since.
    • The user can refresh the list to check whether the source item changed after publishing the translation; in that case, Lokalise will change the item status and the item will move back to the Untranslated content tab.

Translatable item conversion to cache item(s)

In order to ensure the ability to consistently retrieve and publish content items, it is necessary to use the UniqueItemIdentifier object to link the information between the connector and the third party platform. This object consists of the following fields:

  • uniqueId - A custom unique identifier. It should be unique per item across the whole exchange process.
  • groupId - This identifier is used to group items that should be translated and published together. The user will be presented with translation items joined by this identifier.
  • metadata - This is a custom structure JSON object that the connector can use to help identify and match the content item. Lokalise will store it, but will not process it in any way.

Let's see an example with a third party content platform that allows users to publish articles. An article consists of a title and a body. Therefore, a connector between Lokalise and the platform should convert the article into two separate cache items with different unique ids; and they should have the same group id in order to ensure that they're processed, translated and published together. The metadata could contain properties like field, in order to help the connector identify which part of the original article the translated content refers to.

Connector API (Beta)

This is the API contract between Lokalise and the connector you'll be building. Please note this API specification is in beta, still subject to change based on feedback we'll be collecting from app developers such as yourself. Don't hesitate to send your suggestions and questions.

openapi: 3.0.0
info:
  version: 1.0.0
  title: Connector API
  description: Connector API requirements
servers:
  - url: 'http://localhost:3000/v1'
paths:
  /:
    get:
      security: []
      description: Healthcheck endpoint
      responses:
        '200':
            description: Service is up
  /auth:
    post:
      security: []
      description: Initiate authentication process
      requestBody:
        description: Payload depends on the authentication flow used by connector.
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  description: 'For key flow, the key will be provided'
                  required:
                    - key
                  properties:
                    key:
                      type: string
                      description: The 3rd party API key
                - type: object
                  description: >-
                    For Oauth flow, the callback url will be provided to handle
                    3rd party auth result.
                  properties:
                    callbackUrl:
                      type: string
                      description: Callback url
      responses:
        '200':
          description: API key is valid
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    description: 'For key flow, the API key to store and use'
                    required:
                      - key
                    properties:
                      key:
                        type: string
                  - type: object
                    description: 'For OAuth flow, the url user should be redirected to'
                    required:
                      - url
                    properties:
                      url:
                        type: string
                        description: Redirect url
        '403':
          description: API key is invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      tags:
        - Auth
  /auth/refresh:
    post:
      description: Renew the token used.
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  description: 'For OAuth flow, the refresh token.'
                  required:
                    - refreshKey
                  properties:
                    refreshKey:
                      type: string
                      description: Refresh token
      responses:
        '200':
          description: Returns refreshed access token
          content:
            application/json:
              schema:
                oneOf:
                  - type: object
                    required:
                      - key
                    properties:
                      key:
                        type: string
                        description: Access token
        '403':
          description: Not authorized error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      tags:
        - Auth
  /env:
    get:
      tags:
        - Environment
      description: >-
        Get all locales, default locale and hashmap with fields and labels for
        cache items
      responses:
        '200':
          description: List of locale codes and names
          content:
            application/json:
              schema:
                type: object
                properties:
                  defaultLocale:
                    type: string
                    example: en_AU
                  locales:
                    type: array
                    items:
                      $ref: '#/components/schemas/Locale'
                    example:
                      - name: English Australia
                        code: en_AU
                      - name: German
                        code: ge
                  cacheItemStructure:
                    $ref: '#/components/schemas/CacheItemStructure'
        '403':
          description: Not authorized error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
  /cache:
    get:
      tags:
        - Cache
      description: Get translatable item IDs
      responses:
        '200':
          description: List of translatable item IDs
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/UniqueItemIdentifier'
        '403':
          description: Not authorized error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
  /cache/items:
    post:
      tags:
        - Cache
      description: Retrieve cache items based on ids
      requestBody:
        description: List of cache item identifiers
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                items:
                  type: array
                  items:
                    $ref: '#/components/schemas/UniqueItemIdentifier'
      responses:
        '200':
          description: List of cache items
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/CacheItem'
  /publish:
    post:
      tags:
        - Translation process
      description: Update content in Content Management System
      requestBody:
        description: List of content items with translations
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                items:
                  type: array
                  items:
                    $ref: '#/components/schemas/ContentItem'
                  example:
                    - uniqueId: 'post:1:title'
                      groupId: 'post:1'
                      metadata:
                        contentType: post
                        field: title
                      translations:
                        en_US: Hello World!
                        ge: Hallo Welt!
                    - uniqueId: 'post:1:body'
                      groupId: 'post:1'
                      metadata:
                        contentType: post
                        field: body
                      translations:
                        en_US: >-
                          Lorem Ipsum is simply dummy text of the printing and
                          typesetting industry.
                        ge: >-
                          Lorem Ipsum ist einfach Blindtext der Druck- und
                          Satzindustrie.
      responses:
        '200':
          description: Content successfully updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    format: int32
                    example: 200
                  message:
                    type: string
                    example: Content successfully updated
        '400':
          description: List of validation errors for each content item grouped by language
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                    format: int32
                    example: 400
                  validationErrors:
                    type: object
                    items:
                      type: string
                    example:
                      'post:1:title':
                        en_US:
                          - Malformed content
                          - Content is too short
                        ge:
                          - Content is too long
                      'post:1:body':
                        ge:
                          - Content is too long
        '403':
          description: Not authorised
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '413':
          description: Payload Too Large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                code: 413
                message: Payload Too Large
        '429':
          description: Too many requests
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              example:
                code: 429
                message: Too many requests
  /translate:
    post:
      tags:
        - Translation process
      description: Get translatable content from Content Management System
      requestBody:
        description: List of cache item identifiers and required locales
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                locales:
                  type: array
                  items:
                    type: string
                  example:
                    - en
                    - en_US
                    - ru
                items:
                  type: array
                  items:
                    $ref: '#/components/schemas/UniqueItemIdentifier'
      responses:
        '200':
          description: List of translatable items
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ContentItem'
        '403':
          description: Not authorized error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
components:
  schemas:
    UniqueItemIdentifier:
      type: object
      required:
        - groupId
        - uniqueId
        - metadata
      properties:
        uniqueId:
          type: string
          minLength: 1
          maxLength: 256
          example: 'post:1:title'
        groupId:
          type: string
          minLength: 1
          maxLength: 256
          example: 'post:1'
        metadata:
          $ref: '#/components/schemas/UniqueIdMetadata'
    UniqueIdMetadata:
      type: object
      description: >-
        This object with flexible structure will be used to help mapping content
        items to items in Content Management system
      example:
        contentType: post
        field: title
    Locale:
      type: object
      required:
        - name
        - code
      properties:
        name:
          type: string
          example: English Australia
        code:
          type: string
          example: en_AU
    CacheItem:
      allOf:
        - $ref: '#/components/schemas/UniqueItemIdentifier'
        - type: object
          required:
            - fields
          properties:
            fields:
              type: object
              additionalProperties:
                description: Values to display in the cache table
                type: string
              example:
                uniqueId: 'post:1:title'
                title: Hello World
                id: 1
                contentType: Post
                createdAt: "2020-01-01T00:00:00.000Z"
                updatedAt: "2020-01-01T00:00:00.000Z"
    CacheItemStructure:
      description: Structure of cache item. Hashmap of field names and matching labels
      type: object
      example:
        title: Title
        contentType: Content type
        createdAt: Created at
        updatedAt: Updated at
    ContentItem:
      allOf:
        - $ref: '#/components/schemas/UniqueItemIdentifier'
      type: object
      required:
        - translations
      properties:
        translations:
          type: object
          additionalProperties:
            type: string
          example:
            en_US: Hello World!
            ge: Hallo Welt!
    ApiError:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
          example: 403
        message:
          type: string
          example: Not authorised
  securitySchemes:
    accessToken:
      type: apiKey
      description: AccessToken key to authorize requests.
      in: header
      name: X-Api-Token
security:
  - accessToken: []

Package your app

Packaging requirements coming soon.


Did this page help you?