# Chapter 19

| # Internationalization Guidelines

> This chapter covers the following topics:

- Internationalization--page 445
- Software Guidelines--page 446

## Internationalization

> Sun Management Center consoles and associated GUI clients operate in a global environment. To do this, a mechanism is required to isolate the language dependent code/information from the language independent code and provide a straightforward method for graphical developers to reference the language dependent information. This chapter details this mechanism, and describes specific guidelines for creating internationalized consoles for Sun Management Center.

> This chapter also describes the effects of running the Sun Management Center console in non-English environments, where the console needs to handle non-ASCII user input and outlines the methodology for dealing with this sort of information.

> This chapter provides implementation details and guidelines for creating internationalized consoles and managing localization information (agents and consoles) for Sun Management Center 2.x.

### Terminology

> Internationalization is the process of designing an application so that it can be adapted to various languages and regions without engineering changes. Sometimes the term internationalization is abbreviated as i18n, because there are 18 letters between the first i and the last n.

> Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text. The term localization is often abbreviated as l10n, because there are 10 letters between the l and the n.

### Constraints

> To maximize maintainability, the Sun Management Center internationalization system must be compatible with the standard JDK 1.2 internationalization mechanism and algorithms.

### Assumptions and Dependencies

> The descriptions in this chapter assume that the developer is familiar with the JDK 1.2 support for i18n. Concepts such as locale, ResourceBundle, and properties file must be understood completely.

## Software Guidelines

### Properties Files

> The preferred method for managing internationalization information is to store all localized console information in Java properties files. According to the Java specification, properties files contain key=value pairs, with each entry separated by newlines. The key corresponds to the programmatic identifier for the internationalized information, which is either hardcoded in the Java program or is contained in the associated console configuration file. With the exception of spaces, the key identifier is free-form, but the proposed key format must be a dot separated list of identifiers that provide some form of hierarchical organization within a functional group. For example, the label for the exit button in the file menu might appear in the properties file as:

>     menu.file.exit.label=Au Revoir

> > Note - Following the equal sign ( =), leading and trailing spaces are included in the localized value.

> There are two reasons for selecting property files as the preferred mechanism for managing internationalization information. First, display text information can be modified without recompiling Java code, which simplifies the maintenance of the internationalization information. Second, this form of property/resource specification is consistent with the configuration file system used to build the Sun Management Center consoles, thus retaining a familiarity within the product development.

### ResourceBundle Class Instances

> The use of properties files is the preferred method for managing internationalization information, but the alternate use of ResourceBundle classes is also allowed. In this case, the ResourceBundle class is extended (or, more preferably, the ListResourceBundle class is extended), with the handleGetObject() method being defined to provide the appropriate internationalization information for the provided key. The suggested key format is as described previously for the property files.

### Obtaining Resource Bundles/Properties Files

> In order to properly obtain the property files and/or resource bundle class instances from the Sun Management Center server or the HTTP server, the appropriate ClassLoader instance for the requesting Java object must be used. For example, a console bean object that was installed in the Sun Management Center server installation area would be loaded through the RMI class loader. As a result, internationalization information for that bean, which is also be installed in the Sun Management Center server area, must be loaded with this particular class loader instance.

> Investigation of the implementation of the ResourceBundle class in JDK 1.2 reveals that the static getBundle() methods will handle this situation properly. An internal method (getLoader()) determines the appropriate ClassLoader instance for the current object context (using a native method getClassContext()). As a result, if the bean object mentioned above requests an information bundle through the getBundle() method, this process correctly uses the RMI class loader that provided the bean class in the first place. It also properly hashes the cached information, preventing possible conflicts arising from multiple source definitions of the same resource bundles. Thus, we are able to utilize the standard ResourceBundle methods, without modification and maintain standard performance/caching capabilities.

> > Note - This level of detail for the process description has been provided in the interest of future debugging in case this process fails in the future. If the process does fail, an appropriate alternative must be provided to properly support the directed information retrieval.

### Independent Client/Bean Usage

> An independent Java application (one running outside of the Sun Management Center configuration file framework) will have several methods for directly obtaining localized information.

#### UcInternationalizer Class

> This is the class that is used by the `i18n` and `i18n-specific` type converters to parse the functional_group:key specification, load the resource bundles, and translate the key. To use it in your Java application, make the following declaration:

>     import com.sun.symon.base.utility.UcInternationalizer;

> This declaration presents a static method translateKey() that takes in the functional_group:key value and returns the corresponding localized value according to the rules for the i18n converter specified previously. Also, an overloaded version of this method takes a fallback resource in the event that the specified internationalization key is not found. So, for example, if you are looking for the localized form of the mybean label from the ConsoleGeneric functional group, the code might be:

>     String lbl = UcInternationalizer.translateKey(
>     "base.console.ConsoleGeneric:mybean.label");

>     String lbl = UcInternationalizer.translateKey(
>     "base.console.ConsoleGeneric:mybean.label",
>     "Help Me");

> or

> As previously described, the `com.sun.symon` part of the location can be dropped for convenience.

> An additional feature of this translateKey() method can take a single argument that can be inserted into the final localized string value. This is a very simple interface to the Java MessageFormat interface. The following example illustrates the use of this simple argument facility:

>     String arg = "brown";
>     String lbl = UcInternationalizer.translateKey(
>     "example:dog.story(" + arg + ")");

> In this case, example:dog.story is the key used to find the resource bundle and localized string information and brown is the argument value. Now, if the example `properties` file contains the line:

>     dog.story = The quick {0} fox ...

> the result is "The quick brown fox ...". The value provided in the argument replaces the {0} argument indicator in the localized string value.

> Two important things to note about this argument form of the internationalization key:

- The argument is not parsed or converted to a localized value. The argument is intended for instance values that have been user specified and are not stored in properties files. If an argument of the form functional_group:key is given, that is how it can be substituted in the main localized string.
- If the argument substitution value {0} does not appear in the localized string in the properties file, no error will occur, but the argument value will not appear. Being able to hide the argument for certain locales may or may not be a feature.

#### Direct ResourceBundle Management

> Developers who are creating Sun Management Center client applications or beans in Java also have the option to manage the i18n of their application as defined by Java. At any point where internationalization information is needed, the programmer can request the property bundle for the required functional_group and lookup the localized information within the bundle using the information key. As for a previous example, if you require the internationalized resource directly for the key `menu.filebutton` for the functional group `console` you can use the following code:

>     /* Get the internationalized button label */
>     ResourceBundle bundle = ResourceBundle.getBundle("console");
>     String label = bundle.getString("menu.filebutton");

> The getString() method is used regardless of whether the resource bundle information was stored in a properties file or class definition (see "Properties Files"). You will also need to handle the MissingResourceException, that is generated whenever the requested resource bundle or key entry is missing. Note that the getBundle() call must receive the full path to properties/class files associated with the functional group (that is, the `com.sun.symon` prefix must be specified, if applicable).

> This method of obtaining internationalized information can also be used directly within Sun Management Center bean objects, using the code provided above. Doing this is not suggested, as the Sun Management Center infrastructure provides the AWX type converters and the UcInternationalizer class to centralize internationalization information retrieval. One advantage of this centralization is that the UcInternationalizer code can be modified to warn about undefined key values or mark values that have been properly internationalized, allowing potential internationalization problems to be identified prior to the final localization process/testing.

#### Formatted Messages

> There can be circumstances where a variable piece of information must be embedded into an internationalized string. Examples of these embedded bits are module instance names or dynamic object counts. This embedding is available as a standard Java feature through the java.text.MessageFormat class. This class is not fully documented here. See the appropriate Java documentation for more details. However, there are two specific examples that cover almost all of the cases where embedding is required. These are:

- where a string value needs to be inserted into a localized string, and
- the embedding of an integer value into the localized string

> The first example is where a string value (that is, a module instance name) needs to be inserted into a localized string. This corresponds to the simple argument mechanism available through the UcInternationalizer class described in "UcInternationalizer Class". The code that accomplishes this is as follows:

>     String name = "Your Name Here";
>     String baseStr =
>     UcInternationalizer.translateKey("example:advert");
>     String lbl = MessageFormat.format(baseStr, new Object[] { name } );

> In this case, the localized value for the advert key in the example functional group marks the point where the text is to be embedded by the marker "{0}". For example, this value might be defined as:

>     advert=Space For Rent [{0}]

> Thus, the final lbl value is "Space For Rent [Your Name Here]".

> Situations where multiple strings need to be embedded can be accommodated by passing multiple entries in the object array and marking their locations by {0}, {1}, {2}.... The values in the braces indicate the argument number, so that the embedded string values can be rearranged as required. An embedded string can be dropped without any errors by leaving the marker out of the localized definition.

> The second example is the embedding of an integer value into the localized string. This can be accomplished by turning the integer value into a string and using the embedding described above. This method would not properly accommodate locales where integer values are not represented by left-to-right roman digits. There are Java classes that can be used to manually convert the integer to a string value according to the locale, but the MessageFormat class handles this automatically. The code that accomplishes this is:

>     int val = 20;
>     String baseStr =
>     UcInternationalizer.translateKey("example:found");
>     String lbl = MessageFormat.format(baseStr,
>     new Object[] { new Integer(val) } );
>     and the localized found value in the example properties file looks
>     like
>     found=I found {0,number,integer} grapplegrommets.

> The only real differences between this and the string example above are that the object passed to the format() call is an integer instance and that the marker in the localized value contains additional formatting information that indicates the argument is an integer. Again, multiple integers can be used by indexing the arguments {0...}, {1...}, ... and passing multiple values in the Object array. For information on other available formats, see the appropriate Java documentation.

#### Handling Non-ASCII Input

> The majority of this document focuses on the presentation of "static" information in the Sun Management Center console/client applications (through the use of fixed keys and localized lookup tables), but a global application also needs to properly handle user input in any locale. For a purely Java application this is not a problem, as the string class is 16-bit based and all AWT/swing input/output fields use Unicode to fully support all languages/character sets. In many cases, developers within the Sun Management Center framework are insulated by the Client API and do not need to deal explicitly with non-ASCII user input.

> The Sun Management Center agents are not written in Java, nor do they properly handle Unicode or 16-bit text. In fact, the Tcl parsing mechanism (as of Tcl 7.5 that is used by Sun Management Center 2.x software) has difficulty handling 8-bit non-ASCII character values (>0x7F). The standard Java Unicode string conversion function (which is used by the server process to convert from string to SNMP Octet strings) uses UTF-8 encoding that does produce non-ASCII 8-bit characters. As a result, a non-ASCII Unicode string sent in raw form from a console/client application to the agent can potentially cause a problem, as the agent Tcl engine tries to parse the UTF encoded data.

> Two types of data have specific handling requirements, as described in the following sections.

#### Data Only Stored in Agents

> This type of data is stored in the agent for later use by a Sun Management Center console/client application, and is never directly used by the agent itself. Examples of this type of data are domain names, object names/descriptions, graph labels, and so forth. Typically, this data is managed by methods within the Client API (insulating the users of the data from the specifics of the agent). As a result, developers of functionality within the Sun Management Center Client API as well as developers of client applications that directly communicate with the agent (bypassing the Client API) are responsible for massaging the non-ASCII data into a form that can be safely handled by the agents.

> The UcListUtil class contains two static methods that are used to encode/decode non-ASCII string data into an ASCII form that can safely pass through the agent Tcl parser. UnicodeToAscii() takes a Java string corresponding to user input and returns an ASCII encoded version of the information suitable for storage in the agent. AsciiToUnicode() reverses this process to regenerate the non-ASCII string information for display. The encoding scheme is designed with an escape sequence that will not interfere with Tcl escapes, does not appear in standard data (so that the decoder is safe to use generically) and is compact in order to reduce data overhead.

#### Data Stored in and Manipulated By Agents

> This represents information that is typically used to configure the agent. For example, alarm limits and actions, filenames, and so forth, all fall into this category. In this case, the user input should be specifically restricted to prevent the entering of any non-ASCII information, as there is no encoding mechanism relevant to the agent that can be used.

> No central mechanism (as the Client API was before) exists to handle this case, since every situation is different. As a result, the client or the Sun Management Center bean must handle this situation properly and provide appropriate user feedback if the user enters non-ASCII text. Ideally, the bean will refuse to accept the data and request that the user enter ASCII-only text where required.

> In the Sun Management Center console, the vast majority of this type of information is handled through the attribute editor bean, which has a protocol for indicating that entries are directly used by the agent and cannot accept non-ASCII text.

### Agent Internationalization

> This section describes the various pieces of information that are defined by the agents themselves for console display and how the internationalization information is to be specified (either in the infrastructure or in the module definitions).

> > Note - This information refers to static display strings that can be internationalized. Dynamic structures such as data input or status messages are not internationalized in Sun Management Center 2.x software.

#### Objects/Classes/Properties

> This internationalization information is typically used in the hierarchy/topology/table displays associated with the browser tab in the details window. Each instance of MANAGED-OBJECT, MANAGED-PROPERTY-CLASS and MANAGED-PROPERTY needs to define an internationalization key to allow the console to lookup a localized name for the object, class and property, respectively. This key information is also defined in other agent objects that may appear in the console (such as the mibman.modules hierarchy).

> The specification of the internationalized name is made alongside the value:mediumDesc specification (that is, in the module models file (See "Module Building"). However, in this case the consoleHint:mediumDesc value is defined. The value corresponds to the functional_group:key specification used in the Console configuration files and by the UcInternationalizer class.

> For example, an entry for the user-managed object in the `solaris-standard-models-d.x` file:

>     user = { [ use MANAGED-OBJECT ]
>     mediumDesc = User Statistics
>     consoleHint:mediumDesc = base.modules.solaris:user

> This module has a private properties file contained in the base/modules Sun Management Center directory.

> The next section describes the definitions required for the MANAGED-MODULE object.

#### Modules

> Module instance internationalization involves two areas: internationalizing the name of the module instance, and internationalizing the parameters used in loading/editing the module, which are described in the following sections.

> Module Instance Naming

> There are two basic types of modules:

- those that are single instance (that is, Solaris operating environment)
- those where multiple independent instances can be loaded by the user (for example, `fscan`).

> First consider the single instance modules. In this case, there is no specific instance identifier, so the "name" of the module can be specified in the MANAGED-MODULE object of the module hierarchy using the consoleHint:mediumDesc value described above. To reduce confusion, this description specification is identical to the i18nModuleName parameter described below. In fact, if the consoleHint:mediumDesc value is not found in the module root object, the i18nModuleName parameter can be used in its place.

> The second type of module (multiple instances) is more complicated as the name of the module has to be shown in the console in combination with the user specified instance description. In this case, the i18nModuleName parameter and the consoleHint:mediumDesc must be different (and fully specified). The i18nModuleName is the general (the non-instanced) name of the module. For example, for the `fscan` module this is File Scanning, which is specified as described in this section.

> However, when the module is viewed in the browser hierarchy, the user specified instance of the module must also appear. In this case, the consoleHint:mediumDesc value is used to obtain the base internationalization key, and then the instance details are appended to this key as an argument. For example, the root object of the `fscan` module has the following specification:

>     consoleHint:mediumDesc = base.modules.fscan:moduleDetail

> When the module description is obtained (for the System Log instance), the following key is sent to the console:

>     base.modules.fscan:moduleDetail(System Log)

> The definition of the moduleDetail key in the `fscan.properties` file is:

>     moduleDetail=File Scanning[{0}]

> From "Formatted Messages", the {0} identifier indicates the location where the argument (in this case, the instance name) appears, so that the module name that appears in the console is `"File Scanning [System Log]"` for example. If the consoleHint:mediumDesc is not specified, the key falls back to the one specified in the i18nModuleName, which does not have this marker defined, so the module instance information is not displayed in the console.

> Module Parameters

> Several parameter specifications (contained in the `<module><-subspec>-m.x` file) must be internationalized or involve internationalization, as described below. Note that in almost every case, the internationalization value is a functional_group:key specification as used in console configuration files or by the UcInternationalizer class described previously.

- The first (and most obvious) piece of internationalization is the descriptions of parameters and parameter groups. The parameter group description is what appears in the "tab" label, and is specified for the param group as:

      ?param:?description = base.modules.default:moduleParam

  The parameter description is what appears to the left of the data entry field, and is specified as follows (for the module parameter):

      ?param:module?description = base.modules.default:module

- The second parameter-related internationalization involves parameter values that must be internationalized. Examples of these are the i18nModuleName, the i18nModuleType and the i18nModuleDesc parameters, that respectively provide the key specification for the name of the module ("File Scanning"), the type of the module ("Local Applications") and a longer description of the module. In each case, the value of the parameter is the familiar functional_group:key identifier and a line of the following form must appear for each parameter to indicate to the module loader/editor that the value is to be parsed to obtain a localized value:

      ?param:i18nModuleName?i18n = yes

  These parameters must be read-only, otherwise the user can edit the localized value and send that back as the key (with unusual effects). There is also a parameter format specification of i18ncomment that does not actually indicate that the key is to be translated, but instead indicates that the value is a multiline label to appear as unbordered text (for the description).
- The third parameter internationalization point is the handling of list parameters, where a choice of several values are sent to the agent but an internationalized list is displayed to the user. In this case, the list format appears as follows:

      ?param:parameter?format = 		\
      list:val1,fn_group:key1|val2,fn_group:key2|...

  Thus, the list values are separated by the pipe character (|). Each entry contains the selection value to send to the agent, followed by a comma and the functional_group:key specification for the internationalized text to appear in the selection box pulldown.
  The final internationalization-related parameter settings involve those parameters that are input by the users that are not processed by the agent, but are only used for console display. Currently, this is only used for the instance name provided by the user for each loaded module instance.
  This is different from the instance parameter, which is used as a context key in the agent. In this case, the user can enter localized text in the native language the console is currently running in. Behind the scenes, the module loader/editor can use the ASCII encoding/decoding mechanism, described in "Data Only Stored in Agents", to translate the user entered instance name into a form that is safe to store in the agent. To indicate that this (or any other) module parameter is permitted to take non-ASCII text from the user, the following format specification is required:

      ?param:parameter?format = unicode

  This format should not be used for any parameter that can be utilized by the agent, as there are no facilities in the agent to decompose the encoding scheme.

#### Attribute Editing

> Developers who are adding or modifying the shadow maps that are edited through the attribute editor, or who are customizing the shadow entries that are edited by a specific object must internationalize properly the labels and possibly the values associated with these attributes. The following sections describe the three components of internationalizing attributes.

> Attribute Groups

> The set of shadow groups, as defined by the value:shadowGroups() settings in the edited object, is a single level Tcl list where each pair of values defines the access key and the internationalized information key. For example, a definition can appear as:

    shadowGroups()=Info
    base.console.ConsoleGeneric:editGroup.info \
      Module base.console.ConsoleGeneric:editGroup.module

> Each pair of values has first a simple (English) key that is used to organize the attributes in that group, and second the familiar functional_group:key specification, that is used to determine the localized text to display in the group selection tab of the attribute editor.

> Scalar Attributes

> For each scalar attribute, there is a single internationalized description that is given as the third entry in the shadowSpec() definition for that attribute. For example, the object name definition can appear as:

>     shadowSpec(name) = \
>     "name {Object Name} base.console.ConsoleGeneric:editAtt.name
>      string {} ro scalar";

> The provided key is used to find a localized label for that attribute to display alongside the value in the attribute editor. In practice (observe base-shadowmap-d.x) this value is never specified. Instead, a blank entry {} appears in its place. If a blank internationalization name is given for a scalar variable, the key is programmatically determined by appending the shadowmap key (name in this case) to the string `base.console.ConsoleGeneric:editAtt.`, arriving at the result shown above for this example.

> There are other internationalization issues related to the value of the attribute and how the user edits it (as specified by the format indicator in the shadow specification). For example, the value can be an internationalization key that is translated prior to displaying (read-only) in the editor, or the value can be a selection list where the value provided to the agent corresponds to an internationalized value to display in the pulldown list. The following list describes the various formats that relate to internationalized values:

> i18n | The value is an internationalized key that is translated prior to display in the editor. These attributes are always read-only.
> i18ncomment | Identical to the i18n format, except the presentation is slightly different to accommodate multline values.
> list... | Specifies a selection list with mapping between the agent values and the internationalized display information. See the module parameters description for more information on the exact specification of this format.
> `unicode` | `E`nables the user to enter non-ASCII text as a value for this attribute. Generally not used as the agent cannot utilize the entered value (but there are always exceptions).

> Vector Attributes

> With the exception of the attribute label, these attributes are identical to the scalar attributes described above as the data type and format information is identical for all members of the vector. However, each attribute in the vector can have a different description (and hence different internationalization information). This description is obtained using the method specified in the shadowInfo() specification for the vector attribute. The name returned from this method is appended to "base.console.ConsoleGeneric.editAtt." to construct the internationalization key for that entry of the vector. For example, a numeric alarm limit might have two attributes in the vector, the bigger and smaller limits, for which the shadowInfo() method returns too-big and too-small. In this case, the `ConsoleGeneric.properties` file will have the lines:

>     editAtt.too-big=Error if bigger than
>     editAtt.too-small=Error if smaller than

> > Note - Rule parameters, which are technically vector attributes, are handled by their own naming mechanism as described in the next section.

#### Dynamic Tables (RFC1903)

> For certain modules, there are tables in which rows can be dynamically added/deleted/enabled/disabled by the user (using the RFC1903 protocol). For example, the `fscan` module enables the user to add new patterns to be scanned for in the file. In this case, the row add/edit window needs two pieces of internationalization information: the column entry label and (optionally) the format of the column data. The entry label (that appears alongside the editor field) is obtained from the setting of consoleHint:mediumDesc as described in "Objects/Classes/Properties". The value format is obtained from the value:dataFormat setting, which can be any of the standard attribute editor format types. With regards to internationalization, the two formats of interest are the list setting and the unicode setting, as described in "Scalar Attributes" above. In this case the unicode setting is a common thing for the row description, which is not used by the agent and is returned to the console to label the rows in the table as the user describes them.

#### Rules

> For each parameter defined in a rule (by the rule:<rule>-editparam setting), the attribute editor will present an entry field that needs an internationalized label for that parameter. The internationalized label key is constructed from several sources. First, the base path of the functional_group is specified in the rule:<rule>-keypath setting and the name of the module is appended to give the full group specification. The key entry in the properties file is determined by combining the string "editAtt." with the name of the rule and the name of the rule parameter. For example, consider an "example" module that has the following lines in the rules definition block:

>     rule:da_rule-editparam = "one two"
>     rule:da_rule-keypath = base.modules

> The internationalized label entries for the "one" and "two" rules are contained in the functional group [com.sun.symon].base.modules.example and the .properties file contains the following entries:

>     editAtt.da_rule.one=Rule One
>     editAtt.da_rule.two=Rule Two

### Installation/Setup Script Internationalization

> Solaris software-based add on products that require their own scripts to handle installation and/or setup will need to internationalize these scripts if they are to be used globally. The basic method here is that which is used in C programming. A portable object file is created (`filename.po`) that contains the text displayed by the scripts. The `msgfmt` command is run on this file to create a message object file (`filename.mo`). As the script runs, it obtains localized messages from the message object file. This guideline does not describe all of the details of internationalizing C programs. Refer to the man pages for `msgfmt, gettext` and `textdomain` for details.

> The way a script obtains localized messages from its message object file is through the TEXTDOMAIN variable. This variable is set to the name of the script's message object file. Sun Management Center software provides a function called setup_textdomain, that can be used by add-on products to establish a domain for their own localized messages. You can call this function in the beginning of your script and pass it the name of your message object file. While your script runs, it obtains messages from its own message object file.

> If your script must call a function within the core Sun Management Center scripts (other than setup_textdomain) , set TEXTDOMAIN to the core Sun Management Center setting for the duration of this call. If your script is invoked from the core Sun Management Center script, save the current setting of TEXTDOMAIN before changing it to your domain value. When the function ends and control returns to your script, reset TEXTDOMAIN to your own setting. For example:

>     # SyMON script running... calls your script
>     domain_save = TEXTDOMAIN
>     setup_textdomain SUNW_MY_DOMAIN
>     # your script running...
>     setup_textdomain domain_save
>     # call core SyMON script function...
>     # core SyMON script function ends, returns to your script
>     setup_textdomain SUNW_MY_DOMAIN
>     # return to your script running...

> If your script is invoked independently of the core Sun Management Center scripts, and you must call a core Sun Management Center script function, the value of the core Sun Management Center setting for TEXTDOMAIN is SUNW_ES_SCRIPTS. Simply call `setup_textdomain` with this value before making the core function call.

> Of course, if you have no need to call a core Sun Management Center function, then you only need to set TEXTDOMAIN at the beginning of your script. If you are invoked from the core script, reset it to the core setting upon completion.

> > Note - If your add-on product has packages that can be installed using the core Sun Management Center install script, your message object file must be in `$PKG_DIR/locale/$LANG/LC_MESSAGES`.

Copyright © 2000 Sun Microsystems, Inc. All Rights Reserved.