flow icon indicating copy to clipboard operation
flow copied to clipboard

Vaadin should set the language (html lang attribute) on each page

Open jflamy opened this issue 3 years ago • 3 comments

Describe your motivation

I have a Flow v23 application where the users can select the language on each tab they open. But then frontend/index.html states that the page is lang="en" in spite of the content being actually in Spanish (for example). Then Auto-translation kicks in (many people auto translate from English to their language) and undesirable results happen.

The W3C recommended way to specify language is to use the html lang tag, and using Content-Language headers is not the recommended approach.

Describe the solution you'd like

  • A way for the com.vaadin.flow.component.page.AppShellConfigurator#configurePage to change the html lang attribute.
  • Also, setting headers for a page should be documented (see code below)

Describe alternatives you've considered

  • I currently remove the lang="en" from the index.html to let the translator guess.
  • I force a header, and I force a http-equiv tag, as follows.
    @Override
    public void configurePage(AppShellSettings settings) {
        HttpServletResponse response = VaadinServletResponse.getCurrent().getHttpServletResponse();
        response.addHeader("Content-Language",  MySession.getLocale().toString().replace("_", "-"));
        settings.addInlineWithContents("<meta http-equiv='content-language' content='"+MySession.getLocale().toString().replace("_", "-")+"'>",  null);
        settings.addLink("shortcut icon", "icons/owlcms.ico");
        settings.addFavIcon("icon", "icons/owlcms.png", "96x96");
    }

Additional context

Generally improve the page configurator documentation to explain that it is invoked on every page. Add a hook to call soup or some such to dynamically manipulate the index.

jflamy avatar Dec 25 '22 15:12 jflamy

It's already possible with something like this on initial page load, where you for example can read a user's setting:


@Component
public class UserIndexHtmlRequestListener implements IndexHtmlRequestListener {

  private final UserService service;

  @Autowired
  public UserIndexHtmlRequestListener(UserService service) {
    this.service = service;
  }

  @Override
  public void modifyIndexHtmlResponse(IndexHtmlResponse indexHtmlResponse) {
    indexHtmlResponse.getDocument().getElementsByTag("html")
      .attr("lang", service.getCurrentUserLanguage());
  }
}

knoobie avatar Dec 27 '22 07:12 knoobie

Thanks!

The magic required to get this to work without SpringBoot is as follows. See the VaadinServiceInitListener documentation for how to get the config to work. I did not try to figure out how to get the current language from the user service as I already had another way to get it.

@Push
@Theme(themeClass=Lumo.class)
public class AppShell implements AppShellConfigurator, VaadinServiceInitListener, IndexHtmlRequestListener {
    
    /**
     * @see com.vaadin.flow.component.page.AppShellConfigurator#configurePage(com.vaadin.flow.server.AppShellSettings)
     */
    @Override
    public void configurePage(AppShellSettings settings) {
        settings.addLink("shortcut icon", "icons/owlcms.ico");
        settings.addFavIcon("icon", "icons/owlcms.png", "96x96");
    }

    private String getCurrentUserLanguage() {
        // retrieves a session attribute -- use whatever your application provides
        return OwlcmsSession.getLocale().toString().replace("_", "-");
    }
    
    /**
     * @see com.vaadin.flow.server.VaadinServiceInitListener#serviceInit(com.vaadin.flow.server.ServiceInitEvent)
     */
    @Override
    public void serviceInit(ServiceInitEvent serviceInitEvent) {
        serviceInitEvent.addIndexHtmlRequestListener(this);
    }

    /**
     * @see com.vaadin.flow.server.communication.IndexHtmlRequestListener#modifyIndexHtmlResponse(com.vaadin.flow.server.communication.IndexHtmlResponse)
     */
    @Override
    public void modifyIndexHtmlResponse(IndexHtmlResponse indexHtmlResponse) {
        indexHtmlResponse.getDocument().getElementsByTag("html").attr("lang", getCurrentUserLanguage());
    }
}

jflamy avatar Dec 27 '22 16:12 jflamy

Some updates have been done since Vaadin 23. In Vaadin 24.x, if your application has only one language you can set the value in the frontend/index.html, the file is generated by Vaadin but can be customized. If the tag is not set in the frontend/index.html then the language will be set with the default value of the I18nProvider. So if you are using only one language and a I18nProvider that should be fine.

If you are using multiple languages then you need to set it in the modifyIndexHtmlResponse as mentioned above.

jcgueriaud1 avatar Feb 08 '24 09:02 jcgueriaud1