diff --git a/flow-server/src/main/java/com/vaadin/flow/component/Component.java b/flow-server/src/main/java/com/vaadin/flow/component/Component.java index 89a9862bdab..c8f97836dee 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/Component.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/Component.java @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.Locale; import java.util.Optional; -import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.Stream.Builder; @@ -33,9 +32,9 @@ import com.vaadin.flow.dom.ShadowRoot; import com.vaadin.flow.i18n.I18NProvider; import com.vaadin.flow.internal.AnnotationReader; +import com.vaadin.flow.internal.LocaleUtil; import com.vaadin.flow.internal.nodefeature.ElementData; import com.vaadin.flow.server.Attributes; -import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.shared.Registration; /** @@ -636,10 +635,11 @@ protected boolean isTemplateMapped() { * null) */ public String getTranslation(String key, Object... params) { - final Optional i18NProvider = getI18NProvider(); + final Optional i18NProvider = LocaleUtil + .getI18NProvider(); return i18NProvider .map(i18n -> i18n.getTranslation(key, - getLocale(() -> i18NProvider), params)) + LocaleUtil.getLocale(() -> i18NProvider), params)) .orElseGet(() -> "!{" + key + "}!"); } @@ -660,10 +660,11 @@ public String getTranslation(String key, Object... params) { * null) */ public String getTranslation(Object key, Object... params) { - final Optional i18NProvider = getI18NProvider(); + final Optional i18NProvider = LocaleUtil + .getI18NProvider(); return i18NProvider .map(i18n -> i18n.getTranslation(key, - getLocale(() -> i18NProvider), params)) + LocaleUtil.getLocale(() -> i18NProvider), params)) .orElseGet(() -> "!{" + key + "}!"); } @@ -686,7 +687,7 @@ public String getTranslation(Object key, Object... params) { */ @Deprecated public String getTranslation(String key, Locale locale, Object... params) { - return getI18NProvider() + return LocaleUtil.getI18NProvider() .map(i18n -> i18n.getTranslation(key, locale, params)) .orElseGet(() -> "!{" + key + "}!"); } @@ -710,7 +711,7 @@ public String getTranslation(String key, Locale locale, Object... params) { */ @Deprecated public String getTranslation(Object key, Locale locale, Object... params) { - return getI18NProvider() + return LocaleUtil.getI18NProvider() .map(i18n -> i18n.getTranslation(key, locale, params)) .orElseGet(() -> "!{" + key + "}!"); } @@ -753,11 +754,6 @@ public String getTranslation(Locale locale, Object key, Object... params) { return getTranslation(key, locale, params); } - private Optional getI18NProvider() { - return Optional.ofNullable( - VaadinService.getCurrent().getInstantiator().getI18NProvider()); - } - /** * Gets the locale for this component. *

@@ -769,15 +765,7 @@ private Optional getI18NProvider() { * @return the component locale */ protected Locale getLocale() { - return getLocale(() -> getI18NProvider()); - } - - private Locale getLocale(Supplier> i18NProvider) { - return Optional.ofNullable(UI.getCurrent()).map(UI::getLocale) - .or(() -> i18NProvider.get() - .map(I18NProvider::getProvidedLocales) - .flatMap(locales -> locales.stream().findFirst())) - .orElseGet(Locale::getDefault); + return LocaleUtil.getLocale(LocaleUtil::getI18NProvider); } /** diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/LocaleUtil.java b/flow-server/src/main/java/com/vaadin/flow/internal/LocaleUtil.java index 14c6d3f5458..22c645ca571 100644 --- a/flow-server/src/main/java/com/vaadin/flow/internal/LocaleUtil.java +++ b/flow-server/src/main/java/com/vaadin/flow/internal/LocaleUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2023 Vaadin Ltd. + * Copyright 2000-2022 Vaadin Ltd. * * 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 @@ -19,15 +19,18 @@ import java.util.List; import java.util.Locale; import java.util.Optional; +import java.util.function.Supplier; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.i18n.I18NProvider; import com.vaadin.flow.server.VaadinRequest; +import com.vaadin.flow.server.VaadinService; /** * Utility class for locale handling. *

* For internal use only. May be renamed or removed in a future release. * - * @since 1.0 */ public final class LocaleUtil { @@ -84,4 +87,35 @@ public static Optional getLocaleMatchByLanguage( } return Optional.ofNullable(foundLocale); } + + /** + * Get the I18nProvider from the current VaadinService. + *

+ * + * @return the optional value of I18nProvider + */ + public static Optional getI18NProvider() { + return Optional.ofNullable( + VaadinService.getCurrent().getInstantiator().getI18NProvider()); + } + + /** + * Get the locale for the given UI. + *

+ * - If UI is not null, then it is used to get the locale, - if UI is null, + * then the I18NProvider providedLocales first match will be returned, - if + * I18NProvider is null, then default locale is returned. + * + * @param i18NProvider + * - supplier for the i18n provider + * @return the locale for the UI + */ + public static Locale getLocale( + Supplier> i18NProvider) { + return Optional.ofNullable(UI.getCurrent()).map(UI::getLocale) + .or(() -> i18NProvider.get() + .map(I18NProvider::getProvidedLocales) + .flatMap(locales -> locales.stream().findFirst())) + .orElseGet(Locale::getDefault); + } } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java index 1a7afdcaa22..86fb5043ecd 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinService.java @@ -779,8 +779,8 @@ protected Lock lockSession(WrappedSession wrappedSession) { /** * Releases the lock for the given session for this service instance. - * Typically you want to call {@link VaadinSession#unlock()} instead of this - * method. + * Typically, you want to call {@link VaadinSession#unlock()} instead of + * this method. *

* Note: The method and its signature has been changed to get lock instance * as parameter in Vaadin X.X.0. If you have overriden this method, you need @@ -926,6 +926,7 @@ private VaadinSession createAndRegisterSession(VaadinRequest request) { private void setLocale(VaadinRequest request, VaadinSession session) { I18NProvider provider = getInstantiator().getI18NProvider(); List providedLocales = provider.getProvidedLocales(); + if (providedLocales.size() == 1) { session.setLocale(providedLocales.get(0)); } else { @@ -992,13 +993,7 @@ protected VaadinSession getExistingSession(VaadinRequest request, final WrappedSession session = getWrappedSession(request, allowSessionCreation); - VaadinSession vaadinSession = loadSession(session); - - if (vaadinSession == null) { - return null; - } - - return vaadinSession; + return loadSession(session); } /** diff --git a/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java b/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java index 4602acbf8bc..5fb30e6bab9 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java @@ -15,13 +15,10 @@ */ package com.vaadin.flow.server.communication; -import static com.vaadin.flow.component.UI.SERVER_ROUTING; -import static com.vaadin.flow.shared.ApplicationConstants.CONTENT_TYPE_TEXT_HTML_UTF_8; -import static java.nio.charset.StandardCharsets.UTF_8; - import java.io.IOException; import java.io.Serializable; import java.io.UncheckedIOException; +import java.util.Locale; import java.util.Optional; import org.jsoup.Jsoup; @@ -37,6 +34,7 @@ import com.vaadin.flow.internal.BootstrapHandlerHelper; import com.vaadin.flow.internal.BrowserLiveReload; import com.vaadin.flow.internal.BrowserLiveReloadAccessor; +import com.vaadin.flow.internal.LocaleUtil; import com.vaadin.flow.internal.UsageStatisticsExporter; import com.vaadin.flow.internal.springcsrf.SpringCsrfTokenUtil; import com.vaadin.flow.server.AppShellRegistry; @@ -55,6 +53,10 @@ import elemental.json.JsonObject; import elemental.json.impl.JsonUtil; +import static com.vaadin.flow.component.UI.SERVER_ROUTING; +import static com.vaadin.flow.shared.ApplicationConstants.CONTENT_TYPE_TEXT_HTML_UTF_8; +import static java.nio.charset.StandardCharsets.UTF_8; + /** * This class is responsible for serving the index.html according * to the template provided in the frontend folder. The handler will calculate @@ -85,6 +87,12 @@ public boolean synchronizedHandleRequest(VaadinSession session, prependBaseHref(request, indexDocument); + Element htmlElement = indexDocument.getElementsByTag("html").get(0); + if (!htmlElement.hasAttr("lang")) { + Locale locale = LocaleUtil.getLocale(LocaleUtil::getI18NProvider); + htmlElement.attr("lang", locale.getLanguage()); + } + JsonObject initialJson = Json.createObject(); if (service.getBootstrapInitialPredicate() @@ -132,7 +140,7 @@ public boolean synchronizedHandleRequest(VaadinSession session, redirectToOldBrowserPageWhenNeeded(indexDocument); - // modify the page based on registered IndexHtmlRequestListener:s + // modify the page based on registered IndexHtmlRequestListener: service.modifyIndexHtmlResponse(indexHtmlResponse); if (!config.isProductionMode()) { diff --git a/flow-server/src/main/java/com/vaadin/flow/server/communication/JavaScriptBootstrapHandler.java b/flow-server/src/main/java/com/vaadin/flow/server/communication/JavaScriptBootstrapHandler.java index 9b1b4681459..68ddc554274 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/communication/JavaScriptBootstrapHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/communication/JavaScriptBootstrapHandler.java @@ -54,13 +54,13 @@ /** * Processes a 'start' request type from the client to initialize server session - * and UI. It returns a JSON response with everything needed to bootstrapping - * flow views. + * and UI. It returns a JSON response with everything needed to bootstrap flow + * views. *

* The handler is for client driven projects where `index.html` does not contain - * bootstrap data. Bootstraping is the responsability of the `@vaadin/flow` + * bootstrap data. Bootstrapping is the responsibility of the `@vaadin/flow` * client that is able to ask the server side to create the vaadin session and - * do the boostrapping lazily. + * do the bootstrapping lazily. *

* For internal use only. May be renamed or removed in a future release. * diff --git a/flow-server/src/main/resources/com/vaadin/flow/server/frontend/index.html b/flow-server/src/main/resources/com/vaadin/flow/server/frontend/index.html index a5cdd4018a7..d36e593475c 100644 --- a/flow-server/src/main/resources/com/vaadin/flow/server/frontend/index.html +++ b/flow-server/src/main/resources/com/vaadin/flow/server/frontend/index.html @@ -3,7 +3,7 @@ This file is auto-generated by Vaadin. --> - + diff --git a/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java b/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java index 98db00c3254..e99ffab906d 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java @@ -167,6 +167,17 @@ public void serveNotFoundIndexHtml_requestWithRootPath_failsWithIOException() Assert.assertEquals(expectedError, expectedException.getMessage()); } + @Test + public void serveIndexHtml_language_attribute_is_present() + throws IOException { + indexHtmlRequestHandler.synchronizedHandleRequest(session, + createVaadinRequest("/"), response); + String indexHtml = responseOutput + .toString(StandardCharsets.UTF_8.name()); + Assert.assertTrue("Response should have a language attribute", + indexHtml.contains(" - + diff --git a/flow-server/src/test/resources/frontend/index.html b/flow-server/src/test/resources/frontend/index.html index 0d45c39e173..8cde5d6785e 100644 --- a/flow-server/src/test/resources/frontend/index.html +++ b/flow-server/src/test/resources/frontend/index.html @@ -3,7 +3,7 @@ This file is auto-generated by Vaadin. --> - + diff --git a/flow-tests/test-ccdm-flow-navigation/frontend/index.html b/flow-tests/test-ccdm-flow-navigation/frontend/index.html index 870da89d663..8679d7796ff 100644 --- a/flow-tests/test-ccdm-flow-navigation/frontend/index.html +++ b/flow-tests/test-ccdm-flow-navigation/frontend/index.html @@ -1,5 +1,5 @@ - + diff --git a/flow-tests/test-ccdm/frontend/index.html b/flow-tests/test-ccdm/frontend/index.html index 96523a9c51f..06d1d057b54 100644 --- a/flow-tests/test-ccdm/frontend/index.html +++ b/flow-tests/test-ccdm/frontend/index.html @@ -1,5 +1,5 @@ - + diff --git a/flow-tests/test-express-build/test-theme-dev-bundle/frontend/index.html b/flow-tests/test-express-build/test-theme-dev-bundle/frontend/index.html index 5ca0ad0a1a5..10af5fff52a 100644 --- a/flow-tests/test-express-build/test-theme-dev-bundle/frontend/index.html +++ b/flow-tests/test-express-build/test-theme-dev-bundle/frontend/index.html @@ -3,7 +3,7 @@ This file is auto-generated by Vaadin. --> - + diff --git a/flow-tests/test-frontend/vite-basics/frontend/index.html b/flow-tests/test-frontend/vite-basics/frontend/index.html index 5c605f3d398..c4e0f277dc6 100644 --- a/flow-tests/test-frontend/vite-basics/frontend/index.html +++ b/flow-tests/test-frontend/vite-basics/frontend/index.html @@ -3,7 +3,7 @@ This file is auto-generated by Vaadin. --> - + diff --git a/flow-tests/test-frontend/vite-embedded-webcomponent-resync/frontend/index.html b/flow-tests/test-frontend/vite-embedded-webcomponent-resync/frontend/index.html index a5cdd4018a7..d36e593475c 100644 --- a/flow-tests/test-frontend/vite-embedded-webcomponent-resync/frontend/index.html +++ b/flow-tests/test-frontend/vite-embedded-webcomponent-resync/frontend/index.html @@ -3,7 +3,7 @@ This file is auto-generated by Vaadin. --> - + diff --git a/flow-tests/test-frontend/vite-embedded/frontend/index.html b/flow-tests/test-frontend/vite-embedded/frontend/index.html index a5cdd4018a7..d36e593475c 100644 --- a/flow-tests/test-frontend/vite-embedded/frontend/index.html +++ b/flow-tests/test-frontend/vite-embedded/frontend/index.html @@ -3,7 +3,7 @@ This file is auto-generated by Vaadin. --> - + diff --git a/flow-tests/test-frontend/vite-production/frontend/index.html b/flow-tests/test-frontend/vite-production/frontend/index.html index fc96e459156..22d40f41733 100644 --- a/flow-tests/test-frontend/vite-production/frontend/index.html +++ b/flow-tests/test-frontend/vite-production/frontend/index.html @@ -1,6 +1,6 @@ - + diff --git a/flow-tests/test-frontend/vite-pwa-custom-offline-path/src/main/webapp/offline.html b/flow-tests/test-frontend/vite-pwa-custom-offline-path/src/main/webapp/offline.html index 376ee42485b..8ac03cc92d9 100644 --- a/flow-tests/test-frontend/vite-pwa-custom-offline-path/src/main/webapp/offline.html +++ b/flow-tests/test-frontend/vite-pwa-custom-offline-path/src/main/webapp/offline.html @@ -1,5 +1,5 @@ - + - + diff --git a/flow-tests/test-frontend/vite-pwa/frontend/index.html b/flow-tests/test-frontend/vite-pwa/frontend/index.html index a5cdd4018a7..d36e593475c 100644 --- a/flow-tests/test-frontend/vite-pwa/frontend/index.html +++ b/flow-tests/test-frontend/vite-pwa/frontend/index.html @@ -3,7 +3,7 @@ This file is auto-generated by Vaadin. --> - + diff --git a/flow-tests/test-pwa/src/main/webapp/offline.html b/flow-tests/test-pwa/src/main/webapp/offline.html index e9fd4d16f1f..a6173559a4c 100644 --- a/flow-tests/test-pwa/src/main/webapp/offline.html +++ b/flow-tests/test-pwa/src/main/webapp/offline.html @@ -1,5 +1,5 @@ - + - + - +