以下是我对您的问题的解决方案。它体积庞大,但已完成,内容丰富,据我所知,完整。有了它,您将能够根据当前语言从一组以语言为后缀的视图中包含必要的视图。
我对您的设置的假设
- 您正在处理描述语言的语言环境,即
Locale.ENGLISH
格式;
- 您选择的语言存储在会话范围的 bean 中;
- 您将国际化页面保存为以下格式:
page.xhtml
、、、page_en.xhtml
等page_fr.xhtml
;
- 默认语言为英语;
- 您
FacesServlet
的映射到*.xhtml
.
我的解决方案的标准设置
会话范围的 bean,包含可用的语言和用户选择:
@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
) 带有指向国际化页面的链接,并且能够通过下拉框更改当前语言:
<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>
基于多个页面的解决方案 - 每种语言一个
page-multiple.xhtml
通过添加 _lang 后缀 ( )国际化的基页
<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>
德语(无视图,模拟丢失文件)。
执行重定向的托管 bean:
@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
侦听器不执行重定向。
作为备注,我的(三个)视图存储在international/
网页文件夹中。
基于所有语言的单一页面的解决方案
尽管您的问题应该由以前的设置解决,但我想到了另一个想法,我将在下面描述。
有时,不创建与支持的语言一样多的视图(+1 用于重定向)可能更容易,而是创建一个单一的视图,根据当前选择的语言有条件地呈现其输出。
视图(page-single.xhtml
也位于服务器上的同一文件夹中)可能如下所示:
<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>
使用此视图,您可以指定内部的所有数据,有条件地仅呈现所需语言所需的数据或默认数据。
提供自定义资源解析器
资源解析器将根据视图的当前语言环境包含所需的文件。
资源解析器:
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>
使用此设置,可以呈现以下视图:
查看哪些使用<ui:include>
,其中国际化包含将使用创建的//ml/
前缀定义:
<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
,但会有每种语言的视图,例如:
page-include_en.xhtml
:
<h:outputText value="Welcome" />
page-include_fr.xhtml
:
<h:outputText value="Bienvenue" />
这样,解析器将根据当前语言环境选择正确的国际化包含视图。