Created
April 17, 2014 12:47
-
-
Save rmuller/10980706 to your computer and use it in GitHub Desktop.
Change Form Locale of Vaadin UI on the fly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* VaadinUtils.java | |
* | |
* Created: 2014-03-20 (Year-Month-Day) | |
* Character encoding: UTF-8 | |
* | |
****************************************** LICENSE ******************************************* | |
* | |
* Copyright (c) 2014 XIAM Solutions B.V. (http://www.xiam.nl) | |
* | |
* 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 eu.infomas.vaadin; | |
import java.lang.reflect.Method; | |
import java.util.Enumeration; | |
import java.util.Locale; | |
import java.util.ResourceBundle; | |
import java.util.SortedSet; | |
import java.util.TreeSet; | |
import com.vaadin.ui.Component; | |
import com.vaadin.ui.HasComponents; | |
import com.vaadin.ui.UI; | |
import eu.infomas.logging.Logger; | |
import eu.infomas.logging.LoggerFactory; | |
/** | |
* {@code VaadinUtils} offers some utility functions for the | |
* <a href="https://vaadin.com">Vaadin UI Framework</a>. | |
* | |
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a> | |
* @since INFOMAS NG 3.0 | |
*/ | |
public final class VaadinUtils { | |
private static final Logger LOG = LoggerFactory.getLogger(); | |
// Utility class | |
private VaadinUtils() { | |
} | |
/** | |
* Change all {@code Locale} dependant properties of the {@code com.vaadin.ui.Component}s | |
* within of the given component container (typically an {@link UI} or other top level | |
* layout component). | |
* If the specified {@code Locale} is the same as the current {@code Locale} of the | |
* component container, this method does nothing. | |
* A property is considered {@code Locale} dependant if there exists a key for this | |
* property in the given {@code ResourceBundle}. The key must have the format | |
* "componentId.propertyName", i.e. "user.caption". | |
* <p> | |
* To use this method, do something like: | |
* <pre> | |
* public class MyUI extends UI { | |
* | |
* {@literal @}Override | |
* public void setLocale(Locale locale) { | |
* VaadinUtils.updateLocale(this, locale, | |
* ResourceBundle.getBundle("i18n/Login", locale)); | |
* super.setLocale(locale); | |
* } | |
* | |
* {@literal @}Override | |
* public void init(final VaadinRequest request) { | |
* // ... usual code | |
* // somewhere in the UI the user can change the "Form Locale". This code must | |
* // call myUI#setLocale(newLocale); | |
* } | |
* | |
* // ... | |
* | |
* } | |
* </pre> | |
* | |
* @param ui The component container for which the {@code Locale} dependent component | |
* properties must be changed, never {@code null} | |
* @param locale The new {@code Locale}, never {@code null} | |
* @param rb The {@code ResourceBundle} for the specified {@code Locale}, | |
* never {@code null} | |
*/ | |
public static void updateLocale(final HasComponents ui, final Locale locale, | |
final ResourceBundle rb) { | |
// locale may not be null, howvever the current UI Locale may be null! | |
if (locale.equals(ui.getLocale())) { | |
return; | |
} | |
final long time = System.currentTimeMillis(); | |
final SortedSet<String> ss = new TreeSet<>(); | |
for (Enumeration<String> e = rb.getKeys(); e.hasMoreElements(); ) { | |
ss.add(e.nextElement()); | |
} | |
walkComponentTree(ui, new Consumer<Component>() { | |
@Override | |
public void accept(Component c) { | |
final String id = c.getId(); | |
if (id == null) { | |
return; | |
} | |
final Class<?> type = c.getClass(); | |
final int prefixLen = id.length() + 1; | |
// char '/' is the char right after '.' in Unicode | |
for (String key : ss.subSet(id.concat("."), id.concat("/"))) { | |
if (rb.containsKey(key)) { | |
// update value! | |
final String mname = keyToMethodName(key, prefixLen); | |
try { | |
final Method m = type.getMethod(mname, String.class); | |
final String value = rb.getString(key); | |
LOG.info("%s(%s) [key=%s]", mname, value, key); | |
m.invoke(c, value); | |
} catch (Exception ex) { | |
LOG.error(ex); | |
} | |
} | |
} | |
} | |
}); | |
LOG.info("Locale updated: %s -> %s in %d ms.", | |
ui.getLocale(), locale, System.currentTimeMillis() - time); | |
} | |
// private | |
// if switching to Java 8, remove this and replace by java.util.funtion.Consumer | |
// (and make to code more functional) | |
interface Consumer<T> { | |
void accept(T t); | |
} | |
// recursively walk the Component true | |
private static void walkComponentTree(Component c, Consumer<Component> visitor) { | |
visitor.accept(c); | |
if (c instanceof HasComponents) { | |
for (Component child : ((HasComponents)c)) { | |
walkComponentTree(child, visitor); | |
} | |
} | |
} | |
// keyToMethodName("user.caption", 5) -> "setCaption" | |
private static String keyToMethodName(final String key, final int prefixLen) { | |
final int n = key.length() - prefixLen; | |
final char[] buffer = new char[n + 3]; | |
"set".getChars(0, 3, buffer, 0); | |
key.getChars(prefixLen, prefixLen + n, buffer, 3); | |
buffer[3] = Character.toUpperCase(buffer[3]); | |
return new String(buffer); | |
} | |
} |
I think I found the issue after reading the comments. I'll adjust to my case. Thanks for the code!
See my revision to this code here.
Basically made the following changes:
- Works with Components implementing the Property interface (i.e. Label).
- Works with any component that has an id matching a key in the provided resource bundle. No special formatting needed.
- Works with components with complex captions/values. For example a label with multiple keys.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This looks interesting but is not working for me. While debugging all components return null when using on line 108 so nothing is actually translated.