7

あるテンプレート<ui:insert name="help_contents" />とを定義するページがあります。定義<ui:define name="help_contents><!-- actual contents --></ui:define>のコンテンツはJSFベース(プレーンhtml / xhtmlだけでなく)であり、facesサーブレットによって処理され、ロケールによって異なります。ただし、リソースバンドルではこれを行いたくありません。これは、プロパティごとに大量のテキストが必要であり、テキストが散在するすべてのコンポーネントごとに分割する必要があるためです。つまり、ロケールごとにファセットが必要であり、アクティブなロケールに基づいて適切なファセットを含めます。

それが基本的に問題です。検索している他の人のために、以下のコンテキストをスキップしてください。私が何を意味するのかをすでに理解している場合はスキップしてください。

JSF 2での国際化は、ほとんどの場合、非常に簡単です。1つ以上のリソースバンドルを作成し、faces-config.xmlでそれらを宣言すると、プロパティを使用する準備が整います。しかし、そのようなプロパティファイルは、短いラベルテキスト、列ヘッダー、おそらくいくつかのパラメータを含む小さなメッセージにのみ適していると思います...テキストの大部分に関しては、扱いにくいように見えます。特に、テキストにXHTMLタグまたはJSFコンポーネントを散在させる必要がある場合は、テキストを分割しすぎる必要があります。

現在、私はJSF 2を使用するWebアプリケーションに取り組んでおり、PrimeFacesをコンポーネントバンドルとして使用しています。これは、通常の意味でi18nのリソースバンドルを使用します。ただし、さまざまなビューにはヘルプページが必要です。これらのヘルプページでもJSF/PrimeFacesコンポーネントを使用して、入力されたテーブルまたはダイアログの例がビュー自体と同じように見えるようにしたいと思います。

ただし、ロケールに基づいて構成コンテンツを含めることは、私が思っていたほど簡単ではないようです。_enや_frなどのロケールサフィックスが付いたXHTMLページ(フェイスレット)を作成し、アクティブなロケールに基づいて適切なページを選択したいと思います。そのようなページが存在しない場合は、デフォルトで_enページ(または英語のコンテンツのみを含むサフィックスのないページ)に設定する必要があります。facescontextからロケール文字列を取得することは問題ではありませんが、ページが存在するかどうかを検出するのは難しいようです。JSFまたはELを介してこれを行う方法はありますか、それともマネージドBeanを介して行う必要がありますか?このためのカスタムタグを作成すると便利かもしれませんが、これにどれだけの作業が必要かはわかりません。

私はこの関連する質問を見つけましたが、それは純粋なHTMLコンテンツを挿入したくない場合にのみ役立つようです。JSFコンテンツを含むページを含めて、実際にJSFサーブレットによって処理およびレンダリングされるようにしたいと思います。

4

3 に答える 3

3

以下は、あなたの問題に対する私の解決策です。かさばりますが、完成しており、有益であり、私が見る限り完全です。それを使用すると、現在の言語に基づいて、言語接尾部のビューのファミリーから必要なビューを含めることができます。

あなたのセットアップに関する私の仮定

  1. 言語を記述するロケールを扱っています。つまり、Locale.ENGLISH形式があります。
  2. 選択した言語は、セッション スコープ Bean に格納されます。
  3. 国際化されたページはpage.xhtmlpage_en.xhtmlpage_fr.xhtml、 などの形式で保持します。
  4. デフォルトの言語は英語です。
  5. あなた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>

複数のページに基づくソリューション - 言語ごとに 1 つ

_lang サフィックスを追加して国際化されたベース ページ ( 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>

ドイツ語の場合 (ビューなし、欠落ファイルのシミュレーション)。

リダイレクトを実行するマネージド 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リスナーがリダイレクトを実行しないサフィックスのないビューで提供できます。

備考として、私の(3つの)ビューはinternational/Webページのフォルダーに保存されていました。

すべての言語の単一ページに基づくソリューション

あなたの問題は以前のセットアップでカバーされるはずですが、別のアイデアが頭に浮かびました。それについては以下で説明します。

サポートされている言語と同じ数のビュー (リダイレクトの場合は +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" />

このようにして、リゾルバーは現在のロケールに基づいて、適切な国際化されたインクルード ビューを選択します。

于 2013-02-27T19:54:00.300 に答える
2

たとえば、標準のファサードだけになる複合コンポーネントを定義できますui:include

リソース/myComponents/localeInclude.xhtml:

<cc:interface>
  <cc:attribute name="src" required="true" type="java.lang.String"/>
</cc:interface>

<cc:implementation>
  <ui:include src="#{myResolver.resolve(cc.attrs.src)}">
    <cc:insertChildren/>
  </ui:inclue>
</cc:implementation>

メソッドで完全にステートレスでmyResolverあるため、名前付きマネージド Bean を作成します。@ApplicationScopedresolve()

public String resolve(String src) {
  String srcWithoutExt = src.replace(".xhtml", "");
  FacesContext facesContext = FacesContext.getCurrentInstance();
  ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext();
  Locale locale = facesContext.getViewRoot().getLocale();
  String localizedSrc = srcWithoutExt + "_" + locale.getLanguage();
  URL url = null;
  if (src.startsWith("/")) {
    url = facesContext.getExternalContext().getResource(localizedSrc + ".xhtml");
  } else {
    try {
      url = new URL((HttpServletRequest) request).getRequestURL(), localizedSrc + ".xhtml");
    } catch (Exception e) { /* Doesn't exist */ }
  }
  if (url != null) {
    return localizedSrc + ".xhtml";
  } else {
    return src;
  }
}

この場合、srcロケール拡張なしでページに配置し、メソッドにこれを解決させます。

<my:localeInclude src="myPage.xhtml/>

ui:paramお子様も含めましたので、オリジナルと同様にお渡しできます。

さらに、(部分だけでなく) ロケールに従ってページ全体を解決したくない人にとっては、使いやすいFilterです。メソッドではdoFilter()、そのリソースが存在するかどうかを確認し、そうでない場合はリクエストを別のページに転送できます。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {

  if (request.getServletContext().getResource(request.getRequestURI()) == null) {
    // Here your page doesn't exist so forward user somewhere else...
  }
}

必要に応じて、このマッピングを構成Filterします。

于 2013-02-27T12:52:11.737 に答える
1

このリンク@SOから、コンテンツを動的に含めることができます(チェックされた回答を確認してください)。バッキングファイルでは、ファイル名を適切に設定できるフックがあれば、それでうまくいくと思います。

これについてはよくわかりません。引数、つまりELのメソッドへの部分パスを渡すことができるかどうかを確認できます。残りは、フルパスの作成、現在のロケールの追加、ファイルの確認など、メソッド内で処理できます。

お役に立てれば。

更新(コメントに答えるため):

はい、そうです。リンクJSF2fu、パート2:テンプレートおよび複合コンポーネントをご覧ください。

于 2013-02-27T12:58:21.447 に答える