From a9abd318ab3a5b8c1ad453a2d88b1e4046fad784 Mon Sep 17 00:00:00 2001 From: czp13 <61667986+czp13@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:19:15 +0200 Subject: [PATCH] fix/feature/optimization: use lang attribute in the html tag (#16410) (CP:23.3) (#16500) * fix/feature/optimization: use lang attribute in the html tag (#16410) Fix and feature add language to HTML tags dynamically: 0. It is checking if it is already added (if not, then it will try to add it based on the following rules: 1. trying to get the locale from the UI 2. if there is an I18N provider then the first locale will be used 3. last option is to fallback to the Locale.getDefault() This is the same logic as is used in the Component class as well, to have consistency within the application. Covering tests and optimization, refactoring happened and added. Thanks to @knoobie and @tepi for reviews, ideas, and optimizations. Fixes # (issue): * Fix my merge/conflict work/commit discrepencies. (Did not accept all the correc changes first, but with this one it shall be good) * mvn formatter:format executed. * Fix build error (comment format:/ hopefully!) --- .../com/vaadin/flow/component/Component.java | 32 ++++++----------- .../com/vaadin/flow/internal/LocaleUtil.java | 36 ++++++++++++++++++- .../com/vaadin/flow/server/VaadinService.java | 13 +++---- .../IndexHtmlRequestHandler.java | 10 +++++- .../JavaScriptBootstrapHandler.java | 10 +++--- .../vaadin/flow/server/frontend/index.html | 2 +- .../IndexHtmlRequestHandlerTest.java | 13 ++++++- .../META-INF/VAADIN/webapp/index.html | 2 +- .../frontend/index.html | 2 +- flow-tests/test-ccdm/frontend/index.html | 2 +- .../vite-basics/frontend/index.html | 2 +- .../frontend/index.html | 2 +- .../vite-embedded/frontend/index.html | 2 +- .../vite-production/frontend/index.html | 2 +- .../src/main/webapp/offline.html | 2 +- .../vite-pwa-production/frontend/index.html | 2 +- .../vite-pwa/frontend/index.html | 2 +- .../test-pwa/src/main/webapp/offline.html | 2 +- .../src/main/webapp/offline.html | 2 +- flow-tests/test-themes/frontend/index.html | 2 +- 20 files changed, 89 insertions(+), 53 deletions(-) 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 64cbf401287..58cabd94a4a 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; @@ -32,9 +31,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; /** @@ -618,10 +617,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 + "}!"); } @@ -642,10 +642,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 + "}!"); } @@ -668,7 +669,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 + "}!"); } @@ -692,7 +693,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 + "}!"); } @@ -735,11 +736,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. *

@@ -751,15 +747,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 8919e0f3003..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 @@ -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 9d727249207..19863c677bc 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 @@ -810,8 +810,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 @@ -957,6 +957,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 { @@ -1023,13 +1024,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 8994d673455..8d70df8c230 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2022 Vaadin Ltd. + * Copyright 2000-2023 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,6 +19,7 @@ import java.io.IOException; import java.io.Serializable; import java.io.UncheckedIOException; +import java.util.Locale; import java.util.Optional; import org.jsoup.Jsoup; @@ -35,6 +36,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; @@ -84,6 +86,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() 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 55aa39f50d7..59d551c5f49 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2022 Vaadin Ltd. + * Copyright 2000-2023 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 @@ -52,13 +52,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 3ca56aba6be..1f900b38f2a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2022 Vaadin Ltd. + * Copyright 2000-2023 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 @@ -165,6 +165,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-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-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 @@ - + - + - +