Jmix version: 2.5.1
Jmix Studio plugin version: 2.5.1-243
IntelliJ IDEA 2024.3.5 (Community Edition)
Build #IC-243.26053.27, built on March 16, 2025
Runtime version: 21.0.6+8-b631.39 x86_64 (JCEF 122.1.9)
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Toolkit: sun.lwawt.macosx.LWCToolkit
GC: G1 Young Generation, G1 Concurrent GC, G1 Old Generation
Kotlin: 243.21565.193-IJ
java 17.0.10 2024-01-16 LTS
Java™ SE Runtime Environment (build 17.0.10+11-LTS-240)
Java HotSpot™ 64-Bit Server VM (build 17.0.10+11-LTS-240, mixed mode, sharing)
Operating System: macOS 12.7.6 (21H1320)
Metal Rendering: ON
File System: Case-Sensitive Journaled HFS+ (APFS)
Browser: Safari - Version 17.6 (17618.3.11.11.7, 17618)
Database: PostgreSQL 13
Hello Everyone
I am migrating my version 1.5.x user registration code to version 2.5.1, and I require assistance with a MainView locale problem.
Here is my use case workflow:
- An anonymous user opens the MainView with its default English locale (en); the MainView contains a select component, that allows the user to choose the locale: en, de_CH, or de_DE. Upon selecting the locale, the MainView is reloaded to display all of the components in the new locale; during the reload I also load an html page into an Iframe with the following code in the onInit() handler.
mainIFrame.setSrc("img/nfmain.” + VaadinSession.getCurrent().getLocale().toString() + ".html");
This step works correctly; the anonymous user can select each of the three locales, in any order and multiple times, and each time both the menu items in the MainView’s sidebar and the Iframe contents are updated correctly.
Please refer to this solution: Anonymous user MainView/LoginView display problem with Flow UI
-
The anonymous selects the de_CH (German [Switzerland]), and the entire MainView is reloaded correctly
-
The anonymous user then clicks on the “Benutzer registrieren” (“User Registration” ) menu item, and the “Benutzer registrieren” view is loaded with the correct de_CH locale (everything is in German [Switzerland])
-
The anonymous user enters his username, email address, etc. and clicks the “Registrieren” (“Register”) button.
-
The registration view controller creates a temporary User entity, with the information entered in the view’s fields, including the current locale (de_CH); a temporary token is generated and also stored in the new User entity. The user registration controller then sends an email to the user containing a link+token to the User Activation view.
-
The anonymous user opens his email, clicks on the link and the User Activation view is opened. The user is located using the token, the user’s locale is retrieved from his entity, the view’s locale is set with VaadinSession.getCurrent().setLocale(), only if it differs from the current locale, and then the view is reloaded with UI.getCurrent().getPage().reload(); This view is defined as follows:
@Route(value = "user-activation-view")
@ViewController(id = "UserActivationView")
@ViewDescriptor(path = "user-activation-view.xml")
public class UserActivationView extends StandardView {
- After the UserActivationView has reloaded, the user sees the view in his locale. He then enters his password and confirms it in a second passwordField. When he presses the Activate button, the new password is validated and saved, and the following code is executed to login him in and open the MainView with his locale, so he can start using the application in his language:
private void loginAsTrusted() {
log.info("Login without password.");
SystemAuthenticationToken token = new SystemAuthenticationToken(user.getUsername());
Authentication authentication = authenticationManager.authenticate(token);
VaadinServletRequest request = VaadinServletRequest.getCurrent();
VaadinServletResponse response = VaadinServletResponse.getCurrent();
SecurityContextRepository securityContextRepository =
new HttpSessionSecurityContextRepository();
SecurityContextHolderStrategy securityContextHolderStrategy =
SecurityContextHolder.getContextHolderStrategy();
SecurityContext context = securityContextHolderStrategy.createEmptyContext();
context.setAuthentication(authentication);
securityContextHolderStrategy.setContext(context);
securityContextRepository.saveContext(context, request, response);
viewNavigators.view(this, MainView.class)
.withQueryParameters(QueryParameters.of("locale", user.getLocaleString()))
.navigate();
}
Originally I only used the following to open the MainView…
viewNavigators.view(this, MainView.class)
.navigate();
But when I did this, the locale was not set properly for the all of the MainView’s components. In the MainView’s onInit() below, I load a static HTML page using the current locale, and this was/is working correctly. If the user’s locale was “de_CH” (German Switzerland), then the HTML page was loaded in the German language for Switzerland. However, all of the menu items in the sidebar were still in English (locale == “en”).
@Subscribe
public void onInit(final InitEvent event) {
mainIFrame.setSrc("img/nfmain." + VaadinSession.getCurrent().getLocale().toString() + ".html");
Authentication authentication = currentAuthentication.getAuthentication();
anonymousUser = (authentication instanceof AnonymousAuthenticationToken);
initLoginOverlay();
initLocales();
}
I therefore decided to also reload the MainView to change the menu item locale, as I did for the UserActivationView by sending the locale as a QueryParameter…
viewNavigators.view(this, MainView.class)
.withQueryParameters(QueryParameters.of("locale", user.getLocaleString()))
.navigate();
However, when I reload the MainView from its localeChange() handler, I get an error screen referring to the UserActivationView…
My MainView code looks like this (partial listing)…
@Subscribe
public void onInit(final InitEvent event) {
mainIFrame.setSrc("img/notefallmain." + VaadinSession.getCurrent().getLocale().toString() + ".html");
Authentication authentication = currentAuthentication.getAuthentication();
anonymousUser = (authentication instanceof AnonymousAuthenticationToken);
initLoginOverlay();
}
@Subscribe
public void onQueryParametersChange(final QueryParametersChangeEvent event) {
String receivedLocaleString;
Locale locale;
if (event.getQueryParameters().getSingleParameter("locale").isPresent()) {
receivedLocaleString = event.getQueryParameters()
.getSingleParameter("locale").get();
locale = currentUserService.stringToLocale(receivedLocaleString);
localeInitialized = !localeInitialized; // needed for the reload (1x)
} else if (VaadinSession.getCurrent().getLocale() != null) {
locale = VaadinSession.getCurrent().getLocale();
} else {
locale = currentUserService.stringToLocale("en"); // default
}
initLocales(locale);
}
@Subscribe
public void onBeforeShow(final BeforeShowEvent event) {
if (anonymousUser) {
return;
}
// Other code
}
@Subscribe
public void onReady(final ReadyEvent event) {
initComponentsVisibility();
localeInitialized = true;
}
protected void initLoginOverlay() {
loginOverlay = uiComponents.create(LoginOverlay.class);
getContent().addToDrawer(loginOverlay);
loginOverlay.setForgotPasswordButtonVisible(false);
loginOverlay.addLoginListener(this::onLogin);
LoginOverlay.LoginOverlayCustomFormArea loginOverlayCustomFormArea = loginOverlay.getCustomFormArea();
LoginRegistrationFragment loginRegistrationFragment = fragments.create(this, LoginRegistrationFragment.class);
loginOverlayCustomFormArea.add(loginRegistrationFragment);
}
protected void initComponentsVisibility() {
if (anonymousUser) {
userLocale.setVisible(true);
loginButton.setVisible(true);
} else {
userIndicator.setVisible(true);
logoutButton.setVisible(true);
}
}
protected void initLocales(Locale locale) {
LinkedHashMap<Locale, String> locales = coreProperties.getAvailableLocales().stream()
.collect(Collectors.toMap(Function.identity(), messageTools::getLocaleDisplayName, (s1, s2) -> s1,
LinkedHashMap::new));
ComponentUtils.setItemsMap(userLocale, locales);
userLocale.setValue(locale);
}
@Subscribe("userLocale")
public void onUserLocaleComponentValueChange(final AbstractField.ComponentValueChangeEvent<JmixSelect<Locale>, Locale> event) {
if ((event.getOldValue() == null) || (event.getValue() == null)) {
// do nothing
// initial setup
return;
}
VaadinSession.getCurrent().setLocale(event.getValue());
}
@Override
public void localeChange(LocaleChangeEvent event) {
if (localeInitialized) {
UI.getCurrent().getPage().reload();
}
}
Why is the reload() in the localeChange() method (directly above) trying to navigate to the UserActivationView?
Is this a bug? If not, how can I correct it?
Furthermore, is there a better way of doing all of this without the reloads?
Thank you in advance for your support.
Best regards
Chris