Message Tables

From The Foundry MODO SDK wiki
Jump to: navigation, search

Scripts and plug-ins need to be able to communicate with the user in a variety of languages. This makes hard-coding strings in your application unwise, as you then need to manually edit your code whenever you want to add a new localization.

To avoid this issue, modo uses message tables, and makes these available to scripts and plug-ins. These are stored in config files; you can find many examples in the modo resrc directory (all the files starting with msg contain message tables).

Config Format

To use message tables in your script, you first need to create a message table config. Here is a simple example.

<?xml version="1.0"?>
<configuration>
    <atom type="Messages">

         <hash type="Table" key="myMessages.en_US">
             <hash type="T" key="HelloUser">Hello %1!</hash>
             <hash type="T" key="Goodbye">Bye!</hash>
         </hash>

    </atom>
</configuration>

Tables

Tables are where messages reside, and provide a mapping from internal message IDs to human-readable strings. Previously the message IDs could only be integers, but now they can be any string.

A message table's key is the name of the table, followed by a period and a language code. In the example above, the table myMessages is followed by .en_US to indicate US english. The list of possible language codes are standardized using the ISO-639 language string followed by an optional ISO-3166 country code, which allows en_UK to spell "color" as "colour", for example. If the localization requested cannot be found, the system will default to en_US when performing lookups.

The table itself contains a number of "T"-type hashes, with a string identifying the message as its key. The value contains the translated string to present to the user.

Substitutions

Messages support substitution strings, or arguments. These take the form of %1, %2, etc.. They can be in any order within the message. These are replaced at runtime with strings representing whatever information you feel appropriate.

Special Characters

Messages are standard text strings, but should be restricted to fairly standard characters. Some special XML-friendly character codes are also defined. Note that other XML character codes will not work.

XML Code Character Description
&lt; < Less Than
&gt; > Greater Than
&amp; & Ampersand
&apos; ' Apostrophe

Some parts of the application also support inserting new line characters in the form of the \ and n characters (that's "\n", not ASCII 10). These will automatically be replaced with a new line in the message. One example of a system that supports this is the dialog commands.


Alternates

A single message table entry can support multiple alternate messages. Most clients only use the primary message, but some clients (notably tree column headers) will choose the message that best fits the available space. You can have as many alternates as you like, usually with the longest one being the default "T" entry, then the alternates listed in order from longest to shortest.

For example, here is the definition for the "ColumnStars" message table, which is used for the "Star Rating" column in the Preset Browser. If the column is too narrow to fit the full "Star Rating" name, it will try the alternate, shorter "Rating" name. If there were more alternates, they would be tried until one is found that fits, or there are no more to try, in which case the shortest one is used and truncated for display.

   <hash type="T" key="ColumnStars" val="Star Rating">
       <list type="Alternate">Rating</list>
   </hash>

Note that not all clients support alternates, so specifying them might still only show the "T" entry even if there isn't enough room for it. At this time only tree column headers do, but more maybe added in the future.

Dictionaries

Dictionaries are somewhat legacy, but are still supported. A dictionary creates a mapping between internal strings that you use directly in your script, and the old integer message IDs used for message lookup. This is no longer necessary, as 'T' table entries can use dictionary keys as message IDs as above. When used, the dictionary and table must have the same name, although the dictionary doesn't include the language code.

In this example, you can see that the dictionary includes multiple "E"-type hashes. The key of those hashes is the internal name, while the value is the numeric ID that it maps to in the table. It can also be used to map string keys, if for some reason you wanted a message with two different keys.

<?xml version="1.0"?>
<configuration>
    <atom type="Messages">

         <hash type="Dictionary" key="myMessages">
             <hash type="E" key="HelloUser">1</hash>
             <hash type="E" key="Goodbye">2</hash>
         </hash>

         <hash type="Table" key="myMessages.en_US">
             <hash type="T" key="1">Hello %1!</hash>
             <hash type="T" key="2">Bye!</hash>
         </hash>

    </atom>
</configuration>

Using Message Tables

Once you have created a message table, you can drop its config into your kit or your user directory so that modo can find it. You can use these tables in many places that you can type in user strings that would be stored in the config, such as the names of forms or controls. To do so, you use the format @table@dict@ or @table@@id@ Either mode should work for either a dictionary lookup or a message ID these days.

To use the messages from within a script, you can use the ']]ScriptQuery: messageservice|messageservice]] ScriptQuery interface. For example to obtain the value of the message with ID "Goodbye" in our test table through msgfind, you could enter the following query:

query messageservice msgfind ? @myMessages@Goodbye@

This automatically finds the message that best matches the language code, which in our case returns the string "Bye!".

To get the "HelloUser" message, we'd use:

query messageservice msgfind ? @myMessages@HelloUser@

This returns the string "Hello %1!". That’s neat, but really we want to replace that %1 with something more useful. For that we use msgsub:

query messageservice msgsub ? "Bob"

This now returns the string "Hello Bob!". The %1 in the message we last found with msgfind was replaced with the string "Bob" by msgsub. You can keep calling msgsub for each successive argument in the message, each time getting back a more complete message until there are no more substitutions left to make.

If you just want to do this all in one call, you can use msgcompose:

query messageservice msgcompose ? {@myMessages@@HelloUser@ {Bob}}

msgcompose takes a table/dict or table/id pair followed by an argument list. Each argument is wrapped in curly braces, thus allowing quotes and other curly braces to easily be embedded in the string.

You can use these functions to provide human-readable, localized strings to your users instead of making them deal with whatever spoken language you happen to have coded you script in.

Other Messages

Messages can also be declared in other parts of the configs where they can be more closely associated with the context in which they are used. The Command help can declare messages for use by the command, and they are looked up using the command name as the message table. Messages can also be added to Item help and use the item name as message table.

More Information