Building language capabilities into products

One of the challenges we have been attacking is how to build our products in such a manner that we could embed local language support. The majority of the content can be shipped in language product loads that allow the installer to set the language to be installed when the LPP is installed, however some of our compiled program content would have to be externalized so that it could be brought in at run time.

First we looked at a number of ways to store the static text while still allowing the variable content to be added, the way we can send a messages using API’s and commands is great for sending messages but we needed something that allowed the output to be sent to other targets such as log files and screen variables. Initially we used a keyed file where the key would be a MsgID and another field would contain the static text. The static text would have place holders where the variable text would be inserted just as we have the &x place holders in a message description only these would need to be based on the printf() placeholders.

Next problem we had to decide how to build a final string using the static text and some variable data. We use sprintf() within our compiled programs to create strings from static and variable data, however this is a different requirement. We have to bring in the static text from an external source and then add in the variable data to build a string of text. The original programs used #define to bring the static text strings from a header file at compile time, we did not want to have to ship programs and service programs for each individual language we supported so this was not an option. We needed to find a method that would allow an external static text string to be brought in and then a sprintf() like function to build the final message using the placeholders in the static string. We though about building a function like sprintf() that would allows us to walk through the static text, find the pace holders and then add the variable data, luckily ‘C’ already has a function to do that for us.

The vsprintf() function allows a format and arguments to be merged into an output string. All we had to do was create a function that accepted a string which would be built using a passed in format and any number of arguments. Here is a sample program that we used to test the theory.

#include <recio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#pragma mapinc(“tstf”,”CHLIB/LNG2924(LNG2924)”,”both”,””,””,”DATA_F”)
#include “tstf”
typedef DATA_F_LNG2924_both_t FREC;
#define _REC sizeof(FREC)

void vout(char *string, char *fmt, …);

int main(int argc, char **argv) {
_RFILE *fp; /* File Ptr */
_RIOFB_T *fdbk; /* Feed back Ptr */
FREC Filerec; /* File struct */
char Q_File[22] = “CHLIB/LNG2924”; /* test file name */
char t_string[12] = “my_string”; /* test string */
char msg_dta[1024]; /* buffer */
char fmt[1024]; /* format */

if((fp = _Ropen(Q_File,”rr”)) == NULL) {
    printf(“Failed to open File %s\n”,Q_File);
fdbk = _Rreadn(fp,&Filerec,_REC,__DFT);
if(fdbk->num_bytes == _REC) {
    do {
        fdbk = _Rreadn(fp,&Filerec,_REC,__DFT);
        }while(fdbk->num_bytes == _REC);
return 1;

void vout(char *string, char *fmt, …) {
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsprintf(string, fmt, arg_ptr);

The file we read in is built using the following SQL statements.

INSERT INTO CHLIB/LNG2924 VALUES(‘_IFS0000’, ‘Command Failed on Target’)
INSERT INTO CHLIB/LNG2924 VALUES(‘_IFS0001′,’Could not retrieve journal info %s’) 
INSERT INTO CHLIB/LNG2924 VALUES(‘_IFS0002′,’Qp0lGetPathFromFileID : %s’)
INSERT INTO CHLIB/LNG2924 VALUES(‘_IFS0003’, ‘Auto Resync request for IFS stored on target’)

The VAL field is where we store the static text that the final message will be built from, because we added the VAL field as a VARCHAR the ‘C’ compiler automatically creates a structure that allows us to determine the length of the string (it is set when the content is written to the file). the program is very simple, we read a file entry into the structure, we then copy the text string into a new variable and add a NULL terminator to the end of the string. We then pass that string as well as a pointer to our output string and the variables we want added to the format into a function called vout(). In the vout() function we set a pointer to the format that we want to be built and pass those into the vsprintf() function to build the string. Once we are finished we drop the pointer to the format and return.

(We did enhance the program a little to send a lot more parameters of different types to ensure the output matched what we would expect from a sprintf() request which proved the concept works.)

Here is a sample of the output using the above program

Command Failed on Target
Could not retrieve journal info my_string
Qp0lGetPathFromFileID : my_string
Auto Resync request for IFS stored on target

The next challenge was how to remove the need to manage the file open/read/close each time we wanted to write out some text. Opening and closing the file at program initialization and closing when the program were terminated would require significant program changes so we needed to look for an alternative method. We went for User Indexes, this allows us to key the data and store variable length content in the User Index while not needing to constantly add file management overhead(open/read/close) to every request for formatted output. Setting up the User Index is very easy using the API’s provided and it allows us a lot of flexibility in managing the content. We will have a program that creates the User index on LPP installation and populate the content based on the language ID being installed. If we have to change the content of the User Index as part of a PTF we can simply add or update the existing content within a program that will be shipped with the PTF.

So all we need now is the existing content to be translated (German will be our first attempt) and we will be able to provide a language based product to our users. We intend to have our first announcement of a product with local language support later this year and hope to convert all of our existing products to use the same technology soon after. Most HA/DR products are only provided with ‘English’ language content because ‘English’ is spoken by most (even if it is a second language), we think having the ability to choose local language content offers more flexibility and decreases complexity for the users.  

If you have a need for a High Availability / Disaster Recovery Solution that does not break the bank while still delivering a high level of sophistication and capabilities gives us a call, we know that we can offer a solution that meets your needs at a fraction of the cost of our competitors.