JAVA國際化基礎知識(三)

數字格式化示例

這個 JIBNumberGUI 示例使用法語作爲缺省語言環境 ― fr_FR。

JIBNumberGUI(參閱 JIBNumberGUI.java:NumberFormat 示例 )有意地以非常類似於日期格式化示例的方式運行。該應用程序允許用戶以本地格式輸入數字或百分數(如果選擇的話)。輸入時確定本地語言環境,並且該語言環境被顯示在 OK 按鈕的旁邊。當用戶按下 OK 時,則解析輸入數字並以選中的語言環境顯示該日期。該值也被單獨作爲標準數值型值進行解析和顯示。

代碼也非常相似。程序以定義 Lucida 字體、缺省語言環境以及缺省的和選中的 NumberFormat 開始。定義了支持的語言環境的數組以及本機的和本地化的語言環境顯示名稱。還定義了一個數組以顯示數字或百分比下拉框。

  Font fLucida;

  ...

  Locale     lDefault = Locale.getDefault();

  Locale[]   alSupported;

 

  NumberFormat nfLocal = NumberFormat.getNumberInstance(),

               nfSelected;

                

  String[]   asDNames,

             asLDNames,

             asDP = { "Number", "Percent"};

 

在構造器中,除了將輸入域初始化成數字併爲Number/Percent 輸入添加了一個額外的 JComboBox(jcbDP)之外,代碼幾乎同JIBDateGUI.java 中構造器的代碼相同。

    jtI.setText( nfLocal.format( 123456.7 ) );

    ...

    jcbDP = new JComboBox( asDP );

 

同樣,在 ActionListener 中處理了另一個 I18N 功能。如果 Number 輸入和 Percent 輸入之間有變化,那麼就設置跟蹤輸入類型的標記,並使用現有的本地 NumberFormat 來解析當前的輸入值。然後通過使用NumberFormat.getNumberInstance() 或 NumberFormat.getPercentInstance() 適當地創建一個新的 NumberFormat。通過使用新的本地 NumberFormat 重新格式化輸入值,並且代碼繼續爲選中的 NumberFormat 做相同的工作。

    if( oSource == jcbDP )

    {

      if( jcbDP.getSelectedIndex() == 0 )

      {

        bNumberFormat = true;

        try { n = nfLocal.parse( jtI.getText() ); }

        catch( ParseException pe ) {}

        nfLocal = NumberFormat.getNumberInstance();

      }

      else

      {

        bNumberFormat = false;

        try { n = nfLocal.parse( jtI.getText() ); }

        catch( ParseException pe ) {}

        nfLocal = NumberFormat.getPercentInstance();

      }

      jtI.setText( nfLocal.format( n ) );

      // set to perform jcb operation

      oSource = jcb;

    }

 

如果 Display Names 下拉框更改了,那麼就爲選中的語言環境創建一個適當的新 NumberFormat。代碼然後繼續將新 NumberFormat 應用於輸入和顯示值。

    if( oSource == jcb )

    {

      if( bNumberFormat )

      {

        nfSelected = NumberFormat.getNumberInstance(

           alSupported[ jcb.getSelectedIndex() ] );

      }

      else

      {

        nfSelected = NumberFormat.getPercentInstance(

           alSupported[ jcb.getSelectedIndex() ] );

      }

    }  // end if jcb, continue on

 

無論是否從由組合框的更改或直接由 OK 按鈕引起操作繼續,顯示域都將被清空。代碼試圖從輸入解析 Number 並使用缺省的本地 NumberFormat 對其進行格式化以供輸出。接下來,格式化選中的 NumberFormat 的顯示。最後,解析該值並用它來創建 Number,Number 接下來被用來顯示原始值。

    jtD.setText( "" );

    jtP.setText( "" );

   

    try

    {

      n = nfLocal.parse( jtI.getText() );

      jtI.setText( nfLocal.format( n ) );

      jtD.setText( nfSelected.format( n ) );

      n = nfSelected.parse( jtD.getText() );

      jtP.setText( n.toString() );

    }

    catch( ParseException pe )

    {

      JOptionPane.showMessageDialog( this,

      pe.getMessage(), "", JOptionPane.ERROR_MESSAGE);

    }

 

同樣,JIBNumberGUI.java:NumberFormat 示例中列出了完整的程序。

 

貨幣格式化示例

這個 JIBCurrencyGUI 示例使用俄語作爲缺省語言環境 ― ru_RU。

同樣,JIBCurrencyGUI(參閱 JIBCurrencyGUI.java:CurrencyFormat 示例)非常象以前的示例那樣運行。該應用程序允許用戶以本地格式輸入貨幣值。輸入時確定本地語言環境,並且該語言環境顯示在“Require Symbol”複選框的旁邊。當用戶按下 OK 時,解析輸入值並使用選中的語言環境的貨幣符號顯示輸入值。這是純粹機械的格式化;在兩種貨幣之間沒有自動值轉換。在現實生活中,您將不得不自己處理匯率。還對選中的格式文本進行了解析並將其作爲標準數值型值單獨顯示。

至此,這些示例已據實地使用了標準格式化和解析。如果您使用這些程序中的值,您會發現這些值偶爾很不靈活或讓人惱火。這個示例處理了一個主要的不便之處:雖然我們期望有一個 NumberFormat CurrencyInstance 來顯示貨幣符號,但我們通常不想強迫最終用戶在鍵入時包括它。如果選中“Require Symbol”複選框,那麼用戶就必須在輸入時包含貨幣符號來避免 ParseException。這是標準行爲。如果沒有選中該複選框,就只能輸入數值型值。在提供這一行爲期間,我們將簡單地研究一下 DecimalFormatSymbols。

首先,定義到目前爲止您已很熟悉的同 I18N 有關的數據類型。主要變化是缺省的和選中的 NumberFormat 現在含有 CurrencyInstance。我們還定義了一個標準 NumberFormat 來處理不含貨幣符號的項,還定義了 String 來包含當前的貨幣符號。這是一個 String 而不是 char,因爲符號中可能不止一個字符,例如“DM”― 德國馬克。

  NumberFormat cfLocal = NumberFormat.getCurrencyInstance(),

               cfSelected,

               nfLocal = NumberFormat.getInstance();

  ...

  String     sCurSymbol = "";

 

在構造器中,除了添加了新複選框之外,最後一個代碼塊之前的所有代碼都與 JIBNumberGUI 中相同。首先,代碼確保 NumberFormat.getCurrencyInstance() 請求返回一個 DecimalFormat。如果沒有返回,我們就不能獲取所需的信息,複選框也被禁用,意味着程序運行期間“Symbol Required”將爲真。否則,我們就獲得 DecimalFormat 的相關的 DecimalFormatSymbols 並獲得缺省貨幣符號。代碼還檢查 MonetaryDecimalSeparator 和 DecimalSeparator 是否相同。如果不同,就將 DecimalSeparator 設置成 MonetaryDecimalSeparator。然後將用於備用的 NumberFormat 的 DecimalFormatSymbols 設置成 CurrencyInstance。

    if( cfLocal instanceof DecimalFormat )

    {

      DecimalFormatSymbols dfs =

       ((DecimalFormat)cfLocal).getDecimalFormatSymbols();

      sCurSymbol = dfs.getCurrencySymbol();

 

      char chMDS = dfs.getMonetaryDecimalSeparator();

      if( chMDS != dfs.getDecimalSeparator() )

      {

        dfs.setDecimalSeparator( chMDS );

      }

 

      if( nfLocal instanceof DecimalFormat )

      {

        ((DecimalFormat)nfLocal).setDecimalFormatSymbols(

             dfs );

      }

      else

      { jchkb.setEnabled( false ); }

    } // end if cfLocal instanceof DecimalFormat

    else

    { jchkb.setEnabled( false ); }

 

在 actionPerformed() 中,如果選中的語言環境改變了,那麼就獲得一個適當的新的 CurrencyInstance,然後就應用用於 OK 按鈕的代碼。如果用戶按下 OK,就清空顯示域。

    if( oSource == jcb )

    {

      cfSelected = NumberFormat.getCurrencyInstance(

         alSupported[ jcb.getSelectedIndex() ] );

    }  // end if jcb, continue on

 

接下來是使接受不鍵入貨幣符號的輸入成爲可能的代碼:代碼檢查是否需要貨幣符號。如果需要,則使用本地 CurrencyInstance 來解析輸入;否則,代碼確定輸入中是否包含貨幣符號。如果包含,就使用 CurrencyInstance;否則,本地DecimalFormat 被用於解析。除了 CurrencyInstance 被用於格式化之外,代碼的餘下部分類似於前面的示例。

    jtD.setText( "" );

    jtP.setText( "" );

 

    try

    {

      if( bRequireSymbol )

      {

         n = cfLocal.parse( sText );

      }

      else

      { // currency symbol may still be present, check

        if( sText.indexOf( sCurSymbol ) == -1 )

        {

          n = nfLocal.parse( sText );

        }

        else

        {

          n = cfLocal.parse( sText );

        }

      }

 

      jtI.setText( cfLocal.format( n ) );

      jtD.setText( cfSelected.format( n ) );

      n = cfSelected.parse( jtD.getText() );

      jtP.setText( n.toString() );

    }

    catch( ParseException pe )

    {

      JOptionPane.showMessageDialog( this,

      pe.getMessage(), "", JOptionPane.ERROR_MESSAGE);

    }

 

參閱在 JIBCurrencyGUI.java: CurrencyFormat 示例中的完整清單。

將各部分放在一起

清潔工程師維護(Sanitation EngineerMaintenance)概述

這個 JIBSEM 示例使用德語作爲缺省語言環境 ― de_DE。

最後這個示例將所有的部分放在一起。

在我們的人造方案中,我們涉及一種職業其從業者實際上是在做清理工作 ― 清潔工程(SanitationEngineering)。對於我們來說幸運的是,在許多國家或地區老闆希望工程師們能夠用任何語言談天論地。實際需要兩個最終程序:1) 一個被限制成從用戶的特定國家或地區選擇工程師的查詢,供低層經理使用;以及 2) 一個顯示所有工程師並允許使用特定於選中的工程師的語言環境格式編輯薪水、僱傭日期和負責的噸位的程序,只有高層管理人員有權訪問該程序。

JIBSEM.java 是一個原型,設計成用來處理這兩方面以確保我們能夠提供希望的功能。開始時,應用程序將特別處理美國英語、法語、德語、和俄語語言環境,缺省是美國英語。進入時,程序中的描述性的標籤將用本地語言環境(如果受支持的話)顯示。應該用特定於工程師的語言環境顯示數據,解析數據並格式化數據。由於數據正常情況下是在特定地點以當地格式輸入,並且在數據庫中也是那樣保存的,所以這是必需的。數據庫包括每位工程師的語言環境信息以支持這種功能。爲簡單起見,我們的原型僅僅將四行匹配到四個受支持的語言環境。當然我們需要修改並優化該程序以供生產,但現在它已經可以滿足我們的用途了。

JIBSEM 爲每種受支持的語言環境顯示具有模擬數據的 JTable。選中一行時,屏幕底部兩次顯示適當的數據:一次在一個可編輯域,另一次在一個非編輯域中,這樣用戶就可以跟蹤其當前值了。用戶可以更改這些值並按 OK 來應用它們。如果值通過編輯,那麼新值顯示在兩個域中,並且支持的存儲也被更新;否則,會顯示一個錯誤對話框。

您會注意到這一示例中有好些 Swing 代碼,但與 I18N 相關的部分應該已經熟悉了。提供了支持類JIBSEMATM.java(AbstractTableModel 實現)和 JIBSEMRow.java(包含行數據),分別是下列properties 文件:

l      JIBSEMrb.properties(缺省用美國值)

l      JIBSEMrb_de_DE.properties(德語)

l      JIBSEMrb_fr_FR.properties(法語)

l      JIBSEMrb_ru_RU.properties(俄語)

請注意,在這種情形下無需重複的缺省文件,雖然這種重複不會有什麼壞處;在啓動時我們只查詢 ResourceBundle,並且對於 ResourceBundle 訪問,語言環境從不改變。

清潔工程師維護:I18N 詳細信息

定義的 I18N 有關的數據類型是:

  DateFormat[] aDF;

  DateFormat dfSelected;

 

  Font fLucida,

       fLucidaNormal,

       fLucidaTitle;

  ...

  Locale     lDefault = Locale.getDefault();

  Locale[]   alSupported = {

                Locale.US,

                Locale.FRANCE,

                Locale.GERMANY,

                new Locale( "ru", "RU" )

                           };

  NumberFormat[] aCF,

                 aNF;

  NumberFormat   cfSelected,

                 nfSelected;

  ...

  ResourceBundle rb;

 

  String[]   asHeaders = new String[2];

  String sRBName = getClass().getName() + "rb";

 

在構造器中,爲支持的語言環境裝入含有貨幣、日期及數字的格式化器的數組。代碼還確定是否支持缺省語言環境;如果不支持,則美國語言環境用於缺省語言環境。接下來,通過對 loadFromResourceBundle() 的調用將標籤從適當的ResourceBundle 裝入。注:我們還爲表顯示設置了Lucida Sans 字體。

    aCF = new NumberFormat[ alSupported.length ];

    aDF = new DateFormat[ alSupported.length ];

    aNF = new NumberFormat[ alSupported.length ];

 

    boolean bLocaleMatched = false;

    Locale lTemp;

    for( i = 0; i < alSupported.length; i++ )

    {

      lTemp = alSupported[i];

      aCF[i] = NumberFormat.getCurrencyInstance(

                  lTemp );

 

      aDF[i] = DateFormat.getDateInstance(

                  DateFormat.SHORT,

                  lTemp );

      aDF[i].setLenient( false );

 

      aNF[i] = NumberFormat.getNumberInstance(

                  lTemp );

      if( lDefault.equals( lTemp ) )

      {

        bLocaleMatched = true; 

      }

    } // end for

    if( !bLocaleMatched ) { lDefault = Locale.US; }

    ...

    loadFromResourceBundle(); // get localized labels

    ...

    jtbl.setFont( fLucidaNormal );

    jtbl.getTableHeader().setFont( fLucidaNormal );

 

定義了三種格式化方法來處理三種值:formatCurrency(doubledSalary)、formatDate(java.util.Dated) 和 formatNumber(doubledTonnage)。請注意代碼的相似處。

  public String formatCurrency( double dSalary )

  {

    cfSelected = aCF[iRowIndex];

    return cfSelected.format( dSalary );

  } // end formatCurrency

 

 

  public String formatDate( java.util.Date d )

  {

    dfSelected = aDF[iRowIndex];

    return dfSelected.format( d );

  } // end formatDate

 

 

  public String formatNumber( double dTonnage )

  {

    nfSelected = aNF[iRowIndex];

    return nfSelected.format( dTonnage );

  } // end formatDate

 

下面是 loadFromResourceBundle() 方法。就象大多數 I18N 程序一樣,該方法(和資源裝入)只在啓動時調用一次:

  public void loadFromResourceBundle()

  {

    try

    { // get the PropertyResourceBundle

      rb = ResourceBundle.getBundle( sRBName,

                                     getLocale() );

      // get data associated with keys

      jlTitle.setText( rb.getString( "title" ));

      asHeaders[0] = rb.getString( "Engineer" );

      asHeaders[1] = rb.getString( "Name" );

 

      jlE.setText( asHeaders[0] + ":" );

      jlEdit.setText( rb.getString( "Edit" ));

      jlCurrent.setText( rb.getString( "Current" ));

      jlCI.setText( rb.getString( "Salary" ));

      jlDI.setText( rb.getString( "Date" ));

      jlNI.setText( rb.getString( "Tons" ));

    } // end try

    catch( MissingResourceException mre )

    {

      JOptionPane.showMessageDialog( this,

        "ResourceBundle problem;\n" +

        "Specific error: " + mre.getMessage(),

        "", JOptionPane.ERROR_MESSAGE);

    }

  } // end loadFromResourceBundle

 

和通常一樣,大多數操作在 actionPerformed() 中。當用戶按下 OK 時,代碼試圖使用選中的格式化器解析貨幣、日期和數字值。如果拋出一個異常,那麼會顯示一個錯誤對話框並且在方法返回之前不再做任何工作。否則,數據被更新,兩組域都顯示新值,並且我們也準備處理新行。當在 valueChanged() 中選中了特定行時,用於建立索引的 iRowIndex 字段被捕獲。同前面提到的一樣,此時原型的行和數組元素匹配。

  public void actionPerformed(ActionEvent ae)

  {

    Object oSource = ae.getSource();

 

    boolean bError = false;

    java.util.Date d = null;

    Number n = null,

           nCur = null;

   

    try

    {

      nCur = cfSelected.parse( jtCI.getText() );

      d = dfSelected.parse( jtDI.getText() );

      n = nfSelected.parse( jtNI.getText() );

    }

    catch( ParseException pe )

    {

      JOptionPane.showMessageDialog( this,

      pe.getMessage(), "", JOptionPane.ERROR_MESSAGE);

      bError = true;

    }

 

    if( bError == false )

    {

      aRows[iRowIndex].setSalary( nCur.floatValue() );

      jtCD.setText( jtCI.getText() );

      aRows[iRowIndex].setHireDate( d );

      jtDD.setText( jtDI.getText() );

      aRows[iRowIndex].setTonnage( n.doubleValue() );

      jtND.setText( jtDI.getText() );

      jtbl.requestFocus();

    }

  }  // End actionPerformed

 

請參閱 JIBSEM.java:Sanitation Engineer Maintenance 示例中完整應用程序的清單。

 

 

結束語與參考資料

結束語

雖然本教程僅僅只在編程級別觸及了處理 I18N 這一問題的冰山一角,但是至此您應該有了足夠的信息和材料來處理 I18N 程序員通常要面對的大多數問題:

l      Java字符與 char 數據類型 

l      字體、字體特性及 Lucida 字體 

l      創建語言環境 

l      使用資源束 

l      日期、數字和貨幣

我們也在國際化 中大體上簡單地討論了 I18N 並在國際化和 Java 編程語言 中描述了 Java API 支持。我們提供了參考資料 以供您進一步研究。最後,我們完成了幾個示例,包括清潔工程師維護(Sanitation Engineer Maintenance)概述,它將所有特定元素集中到一個應用程序中。

參考資料

  • 下載本教程中使用的完整源代碼和類i18n-source.jar。(在 Netscape 中,單擊鼠標右鍵然後選擇“Save link as.”)
  • The Java Language Specification(http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html)是 Java 語言的權威性文檔。
  • 請閱讀正式的 JDK 1.3 國際化文檔(http://java.sun.com/j2se/1.3/docs/guide/intl/index.html)。
  • Sun Microsystems 的 Java Tutorial 包括 Internationalization(http://java.sun.com/docs/books/tutorial/i18n/index.html)一節。
  • Tomohiro Kubota 撰寫的 Introduction to i18n(http://www.debian.org/doc/manuals/intro-i18n/index.html#contents)從 Linux 的觀點提供了 I18N 準則。
  • 還有 I18N 方面的問題嗎?請訪問 jGuru I18N FAQ(http://www.jguru.com/faq/I18N),Joe Sam Shirah 擔任那裏的 FAQ 管理員。
  • Eric Olson 在其“Introducing inheritance to PropertyResourceBundles ”(developerWorks,2001 年 5 月,http://www.ibm.com/developerworks/library/j-bundles/)中討論了使用 PropertyResourceBundle 創建完全國際化的 Java 應用程序時可能出現的問題,並演示了一個提升資源束間關係的同時最大化束重用的解決方案。
  • RBManager(http://www.alphaworks.ibm.com/tech/rbmanager)是一個來自 IBM alphaWorks 的工具,它使許多同創建、更新及管理資源束文件有關的任務自動化,Jared Jackson 在其文章“Harnessing internationalization”(developerWorks,2000 年 12 月,http://www.ibm.com/developerworks/library/j-rbmgr/)中描述了該工具。
  • Global Internet Statistics(http://www.glreach.com/globstats/index.php3)由 Global Reach 維護,它是對各種來源的因特網統計信息的一個集合。
  • Chuck McManis 是 Oak 小組的原始成員之一,他在其文章“An in-depth look at Java's character type”(JavaWorld,1998 年,http://www.javaworld.com/jw-01-1998/jw-01-indepth_p.html)對關於 Java 字符、閱讀器和寫入器的決策進行了深入的探討。
  • 在“Getting Java ready for the world: A brief history of IBM and Sun's internationalization efforts”(developerWorks,1999 年 7 月,http://www.ibm.com/developerworks/library/sun-ibm-java.html)中,Laura Werner 對 Java 國際化支持的起源進行了有趣的探討。
  • 在“Efficient text searching in Java: Finding the right string in any language”(developerWorks,1999 年 4 月,http://www.ibm.com/developerworks/library/text-searching.html)中,Laura Werner 討論了與排序及搜索國際化文本有關的難題。
  • Unicode Consortium(http://www.unicode.org/)是官方 Unicode 站點,上面有關於 Unicode 的標準、文檔及程序。
  • developerWorks Unicode 專題(http://www.ibm.com/developerworks/unicode/)提供與 Unicode 有關的代碼、文章和公告。
  • macchiato.com 是一個由 Mark Davis 維護的站點,它提供來自在 Unicode 和國際化開發方面著名人物的代碼、文章和評論。
  • Mark Davis 在其文章“The Java International API: Beyond JDK 1.1”(developerWorks,1998 年 10 月,http://www.ibm.com/developerworks/library/intljava.html)中討論了 Java 2 平臺的國際化支持中的變化。
  • 在附錄 C Java Look and Feel Design Guidelines, 2nd edition (http://java.sun.com/products/jlf/ed2/book/index.html)中,您將發現幾種語言的常備詞彙和短語。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章