From e4341984bb33f0322625d748e00164a1da37fd10 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 1/1] 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 --- nbproject/project.properties | 2 +- .../JobsLocalizationSessionBean.java | 235 +++++++++++++++--- .../JobsLocalizationSessionController.java | 36 --- .../localization/bundle_de_DE.properties | 4 + .../localization/bundle_en_US.properties | 2 + web/WEB-INF/templates/base.tpl | 54 ++-- .../generic/locale_selection_box.tpl | 16 +- 7 files changed, 247 insertions(+), 102 deletions(-) diff --git a/nbproject/project.properties b/nbproject/project.properties index 4f06757c..7439f5c6 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -60,7 +60,7 @@ j2ee.compile.on.save=true j2ee.copy.static.files.on.save=true j2ee.deploy.on.save=true j2ee.platform=1.7-web -j2ee.platform.classpath=${j2ee.server.home}/modules/endorsed/javax.annotation-api.jar:${j2ee.server.home}/modules/endorsed/webservices-api-osgi.jar:${j2ee.server.home}/modules/endorsed/jaxb-api.jar:${j2ee.server.home}/modules/javax.batch-api.jar:${j2ee.server.home}/modules/javax.security.auth.message-api.jar:${j2ee.server.home}/modules/javax.faces.jar:${j2ee.server.home}/modules/javax.servlet.jsp.jstl-api.jar:${j2ee.server.home}/modules/javax.transaction-api.jar:${j2ee.server.home}/modules/javax.servlet.jsp.jar:${j2ee.server.home}/modules/webservices-osgi.jar:${j2ee.server.home}/modules/javax.mail.jar:${j2ee.server.home}/modules/javax.interceptor-api.jar:${j2ee.server.home}/modules/javax.inject.jar:${j2ee.server.home}/modules/javax.resource-api.jar:${j2ee.server.home}/modules/javax.enterprise.concurrent-api.jar:${j2ee.server.home}/modules/javax.el.jar:${j2ee.server.home}/modules/javax.ejb-api.jar:${j2ee.server.home}/modules/javax.xml.rpc-api.jar:${j2ee.server.home}/modules/javax.security.jacc-api.jar:${j2ee.server.home}/modules/javax.json.jar:${j2ee.server.home}/modules/javax.xml.registry-api.jar:${j2ee.server.home}/modules/javax.websocket-api.jar:${j2ee.server.home}/modules/javax.management.j2ee-api.jar:${j2ee.server.home}/modules/javax.servlet-api.jar:${j2ee.server.home}/modules/javax.jms-api.jar:${j2ee.server.home}/modules/javax.enterprise.concurrent.jar:${j2ee.server.home}/modules/javax.servlet.jsp.jstl.jar:${j2ee.server.home}/modules/javax.enterprise.deploy-api.jar:${j2ee.server.home}/modules/bean-validator.jar:${j2ee.server.home}/modules/javax.ws.rs-api.jar:${j2ee.server.home}/modules/javax.servlet.jsp-api.jar:${j2ee.server.home}/modules/jaxb-osgi.jar:${j2ee.server.home}/modules/cdi-api.jar:${j2ee.server.home}/modules/javax.persistence.jar:${j2ee.server.middleware}/mq/lib/jaxm-api.jar +j2ee.platform.classpath=${j2ee.server.home}/modules/endorsed/javax.annotation-api.jar:${j2ee.server.home}/modules/endorsed/webservices-api-osgi.jar:${j2ee.server.home}/modules/endorsed/jaxb-api.jar:${j2ee.server.home}/modules/javax.batch-api.jar:${j2ee.server.home}/modules/javax.security.auth.message-api.jar:${j2ee.server.home}/modules/javax.faces.jar:${j2ee.server.home}/modules/javax.servlet.jsp.jstl-api.jar:${j2ee.server.home}/modules/javax.transaction-api.jar:${j2ee.server.home}/modules/javax.servlet.jsp.jar:${j2ee.server.home}/modules/webservices-osgi.jar:${j2ee.server.home}/modules/javax.mail.jar:${j2ee.server.home}/modules/javax.interceptor-api.jar:${j2ee.server.home}/modules/javax.inject.jar:${j2ee.server.home}/modules/javax.resource-api.jar:${j2ee.server.home}/modules/javax.enterprise.concurrent-api.jar:${j2ee.server.home}/modules/javax.el.jar:${j2ee.server.home}/modules/javax.ejb-api.jar:${j2ee.server.home}/modules/javax.xml.rpc-api.jar:${j2ee.server.home}/modules/javax.security.jacc-api.jar:${j2ee.server.home}/modules/javax.json.jar:${j2ee.server.home}/modules/javax.xml.registry-api.jar:${j2ee.server.home}/modules/javax.websocket-api.jar:${j2ee.server.home}/modules/javax.management.j2ee-api.jar:${j2ee.server.home}/modules/javax.servlet-api.jar:${j2ee.server.home}/modules/javax.jms-api.jar:${j2ee.server.home}/modules/javax.enterprise.concurrent.jar:${j2ee.server.home}/modules/javax.servlet.jsp.jstl.jar:${j2ee.server.home}/modules/javax.enterprise.deploy-api.jar:${j2ee.server.home}/modules/javax.ws.rs-api.jar:${j2ee.server.home}/modules/javax.servlet.jsp-api.jar:${j2ee.server.home}/modules/jaxb-osgi.jar:${j2ee.server.home}/modules/cdi-api.jar:${j2ee.server.home}/modules/javax.persistence.jar:${j2ee.server.middleware}/mq/lib/jaxm-api.jar j2ee.platform.embeddableejb.classpath=${j2ee.server.home}/lib/embedded/glassfish-embedded-static-shell.jar j2ee.platform.wscompile.classpath=${j2ee.server.home}/modules/webservices-osgi.jar j2ee.platform.wsgen.classpath=${j2ee.server.home}/modules/webservices-osgi.jar:${j2ee.server.home}/modules/endorsed/webservices-api-osgi.jar:${j2ee.server.home}/modules/jaxb-osgi.jar:${j2ee.server.home}/modules/endorsed/jaxb-api.jar diff --git a/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionBean.java b/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionBean.java index 21b49eb9..fd744fcb 100644 --- a/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionBean.java +++ b/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionBean.java @@ -17,12 +17,21 @@ package org.mxchange.jjobs.beans.localization; import java.text.MessageFormat; +import java.util.Iterator; +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.inject.Inject; import javax.inject.Named; +import org.mxchange.jcoreee.events.locale.LocaleChangeEvent; +import org.mxchange.jcoreee.events.locale.ObservableLocaleChangeEvent; import org.mxchange.jjobs.beans.BaseJobsController; import org.mxchange.juserlogincore.events.login.ObservableUserLoggedInEvent; import org.mxchange.juserlogincore.events.logout.ObservableUserLogoutEvent; @@ -42,19 +51,39 @@ public class JobsLocalizationSessionBean extends BaseJobsController implements J /** * Serial number */ - private static final long serialVersionUID = 1_867_671_657_629_601_528L; + private static final long serialVersionUID = 158_768_216_759_107L; /** * Current Locale */ private Locale locale; + /** + * Event being fired when a locale has changed successfully + */ + @Inject + @Any + private Event localeChangeEvent; + + /** + * Locale code (example: de for Germany) + */ + private String localeCode; + + /** + * A map of all supported locales, using language code as key + */ + private final Map supportedLocales; + /** * Default constructor */ public JobsLocalizationSessionBean () { // Call super constructor super(); + + // Init list + this.supportedLocales = new LinkedHashMap<>(2); } /** @@ -80,8 +109,11 @@ public class JobsLocalizationSessionBean extends BaseJobsController implements J // Is the locale set? if (event.getLoggedInUser().getUserLocale() instanceof Locale) { - // Set locale here - this.setLocale(event.getLoggedInUser().getUserLocale()); + // Get user local + Locale userLocale = event.getLoggedInUser().getUserLocale(); + + // Change locale + this.changeLocale(userLocale); } } @@ -110,50 +142,98 @@ public class JobsLocalizationSessionBean extends BaseJobsController implements J this.clear(); } - @Override - public String getLanguage () { - return this.getLocale().getLanguage().toLowerCase(); - } - - @Override - public void setLanguage (final String language) { - // Is the language null? - if (null == language) { - // This may sometimes happen, so abort here - return; + /** + * 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"); } - // Language splits - String[] splits = language.split("_"); //NOI18N - if (null == splits[1]) { - splits[1] = ""; //NOI18N + // 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; + } } - // Get new locale with upper-case country code - Locale loc = new Locale(splits[0], splits[1]); + // 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."); + } - // Set it here and in the JSF context - this.setLocale(loc); - FacesContext.getCurrentInstance().getViewRoot().setLocale(loc); + // Then change it + this.changeLocale(newLocale); } - @Override + /** + * Getter for locale + *

+ * @return Locale + */ public Locale getLocale () { + System.out.println("Getting this.locale=" + this.locale); return this.locale; } - @Override + /** + * Setter for locale + *

+ * @param locale Locale + */ public void setLocale (final Locale locale) { + System.out.println("Setting locale=" + locale); this.locale = locale; } - @Override - public Locale[] getSelectableLocalizations () { - Locale[] locales = { - Locale.GERMANY, - Locale.US - }; - return locales; + /** + * Getter for localeCode code + *

+ * @return Language code + */ + public String getLocaleCode () { + System.out.println("Getting this.localeCode=" + this.localeCode); + return this.localeCode; + } + + /** + * Setter for localeCode code + *

+ * @param localeCode Language code + */ + public void setLocaleCode (final String localeCode) { + System.out.println("Setting localeCode=" + localeCode); + this.localeCode = localeCode; + } + + /** + * Getter for selectable localizations + *

+ * @return Selectable localizations + */ + @SuppressWarnings ("ReturnOfCollectionOrArrayField") + public Map getSupportedLocales () { + // Return full list + return this.supportedLocales; } /** @@ -161,11 +241,92 @@ public class JobsLocalizationSessionBean extends BaseJobsController implements J */ @PostConstruct public void init () { - // Create locale instance from context - Locale loc = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale(); + // Get default locale + Locale defaultLocale = FacesContext.getCurrentInstance().getApplication().getDefaultLocale(); + System.out.println("defaultLocale=" + defaultLocale); + + // Add it to list + this.getSupportedLocales().put(defaultLocale.toString(), defaultLocale); + + // Get iterator from faces context + Iterator iterator = FacesContext.getCurrentInstance().getApplication().getSupportedLocales(); + + // Add all locales + while (iterator.hasNext()) { + // Get next locale + Locale supportedLocale = iterator.next(); + System.out.println("supportedLocale=" + supportedLocale); + + // Add it + this.getSupportedLocales().put(supportedLocale.toString(), supportedLocale); + } + + // 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; + } + } + + // 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. + * 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) { + // 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); - // Set it here - this.setLocale(loc); + // Fire event + this.localeChangeEvent.fire(new LocaleChangeEvent(locale)); } /** @@ -173,7 +334,7 @@ public class JobsLocalizationSessionBean extends BaseJobsController implements J */ private void clear () { // Clear all fields - this.setLanguage(null); + this.setLocaleCode(null); this.setLocale(null); } diff --git a/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionController.java b/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionController.java index 951c5c0a..622dbf7f 100644 --- a/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionController.java +++ b/src/java/org/mxchange/jjobs/beans/localization/JobsLocalizationSessionController.java @@ -17,7 +17,6 @@ package org.mxchange.jjobs.beans.localization; import java.io.Serializable; -import java.util.Locale; /** * An interface for localization change beans @@ -26,39 +25,4 @@ import java.util.Locale; */ public interface JobsLocalizationSessionController extends Serializable { - /** - * Getter for locale - *

- * @return Locale - */ - Locale getLocale (); - - /** - * Setter for locale - *

- * @param locale Locale - */ - void setLocale (final Locale locale); - - /** - * Getter for language code - *

- * @return Language code - */ - String getLanguage (); - - /** - * Setter for language code - *

- * @param language Language code - */ - void setLanguage (final String language); - - /** - * Getter for selectable localizations - *

- * @return Selectable localizations - */ - Locale[] getSelectableLocalizations (); - } diff --git a/src/java/org/mxchange/localization/bundle_de_DE.properties b/src/java/org/mxchange/localization/bundle_de_DE.properties index a3d7f44d..2d24b4d9 100644 --- a/src/java/org/mxchange/localization/bundle_de_DE.properties +++ b/src/java/org/mxchange/localization/bundle_de_DE.properties @@ -860,3 +860,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 0f41a55e..dabb9b19 100644 --- a/src/java/org/mxchange/localization/bundle_en_US.properties +++ b/src/java/org/mxchange/localization/bundle_en_US.properties @@ -840,3 +840,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 945c4ea5..c4ad9f0f 100644 --- a/web/WEB-INF/templates/base.tpl +++ b/web/WEB-INF/templates/base.tpl @@ -5,28 +5,28 @@ xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> - - + + + + - - + - + - - + + - - <h:outputText value="JJobs" /> - <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> + + - - +