diff --git a/src/main/java/org/qortal/api/HTMLParser.java b/src/main/java/org/qortal/api/HTMLParser.java
index 4887cf84..f5af7138 100644
--- a/src/main/java/org/qortal/api/HTMLParser.java
+++ b/src/main/java/org/qortal/api/HTMLParser.java
@@ -1,14 +1,13 @@
package org.qortal.api;
+
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.qortal.arbitrary.misc.Service;
-
import java.util.Objects;
-
public class HTMLParser {
private static final Logger LOGGER = LogManager.getLogger(HTMLParser.class);
@@ -22,10 +21,11 @@ public class HTMLParser {
private String identifier;
private String path;
private String theme;
+ private String lang;
private boolean usingCustomRouting;
public HTMLParser(String resourceId, String inPath, String prefix, boolean includeResourceIdInPrefix, byte[] data,
- String qdnContext, Service service, String identifier, String theme, boolean usingCustomRouting) {
+ String qdnContext, Service service, String identifier, String theme, boolean usingCustomRouting, String lang) {
String inPathWithoutFilename = inPath.contains("/") ? inPath.substring(0, inPath.lastIndexOf('/')) : String.format("/%s",inPath);
this.qdnBase = includeResourceIdInPrefix ? String.format("%s/%s", prefix, resourceId) : prefix;
this.qdnBaseWithPath = includeResourceIdInPrefix ? String.format("%s/%s%s", prefix, resourceId, inPathWithoutFilename) : String.format("%s%s", prefix, inPathWithoutFilename);
@@ -36,6 +36,7 @@ public class HTMLParser {
this.identifier = identifier;
this.path = inPath;
this.theme = theme;
+ this.lang = lang;
this.usingCustomRouting = usingCustomRouting;
}
@@ -61,9 +62,13 @@ public class HTMLParser {
String identifier = this.identifier != null ? this.identifier.replace("\\", "").replace("\"","\\\"") : "";
String path = this.path != null ? this.path.replace("\\", "").replace("\"","\\\"") : "";
String theme = this.theme != null ? this.theme.replace("\\", "").replace("\"","\\\"") : "";
+ String lang = this.lang != null ? this.lang.replace("\\", "").replace("\"", "\\\"") : "";
String qdnBase = this.qdnBase != null ? this.qdnBase.replace("\\", "").replace("\"","\\\"") : "";
String qdnBaseWithPath = this.qdnBaseWithPath != null ? this.qdnBaseWithPath.replace("\\", "").replace("\"","\\\"") : "";
- String qdnContextVar = String.format("", qdnContext, theme, service, name, identifier, path, qdnBase, qdnBaseWithPath);
+ String qdnContextVar = String.format(
+ "",
+ qdnContext, theme, lang, service, name, identifier, path, qdnBase, qdnBaseWithPath
+ );
head.get(0).prepend(qdnContextVar);
// Add base href tag
diff --git a/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java b/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java
index 7972c551..bdaf1ced 100644
--- a/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java
+++ b/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java
@@ -142,10 +142,20 @@ public class DevProxyServerResource {
}
}
+ String lang = request.getParameter("lang");
+ if (lang == null || lang.isBlank()) {
+ lang = "en"; // fallback
+ }
+
+ String theme = request.getParameter("theme");
+ if (theme == null || theme.isBlank()) {
+ theme = "light";
+ }
+
// Parse and modify output if needed
if (HTMLParser.isHtmlFile(filename)) {
// HTML file - needs to be parsed
- HTMLParser htmlParser = new HTMLParser("", inPath, "", false, data, "proxy", Service.APP, null, "light", true);
+ HTMLParser htmlParser = new HTMLParser("", inPath, "", false, data, "proxy", Service.APP, null, theme , true, lang);
htmlParser.addAdditionalHeaderTags();
response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' data: blob:; img-src 'self' data: blob:; connect-src 'self' ws:; font-src 'self' data:;");
response.setContentType(con.getContentType());
diff --git a/src/main/java/org/qortal/api/restricted/resource/RenderResource.java b/src/main/java/org/qortal/api/restricted/resource/RenderResource.java
index 5eb169a4..3bab7bf3 100644
--- a/src/main/java/org/qortal/api/restricted/resource/RenderResource.java
+++ b/src/main/java/org/qortal/api/restricted/resource/RenderResource.java
@@ -71,33 +71,33 @@ public class RenderResource {
@Path("/signature/{signature}")
@SecurityRequirement(name = "apiKey")
public HttpServletResponse getIndexBySignature(@PathParam("signature") String signature,
- @QueryParam("theme") String theme) {
+ @QueryParam("theme") String theme, @QueryParam("lang") String lang) {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
- return this.get(signature, ResourceIdType.SIGNATURE, null, null, "/", null, "/render/signature", true, true, theme);
+ return this.get(signature, ResourceIdType.SIGNATURE, null, null, "/", null, "/render/signature", true, true, theme, lang);
}
@GET
@Path("/signature/{signature}/{path:.*}")
@SecurityRequirement(name = "apiKey")
public HttpServletResponse getPathBySignature(@PathParam("signature") String signature, @PathParam("path") String inPath,
- @QueryParam("theme") String theme) {
+ @QueryParam("theme") String theme, @QueryParam("lang") String lang) {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
- return this.get(signature, ResourceIdType.SIGNATURE, null, null, inPath,null, "/render/signature", true, true, theme);
+ return this.get(signature, ResourceIdType.SIGNATURE, null, null, inPath,null, "/render/signature", true, true, theme, lang);
}
@GET
@Path("/hash/{hash}")
@SecurityRequirement(name = "apiKey")
public HttpServletResponse getIndexByHash(@PathParam("hash") String hash58, @QueryParam("secret") String secret58,
- @QueryParam("theme") String theme) {
+ @QueryParam("theme") String theme, @QueryParam("lang") String lang) {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
- return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, "/", secret58, "/render/hash", true, false, theme);
+ return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, "/", secret58, "/render/hash", true, false, theme, lang);
}
@GET
@@ -105,11 +105,11 @@ public class RenderResource {
@SecurityRequirement(name = "apiKey")
public HttpServletResponse getPathByHash(@PathParam("hash") String hash58, @PathParam("path") String inPath,
@QueryParam("secret") String secret58,
- @QueryParam("theme") String theme) {
+ @QueryParam("theme") String theme, @QueryParam("lang") String lang) {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
- return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, inPath, secret58, "/render/hash", true, false, theme);
+ return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, inPath, secret58, "/render/hash", true, false, theme, lang);
}
@GET
@@ -119,12 +119,12 @@ public class RenderResource {
@PathParam("name") String name,
@PathParam("path") String inPath,
@QueryParam("identifier") String identifier,
- @QueryParam("theme") String theme) {
+ @QueryParam("theme") String theme, @QueryParam("lang") String lang) {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, name, service, null);
String prefix = String.format("/render/%s", service);
- return this.get(name, ResourceIdType.NAME, service, identifier, inPath, null, prefix, true, true, theme);
+ return this.get(name, ResourceIdType.NAME, service, identifier, inPath, null, prefix, true, true, theme, lang);
}
@GET
@@ -133,18 +133,18 @@ public class RenderResource {
public HttpServletResponse getIndexByName(@PathParam("service") Service service,
@PathParam("name") String name,
@QueryParam("identifier") String identifier,
- @QueryParam("theme") String theme) {
+ @QueryParam("theme") String theme, @QueryParam("lang") String lang) {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, name, service, null);
String prefix = String.format("/render/%s", service);
- return this.get(name, ResourceIdType.NAME, service, identifier, "/", null, prefix, true, true, theme);
+ return this.get(name, ResourceIdType.NAME, service, identifier, "/", null, prefix, true, true, theme, lang);
}
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
- String inPath, String secret58, String prefix, boolean includeResourceIdInPrefix, boolean async, String theme) {
+ String inPath, String secret58, String prefix, boolean includeResourceIdInPrefix, boolean async, String theme, String lang) {
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath,
secret58, prefix, includeResourceIdInPrefix, async, "render", request, response, context);
@@ -152,6 +152,9 @@ public class RenderResource {
if (theme != null) {
renderer.setTheme(theme);
}
+ if (lang != null) {
+ renderer.setLang(lang);
+ }
return renderer.render();
}
diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java
index eb51e8a4..72cd4097 100644
--- a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java
+++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java
@@ -37,6 +37,7 @@ public class ArbitraryDataRenderer {
private final Service service;
private final String identifier;
private String theme = "light";
+ private String lang = "en";
private String inPath;
private final String secret58;
private final String prefix;
@@ -166,7 +167,7 @@ public class ArbitraryDataRenderer {
if (HTMLParser.isHtmlFile(filename)) {
// HTML file - needs to be parsed
byte[] data = Files.readAllBytes(filePath); // TODO: limit file size that can be read into memory
- HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, includeResourceIdInPrefix, data, qdnContext, service, identifier, theme, usingCustomRouting);
+ HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, includeResourceIdInPrefix, data, qdnContext, service, identifier, theme, usingCustomRouting, lang);
htmlParser.addAdditionalHeaderTags();
response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; media-src 'self' data: blob:; img-src 'self' data: blob:; connect-src 'self' wss:;");
response.setContentType(context.getMimeType(filename));
@@ -256,5 +257,8 @@ public class ArbitraryDataRenderer {
public void setTheme(String theme) {
this.theme = theme;
}
+ public void setLang(String lang) {
+ this.lang = lang;
+ }
}
diff --git a/src/main/resources/q-apps/q-apps.js b/src/main/resources/q-apps/q-apps.js
index d7222750..486c6543 100644
--- a/src/main/resources/q-apps/q-apps.js
+++ b/src/main/resources/q-apps/q-apps.js
@@ -45,6 +45,7 @@ function parseUrl(url) {
// Remove theme, identifier, and time queries if they exist
parsedUrl.searchParams.delete("theme");
+ parsedUrl.searchParams.delete("lang");
parsedUrl.searchParams.delete("identifier");
parsedUrl.searchParams.delete("time");
parsedUrl.searchParams.delete("isManualNavigation");
@@ -213,8 +214,11 @@ function buildResourceUrl(service, name, identifier, path, isLink) {
if (path != null) url = url.concat((path.startsWith("/") ? "" : "/") + path);
}
- if (isLink) url = url.concat((url.includes("?") ? "" : "?") + "&theme=" + _qdnTheme);
-
+ if (isLink) {
+ const hasQuery = url.includes("?");
+ const queryPrefix = hasQuery ? "&" : "?";
+ url += queryPrefix + "theme=" + _qdnTheme + "&lang=" + _qdnLang;
+ }
return url;
}