Database JSF中的国际化,使用从数据库加载的ResourceBundle条目

Database JSF中的国际化,使用从数据库加载的ResourceBundle条目,database,jsf,jpa,internationalization,resourcebundle,Database,Jsf,Jpa,Internationalization,Resourcebundle,我正在使用JPA/EJB/JSF进行一个JavaEE6项目,在设计实体的多语言支持时遇到了一些问题。有三个相关实体: 语言(具有id) 能力(具有id) 能力名称(具有能力引用、语言引用和字符串) Competency使用映射实现对CompetencyName的一对多引用,该映射包含一个对象,表示存在某个Competency名称的每种语言。请注意,能力是动态创建的,因此它们的名称不能存在于资源包中 当在网页上列出能力时,我希望它们以当前登录用户的语言显示,这存储在会话范围的托管Bean中 有没有

我正在使用JPA/EJB/JSF进行一个JavaEE6项目,在设计实体的多语言支持时遇到了一些问题。有三个相关实体:

语言(具有id)
能力(具有id)
能力名称(具有能力引用、语言引用和字符串)

Competency使用映射实现对CompetencyName的一对多引用,该映射包含一个对象,表示存在某个Competency名称的每种语言。请注意,能力是动态创建的,因此它们的名称不能存在于资源包中

当在网页上列出能力时,我希望它们以当前登录用户的语言显示,这存储在会话范围的托管Bean中

有没有什么好方法可以在不破坏好的MVC设计的情况下实现这一点?我的第一个想法是通过FacesContext直接从Competency实体中的“getName”方法获取会话范围bean,并在CompetencyName映射中查找它,如下所示:

public class Competence
{
...
@MapKey(name="language")
@OneToMany(mappedBy="competence", cascade=CascadeType.ALL, orphanRemoval=true)
private Map<Language, CompetenceName> competenceNames;

public String getName(String controller){
    FacesContext context = FacesContext.getCurrentInstance();
    ELResolver resolver = context.getApplication().getELResolver();
    SessionController sc = (SessionController)resolver.getValue(context.getELContext(), null, "sessionController");
    Language language = sc.getLoggedInUser().getLanguage();
    if(competenceNames.get(language) != null)
        return competenceNames.get(language).getName();
    else
        return "resource missing";
}
公共课堂能力
{
...
@MapKey(name=“language”)
@OneToMany(mappedBy=“胜任”,cascade=CascadeType.ALL,orphan=true)
私人地图能力名称;
公共字符串getName(字符串控制器){
FacesContext context=FacesContext.getCurrentInstance();
ELResolver resolver=context.getApplication().getELResolver();
SessionController sc=(SessionController)resolver.getValue(context.getELContext(),null,“SessionController”);
Language Language=sc.getLoggedInUser().getLanguage();
if(能力名称.获取(语言)!=null)
返回competinceNames.get(语言).getName();
其他的
返回“资源丢失”;
}
这个解决方案感觉非常粗糙,因为实体依赖于控制器层,每次我想要它的名称时都必须获取一个会话控制器。更符合MVC的解决方案是获取一个语言参数,但这意味着来自JSF的每个调用都必须包含从会话范围的托管bean获取的语言这也不是一个好的解决方案


有人对此问题有什么想法或设计模式吗?

我会将特定于语言的部分从您的模型移动到资源包中。只需对能力、语言和用户进行建模。如果用户请求页面,您将显示其能力并从资源包中查找特定于语言的能力(能力名称)

我搜索了示例代码以帮助您开始并找到,请参见清单19.16 customerDetails.jsp

比如:

<fmt:message key="${competence}" />

国际化/本地化最好完全在视图端完成。模型不应该意识到这一点

在JSF中,
faces config.xml
中的
条目和XHTML中的
也可以指向一个完整的
ResourceBundle
类,而不是
.properties
文件的basename。在Java SE 6中,有一个新的API可以完全控制加载和填充捆绑包

了解这些事实后,应该可以使用自定义的
ResourceBundle
Control
从数据库加载捆绑包消息

公共类CompetencyBundle扩展了ResourceBundle{
受保护的静态最终字符串BASE_NAME=“competency.messages”;//可以是@NamedQuery的名称
受保护的静态最终控件DB_Control=new DBControl();
私人地图信息;
公共能力包(){
setParent(ResourceBundle.getBundle(基本名称,
FacesContext.getCurrentInstance().getViewRoot().getLocale(),DB_控件));
}
受保护的能力包(映射消息){
this.messages=消息;
}
@凌驾
受保护对象handleGetObject(字符串键){
返回消息!=null?messages.get(键):parent.getObject(键);
}
@凌驾
公共枚举getKeys(){
返回消息!=null?集合。枚举(messages.keySet()):parent.getKeys();
}
受保护的静态类DBControl扩展了控制{
@凌驾
公共资源包新绑定
(字符串baseName、区域设置、字符串格式、类加载器、布尔重载)
抛出IllegalAccessException、实例化Exception、IOException
{
String language=locale.getLanguage();
Map messages=getItSomehow(baseName,language);//执行JPA操作。baseName可以用作@NamedQuery name。
返回新的CompetenceBundle(消息);
}
}
}
通过这种方式,您可以在
faces config.xml
中声明它,如下所示:


com.example.i18n.competincebundle
能力束
或在Facelet中如下所示:


无论哪种方式,您都可以按常规方式使用:



问题在于能力不是静态的,管理员可以随时向系统添加新的能力或现有能力的能力名称。据我所知,捆绑包无法写入programmaticly。jdk 6允许重新加载资源捆绑包:感谢您的回答,这看起来确实很有趣。关于bundles:它们有什么作用域?假设一个用户更改了一个能力的名称,并调用newBundle刷新他的更改。这会反映所有用户的捆绑包,还是只反映他的捆绑包?捆绑包基本上是应用程序范围内按basename和locale缓存的。因此,任何更改都会反映给所有用户。我现在已经实现了这一点,并且需要付费这比让模型询问控制器语言详细信息要好得多。不过,我在重命名能力时遇到了一些问题,调用CompetencyBundle.clearCache();从java读取捆绑包时会清除缓存,但网站仍然显示旧值。应用程序服务器是否有自己的缓存?我猜是这样的