home *** CD-ROM | disk | FTP | other *** search
/ DOS/V Power Report 1998 February / VPR9802A.ISO / APP_DEMO / VC / MAIN.BIN / Locale.java < prev    next >
Text File  |  1997-10-27  |  23KB  |  632 lines

  1. /*
  2.  * @(#)Locale.java    1.21 97/01/29
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
  8.  *
  9.  *   The original version of this source code and documentation is copyrighted
  10.  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  11.  * materials are provided under terms of a License Agreement between Taligent
  12.  * and Sun. This technology is protected by multiple US and International
  13.  * patents. This notice and attribution to Taligent may not be removed.
  14.  *   Taligent is a registered trademark of Taligent, Inc.
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software
  17.  * and its documentation for NON-COMMERCIAL purposes and without
  18.  * fee is hereby granted provided that this copyright notice
  19.  * appears in all copies. Please refer to the file "copyright.html"
  20.  * for further important copyright and licensing information.
  21.  *
  22.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  23.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  24.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  25.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  26.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  27.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  28.  *
  29.  */
  30.  
  31. package java.util;
  32. import java.io.Serializable;
  33.  
  34. /**
  35.  *
  36.  * A <code>Locale</code> object represents a specific geographical, political,
  37.  * or cultural region. An operation that requires a <code>Locale</code> to perform
  38.  * its task is called <em>locale-sensitive</em> and uses the <code>Locale</code>
  39.  * to tailor information for the user. For example, displaying a number
  40.  * is a locale-sensitive operation--the number should be formatted
  41.  * according to the customs/conventions of the user's native country,
  42.  * region, or culture.
  43.  * 
  44.  * <P>
  45.  * You create a <code>Locale</code> object using one of the two constructors in
  46.  * this class:
  47.  * <blockquote>
  48.  * <pre>
  49.  * Locale(String language, String country)
  50.  * Locale(String language, String country, String variant)
  51.  * </pre>
  52.  * </blockquote>
  53.  * The first argument to both constructors is a valid <STRONG>ISO
  54.  * Language Code.</STRONG> These codes are the lower-case two-letter
  55.  * codes as defined by ISO-639.
  56.  * You can find a full list of these codes at a number of sites, such as:
  57.  * <BR><a href ="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
  58.  * <code>http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt</code></a>
  59.  *
  60.  * <P>
  61.  * The second argument to both constructors is a valid <STRONG>ISO Country
  62.  * Code.</STRONG> These codes are the upper-case two-letter codes
  63.  * as defined by ISO-3166.
  64.  * You can find a full list of these codes at a number of sites, such as:
  65.  * <BR><a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
  66.  * <code>http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html</code></a>
  67.  *
  68.  * <P>
  69.  * The second constructor requires a third argument--the <STRONG>Variant.</STRONG>
  70.  * The Variant codes are vendor and browser-specific.
  71.  * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
  72.  * Where there are two variants, separate them with an underscore, and
  73.  * put the most important one first. For
  74.  * example, a Traditional Spanish collation might be referenced, with
  75.  * "ES", "ES", "Traditional_WIN".
  76.  *
  77.  * <P>
  78.  * Because a <code>Locale</code> object is just an identifier for a region,
  79.  * no validity check is performed when you construct a <code>Locale</code>.
  80.  * If you want to see whether particular resources are available for the
  81.  * <code>Locale</code> you construct, you must query those resources. For
  82.  * example, ask the <code>NumberFormat</code> for the locales it supports
  83.  * using its <code>getAvailableLocales</code> method.
  84.  * <BR><STRONG>Note:</STRONG> When you ask for a resource for a particular
  85.  * locale, you get back the best available match, not necessarily
  86.  * precisely what you asked for. For more information, look at 
  87.  * <a href="java.util.ResourceBundle.html"><code>ResourceBundle</code></a>.
  88.  *
  89.  * <P>
  90.  * The <code>Locale</code> class provides a number of convenient constants
  91.  * that you can use to create <code>Locale</code> objects for commonly used
  92.  * locales. For example, the following creates a <code>Locale</code> object
  93.  * for the United States:
  94.  * <blockquote>
  95.  * <pre>
  96.  * Locale.US
  97.  * </pre>
  98.  * </blockquote>
  99.  *
  100.  * <P>
  101.  * Once you've created a <code>Locale</code> you can query it for information about
  102.  * itself. Use <code>getCountry</code> to get the ISO Country Code and
  103.  * <code>getLanguage</code> to get the ISO Language Code. You can
  104.  * use <code>getDisplayCountry</code> to get the
  105.  * name of the country suitable for displaying to the user. Similarly,
  106.  * you can use <code>getDisplayLanguage</code> to get the name of
  107.  * the language suitable for displaying to the user. Interestingly,
  108.  * the <code>getDisplayXXX</code> methods are themselves locale-sensitive
  109.  * and have two versions: one that uses the default locale and one
  110.  * that uses the locale specified as an argument.
  111.  *
  112.  * <P>
  113.  * The JDK provides a number of classes that perform locale-sensitive
  114.  * operations. For example, the <code>NumberFormat</code> class formats
  115.  * numbers, currency, or percentages in a locale-sensitive manner. Classes
  116.  * such as <code>NumberFormat</code> have a number of convenience methods
  117.  * for creating a default object of that type. For example, the
  118.  * <code>NumberFormat</code> class provides these three convenience methods
  119.  * for creating a default <code>NumberFormat</code> object:
  120.  * <blockquote>
  121.  * <pre>
  122.  * NumberFormat.getInstance()
  123.  * NumberFormat.getCurrencyInstance()
  124.  * NumberFormat.getPercentInstance()
  125.  * </pre>
  126.  * </blockquote>
  127.  * These methods have two variants; one with an explicit locale
  128.  * and one without; the latter using the default locale.
  129.  * <blockquote>
  130.  * <pre>
  131.  * NumberFormat.getInstance(myLocale)
  132.  * NumberFormat.getCurrencyInstance(myLocale)
  133.  * NumberFormat.getPercentInstance(myLocale)
  134.  * </pre>
  135.  * </blockquote>
  136.  * A <code>Locale</code> is the mechanism for identifying the kind of object
  137.  * (<code>NumberFormat</code>) that you would like to get. The locale is
  138.  * <STRONG>just</STRONG> a mechanism for identifying objects,
  139.  * <STRONG>not</STRONG> a container for the objects themselves.
  140.  *
  141.  * <P>
  142.  * Each class that performs locale-sensitive operations allows you
  143.  * to get all the available objects of that type. You can sift
  144.  * through these objects by language, country, or variant,
  145.  * and use the display names to present a menu to the user.
  146.  * For example, you can create a menu of all the collation objects
  147.  * suitable for a given language. Such classes must implement these
  148.  * three class methods:
  149.  * <blockquote>
  150.  * <pre>
  151.  * public static Locale[] getAvailableLocales()
  152.  * public static String getDisplayName(Locale objectLocale,
  153.  *                                     Locale displayLocale)
  154.  * public static final String getDisplayName(Locale objectLocale)
  155.  *     // getDisplayName will throw MissingResourceException if the locale
  156.  *     // is not one of the available locales.
  157.  * </pre>
  158.  * </blockquote>
  159.  *
  160.  * @see         ResourceBundle
  161.  * @see         java.text.Format
  162.  * @see         java.text.NumberFormat
  163.  * @see         java.text.Collation
  164.  * @version     1.21 29 Jan 1997
  165.  * @author      Mark Davis
  166.  */
  167.  
  168. public final class Locale implements Cloneable, Serializable {
  169.  
  170.     /** Useful constant for language.
  171.      */
  172.     static public final Locale ENGLISH = new Locale("en","","");
  173.  
  174.     /** Useful constant for language.
  175.      */
  176.     static public final Locale FRENCH = new Locale("fr","","");
  177.  
  178.     /** Useful constant for language.
  179.      */
  180.     static public final Locale GERMAN = new Locale("de","","");
  181.  
  182.     /** Useful constant for language.
  183.      */
  184.     static public final Locale ITALIAN = new Locale("it","","");
  185.  
  186.     /** Useful constant for language.
  187.      */
  188.     static public final Locale JAPANESE = new Locale("ja","","");
  189.  
  190.     /** Useful constant for language.
  191.      */
  192.     static public final Locale KOREAN = new Locale("ko","","");
  193.  
  194.     /** Useful constant for language.
  195.      */
  196.     static public final Locale CHINESE = new Locale("zh","","");
  197.  
  198.     /** Useful constant for language.
  199.      */
  200.     static public final Locale SIMPLIFIED_CHINESE = new Locale("zh","CN","");
  201.  
  202.     /** Useful constant for language.
  203.      */
  204.     static public final Locale TRADITIONAL_CHINESE = new Locale("zh","TW","");
  205.  
  206.     /** Useful constant for country.
  207.      */
  208.     static public final Locale FRANCE = new Locale("fr","FR","");
  209.  
  210.     /** Useful constant for country.
  211.      */
  212.     static public final Locale GERMANY = new Locale("de","DE","");
  213.  
  214.     /** Useful constant for country.
  215.      */
  216.     static public final Locale ITALY = new Locale("it","IT","");
  217.  
  218.     /** Useful constant for country.
  219.      */
  220.     static public final Locale JAPAN = new Locale("ja","JP","");
  221.  
  222.     /** Useful constant for country.
  223.      */
  224.     static public final Locale KOREA = new Locale("ko","KR","");
  225.  
  226.     /** Useful constant for country.
  227.      */
  228.     static public final Locale CHINA = new Locale("zh","CN","");
  229.  
  230.     /** Useful constant for country.
  231.      */
  232.     static public final Locale PRC = new Locale("zh","CN","");
  233.  
  234.     /** Useful constant for country.
  235.      */
  236.     static public final Locale TAIWAN = new Locale("zh","TW","");
  237.  
  238.     /** Useful constant for country.
  239.      */
  240.     static public final Locale UK = new Locale("en","GB","");
  241.  
  242.     /** Useful constant for country.
  243.      */
  244.     static public final Locale US = new Locale("en","US","");
  245.  
  246.     /** Useful constant for country.
  247.      */
  248.     static public final Locale CANADA = new Locale("en","CA","");
  249.  
  250.     /** Useful constant for country.
  251.      */
  252.     static public final Locale CANADA_FRENCH = new Locale("fr","CA","");
  253.  
  254.     /**
  255.      * Construct a locale from language, country, variant.
  256.      * @param language lowercase two-letter ISO-639 code.
  257.      * @param country uppercase two-letter ISO-3166 code.
  258.      * @param variant vendor and browser specific code. See class description.
  259.      */
  260.     public Locale(String language, String country, String variant) {
  261.         this.language = toLowerCase(language);
  262.         this.country = toUpperCase(country);
  263.         this.variant = toUpperCase(variant);
  264.     }
  265.  
  266.     /**
  267.      * Construct a locale from language, country.
  268.      * @param language lowercase two-letter ISO-639 code.
  269.      * @param country uppercase two-letter ISO-3166 code.
  270.      */
  271.     public Locale(String language, String country) {
  272.         this.language = toLowerCase(language);
  273.         this.country = toUpperCase(country);
  274.         this.variant = "";
  275.     }
  276.  
  277.     /**
  278.      * Common method of getting the current default Locale.
  279.      * Used for the presentation: menus, dialogs, etc.
  280.      * Generally set once when your applet or application is initialized,
  281.      * then never reset. (If you do reset the default locale, you
  282.      * probably want to reload your GUI, so that the change is reflected
  283.      * in your interface.)
  284.      * <p>More advanced programs will allow users to use different locales
  285.      * for different fields, e.g. in a spreadsheet.
  286.      * <BR>Note that the initial setting will match the host system.
  287.      */
  288.     public static synchronized Locale getDefault() {
  289.         if (defaultLocale == null) {
  290.         String language = System.getProperty("user.language", "EN");
  291.         String region = System.getProperty("user.region", "");
  292.  
  293.         defaultLocale = new Locale(language, region);
  294.     }
  295.         return defaultLocale;
  296.     }
  297.  
  298.     /**
  299.      * Sets the default.
  300.      * Normally set once at the beginning of applet or application,
  301.      * then never reset. <code>setDefault</code> does not reset the host locale.
  302.      * @param newLocale Locale to set to.
  303.      */
  304.     public static synchronized void setDefault(Locale newLocale) {
  305.     SecurityManager security = System.getSecurityManager();
  306.     if (security != null) {
  307.         security.checkPropertyAccess("user.language");
  308.     }
  309.         defaultLocale = newLocale;
  310.     }
  311.  
  312.     /**
  313.      * Getter for programmatic name of field,
  314.      * an lowercased two-letter ISO-639 code.
  315.      * @see #getDisplayLanguage
  316.      */
  317.     public String getLanguage() {
  318.         return language;
  319.     }
  320.  
  321.     /**
  322.      * Getter for programmatic name of field,
  323.      * an uppercased two-letter ISO-3166 code.
  324.      * @see #getDisplayCountry
  325.      */
  326.     public String getCountry() {
  327.         return country;
  328.     }
  329.  
  330.     /**
  331.      * Getter for programmatic name of field.
  332.      * @see #getDisplayVariant
  333.      */
  334.     public String getVariant() {
  335.         return variant;
  336.     }
  337.  
  338.     /**
  339.      * Getter for the programmatic name of the entire locale,
  340.      * with the language, country and variant separated by underbars.
  341.      * If a field is missing, at most one underbar will occur.
  342.      * Example: "EN", "DE_DE", "EN_US_WIN", "DE_POSIX", "FR_MAC"
  343.      * @see #getDisplayName
  344.      */
  345.     public final String toString() {
  346.         StringBuffer result = new StringBuffer(language);
  347.         if (country.length() != 0) {
  348.             result.append('_');
  349.             result.append(country);
  350.             if (variant.length() != 0) {
  351.                 result.append('_');
  352.                 result.append(variant);
  353.             }
  354.         }
  355.         return result.toString();
  356.     }
  357.  
  358.     /**
  359.      * Getter for the three-letter ISO language abbreviation
  360.      * of the locale.
  361.      * @exception MissingResourceException Throws MissingResourceException if the
  362.      * three-letter language abbreviation is not available for this locale.
  363.      */
  364.     public String getISO3Language() throws MissingResourceException {
  365.         ResourceBundle resource
  366.             = ResourceBundle.getBundle
  367.             ("java.text.resources.LocaleElements", this);
  368.         return resource.getString("ShortLanguage");
  369.     }
  370.  
  371.     /**
  372.      * Getter for the three-letter ISO country abbreviation
  373.      * of the locale.
  374.      * @exception MissingResourceException Throws MissingResourceException if the
  375.      * three-letter language abbreviation is not available for this locale.
  376.      */
  377.     public String getISO3Country() throws MissingResourceException {
  378.         ResourceBundle resource =
  379.         ResourceBundle.getBundle
  380.         ("java.text.resources.LocaleElements", this);
  381.         return resource.getString("ShortCountry");
  382.     }
  383.  
  384.     /**
  385.      * Getter for the Locale ID, as used in Windows.
  386.      * @exception MissingResourceException Throws MissingResourceException if there is
  387.      * no LCID associated with the locale.
  388.      */
  389.     // FIXME this should go away completely!
  390.     private int getLCID() throws MissingResourceException {
  391.         ResourceBundle resource =
  392.         ResourceBundle.getBundle
  393.         ("java.text.resources.LocaleElements", this);
  394.         String lcid = resource.getString("LocaleID");
  395.         return Integer.parseInt(lcid,16);
  396.     }
  397.  
  398.     /**
  399.      * Getter for display of field to user.
  400.      * If the localized name is not found, returns the ISO code.
  401.      * The desired user language is from the default locale.
  402.      */
  403.     public final String getDisplayLanguage() {
  404.         return getDisplayLanguage(getDefault());
  405.     }
  406.  
  407.     /**
  408.      * Getter for display of field to user.
  409.      * If the localized name is not found, returns the ISO codes.
  410.      * Example: "English (UK)", "Deutch", "Germany"
  411.      * @param inLocale specifies the desired user language.
  412.      */
  413.     public String getDisplayLanguage(Locale inLocale) {
  414.         try {
  415.             ResourceBundle resource =
  416.         ResourceBundle.getBundle
  417.         ("java.text.resources.LocaleElements", this);
  418.             return findStringMatch((String[][])resource.getObject("Languages"),
  419.                                inLocale.language, language);
  420.         } catch (Exception e) {
  421.             return language;
  422.         }
  423.     }
  424.  
  425.     /**
  426.      * Getter for display of field to user.
  427.      * If the localized name is not found, returns the ISO code.
  428.      * The default locale is used for the presentation language.
  429.      */
  430.     public final String getDisplayCountry() {
  431.         return getDisplayCountry(getDefault());
  432.     }
  433.  
  434.     /**
  435.      * Getter for display of field to user.
  436.      * If the localized name is not found, returns the ISO code.
  437.      * @param inLocale specifies the desired user language.
  438.      */
  439.     public String getDisplayCountry(Locale inLocale) {
  440.         try {
  441.             ResourceBundle resource =
  442.         ResourceBundle.getBundle
  443.         ("java.text.resources.LocaleElements",this);
  444.             return findStringMatch((String[][])resource.getObject("Countries"),
  445.                                inLocale.language, language);
  446.         } catch (Exception e) {
  447.             return country;
  448.         }
  449.     }
  450.  
  451.     /**
  452.      * Getter for display of field to user.
  453.      * If the localized name is not found, returns the variant code.
  454.      * The default locale is used for the presentation language.
  455.      */
  456.     public final String getDisplayVariant() {
  457.         return getDisplayVariant(getDefault());
  458.     }
  459.  
  460.     /**
  461.      * Getter for display of field to user
  462.      * If the localized name is not found, returns the variant code.
  463.      * @param inLocale specifies the desired user language.
  464.      */
  465.     public String getDisplayVariant(Locale inLocale) {
  466.         try {
  467.             ResourceBundle resource =
  468.         ResourceBundle.getBundle
  469.         ("java.text.resources.LocaleElements", inLocale);
  470.             String[][] pairedStrings = (String[][])resource.getObject("Variants");
  471.             if (pairedStrings == null)
  472.                 return variant;
  473.             return findStringMatch(pairedStrings, inLocale.language, language);
  474.          } catch (Exception e) {
  475.             return variant;
  476.         }
  477.    }
  478.  
  479.     /**
  480.      * Getter for display of the entire locale to user.
  481.      * If the localized name is not found, uses the ISO codes.
  482.      * The default locale is used for the presentation language.
  483.      */
  484.     public final String getDisplayName() {
  485.         return getDisplayName(getDefault());
  486.     }
  487.  
  488.     /**
  489.      * Getter for display of the entire locale to user.
  490.      * If the localized name is not found, uses the ISO codes
  491.      * @param inLocale specifies the desired user language.
  492.      */
  493.     public String getDisplayName(Locale inLocale) {
  494.         // TODO: use pattern string from locale data
  495.         StringBuffer result = new StringBuffer (getDisplayLanguage(inLocale));
  496.         String aCountry = getDisplayCountry(inLocale);
  497.         String aVariant = getDisplayVariant(inLocale);
  498.         if (aCountry.length() != 0 || aVariant.length() != 0) {
  499.             result.append(" (");
  500.             result.append(getDisplayCountry(inLocale));
  501.             if (aCountry.length() != 0 && aVariant.length() != 0)
  502.                 result.append(",");
  503.             result.append(getDisplayVariant(inLocale));
  504.             result.append(")");
  505.         }
  506.         return result.toString();
  507.     }
  508.  
  509.     /**
  510.      * Overrides Cloneable
  511.      */
  512.     public Object clone()
  513.     {
  514.         try {
  515.             Locale that = (Locale)super.clone();
  516.             return that;
  517.         } catch (CloneNotSupportedException e) {
  518.             throw new InternalError();
  519.         }
  520.     }
  521.  
  522.     /**
  523.      * Override hashCode.
  524.      * Since Locales are often used in hashtables, caches the value
  525.      * for speed.
  526.      */
  527.       // XXX Depending on performance of synchronized, may want to
  528.       // XXX just compute in constructor.
  529.     public synchronized int hashCode() {
  530.         if (hashcode == -1) {
  531.             hashcode =
  532.         language.hashCode() ^
  533.         country.hashCode() ^
  534.         variant.hashCode();
  535.         }
  536.         return hashcode;
  537.     }
  538.  
  539.     // Overrides
  540.  
  541.     public boolean equals(Object obj) {
  542.         if (this == obj)                      // quick check
  543.             return true;
  544.         if (!(obj instanceof Locale))         // (1) same object?
  545.             return false;
  546.         Locale other = (Locale) obj;
  547.         if (hashCode() != other.hashCode()) return false;       // quick check
  548.         if (!language.equals(other.language)) return false;
  549.         if (!country.equals(other.country)) return false;
  550.         if (!variant.equals(other.variant)) return false;
  551.         return true; // we made it through the guantlet.
  552.         // (1)  We don't check super.equals since it is Object.
  553.         //      Since Locale is final, we don't have to check both directions.
  554.     }
  555.  
  556.     // ================= privates =====================================
  557.  
  558.     // XXX instance and class variables. For now keep these separate, since it is
  559.     // faster to match. Later, make into single string.
  560.  
  561.     private String language = "";
  562.     private String country = "";
  563.     private String variant = "";
  564.  
  565.     private /*FIXME(83) transient*/ int hashcode = -1;        // lazy evaluated
  566.  
  567.     private static Locale defaultLocale = null;
  568.  
  569.     /*
  570.      * Locale needs its own, locale insenitive version of toLowerCase to
  571.      * avoid circularity problems between Locale and String.
  572.      * The most straightforward algorithm is used. Look at optimizations later.
  573.      */
  574.     private String toLowerCase(String str) {
  575.         char[] buf = str.toCharArray();
  576.         for (int i = 0; i < buf.length; i++) {
  577.             buf[i] = Character.toLowerCase( buf[i] );
  578.         }
  579.         return new String( buf );
  580.     }
  581.     
  582.     /*
  583.      * Locale needs its own, locale insenitive version of toUpperCase to
  584.      * avoid circularity problems between Locale and String.
  585.      * The most straightforward algorithm is used. Look at optimizations later.
  586.      */
  587.     private String toUpperCase(String str) {
  588.         char[] buf = str.toCharArray();
  589.         for (int i = 0; i < buf.length; i++) {
  590.             buf[i] = Character.toUpperCase( buf[i] );
  591.         }
  592.         return new String( buf );
  593.     }
  594.     
  595.     private String findStringMatch(String[][] languages,
  596.                                    String desiredLanguage, String fallbackLanguage)
  597.     {
  598.         for (int i = 0; i < languages.length; ++i)
  599.             if (desiredLanguage.equals(languages[i][0]))
  600.                 return languages[i][1];
  601.         if (!fallbackLanguage.equals(desiredLanguage))
  602.             for (int i = 0; i < languages.length; ++i)
  603.                 if (fallbackLanguage.equals(languages[i][0]))
  604.                     return languages[i][1];
  605.         if (!"EN".equals(desiredLanguage) && "EN".equals(fallbackLanguage))
  606.             for (int i = 0; i < languages.length; ++i)
  607.                 if ("EN".equals(languages[i][0]))
  608.                     return languages[i][1];
  609.         return "";
  610.     }
  611.     /*
  612.      * Does an equals check, plus an empty field in this means a wildcard;
  613.      * matches any other field in other.
  614.      */
  615.     private boolean matches (Locale other) {
  616.         //System.out.println("language:" + language + "," + other.language);
  617.         if (language.length() != 0)
  618.             if (!language.equals(other.language))
  619.                 return false;
  620.         //System.out.println("country:" + country + "," + other.country);
  621.         if (country.length() != 0)
  622.             if (!country.equals(other.country))
  623.                 return false;
  624.         //System.out.println("variant:" + variant + "," + other.variant);
  625.         if (variant.length() != 0)
  626.             if (!variant.equals(other.variant))
  627.                 return false;
  628.         return true; // whew, we made it through the guantlet.
  629.     }
  630.  
  631. }
  632.