Translation considerations for scripted assets

TrainzDev

New member
If your asset has a script, then you have the potential for all kinds of problems when it comes time to translate it.

You must not display text directly from the script file itself. Put all displayed text in the string table, and load it from there. Debug logging is the only exception to this rule.

Do not put anything in the String Table that is not used directly as displayed text. Localisation teams will re-write the entire contents of the string-table container. If you are looking for a location to load arbitrary configuration data, you should be using the extensions container.

You can get an object representing the string table by calling GetStringTable() on an Asset. A 'StringTable' object will be returned. This code will get the string table for the current asset:

Code:
StringTable textStrings = GetAsset().GetStringTable();

Strings from that string table object will be in the users local language, or English if no local translation is available.

Generally you'd declare the string table as a member in your class, and initialise it within the Init() function, like this:

Code:
class MyCustomScriptedLoco isclass Locomotive
{
  StringTable m_textStrings; // string table gets loaded here

  ...

  public void Init(void)
  {
    inherited();
    
    // Load the string table
    m_textStrings = GetAsset().GetStringTable(); 

    ...

  }

  ...

};

At any point in the code, When you need a string, you can then use:

Code:
m_textStrings.GetString("example-string-table-entry");

This will look up the line "example-string-table-entry" in the string table, and return that text.


Sometimes, a single entry isn't enough. If you are making a sentence, avoid any code which makes assumptions about how the sentence is constructed - those assumptions may not be valid in other languages. In particular, concatenating strings in code is going to cause problems.

Let's look at a sentence like this:

"Pick up the (3) loaded (flat/stake/box/gondola/hopper) cars from (<Bob's Lumber>)."

... with anything in brackets capable of changing.

It is understandably tempting to write something like this:

Code:
outString = m_textStrings.GetString("pickup-part-1") + carCount + m_textStrings.GetString("pickup-part-2") + m_textStrings.GetString("car-description-" + carType) + m_textStrings.GetString("pickup-part-3") + theIndustry.GetLocalisedName();

Please don't. While this might look fine in English, and maybe in a few other western European languages, you've coded the sentence structure into the script. The moment you need to switch the order of the number, car type, or industry name within the sentence for a language, it stops working. Instead you should write something like:

Code:
outString = m_textStrings.GetString3("pickup-text", carCount, m_textStrings.GetString("car-description-" + carType), theIndustry.GetLocalisedName();

The string table should look like this:

Code:
string-table
{
  pickup-text                   "Pick up the $0 loaded $1 cars from $2."
  car-description-box           "box"
  car-description-flat          "flat"
  car-description-gondola       "gondola"
  car-description-hopper        "hopper"
  car-description-stake        "stake"
}

The tokens ($0, $1, and $2) in 'pickup-text' are replaced by the strings used in the parameters to the GetString3() call. You can do this with up to ten substitutions, using the functions GetString1() to GetString10().
 
In some cases the scripts can be uploaded but in an encrypted form. Is there an internal mechanism that is applied automatically to select the correct string table (as mentioned in http://forums.auran.com/trainz/entry.php?872-Asset-Translation) or should script writers use the GetLanguage() function and use a case statement to select the correct string table for the translated entries in the string table. If the local publishers are only able to access the string tables in the config for localisation, then applying the method in your examples only addresses part of method.

If a script writer needs to place their own mechanism for selecting the correct table, then a working example would be appreciated as it would also document the number of languages that the GetLanguage() and return values supported in the current version of system. I am sure that many script writers do not want to install all the languages just to discover what is returned. Please note that the documentation supplied here: http://online.ts2009.com/mediaWiki/index.php/Class_StringTable#GetLanguage does not provide any information apart from the fact that the call exists.

Thanks
 
Kessica;bt3167 said:
In some cases the scripts can be uploaded but in an encrypted form. Is there an internal mechanism that is applied automatically to select the correct string table (as mentioned in http://forums.auran.com/trainz/entry.php?872-Asset-Translation)

Yes. The correct string-table is chosen for you in exactly that way.

If the local publishers are only able to access the string tables in the config for localisation, then applying the method in your examples only addresses part of method.

Translation should never require modifications to script, assuming the script has been written correctly in the first place.

All the script writer needs to do is make sure they use the string table appropriately (all displayed strings go in the string table, things that aren't displayed strings don't go in the string table), and avoid coding any assumptions about the format of the strings in the script. As I mentioned in my post, sentence structure and number format are typical examples that script writers make assumptions about that can cause problems once the asset is translated.
 
Do not put anything in the String Table that is not used directly as displayed text. Localisation teams will re-write the entire contents of the string-table container. If you are looking for a location to load arbitrary configuration data, you should be using the extensions container.
may it be, it will be usefull to make a function

Soup Asset.GetExtensionsSoup()

Because now the construction of Asset.GetConfigSoup().GetNamedSoup("extensions") is too complicated to write every time.
 
TRam__;bt3307 said:
may it be, it will be usefull to make a function

Soup Asset.GetExtensionsSoup()

Because now the construction of Asset.GetConfigSoup().GetNamedSoup("extensions") is too complicated to write every time.

Not really. It's a few extra characters to type. If you're using the extensions container heavily in your script, then you should follow two guidelines which will both save you typing and improve performance:

1. Store a reference to the 'extensions' soup in a local variable, instead of looking it up again each time you use it.
2. Read your 'extensions' data in your Init() function, rather than at runtime. Copy any persistent values into member variables once, rather than every time you need to use them.

hth,

chris
 
Back
Top