使用从数据库加载的ResourceBundle条目在JSF中进行国际化

我正在使用JPA / EJB / JSF开发Java EE6项目,我在为实体设计多语言支持方面遇到了一些麻烦。有三个相关实体: 语言(有id) 能力(有id) CompetenceName(具有能力参考,语言参考和字符串) Competence对使用Map实现的CompetenceName有一对多的引用,每个语言都包含一个对象,其中存在一个Competence的名称。请注意,权限是动态创建的,因此它们的名称不能存在于资源包中。 在网页上列出Competences时,我希望它们以当前登录用户的语言显示,它存储在Session Scoped Managed Bean中。 有没有什么好方法可以在不破坏MVC设计的情况下实现这一目标?我的第一个想法是通过FacesContext直接从Competence实体中的“getName”方法获取会话范围bean,并在CompetenceNames的地图中查找如下:
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";
}
这个解决方案感觉非常粗糙,因为实体依赖于Controller层,并且每次我想要它的名字时都必须获取会话控制器。更符合MVC的解决方案是采用Language参数,但这意味着来自JSF的每一次调用都必须包含从会话作用域托管bean中获取的语言,这也不是一个好的解决方案。 有没有人对此问题有任何想法或设计模式?     
已邀请:
国际化/本地化应该优选完全在视图方面完成。该模型不应该意识到这一点。 在JSF中,
faces-config.xml
中的
<resource-bundle>
条目和XHTML中的
<f:loadBundle>
也可以指向一个完整的
ResourceBundle
类而不是
.properties
文件的基本名称。在Java SE 6中,有一个新的
ResourceBundle.Control
API可以完全控制加载和填充包。 了解这些事实后,应该可以使用自定义
ResourceBundle
Control
从DB加载捆绑消息。这是一个启动示例:
public class CompetenceBundle extends ResourceBundle {

    protected static final String BASE_NAME = "Competence.messages"; // Can be name of @NamedQuery
    protected static final Control DB_CONTROL = new DBControl();

    private Map<String, String> messages;

    public CompetenceBundle() {
        setParent(ResourceBundle.getBundle(BASE_NAME, 
            FacesContext.getCurrentInstance().getViewRoot().getLocale(), DB_CONTROL));
    }

    protected CompetenceBundle(Map<String, String> messages) {
        this.messages = messages;
    }

    @Override
    protected Object handleGetObject(String key) {
        return messages != null ? messages.get(key) : parent.getObject(key);
    }

    @Override
    public Enumeration<String> getKeys() {
        return messages != null ? Collections.enumeration(messages.keySet()) : parent.getKeys();
    }

    protected static class DBControl extends Control {

        @Override
        public ResourceBundle newBundle
            (String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
                throws IllegalAccessException, InstantiationException, IOException
        {
            String language = locale.getLanguage();
            Map<String, String> messages = getItSomehow(baseName, language); // Do your JPA thing. The baseName can be used as @NamedQuery name.
            return new CompetenceBundle(messages);
        }

    }

}
这样你就可以在
faces-config.xml
中声明如下:
<resource-bundle>
    <base-name>com.example.i18n.CompetenceBundle</base-name>
    <var>competenceBundle</var>
</resource-bundle>
或者在Facelet中如下:
<f:loadBundle basename="com.example.i18n.CompetenceBundle" var="competenceBundle" />
无论哪种方式,您都可以通常的方式使用它:
<h:outputText value="#{competenceBundle.name}" />
    
我会将您的模型中特定于语言的部分移动到recource包中。只需模拟能力,语言和用户。如果用户请求页面,则显示其权限并从recource包中查找特定于语言的能力(CompetenceName)。 我搜索了示例代码以帮助您入门并找到它,请参见清单19.16 customerDetails.jsp。 就像是:
<fmt:message key="${competence}" />
    

要回复问题请先登录注册