2012-12-04 41 views
1

要國際化複合組件,必須將名稱與組件本身同名且位於同一文件夾中的文件放在同一文件夾中。JSF2.0多語言複合組件

從XHTML,你可以通過$ {} cc.resourceBundleMap.key訪問這些翻譯。

到現在爲止,一切都很好,併爲我工作。當問題開始時我添加更多的.properties文件用於其他語言。無論我的計算機位於哪個本地,選擇的語言都是默認的語言(component.properties)。

這似乎是一個經常性的問題,因爲Ziletka也How to localize JSF 2 composite components報告了同樣的問題,但沒有得到答覆。

我已經嘗試過各種可能性:

  • 沒有默認的.properties文件

    component_fr.properties 
    component_fr_CA.properties 
    component_fr_FR.properties 
    component_en.properties 
    component_en_CA.properties 
    component_en_US.properties 
    

    但它會導致:

    javax.el.ELException: [...] /resources/component/component.xhtml default="${cc.resourceBundleMap.key}": java.lang.NullPointerException 
    
  • 默認.properties文件加上語言規範

    component.properties 
    component_fr.properties 
    component_en.properties 
    

    只加載默認值。

  • 默認的.properties文件加上語言和國家規格

    component.properties 
    component_fr_CA.properties 
    component_fr_FR.properties 
    component_en_CA.properties 
    component_en_US.properties 
    

    並再次:只有默認加載。

我很想避免必須依賴backing bean來提供翻譯,並且不能相信它不被支持。誰能幫忙?

回答

1

此功能實現在很久以前MyFaces的核心。參見:MYFACES-3308。可以找到完成的測試用例here

應用於複合組件的區域設置取決於從UIViewRoot.getLocale()中檢索的值。

0

顯然問題仍然存在,其根源在javax.faces.component.UIComponent類中,特別是在findComponentResourceBundleLocaleMatch方法中。被剪斷的是低於

private Resource findComponentResourceBundleLocaleMatch(FacesContext context, 
     String resourceName, String libraryName) { 
    Resource result = null; 
    ResourceBundle resourceBundle = null; 
    int i; 
    if (-1 != (i = resourceName.lastIndexOf("."))) { 
     resourceName = resourceName.substring(0, i) + 
       ".properties"; //THE PROBLEM IS HERE 
     if (null != context) { 
      result = context.getApplication().getResourceHandler(). 
        createResource(resourceName, libraryName); 
      InputStream propertiesInputStream = null; 
      try { 
       propertiesInputStream = result.getInputStream(); 
       resourceBundle = new PropertyResourceBundle(propertiesInputStream); 
      } catch (IOException ex) { 
       Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex); 
      } finally{ 
       if(null != propertiesInputStream){ 
        try{ 
         propertiesInputStream.close(); 
        } catch(IOException ioe){ 
         if (LOGGER.isLoggable(Level.SEVERE)) { 
          LOGGER.log(Level.SEVERE, null, ioe); 
         } 
        } 
       } 
      } 
     } 
    } 
    result = (null != resourceBundle) ? result : null; 

    return result; 
} 

你可以看到它的評論,指出'問題在這裏'。正是在查找屬性文件來加載它不尊重任何語言和/或國家代碼。它總是加載一個默認資源。

可能的解決方法 的「問題」的方法是同一類的另一種方法getResourceBundleMap,你感興趣的是標有註釋(行號1000)

// Step 2: if this is a composite component, look for a 
// ResourceBundle as a Resource 
代碼的一部分被稱爲

因爲您需要複合組件,所以這並不令人意外。因此,解決方案將是爲您的複合組件定義一個支持組件類,並重新定義加載的resourceBundleMap。下面你會發現,只有尊重語言,這意味着它會爲文件的工作就像組件名_en的.properties和組件名稱_de的.properties但執行不會對類似組件名_en_US的.properties

你的.properties文件應在同一目錄中的組件testComponent.xhtml的你的組件

testComponent.properties 
testComponent_de.properties 
testComponent_en.properties 
testComponent_fr.properties 

的定義componentType指定屬性定義的類。

<cc:interface componentType="test.component"> 
    .... 
</cc:interface> 

該組件可能如下所示。我使用原始代碼主要是對夫婦的變化。這個想法是重寫有問題的方法,並用代碼嘗試先讀取指定語言的屬性文件,如果找不到,請閱讀默認的。

@FacesComponent("test.component") 
public class TestComponent extends UINamingContainer { 

    private static final String PROPERTIES_EXT = ".properties"; 


    private Logger LOGGER = <use one you like>; 

    private Map<String, String> resourceBundleMap = null; 

    @Override 
    public Map<String, String> getResourceBundleMap() { 
     ResourceBundle resourceBundle = null; 
     if (null == resourceBundleMap) { 
      FacesContext context = FacesContext.getCurrentInstance(); 
      UIViewRoot root = context.getViewRoot(); 
      Locale currentLocale = null; 
      if (null != context) { 
       if (null != root) { 
        currentLocale = root.getLocale(); 
       } 
      } 
      if (null == currentLocale) { 
       currentLocale = Locale.getDefault(); 
      } 

      if (this.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY)) { 
       Resource ccResource = (Resource) 
         this.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY); 
       if (null != ccResource) { 
        InputStream propertiesInputStream = null; 
        try { 
         propertiesInputStream = ccResource.getInputStream(); 
         resourceBundle = findComponentResourceBundleLocaleMatch(ccResource.getResourceName(), 
           ccResource.getLibraryName(), currentLocale.getLanguage()); 
        } catch (IOException ex) { 
         LOGGER.error(null, ex); 
        } finally { 
         if (null != propertiesInputStream) { 
          try { 
           propertiesInputStream.close(); 
          } catch (IOException ioe) { 
           LOGGER.error(null, ioe); 
          } 
         } 
        } 
       } 
      } 

      if (null != resourceBundle) { 
       final ResourceBundle bundle = resourceBundle; 
       resourceBundleMap = 
         new Map() { 
          // this is an immutable Map 

          public String toString() { 
           StringBuffer sb = new StringBuffer(); 
           Iterator<Map.Entry<String, Object>> entries = 
             this.entrySet().iterator(); 
           Map.Entry<String, Object> cur; 
           while (entries.hasNext()) { 
            cur = entries.next(); 
            sb.append(cur.getKey()).append(": ").append(cur.getValue()).append('\n'); 
           } 

           return sb.toString(); 
          } 

          // Do not need to implement for immutable Map 
          public void clear() { 
           throw new UnsupportedOperationException(); 
          } 


          public boolean containsKey(Object key) { 
           boolean result = false; 
           if (null != key) { 
            result = (null != bundle.getObject(key.toString())); 
           } 
           return result; 
          } 


          public boolean containsValue(Object value) { 
           Enumeration<String> keys = bundle.getKeys(); 
           boolean result = false; 
           while (keys.hasMoreElements()) { 
            Object curObj = bundle.getObject(keys.nextElement()); 
            if ((curObj == value) || 
              ((null != curObj) && curObj.equals(value))) { 
             result = true; 
             break; 
            } 
           } 
           return result; 
          } 


          public Set<Map.Entry<String, Object>> entrySet() { 
           HashMap<String, Object> mappings = new HashMap<String, Object>(); 
           Enumeration<String> keys = bundle.getKeys(); 
           while (keys.hasMoreElements()) { 
            String key = keys.nextElement(); 
            Object value = bundle.getObject(key); 
            mappings.put(key, value); 
           } 
           return mappings.entrySet(); 
          } 


          @Override 
          public boolean equals(Object obj) { 
           return !((obj == null) || !(obj instanceof Map)) 
             && entrySet().equals(((Map) obj).entrySet()); 

          } 


          public Object get(Object key) { 
           if (null == key) { 
            return null; 
           } 
           try { 
            return bundle.getObject(key.toString()); 
           } catch (MissingResourceException e) { 
            return "???" + key + "???"; 
           } 
          } 


          public int hashCode() { 
           return bundle.hashCode(); 
          } 


          public boolean isEmpty() { 
           Enumeration<String> keys = bundle.getKeys(); 
           return !keys.hasMoreElements(); 
          } 


          public Set keySet() { 
           Set<String> keySet = new HashSet<String>(); 
           Enumeration<String> keys = bundle.getKeys(); 
           while (keys.hasMoreElements()) { 
            keySet.add(keys.nextElement()); 
           } 
           return keySet; 
          } 


          // Do not need to implement for immutable Map 
          public Object put(Object k, Object v) { 
           throw new UnsupportedOperationException(); 
          } 


          // Do not need to implement for immutable Map 
          public void putAll(Map t) { 
           throw new UnsupportedOperationException(); 
          } 


          // Do not need to implement for immutable Map 
          public Object remove(Object k) { 
           throw new UnsupportedOperationException(); 
          } 


          public int size() { 
           int result = 0; 
           Enumeration<String> keys = bundle.getKeys(); 
           while (keys.hasMoreElements()) { 
            keys.nextElement(); 
            result++; 
           } 
           return result; 
          } 


          public java.util.Collection values() { 
           ArrayList<Object> result = new ArrayList<Object>(); 
           Enumeration<String> keys = bundle.getKeys(); 
           while (keys.hasMoreElements()) { 
            result.add(
              bundle.getObject(keys.nextElement())); 
           } 
           return result; 
          } 
         }; 

      } 

      if (null == resourceBundleMap) { 
       resourceBundleMap = Collections.EMPTY_MAP; 
      } 
     } 
     return resourceBundleMap; 
    } 

    private ResourceBundle findComponentResourceBundleLocaleMatch(String resourceName, String libraryName, String lng) { 
     FacesContext context = FacesContext.getCurrentInstance(); 
     ResourceBundle resourceBundle = null; 
     int i; 
     if (-1 != (i = resourceName.lastIndexOf("."))) { 
      if (null != context) { 
       InputStream propertiesInputStream = null; 
       try { 
        propertiesInputStream = getResourceInputStream(context, resourceName.substring(0, i), libraryName, lng); 
        resourceBundle = new PropertyResourceBundle(propertiesInputStream); 
       } catch (IOException ex) { 
        LOGGER.error(null, ex); 
       } finally { 
        if (null != propertiesInputStream) { 
         try { 
          propertiesInputStream.close(); 
         } catch (IOException ioe) { 
          LOGGER.error(null, ioe); 
         } 
        } 
       } 
      } 
     } 
     return resourceBundle; 
    } 

    private InputStream getResourceInputStream(FacesContext context, final String resourceName, String libraryName, String lng) throws IOException { 
     InputStream propertiesInputStream = null; 
     propertiesInputStream = getPropertiesResourceInputStream(context, String.format("%s_%s%s", resourceName, lng, PROPERTIES_EXT), libraryName); 
     if (null == propertiesInputStream) { 
      propertiesInputStream = getPropertiesResourceInputStream(context, resourceName + PROPERTIES_EXT, libraryName); 
     } 
     return propertiesInputStream; 
    } 

    private InputStream getPropertiesResourceInputStream(FacesContext context, final String resourceName, String libraryName) throws IOException { 
     Resource result = context.getApplication().getResourceHandler().createResource(resourceName, libraryName); 
     if (null == result) { 
      return null; 
     } 
     return result.getInputStream(); 
    } 

} 

完成。

但是,這顯然是莫哈拉的一個錯誤,我希望它很快就會解決。仔細觀察與複合組件相關的代碼可以發現組件的默認.properties文件被讀取兩次,我猜這也不是一個好主意,但這是另一回事。

PS。如果您願意,您可以輕鬆調整te代碼以尊重國家代碼。