SAGES,""); catinit (); printf(catgets(catfd, foobarSet, foobarRandom_Name, "Random text with string %s"), temp_name); catclose(catfd); exit(0); } catinit () { if (catfd != (nl_catd)-1) catfd = catopen("foobar",MCLoadBySet); } --- A Makefile for the above program is given below: ------- all: foobar catalog foobar: foobar.o gcc -o foobar -O2 foobar.c foobar.o: foobar-nls.h foobar-nls.h: foobar-nls.m gencat -new /dev/null foobar-nls.m -h foobar-nls.h catalog: gencat -new foobar.cat foobar.m install: all install -o root -m 0755 foobar /usr/local/bin install -o root -m 0755 foobar.cat /etc/locale/C clean: /bin/rm -f foobar *.o foobar-nls.h foobar.cat core ------- It is up to you where you group the message files. It may be easier to group the message files in another directory and separate the source code from the message files. 3.2 Writing software that is to be used on locale and non-locale systems It is fairly easy to abstract out the locale specific functions from the rest of the code. The usual method of doing this is via a define statement. eg, within the Makefile add the following: DEFINES = -DNLS foobar.o foobar.c gcc $(DEFINES) foobar.c Now within foobar.c we have the following: #ifdef NLS printf(catgets(catfd, chmodSet, chmodVM_exhausted, "Virtual Memory exhausted")); #else printf("Virtual Memory exhausted"); #endif These #ifdef/#endif statements will need to surround every locale specific function. These will include the and include files, the catfd static descriptor variable, the catinit() routine, catopen(), catclose(), catgets(), and setlocale(). As can be seen, this can get quite messy and can make the code very hard to read. A solution to using all the #ifdef NLS/#endif statements involves using a macro for the software. The macro file would include all the #include and variable descriptors for the locale specific version as well as defining routines to handle printing messages in both a locale capable system and a non-capable system. A sample macro package has been included below: --- #ifdef NLS #include #include extern nl_catd catfd; void catinit (); #endif /* Define Macros used */ #ifdef NLS #define NLS_CATCLOSE(catfd) catclose (catfd); #define NLS_CATINIT catinit (); #define NLS_CATGETS(catfd, arg1, arg2, fmt) \ catgets ((catfd), (arg1), (arg2), (fmt)) #else #define NLS_CATCLOSE(catfd) /* empty */ #define NLS_CATINIT /* empty */ #define NLS_CATGETS(catfd, arg1, arg2, fmt) fmt #endif --- Now instead of having to do: #ifdef NLS printf(catgets(catfd, chmodSet, chmodVM_exhausted, "Virtual Memory exhausted")); #else printf("Virtual Memory exhausted"); #endif all the time, we could rewrite this as: printf(NLS_CATGETS(catfd, chmodSet, chmodVM_exhausted, "Virtual Memory exhausted")); This will handle both cases very easily. Hence the changes now needed to support a locale version and a non-locale version are: - include a -DNLS define in the makefile if the system supports locale functions - #include the macro file into your source code - surround your #include "foobar-nls.h" with #ifdef NLS/#endif statements. Section 4. Where are the message catalogs stored? The following is the situation as I have managed to ascertain from various people. It should only be regarded as a very rough guide until I have had time to check the X/Open Portability Guide 4 standards. Message catalogs and other locale attributes are stored in a nest of subdirectories. The nest has two possible base points: /usr/lib/locale /usr/local/lib/locale The first is used by the software accompanying the base operating system. The second is used by externally installed packages - packages which are not considered part of the base OS. Under these directories, we now have the following subdirectories: LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME Notes: These are not to be confused with the variables of the same name. These are the actual subdirectory names and do not change (unlike their variable counterparts). To avoid confusion, the variables will now be referred to as $(LC_MESSAGES) etc. Under these subdirectories are the various country subdirectories. eg, under /usr/lib/locale/LC_MESSAGES we could have the following directories: C POSIX -> C en_US.88591 de_DE.88591 fr_BE.88591 And under these directories, the language and code specific message catalogs are stored. Hence, the message catalog for the "ls" binary on an American English speaking system would be stored under: /usr/lib/locale/LC_MESSAGES/en_US.88591/ls.cat The general format is as follows: /usr/lib/locale/LC_MESSAGES/xx_YY.ZZZ/mm.cat ^^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^ root category lang catalog The root does not change - its either /usr/lib/locale for system software or /usr/local/lib/locale for externally installed software. The category is only dependent upon the type of locale functions the software is attempting to access. If the software was looking up information on the monetary variables for the particular locale, then it would be searching in: /usr/lib/locale/LC_MONETARY/xx_YY.ZZZ/ for the information. The lang component is possibly the most important and is the component that determines which variables and directories the system searches in to obtain the info it needs. The format of the lang component is as follows: language_country.characterset The following examples will illustrate it: en_US.88591 English language in the USA using the ISO 88591 character set de_DE.88591 German language in Germany using the ISO 88591 character set fr_BE.88591 French language in Belgium using the ISO 88591 character set The lang component is set by the user through the $(LANG) environment variable. The user will establish the correct language, country and character set, and set his $(LANG) environment variable accordingly. The OS will then use the $(LANG) environment variable when searching the appropriate subdirectories to find the information or message catalogs that it needs - as detailed by the setlocale() command. We've outlined the two default places above that the system uses to store message catalogs and other locale attributes. However, the system must also be able to handle users who cannot install message catalogs in either of these places (doing so usually requires superuser privileges) and instead must install message catalogs within their own personal home directories. The system can accommodate message catalogs store here (or in any other non-standard place) by the use of the NLSPATH environment variable. The NLSPATH environment variable lists directories which the OS examines to find the necessary message catalogs. eg, NLSPATH=/usr/lib/locale/LC_MESSAGES/%L/%N:/usr/local/lib/locale/LC_MESSAGES/%L /%N:~/messages/%N where %L represents the value of the LANG environment variable and %N = the name of the catalog These two values (%L and %N) are substituted by the OS at evaluation time. The the user can store their own message catalogs within their home directories and have the system automatically access them. They can even override the default message catalogs stored on the system by rearranging the order of the entries for the NLSPATH environment variable. Section 5. Frequently Asked Questions Q. How do I know if the Unix platform I am using supports the locale routines? A. A Unix platform that supports the full range of locale functions must have two include files: locale.h and nl_types.h These are usually found in /usr/include. If one or both of these files are missing, then the OS may only support a subset of the locale functions. Both are included with Linux. The material covered in this document is variously copyrighted by Alfalfa Software, Mitchum DSouza, and Patrick D'Cruze - 1989-1994. Please send any suggestions, feedback, or notification of errors to the author. I can be contacted at: pdcruze@orac.iinet.com.au