1

要使复合组件国际化,您必须将一个与组件本身具有完全相同名称的.properties文件放在同一文件夹中。

在 xhtml 中,您可以通过 ${cc.resourceBundleMap.key} 访问这些翻译。

直到现在,一切都很好,对我有用。当我为其他语言添加更多.properties文件时,问题就开始了。无论我的计算机在哪个本地,选择的语言都是默认语言(component.properties)。

这似乎是一个反复出现的问题,因为 Ziletka 在如何本地化 JSF 2 复合组件中也报告了相同的问题,但仍未得到答复。

我尝试了各种可能性:

  • 没有默认的 .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
    

    再说一遍:只加载默认值。

我很想避免不得不依赖支持 bean 来提供翻译,并且不能相信它不受支持。任何人都可以帮忙吗?

4

2 回答 2

1

这个功能很久以前就在 MyFaces Core 中实现了。请参阅:MYFACES-3308。完成的测试用例可以在这里找到

应用于复合组件的语言环境取决于从 UIViewRoot.getLocale() 检索到的值。

于 2012-12-04T23:55:12.203 回答
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加载。您可能会在下面找到仅尊重语言的实现,这意味着它适用于 componentName _en .properties 和 componentName _de .properties 之类的文件,不适用于 componentName _en_US .properties之类的文件

您的 .properties 文件应与组件定义位于同一目录中

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

在您的组件中testComponent.xhtml指定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();
    }

}

完毕。

然而,这显然是 Mojarra 中的一个错误,我希望它会很快得到修复。仔细查看与复合组件相关的代码会发现,组件的默认 .properties 文件被读取了两次,我想这也不是一个好主意,但这是另一回事。

PS。如果您愿意,您也可以轻松地调整 te 代码以尊重国家/地区代码。

于 2016-02-26T16:46:45.823 回答