From: Mathieu Baudier Date: Wed, 14 Oct 2015 16:12:05 +0000 (+0000) Subject: Localis(z)ation support X-Git-Tag: argeo-commons-2.1.30~94 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=1d3c6771974fd559382cc6cd97c9d147ba511235;p=lgpl%2Fargeo-commons.git Localis(z)ation support git-svn-id: https://svn.argeo.org/commons/trunk@8483 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/demo/argeo_node_cms.properties b/demo/argeo_node_cms.properties index fcc210955..31b711068 100644 --- a/demo/argeo_node_cms.properties +++ b/demo/argeo_node_cms.properties @@ -24,5 +24,10 @@ org.eclipse.equinox.http.jetty.log.stderr.threshold=info org.osgi.framework.security=osgi java.security.policy=file:../../all.policy +# i18n +argeo.i18n.defaultLocale=fr +argeo.i18n.locales=en,fr +eclipse.registry.MultiLanguage=true + log4j.configuration=file:../../log4j.properties org.eclipse.rap.workbenchAutostart=false \ No newline at end of file diff --git a/demo/argeo_node_rap.properties b/demo/argeo_node_rap.properties index 818fc76bf..22a4283a1 100644 --- a/demo/argeo_node_rap.properties +++ b/demo/argeo_node_rap.properties @@ -41,7 +41,7 @@ org.eclipse.equinox.http.jetty.log.stderr.threshold=info # cd ssl; sh ./ssl.sh; # i18n -argeo.i18n.availableLocales=en,fr,de +argeo.i18n.locales=en,fr,de eclipse.registry.MultiLanguage=true # Logging diff --git a/org.argeo.cms/src/org/argeo/cms/CmsMsg.java b/org.argeo.cms/src/org/argeo/cms/CmsMsg.java index d6172a900..6d118db07 100644 --- a/org.argeo.cms/src/org/argeo/cms/CmsMsg.java +++ b/org.argeo.cms/src/org/argeo/cms/CmsMsg.java @@ -11,6 +11,12 @@ public class CmsMsg extends DefaultsResourceBundle { public final static Msg login = new Msg("sign in"); public final static Msg register = new Msg("register"); + public final static Msg changePassword = new Msg("change password"); + public final static Msg currentPassword = new Msg("current password"); + public final static Msg newPassword = new Msg("new password"); + public final static Msg repeatNewPassword = new Msg("repeat new password"); + public final static Msg passwordChanged = new Msg("password changed"); + static { Msg.init(CmsMsg.class); } diff --git a/org.argeo.cms/src/org/argeo/cms/CmsMsg_fr.properties b/org.argeo.cms/src/org/argeo/cms/CmsMsg_fr.properties index 23817233d..dc25d31a1 100644 --- a/org.argeo.cms/src/org/argeo/cms/CmsMsg_fr.properties +++ b/org.argeo.cms/src/org/argeo/cms/CmsMsg_fr.properties @@ -2,4 +2,10 @@ username=identifiant password=mot de passe logout=déconnexion login=connexion -register=créer un compte \ No newline at end of file +register=créer un compte + +changePassword=changement de mot de passe +currentPassword=mot de passe actuel +newPassword=nouveau mot de passe +repeatNewPassword=répéter le nouveau mot de passe +passwordChanged=mot de passe changé diff --git a/org.argeo.cms/src/org/argeo/cms/i18n/Msg.java b/org.argeo.cms/src/org/argeo/cms/i18n/Msg.java index 64982f6c7..bbbd590cd 100644 --- a/org.argeo.cms/src/org/argeo/cms/i18n/Msg.java +++ b/org.argeo.cms/src/org/argeo/cms/i18n/Msg.java @@ -48,9 +48,20 @@ public class Msg { /** When used as the first word of a sentence. */ public String lead() { - String raw = toString(); - return raw.substring(0, 1).toUpperCase(UiContext.getLocale()) - + raw.substring(1); + return lead(UiContext.getLocale()); + } + + public String lead(Locale locale) { + return lead(this, locale); + } + + private static String lead(Msg msg, Locale locale) { + String raw = msg.local(locale).toString(); + return lead(raw, locale); + } + + public static String lead(String raw, Locale locale) { + return raw.substring(0, 1).toUpperCase(locale) + raw.substring(1); } public Object local() { @@ -62,12 +73,25 @@ public class Msg { return local; } + public Object local(Locale locale) { + Object local = local(this, locale); + if (local == null) + local = getDefault(); + if (local == null) + throw new CmsException("No translation found for " + id); + return local; + } + private static Object local(Msg msg) { + Locale locale = UiContext.getLocale(); + return local(msg, locale); + } + + private static Object local(Msg msg, Locale locale) { String key = msg.getId(); int lastDot = key.lastIndexOf('.'); String className = key.substring(0, lastDot); String fieldName = key.substring(lastDot + 1); - Locale locale = UiContext.getLocale(); ResourceBundle rb = ResourceBundle.getBundle(className, locale, msg.getClassLoader()); return rb.getString(fieldName); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java index d5c6f0024..acb75d1e5 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java @@ -53,8 +53,8 @@ public class ConsoleCallbackHandler implements CallbackHandler { LocaleChoice callback = (LocaleChoice) callbacks[i]; writer.write("Language"); writer.write("\n"); - for (int j = 0; j < callback.getAvailableLocales().size(); j++) { - Locale locale = callback.getAvailableLocales().get(j); + for (int j = 0; j < callback.getLocales().size(); j++) { + Locale locale = callback.getLocales().get(j); writer.print(j + " : " + locale.getDisplayName() + "\n"); } writer.write("(" + callback.getDefaultIndex() + ") : "); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java index b95c19558..b66140e31 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java @@ -2,10 +2,12 @@ package org.argeo.cms.internal.kernel; import static bitronix.tm.TransactionManagerServices.getTransactionManager; import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry; +import static java.util.Locale.ENGLISH; import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp; import static org.argeo.cms.internal.kernel.KernelUtils.getOsgiInstancePath; import static org.argeo.jcr.ArgeoJcrConstants.ALIAS_NODE; import static org.argeo.jcr.ArgeoJcrConstants.JCR_REPOSITORY_ALIAS; +import static org.argeo.util.LocaleChoice.asLocaleList; import static org.osgi.framework.Constants.FRAMEWORK_UUID; import java.io.File; @@ -13,6 +15,8 @@ import java.lang.management.ManagementFactory; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Hashtable; +import java.util.List; +import java.util.Locale; import java.util.Map; import javax.jcr.Repository; @@ -84,6 +88,9 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { private DataHttp dataHttp; private KernelThread kernelThread; + private Locale defaultLocale = null; + private List locales = null; + public Kernel() { nodeSecurity = new NodeSecurity(); } @@ -108,11 +115,13 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { Thread.currentThread().setContextClassLoader( Kernel.class.getClassLoader()); // KernelUtils.logFrameworkProperties(log); + defaultLocale = new Locale(getFrameworkProp(I18N_DEFAULT_LOCALE, + ENGLISH.getLanguage())); + locales = asLocaleList(getFrameworkProp(I18N_LOCALES)); try { // Initialise services logger = new NodeLogger(); - // transactionManager = new SimpleTransactionManager(); initBitronixTransactionManager(); repository = new NodeRepository(bc); repositoryFactory = new OsgiJackrabbitRepositoryFactory(); @@ -279,23 +288,16 @@ final class Kernel implements KernelHeader, KernelConstants, ServiceListener { + (httpsPort != null ? " - HTTPS " + httpsPort : "")); } - // private ExtendedHttpService waitForHttpService() { - // final ServiceTracker st = new - // ServiceTracker( - // bc, ExtendedHttpService.class, null); - // st.open(); - // ExtendedHttpService httpService; - // try { - // httpService = st.waitForService(1000); - // } catch (InterruptedException e) { - // httpService = null; - // } - // - // if (httpService == null) - // throw new CmsException("Could not find " - // + ExtendedHttpService.class + " service."); - // return httpService; - // } + @Override + public Locale getDefaultLocale() { + return defaultLocale; + } + + /** Can be null. */ + @Override + public List getLocales() { + return locales; + } final private static void directorsCut(long initDuration) { // final long ms = 128l + (long) (Math.random() * 128d); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java index 9f45edb7f..0fe5b3ae9 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java @@ -18,7 +18,8 @@ public interface KernelConstants { final static String TRANSACTIONS_HOME = "argeo.node.transactions.home"; - final static String I18N_AVAILABLE_LOCALES = "argeo.i18n.availableLocales"; + final static String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale"; + final static String I18N_LOCALES = "argeo.i18n.locales"; // Node Security final static String ROLES_URI = "argeo.node.roles.uri"; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelHeader.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelHeader.java index f4d4d843a..f82f6b1d1 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelHeader.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelHeader.java @@ -1,5 +1,10 @@ package org.argeo.cms.internal.kernel; +import java.util.List; +import java.util.Locale; + public interface KernelHeader { + public Locale getDefaultLocale(); + public List getLocales(); } diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java index 04e4f3c9f..53a23a324 100644 --- a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java +++ b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java @@ -1,9 +1,14 @@ package org.argeo.cms.widgets.auth; +import static org.argeo.cms.CmsMsg.password; +import static org.argeo.cms.CmsMsg.username; import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_ANONYMOUS; import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER; +import static org.argeo.cms.internal.kernel.Activator.getKernelHeader; import java.io.IOException; +import java.util.List; +import java.util.Locale; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; @@ -21,16 +26,21 @@ import org.argeo.cms.CmsStyles; import org.argeo.cms.CmsView; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.HttpRequestCallback; +import org.argeo.cms.i18n.Msg; import org.argeo.cms.util.CmsUtils; import org.argeo.util.LocaleChoice; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; @@ -38,14 +48,20 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; public class CmsLogin implements CmsStyles, CallbackHandler { - private Text username, password; + private Text usernameT, passwordT; private Composite credentialsBlock; + + private final Locale defaultLocale; private LocaleChoice localeChoice = null; private final CmsView cmsView; public CmsLogin(CmsView cmsView) { this.cmsView = cmsView; + defaultLocale = getKernelHeader().getDefaultLocale(); + List locales = getKernelHeader().getLocales(); + if (locales != null) + localeChoice = new LocaleChoice(locales, defaultLocale); } protected boolean isAnonymous() { @@ -116,20 +132,20 @@ public class CmsLogin implements CmsStyles, CallbackHandler { parent.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); // new Label(this, SWT.NONE).setText(CmsMsg.username.lead()); - username = new Text(credentialsBlock, SWT.BORDER); - username.setMessage(CmsMsg.username.lead()); - username.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_USERNAME); + usernameT = new Text(credentialsBlock, SWT.BORDER); + usernameT.setMessage(username.lead(defaultLocale)); + usernameT.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_USERNAME); GridData gd = CmsUtils.fillWidth(); gd.widthHint = textWidth; - username.setLayoutData(gd); + usernameT.setLayoutData(gd); // new Label(this, SWT.NONE).setText(CmsMsg.password.lead()); - password = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD); - password.setMessage(CmsMsg.password.lead()); - password.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_PASSWORD); + passwordT = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD); + passwordT.setMessage(password.lead(defaultLocale)); + passwordT.setData(RWT.CUSTOM_VARIANT, CMS_LOGIN_DIALOG_PASSWORD); gd = CmsUtils.fillWidth(); gd.widthHint = textWidth; - password.setLayoutData(gd); + passwordT.setLayoutData(gd); TraverseListener tl = new TraverseListener() { private static final long serialVersionUID = -1158892811534971856L; @@ -140,14 +156,48 @@ public class CmsLogin implements CmsStyles, CallbackHandler { } }; credentialsBlock.addTraverseListener(tl); - username.addTraverseListener(tl); - password.addTraverseListener(tl); + usernameT.addTraverseListener(tl); + passwordT.addTraverseListener(tl); parent.setTabList(new Control[] { credentialsBlock }); - credentialsBlock.setTabList(new Control[] { username, password }); + credentialsBlock.setTabList(new Control[] { usernameT, passwordT }); credentialsBlock.setFocus(); + + if (localeChoice != null) + createLocalesBlock(credentialsBlock); return credentialsBlock; } + protected Composite createLocalesBlock(final Composite parent) { + Composite c = new Composite(parent, SWT.NONE); + c.setLayout(CmsUtils.noSpaceGridLayout()); + c.setLayoutData(CmsUtils.fillAll()); + + SelectionListener selectionListener = new SelectionAdapter() { + private static final long serialVersionUID = 4891637813567806762L; + + public void widgetSelected(SelectionEvent event) { + localeChoice.setSelectedIndex((Integer) event.widget.getData()); + Locale selectedLocale = localeChoice.getSelectedLocale(); + usernameT.setMessage(username.lead(selectedLocale)); + passwordT.setMessage(password.lead(selectedLocale)); + }; + }; + + List locales = localeChoice.getLocales(); + for (Integer i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + Button button = new Button(c, SWT.RADIO); + button.setData(i); + button.setText(Msg.lead(locale.getDisplayName(locale), locale) + + " (" + locale + ")"); + // button.addListener(SWT.Selection, listener); + button.addSelectionListener(selectionListener); + if (i == localeChoice.getDefaultIndex()) + button.setSelection(true); + } + return c; + } + protected void login() { Subject subject = cmsView.getSubject(); LoginContext loginContext; @@ -174,9 +224,9 @@ public class CmsLogin implements CmsStyles, CallbackHandler { UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback) - ((NameCallback) callback).setName(username.getText()); + ((NameCallback) callback).setName(usernameT.getText()); else if (callback instanceof PasswordCallback) - ((PasswordCallback) callback).setPassword(password + ((PasswordCallback) callback).setPassword(passwordT .getTextChars()); else if (callback instanceof HttpRequestCallback) ((HttpRequestCallback) callback).setRequest(RWT.getRequest()); diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties new file mode 100644 index 000000000..4dff7af99 --- /dev/null +++ b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle.properties @@ -0,0 +1 @@ +changePassword=Change password \ No newline at end of file diff --git a/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties new file mode 100644 index 000000000..158d6faa0 --- /dev/null +++ b/org.argeo.security.ui.rap/OSGI-INF/l10n/bundle_fr.properties @@ -0,0 +1 @@ +changePassword=Changer de mot de passe \ No newline at end of file diff --git a/org.argeo.security.ui.rap/build.properties b/org.argeo.security.ui.rap/build.properties index 30f715358..ae37429d8 100644 --- a/org.argeo.security.ui.rap/build.properties +++ b/org.argeo.security.ui.rap/build.properties @@ -1 +1,2 @@ source.. = src/ +bin.includes = OSGI-INF/ diff --git a/org.argeo.security.ui.rap/plugin.xml b/org.argeo.security.ui.rap/plugin.xml index f2fae3c50..693a4ddf0 100644 --- a/org.argeo.security.ui.rap/plugin.xml +++ b/org.argeo.security.ui.rap/plugin.xml @@ -33,7 +33,7 @@ + name="%changePassword"> + label="%changePassword"/> diff --git a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java index c176ba62b..5ff76a9ea 100644 --- a/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java +++ b/org.argeo.security.ui.rap/src/org/argeo/security/ui/rap/SecureEntryPoint.java @@ -34,7 +34,6 @@ import org.argeo.ArgeoException; import org.argeo.cms.auth.AuthConstants; import org.argeo.cms.widgets.auth.DefaultLoginDialog; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.util.LocaleUtils; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.EntryPoint; @@ -104,9 +103,9 @@ public class SecureEntryPoint implements EntryPoint { throw new ArgeoException("Login succeeded but no auth");// fatal // add thread locale to RWT session - if (log.isTraceEnabled()) - log.trace("Locale " + LocaleUtils.threadLocale.get()); - RWT.setLocale(LocaleUtils.threadLocale.get()); + // if (log.isTraceEnabled()) + // log.trace("Locale " + LocaleUtils.threadLocale.get()); + // RWT.setLocale(LocaleUtils.threadLocale.get()); // once the user is logged in, longer session timeout RWT.getRequest().getSession() diff --git a/org.argeo.security.ui/src/org/argeo/security/ui/commands/OpenChangePasswordDialog.java b/org.argeo.security.ui/src/org/argeo/security/ui/commands/OpenChangePasswordDialog.java index 560cc8bc9..12cae7fb3 100644 --- a/org.argeo.security.ui/src/org/argeo/security/ui/commands/OpenChangePasswordDialog.java +++ b/org.argeo.security.ui/src/org/argeo/security/ui/commands/OpenChangePasswordDialog.java @@ -15,6 +15,13 @@ */ package org.argeo.security.ui.commands; +import static org.argeo.cms.CmsMsg.changePassword; +import static org.argeo.cms.CmsMsg.currentPassword; +import static org.argeo.cms.CmsMsg.newPassword; +import static org.argeo.cms.CmsMsg.passwordChanged; +import static org.argeo.cms.CmsMsg.repeatNewPassword; +import static org.eclipse.jface.dialogs.IMessageProvider.INFORMATION; + import java.security.AccessController; import javax.naming.InvalidNameException; @@ -30,7 +37,6 @@ import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.swt.SWT; @@ -58,7 +64,7 @@ public class OpenChangePasswordDialog extends AbstractHandler { HandlerUtil.getActiveShell(event), userAdmin); if (dialog.open() == Dialog.OK) { MessageDialog.openInformation(HandlerUtil.getActiveShell(event), - "Password changed", "Password changed."); + passwordChanged.lead(), passwordChanged.lead()); } return null; } @@ -103,7 +109,7 @@ public class OpenChangePasswordDialog extends AbstractHandler { class ChangePasswordDialog extends TitleAreaDialog { private static final long serialVersionUID = -6963970583882720962L; - private Text currentPassword, newPassword1, newPassword2; + private Text oldPassword, newPassword1, newPassword2; public ChangePasswordDialog(Shell parentShell, UserAdmin securityService) { super(parentShell); @@ -121,11 +127,11 @@ public class OpenChangePasswordDialog extends AbstractHandler { composite.setLayout(new GridLayout(2, false)); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - currentPassword = createLP(composite, "Current password"); - newPassword1 = createLP(composite, "New password"); - newPassword2 = createLP(composite, "Repeat new password"); + oldPassword = createLP(composite, currentPassword.lead()); + newPassword1 = createLP(composite, newPassword.lead()); + newPassword2 = createLP(composite, repeatNewPassword.lead()); - setMessage("Change password", IMessageProvider.INFORMATION); + setMessage(changePassword.lead(), INFORMATION); parent.pack(); return composite; } @@ -135,7 +141,7 @@ public class OpenChangePasswordDialog extends AbstractHandler { if (!newPassword1.getText().equals(newPassword2.getText())) throw new ArgeoException("Passwords are different"); try { - changePassword(currentPassword.getTextChars(), + changePassword(oldPassword.getTextChars(), newPassword1.getTextChars()); close(); } catch (Exception e) { @@ -156,7 +162,7 @@ public class OpenChangePasswordDialog extends AbstractHandler { protected void configureShell(Shell shell) { super.configureShell(shell); - shell.setText("Change password"); + shell.setText(changePassword.lead()); } } diff --git a/org.argeo.util/src/org/argeo/util/LocaleChoice.java b/org.argeo.util/src/org/argeo/util/LocaleChoice.java index aaf719db4..4b4db0eca 100644 --- a/org.argeo.util/src/org/argeo/util/LocaleChoice.java +++ b/org.argeo.util/src/org/argeo/util/LocaleChoice.java @@ -16,61 +16,55 @@ package org.argeo.util; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import javax.security.auth.callback.LanguageCallback; +import org.argeo.ArgeoException; + /** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */ public class LocaleChoice { - private List availableLocales = new ArrayList(); + private final List locales; private Integer selectedIndex = null; - private Integer defaultIndex = null; + private final Integer defaultIndex; + + public LocaleChoice(List locales, Locale defaultLocale) { + Integer defaultIndex = null; + this.locales = Collections.unmodifiableList(locales); + for (int i = 0; i < locales.size(); i++) + if (locales.get(i).equals(defaultLocale)) + defaultIndex = i; - // public LocaleCallback(Integer defaultIndex, List - // availableLocales) { - // this.availableLocales = Collections - // .unmodifiableList(new ArrayList(availableLocales)); - // this.defaultIndex = defaultIndex; - // this.selectedIndex = defaultIndex; - // } + // based on language only + if (defaultIndex == null) + for (int i = 0; i < locales.size(); i++) + if (locales.get(i).getLanguage() + .equals(defaultLocale.getLanguage())) + defaultIndex = i; + + if (defaultIndex == null) + throw new ArgeoException("Default locale " + defaultLocale + + " is not in available locales " + locales); + this.defaultIndex = defaultIndex; + + this.selectedIndex = defaultIndex; + } /** * Convenience constructor based on a comma separated list of iso codes (en, * en_US, fr_CA, etc.). Default selection is default locale. */ public LocaleChoice(String locales, Locale defaultLocale) { - if (locales == null || locales.trim().equals("")) - return; - String[] codes = locales.split(","); - for (int i = 0; i < codes.length; i++) { - String code = codes[i]; - // variant not supported - int indexUnd = code.indexOf("_"); - Locale locale; - if (indexUnd > 0) { - String language = code.substring(0, indexUnd); - String country = code.substring(indexUnd + 1); - locale = new Locale(language, country); - } else { - locale = new Locale(code); - } - availableLocales.add(locale); - if (locale.equals(defaultLocale)) - defaultIndex = i; - } - - if (defaultIndex == null) - defaultIndex = 0; - - this.selectedIndex = defaultIndex; + this(asLocaleList(locales), defaultLocale); } public String[] getSupportedLocalesLabels() { - String[] labels = new String[availableLocales.size()]; - for (int i = 0; i < availableLocales.size(); i++) { - Locale locale = availableLocales.get(i); + String[] labels = new String[locales.size()]; + for (int i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); if (locale.getCountry().equals("")) labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]"; @@ -87,7 +81,7 @@ public class LocaleChoice { public Locale getSelectedLocale() { if (selectedIndex == null) return null; - return availableLocales.get(selectedIndex); + return locales.get(selectedIndex); } public void setSelectedIndex(Integer selectedIndex) { @@ -98,12 +92,35 @@ public class LocaleChoice { return defaultIndex; } - public List getAvailableLocales() { - return availableLocales; + public List getLocales() { + return locales; } public Locale getDefaultLocale() { - return availableLocales.get(getDefaultIndex()); + return locales.get(getDefaultIndex()); + } + + /** Returns null if argument is null. */ + public static List asLocaleList(String locales) { + if (locales == null) + return null; + ArrayList availableLocales = new ArrayList(); + String[] codes = locales.split(","); + for (int i = 0; i < codes.length; i++) { + String code = codes[i]; + // variant not supported + int indexUnd = code.indexOf("_"); + Locale locale; + if (indexUnd > 0) { + String language = code.substring(0, indexUnd); + String country = code.substring(indexUnd + 1); + locale = new Locale(language, country); + } else { + locale = new Locale(code); + } + availableLocales.add(locale); + } + return availableLocales; } public static void main(String[] args) { diff --git a/org.argeo.util/src/org/argeo/util/LocaleUtils.java b/org.argeo.util/src/org/argeo/util/LocaleUtils.java deleted file mode 100644 index bc04afa75..000000000 --- a/org.argeo.util/src/org/argeo/util/LocaleUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.util; - -import java.util.Locale; - -import org.argeo.ArgeoException; - -/** Utilities around internationalization. */ -public class LocaleUtils { - /** - * The locale of the current thread and its children. Allows to deal with - * internationalisation as a cross cutting aspect. Never null. - */ - public final static InheritableThreadLocal threadLocale = new InheritableThreadLocal() { - @Override - protected Locale initialValue() { - return Locale.getDefault(); - } - - @Override - public void set(Locale value) { - if (value == null) - throw new ArgeoException("Thread local cannot be null."); - super.set(value); - } - - }; -}