The Zurich release has arrived! Interested in new features and functionalities? Click here for more

Alex Coope - SN
ServiceNow Employee
ServiceNow Employee

For those reading this just after the Christmas break, I hope you all had a good one and a happy new year. Just before the holidays my team and I were asked "how do you translate a Guided Tour?", so we put pen to paper and made another PoC Localization Framework artifact.

 

Thankfully Guided Tours (architecturally speaking) are actually quite flat. Meaning, there's a primary record on the [sys_embedded_tour_guide] table (which acts a bit like the parent record, then each step is sequentially created as child records on the [sys_embedded_tour_step] table.

 

Most of the translations we would need to make are a combinations of TRF's [sys_translated] and TRT's {sys_translated_text] because of the field types being used for the steps in a Guided Tour's definition. Let's see how simple we can make this.

 

 

The objective

So, to keep it simple, let's take the ootb demo Guided Tour from the /isc portal:

AlexCoopeSN_0-1673260821897.png

 

AlexCoopeSN_1-1673260853497.png

 

Imagine, we needed this in another language (let's take Japanese as an example), we can do this using the Localization Framework to run a query in a Processor script of an Artifact to identify all of the translatable elements.

 

The Artifact

We're going to make an artifact, if you aren't familiar what this is, or how to do that, check out the following posts:

 

In order to make an Artifact we need 3 primary things:

1. An Artifact record

(we gave ours the internal_name of "tour_guides")

AlexCoopeSN_2-1673261264852.png

 

2. A Processor Script

(we called ours "LF_Tour_Guide")

var LF_Tour_Guide = Class.create();
LF_Tour_Guide.prototype = Object.extendsObject(global.LFArtifactProcessorSNC, {
    category: 'localization_framework', // DO NOT REMOVE THIS LINE!

    /**********
     * Extracts the translatable content for the artifact record
     * 
     * @Param params.tableName The table name of the artifact record
     * @Param params.sysId The sys_id of the artifact record 
     * @Param params.language Language into which the artifact has to be translated (Target language)
     * @return LFDocumentContent object
     **********/
    getTranslatableContent: function(params) {
        /**********
         * Use LFDocumentContentBuilder to build the LFDocumentContent object
         * Use the build() to return the LFDocumentContent object
         **********/
        var tableName = params.tableName;
        var sysId = params.sysId;
        var language = params.language;
        var groupName = "";
        var lfDocumentContentBuilder = new global.LFDocumentContentBuilder("v1", language, sysId, tableName);

        var tourCheck = new GlideRecord('sys_embedded_tour_step');
        tourCheck.addQuery('guide', sysId);
        tourCheck.orderBy('order');
        tourCheck.query();

		// we need to call out the first button and last buttons
		lfDocumentContentBuilder.processString('Begin Tour', 'Default Messages', 'First Click');
		lfDocumentContentBuilder.processString('Cancel Tour', 'Default Messages', 'Cancel');
		lfDocumentContentBuilder.processString('Next', 'Default Messages', 'Normal Click');
		lfDocumentContentBuilder.processString('Complete', 'Default Messages', 'Last Click');
		
        while (tourCheck.next()) {
            lfDocumentContentBuilder.processTranslatableFieldsForSingleRecord(tourCheck, tourCheck.order + ' - ' + tourCheck.step_type + ' - ' + tourCheck.name, 'Name');
			// we need to process the action for the button of this step
			lfDocumentContentBuilder.processString(tourCheck.action_event.getDisplayValue().toString(), tourCheck.order + ' - ' + tourCheck.step_type + ' - ' + tourCheck.name, 'Action Button');
        }

        return lfDocumentContentBuilder.build();
    },

    /**********
     * Uncomment the saveTranslatedContent function to override the default behavior of saving translations
     * 
     * @Param documentContent LFDocumentContent object
     * @return
     **********/
    /**********
        saveTranslatedContent: function(documentContent) {},
    **********/

    type: 'LF_Tour_Guide'
});

 

3. A UI action

(I've highlighted the internal_name in the condition so you don't forget to put yours)

AlexCoopeSN_3-1673261386082.png

 

 

When these three things are created, we then need a "setting" in the Localization Framework that covers that artifact (if we don't have one that covers all) so that it understands what we want to do with it and in what languages.

 

 

Using the artifact

Now, if we go and generate a task for this artifact into Japanese, let's have a look at how much we've translated it:

AlexCoopeSN_4-1673261524041.png

 

AlexCoopeSN_7-1673261930107.png

^ It's showing as "Partially translated" because I've done some of it in a demo previously.

 

And if we go into the Comparison UI, we can see the following (with each step clearly listed out):

AlexCoopeSN_8-1673261976390.png

 

We are now free to add the translations how-ever we choose, manually, MT, TMS etc.

 

 

The outcome

What's our final outcome?


Here's the initial prompt:

AlexCoopeSN_9-1673262124507.png

 

And the same first step:

AlexCoopeSN_10-1673262171829.png

 

 

What have we learned?

We've seen that we can keep pushing the Localization Framework to make our translation needs simpler. The more we use it, the more we expand its artifact library, the more a repeatable process we can make. And the less spreadsheets are used, which believe me is a good thing.

 

 

If you've found this helpful / useful, please like share and subscribe as it always helps

 

 

Comments
Chris barnes1
Tera Explorer

Thanks again Alex, this is exactly what I needed.

Version history
Last update:
‎01-09-2023 03:07 AM
Updated by:
Contributors