Best Practices for PrettyFaces and Multiple language sites

Splash Forums PrettyFaces Users Best Practices for PrettyFaces and Multiple language sites

This topic contains 18 replies, has 6 voices, and was last updated by  Christian Kaltepoth 6 years, 6 months ago.

Viewing 15 posts - 1 through 15 (of 19 total)
  • Author
    Posts
  • #17873

    icordoba
    Participant

    Hi there,

    first of all let me say I’m simply impressed on how simply I’ve managed to get a SEO friendly JSF2 site I already had running, thanks to Pretty Faces. I can’t imagine how this could be simpler…

    And now a problem I still need to solve: I a custom content manage system I’ve developed for JSF2, I use the same JSF page to show articles. The system allows to specify language for content categories and the articles can be “linked” to the equivalent translated article. System uses the session locale to show the article in the current language.

    Obviously this was a bad decision as this articles are not SEO urls. Same URL shows different content depending on session locale. i want to forget about this and now I’ve found pretty faces, rewrite the CDI managed beans that show articles.

    Is there any recommended approach / best practice to get a multilingual site using JSF(2) & Pretty Faces while reusing the same template (facelet):

    /en/article/MyArticleId

    /es/article/MyArticleId : same article but in spanish (“es” locale)

    thanks,

    Ignacio

    #20687

    Hey Ignacio,

    nice to hear that you like PrettyFaces! :-)

    Regarding the multi language URLs you could try the new inheritance feature for URL mappings introduced in PrettyFaces 3.2.0. This way you have to declare only one mapping containing the language code and let all other mappings extend this one.

    I didn’t try something like this myself but I think it should work.

    Just take a look at the documentation:

    http://ocpsoft.com/docs/prettyfaces/3.2.0/en-US/html/Configuration.html#config.mapping.parents

    I hope this helps.

    Christian

    #20688

    icordoba
    Participant

    Thanks for your reply Christian,

    I’m trying to set it up first for the homepage:

    URI http://www.mysite.com/ should show the spanish version

    http://www.mysite.com/en should show the same index.jsf but first invoke #{userSessionView.language} named bean property.

    <url-mapping id="enHome"> <!--parentId="home" -->

    <pattern value="/#{ /en/ language }" />

    <query-param name="language">#{userSessionView.language}</query-param>

    <view-id>/index.jsf</view-id>

    </url-mapping>

    The /en/ pattern is recognized and the page shows ok. Shouldn’t this value be transfered as a request parameter “language” with previous code? (Probably I misunderstood the pattern tag doc.

    If I invoke the URL

    http://www.mysite.com/en?language=en

    the setter is invoked and english locale is shown.

    My problem is the just make prettyfaces recognize the /en constant and pass it to the bean via setLanguage.

    Thanks for any ideas,

    Ignacio

    #20689

    Try:

    <pattern value="/#{ /en/ language : userSessionView.language }" />

    It should set the language in the bean just like query-parameter does.

    #20690

    icordoba
    Participant

    Hello Lincoln. Thanks for your reply. Just tested and it works ok. I have solved my home page problem. Let me ask for help in another question.

    In english version of the home page I want to link to english articles, which should be accessed /en/article/my+article+title URI so I’ve tried:

    <url-mapping id="enViewCMArticle" parentId="enHome" >

    <pattern value="/article/#{articleTitle}" />

    <view-id>/contentmanager/article.jsf</view-id>

    </url-mapping>

    This works ok inheriting from the enHome /en mapping if I write the URL in the browser (http://mysite.com/en/article/My+Article) or if I reference it from a direct a href tag but I have a problem referencing this pretty URL from a h:link tag:

    As I want to use the same xhtml/jsf file: /contentmanager/article.jsf I’ve included in faces-config.xml:

    <navigation-rule>

    <navigation-case>

    <from-outcome>/en/contentmanager/article</from-outcome>

    <to-view-id>/contentmanager/article.xhtml</to-view-id>

    </navigation-case>

    </navigation-rule>

    then I try

    <h:link outcome=”/en/contentmanager/article” value=”#{msgs.more}…”><f:param name=”articleTitle” value=”My Title”/></h:link>`

    This should create /en/contentmanager/article outcome as it is referenced in the <h:link> tag, but pretty faces makes h:link render the link to /article/My+Title and not /en/article/My+Title; it fallbacks to the simpler:

    <url-mapping id="viewCMArticle">

    <pattern value="/article/#{articleTitle}" />

    <view-id>/contentmanager/article.jsf</view-id>

    </url-mapping>

    pretty mapping and ignores the /en/ mapping

    Any ideas?

    Thanks again,

    Ignacio

    #20691

    Hey Ignacio,

    you won’t need to add any navigation case to your faces-config.xml. Take a look at this chapter of the documentation:

    http://ocpsoft.com/docs/prettyfaces/3.2.0/en-US/html/components.html

    You can also use the pretty:link component for this:

    <pretty:link mappingId="enViewCMArticle">
    <f:param value="en"/>
    <f:param value="my-article"/>
    Click me!
    </pretty:link>

    #20692

    icordoba
    Participant

    Hello Christian,

    thanks to your pretty:link example I’ve noticed I wasn’t adding the language as a parameter to h:link; I thought “en” could be set in a navigation rule and so make the links in the english home page be the same as in the main language. I’ll make the modifications to the links adding the parameter.

    I’ve used pretty:link and it is also working great but, as I am using JSF2, I’ll try to use h:link whenever possible.

    Now a new (small) issue: This is working great but as now I need to add the “language” parameter to the h:link for the english version I need to have multiple home sites, index.xhml for the standard home page (without any “es”, spanish, language prefix) and index_en.xhtml for the english home page, which adds the language parameter to the <h:link> tags.

    To solve this I’ve tried to use the same index.xhtml and use “rendered” atribute in h:links:

    <f:param rendered=”#{userSessionView.language!=’es’}” name=”language” value=”#{userSessionView.language}”/>

    Which works ok for english (en), but if language is the “default” one in my site (es) I still get the link rendered to:

    http://mysite.com/es/A/My+Article+Title

    while it should be

    http://mysite.com/A/My+Article+Title

    Mapping:

    <url-mapping id=”home”>

    <pattern value=”/” />

    <view-id>/index.jsf</view-id>

    </url-mapping>

    (no “language” parameter)

    I’ve also tried pretty:link

    <pretty:link mappingId=”#{userSessionView.language}ViewCMArticle”><f:param rendered=”#{userSessionView.language!=’es’}” value=”#{userSessionView.language}”/><f:param value=”My+title”/>My title…</pretty:link>

    Which gets the right mappingId, but the problem seems to be in f:param, which doesn’t accept “rendered” attribute (is always rendered, and so I get a pretty error because this mapping expects just one parameter but get two, language and article title.

    I know this is not a big deal now I’ve solved most of the problem. I can forget about “default” language and always use the same mapping /es/article+title, /en/article+title, … so I’lll stick to this as a last resort.

    I hope I can get a “good practice” strategy for JSF2 SEO multilanguage sites (I’lll write a blog entry once I put this to work); a big problem for JSF2 users is that all online info about JSF and i18n starts from the assumption that the language is stored in the session and so user can change the language in the same xhtml page. That is fine for “applications” but is a big problem when you try to reuse JSF xhtml code for SEO friendly content management. Once I got to know about pretty faces I’ve had to rewrite some code to forget about storing language in session.

    Thanks a lot again and thanks a lot OCPSoft for creating PrettyFaces,

    Ignacio

    #20693

    Hey Ignacio,

    I think it is not a very good idea to implement i18n by using different xhtml files. You should really use the standard JSF way for i18n.

    What about doing something like this. Define a parent mapping containing the language as a path parameter that is injected into a request scoped bean:

    <url-mapping id="parent">
    <pattern value="/#{localeBean.language}" />
    ...
    </url-mapping>

    The bean:

    @Named
    @RequestScoped
    public class LocaleBean {

    private String language;

    public String getLanguage() {
    return language;
    }

    public String setLanguage(String language) {
    this.language = language;
    }

    public getLocale() {
    return new Locale(language);
    }

    }

    Then use the getLocale() method of the bean and use it to set the locale of your view:

    <f:view locale=#{localeBean.locale}>

    <!-- your page -->

    </f:view>

    This way the view will have the correct locale set and you will be able to use the standard JSF 118n techniques for loading localized messages from a MessageBundle.

    I didn’t test this in details but thats how I would try it! :-)

    #20694

    icordoba
    Participant

    Hello,

    The only reason for using a different xhtml was because I haven’t been able to find a way to map the pretty-config file so that spanish language is in / and english is in /en while having one unique h:link group for both (I mean, the links in the home spanish page don’t have the “language” parameter and the other languages in the site do have them). I anyway have decided to put spanish content also in a subdir (es) so this way I can use just one home page, so no big deal.

    About your approach, that is exactly what I’ve done (the LocaleBean, which I named UserSessionView is @RequestScoped). What I meant is that most online tutorials about this seem to understand that locale info must be SessionScoped and not RequestScoped and so I had to recode my bean so the locale info is stored in the request.

    Thanks again for your time. I now have my new site working with SEO i18n friendly JSF2 thanks to PrettyFaces.

    #20695

    @icordoba,

    That’s excellent! Would you perhaps be interested in writing a blog entry about your experience? I’d love to feature you as a guest-author on OCPSoft.com! What you just did with PrettyFaces is really important, and I think other people would like to see how it is done.

    Let me know!

    ~Lincoln

    #20696

    icordoba
    Participant

    Hello again,

    of course I will write that blog entry and both in english and spanish, please give me some days to write it; probably you’d like to review it as english is not my first language. I’ll also post a link to this thread on twitter on the fly.

    thanks again,

    Ignacio

    #20697

    Awesome! Sure, I’d love to review it. If it’s not too much, maybe you could even include a demo-application that people could look at! You can email me at lincoln@ocpsoft.com

    ~Lincoln

    #20698

    domdorn
    Participant

    to also add something to this topic:

    I’ve solved this some time ago with a set of files.

    I just put them into this gist:

    https://gist.github.com/a805f5c4be59c70ab9b3

    What I would suggest:

    do your mappings like

    <url-mapping id=”home”>

    <pattern value=”/#{language}/somepage/#{someBean.value}/vblabla” />

    <view-id>/someview.jsf</view-id>

    </url-mapping>

    and then adjust the LangFilter (which is called after the pretty filter) to read out the param “language” from the request object and set the locale accordingly. In the example code, I did it using cookies, but its quite easy to do with request params.

    Your locale is now correctly set on the HttpServletRequest and in JSF, so you can facilitate the JSF built-in ways of doing i18n etc.

    hth,

    dominik

    #20699

    icordoba
    Participant

    Hi domdorn,

    thanks for that approach. I think that you really don’t need that filter. I use a named class (userSessionView) which has a setLanguage() method. In content pages I want to use with PrettyFaces I do:

    /#{ /en/ language : userSessionView.language }

    and so the language is set for the request. The goal is the same as with your filter, but with one difference. i don’t want to use permanent links in all pages. Just the ones that show content (doc pages, blog entries, home, …) but not, for example, in the control panel of the site, which is authentified and i want it to work as regular JSF application (I want the user to set the locale in his preferences and have it the “sessionScoped” approach). With the filter, you get every request to go through it, with the dynamic pattern approach, you only get the language set in the pages you want.

    By the way, I have a problem with the mapping. I’m doing (some kind of “abstract mappings):

    <url-mapping id="en">

    <pattern value="/#{ /en/ language : userSessionView.language }" />

    </url-mapping>

    <url-mapping id="es">

    <pattern value="/#{ /es/ language : userSessionView.language }" />

    </url-mapping>

    And then, for example:

    <url-mapping id="enViewCMCategory" parentId="en">

    <pattern value="/C/#{categoryName}" />

    <view-id>/contentmanager/category.jsf</view-id>

    <url-mapping id="esViewCMCategory" parentId="es">

    <pattern value="/C/#{categoryName}" />

    <view-id>/contentmanager/category.jsf</view-id>

    </url-mapping>

    This will make only the first one be recognized (in this case, /en/C/My+Category+Name)

    I guess this is because even though they inherit from different parent mappings, they both have the same mapping (/C/CategoryName)

    Is this the expected behaviour?

    I have the site working but I can use inheritance only in the first language and “repeat” the wildcard mapping on the other one. i.e.: I have to do, for /es:

    <url-mapping id="esViewCMCategory">

    <pattern value="/#{ /es/ language : userSessionView.language }/C/#{categoryName}" />

    <view-id>/contentmanager/category.jsf</view-id>

    </url-mapping>

    Regards,

    Ignacio

    #20700

    At first glance this sounds like a bug. Do you think you would be able to debug and take a look at what is happening in PrettyFilter when the request comes in to the filter?

Viewing 15 posts - 1 through 15 (of 19 total)

You must be logged in to reply to this topic.

Comments are closed.