每个区域设置JSF 2中的不同面(用于模板) [英] Different facelets (for use in templates) in JSF 2 per locale

查看:91
本文介绍了每个区域设置JSF 2中的不同面(用于模板)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在某处有一个模板,该模板具有<ui:insert name="help_contents" />和一个定义<ui:define name="help_contents><!-- actual contents --></ui:define>的页面,其中定义中的内容应基于JSF(不仅仅是纯html/xhtml),由face servlet处理,并且基于在语言环境中.但是我不想使用资源包来这样做,因为这将需要每个属性包含大量文本,并且必须为散布在文本中的每个组件分解文本.换句话说,我希望每个语言环境都包含一个facelet,然后根据活动的语言环境包含正确的一个.

I have a template somewhere that has <ui:insert name="help_contents" /> and a page that defines <ui:define name="help_contents><!-- actual contents --></ui:define>, where the contents in the definition should be JSF-based (not just plain html/xhtml), handled by the faces servlet and differing based on locale. But I don't want to do this with resource bundles, since that would require large swathes of text per property and having to break it up for every component that's interspersed with the text. In other words, I want a facelet per locale, and then include the right one based on the active locale.

这基本上就是问题.为了其他人的搜索,下面的上下文中,如果您已经理解我的意思,请跳过.

That's basically the question. Context below for the sake of others who are searching, skip if you already understand what I mean.

在大多数情况下,JSF 2中的国际化非常容易.您制作了一个或多个资源包,在faces-config.xml中声明了这些资源包,就可以使用这些属性了.但是在我看来,这样的属性文件仅适用于短标签文本,列标题,其中可能包含几个参数的小消息……当涉及大部分文本时,它们似乎很笨拙.尤其是如果文本中应穿插XHTML标记或JSF组件,在这种情况下,您需要将其分解得太多.

Internationalization in JSF 2 is, for the most part, very easy. You make one or more resource bundles, declare those in your faces-config.xml and you're ready to use the properties. But such properties files are to my feeling only good for short label texts, column headers, small messages with maybe a couple of parameters in them... When it comes to large portions of text, they seem unwieldy. Especially if the text should be interspersed with XHTML tags or JSF components, in which case you'd need to break it up far too much.

当前,我正在开发一些使用JSF 2的Web应用程序,并以PrimeFaces作为组件捆绑包,该应用程序通常使用i18n的资源捆绑包.但是各种视图都需要一个帮助页面.我也想在这些帮助页面中使用JSF/PrimeFaces组件,以便填充表格或对话框的示例看起来与视图本身相同.

Currently I'm working on some web application that uses JSF 2, with PrimeFaces as component bundle, which uses resource bundles for i18n in the regular sense. But various views need a help page. I'd like to use JSF/PrimeFaces components in those help pages as well, so that examples of populated tables or dialogs look the same as in the view itself.

但是,包括基于区域设置的合成内容似乎比我想象的要简单.我想拥有带有_en或_fr之类的语言环境后缀的XHTML页面(facelets),并根据活动的语言环境选择正确的页面.如果不存在这样的页面,则应默认为_en页面(或不带后缀的页面,仅包含英文内容).从facescontext获取语言环境字符串不是问题,但是检测页面是否存在似乎更困难.在JSF中或通过EL有什么方法可以做到这一点,还是应该通过托管bean来做到这一点?为此编写一个自定义标签可能会很有用,但是我不确定这需要进行多少工作.

However, including composition content based on locale appears to be less straightforward than I thought. I'd like to have XHTML pages (facelets) with locale suffixes like _en or _fr and select the right page based on the active locale. If no such page exists, it should default to the _en page (or one without a suffix that just includes the English content). Getting the locale string from the facescontext isn't a problem, but detecting whether a page exists seems harder. Is there some way to do this in JSF or via the EL, or should this be done through a managed bean? Maybe it could be useful to write a custom tag for this, but I'm not sure how much work this entails.

我确实找到了此相关问题,但这仅在我不想注入纯HTML内容的情况下才有用.我想包含具有JSF内容的页面,以便它们实际上由JSF servlet处理和呈现.

I did find this related question, but that only seems useful if I wan't to inject pure HTML content. I'd like to include pages with JSF content so they're actually handled and rendered by the JSF servlet.

推荐答案

以下是我为您解决的问题的解决方案.它很笨重,但是完成了,提供了很多信息,据我所知,它是完整的.有了它,您将能够基于当前语言从一系列带有语言后缀的视图中获取必要的视图.

Below is my solution to your problem. It is bulky, but finished, informative and, as far as I can see, complete. With it you shall be able to incude the necessary view from a family of language-suffixed views, basing on the current language.

我对您的设置的假设

  1. 您正在处理描述语言的语言环境,即采用Locale.ENGLISH格式;
  2. 您选择的语言存储在会话范围的bean中;
  3. 您以以下格式保留国际化页面:page.xhtmlpage_en.xhtmlpage_fr.xhtml等;
  4. 默认语言为英语;
  5. 您的FacesServlet被映射到*.xhtml.
  1. You are dealing with locales that describe languages, i.e. are in Locale.ENGLISH format;
  2. You selected language is stored in a session scoped bean;
  3. You keep the internationalized pages in the following format: page.xhtml, page_en.xhtml, page_fr.xhtml, etc;
  4. Default language is English;
  5. Your FacesServlet is mapped to *.xhtml.

我的解决方案的标准设置

会话作用域bean,其中包含可用的语言和用户选择:

Standard settings for my solution

Session scoped bean, holding available languages and user selection:

@ManagedBean
@SessionScoped
public class LanguageBean implements Serializable {

    private List<Locale> languages;//getter
    private Locale selectedLanguage;//getter + setter

    public LanguageBean() {
        languages = new ArrayList<Locale>();
        languages.add(Locale.ENGLISH);
        languages.add(Locale.FRENCH);
        languages.add(Locale.GERMAN);
        selectedLanguage = Locale.ENGLISH;
    }

    public Locale findLocale(String value) {
        for(Locale locale : languages) {
            if(locale.getLanguage().equals(new Locale(value).getLanguage())) {
                return locale;
            }
        }
        return null;
    }

    public void languageChanged(ValueChangeEvent e){
        FacesContext.getCurrentInstance().getViewRoot().setLocale(selectedLanguage);
    }

}

语言环境的转换器:

@ManagedBean
@RequestScoped
public class LocaleConverter implements Converter {

    @ManagedProperty("#{languageBean}")
    private LanguageBean languageBean;//setter

    public LocaleConverter() {   }

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if(value == null || value.equals("")) {
            return null;
        }
        Locale locale = languageBean.findLocale(value);
        if(locale == null) {
            throw new ConverterException(new FacesMessage("Locale not supported: " + value));
        }
        return locale;
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (!(value instanceof Locale) || (value == null)) {
            return null;
        }
        return ((Locale)value).getLanguage();
    }

}

主视图(main.xhtml),具有指向国际化页面的链接,并能够通过下拉框更改当前语言:

Main view (main.xhtml) with links to internationalized pages and with ability to change current language via a dropdown box:

<f:view locale="#{languageBean.selectedLanguage}">
    <h:head>
        <title>Links to internationalized pages</title>
    </h:head>
    <h:body>
        <h:form>
            <h:selectOneMenu converter="#{localeConverter}" value="#{languageBean.selectedLanguage}" valueChangeListener="#{languageBean.languageChanged}" onchange="submit()">
                <f:selectItems value="#{languageBean.languages}"/>
            </h:selectOneMenu>
        </h:form>
        <br/>
        <h:link value="Show me internationalized page (single)" outcome="/international/page-single"/>
        <br/>
        <h:link value="Show me internationalized page (multiple)" outcome="/international/page-multiple"/>
    </h:body>
</f:view>

基于多页的解决方案-每种语言一页

通过添加_lang后缀(page-multiple.xhtml)国际化的基础页面

Solution based on multiple pages - one per language

Base page that is internationalized by adding _lang suffixes (page-multiple.xhtml)

<f:metadata>
    <f:event type="preRenderView" listener="#{pageLoader.loadPage}"/>
</f:metadata>

国际化页面:

对于英语(page-multiple_en.xhtml):

<h:head>
    <title>Hello - English</title>
</h:head>
<h:body>
    Internationalized page - English
</h:body>

对于法语(page-multiple_fr.xhtml):

<h:head>
    <title>Hello - Français</title>
</h:head>
<h:body>
    Page internationalisé - Français
</h:body>

对于德语(无视图,缺少文件的模拟).

For German (no view, simulation of missing file).

执行重定向的托管bean:

Managed bean that performs redirection:

@ManagedBean
@RequestScoped
public class PageLoader {

    @ManagedProperty("#{languageBean}")
    private LanguageBean languageBean;//setter

    public PageLoader() {   }

    public void loadPage() throws IOException {
        Locale locale = languageBean.getSelectedLanguage();
        FacesContext context = FacesContext.getCurrentInstance();
        ExternalContext external = context.getExternalContext();
        String currentPath = context.getViewRoot().getViewId();
        String resource = currentPath.replace(".xhtml", "_" + locale.toString() + ".xhtml");
        if(external.getResource(resource) == null) {
            resource = currentPath.replace(".xhtml", "_en.xhtml");
        }
        String redirectedResource = external.getRequestContextPath() + resource.replace(".xhtml", ".jsf");
        external.redirect(redirectedResource);
    }

}

每次请求视图page-multiple.xhtml时,如果未找到目标语言的视图,则将其重定向到带语言后缀的视图,或重定向到英语视图.当前语言取自会话作用域Bean,所有视图必须位于服务器上的同一文件夹中.当然,可以根据视图参数中定义的语言来重做.目标页面可以使用合成.可以在没有后缀的视图中提供默认数据,并且preRenderView侦听器不执行重定向.

Every time view page-multiple.xhtml is requested it is redirected to the language-suffixed views, or to the english view, if target language's view is not found. Current language is taken from session scoped bean, all views must be located in the same folder on server. Of course, that can be redone, basing on language defined in a view parameter instead. The target pages can use a composition. Default data can be served in a non-suffixed view with preRenderView listener not performing redirection.

请注意,我的(三个)视图存储在网页的international/文件夹中.

As a remark, my (three) views were stored in international/ folder of web pages.

尽管您的问题应该由以前的设置解决,但我想到了另一个想法,我将在下面进行描述.

Though your problem should be covered by the former setup, another idea came to mind, that I will describe below.

有时候,创建与支持的语言一样多的视图(重定向为+1)可能更容易,而是创建一个将基于当前选择的语言有条件地呈现其输出的视图.

Sometimes it might be easier not to create as many views (+1 for redirection) as there are supported languages, but instead create a single view that will conditionally render its output, basing on the currently selected language.

视图(page-single.xhtml,也位于服务器的同一文件夹中)如下所示:

The view (page-single.xhtml, located in the same folder on server as well) could look like:

<ui:param name="lang" value="#{languageBean.selectedLanguage}"/>
<ui:fragment rendered="#{lang == 'en'}">
    <h:head>
        <title>Hello - English</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF8" />
    </h:head>
    <h:body>
        Internationalized page - English
    </h:body>
</ui:fragment>
<ui:fragment rendered="#{lang == 'fr'}">
    <h:head>
        <title>Hello - Français</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF8" />
    </h:head>
    <h:body>
        Page internationalisé - Français
    </h:body>
</ui:fragment>
<ui:fragment rendered="#{(lang ne 'en') and (lang ne 'fr')}">
    <h:head>
        <title>Hello - Default</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF8" />
    </h:head>
    <h:body>
        Internationalized page - Default
    </h:body>
</ui:fragment>

通过此视图,您可以在其中指定所有数据,有条件地仅呈现所需语言所需的数据或默认数据.

With this view you specify all data inside, conditionally rendering only the data demanded by the needed language, or default data.

资源解析器将基于视图的当前语言环境包括所需的文件.

Resourse resolver will include the needed file, basing on the current locale of the view.

资源解析器:

public class InternalizationResourceResolver extends ResourceResolver {

    private String baseLanguage;
    private String delimiter;
    private ResourceResolver parent;

    public InternalizationResourceResolver(ResourceResolver parent) {
        this.parent = parent;
        this.baseLanguage = "en";
        this.delimiter = "_";
    }

    @Override
    public URL resolveUrl(String path) {
        URL url = parent.resolveUrl(path);
        if(url == null) {
            if(path.startsWith("//ml")) {
                path = path.substring(4);
                Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
                URL urlInt = parent.resolveUrl(path.replace(".xhtml", delimiter + locale.toString() + ".xhtml"));
                if(urlInt == null) {
                    URL urlBaseInt = parent.resolveUrl(path.replace(".xhtml", delimiter + baseLanguage + ".xhtml"));
                    if(urlBaseInt != null) {
                        url = urlBaseInt;
                    }
                } else {
                    url = urlInt;
                }
            }
        }
        return url;
    }

}

web.xml中启用解析器:

<context-param>
    <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
    <param-value>i18n.InternalizationResourceResolver</param-value>
</context-param>

通过此设置,可以渲染以下视图:

With this setup it is possible to render the following view:

使用<ui:include>的视图,其中将使用创建的//ml/前缀定义国际化包含的内容:

View which uses <ui:include>, in which internatiaonalised includes will be defined with the created //ml/ prefix:

<f:view locale="#{languageBean.selectedLanguage}">
    <h:head>
    </h:head>
    <h:body>
        <ui:include src="//ml/international/page-include.xhtml" />
    </h:body>
</f:view>

将没有page-include.xhtml,但是将有每种语言的视图,例如:

There will be no page-include.xhtml, but there will be per language views, like:

page-include_en.xhtml:

<h:outputText value="Welcome" />

page-include_fr.xhtml:

<h:outputText value="Bienvenue" />

这样,解析器将根据当前语言环境选择正确的国际化包含视图.

This way, the resolver will choose the right internationalized included view, basing on the current locale.

这篇关于每个区域设置JSF 2中的不同面(用于模板)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆