Change locale in JSF and reload the data from database - jsf-2

In our application we have a requirement to change the locale on the fly by clicking on button. I have referred the below link and was able to reload labels which are coming from the resource bundle with different locale.
Localization in JSF, how to remember selected locale per session instead of per request/view
But my problem is that we have to call SP to get the data in different language according to locale. So the data is not getting loaded from database when i am reloading my page. I have added my code below which i have written so far.
In XHTML Code:
<p:commandButton id="langbutton" value="#{language.lang}"
actionListener="#{language.langChanged}"
ajax="false" styleClass="loginlangbutton"/>
In Bean Class:
public String langChanged() {
FacesContext context = FacesContext.getCurrentInstance();
if (user != null && user.getLang().equalsIgnoreCase("E")) {
user.setLang("F");
lang = "English";
FacesContext.getCurrentInstance().getViewRoot()
.setLocale(Locale.FRENCH);
this.localeCode = Locale.FRENCH;
} else {
user.setLang("E");
lang = "Français";
FacesContext.getCurrentInstance().getViewRoot()
.setLocale(Locale.ENGLISH);
this.localeCode = Locale.ENGLISH;
}
UserBean uBean = (UserBean) context.getApplication()
.evaluateExpressionGet(context, "#{user}",
UserBean.class);
uBean.setLoginDtl(user);
return FacesContext.getCurrentInstance().getViewRoot().getViewId()
+ "?faces-redirect=true";
}

Related

JSF2 internationalization example

Taking as a start point the JSF2 internationalization example of this link:
http://www.mkyong.com/jsf2/jsf-2-internationalization-example/
I want this example to display the language of the combos in the actual language selected.
Could somebody point me how this can be done?
Thanks!
It's just a matter of setting the proper labels in the static variable countries:
static {
countries = new LinkedHashMap<String,Object>();
countries.put("English", Locale.ENGLISH); //label, value
countries.put("Deutsch", Locale.GERMAN);
countries.put("Français", Locale.FRENCH);
// ... fill in with additional languages/locales as needed
}
You can get a bigger list of language names in their original language here: http://www.omniglot.com/language/names.htm
UPDATE: According to OP's comment, he needs the language names translated to every language. For that, one could come up with a solution simply making a map of language maps (that's quite a few maps), like this:
// set a default value for localeCode
private String localeCode = Locale.ENGLISH.toString();
// ...
static {
countries = new LinkedHashMap<Object, <String,Object>>();
englishCountries = new LinkedHashMap<String,Object>();
englishCountries.put("English", Locale.ENGLISH); //label, value
englishCountries.put("German", Locale.GERMAN);
englishCountries.put("French", Locale.FRENCH);
countries.put(Locale.ENGLISH, englishCountries);
germanCountries = new LinkedHashMap<String,Object>();
germanCountries.put("Englisch", Locale.ENGLISH);
germanCountries.put("Deutsch", Locale.GERMAN);
germanCountries.put("Französisch", Locale.FRENCH);
countries.put(Locale.GERMAN, germanCountries);
frenchCountries = new LinkedHashMap<String,Object>();
frenchCountries.put("Anglais", Locale.ENGLISH);
frenchCountries.put("Allemand", Locale.GERMAN);
frenchCountries.put("Français", Locale.FRENCH);
countries.put(Locale.FRENCH, frenchCountries);
// ... fill in with additional languages/locales as needed
}
public Map<Object, <String,Object>> getCountriesInMap() {
return countries;
}
// adapted value change listener from original:
public void countryLocaleCodeChanged(ValueChangeEvent e){
String newLocaleValue = e.getNewValue().toString();
//loop country map to compare the locale code
for (Object key : countries.keySet()) {
if (key.toString().equals(newLocaleValue)) {
FacesContext.getCurrentInstance().getViewRoot()
.setLocale((Locale) key);
}
}
}
and then you would choose the proper map to be used for the selectItems, with something like this:
<h:selectOneMenu value="#{language.localeCode}" onchange="submit()"
valueChangeListener="#{language.countryLocaleCodeChanged}">
<f:selectItems value="#{language.countriesInMap[language.localeCode]}" />
</h:selectOneMenu>
Note: Don't forget to set a default value for language.localeCode, or the dropdown won't show any options
Please note, though, that this is probably NOT a good idea for usability, since a user who chooses a foreign language by mistake may have a hard time to get back to a known language for her (that's why it's a good practice to make the lists have the language names in each own language).

Get CultureInfo from current visitor and setting resources based on that?

How can I (in ASP .NET MVC) get the CultureInfo of the current visitor (based on his/her browser languages)?
I have no idea where to start. I tried looking into the "Accept-Languages" header sent by the browser. But is that the best way of doing it?
Request.UserLanguages is the property you're looking for. Just keep in mind that this array may contain arbitrary (even non-exsitent) languages as set by request headers.
UPDATE
Example:
// Get Browser languages.
var userLanguages = Request.UserLanguages;
CultureInfo ci;
if (userLanguages.Count() > 0)
{
try
{
ci = new CultureInfo(userLanguages[0]);
}
catch(CultureNotFoundException)
{
ci = CultureInfo.InvariantCulture;
}
}
else
{
ci = CultureInfo.InvariantCulture;
}
// Here CultureInfo should already be set to either user's prefereable language
// or to InvariantCulture if user transmitted invalid culture ID
Asp.Net Core version: using RequestLocalization ie the culture is retrieved form the HTTP Request.
in Startup.cs - Configure
app.UseRequestLocalization();
Then in your Controller/Razor Page.cs
var locale = Request.HttpContext.Features.Get<IRequestCultureFeature>();
var BrowserCulture = locale.RequestCulture.UICulture.ToString();
You can use code similar to the following to get various details from your user (including languages):
MembershipUser user = Membership.GetUser(model.UserName);
string browser = HttpContext.Request.Browser.Browser;
string version = HttpContext.Request.Browser.Version;
string type = HttpContext.Request.Browser.Type;
string platform = HttpContext.Request.Browser.Platform;
string userAgent = HttpContext.Request.UserAgent;
string[] userLang = HttpContext.Request.UserLanguages
It appears Request.UserLanguages is not available in later mvc versions (Asp.net core mvc 2.0.2 didn't have it.)
I made an extension method for HTTPRequest. Use it as follows:
var requestedLanguages = Request.GetAcceptLanguageCultures();
The method will give you the cultures from the Accept-Language header in order of preference (a.k.a. "quality").
public static class HttpRequestExtensions
{
public static IList<CultureInfo> GetAcceptLanguageCultures(this HttpRequest request)
{
var requestedLanguages = request.Headers["Accept-Language"];
if (StringValues.IsNullOrEmpty(requestedLanguages) || requestedLanguages.Count == 0)
{
return null;
}
var preferredCultures = requestedLanguages.ToString().Split(',')
// Parse the header values
.Select(s => new StringSegment(s))
.Select(StringWithQualityHeaderValue.Parse)
// Ignore the "any language" rule
.Where(sv => sv.Value != "*")
// Remove duplicate rules with a lower value
.GroupBy(sv => sv.Value).Select(svg => svg.OrderByDescending(sv => sv.Quality.GetValueOrDefault(1)).First())
// Sort by preference level
.OrderByDescending(sv => sv.Quality.GetValueOrDefault(1))
.Select(sv => new CultureInfo(sv.Value.ToString()))
.ToList();
return preferredCultures;
}
}
Tested with ASP.NET Core MVC 2.0.2
It's similar to #mare's answer, but a bit more up-to-date and the q (quality) is not ignored. Also, you may want to append the CultureInfo.InvariantCulture to the end of the list, depending on your usage.
I am marking this question for myself with a star and sharing here some code that essentially turns the Request.UserLanguages into an array of CultureInfo instances for further use in your application. It is also more flexible to work with CultureInfo than just the ISO codes, because with CultureInfo you get access to all the properties of a culture (like Name, Two character language name, Native name, ...):
// Create array of CultureInfo objects
string locale = string.Empty;
CultureInfo[] cultures = new CultureInfo[Request.UserLanguages.Length + 1];
for (int ctr = Request.UserLanguages.GetLowerBound(0); ctr <= Request.UserLanguages.GetUpperBound(0);
ctr++)
{
locale = Request.UserLanguages[ctr];
if (!string.IsNullOrEmpty(locale))
{
// Remove quality specifier, if present.
if (locale.Contains(";"))
locale = locale.Substring(0, locale.IndexOf(';'));
try
{
cultures[ctr] = new CultureInfo(locale, false);
}
catch (Exception) { continue; }
}
else
{
cultures[ctr] = CultureInfo.CurrentCulture;
}
}
cultures[Request.UserLanguages.Length] = CultureInfo.InvariantCulture;
HTH
var userLanguage = CultureInfo.CurrentUICulture;

Validation Error:: Value is not valid in Selectone menu [duplicate]

This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 7 years ago.
I got this error many time.. i am using two h:selectonemenu in my JSF page, mediaList and Unitlist. while selecting any Media . my UnitList populate automatically, but some time it gives Validation Error: value is not valid;
My JSF code
<h:selectOneMenu id="media" value="#{workOrderMbean.selectedMedia}" converter="MediaConverter" onchange="submit()" valueChangeListener="#{workOrderMbean.onChangeMediaCombo}" immediate="true">
<f:selectItems value="#{workOrderMbean.mediaCombo}"/>
</h:selectOneMenu>
<h:selectOneMenu id="hUnit" value="#{workOrderMbean.selectedHeightUnit}" converter="UnitConverter" >
<f:selectItems value="#{workOrderMbean.unitCombo}"/>
</h:selectOneMenu>
onchane event of Mediacombo is
public void onChangeMediaCombo(ValueChangeEvent e) throws SearchBLException {
if (e.getNewValue() != null) {
Media media = (Media) e.getNewValue();
if (unitCombo != null && !unitCombo.isEmpty()) {
unitCombo.clear();
seclectedWidthUnit=new Unit();
selectedHeightUnit=new Unit();
}
unitCombo = ComboLoader.getUnitsComboByMediaid(media.getMediaId());
}
else
{
if (unitCombo != null && !unitCombo.isEmpty()) {
unitCombo.clear();
seclectedWidthUnit=null;
selectedHeightUnit=null;
}
unitCombo = ComboLoader.getUnitsComboByMediaid(-1);
}
}
i am also using converter for 'Unit'
my media converter is
#FacesConverter(value = "MediaConverter")
public class MediaConverter implements Converter{
MediaDAO mediadao=new MediaDAOImpl();
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Media media=null;
try {
media=mediadao.getMedia(Integer.parseInt(value));
} catch (SearchBLException ex) {
Logger.getLogger(MediaConverter.class.getName()).log(Level.SEVERE, null, ex);
}
return media;
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
String str = "";
if (value instanceof Media) {
str = "" + ((Media) value).getMediaId();
}
return str;
}
}
The problem is most likely in your converter and model class (Media).
You don't show how you exactly do the conversion, but I guess you're converting to String by returning the Media's Id, and converting back to Media by getting a new instance from some place like a DB?
In that case, your Media class needs to implement a custom equals and hashcode method.
JSF compares if the value send by the user corresponds with the values in the list you bind to the selectitems. It uses equals for that, which by default compares object Ids (kind of memory references). Unless you have the exact same instances, this will always be false.
Instead of defining an equals method, you can alternatively let your converter get the model object you need from the same list as the selectitems come from. There was an article on http://jdevelopment.nl a while back about this.

JSF 2 redirection problem

I have following two methods in my backing bean -
public String validateUser() {
FacesContext facesCtx = FacesContext.getCurrentInstance();
if(userName.equals("user1") && password.equals("pass1")) {
User user = new User();
user.setUserName(userName);
HttpSession session = (HttpSession) facesCtx.getExternalContext().getSession(false);
session.setAttribute(User.SESSION_ATTRIBUTE, user);
return "secured/home.jsf?faces-redirect=true";
}
if(!userName.equals(LoginBean.USERNAME)) {
FacesMessage msgForUserName = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Username did not match.", null);
facesCtx.addMessage("loginForm:userName", msgForUserName);
}
if(!password.equals(LoginBean.PASSWORD)) {
FacesMessage msgForPassword = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Password did not match.", null);
facesCtx.addMessage("loginForm:password", msgForPassword);
}
return null;
}
public String logout() {
logger.info("Logging out .........................................");
FacesContext facesCtx = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession) facesCtx.getExternalContext().getSession(false);
session.invalidate();
return "login.jsf?faces-redirect=true";
}
I don't know why the redirection is working in the first method (i.e. validateUser()), but it's not working in the second method (i.e. logout()).
The code inside the logout method is actually executed, the session also gets invalidated,but somehow the browser stays on the same page.
And, I am using PrimeFaces p:commandButton and the ajax is enabled on both of them.
Any one, any idea?
Thank you.
but somehow the browser stays on the same page. And, I am using PrimeFaces p:commandButton and the ajax is enabled on both of them
I wouldn't expect it to fail. I suspect that this has something to do with the invalidated session. Try it with ajax="false" on the <p:commandButton>.
Unrelated to the problem, you should try to minimize the javax.servlet imports in your JSF managed beans. They often indicate that you're doing things in the wrong place or the clumsy way. In pure JSF2, you can invalidate the session as follows:
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
You can get/set objects in the session by the session Map.
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
// ...
Or just make it a managed bean (property).
See also:
How can I create a new session with a new User login on the application?

MVC data annotation layer: where to put the set CurrentUICulture statement?

I am getting crazy with the localization of an MVC application.
After a recent question of mine I have followed this approach:
The language is stored in Session["lang"]
Each controller inherits from my own BaseController, which overrides OnActionExecuting, and in this method reads the Session and sets CurrentCulture and CurrentUICulture
This works great, until the Data Annotation Layer comes in. It seems like it gets called BEFORE the action itself is executed, and therefore it always gets the error messages in the default language!
The fields declarations go like this:
[Required(ErrorMessageResourceName = "validazioneRichiesto", ErrorMessageResourceType = typeof(Resources.Resources))]
public string nome { get; set; }
So, is there any reasonable place where I can put the call?
I initialize the Data Annotation Model Binder in my Controller constructor.
public CardController() : base() {
ModelBinders.Binders.DefaultBinder =
new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder();
}
So, since Session is always null in the controller's constructor, and the action override is called AFTER the data annotation has validated the fields, where can I possibly set the CurrentCulture and CurrentUICulture to get localized errors?
I tried putting the CurrentCulture and CurrentUiCulture in Application_* (e.g. Application_AcquireRequestState or Application_PreRequestHandlerExecute) seems to have no effect...
As the culture is a global user setting, I am using it in the global.asax.cs file in the Application_BeginRequest method.
It does the work for me (using cookies) but the Session is also available there.
EDIT:
/by Brock Allen:
http://www.velocityreviews.com/forums/t282576-aspnet-20-session-availability-in-globalasax.html/
Session is available in PreRequesthandlerExecute.
The problem is that your code is being executed for every request into the server, and some requests (like ones for WebResourxe.axd) don't utlilize
Session (because the handler doesn't implement IRequireSessionState). So change your code to only access Session if that request has access to it.
Change your code to do this:
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
SetCulture();
}
Anyway, not sure if it works with mvc
After reading your question more carefully, I think that your problem is more in the way the resources are compiled.
Check in the Resource.resx properties, find Build Action and set it to Embedded Resource
Also change Custom Tool to PublicResXFileCodeGenerator
alt text http://img208.imageshack.us/img208/2126/captuream.png
I have tested a mini solution and works perfectly.
If you have more problem, I can send the example to you.
Another approach you can use is to put the lang in the URL, with this benefits:
The site is spidered by search engines in different languages
The user can send a URL to a friend in the selected language
To do this, use the Application_BeginRequest method in Global.asax
Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
Dim lang As String
If HttpContext.Current.Request.Path.Contains("/en/") Then
lang = "en"
Else
lang = "es"
End If
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(lang)
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(lang)
End Sub
See this question for more info on how to implement it
The OP posted the final solution as the following, thanks to the accepted answer by twk:
void Application_PreRequestHandlerExecute(object sender, EventArgs e) {
if (Context.Handler is IRequiresSessionState ||
Context.Handler is IReadOnlySessionState) {
if (Session["lang"] == null) {
Session["lang"] = "it";
}
if (Request.QueryString["lang"] == "it" || Request.QueryString["lang"] == "en") {
Session["lang"] = Request.QueryString["lang"];
}
string lang1 = Session["lang"].ToString();
string lang2 = Session["lang"].ToString().ToUpper();
if (lang2 == "EN")
lang2 = "GB";
System.Threading.Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.CreateSpecificCulture(lang1 + "-" + lang2);
System.Threading.Thread.CurrentThread.CurrentUICulture =
System.Globalization.CultureInfo.CreateSpecificCulture(lang1 + "-" + lang2);
}
}

Resources