From c924043dd5edf0670695aaaba597fbd437c920a9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Roland=20H=C3=A4der?= Date: Sat, 19 Aug 2017 13:18:04 +0200 Subject: [PATCH] Please cherry-pick: - changed List to Map to have locale name (e.g. de_DE) as key and the locale itself as value stored in a map for a quick lookup - this map is also being used to handle browser's language-only locales, like "de" and not "de_AT" or other equivalents - there is now a submit button for JS-less users to change locale MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Roland Häder --- .../FinancialsLocalizationSessionBean.java | 173 +++++++++++------- .../localization/bundle_de_DE.properties | 4 + .../localization/bundle_en_US.properties | 2 + web/WEB-INF/templates/base.tpl | 52 +++--- .../generic/locale_selection_box.tpl | 16 +- 5 files changed, 155 insertions(+), 92 deletions(-) diff --git a/src/java/org/mxchange/jfinancials/beans/localization/FinancialsLocalizationSessionBean.java b/src/java/org/mxchange/jfinancials/beans/localization/FinancialsLocalizationSessionBean.java index a290f39c..24bf5bd7 100644 --- a/src/java/org/mxchange/jfinancials/beans/localization/FinancialsLocalizationSessionBean.java +++ b/src/java/org/mxchange/jfinancials/beans/localization/FinancialsLocalizationSessionBean.java @@ -18,16 +18,16 @@ package org.mxchange.jfinancials.beans.localization; import java.text.MessageFormat; import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.LinkedHashMap; import java.util.Locale; +import java.util.Map; +import java.util.Objects; import javax.annotation.PostConstruct; import javax.enterprise.context.SessionScoped; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.enterprise.inject.Any; import javax.faces.context.FacesContext; -import javax.faces.event.ValueChangeEvent; import javax.inject.Inject; import javax.inject.Named; import org.mxchange.jcoreee.events.locale.LocaleChangeEvent; @@ -71,9 +71,9 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController private String localeCode; /** - * A list of all supported locales + * A map of all supported locales, using language code as key */ - private final List supportedLocales; + private final Map supportedLocales; /** * Default constructor @@ -83,7 +83,7 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController super(); // Init list - this.supportedLocales = new LinkedList<>(); + this.supportedLocales = new LinkedHashMap<>(2); } /** @@ -142,12 +142,56 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController this.clear(); } + /** + * Changes locale in application + */ + public void doChangeLocale () { + // Is locale code set? + if (this.getLocaleCode() == null) { + // Throw NPE + throw new NullPointerException("this.localeCode is null"); + } else if (this.getLocaleCode().isEmpty()) { + // Throw IAE + throw new IllegalArgumentException("this.localeCode is empty"); + } + + // Default new locale is null, will be handled later + Locale newLocale = null; + + // Iterate over whole map + for (Map.Entry entry : this.getSupportedLocales().entrySet()) { + Locale foundLocale = entry.getValue(); + System.out.println(MessageFormat.format("foundLocale={0},this.localeCode={1}", foundLocale, this.getLocaleCode())); + + // Does the language match? + if (Objects.equals(foundLocale.toString(), this.getLocaleCode())) { + // Is found, take it as request locale + newLocale = foundLocale; + break; + } + } + + // Clear locale code field + this.clear(); + + // Has a matching locale + System.out.println("newLocale=" + newLocale); + if (null == newLocale) { + // Throw NPE + throw new NullPointerException("this.localeCode=" + this.getLocaleCode() + " cannot be found."); + } + + // Then change it + this.changeLocale(newLocale); + } + /** * Getter for locale *

* @return Locale */ public Locale getLocale () { + System.out.println("Getting this.locale=" + this.locale); return this.locale; } @@ -157,6 +201,7 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController * @param locale Locale */ public void setLocale (final Locale locale) { + System.out.println("Setting locale=" + locale); this.locale = locale; } @@ -166,6 +211,7 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController * @return Language code */ public String getLocaleCode () { + System.out.println("Getting this.localeCode=" + this.localeCode); return this.localeCode; } @@ -175,6 +221,7 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController * @param localeCode Language code */ public void setLocaleCode (final String localeCode) { + System.out.println("Setting localeCode=" + localeCode); this.localeCode = localeCode; } @@ -184,7 +231,7 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController * @return Selectable localizations */ @SuppressWarnings ("ReturnOfCollectionOrArrayField") - public List getSupportedLocales () { + public Map getSupportedLocales () { // Return full list return this.supportedLocales; } @@ -194,17 +241,12 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController */ @PostConstruct public void init () { - // Get locale instance from context - Locale currentLocale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale(); - - // Change locale, may set same back in faces context, but triggers event - this.changeLocale(currentLocale); - // Get default locale Locale defaultLocale = FacesContext.getCurrentInstance().getApplication().getDefaultLocale(); + System.out.println("defaultLocale=" + defaultLocale); // Add it to list - this.getSupportedLocales().add(defaultLocale); + this.getSupportedLocales().put(defaultLocale.toString(), defaultLocale); // Get iterator from faces context Iterator iterator = FacesContext.getCurrentInstance().getApplication().getSupportedLocales(); @@ -213,71 +255,72 @@ public class FinancialsLocalizationSessionBean extends BaseFinancialsController while (iterator.hasNext()) { // Get next locale Locale supportedLocale = iterator.next(); + System.out.println("supportedLocale=" + supportedLocale); // Add it - this.getSupportedLocales().add(supportedLocale); + this.getSupportedLocales().put(supportedLocale.toString(), supportedLocale); } - } - - /** - * Listens on value-change event and changes locale accordingly, if found in - * list. - *

- * This method has been taken from - * https://www.mkyong.com/jsf2/jsf-2-internationalization-example/ and has - * been customized, e.g. checked parameter deeply. If the selected locale - * does not exist (someone changed the value in HTML form) then a proper - * exception is thrown. - *

- * @param event Event being triggered by JSF view/template - * - * @throws NullPointerException If event or newValue field is empty - * @throws IllegalArgumentException If newValue is empty or the locale does - * not exist - */ - public void localeCodeChanged (final ValueChangeEvent event) { - // Validate event - if (null == event) { - // Abort here - throw new NullPointerException("event is null"); //NOI18N - } else if (event.getNewValue() == null) { - // Abort again - throw new NullPointerException("event.newValue is null"); //NOI18N - } - - // First, get new value from event - String newCountryCode = event.getNewValue().toString(); - // Is it valid? - if (null == newCountryCode) { - // Abort here - throw new NullPointerException("newCountryCode is null"); //NOI18N - } else if (newCountryCode.isEmpty()) { - // Is empty - throw new IllegalArgumentException("newCountryCode is empty"); //NOI18N + // Get locale instance from request (e.g. browser) + Locale requestLocale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale(); + + // Is no country code found? + if (requestLocale.getCountry().isEmpty()) { + // Then try to find one + System.out.println("Request locale is without country information, looking up in map ..."); + + // Get language from it + String language = requestLocale.getLanguage(); + Boolean found = Boolean.FALSE; + + // Iterate over whole map + for (Map.Entry entry : this.getSupportedLocales().entrySet()) { + String languageCode = entry.getKey(); + Locale foundLocale = entry.getValue(); + System.out.println(MessageFormat.format("languageCode={0},language={1}", languageCode, language)); + + // Does the language match? + if (languageCode.startsWith(language)) { + // Is found, take it as request locale + requestLocale = foundLocale; + found = Boolean.TRUE; + break; + } + } + + // Nothing found? + if (!found) { + // Then set default locale + requestLocale = defaultLocale; + } } - // Generate locale from new value (which is the 2-letter country code) - Locale newLocale = new Locale(newCountryCode); - - // Lookup locale in list, very easy - if (!this.getSupportedLocales().contains(newLocale)) { - // Locale is not in supportedLocales list - throw new IllegalArgumentException(MessageFormat.format("Country code {0} is not supported.", newCountryCode)); //NOI18N - } - - // All fine, then change locale - this.changeLocale(newLocale); + // Change locale, may set same back in faces context, but triggers event + this.changeLocale(requestLocale); } /** - * Changes current locale in this controller and faces context to given + * Changes current locale in this controller and faces context to given. + * This method makes sure that locales with at least language and country + * information are being changed to. *

* @param locale New locale to set */ private void changeLocale (final Locale locale) { - // Set locale here + // Is the locale language_country at least? + if (null == locale) { + // Throw NPE + throw new NullPointerException("locale is null"); + } else if (!locale.toString().contains("_")) { + // Throw IAE + throw new IllegalArgumentException(MessageFormat.format("locale={0} does not have country information set.", locale)); + } + + System.out.println("Changing locale to " + locale + " ..."); + + // Set locale + code here this.setLocale(locale); + this.setLocaleCode(locale.toString()); // Inform faces context FacesContext.getCurrentInstance().getViewRoot().setLocale(locale); diff --git a/src/java/org/mxchange/localization/bundle_de_DE.properties b/src/java/org/mxchange/localization/bundle_de_DE.properties index b624c290..eedf9d20 100644 --- a/src/java/org/mxchange/localization/bundle_de_DE.properties +++ b/src/java/org/mxchange/localization/bundle_de_DE.properties @@ -864,3 +864,7 @@ ADMIN_BUSINESS_DATA_COMPANY_EMAIL_ADDRESS=Haupt-Email-Adresse: #@TODO Please fix German umlauts! BUTTON_ADMIN_CONTINUE_BUSINESS_CONTACT_PERSON=Basisdaten hinzufuegen ERROR_GUEST_REGISTRATION_IN_INDEX_ENABLED=Fehler: Falscher Aufruf der Anmeldeseite, da die Eingangsseite als Anmeldeseite fungiert. +#@TODO Please fix German umlauts! +BUTTON_CHANGE_LOCALE=Aendern +#@TODO Please fix German umlauts! +BUTTON_CHANGE_LOCALE_TITLE=Aendern Sie hier in der aktuellen Sitzung die angezeigte Sprache. diff --git a/src/java/org/mxchange/localization/bundle_en_US.properties b/src/java/org/mxchange/localization/bundle_en_US.properties index 02360c17..4118bf17 100644 --- a/src/java/org/mxchange/localization/bundle_en_US.properties +++ b/src/java/org/mxchange/localization/bundle_en_US.properties @@ -831,3 +831,5 @@ ADMIN_BUSINESS_DATA_COMPANY_EMAIL_ADDRESS=Main email address: BUTTON_ADMIN_CONTINUE_BUSINESS_CONTACT_PERSON=Add basic data #Fehler: Falscher Aufruf der Anmeldeseite, da die Eingangsseite als Anmeldeseite fungiert. ERROR_GUEST_REGISTRATION_IN_INDEX_ENABLED=Error: Wrong request on registration page as the index page serves as registration page. +BUTTON_CHANGE_LOCALE=Change +BUTTON_CHANGE_LOCALE_TITLE=Change here in your current session the used language for text output. diff --git a/web/WEB-INF/templates/base.tpl b/web/WEB-INF/templates/base.tpl index 4f96022d..3c24222d 100644 --- a/web/WEB-INF/templates/base.tpl +++ b/web/WEB-INF/templates/base.tpl @@ -6,27 +6,27 @@ - + + + - - + - + - - + + - - <h:outputText value="JFinancials" /> - <h:outputText value=" - " /> - <ui:insert name="title"> - <h:outputText value="Default title" /> - </ui:insert> - - + + <h:outputText value="JFinancials" /> + <h:outputText value=" - " /> + <ui:insert name="title"> + <h:outputText value="Default title" /> + </ui:insert> + + - - +