Database Datas Translation


The database data translation feature in Activator is designed to meet the needs of multilingual environments by providing efficient management of dynamic data translations. In many cases, applications must present information tailored to the end user's language, whether for user interfaces or user-generated content.

This feature prevents the need to manually manage the translation of records or duplicate data sets for each language, which would otherwise lead to potential errors and a significant increase in complexity. For instance, imagine an e-commerce platform where product descriptions, categories, and payment information need to be available in multiple languages. Thanks to the data translation feature in Activator Admin, these data can be centralized and managed efficiently, ensuring a consistent and localized user experience.

In this documentation, we will explore how to configure and use this feature to optimize translation management in your Activator environment.

 

Languages Supported By Activator

This multilingual📚 capability is essential for creating inclusive applications that meet the needs of diverse user groups. Whether developing or interacting with content, Activator's language support enables a personalized experience, making it easier to access and understand information in the chosen language.

 

Languages Supported By Activator And Default Language

Activator supports two languages: English and French.

 

Managing Languages With Activator Cookies

Activator leverages the language cookie to dynamically translate and provide localized values within an application. This approach ensures that users receive content in their preferred language, enhancing the overall user experience. Based on the language cookie, Activator can automatically detect and apply appropriate translations for various data elements, such as form labels, error messages and dynamic content.

 

How does it work?

On Activator, a field named _lang_keys is natively integrated into all data types. This field is specifically designed to store translation values of entities records, thereby facilitating multilingual data management.

The _lang_keys field is used to store translations of properties located at the same level within an object (consider an object here as an entity record). This means that each set of properties in an object, whether at the root level or a nested level, can have its own _lang_keys field to manage translations independently.

Here's an example to illustrate👇:

{
    "title": "A car",
    "description": "A car is a motor vehicle with wheels",
    "others": {
        "color": "blue",
        "_lang_keys": [
            {
                "name": "color",
                "languages": [
                    {
                        "name": "french",
                        "value": "bleue"
                    },
                    {
                        "name": "english",
                        "value": "blue"
                    }
                ]
            }
        ]
    },
    "_lang_keys": [
        {
            "name": "title",
            "languages": [
                {
                    "name": "french",
                    "value": "Une voiture"
                },
                {
                    "name": "english",
                    "value": "A car"
                }
            ]
        },
        {
            "name": "description",
            "languages": [
                {
                    "name": "french",
                    "value": "Une voiture est un véhicule à roues, motorisé"
                },
                {
                    "name": "english",
                    "value": "A car is a motor vehicle with wheels"
                }
            ]
        }
    ]
}

In this example, the _lang_keys field is present at two different levels of the object: at the root level to translate the properties title and description, and within the nested property others to translate the property color. This provides maximum flexibility in managing translations, ensuring that each property in an object can be translated contextually, based on its location within the data structure.


Here is a table describing the _lang_keys property:

PROPERTYTYPEDESCRIPTION
nameStringThe name of the main object property to which translations are associated.
languagesTable of objects

Contains translations of the specified property in different languages. Each object represents a translation for a specific language.

It must contain a collection of objects following the format:

{
    "name": "string",
    "value": "string"
}

name: The name of the language for which the translation is provided (“French”, “English”). 

value: The translated value of the property in the specified language.

 

Methods For Recording

This section aims to describe the different approaches and best practices for capturing and storing translations within the _lang_keys property.

 

In Client APIs

Using this API, you can add translations for different languages directly within an existing data record, ensuring that end users see the content in their preferred language.

API Details

 

PROPERTYDESCRIPTION
URL

https://v2_modulesapi_dev.asmlogic.com/api/tenants/{tenantId}/entities/{entityName}/records/{recordId}/languageResources

  • tenantId: The unique identifier of the tenant within the system.
  • entityName: The name of the Record entity on which the operation is to be performed.
  • recordId: The unique identifier of the specific record within the entity
MethodPOST
BODY
{
    "route": "string",
    "resources": [
        {
            "name": "string",
            "languages": [
                {
                    "name": "string",
                    "value": "string"
                },
                {
                    "name": "string",
                    "value": "string"
                }
            ]
        }
    ]
}

 

In A storedFunction Component

To record translations in storedfunction type components, you can use the following code as a shortcut. This code updates the language resources for a specific record within an entity.

// Unique identifier of the record for which translations will be applied
Guid recordId = Guid.Parse("ebbe2919-54d6-4891-b0ce-578a0f498000");

// Path within data object (empty if applicable at root level)
string path = string.Empty;

// Unserialize JSON data containing translations in a list of language resources
var langKeysDatas = this.DeserializeObjectFromJson<List<EntityRecordLanguageResource>>(modelJson);

// Update registration language resources
await this.Context.Entities.UpdateRecordLanguageResources(
    "activatord.entities.entityName", // Entity name
    recordId,                          // Record ID
    path,                              // Path within object (can be empty)
    langKeysDatas                      // Translation data
);

 

JSON format (model Json)
The JSON passed as an argument, modelJson, must respect the following format:

[
    {
        "name": "string",       // Target property name
        "languages": [
            {
                "name": "string",  // Language (for example, “french”, “english”)
                "value": "string"  // Corresponding translation
            }
        ]
    }
]

 

Usage In A Driver For Display

When retrieving data, translated information is often returned in the same format as it was updated. For example, an object containing translations for a title and description might look like this:

{
    "title": "A car",
    "description": "A car is a motor vehicle with wheels",
    "_lang_keys": [
        {
            "name": "title",
            "languages": [
                {
                    "name": "french",
                    "value": "Une voiture"
                },
                {
                    "name": "english",
                    "value": "A car"
                }
            ]
        },
        {
            "name": "description",
            "languages": [
                {
                    "name": "french",
                    "value": "Une voiture est un véhicule à roues, motorisé"
                },
                {
                    "name": "english",
                    "value": "A car is a motor vehicle with wheels"
                }
            ]
        }
    ]
}

Let's assume that this data is stored in a variable called item. The aim is to display the title and description in the user's current language.

The code below allows you to select and display translations according to the user's language:

const getTranslatedItem = function (objectElement, item) {
    if (objectElement._lang_keys && objectElement._lang_keys.length > 0) {
        // Find translations corresponding to the specified element (title or description)
        let translatedItem = objectElement._lang_keys.find(el => el.name == item)
        if (translatedItem && translatedItem.languages.length > 0) {
            // Retrieve the user's current language code (default is “english”)
            const langCode = $.cookie('user_language') || "english"
            // Find the right translation for your current language
            translatedItem = translatedItem.languages.find(el => el.name == langCode)
            // Return translated value or default value if translation not available
            return translatedItem ?.value || objectElement[item]
        }
    }
    // Return default value if no translation found
    return objectElement[item]
}

// Suppose the variable item contains the data of the record from the system.
getTranslatedItem('title', item)
getTranslatedItem('description', item)

 

Explanation of the Code

Function getTranslatedItem:

- Parameters:
 - objectElement: The name of the property to translate (e.g., title or description).
 - item: The object containing the data, including the translations.

Checking for Available Translations:

- The function begins by checking if the object contains translations (_lang_keys). If so, it looks for the translation corresponding to the specified item (e.g., the title).

Selecting the Language:

- The code retrieves the user's current language by checking a cookie ($.cookie('user_language')). If this cookie doesn't exist, the default language is set to English.

Finding the Appropriate Translation:

- The function searches for the translation in the user's language. If a translation is found, it is returned. Otherwise, the default value of the item (i.e., the untranslated version) is used.

Returning the Value:

- The function returns the found translation or, if unavailable, the default value.

Using the Function:

- To obtain the translation for the title or description, simply call getTranslatedItem('title', item) or getTranslatedItem('description', item).

 

Activator's Translation Utility

A UI tool that facilitates translations

 

Issues

This utility allows users to input the values of an item in multiple languages simultaneously, ensuring that the data is consistently available and aligned across all specified languages.

 

Graphic Presentation Of This Helper

Here's a presentation of the translation utility to show you how it works.

Utility Translation
Utility Translation
  1. Name of element to be translated
  2. Field for setting the value of an element according to language
  3. translation tab(The language changes from one tab to another on click)

 

How it works

This component is controlled by a setting found in the system settings⚙️ module.

Input Form Lamguages
Input Form Lamguages
  1. Accesses the component driver
  2. Component driver
  3. JSON content for management

JSON Content:

{
    "multiple": true,
    "resourceName": "sys.languageresources.sysMainResource",
    "languages": [
        {
            "name": "english",
            "title": "English",
            "description": "English",
            "titleResKey": "english",
            "descResKey": "english"
        }
    ]
}

 

PROPERTYREQUIREDDEFAULLT VALUEDESCRIPTION
multipleYestrueDetermines whether the component can handle multiple languages simultaneously 
ressourceNameYesSpecifies the name of the resource to which the component refers to manage translations.Specifies the name of the resource to which the component refers to manage translations.
languagesYes
[
    {
        "name": "english",
        "title": "English",
        "description": "English",
        "titleResKey": "english",
        "descResKey": "english"
    }
]

List of languages supported by the component. Each element in the table is a language object.

  • name: Language code or identifier (e.g. “english” for English).
  • title: Language title as it appears in the user interface (e.g. “English”).
  • description: Description of the language in the user interface (e.g. “English”).
  • titleResKey: Resource key used to retrieve the title associated with the language
  • descResKey: Resource key used to retrieve the description associated with the language.

 

How To Integrate It Into A Driver Component

To integrate🗃️ translation functionality into a driver, let's assume that we want to implement it in a specific context. This context will contain several important elements, including :

  • The container: the main container in which data is managed.
  • Item record (optional): specific data for the item to be translated, if available.

In this example, we'll concentrate on translating user input. If you'd like to know more about translating static text to be displayed in the application, please see the section on Language Ressources. However, the final summary in this example will also include translations of static texts to ensure a complete understanding of the process.

Load Helper

To start integrating the translation functionality into your driver, the first step is to load the appropriate helper. Here's how to do it:

$activator.ui.jsmodules.load('sys.jshelpers.sysRenderFieldForMultiLanguage')
.then((helper) => {
    // Now we simply need to instantiate the helper
});

This code loads the JS Helpers module sys.jshelpers.sysRenderFieldForMultiLanguage, which is essential for managing the display of multilingual fields in the user interface. Once this helper is loaded, you can instantiate the helper and use it to manage translations

Parameter Definition And Initialization

After loading the helper, the next step is to define the necessary parameters and initialize the helper. Here's how to do it:

$activator.ui.jsmodules.load('sys.jshelpers.sysRenderFieldForMultiLanguage')
.then((helper) => {
    
    /** Initialize internationalization of input fields */
    let param = { ...context };  // Copy of the context passed in parameter
    param.formId = "save-jobPositionTypes-form";  // Form identifier (HTML attribute value)
    param.container = $('body');  // Container in which the form will be rendered

    /*
        param.lang will contain the list of texts to be displayed.
        These values must be translated before use.
    */
    param.lang = {
        error: "Error",
        something_went_wrong: "Something went wrong",
        this_field_is_required: "This field is required",
        in: "in"
    };

    // Instantiate helper with defined parameters
    const InputFormLanguageHelper = new helper(param);
    InputFormLanguageHelper.initialize()
        .then(() => {
            // Actions to be taken after successful initialization
            // For example, here you can display data,
            // return the form or perform other operations
        })
        .catch(error => {
            toastr.error("Something went wrong", "Error");
            console.log(error);
        })
        .finally(() => {
           // Actions to be performed, such as hiding the loader
        });
    /** End of internationalization of entry fields */
});

 

PROPERTYDESCRIPTION
paramAn object containing the parameters needed to configure the helper, including the formId, the container, and a lang object for message translation.
InputFormLanguageHelper An instance of the helper that is initialized with the defined parameters.
initialize()This method initializes the helper, enabling the form to be configured and rendered according to the defined translations.

 

Form Display

After loading and initializing the helper, the last step is to display the form in the user interface. Here’s how to proceed:

$activator.ui.jsmodules.load('sys.jshelpers.sysRenderFieldForMultiLanguage')
.then((helper) => {
    
    /** Input Internationalization */
    let param = { ...context };
    param.formId = "save-jobPositionTypes-form";  // The form id (HTML attribute value)
    param.container = $('body');  // The container in which the form will be rendered

    /*
        param.lang will contain the list of texts to be displayed.
        Please ensure these values are translated accordingly.
    */
    param.lang = {
        error: "Error",
        something_went_wrong: "Something went wrong",
        this_field_is_required: "This field is required",
        in: "in"
    };

    // Instantiate the helper with the defined parameters
    const InputFormLanguageHelper = new helper(param);
    InputFormLanguageHelper.initialize()
        .then(() => {

            //CONTAINER = context.container Render the form within the container
            CONTAINER.html(`
                <form id="save-jobPositionTypes-form">
                    <div class="row">
                        <div class="col-lg-12">
                            ${InputFormLanguageHelper.render(
                                /** Input Internationalization */
                                {
                                    id: "title", // The name and id of the input
                                    title: "Title", // The input label
                                    placeholder: "Enter The Title", // The input placeholder
                                    type: "input", // The input type ['input' || 'textarea']
                                    requiredMessage: `This field is required` // Message when the field is required
                                },
                                true // Whether the field is required or not
                                /** End Input Internationalization */
                            )}
                        </div>
                        <div class="col-lg-12">
                            ${InputFormLanguageHelper.render(
                                {
                                    id: "description",
                                    title: "Description",
                                    placeholder: "Enter The Description",
                                    type: "textarea",
                                    requiredMessage: `This field is required`
                                },
                                true
                            )}
                        </div>
                    </div>
                </form>
            `);

        }).catch(error => {
            toastr.error(`Something went wrong`, `Error`);
            console.log(error);
        }).finally(() => {
           // For example, here you can hide the loader
        });
    /** End Input Internationalization */
});

 

PROPERTYDESCRIPTION
param.langContains text strings that should be displayed in the UI, such as error messages. These need to be translated as needed.
InputFormLanguageHelper.render()

there are two parameters, the first contains the definition of the field to be generated and the second tells you whether it's required or not.
First parameter:

  • id: will act as the id attribute of the field to be generated
  • title: will be the label of the generated field
  • Placeholder: the placaholder of the generated field
  • requiredMessage: the message displayed if the genere field is required but contains no value.

The second parameter, required, takes true or false.

CONTAINER.html()Injects the rendered form into the specified container.

 

✋CAUTION

In cases where the required parameter is set to true, the mandatory field will be the one corresponding to the current language the user is connected with. If the user does not provide a value, the translation for that field in the current language will not be performed.

The default value returned in the main object for a given field will be the value provided in the first input (the one corresponding to the current language the user is connected with).

 

How To Send It Default Values For Fields

To send default values to form fields, you can use the setValue{} method of the InputFormLanguageHelper object. Here's how you can do it in the context you've provided:

  1. Load Modules: The script begins by loading a JavaScript module called sysRenderFieldForMultiLanguage to manage the internationalization of form fields.
  2. Helper initialization: The form is initialized with translation parameters and HTML attributes such as formId and container.
  3. Render Form: The form is generated with input fields for title and description. Properties such as id, title, placeholder and requiredMessage are defined for each field.
  4. Load Default Values: If data (item) is available, it is used to pre-populate form fields using InputFormLanguageHelper setValue method.

Here is the commented code for a better understanding:


$activator.ui.jsmodules.load('sys.jshelpers.sysRenderFieldForMultiLanguage')
.then((helper) => {
    
    /** Input Internationlization */
    let param = { ...context }
    param.formId = "save-jobPositionTypes-form"  // The form id (HTML attribute value)
    param.container = $('body')

    /*
        param.lang contiendra la liste des textes qui pourront etre affichés, bien vouloir traduire apres ces valeurs
    */
    param.lang = {
        error: "Error",
        something_went_wrong: "Something went wrong",
        this_field_is_required: "This field is required",
        in: "in"
    }

    const InputFormLanguageHelper = new helper(param)
    InputFormLanguageHelper.initialize()
        .then(() => {

            CONTAINER.html(`
                <form id="save-jobPositionTypes-form">
                    <div class="row">
                        <div class="col-lg-12">
                            ${InputFormLanguageHelper.render(
                                /** Input Internationlization */
                                {
                                    id: "title", // The name and id of the input. 
                                    title: "Title", // The input label
                                    placeholder: "Enter The Title", // The input placeholder
                                    type: "input", // The input type ['input' || 'textarea']
                                    requiredMessage: `This field is required`
                                },
                                true // If the field is required or not
                                /** End Input Internationlization */
                            )}
                        </div>
                        <div class="col-lg-12">
                            ${InputFormLanguageHelper.render(
                                {
                                    id: "description",
                                    title: "Description",
                                    placeholder: "Enter The Description",
                                    type: "textarea",
                                    requiredMessage: `This field is required`
                                },
                                true
                            )}
                        </div>
                    </div>
                </form>
            `);

        }).catch(error => {
            toastr.error(`Something went wrong`, `Error`);
            console.log(error)
        }).finally(() => {
           // For example, here you can hide the loader
        })
    /** End Input Internationlization */
})

setValue{}: This method is used to fill form fields with default values. You pass an array of objects describing each field and the corresponding value of the item object.

This allows form fields to be pre-filled with existing values, for example when editing a record.

 

How To Retrieve Entered Values

To retrieve the values entered in the form fields, you can use the validateFields{} method of InputFormLanguageHelper. This method returns a promise that resolves with an object containing the field values if validation is successful and reject() when all required fields in the form are not completely filled out.

Here's how you can implement this in the click event listener of the submit button:

// (You can implement this piece of code in the click listener of your submit button.)
$('#submit-btn').on('click', function() {
    InputFormLanguageHelper.validateFields()
        .then(datas => {
            // 'datas' contains the values entered in the form fields
            console.log(datas); // Log the retrieved values
        })
        
});

This approach allows you to retrieve the entered data from the form and process it after ensuring that all required fields are correctly filled out.

 

Summary

After gaining an understanding of how the Activator Helpers sys.jshelpers.sysRenderFieldForMultiLanguage functions, it becomes much easier to implement clean and efficient code.

Suppose we have a language resource component that contains our translation 

 {
    "keys": [
        {
            "name": "error",
            "languages": [
                {
                    "name": "french",
                    "value": "Erreur"
                },
                {
                    "name": "english",
                    "value": "Error"
                }
            ]
        },
        {
            "name": "something_went_wrong",
            "languages": [
                {
                    "name": "french",
                    "value": "Une erreur est survenue"
                },
                {
                    "name": "english",
                    "value": "Something went wrong"
                }
            ]
        },
        {
            "name": "this_field_is_required",
            "languages": [
                {
                    "name": "french",
                    "value": "Ce champ est obligatoire"
                },
                {
                    "name": "english",
                    "value": "This field is required"
                }
            ]
        },
        {
            "name": "in",
            "languages": [
                {
                    "name": "french",
                    "value": "dans"
                },
                {
                    "name": "english",
                    "value": "in"
                }
            ]
        },
        {
            "name": "title",
            "languages": [
                {
                    "name": "french",
                    "value": "Titre"
                },
                {
                    "name": "english",
                    "value": "Title"
                }
            ]
        },
        {
            "name": "enter_title",
            "languages": [
                {
                    "name": "french",
                    "value": "Entrer le titre"
                },
                {
                    "name": "english",
                    "value": "Enter The Title"
                }
            ]
        },
        {
            "name": "description",
            "languages": [
                {
                    "name": "french",
                    "value": "Description"
                },
                {
                    "name": "english",
                    "value": "Description"
                }
            ]
        },
        {
            "name": "enter_description",
            "languages": [
                {
                    "name": "french",
                    "value": "Entrer la description"
                },
                {
                    "name": "english",
                    "value": "Enter The Description"
                }
            ]
        },
        {
            "name": "submit",
            "languages": [
                {
                    "name": "french",
                    "value": "Envoyer"
                },
                {
                    "name": "english",
                    "value": "Submit"
                }
            ]
        }
    ]
}

 

Let’s integrate the translation of static texts into our final summary. Please refer to Language Ressources for more details.

 // This time, we will use a Promise.all() because we are adding a new request before displaying our form.
Promise.all([
    $activator.ui.jsmodules.load('sys.jshelpers.sysRenderFieldForMultiLanguage'),
    $activator.ui.loadResource({source: 'activatord.languageresources.languageSourceDemo'}) // Add the request for our language resource if it has not yet been loaded into memory
]).then((results) => {
    const helper = results[0]
    /** Input Internationalization */
    let param = { ...context }
    param.formId = "save-jobPositionTypes-form"  // The form id (HTML attribute value)
    param.container = $('body')

    /*
        param.lang will contain the list of texts that can be displayed, please translate these values accordingly
    */
    param.lang = {
        error: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'error' }),
        something_went_wrong: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'something_went_wrong' }),
        this_field_is_required: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'this_field_is_required' }),
        in: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'in' })
    }

    const InputFormLanguageHelper = new helper(param)
    InputFormLanguageHelper.initialize()
        .then(() => {

            CONTAINER.html(`
                <form id="save-jobPositionTypes-form">
                    <div class="row">
                        <div class="col-lg-12">
                            ${InputFormLanguageHelper.render(
                                /** Input Internationalization */
                                {
                                    id: "title", // The name and id of the input. 
                                    title: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'title' }), // The input label
                                    placeholder: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'enter_title' }), // The input placeholder
                                    type: "input", // The input type ['input' || 'textarea']
                                    requiredMessage: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'this_field_is_required' })
                                },
                                true // If the field is required or not
                                /** End Input Internationalization */
                            )}
                        </div>
                        <div class="col-lg-12">
                            ${InputFormLanguageHelper.render(
                                {
                                    id: "description",
                                    title: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'description' }),
                                    placeholder: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'enter_description' }),
                                    type: "textarea",
                                    requiredMessage: $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'this_field_is_required' })
                                },
                                true
                            )}
                        </div>
                        <div class="col-lg-12">
                            <button type="button" class="btn btn-success" id="submit-btn">${$activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'submit' })}</button>
                        </div>
                    </div>
                </form>
            `);


            // Loading default values in the form (in the case of an update)
            if (item) {
                /** Input Internationalization */
                InputFormLanguageHelper.setValue(
                    [
                        {
                            id: "title",
                            field: "title"
                        },
                        {
                            id: "description",
                            field: "description"
                        }
                    ],
                    item
                )
                /** End Input Internationalization */
            }
            // End loading

        }).catch(error => {
            toastr.error($activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'something_went_wrong' }), $activator.ui.resourceString({ source: 'activatord.languageresources.languageSourceDemo', key: 'error' }));
            console.log(error)
        }).finally(() => {
           // For example, here you can hide the loader
        })
    /** End Input Internationalization */
})

 

Conclusion

Activator's database data translation functionality plays an important role in ensuring that an application's dynamic data is translated for various user groups. By enabling developers to manage translations efficiently, it ensures that data remains consistent and accessible, whatever the user's preferred language, enhancing the overall usability and international reach of the application.