diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 5d844cac27..380e7c72d0 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -109,6 +109,7 @@ public class KeycloakApplication extends Application { singletons.add(new ObjectMapperResolver()); classes.add(WelcomeResource.class); + classes.add(LocaleResource.class); platform.onStartup(this::startup); platform.onShutdown(this::shutdown); diff --git a/services/src/main/java/org/keycloak/services/resources/LocaleResource.java b/services/src/main/java/org/keycloak/services/resources/LocaleResource.java new file mode 100644 index 0000000000..4622845c1f --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/LocaleResource.java @@ -0,0 +1,93 @@ +package org.keycloak.services.resources; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.util.LocaleUtil; +import org.keycloak.theme.Theme; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import static java.util.stream.Collectors.*; + +@Path("/localisation") +@Produces(MediaType.APPLICATION_JSON) +public class LocaleResource { + + @Context + protected KeycloakSession session; + + @GET + @Path("{realm}/{theme}/{locale}") + + public List getLocalizationTexts(@PathParam("realm") String realmName, @PathParam("theme") String theme, + @PathParam("locale") String localeString, @QueryParam("source") boolean showSource) throws IOException { + RealmManager realmManager = new RealmManager(session); + RealmModel realm = realmManager.getRealmByName(realmName); + session.getContext().setRealm(realm); + + Theme theTheme = session.theme().getTheme(Theme.Type.valueOf(theme.toUpperCase())); + final Locale locale = Locale.forLanguageTag(localeString); + if (showSource) { + Properties messagesByLocale = theTheme.getMessages("messages", locale); + Set result = messagesByLocale.entrySet().stream().map(e -> + new KeySource((String) e.getKey(), (String) e.getValue(), Source.THEME)).collect(toSet()); + + Map realmLocalizationMessages = LocaleUtil.getRealmLocalizationTexts(realm, locale); + for (Locale currentLocale = locale; currentLocale != null; currentLocale = LocaleUtil.getParentLocale(currentLocale)) { + final List realmOverride = realmLocalizationMessages.get(currentLocale).entrySet().stream().map(e -> + new KeySource((String) e.getKey(), (String) e.getValue(), Source.OVERRIDE)).collect(toList()); + result.addAll(realmOverride); + } + + return new ArrayList<>(result); + } + return theTheme.getEnhancedMessages(realm, locale).entrySet().stream().map(e -> + new KeySource((String) e.getKey(), (String) e.getValue())).collect(toList()); + } +} + +enum Source { + THEME, + OVERRIDE +} +class KeySource { + private String key; + private String value; + private Source source; + + public KeySource(String key, String value) { + this.key = key; + this.value = value; + } + + public KeySource(String key, String value, Source source) { + this(key, value); + this.source = source; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public Source getSource() { + return source; + } +} diff --git a/services/src/main/java/org/keycloak/services/util/LocaleUtil.java b/services/src/main/java/org/keycloak/services/util/LocaleUtil.java index 09ad7d4e1e..a40fd5914d 100644 --- a/services/src/main/java/org/keycloak/services/util/LocaleUtil.java +++ b/services/src/main/java/org/keycloak/services/util/LocaleUtil.java @@ -200,7 +200,7 @@ public class LocaleUtil { return mergeGroupedMessages(locale, realmLocalizationMessages, themeMessages); } - private static Map getRealmLocalizationTexts(RealmModel realm, Locale locale) { + public static Map getRealmLocalizationTexts(RealmModel realm, Locale locale) { LinkedHashMap groupedMessages = new LinkedHashMap<>(); List applicableLocales = getApplicableLocales(locale);