Hi,
I haven’t used portlets myself, and am unlikely to; however, I can’t imagine that “live” locale switching is much more than an thing one does for demonstrations (“Look, here it is in English; press this button, ooh, Finnish!”). Not discounting that as a valid reason, mind you.
The only other realistic use-case I can imagine is when a user signs in : web page starts off in the company’s locale, user signs in, change user’s locale. Actually, scratch that, I can think of another use-case - a translator wanting to translate a web application into a different language, and dynamically switching locales.
(Sorry, I was think aloud).
Anyway, I’m not sure that it’s something that Vaadin should support directly out of the box : which Resource Bundle? How is it found? How is it associated? What about multiple resource bundles, hierarchical searching. That kind of thing is precisely the kind of thing that varies from project to project depending on complexity : one solution will never work for all.
Having said that, we are considering doing something like this to ease translation of our app to different locales. I’ll try and outline what our approach is going to be - we haven’t tried it yet, but I can’t see a problem with it; maybe someone else will point out a problem, and maybe the idea will help you.
We are going to store the resource bundle key in the DebugId of the component when we set the (translated) caption on the component. When we want to change the locale, we’ll search the component tree from application, looking at the debug ids - if they are present, we’ll switch the caption. No doubt one would need to do slightly different things for different components - (date picker, for example, you’d need to change the formatter as well as the caption. Table Headers will be interesting, and you’ll also have to cope with labels on tabs), but the principle would hold true.
As an added bonus, a significant proportion of your visible component will have a meaningful DebugId, which will help with automated testing tools!
I attach some some pseudo code to this message - let me reiterate, we’ve not tried this yet - I am thinking aloud in code. Also, our find-the-literal-from-the-resource-bundle needs to be more sophisticated - none-the-less, I’d imagine something like this.
Hope this helps someone - thinking it through helped me!
Cheers,
Charles [code]
// Creating the initial component
TextField projectName = new TextField();
setInitialCaption(projectName, "projectName", theCurrentLocale);
[...]
// This application listener doesn't exist. Easy enough to add.
application.addLocaleChangeListener(new LocaleChangeListener(){
public void onLocaleChange(LocaleChangeEvent event){
localizeComponents(event.getApplication());
}
});
[...]
/* Set the initial caption on a component. */
public void setInitialCaption(Component component, String resourceBundleKey, Locale locale) {
component.setDebugId(resourceBundleKey);
populateCaption(component, locale);
}
/* The actual lets-set-the-caption on the component method */
public void populateCaption(Component component, Locale locale) {
ResourceBundle rb = ResourceBundle.getBundle(“a-resource-bundle”, locale);
String key = component.getDebugId();
if (key != null) { // May need a smarter check, if debug id is set on a non-captioned component : maybe is key non-null and start with a given prefix?
String caption = rb.getString(key);
component.setCaption(caption == null?key : caption);
}
}
/** A Component walker for an application **/
private void localizeComponents(Application application) {
Collection windows = getWindows();
for (Window window : windows) {
localizeComponent(window, application.getLocale());
}
}
public void localizeComponent(Window window, Locale locale) {
populateCaption(window, locale);
/* TODO : Grey Area here - a window is a component container, and contains a component container (content);
beed to be careful not to process the tree twice. This is probably slightly wrong. */
localizeComponent((ComponentContainer) window, locale);
Set<Window> childWindows = window.getChildWindows();
for (Window childWindow : childWindows) {
localizeComponent(childWindow, locale);
}
}
public void localizeComponent(ComponentContainer componentContainer, Locale locale) {
// Update the caption on the container itself
populateCaption(componentContainer, locale);
// Update all the contained components
Iterator childIterator = componentContainer.getComponentIterator();
while(childIterator.hasNext()){
Component component = (Component) childIterator.next();
localizeComponent(component, locale);
}
}
public void localizeComponent(DateField component, Locale locale) {
/* THis override method might be unecessary for DateField if
DateField#getLocale ultimately goes to Application#getLocale */
populateCaption(component, locale);
component.setLocale(locale);
}
/* This is the basic update-the-caption-on-a-component */
public void localizeComponent(Component component, Locale locale) {
populateCaption(component, locale);
}
[/code]