diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index 70966dcc2a..f9b3d23759 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -2985,4 +2985,5 @@ readBlog=Read blog customValue=Custom value termsAndConditionsUserAttribute=Terms and conditions accepted timestamp realmOverridesDescription= Realm overrides allow you to specify translations that will take effect for the entire realm. These translations will override any translation specified by a theme. -addTranslation=Add translation \ No newline at end of file +addTranslation=Add translation +effectiveMessageBundlesDescription=An effective message bundle is the set of translations for a given language, theme, and theme type. It also takes into account any realm overrides, which will take precedence. \ No newline at end of file diff --git a/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx b/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx index ba93f56c69..3810c0a471 100644 --- a/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx +++ b/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx @@ -13,9 +13,12 @@ import { Select, SelectOption, SelectVariant, + Text, + TextContent, + TextVariants, } from "@patternfly/react-core"; import { pickBy } from "lodash-es"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { adminClient } from "../../admin-client"; @@ -27,6 +30,7 @@ import { useServerInfo } from "../../context/server-info/ServerInfoProvider"; import { useWhoAmI } from "../../context/whoami/WhoAmI"; import { DEFAULT_LOCALE } from "../../i18n/i18n"; import { localeToDisplayName } from "../../util"; +import useLocaleSort, { mapByKey } from "../../utils/useLocaleSort"; type EffectiveMessageBundlesProps = { defaultSupportedLocales: string[]; @@ -53,6 +57,7 @@ export const EffectiveMessageBundles = ({ const { realm } = useRealm(); const serverInfo = useServerInfo(); const { whoAmI } = useWhoAmI(); + const localeSort = useLocaleSort(); const [searchDropdownOpen, setSearchDropdownOpen] = useState(false); const [searchPerformed, setSearchPerformed] = useState(false); const [selectThemesOpen, setSelectThemesOpen] = useState(false); @@ -61,15 +66,29 @@ export const EffectiveMessageBundles = ({ const [activeFilters, setActiveFilters] = useState< Partial >({}); - const themes = serverInfo.themes; - const themeKeys = themes - ? Object.keys(themes).sort((a, b) => a.localeCompare(b)) - : []; - const themeTypes = Object.values(themes!) - .flatMap((theme) => theme.map((item) => item.name)) - .filter((value, index, self) => self.indexOf(value) === index) - .sort((a, b) => a.localeCompare(b)); const [key, setKey] = useState(0); + const themes = serverInfo.themes; + + const themeTypes = useMemo(() => { + if (!themes) { + return []; + } + + return localeSort(Object.keys(themes), (key) => key); + }, [themes]); + + const themeNames = useMemo(() => { + if (!themes) { + return []; + } + + return localeSort( + Object.values(themes) + .flatMap((theme) => theme.map((item) => item.name)) + .filter((value, index, self) => self.indexOf(value) === index), + (name) => name, + ); + }, [themes]); const filterLabels: Record = { @@ -82,7 +101,7 @@ export const EffectiveMessageBundles = ({ const { getValues, reset, - formState: { isDirty }, + formState: { isDirty, isValid }, control, } = useForm({ mode: "onChange", @@ -101,11 +120,18 @@ export const EffectiveMessageBundles = ({ }, ); - return filter.hasWords.length > 0 - ? messages.filter((m) => - filter.hasWords.some((f) => m.value.includes(f)), - ) - : messages; + const filteredMessages = + filter.hasWords.length > 0 + ? messages.filter((m) => + filter.hasWords.some( + (f) => m.value.includes(f) || m.key.includes(f), + ), + ) + : messages; + + const sortedMessages = localeSort([...filteredMessages], mapByKey("key")); + + return sortedMessages; } catch (error) { return []; } @@ -159,6 +185,13 @@ export const EffectiveMessageBundles = ({ direction={{ default: "column" }} spaceItems={{ default: "spaceItemsNone" }} > + + + + {t("effectiveMessageBundlesDescription")} + + +
- + (value || "").length > 0, + }} render={({ field }) => ( - + (value || "").length > 0, + }} render={({ field }) => (