11

JPA/EJB/JSF を使用して Java EE6 プロジェクトに取り組んでおり、エンティティの複数言語サポートの設計に問題があります。関連する 3 つのエンティティがあります。

Language (ID あり)
Competence (ID あり)
CompetenceName (Competence リファレンス、Language リファレンス、および文字列を含む)

Competence には、Map で実装された CompetenceName への 1 対多の参照があり、Competence の名前が存在する言語ごとに 1 つのオブジェクトが含まれています。コンピテンスは動的に作成されるため、その名前はリソース バンドルに存在できないことに注意してください。

コンピテンスを Web ページに一覧表示するときに、現在ログインしているユーザーの言語で表示するようにしたいのですが、これはセッション スコープのマネージド 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";
}

このソリューションは、エンティティがコントローラー レイヤーに依存しており、その名前が必要になるたびにセッション コントローラーを取得する必要があるため、非常に粗雑に感じられます。より MVC に準拠したソリューションは、Language パラメーターを使用することですが、これは、JSF からのすべての呼び出しに、セッション スコープのマネージド Bean からフェッチされた言語を含める必要があることを意味し、これも適切なソリューションとは思えません。

この問題に対する考えや設計パターンはありますか?

4

2 に答える 2

21

国際化/ローカリゼーションは、できればビュー側で完全に行う必要があります。モデルはこれを認識すべきではありません。

JSF では、XHTML の<resource-bundle>エントリfaces-config.xmlと は、ファイルのベース名ではなく完全なクラスを<f:loadBundle>指すこともできます。Java SE 6では、バンドルのロードとフィルを完全に制御できる新しい API が利用可能になりました。ResourceBundle.propertiesResourceBundle.Control

ResourceBundleこれらの事実を知っていれば、カスタムのandを使用して DB からバンドル メッセージをロードできるはずControlです。キックオフの例を次に示します。

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}" />
于 2010-12-21T15:04:52.080 に答える
0

言語固有の部分をモデルからリソース バンドルに移動します。能力、言語、ユーザーをモデル化するだけです。ユーザーがページを要求した場合、その能力を表示し、リソース バンドルから言語固有の能力 (CompetenceName) を検索します。

始めるためのサンプル コードを検索したところ、これが見つかりまし。リスト 19.16 customerDetails.jsp を参照してください。

何かのようなもの:

<fmt:message key="${competence}" />
于 2010-12-21T13:39:05.453 に答える