要使复合组件国际化,您必须放置 .properties 文件,该文件的完全与组件本身相同,并且位于同一文件夹中。
从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
再次:只加载默认值。
我希望避免不得不依赖于支持bean来提供翻译,并且无法解析为相信它不受支持。有人可以帮忙吗?
答案 0 :(得分:1)
此功能很久以前就已在MyFaces Core中实施。见:MYFACES-3308。完成的测试用例可以找到here
应用于复合组件的语言环境取决于从UIViewRoot.getLocale()检索的值。
答案 1 :(得分: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。如果您愿意,也可以轻松调整代码以尊重国家/地区代码。