		          Locale Library

This document has two parts:

	- Introduction To Locale
	- Writing Localized Applications


NOTE - See also the locale examples which include code you can link
with to self-load your catalogs on V37 machines.



			Introduction to Locale
                        ======================
   (c) Copyright 1991-93 Commodore-Amiga, Inc. All Rights Reserved

Localization is the process by which software dynamically adapts to
different locales.  A locale is made up of a set of attributes
describing a language, a set of cultural and lingual conventions, a
physical location, etc.

Without standardized system support to help deal with localization
issues, the task of localizing applications is significant.  There
needs to be several different versions of every application, each
specially adapted to run in a particular language and country.

Given the importance of the international market to the Amiga, it is
imperative that the operating system provide services to facilitate,
and thus encourage, application software localization.  This is where
locale.library comes in.

The locale.library is an Amiga shared library offering services to let
applications transparently adapt to any locale the user has chosen.
Functions are provided for formatted information display, text catalog
management, character attribute acquisition, string sorting, and more.


Compatibility-Now and in the Future

In a world where inter-platform compatibility is increasingly
important, it is a significant advantage to adhere as closely as
possible to adopted standards.  This makes developing and porting
applications to the Amiga easier, thus encouraging it.

The locale.library offers the necessary services to implement fully
compatible X/Open - ANSI/C localization support.  Simple wrapper
functions need to be provided by compiler writers to adhere to these
standards, but the nuts and bolts of localization is in the library.

The locale.library provides for easy and virtually unlimited expansion.
All character manipulation routines are defined as accepting 32-bit
characters, allowing future support for multi-byte character sets such
as UniCode.  Provisions are also made to allow the use of different
character sets such as Cyrillic.

Since all language-specific text manipulation is implemented within
language drivers, the library does not impose any artificial limitation
on the simplicity or complexity of the algorithms used within the
drivers.  This enables the creation of very specialized sorting
algorithms that adhere closely to a language's grammatical rules.
Workbench Disk

System localization is accomplished by an update to the Workbench disk.
The changes needed include:

locale.library - This is the central element to localization and
contains routines to allow management of locales, catalogs, and strings.

Language drivers - Much of locale.library's functionality is
implemented through language drivers.  There is one such driver for
every language supported.  All the language drivers are stored in the
LOCALE:Languages directory.

Country database - A set of files describing various attributes of
every country supported, including the country's currency symbol,
number format, measuring system, etc.  Country files are stored in the
LOCALE:Countries directory.

Locale preferences editor - A preferences editor found in the Prefs
drawer that lets the user select which languages he speaks, the country
in which he lives, and his time zone.

Revised system programs - Most programs found on the Workbench and
Extras disks need to be updated in order to use locale.library.

System catalogs - A set of system text catalogs stored in the
LOCALE:Catalogs directory.  These provide all the strings needed to
operate the system in different languages.

Application catalogs - A set of application text catalogs stored in
either the PROGDIR:Catalogs or LOCALE:Catalogs directories.  These are
provided by applications and let them run in different languages.


Patching the ROM

When an updated version of IPrefs is run, it instructs locale.library
to patch several ROM routines to transparently provide them with
localized behavior.

The ROM routines patched by locale.library are:

exec.library/RawDoFmt()
    Adds argument ordering support using nn$ specifications (see below
for more information on this).  Adds %U and %D formatting commands for
localized number output.

dos.library/DosGetString() (internal routine)
    Adds the ability to make use of disk-based text catalogs instead of
always returning ROM-based strings.

dos.library/DateToStr() & dos.library/StrToDate()
    Process and output localized dates

utility.library/ToLower() & utility.library/ToUpper()
    Use the current language driver to adapt their behavior based on
the user's current locale

utility.library/Strnicmp() & utility.library/Stricmp()
    Use the current language driver to adapt their behavior based on
the user's current locale

In addition, a new version of the LoadWB command patches a routine in
workbench.library which enables the Workbench to run in the user's
preferred language.


Disk Files

The locale.library loads three types of files from disk to provide its
functionality:

   o  Preferences files
The user's locale preferences are stored as a standard prefs file named
ENV:Sys/locale.prefs.  The Locale prefs editor is provided to let the
user control the contents of this file. An in-memory Locale structure
is built directly based on a locale preferences file.

   o  Language drivers
Each locale has a single language driver bound to it.  When a locale is
loaded in memory, a language driver is also loaded and automatically
bound to the locale.  The language drivers are located in
LOCALE:Languages.  For example:

                LOCALE:Languages/dansk.language
                LOCALE:Languages/francais.language
                LOCALE:Languages/italiano.language

        A language driver is an Amiga shared library providing around a
dozen functions for string and character-oriented operations that need
to be adapted depending on the language being used.  The way each
function is implemented is hidden to locale.library, allowing maximum
flexibility for the driver when dealing with complex languages.

    o  Message catalogs
Catalogs contain a series of translated strings for use by an
application.  They are stored on disk in an IFF file that is processed
and managed by calls in the library.  A utility called CatComp is
provided that lets catalog files be generated easily.

        Each locale contains a list of preferred languages.  When a
message catalog is accessed via the OpenCatalog() function,
locale.library attempts to find a catalog in one of the locale's
preferred languages.  OpenCatalog() looks in two different places for
message catalogs:

                PROGDIR:Catalogs/languageName/catalogName
                LOCALE:Catalogs/languageName/catalogName

        languageName is one of the locale's preferred languages.
catalogName is the name of the catalog the application wishes to open.
Typically, catalogName is the name of the application suffixed with
``.catalog''.

Looking in the LOCALE: directory for catalogs lets the user put message
catalogs for small applications (system tools for example) into a
single directory, or even simpler on a single disk called LOCALE.  In
addition, using LOCALE: gives applications loaded as resident the
ability to load in catalogs.  Since resident applications do not get
the benefit of PROGDIR:, LOCALE: becomes the automatic fall-back.

Of course, language drivers and catalogs are transparently cached by
locale.library and get flushed whenever a memory panic occurs and when
their respective use count is 0.


Functions

locale.library implements the actual localization facilities for
application use.  Around two dozen functions are available to access
and manipulate locales, catalogs, and strings.  Language drivers
implement much of the functionality found in locale.library.  The
drivers offer services needed by the higher-level functions in the
library.  Each locale is bound automatically to a single language
driver.


Locales

The two most basic routines in the library are OpenLocale() and
CloseLocale().  OpenLocale() returns a pointer to a Locale structure,
which can then be used with most of the other functions in the library.
A Locale structure describes all the attributes necessary to localize
an application and is built from the values found in a locale
preferences file.

During the OpenLocale() call, a language driver is automatically bound
to a locale.  The language driver bound to the locale depends on the
list of preferred languages specified by the user.  There is one driver
per supported language.  Drivers are easy to write and new ones can be
added by C= or third parties to support new languages as demand arises.

The language drivers offer specific services needed by the library to
implement its functionality.  This currently involves only text
manipulation functions specifically adapted to the language at hand.
For example, sorting text behaves differently from language to language
and needs to be taken care of by specialized code.


Catalogs

Message catalogs are a simple means for an application to separate the
strings of a program from the actual executable.  By adding new message
catalogs, the application can be made to support new languages and to
display all of its output in the desired language.

Message catalogs are loaded in memory via the OpenCatalog() function.
Two of the parameters required by this function are a locale and a
catalog name.  The locale is provided since it contains the list of the
user's preferred languages.  Based on these languages, OpenCatalog()
attempts to find the best catalog possible from the series of catalogs
provided by the application.

Once a message catalog is in memory, any string it contains can be
retrieved using the GetCatalogStr() routine.  The routine expects a
number specifying which string of the catalog is to be returned.
Sparse numbering of the strings is supported providing the most
convenience possible to the application writer.

Date And Time

A Locale structure specifies special date and time formatting strings.
These strings describe exactly how the date and time are to be
displayed and interpreted.  The FormatDate() routine is provided to
convert an AmigaDOS DateStamp structure into a localized date string,
while the ParseDate() routine converts a string to a DateStamp
structure.


Argument Positioning

The grammar of most languages requires that subjects, objects, adverbs
and complements be written out in a specific order.  This order varies
from language to language.  For example:

        ``Please insert volume Workbench in drive DF0:''

might be said as:

        ``Insert in drive DF0: volume Workbench please''

To address this problem, an extension to the standard C-language
printf() conventions used by FormatString() is argument position
specification.  Specifying the argument position lets the order of the
% commands change while the arguments provided remain the same.  Using
the C printf() call as an example:

eyes = 2;
feet = 3;
ears = 4;
printf("%d eyes, %d feet and %d ears",eyes,feet,ears);
printf("%3$d ears, %1$d eyes and %2$d feet",eyes,feet,ears);

These two statements would produce the following output:

        ``2 eyes, 3 feet and 4 ears''   for the first
        ``4 ears, 2 eyes and 3 feet''   for the second

The argument positioning feature lets you change the format string
being processed while keeping the data stream the same.  This is an
invaluable tool when translating strings to different languages.

So, in practical terms, you can change the order in which things
appear.  As an example, the following string from the workbench catalog:

        ``Amiga Workbench  %lD graphics mem  %lD other mem''

can be changed to:

        ``Amiga Workbench  %2$lD other mem  %1$lD graphics mem''

and the string produced and displayed in the title bar of the Workbench
would be correct.

The locale.library patches exec.library/RawDoFmt() to give it support
for argument ordering.  In addition, locale's own FormatString()
routine supports argument ordering as well.


Sorting

Sorting follows different rules based on the language of the strings
being sorted.  Thus, locale.library provides two routines to deal with
sorting: StrnCmp() and StrConvert().

StrnCmp() is similar to the C function of the same name, except that it
takes an argument specifying which type of sort to perform.  There are
currently three supported sorts, each having its own role.  Refer to
the autodoc entry for the StrnCmp() function for more information.

StrConvert() is a routine to enhance performance.  Its purpose in life
is to convert a string into a special format that can be compared
efficiently.  That is, the more advanced comparison types offered by
StrnCmp() can be several times slower than a standard ASCII-based
strncmp() call in C because StrnCmp() takes into account many more
factors when sorting.

In programs, it is often necessary to compare a single string with many
different strings.  Think for example of how alphabetical insertion
into a list is done.  In these cases, the slower operation of StrnCmp()
vs strncmp() can impact performance.  To alleviate this, StrConvert()
allows you to perform most of the work performed by StrnCmp() only
once.  Once a string has been converted by StrConvert(), it can be used
with regular C strncmp() calls.

To clarify, given two strings A and B, the following will produce
equivalent results:

StrnCmp(A,B)  ==  StrConvert(A,a)
StrConvert(B,b)
strncmp(a,b)


Example

Here is a sample localized C program.  This program requires a message
catalog called sample.catalog to get its strings from.

/* localetest.c */
#include <exec/types.h>
#include <exec/libraries.h>
#include <libraries/locale.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/locale_protos.h>


/*****************************************************************************/


extern struct Library *DOSBase;
extern struct Library *SysBase;
       struct Library *LocaleBase;
       struct Catalog *catalog = NULL;


/*****************************************************************************/


enum AppStringsID
{
    MSG_HELLO,
    MSG_BYE
};



/*****************************************************************************/


STRPTR AppStrings[] =
{
    "Hello World!\n",
    "Bye, I'm leaving...\n"
};


/*****************************************************************************/


STRPTR GetString(enum AppStringsID id)
{
    if (LocaleBase)
        return(GetCatalogStr(catalog,id,AppStrings[id]));

    return(AppStrings[id]);
}


/*****************************************************************************/


VOID main(VOID)
{
    if (LocaleBase = OpenLibrary("locale.library",38))
        catalog = OpenCatalogA(NULL,"sample.catalog",NULL);

    PutStr(GetString(MSG_HELLO));
    PutStr(GetString(MSG_BYE));

    if (LocaleBase)
    {
        CloseCatalog(catalog);
        CloseLibrary(LocaleBase);
    }
}


Structures

The Locale structure is the main public structure provided by
locale.library.  The structure is defined in <libraries/locale.h> and
consists of the following fields:

STRPTR loc_LocaleName
    Locale's name.
STRPTR loc_LanguageName
    The language of the driver bound to this locale.
STRPTR loc_PrefLanguages[10]
    The ordered list of preferred languages for this locale.
ULONG loc_Flags
    Locale flags.  The single current flag specifies whether daylight
    savings time should be used in the system.
ULONG loc_CodeSet
    Specifies the code set required by this locale. Currently, this
value is always 0.
ULONG loc_CountryCode
    The international country code.
ULONG loc_TelephoneCode
    The international telephone code for the country.
LONG loc_GMTOffset
    The offset in minutes of the current location from GMT.
UBYTE loc_MeasuringSystem
    The measuring system being used.
UBYTE loc_CalendarType
    The type of calendar being used.
STRPTR loc_DateTimeFormat
    The date and time format string, ready to pass to FormatDate().
STRPTR loc_DateFormat
    The date format string.
STRPTR loc_TimeFormat
    The time format string.
STRPTR loc_ShortDateTimeFormat
    The short date and time format string, ready to pass to
    FormatDate().
STRPTR loc_ShortDateFormat
    The short date format string.
STRPTR loc_ShortTimeFormat
    The short time format string.
STRPTR loc_DecimalPoint
    The decimal point character used to format non-monetary quantities.
STRPTR loc_GroupSeparator
    The characters used to separate groups of digits before the
    decimal-point character in formatted non-monetary quantities.
STRPTR loc_FracGroupSeparator
    The characters used to separate groups of digits after the
    decimal-point character in formatted non-monetary quantities.
STRPTR loc_Grouping
    A string whose elements indicate the size of each group of digits
    before the decimal-point character in formatted non-monetary
    quantities.
STRPTR loc_FracGrouping
    A string whose elements indicate the size of each group of digits
    after the decimal-point character in formatted non-monetary
    quantities.
STRPTR loc_MonDecimalPoint
    The decimal point used to format monetary quantities.
STRPTR loc_MonGroupSeparator
    The separator for groups of digits before the decimal point in
    monetary quantities.
STRPTR loc_MonFracGroupSeparator
    The separator for groups of digits after the decimal point in
    monetary quantities.
STRPTR loc_MonGrouping
    A string whose elements indicate the size of each group of digits
    before the decimal-point character in monetary quantities.
STRPTR loc_MonFracGrouping
    A string whose elements indicate the size of each group of digits
    after the decimal-point character in monetary quantities.
UBYTE loc_MonFracDigits
    The number of fractional digits (those after the decimal-point) to
    be displayed in a formatted monetary quantity.
UBYTE loc_MonIntFracDigits
    The number of fractional digits (those after the decimal-point) to
    be displayed in an internationally formatted monetary quantity.
STRPTR loc_MonCS
    The local currency symbol applicable to the current locale.
STRPTR loc_MonSmallCS
    The currency symbol for small amounts.
STRPTR loc_MonIntCS
    The international currency symbol applicable to the current locale.
    The first three characters contain the alphabetic international
    currency symbol in accordance with those specified in ISO 4217
    Codes for the Representation of Currency and Funds.  The fourth
    character (immediately preceding the NULL) is the character used to
    separate the international currency symbol from the monetary
    quantity.
STRPTR loc_MonPositiveSign
    The string used to indicate a nonnegative-valued formatted monetary
    quantity.
UBYTE loc_MonPositiveSpaceSep
    Specifies the number of spaces separating the currency symbol from
    the non-negative monetary quantity.
UBYTE loc_MonPositiveSignPos
    Set to a value indicating the positioning of loc_MonPositiveSign
    for a non-negative monetary quantity.
UBYTE loc_MonPositiveCSPos
    Set to 1 or 0 if loc_MonCS respectively precedes or succeeds the
    value for a non-negative monetary quantity.
STRPTR loc_MonNegativeSign
    The string used to indicate a negative-valued monetary quantity.
UBYTE loc_MonNegativeSpaceSep
    Specifies the number of spaces separating the currency symbol from
    the negative monetary quantity.
UBYTE loc_MonNegativeSignPos
    Set to a value indicating the positioning of loc_MonNegativeSign
    for a negative formatted monetary quantity.
UBYTE loc_MonNegativeCSPos
    Set to 1 or 0 if loc_MonCS respectively precedes or succeeds the
    value for a negative formatted monetary quantity.
    The grouping tables pointed to by loc_Grouping, loc_FracGrounping,
    loc_MonGrouping, and loc_MonFracGrouping contain a stream of bytes
    with the following values:

255     No further grouping is to be performed.

0       The previous element is to be repeatedly used for the remainder
of the digits.

1..254  The integer value is the number of digits that comprise the
current group.  The next element is examined to determine the size of
the next group of digits before the current group.

The values of loc_MonPositiveSignPos and loc_MonNegativeSignPos are
interpreted according to the following:

0       Parentheses surround the quantity and currency symbol
1       The sign string precedes the quantity and currency symbol
2       The sign string succeeds the quantity and currency symbol
3       The sign string immediately precedes the currency symbol
4       The sign string immediately succeeds the currency symbol.


Using Locale.library from ARexx

locale.library provides an ARexx function host interface enabling ARexx
programs to take advantage of system localization.  The functions
provided by the interface are directly analogous to the functions
available to C and assembly programmers, with the differences mostly
being in the way they are called.

The function host library vector is located at offset -30 from the
library.  This is the value you provide to ARexx in the AddLib()
function call.  Here is a sample ARexx script to add locale.library to
ARexx's function host list:

    /* Make sure the library is loaded as a function host */
    IF ~SHOW(L,'locale.library') THEN DO
      CALL ADDLIB('locale.library',0,-30)
    END;

As mentioned, the functions available through the function host are
very similar to the ones available from C and assembly.  Here is a list
of the commands supported through the function host.  Each command has
an AmigaDOS-style template that describes the arguments it accepts,
along with a short description of what the command does.  Refer to the
autodoc entries with the same names for more complete explanations of
what each command is intended for.

CloseCatalog (CATALOG/N/A)
    Closes a previously opened message catalog.  Pass it the return
    value from OpenCatalog().
ConvToLower (CHARACTER/A)
    Pass it the character to convert and it returns the converted
    character.
ConvToUpper (CHARACTER/A)
    Pass it the character to convert and it returns the converted
    character.
GetCatalogStr (CATALOG/A,STRING/N/A,DEFAULT/A)
    Extract a string from a catalog.  Pass it the catalog obtained from
    OpenCatalog(), the number of the string to extract from the
    catalog, and a default string to return in case the requested
    string is not in the catalog. IsAlNum (CHARACTER/A) - Pass it a
    character and it returns 1 if the character is alphanumeric, or
    returns 0 otherwise
IsAlpha (CHARACTER/A)
    Pass it a character and it returns 1 if the character is
    alphabetic, or returns 0 otherwise
IsCntrl (CHARACTER/A)
    Pass it a character and it returns 1 if the character is a control
    character, or returns 0 otherwise
IsDigit (CHARACTER/A)
    Pass it a character and it returns 1 if the character is a digit,
    or returns 0 otherwise
IsGraph (CHARACTER/A)
    Pass it a character and it returns 1 if the character can be
    represented graphically, or returns 0 otherwise
IsLower (CHARACTER/A)
    Pass it a character and it returns 1 if the character is lower
    case, or returns 0 otherwise
IsPrint (CHARACTER/A)
    Pass it a character and it returns 1 if the character is a space,
    or returns 0 otherwise
IsPunct (CHARACTER/A)
    Pass it a character and it returns 1 if the character is a
    punctuation mark, or returns 0 otherwise
IsSpace (CHARACTER/A)
    Pass it a character and it returns 1 if the character is a white
    space, or returns 0 otherwise
IsUpper (CHARACTER/A)
    Pass it a character and it returns 1 if the character is upper
    case, or returns 0 otherwise
IsXDigit (CHARACTER/A)
    Pass it a character and it returns 1 if the character is a
    hexadecimal digit (0..9, a..f, A..F), or returns 0 otherwise
OpenCatalog (NAME/A,BUILTIN/A,VERSION/N/A)
    Opens a message catalog and returns a value to be used for
    GetCatalogStr() and CloseCatalog(). The first parameter specifies
    the name of the catalog to load, the second parameter specifies the
    language used for the default strings in the script, and the final
    parameter specifies the version number of the catalog to open.  A
    version of 0 means any version is fine.
StrnCmp(STRING1/A,STRING2/A,TYPE/N/A)
    Compares two strings and returns -1 if STRING1 comes before
    STRING2, returns 0 if both strings are equal, and returns 1 if
    STRING1 comes after STRING2.  The TYPE parameter is either 0 to do
    an ASCII comparison, 1 to do a single pass collation, or 2 to do a
    two pass collation (this corresponds to the C SC_ASCII, SC_COLLATE1
    and SC_COLLATE2 constants explained in the autodoc entry for
    StrnCmp() ). Here is a sample ARexx script that exercises some of
    locale's function host commands:

        /* localetest.rexx */

        /* Make sure locale is loaded as a function host */
        IF ~SHOW(L,'locale.library') THEN DO
          CALL ADDLIB('locale.library',0,-30);
        END;

        say ConvToLower("A");
        say ConvToUpper("b");
        say IsAlpha("1");

        catalog = OpenCatalog("workbench.catalog","english",0);
        say GetCatalogStr(catalog,34,"default");
        say CloseCatalog(catalog);
        say StrnCmp("abc","def",2);




		    Writing Localized Applications
                    ==============================

   (c) Copyright 1991-93 Commodore-Amiga, Inc. All Rights Reserved

One of the important goals while developing locale.library was to keep
it simple in order to encourage its use.  The result is that
applications can be localized with little effort on the programmer's
part.  Of course, there are some issues to consider in order to do a
better localization job.

The central part of localizing an application is getting all of the
user-interface running in different languages.  locale.library provides
text catalog management routines to solve this problem.  To make use of
these catalogs, an application must group all of its strings in one
location within the source code, and only refer to them using symbolic
names from within the rest of the program.  See the sample program at
the end of this document for an example of how this can be done easily.

Translated strings vary in length from their originals.  This can cause
headaches when trying to perform screen layout, or otherwise formatting
tables, etc...  On the average, it is safe to assume any given English
string will double in length when translated to a variety of languages.
This fact must always be considered when planning the visuals of a
program.

Remember to take into account the time it takes to translate the
strings of your application when you plan development schedules.  Be
flexible, and prepare to maintain a dialog with the translators.  It
may be necessary to extend the size of some of your gadgets to
accommodate special cases.  Take this into account from the start, and
design your application so these kinds of modifications remain easy.

Localization also involves number output.  From country to country,
numbers are represented in different ways.  As such, it is not correct
to assume anything about the length and format of numeric values.

Date format is another localized item.  Whenever displaying a date or
requesting a date from the user, the FormatDate() and ParseDate()
routines from locale.library should be used.  These routines handle all
the details of the variations in date format.  Again, it is not safe to
assume the length or format of a date string.

A localized application will potentially be viewed by audiences with
radically different cultural backgrounds.  This is an important point
to note when designing icons for an application.  Icons designed in the
US for example, may seem perfectly obvious to an American, but may be
totally meaningless to a Swedish user.  Be flexible with your icon
designs.  Make sure to show them to users of your target countries
before releasing your applications.

Something else which is important is naming.  When naming programs or
features of programs, you should verify that the chosen name is
suitable for all of your target markets.  Some words may have totally
different meanings, or even be insulting, when viewed in different
languages.


Screen Layout

Screen layout is a problem when localizing applications.  Strings vary
in length from language to language and it becomes difficult to create
effective displays that will work well when translated. When creating a
user-interface display on the Amiga, there are two main areas of
concern: menus and gadgets.

Menus are easily localized thanks to gadtools.library.  Its dynamic
menu layout code makes adapting to different strings virtually
automatic.

Gadgets are not so easily handled.  As the text they contain can
change, so can their size.  And when their size change, it can affect
their positioning, which can then start to affect the positioning of
surrounding gadgets, which ultimately affect the dimensions of the
window.

There are two main ways to handle localization of gadgets:

   o  make all gadgets large enough to hold any string
   o  dynamic screen layout

The first approach is simplest and involves making all gadgets large
enough to accommodate the largest translated string for that gadget.
For example, a gadget containing the string "Use" in an English
program should not be made just large enough to hold three characters,
but instead should be large enough to contain the longest translation
of the word "Use."  The 2.1 system Preferences editors make use of
this approach to localize their gadgetry.

The second approach to handle gadgets is by doing font sensitive
layout.  This involves determining exactly the size of all strings used
to create a display, then to scale and position every component so it
can deal with the largest strings out there.  Font sensitive layout is
normally thought of as a way of accomodating different fonts, but it
also serves to accommodate different strings.  The 2.1 system
Calculator is an example of a font sensitive application.


Adapting GadTools

GadTools' NewMenu and NewGadget structures both have Label fields
requiring a pointer to a string.  The string is used to build the
user-interface component.  Many programs currently declare a static
array of these structures to pass them to either CreateMenus() or
CreateGadget().  Static initialization will not work however within a
localized application due to the fact the label strings are not known
at compile-time, and are instead obtained at run-time from disk-based
catalogs.


Localizing GadTools Gadgets

Here is a routine that lets you easily localize GadTools gadgets.
Instead of calling the GadTools CreateGadget() routine, simply call
CreateLocGadget().

extern struct Catalog *catalog;

struct Gadget *CreateLocGadget(ULONG kind, struct Gadget *previous,
                              struct NewGadget *newGad, ULONG tag, ...)
{
struct NewGadget ng;

    ng = *newGad;
    ng.ng_Label = GetCatalogStr(catalog,
                                (LONG)ng.ng_Label,
                                GetAppStr((LONG)ng.ng_Label));

    return (CreateGadgetA(kind,previous,&ng,&tag));
}

This function assumes you have an opened message catalog called
"catalog," and that your application has a function called GetAppStr()
that returns the built-in string of the application given a string ID
number.

Now the trick with this function is that when you declare your
NewGadget structure, you initialize the ng_Label field to contain the
string ID of the desired string, instead of a pointer to the string
itself.  CreateLocGadget() will then replace the string ID by an actual
string pointer and will call the real GadTools routine to create the
gadget.


Localizing GadTools Menus

Localizing menus is a bit trickier than gadgets because the
CreateMenus() call accepts an array of NewMenu structures as a
parameter instead of a single structure at a time like CreateGadget().
Still, the solution remains relatively simple:

struct Menu *CreateLocMenus(struct NewMenu *newMenus, ULONG tag, ...)
{
UWORD           i;
struct NewMenu *nm;
struct Menu    *menus;

 i = 0;
 while (nm[i++].nm_Type != NM_END) {}

 if (!(nm = AllocVec(sizeof(struct NewMenu)*i,MEMF_CLEAR|MEMF_PUBLIC)))
     return(NULL);

 while (i--)
 {
     nm[i] = newMenus[i];

     if (nm[i].nm_Label != NM_BARLABEL)
     {
         nm[i].nm_CommKey = GetCatalogStr(catalog,
                                     (LONG)nm[i].nm_Label,
                                     GetAppStr((LONG)nm[i].nm_Label));

    nm[i].nm_Label = nm[i].nm_CommKey+2;

         if (nm[i].nm_CommKey[0] == ' ')
             nm[i].nm_CommKey = NULL;
     }
 }



 if (menus = CreateMenusA(nm,&tag))
 {
     if (!(LayoutMenus(menus,visualInfo,GTMN_NewLookMenus,TRUE,
                                        TAG_DONE)))
     {
         FreeMenus(menus);
                menus = NULL;
            }
 }

 FreeVec(nm);

 return(menus);
}

Like the gadget example above, this function expects to have both an
open catalog and a GetAppStr() function available.  In addition, this
function expects to find the menu strings in a specific format.  That
is, all strings must be preceded by their keyboard shortcuts and a NULL
byte.  For example:

        "X\0Cut"
        "C\0Copy"
        "V\0Paste"

This is compact, and lets the keyboard shortcuts be easily localized
and associated with the menu item.  If a menu item has no keyboard
shortcut, it needs to be specified using a leading space such as:

        " \0Erase"

As with gadgets, the trick with this function is to initialize all the
nm_Label fields to be string IDs instead of direct string pointers.
CreateLocMenus() then allocates a new array, replacing the string IDs
with string pointers, and passes the result to GadTools for the actual
menu strip creation and layout.


Character Sets

A character set defines the meaning and graphical representation
associated with a sequence of numbers.  From the beginning, the Amiga
has used the ECMA-Latin 1 character set which is a superset of regular
ASCII designed by the European Computer Manufacturer's Association.
This character set is sufficient to represent alphabets used by
languages spoken in Western Europe.

Commodore has never endorsed character sets other than ECMA-Latin 1,
which means that alphabets used in Eastern Europe, in the Middle-East,
or in Asia, cannot be represented in a standard way on an Amiga.
Designing fonts with the needed characters in them is not sufficient,
because without a formal standard, there is no way to guarantee that a
given character will be used in every variation of a font for a given
alphabet.  To clarify, without a standard, it is not possible to assume
that the Greek Omega letter will be represented by the same character
value from one Greek font to the next.

The locale.library assumes nothing about the character set being used.
The character set is dependant on the current language driver and
catalog.  At this time, the only character set formally recognized is
ECMA-Latin 1.  New character sets can be specified easily.

It is possible that new character sets may require more than one byte
of storage per character.  This would be the case for the Unicode
character set.  Applications written now only need to deal with the
current 1-byte character set.  In the future, applications may want to
support new extended character sets as they are standardized upon by
Commodore.


CatComp: the Catalog Compiler

CatComp is a program to handle the creation and maintenance of the
message catalogs used by locale.library. Message catalogs are IFF files
read by locale.library that contain all the text strings used by an
application. By providing several of these catalog files, an
application can use locale.library and transparently adapt itself to
the user's preferred language.

For more information on CatComp, consult catcomp.doc that comes with the
CatComp program.

