flow icon indicating copy to clipboard operation
flow copied to clipboard

Lang attribute on the html tag is not in sync with the UI locale

Open jcgueriaud1 opened this issue 6 months ago • 1 comments

Description of the bug

The lang attribute on the html tag is always set with the default language tag (the first in the the I18nProvider).

That's an accessibility issue (Success Criterion 3.1.1 Language of Page level A) : https://www.w3.org/TR/WCAG21/#language-of-page

Expected behavior

When I have multiple languages and I'm switching my application language, I want the lang attribute on the html tag set with the correct language tag, not the default one.

Minimal reproducible example

See https://github.com/jcgueriaud1/translation-test Start the application and go to http://localhost:8080/ You can see the html lang tag which is never updated.

The lang attribute is set here: https://github.com/vaadin/flow/blob/main/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java#L105 The UI is always null (since it always happens before, if I'm not doing a mistake), so it will always take the default (first locale in the I18nProvider) The UI locale is set based on the VaadinSession. So you have to manage manually the lang attribute for the html if you have multiple languages. Here is a code example to change it based on a cookie:

@Component
public class ApplicationFormBaseUIInitializer implements VaadinServiceInitListener {

    @Override
    public void serviceInit(ServiceInitEvent event) {
        event.addIndexHtmlRequestListener(indexHtmlResponse -> {
            String lang = readCookie(indexHtmlResponse.getVaadinRequest().getCookies(), "locale").orElse("de-DE");
            Locale locale = Locale.forLanguageTag(lang);
            indexHtmlResponse.getDocument().getElementsByTag("html")
                    .attr("lang", locale.getLanguage());

            VaadinSession.getCurrent().setLocale(locale);
        });
    }

    public Optional<String> readCookie(Cookie[] cookies, String key) {
        if (cookies == null) {
            return Optional.empty();
        }
        return Arrays.stream(cookies)
                .filter(c -> key.equals(c.getName()))
                .map(Cookie::getValue)
                .findFirst();
    }
}

Versions

  • Vaadin / Flow version: 24.3.3
  • Java version: 21 / 17

jcgueriaud1 avatar Feb 01 '24 08:02 jcgueriaud1

Related: https://github.com/vaadin/flow/issues/15573

tepi avatar Feb 07 '24 12:02 tepi