Using The MessageTrans System

Ben Summers shows how to file your messages

RISC OS 3 contains many features which allow easy modification of the system for foreign markets. One of the most useful is MessageTrans, a module which provides facilities for looking up text in a file. This allows any text the system uses to communicate with the user to be kept separate from the code and in a central resource. This enables anyone translating the system to work without access to the code.

You may have seen files called Messages inside applications which contain all the textual messages that the application uses. Under RISC OS 3, all parts of the operating system use message files as well.

If you click Menu on the Apps icon on the icon bar, choose Open '$' and then open the directory Resources and you'll see a directory for each module, most of them containing just a message file.

Unsurprisingly, RISC OS provides support for accessing these files in the form of MessageTrans. This module can load message files and extract text from them in a variety of ways. Each entry in the file has a token which is used by the program to identify which bit of text it requires.

The Message file format

It's not necessary to put the text used in window templates into a message file. Message files are designed to allow modification of textual messages without changing the program code, so as you only use the template editor to change text in templates, you wouldn't need to change your code anyway.

Message files are text files which follow a simple format. In the most basic case, it is simply a file with lines consisting of a token and value on each line, separated by a : (colon) like this:

TOKEN:Some text

When the token TOKEN is looked up by the program, the string Some text will be returned. This string is the value. You can have multiple tokens for the same value, separated by newlines or /.

This allows you to use the same value several times in your program, but retain the flexibility of being able to change just one without modifying the code. In the file:

TOKEN1
TOKEN2/TOKEN3:Some text

the value Some text is returned for the tokens TOKEN1, TOKEN2 and TOKEN3. Comments can be added in the file to help you find your way around it when you are editing it, by starting the line with a #, like this:

# This is a comment

You can also indicate where you want parameters to be substituted into the strings by placing a % followed by 0, 1, 2 or 3 into the value as shown below:

TOKEN:Parameter 0 = %0, parameter 1 = %1

In the call to find the value, you can specify up to four parameters which are substituted for these values. If no parameter is specified, the string is left alone. If %% is included in the value, % is substituted for it.

You can also include wildcarded tokens using the ? character. For example, in the file:

TOKEN1:Some text
TOKEN2/TOKEN3:Some more text
TOKEN?:Even more text

TOKEN1 would return the value Some text, while TOKEN2 and TOKEN3 would return Some more text and any other six letter token starting with TOKEN would return Even more text.

You can use both upper and lower case letter for tokens, and they are case-sensitive. You should use short, but informative, tokens for quick retrieval and ease of identification when you are programming.

Using a message file

Before using a message file you must first open it. You can get MessageTrans to create a buffer for it in the RMA, but it's best to create one in your application's workspace to avoid fragmentation of the RMA. This will also stop your program from leaving claimed blocks of memory in the RMA if it crashes during development.

To open a message file, use code like:

SYS "MessageTrans_FileInfo", , filename$ TO f%, , size%
IF (f% AND 1) = 0 THEN DIM msg_buffer% size%
DIM msgs% 256
$(msgs%+16) = filename$
SYS "MessageTrans_OpenFile", msgs%, msgs%+16, msg_buffer%
DIM msg_out% 256

filename$ should be set to the filename of the message file you want to use. For an application, it would be of the form <App$Dir>.Messages. You can have more than one file open at a time if necessary.

This code uses the SWI MessageTrans_FileInfo to find out how much memory is needed to buffer the file in memory. It returns some flags in f%, and the size required in size%.

Bit 0 of the flags is set if the file is already in memory, for example, held in the Resource filing system, where applications such as Edit and Draw are kept. If this is not the case, the code claims a block of memory using the DIM statement to store the file.

Another block is claimed to hold the file descriptor - a 16-byte block of memory which MessageTrans uses to identify which message file you want to access - and the filename of the file.

The SWI MessageTrans_OpenFile is used to load the file into memory and set up the file descriptor. Finally, a block of memory, msg_out%, is claimed for use in look-up calls.

To look up a token, use MessageTrans_Lookup. In the simplest case with no parameter substitution, the call would look like this.

SYS "MessageTrans_Lookup", msgs%, token$, msg_out%, 256, 0, 0, 0, 0 TO , , value$

msgs% is the file descriptor which was used to load the message file, token$ is the token to be looked up and msg_out% is the block claimed earlier to receive the string.

The number 256 is the size of msg_out%, and the four zeros show that no parameters are to be substituted. The value is returned in value$.

In all cases when you supply a token, you can also supply a default value which is used if the token cannot be found in the message file. If this default value was not present when the token could not be found, an error would be reported. You supply the default value by appending it to the token you pass, separated by a : like this:

token:Default value

It isn't necessary to use this facility - after all, if someone messes around with your message file they shouldn't be surprised if your program doesn't like it. It is useful, however, if you're writing a library of routines which may be used by someone else.

When you have finished with the message file, you must close it with MessageTrans_CloseFile:

SYS "MessageTrans_CloseFile", msgs%

This should be called up in your application's quit routine.

MessageTrans' ability to perform parameter substitution is perhaps one of its most powerful features. Instead of looking up lots of different tokens to make up the required string, you can simply specify up to four places where parameters are to go. You pass these parameters to the look- up call.

In the example of the look- up call above, all the parameters were zero specifying that a substitution should not take place. However, if those parameters are pointers to strings, they will be substituted into the string retrieved from the message file. To substitute parameters, use code like:

SYS "MessageTrans_Lookup", msgs%, token$, msg_out%, 256, p0$, p1$, p2%, p3% TO , , value$

In the above example, p0$ is substituted for %0 in the value, p1$ for %1 and so on. If you use parameter substitution, you don't have to include all the parameters. Any number of parameters can be zero.

If you want to substitute a number rather than a string, use STR$(number) to get Basic to create a string for you, as in:

SYS "MessageTrans_Lookup", msgs%, token$, msg_out%, 256, STR$(p), 0, 0, 0 TO , , value$

where p is the number you want to substitute for %0 in the value.

MessageTrans library

I've written a library of routines which you can use in your own programs. It's on this months MegaDisk. You can either include it as a library, like the example program, or include it directly in your program.

To load the message file and to initialise the library, use

PROCmsgtrans_init(filename$)

where filename$ is the filename of the messages file you want to load. If you are using it in an application, you should have set a system variable in your Run file similar to App$Dir - where App is the name of your application - with a line like:

Set App$Dir <Obey$Dir>

If your message file is called Messages and is within the application directory, you would use:

PROCmsgtrans_init("<App$Dir>.Messages")

to initialise the library. When your application has quit, you should close the message file with:

PROCmsgtrans_final

To look up a value in your messages file, use one of the look-up functions provided. There are five to allow up to four parameters for substitution into the value to be given.

These functions all have the form FNmsgtrans_lookupx (token$, p0$ ...) where x is the number of parameters it takes, and there are that number of parameters after token$ in its arguments.

For example, to look-up a value with no substitution, use:

value$ = FNmsgtrans_lookup0(token$)

where token$ is the token you want to look up. To look up a token and substitute two parameters, use:

value$ = FNmsgtrans_lookup2(token$, p0$, p1$)

where p0$ and p1$ are the two parameters you want to substitute. As with calling the SWI direct, you can use STR$(number) to substitute a number rather than a string, as in:

value$ = FNmsgtrans_lookup1(token$, STR$(p))

where p is the number you want to substitute.

Example program

On the MegaDisk, there's an example program which uses the library to retrieve messages from a messages file. You should run it using RunExample so that the program can find the message file.

Click here to download the example files for this article


Source: Acorn Computing June 1994
Publication: Acorn Computing
Contributor: Ben Summers