微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用从数据库加载的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中,< resource-bundle> faces-config.xml中的条目和< f:loadBundle>在XHTML中也可以指向一个完整的ResourceBundle类而不是.properties文件的basename.在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}" />

原文地址:https://www.jb51.cc/mssql/77226.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐