Skip to content

Commit 6f8509a

Browse files
committed
LPD-52709 Add a frontend resource request handler for I18N labels modules
1 parent 0e3dac2 commit 6f8509a

File tree

5 files changed

+295
-2
lines changed

5 files changed

+295
-2
lines changed

modules/apps/frontend-js/frontend-js-web/src/main/java/com/liferay/frontend/js/web/internal/js/importmaps/extender/FrontendJsWebDynamicJSImportMapsContributor.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package com.liferay.frontend.js.web.internal.js.importmaps.extender;
77

88
import com.liferay.frontend.js.importmaps.extender.DynamicJSImportMapsContributor;
9+
import com.liferay.frontend.js.web.internal.resource.handler.LanguageFrontendResourceRequestHandler;
910
import com.liferay.petra.string.StringPool;
1011
import com.liferay.portal.kernel.exception.PortalException;
1112
import com.liferay.portal.kernel.frontend.hashed.files.HashedFilesRegistry;
@@ -31,14 +32,19 @@ public void writeGlobalImports(
3132
HttpServletRequest httpServletRequest, Writer writer)
3233
throws IOException {
3334

34-
writer.write("\"@liferay/language/\": \"");
35+
writer.write(StringPool.QUOTE);
36+
writer.write(
37+
LanguageFrontendResourceRequestHandler.LANGUAGE_MODULE_PREFIX);
38+
writer.write("\": \"");
3539

3640
String cdnHost = _getCDNHost(httpServletRequest);
3741

3842
writer.write(cdnHost);
3943

4044
writer.write(_portal.getPathContext(httpServletRequest));
41-
writer.write("/o/js/language/\"");
45+
writer.write(
46+
LanguageFrontendResourceRequestHandler.LANGUAGE_URI_PREFIX);
47+
writer.write(StringPool.QUOTE);
4248

4349
_hashedFilesRegistry.forEach(
4450
(unhashedFileURI, hashedFileURI) -> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* SPDX-FileCopyrightText: (c) 2025 Liferay, Inc. https://liferay.com
3+
* SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
4+
*/
5+
6+
package com.liferay.frontend.js.web.internal.resource;
7+
8+
import com.liferay.petra.string.StringPool;
9+
import com.liferay.portal.kernel.json.JSONArray;
10+
import com.liferay.portal.kernel.json.JSONException;
11+
import com.liferay.portal.kernel.json.JSONFactory;
12+
import com.liferay.portal.kernel.json.JSONObject;
13+
import com.liferay.portal.kernel.language.Language;
14+
import com.liferay.portal.kernel.log.Log;
15+
import com.liferay.portal.kernel.log.LogFactoryUtil;
16+
import com.liferay.portal.kernel.util.ContentTypes;
17+
import com.liferay.portal.kernel.util.LocaleUtil;
18+
import com.liferay.portal.kernel.util.StringUtil;
19+
import com.liferay.portal.kernel.util.URLUtil;
20+
21+
import java.io.ByteArrayInputStream;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
25+
import java.net.URL;
26+
27+
import java.nio.charset.StandardCharsets;
28+
29+
import java.util.Locale;
30+
31+
/**
32+
* @author Iván Zaera Avellón
33+
*/
34+
public class LanguageFrontendResource implements FrontendResource {
35+
36+
public LanguageFrontendResource(
37+
JSONFactory jsonFactory, Language language, String languageId,
38+
long maxAge, boolean sendNoCache, URL url) {
39+
40+
_jsonFactory = jsonFactory;
41+
_language = language;
42+
_languageId = languageId;
43+
_maxAge = maxAge;
44+
_sendNoCache = sendNoCache;
45+
_url = url;
46+
}
47+
48+
@Override
49+
public String getContentType() {
50+
return ContentTypes.TEXT_JAVASCRIPT;
51+
}
52+
53+
@Override
54+
public String getETag() {
55+
return null;
56+
}
57+
58+
@Override
59+
public InputStream getInputStream() throws IOException {
60+
JSONArray languageKeysJSONArray = _getLanguageKeysJSONArray();
61+
62+
StringBuilder sb = new StringBuilder();
63+
64+
Locale locale = LocaleUtil.fromLanguageId(_languageId);
65+
66+
for (int i = 0; i < languageKeysJSONArray.length(); i++) {
67+
String key = languageKeysJSONArray.getString(i);
68+
69+
String label = _language.get(locale, key);
70+
71+
sb.append(StringPool.APOSTROPHE);
72+
sb.append(key.replaceAll("'", "\\\\'"));
73+
sb.append("':'");
74+
sb.append(label.replaceAll("'", "\\\\'"));
75+
sb.append("',\n");
76+
}
77+
78+
String content = StringUtil.replace(
79+
_TPL_JAVA_SCRIPT, new String[] {"[$LABELS$]"},
80+
new String[] {sb.toString()});
81+
82+
return new ByteArrayInputStream(
83+
content.getBytes(StandardCharsets.UTF_8));
84+
}
85+
86+
@Override
87+
public long getMaxAge() {
88+
return _maxAge;
89+
}
90+
91+
@Override
92+
public boolean isImmutable() {
93+
return false;
94+
}
95+
96+
@Override
97+
public boolean isSendNoCache() {
98+
return _sendNoCache;
99+
}
100+
101+
private static String _loadTemplate(String name) {
102+
try (InputStream inputStream =
103+
LanguageFrontendResource.class.getResourceAsStream(
104+
"dependencies/" + name)) {
105+
106+
return StringUtil.read(inputStream);
107+
}
108+
catch (Exception exception) {
109+
_log.error("Unable to read template " + name, exception);
110+
}
111+
112+
return StringPool.BLANK;
113+
}
114+
115+
private JSONArray _getLanguageKeysJSONArray() throws IOException {
116+
try {
117+
JSONObject jsonObject = _jsonFactory.createJSONObject(
118+
URLUtil.toString(_url));
119+
120+
return jsonObject.getJSONArray("keys");
121+
}
122+
catch (JSONException jsonException) {
123+
throw new IOException(
124+
"Invalid language JSON file " + _url, jsonException);
125+
}
126+
}
127+
128+
private static final String _TPL_JAVA_SCRIPT;
129+
130+
private static final Log _log = LogFactoryUtil.getLog(
131+
LanguageFrontendResource.class);
132+
133+
static {
134+
_TPL_JAVA_SCRIPT = _loadTemplate("all.js.tpl");
135+
}
136+
137+
private final JSONFactory _jsonFactory;
138+
private final Language _language;
139+
private final String _languageId;
140+
private final long _maxAge;
141+
private final boolean _sendNoCache;
142+
private final URL _url;
143+
144+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* SPDX-FileCopyrightText: (c) 2025 Liferay, Inc. https://liferay.com
3+
* SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
4+
*/
5+
6+
package com.liferay.frontend.js.web.internal.resource.handler;
7+
8+
import com.liferay.frontend.js.web.internal.configuration.FrontendCachingConfiguration;
9+
import com.liferay.frontend.js.web.internal.resource.FrontendResource;
10+
import com.liferay.frontend.js.web.internal.resource.LanguageFrontendResource;
11+
import com.liferay.petra.string.StringBundler;
12+
import com.liferay.petra.string.StringPool;
13+
import com.liferay.portal.configuration.module.configuration.ConfigurationProvider;
14+
import com.liferay.portal.kernel.frontend.hashed.files.HashedFilesRegistry;
15+
import com.liferay.portal.kernel.json.JSONFactory;
16+
import com.liferay.portal.kernel.language.Language;
17+
import com.liferay.portal.kernel.log.Log;
18+
import com.liferay.portal.kernel.log.LogFactoryUtil;
19+
import com.liferay.portal.kernel.module.configuration.ConfigurationException;
20+
import com.liferay.portal.kernel.util.Portal;
21+
22+
import jakarta.servlet.ServletException;
23+
import jakarta.servlet.http.HttpServletRequest;
24+
25+
import java.io.IOException;
26+
27+
import java.net.URL;
28+
29+
/**
30+
* @author Iván Zaera Avellón
31+
*/
32+
public class LanguageFrontendResourceRequestHandler
33+
implements FrontendResourceRequestHandler {
34+
35+
public static final String LANGUAGE_MODULE_PREFIX = "@liferay/language/";
36+
37+
public static final String LANGUAGE_URI_PREFIX = "/o/js/language/";
38+
39+
public LanguageFrontendResourceRequestHandler(
40+
ConfigurationProvider configurationProvider,
41+
HashedFilesRegistry hashedFilesRegistry, JSONFactory jsonFactory,
42+
Language language, Portal portal) {
43+
44+
_configurationProvider = configurationProvider;
45+
_hashedFilesRegistry = hashedFilesRegistry;
46+
_jsonFactory = jsonFactory;
47+
_language = language;
48+
_portal = portal;
49+
}
50+
51+
@Override
52+
public boolean canHandleRequest(HttpServletRequest httpServletRequest) {
53+
String requestURI = httpServletRequest.getRequestURI();
54+
55+
return requestURI.startsWith(LANGUAGE_URI_PREFIX);
56+
}
57+
58+
@Override
59+
public FrontendResource handleRequest(HttpServletRequest httpServletRequest)
60+
throws IOException, ServletException {
61+
62+
String requestURI = httpServletRequest.getRequestURI();
63+
64+
requestURI = requestURI.substring(LANGUAGE_URI_PREFIX.length());
65+
66+
String[] requestURIParts = requestURI.split(StringPool.SLASH);
67+
68+
if ((requestURIParts.length != 3) ||
69+
!requestURIParts[2].equals("all.js")) {
70+
71+
return null;
72+
}
73+
74+
long maxAge = 3600;
75+
boolean sendNoCache = false;
76+
77+
long companyId = _portal.getCompanyId(httpServletRequest);
78+
79+
try {
80+
FrontendCachingConfiguration frontendCachingConfiguration =
81+
_configurationProvider.getCompanyConfiguration(
82+
FrontendCachingConfiguration.class, companyId);
83+
84+
maxAge = frontendCachingConfiguration.labelsModulesMaxAge();
85+
sendNoCache =
86+
frontendCachingConfiguration.sendNoCacheForLabelsModules();
87+
}
88+
catch (ConfigurationException configurationException) {
89+
_log.error(
90+
"Unable to get frontend caching configuration: will use " +
91+
"reasonable defaults instead",
92+
configurationException);
93+
}
94+
95+
URL resourceURL = _hashedFilesRegistry.getResource(
96+
StringBundler.concat(
97+
Portal.PATH_MODULE, StringPool.SLASH, requestURIParts[1],
98+
"/language.json"));
99+
100+
return new LanguageFrontendResource(
101+
_jsonFactory, _language, requestURIParts[0], maxAge, sendNoCache,
102+
resourceURL);
103+
}
104+
105+
private static final Log _log = LogFactoryUtil.getLog(
106+
LanguageFrontendResourceRequestHandler.class);
107+
108+
private final ConfigurationProvider _configurationProvider;
109+
private final HashedFilesRegistry _hashedFilesRegistry;
110+
private final JSONFactory _jsonFactory;
111+
private final Language _language;
112+
private final Portal _portal;
113+
114+
}

modules/apps/frontend-js/frontend-js-web/src/main/java/com/liferay/frontend/js/web/internal/servlet/filter/FrontendResourceFilter.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@
99
import com.liferay.frontend.js.web.internal.resource.FrontendResource;
1010
import com.liferay.frontend.js.web.internal.resource.handler.FrontendResourceRequestHandler;
1111
import com.liferay.frontend.js.web.internal.resource.handler.HashedFileFrontendResourceRequestHandler;
12+
import com.liferay.frontend.js.web.internal.resource.handler.LanguageFrontendResourceRequestHandler;
1213
import com.liferay.frontend.js.web.internal.resource.handler.StyleSheetFrontendResourceRequestHandler;
1314
import com.liferay.petra.io.StreamUtil;
1415
import com.liferay.petra.string.StringPool;
1516
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;
17+
import com.liferay.portal.configuration.module.configuration.ConfigurationProvider;
1618
import com.liferay.portal.kernel.frontend.hashed.files.HashedFilesRegistry;
19+
import com.liferay.portal.kernel.json.JSONFactory;
20+
import com.liferay.portal.kernel.language.Language;
1721
import com.liferay.portal.kernel.service.ThemeLocalService;
1822
import com.liferay.portal.kernel.servlet.HttpHeaders;
1923
import com.liferay.portal.kernel.util.ContentTypes;
@@ -101,6 +105,11 @@ protected void activate(Map<String, Object> properties) {
101105
frontendCachingConfiguration, _hashedFilesRegistry, _portal,
102106
_themeLocalService));
103107

108+
frontendResourceRequestHandlers.add(
109+
new LanguageFrontendResourceRequestHandler(
110+
_configurationProvider, _hashedFilesRegistry, _jsonFactory,
111+
_language, _portal));
112+
104113
_frontendResourceRequestHandlers.set(frontendResourceRequestHandlers);
105114
}
106115

@@ -199,13 +208,22 @@ protected void send(
199208
}
200209
}
201210

211+
@Reference
212+
private ConfigurationProvider _configurationProvider;
213+
202214
private final AtomicReference<List<FrontendResourceRequestHandler>>
203215
_frontendResourceRequestHandlers = new AtomicReference<>(
204216
Collections.emptyList());
205217

206218
@Reference
207219
private HashedFilesRegistry _hashedFilesRegistry;
208220

221+
@Reference
222+
private JSONFactory _jsonFactory;
223+
224+
@Reference
225+
private Language _language;
226+
209227
@Reference
210228
private Portal _portal;
211229

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const labels = {
2+
[$LABELS$]};
3+
4+
if (Liferay && Liferay.Language && Liferay.Language._cache) {
5+
Liferay.Language._cache = {
6+
...Liferay.Language._cache,
7+
...labels
8+
};
9+
}
10+
11+
export default labels;

0 commit comments

Comments
 (0)