mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-02-07 00:17:07 +01:00
Add: Configurable UI Language Support with Dynamic Filtering (#2846)
# Description of Changes ### Summary - Added support for configuring UI languages via `settings.yml` (`languages` field). - Modified `LanguageService` to respect the configured languages, while ensuring British English (`en_GB`) is always enabled. - Updated Thymeleaf templates to dynamically display only the allowed languages. - Improved logging and refactored some list-to-set conversions for better efficiency. ### Why the Change? - Allows administrators to limit available UI languages instead of displaying all detected languages. - Provides better customization options and simplifies language management. ### Challenges Encountered - Ensuring backwards compatibility: If `languages` is empty, all languages remain enabled. - Handling `Set<String>` instead of `List<String>` in `LanguageService` for optimized lookups. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [x] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [x] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
parent
46c53a9c88
commit
b37457b41d
@ -18,9 +18,7 @@ Any SVG flags are fine; most of the current ones were sourced from [here](https:
|
|||||||
For example, to add Polish, you would add:
|
For example, to add Polish, you would add:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL">
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pl_PL')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
||||||
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
|
||||||
</a>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The `data-bs-language-code` is the code used to reference the file in the next step.
|
The `data-bs-language-code` is the code used to reference the file in the next step.
|
||||||
|
@ -4,6 +4,7 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@ -74,6 +75,11 @@ public class AppConfig {
|
|||||||
: "null";
|
: "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = "languages")
|
||||||
|
public List<String> languages() {
|
||||||
|
return applicationProperties.getUi().getLanguages();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = "navBarText")
|
@Bean(name = "navBarText")
|
||||||
public String navBarText() {
|
public String navBarText() {
|
||||||
String defaultNavBar =
|
String defaultNavBar =
|
||||||
|
@ -2,7 +2,9 @@ package stirling.software.SPDF.controller.api;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@ -26,11 +28,11 @@ public class AdditionalLanguageJsController {
|
|||||||
@Hidden
|
@Hidden
|
||||||
@GetMapping(value = "/additionalLanguageCode.js", produces = "application/javascript")
|
@GetMapping(value = "/additionalLanguageCode.js", produces = "application/javascript")
|
||||||
public void generateAdditionalLanguageJs(HttpServletResponse response) throws IOException {
|
public void generateAdditionalLanguageJs(HttpServletResponse response) throws IOException {
|
||||||
List<String> supportedLanguages = languageService.getSupportedLanguages();
|
Set<String> supportedLanguages = languageService.getSupportedLanguages();
|
||||||
response.setContentType("application/javascript");
|
response.setContentType("application/javascript");
|
||||||
PrintWriter writer = response.getWriter();
|
PrintWriter writer = response.getWriter();
|
||||||
// Erstelle das JavaScript dynamisch
|
// Erstelle das JavaScript dynamisch
|
||||||
writer.println("const supportedLanguages = " + toJsonArray(supportedLanguages) + ";");
|
writer.println("const supportedLanguages = " + toJsonArray(new ArrayList<>(supportedLanguages)) + ";");
|
||||||
// Generiere die `getDetailedLanguageCode`-Funktion
|
// Generiere die `getDetailedLanguageCode`-Funktion
|
||||||
writer.println(
|
writer.println(
|
||||||
"""
|
"""
|
||||||
|
@ -36,8 +36,9 @@ public class DatabaseWebController {
|
|||||||
}
|
}
|
||||||
List<FileInfo> backupList = databaseService.getBackupList();
|
List<FileInfo> backupList = databaseService.getBackupList();
|
||||||
model.addAttribute("backupFiles", backupList);
|
model.addAttribute("backupFiles", backupList);
|
||||||
model.addAttribute("databaseVersion", databaseService.getH2Version());
|
String dbVersion = databaseService.getH2Version();
|
||||||
if ("Unknown".equalsIgnoreCase(databaseService.getH2Version())) {
|
model.addAttribute("databaseVersion", dbVersion);
|
||||||
|
if ("Unknown".equalsIgnoreCase(dbVersion)) {
|
||||||
model.addAttribute("infoMessage", "notSupported");
|
model.addAttribute("infoMessage", "notSupported");
|
||||||
}
|
}
|
||||||
return "database";
|
return "database";
|
||||||
|
@ -265,7 +265,8 @@ public class ApplicationProperties {
|
|||||||
return getKeycloak();
|
return getKeycloak();
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedProviderException(
|
throw new UnsupportedProviderException(
|
||||||
"Logout from the provider is not supported? Report it at https://github.com/Stirling-Tools/Stirling-PDF/issues");
|
"Logout from the provider is not supported? Report it at"
|
||||||
|
+ " https://github.com/Stirling-Tools/Stirling-PDF/issues");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,6 +327,7 @@ public class ApplicationProperties {
|
|||||||
private String appName;
|
private String appName;
|
||||||
private String homeDescription;
|
private String homeDescription;
|
||||||
private String appNameNavbar;
|
private String appNameNavbar;
|
||||||
|
private List<String> languages;
|
||||||
|
|
||||||
public String getAppName() {
|
public String getAppName() {
|
||||||
return appName != null && appName.trim().length() > 0 ? appName : null;
|
return appName != null && appName.trim().length() > 0 ? appName : null;
|
||||||
|
@ -1,41 +1,57 @@
|
|||||||
package stirling.software.SPDF.service;
|
package stirling.software.SPDF.service;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class LanguageService {
|
public class LanguageService {
|
||||||
|
|
||||||
|
private final ApplicationProperties applicationProperties;
|
||||||
private final PathMatchingResourcePatternResolver resourcePatternResolver =
|
private final PathMatchingResourcePatternResolver resourcePatternResolver =
|
||||||
new PathMatchingResourcePatternResolver();
|
new PathMatchingResourcePatternResolver();
|
||||||
|
|
||||||
public List<String> getSupportedLanguages() {
|
public LanguageService(
|
||||||
List<String> supportedLanguages = new ArrayList<>();
|
ApplicationProperties applicationProperties) {
|
||||||
|
this.applicationProperties = applicationProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getSupportedLanguages() {
|
||||||
try {
|
try {
|
||||||
Resource[] resources =
|
Resource[] resources =
|
||||||
resourcePatternResolver.getResources("classpath*:messages_*.properties");
|
resourcePatternResolver.getResources("classpath*:messages_*.properties");
|
||||||
for (Resource resource : resources) {
|
|
||||||
if (resource.exists() && resource.isReadable()) {
|
|
||||||
String filename = resource.getFilename();
|
|
||||||
if (filename != null
|
|
||||||
&& filename.startsWith("messages_")
|
|
||||||
&& filename.endsWith(".properties")) {
|
|
||||||
String languageCode =
|
|
||||||
filename.replace("messages_", "").replace(".properties", "");
|
|
||||||
supportedLanguages.add(languageCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return supportedLanguages;
|
return Arrays.stream(resources)
|
||||||
|
.map(Resource::getFilename)
|
||||||
|
.filter(
|
||||||
|
filename ->
|
||||||
|
filename != null
|
||||||
|
&& filename.startsWith("messages_")
|
||||||
|
&& filename.endsWith(".properties"))
|
||||||
|
.map(filename -> filename.replace("messages_", "").replace(".properties", ""))
|
||||||
|
.filter(
|
||||||
|
languageCode -> {
|
||||||
|
Set<String> allowedLanguages =
|
||||||
|
new HashSet<>(applicationProperties.getUi().getLanguages());
|
||||||
|
return allowedLanguages.isEmpty()
|
||||||
|
|| allowedLanguages.contains(languageCode)
|
||||||
|
|| "en_GB".equals(languageCode);
|
||||||
|
})
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error retrieving supported languages", e);
|
||||||
|
return new HashSet<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,7 @@ ui:
|
|||||||
appName: '' # application's visible name
|
appName: '' # application's visible name
|
||||||
homeDescription: '' # short description or tagline shown on the homepage
|
homeDescription: '' # short description or tagline shown on the homepage
|
||||||
appNameNavbar: '' # name displayed on the navigation bar
|
appNameNavbar: '' # name displayed on the navigation bar
|
||||||
|
languages: [] # If empty, all languages are enabled. To display only German and Polish ["de_DE", "pl_PL"]. British English is always enabled.
|
||||||
|
|
||||||
endpoints:
|
endpoints:
|
||||||
toRemove: [] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])
|
toRemove: [] # list endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
<th:block th:fragment="langs">
|
<th:block th:fragment="langs">
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="bg_BG"> <img th:src="@{'/images/flags/bg.svg'}" alt="icon" width="20" height="15"> Български</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'bg_BG')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="bg_BG"> <img th:src="@{'/images/flags/bg.svg'}" alt="icon" width="20" height="15"> Български</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ar_AR"> <img th:src="@{'/images/flags/sa.svg'}" alt="icon" width="20" height="15"> العربية</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ar_AR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ar_AR"> <img th:src="@{'/images/flags/sa.svg'}" alt="icon" width="20" height="15"> العربية</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img th:src="@{'/images/flags/es-ct.svg'}" alt="icon" width="20" height="15"> Català</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ca_CA')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img th:src="@{'/images/flags/es-ct.svg'}" alt="icon" width="20" height="15"> Català</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> 简体中文</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'zh_CN')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> 简体中文</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img th:src="@{'/images/flags/tw.svg'}" alt="icon" width="20" height="15"> 繁體中文</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'zh_TW')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img th:src="@{'/images/flags/tw.svg'}" alt="icon" width="20" height="15"> 繁體中文</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_BO"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> བོད་ཡིག</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'zh_BO')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_BO"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> བོད་ཡིག</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="az_AZ"> <img th:src="@{'/images/flags/az.svg'}" alt="icon" width="20" height="15"> Azərbaycan Dili</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'az_AZ')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="az_AZ"> <img th:src="@{'/images/flags/az.svg'}" alt="icon" width="20" height="15"> Azərbaycan Dili</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="da_DK"> <img th:src="@{'/images/flags/dk.svg'}" alt="icon" width="20" height="15"> Dansk</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'da_DK')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="da_DK"> <img th:src="@{'/images/flags/dk.svg'}" alt="icon" width="20" height="15"> Dansk</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img th:src="@{'/images/flags/de.svg'}" alt="icon" width="20" height="15"> Deutsch</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'de_DE')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img th:src="@{'/images/flags/de.svg'}" alt="icon" width="20" height="15"> Deutsch</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_GB"> <img th:src="@{'/images/flags/gb.svg'}" alt="icon" width="20" height="15"> English (GB)</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_GB"> <img th:src="@{'/images/flags/gb.svg'}" alt="icon" width="20" height="15"> English (GB)</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US"> <img th:src="@{'/images/flags/us.svg'}" alt="icon" width="20" height="15"> English (US)</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'en_US')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US"> <img th:src="@{'/images/flags/us.svg'}" alt="icon" width="20" height="15"> English (US)</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="eu_ES"> <img th:src="@{'/images/flags/eu.svg'}" alt="icon" width="20" height="15"> Euskara</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'eu_ES')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="eu_ES"> <img th:src="@{'/images/flags/eu.svg'}" alt="icon" width="20" height="15"> Euskara</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img th:src="@{'/images/flags/es.svg'}" alt="icon" width="20" height="15"> Español</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'es_ES')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img th:src="@{'/images/flags/es.svg'}" alt="icon" width="20" height="15"> Español</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img th:src="@{'/images/flags/fr.svg'}" alt="icon" width="20" height="15"> Français</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'fr_FR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img th:src="@{'/images/flags/fr.svg'}" alt="icon" width="20" height="15"> Français</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/id.svg'}" alt="icon" width="20" height="15"> Indonesia</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'id_ID')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/id.svg'}" alt="icon" width="20" height="15"> Indonesia</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ga_IE"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ga_IE')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ga_IE"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'it_IT')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'nl_NL')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fa_IR"> <img th:src="@{'/images/flags/ir.svg'}" alt="icon" width="20" height="15"> پارسی</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'fa_IR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fa_IR"> <img th:src="@{'/images/flags/ir.svg'}" alt="icon" width="20" height="15"> پارسی</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pl_PL')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_BR"> <img th:src="@{'/images/flags/pt_br.svg'}" alt="icon" width="20" height="15"> Português (BR)</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pt_BR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_BR"> <img th:src="@{'/images/flags/pt_br.svg'}" alt="icon" width="20" height="15"> Português (BR)</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_PT"> <img th:src="@{'/images/flags/pt_pt.svg'}" alt="icon" width="20" height="15"> Português (PT)</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'pt_PT')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pt_PT"> <img th:src="@{'/images/flags/pt_pt.svg'}" alt="icon" width="20" height="15"> Português (PT)</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ro_RO"> <img th:src="@{'/images/flags/ro.svg'}" alt="icon" width="20" height="15"> Romanian</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ro_RO')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ro_RO"> <img th:src="@{'/images/flags/ro.svg'}" alt="icon" width="20" height="15"> Romanian</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sk_SK"> <img th:src="@{'/images/flags/sk.svg'}" alt="icon" width="20" height="15"> Slovensky</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sk_SK')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sk_SK"> <img th:src="@{'/images/flags/sk.svg'}" alt="icon" width="20" height="15"> Slovensky</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sl_SI"> <img th:src="@{'/images/flags/si.svg'}" alt="icon" width="20" height="15"> Slovenian</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sl_SI')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sl_SI"> <img th:src="@{'/images/flags/si.svg'}" alt="icon" width="20" height="15"> Slovenian</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sv_SE"> <img th:src="@{'/images/flags/se.svg'}" alt="icon" width="20" height="15"> Svenska</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sv_SE')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sv_SE"> <img th:src="@{'/images/flags/se.svg'}" alt="icon" width="20" height="15"> Svenska</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="tr_TR"> <img th:src="@{'/images/flags/tr.svg'}" alt="icon" width="20" height="15"> Türkçe</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'tr_TR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="tr_TR"> <img th:src="@{'/images/flags/tr.svg'}" alt="icon" width="20" height="15"> Türkçe</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ru_RU"> <img th:src="@{'/images/flags/ru.svg'}" alt="icon" width="20" height="15"> Русский</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ru_RU')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ru_RU"> <img th:src="@{'/images/flags/ru.svg'}" alt="icon" width="20" height="15"> Русский</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ko_KR"> <img th:src="@{'/images/flags/kr.svg'}" alt="icon" width="20" height="15"> 한국어</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ko_KR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ko_KR"> <img th:src="@{'/images/flags/kr.svg'}" alt="icon" width="20" height="15"> 한국어</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ja_JP"> <img th:src="@{'/images/flags/jp.svg'}" alt="icon" width="20" height="15"> 日本語</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'ja_JP')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ja_JP"> <img th:src="@{'/images/flags/jp.svg'}" alt="icon" width="20" height="15"> 日本語</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="el_GR"> <img th:src="@{'/images/flags/gr.svg'}" alt="icon" width="20" height="15"> Ελληνικά</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'el_GR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="el_GR"> <img th:src="@{'/images/flags/gr.svg'}" alt="icon" width="20" height="15"> Ελληνικά</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hu_HU"> <img th:src="@{'/images/flags/hu.svg'}" alt="icon" width="20" height="15"> Hungarian</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'hu_HU')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hu_HU"> <img th:src="@{'/images/flags/hu.svg'}" alt="icon" width="20" height="15"> Hungarian</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hi_IN"> <img th:src="@{'/images/flags/in.svg'}" alt="icon" width="20" height="15"> हिन्दी</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'hi_IN')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hi_IN"> <img th:src="@{'/images/flags/in.svg'}" alt="icon" width="20" height="15"> हिन्दी</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sr_LATN_RS"> <img th:src="@{'/images/flags/rs.svg'}" alt="icon" width="20" height="15"> Srpski</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'sr_LATN_RS')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="sr_LATN_RS"> <img th:src="@{'/images/flags/rs.svg'}" alt="icon" width="20" height="15"> Srpski</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="uk_UA"> <img th:src="@{'/images/flags/ua.svg'}" alt="icon" width="20" height="15"> Українська</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'uk_UA')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="uk_UA"> <img th:src="@{'/images/flags/ua.svg'}" alt="icon" width="20" height="15"> Українська</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="cs_CZ"> <img th:src="@{'/images/flags/cz.svg'}" alt="icon" width="20" height="15"> Česky</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'cs_CZ')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="cs_CZ"> <img th:src="@{'/images/flags/cz.svg'}" alt="icon" width="20" height="15"> Česky</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hr_HR"> <img th:src="@{'/images/flags/hr.svg'}" alt="icon" width="20" height="15"> Hrvatski</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'hr_HR')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hr_HR"> <img th:src="@{'/images/flags/hr.svg'}" alt="icon" width="20" height="15"> Hrvatski</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="no_NB"> <img th:src="@{'/images/flags/no.svg'}" alt="icon" width="20" height="15"> Norsk</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'no_NB')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="no_NB"> <img th:src="@{'/images/flags/no.svg'}" alt="icon" width="20" height="15"> Norsk</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="th_TH"> <img th:src="@{'/images/flags/th.svg'}" alt="icon" width="20" height="15"> ไทย</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'th_TH')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="th_TH"> <img th:src="@{'/images/flags/th.svg'}" alt="icon" width="20" height="15"> ไทย</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="vi_VN"> <img th:src="@{'/images/flags/vn.svg'}" alt="icon" width="20" height="15"> Tiếng Việt</a>
|
<a th:if="${#lists.isEmpty(@languages) or #lists.contains(@languages, 'vi_VN')}" class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="vi_VN"> <img th:src="@{'/images/flags/vn.svg'}" alt="icon" width="20" height="15"> Tiếng Việt</a>
|
||||||
</th:block>
|
</th:block>
|
||||||
|
Loading…
Reference in New Issue
Block a user