From 19e7af3433d61399bce69893af493f76126eddaf Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:42:23 +0100 Subject: [PATCH 01/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- assemblies/assembly-localization-in-rhdh.adoc | 14 + .../con-language-persistence.adoc | 23 ++ ...adding-localization-to-custom-plugins.adoc | 301 ++++++++++++++++++ .../proc-customize-rhdh-language.adoc | 28 ++ .../proc-enabling-localization-in-rhdh.adoc | 26 ++ .../proc-overriding-translations.adoc | 84 +++++ .../proc-select-rhdh-language.adoc | 20 ++ titles/customizing/master.adoc | 2 + 8 files changed, 498 insertions(+) create mode 100644 assemblies/assembly-localization-in-rhdh.adoc create mode 100644 modules/customizing-the-appearance/con-language-persistence.adoc create mode 100644 modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc create mode 100644 modules/customizing-the-appearance/proc-customize-rhdh-language.adoc create mode 100644 modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc create mode 100644 modules/customizing-the-appearance/proc-overriding-translations.adoc create mode 100644 modules/customizing-the-appearance/proc-select-rhdh-language.adoc diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc new file mode 100644 index 0000000000..02bb79ac51 --- /dev/null +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -0,0 +1,14 @@ +:_mod-docs-content-type: ASSEMBLY + +[id="assembly-localization-in-rhdh_{context}"] += Localization in {product} + +include::modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc[leveloffset=+1] + +include::modules/customizing-the-appearance/proc-overriding-translations.adoc[leveloffset=+2] + +include::modules/customizing-the-appearance/proc-select-rhdh-language.adoc[leveloffset=+1] + +include::modules/customizing-the-appearance/con-language-persistence.adoc[leveloffset=+2] + +include::modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc[leveloffset=+1] \ No newline at end of file diff --git a/modules/customizing-the-appearance/con-language-persistence.adoc b/modules/customizing-the-appearance/con-language-persistence.adoc new file mode 100644 index 0000000000..8087ff8fc2 --- /dev/null +++ b/modules/customizing-the-appearance/con-language-persistence.adoc @@ -0,0 +1,23 @@ +:_mod-docs-content-type: CONCEPT + +[id="con-language-persistence_{context}"] += Language persistence + +When you change the language in the UI, your preference is saved to storage. On next login or refresh, your chosen language setting is restored. Guest users cannot persist language preferences. + +Default language selection uses the following priority order: + +. *Browser language priority*: The system first checks the user's browser language preferences to provide a personalized experience. + +. *Configuration priority*: If no browser language matches the supported locales, the `defaultLocale` from the `i18n` configuration is used as a fallback. + +. *Fallback priority*: If neither browser preferences nor configuration provide a match, defaults to `en`. + +Red Hat Developer Hub automatically saves and restores user language settings across browser sessions. This feature is enabled by default and uses database storage. To opt-out and use browser storage instead, add the following to your `app-config.yaml`: +[source,yaml,subs="+quotes"] +---- +userSettings: + persistence: browser # <1> +---- +<1> To opt-out and use browser local storage, set this value to `browser`. Optionally, set this value to `database` to persist across browsers and devices. This the default setting and does not require this configuration to be set. + diff --git a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc new file mode 100644 index 0000000000..04b505e1b4 --- /dev/null +++ b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc @@ -0,0 +1,301 @@ +:_mod-docs-content-type: PROCEDURE + +[id="proc-adding-localization-to-custom-plugins_{context}"] += Adding localization to custom plugins +You can implement localization support in your custom Red Hat Developer Hub plugins so that your plugins are accessible to a diverse, international user base and follow recommended best practices. + +.Procedure +. Create the following translation files in your plugin's `src/translations/ directory`: ++ +.`src/translations/ref.ts` English language reference +[source,json] +---- +src/translations/ref.ts - English Reference +import { createTranslationRef } from "@backstage/core-plugin-api/alpha"; + + +export const myPluginMessages = { + page: { + title: "My Plugin", + subtitle: "Plugin description", + }, + common: { + exportCSV: "Export CSV", + noResults: "No results found", + }, + table: { + headers: { + name: "Name", + count: "Count", + }, + }, +}; + + +export const myPluginTranslationRef = createTranslationRef({ + id: "plugin.my-plugin", + messages: myPluginMessages, +}); +---- + ++ +.`src/translations/de.ts` German language reference +[source,json] +---- +import { createTranslationMessages } from "@backstage/core-plugin-api/alpha"; +import { myPluginTranslationRef } from "./ref"; + + +const myPluginTranslationDe = createTranslationMessages({ + ref: myPluginTranslationRef, + messages: { + "page.title": "Mein Plugin", + "page.subtitle": "Plugin-Beschreibung", + "common.exportCSV": "CSV exportieren", + "common.noResults": "Keine Ergebnisse gefunden", + "table.headers.name": "Name", + "table.headers.count": "Anzahl", + }, +}); + + +export default myPluginTranslationDe; +---- + ++ +.`src/translations/fr.ts` French language reference +[source,json] +---- +import { createTranslationMessages } from "@backstage/core-plugin-api/alpha"; +import { myPluginTranslationRef } from "./ref"; + + +const myPluginTranslationFr = createTranslationMessages({ + ref: myPluginTranslationRef, + messages: { + "page.title": "Mon Plugin", + "page.subtitle": "Description du plugin", + "common.exportCSV": "Exporter CSV", + "common.noResults": "Aucun résultat trouvé", + "table.headers.name": "Nom", + "table.headers.count": "Nombre", + }, +}); + + +export default myPluginTranslationFr; +---- + ++ +.`src/translations/index.ts` translation resource +[source,json] +---- +import { createTranslationResource } from "@backstage/core-plugin-api/alpha"; +import { myPluginTranslationRef } from "./ref"; + + +export const myPluginTranslations = createTranslationResource({ + ref: myPluginTranslationRef, + translations: { + de: () => import("./de"), + fr: () => import("./fr"), + }, +}); + + +export { myPluginTranslationRef }; +---- + +. Create translation hooks ++ +.`src/hooks/useTranslation.ts` translation hooks +[source,json] +---- +src/hooks/useTranslation.ts +import { useTranslationRef } from "@backstage/core-plugin-api/alpha"; +import { myPluginTranslationRef } from "../translations"; + + +export const useTranslation = () => useTranslationRef(myPluginTranslationRef); +---- + +. Update your plugin components to replace hardcoded strings with translation calls: ++ +.Before (hardcoded): +[source,json] +---- +const MyComponent = () => { + return ( +
+

My Plugin

+ +
+ ); +}; +---- ++ +.After (translated): +[source,json] +---- +import { useTranslation } from '../hooks/useTranslation'; + + +const MyComponent = () => { + const { t } = useTranslation(); + + + return ( +
+

{t('page.title')}

+ +
+ ); +}; +---- + +. (Optional) For content with variables, use interpolation: ++ +[source,json] +---- +// In your translation files +'table.pagination.topN': 'Top {{count}} items' +---- + ++ +[source,json] +---- +// In your component +const { t } = useTranslation(); +const message = t('table.pagination.topN', { count: '10' }); +For dynamic translation keys (e.g., from configuration): +// Configuration object with translation keys +const CARD_CONFIGS = [ + { id: 'overview', titleKey: 'cards.overview.title' }, + { id: 'details', titleKey: 'cards.details.title' }, + { id: 'settings', titleKey: 'cards.settings.title' }, +]; + + +// In your component +const { t } = useTranslation(); + + +const CardComponent = ({ config }) => { + return ( +
+

{t(config.titleKey as any)}

+ {/* Use 'as any' for dynamic keys */} +
+ ); +}; +---- + + +. Export translation resources ++ +[source,json] +---- +In your plugin's src/index.ts: +// Export your plugin +export { myPlugin } from "./plugin"; + + +// Export translation resources for RHDH +export { myPluginTranslations, myPluginTranslationRef } from "./translations"; +6. Configure in RHDH +For RHDH (dynamic plugins configuration): +Add to your dynamic-plugins.default.yaml: + +backstage-community.plugin-my-plugin: + translationResources: + - importName: myPluginTranslations + ref: myPluginTranslationRef +For local Backstage app development: +// In your app's App.tsx +import { myPluginTranslations } from "@my-org/backstage-plugin-my-plugin"; + + +const app = createApp({ + apis, + __experimentalTranslations: { + availableLanguages: ["en", "de", "fr"], + resources: [myPluginTranslations], + }, +}); +---- + +. Testing Your Translations ++ +[source,json] +---- +Create test mocks (src/test-utils/mockTranslations.ts): +import { myPluginMessages } from "../translations/ref"; + + +function flattenMessages(obj: any, prefix = ""): Record { + const flattened: Record = {}; + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + const value = obj[key]; + const newKey = prefix ? `${prefix}.${key}` : key; + if (typeof value === "object" && value !== null) { + Object.assign(flattened, flattenMessages(value, newKey)); + } else { + flattened[newKey] = value; + } + } + } + return flattened; +} + + +const flattenedMessages = flattenMessages(myPluginMessages); + + +export const mockT = (key: string, params?: any) => { + let message = flattenedMessages[key] || key; + if (params) { + for (const [paramKey, paramValue] of Object.entries(params)) { + message = message.replace( + new RegExp(`{{${paramKey}}}`, "g"), + String(paramValue), + ); + } + } + return message; +}; + + +export const mockUseTranslation = () => ({ t: mockT }); +Update your tests: +import { mockUseTranslation } from "../test-utils/mockTranslations"; + + +jest.mock("../hooks/useTranslation", () => ({ + useTranslation: mockUseTranslation, +})); +---- + +// Your test code... +== Best practices +* Never modify original English strings - Keep them exactly as they were +* Use flat dot notation in translation files (e.g., 'page.title') +* Use nested objects in the reference file for TypeScript support +* Test with mocks to ensure translations work correctly +* Add all languages to your app configuration + +== Common Patterns +Use Case Pattern Example +Simple text t('key') t('page.title') +With variables t('key', {param}) t('table.topN', {count: '5'}) +Dynamic keys t(config.titleKey as any) t('cards.overview.title' as any) + +== Validation Checklist +* All hardcoded strings replaced with translation calls +* Translation files created for all target languages +* Translation resources exported from src/index.ts +* For RHDH: Dynamic plugins configuration updated with translationResources +* For local app: App configuration updated with available languages +* Tests updated with translation mocks +* Language switching works in the UI +* Fallback to English works for missing translations \ No newline at end of file diff --git a/modules/customizing-the-appearance/proc-customize-rhdh-language.adoc b/modules/customizing-the-appearance/proc-customize-rhdh-language.adoc new file mode 100644 index 0000000000..c0df299ad7 --- /dev/null +++ b/modules/customizing-the-appearance/proc-customize-rhdh-language.adoc @@ -0,0 +1,28 @@ +:_mod-docs-content-type: PROCEDURE + +[id="proc-customize-rhdh-language_{context}"] += Customizing the language for your {product-short} instance + +The language settings of {product-very-short} use English by default. You can choose to use one of the following languages instead. + +.Supported languages +* English +* French + +The default language is English, which automatically sets the light or dark theme based on your system preferences. + +[NOTE] +==== +English and French are the supported languages in {product-very-short} 1.8. You can add other languages in the the `i18n` section of your `{my-app-config-file}` configuration file. +==== + +.Prerequisites + +* You are logged in to the {product-short} web console. + +.Procedure + +. From the {product-short} web console, click *Settings*. +. From the *Appearance* panel, click the language dropdown to select your language of choice. ++ +image::rhdh/customize-language-dropdown.png[] \ No newline at end of file diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc new file mode 100644 index 0000000000..5be7cc5254 --- /dev/null +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc @@ -0,0 +1,26 @@ +:_mod-docs-content-type: PROCEDURE + +[id="proc-enabling-localization-in-rhdh_{context}"] += Enabling the localization framework in {product-short} +Enabling localization enhances accessibility, improves the user experience for a global audience, and assists organizations in meeting language requirements in specific regions. + +The language settings of {product-very-short} use English by default. In {product-very-short} {product-version}, you can choose to use one of the following supported languages: + +* English (en) +* French (fr) + +.Prerequisites + +.Procedure +. To enable the localization framework in your {product-very-short} application, add the `i18n` section to your custom {product-short} `{my-app-config-file}` configuration file: ++ +[id=i18n] +.`{my-app-config-file}` fragment with localization `i18n` fields +[source,yaml,subs="+quotes"] +---- +i18n: + locales: # List of supported locales. Must include `en`, otherwise the translation framework will fail to load. + - en + - fr + defaultLocale: en # Optional. Defaults to `en` if not specified. +---- \ No newline at end of file diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc new file mode 100644 index 0000000000..bda680cf9c --- /dev/null +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -0,0 +1,84 @@ +:_mod-docs-content-type: CONCEPT + +[id="prov-overriding-translations_{context}"] += Overriding translations +In {product-very-short} 1.8, English and French are the supported languages. You can implement transalations for other languages by using an `allTranslations.json` file to override existing translation strings and updating the `i18n` section of your `{my-app-config-file}` configuration file. + +.Prerequisitea +* You have enabled localization in your {product-very-short} application. + +.Procedure +. Download the translation strings. +. Create the JSON file containing all your translations, for example: ++ +[id=i18n-enable] +.`allTranslations.json` fragment with translation string overrides +[source,json] +---- +{ + "plugin.npm.translation-ref": { + "en": { + "infoCard.title": "NPM Packet JSON {{packageName}}" + }, + "de": { + "infoCard.title": "NPM Pakettt JSON {{packageName}}" + }, + "zh": { + "infoCard.title": "NPM 包 JSON {{packageName}}" + } + }, + "catalog-import": { + "en": { + "defaultImportPage.headerTitle": "Import an existing Git repository JSON" + }, + "de": { + "defaultImportPage.headerTitle": "Ein vorhandenes Git-Repository importieren JSON" + } + } +} +---- +. Login to your cluster and create a config map for your json translations: ++ +[source,bash] +---- +oc create configmap all-translations --from-file=//allTranslations.json +---- + +. Update your Developer hub helm chart to mount the above config map in the app’s filesystem + +.. In the helm chart -> Root Schema -> Backstage chart schema -> Backstage parameters -> Backstage container additional volume mounts + +.. Select `Add Backstage container additional volume mounts` and add the following ++ +[source,yaml] +---- +MountPath: /opt/app-root/src/translation +Name: all-translations +---- + +.. Add the translations to the `Backstage container additional volumes` in the helm chart ++ +[source,yaml] +---- +name: all-translations +configMap: + defaultMode: 420 + name: all-translations +---- + +. Update the `i18n` section to your custom {product-short} `{my-app-config-file}` configuration file to include the translation override file: ++ +[id=i18n-override] +.`{my-app-config-file}` fragment with localization `i18n` fields +[source,yaml,subs="+quotes"] +---- +i18n: + locales: # List of supported locales. Must include `en`, otherwise the translation framework will fail to load. + - en + - fr + defaultLocale: en # Optional. Defaults to `en` if not specified. + overrides: # List of JSON translation files applied in order (last file wins). Each file may override/add translations for one or more plugins/locales + - /.json + - /.json +---- + diff --git a/modules/customizing-the-appearance/proc-select-rhdh-language.adoc b/modules/customizing-the-appearance/proc-select-rhdh-language.adoc new file mode 100644 index 0000000000..dfa6c335d1 --- /dev/null +++ b/modules/customizing-the-appearance/proc-select-rhdh-language.adoc @@ -0,0 +1,20 @@ +:_mod-docs-content-type: PROCEDURE + +[id="proc-selecting-rhdh-language_{context}"] += Selecting the language for your {product-short} instance + +You can choose to use one of the following supported languages: + +* English (default) +* French + +.Prerequisites + +* You are logged in to the {product-short} web console. + +.Procedure + +. From the {product-short} web console, click *Settings*. +. From the *Appearance* panel, click the language dropdown to select your language of choice. ++ +image::rhdh/customize-language-dropdown.png[] \ No newline at end of file diff --git a/titles/customizing/master.adoc b/titles/customizing/master.adoc index d5c480b50e..29898dda63 100644 --- a/titles/customizing/master.adoc +++ b/titles/customizing/master.adoc @@ -45,3 +45,5 @@ include::assemblies/assembly-customizing-the-homepage.adoc[leveloffset=+1] include::assemblies/assembly-customizing-the-quick-access-card.adoc[leveloffset=+1] include::modules/customizing/proc-customizing-rhdh-metadata-card.adoc[leveloffset=+1] + +include::assemblies/assembly-localization-in-rhdh.adoc[leveloffset=+1] From 9a243faf13b3913ec3950d1da071bd2e69a70560 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:50:20 +0100 Subject: [PATCH 02/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- images/rhdh/customize-language-dropdown.png | Bin 0 -> 51705 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/rhdh/customize-language-dropdown.png diff --git a/images/rhdh/customize-language-dropdown.png b/images/rhdh/customize-language-dropdown.png new file mode 100644 index 0000000000000000000000000000000000000000..ab668d60f32c6da6801c2b1748a1bff0e689e11d GIT binary patch literal 51705 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 I3;x&t0An+OAOHXW literal 0 HcmV?d00001 From d90256923955ab169bb39b9b75bbb7374d78e393 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 14 Oct 2025 19:40:55 +0100 Subject: [PATCH 03/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- assemblies/assembly-localization-in-rhdh.adoc | 7 +- ...adding-localization-to-custom-plugins.adoc | 118 +++++------------- .../ref-best-practices-for-localization.adoc | 34 +++++ 3 files changed, 74 insertions(+), 85 deletions(-) create mode 100644 modules/customizing-the-appearance/ref-best-practices-for-localization.adoc diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc index 02bb79ac51..8466ea8a45 100644 --- a/assemblies/assembly-localization-in-rhdh.adoc +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -11,4 +11,9 @@ include::modules/customizing-the-appearance/proc-select-rhdh-language.adoc[level include::modules/customizing-the-appearance/con-language-persistence.adoc[leveloffset=+2] -include::modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc[leveloffset=+1] \ No newline at end of file +== Localization support for custom plugins + +include::modules/customizing-the-appearance/ref-best-practices-for-localization.adoc[leveloffset=+2] + +include::modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc[leveloffset=+2] + diff --git a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc index 04b505e1b4..945cd1df01 100644 --- a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc +++ b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc @@ -1,19 +1,17 @@ :_mod-docs-content-type: PROCEDURE [id="proc-adding-localization-to-custom-plugins_{context}"] -= Adding localization to custom plugins -You can implement localization support in your custom Red Hat Developer Hub plugins so that your plugins are accessible to a diverse, international user base and follow recommended best practices. += Implementing localization support for your custom plugins +You can implement localization support in your custom {product-very-short} plugins so that your plugins are accessible to a diverse, international user base and follow recommended best practices. .Procedure -. Create the following translation files in your plugin's `src/translations/ directory`: +. Create the following translation files in your plugin's `src/translations/` directory: + -.`src/translations/ref.ts` English language reference +.`src/translations/ref.ts` English reference [source,json] ---- -src/translations/ref.ts - English Reference import { createTranslationRef } from "@backstage/core-plugin-api/alpha"; - export const myPluginMessages = { page: { title: "My Plugin", @@ -31,21 +29,18 @@ export const myPluginMessages = { }, }; - export const myPluginTranslationRef = createTranslationRef({ id: "plugin.my-plugin", messages: myPluginMessages, }); ---- - + -.`src/translations/de.ts` German language reference +.`src/translations/de.ts` German translation [source,json] ---- import { createTranslationMessages } from "@backstage/core-plugin-api/alpha"; import { myPluginTranslationRef } from "./ref"; - const myPluginTranslationDe = createTranslationMessages({ ref: myPluginTranslationRef, messages: { @@ -58,18 +53,15 @@ const myPluginTranslationDe = createTranslationMessages({ }, }); - export default myPluginTranslationDe; ---- - + -.`src/translations/fr.ts` French language reference +.`src/translations/fr.ts` French translation [source,json] ---- import { createTranslationMessages } from "@backstage/core-plugin-api/alpha"; import { myPluginTranslationRef } from "./ref"; - const myPluginTranslationFr = createTranslationMessages({ ref: myPluginTranslationRef, messages: { @@ -82,18 +74,15 @@ const myPluginTranslationFr = createTranslationMessages({ }, }); - export default myPluginTranslationFr; ---- - + -.`src/translations/index.ts` translation resource +.`src/translations/index.ts` Translation resource [source,json] ---- import { createTranslationResource } from "@backstage/core-plugin-api/alpha"; import { myPluginTranslationRef } from "./ref"; - export const myPluginTranslations = createTranslationResource({ ref: myPluginTranslationRef, translations: { @@ -102,24 +91,21 @@ export const myPluginTranslations = createTranslationResource({ }, }); - export { myPluginTranslationRef }; ---- -. Create translation hooks +. Create translation hooks file, as follows: + -.`src/hooks/useTranslation.ts` translation hooks +.`src/hooks/useTranslation.ts` Translation hooks [source,json] ---- -src/hooks/useTranslation.ts import { useTranslationRef } from "@backstage/core-plugin-api/alpha"; import { myPluginTranslationRef } from "../translations"; - export const useTranslation = () => useTranslationRef(myPluginTranslationRef); ---- -. Update your plugin components to replace hardcoded strings with translation calls: +. Update your plugin components to replace hardcoded strings with translation calls. For example: + .Before (hardcoded): [source,json] @@ -139,11 +125,9 @@ const MyComponent = () => { ---- import { useTranslation } from '../hooks/useTranslation'; - const MyComponent = () => { const { t } = useTranslation(); - return (

{t('page.title')}

@@ -153,21 +137,22 @@ const MyComponent = () => { }; ---- -. (Optional) For content with variables, use interpolation: +. (Optional) If your content contains variables, use interpolation: + [source,json] ---- // In your translation files 'table.pagination.topN': 'Top {{count}} items' + +// In your component +const { t } = useTranslation(); +const message = t('table.pagination.topN', { count: '10' }); ---- +. (Optional) If your content contains dynamic translation keys (for example, from your plugin configuration): + [source,json] ---- -// In your component -const { t } = useTranslation(); -const message = t('table.pagination.topN', { count: '10' }); -For dynamic translation keys (e.g., from configuration): // Configuration object with translation keys const CARD_CONFIGS = [ { id: 'overview', titleKey: 'cards.overview.title' }, @@ -175,11 +160,9 @@ const CARD_CONFIGS = [ { id: 'settings', titleKey: 'cards.settings.title' }, ]; - // In your component const { t } = useTranslation(); - const CardComponent = ({ config }) => { return (
@@ -190,48 +173,37 @@ const CardComponent = ({ config }) => { }; ---- - -. Export translation resources +. Export the translation resources + [source,json] +.`src/index.ts` file fragment ---- -In your plugin's src/index.ts: // Export your plugin export { myPlugin } from "./plugin"; - // Export translation resources for RHDH export { myPluginTranslations, myPluginTranslationRef } from "./translations"; -6. Configure in RHDH -For RHDH (dynamic plugins configuration): -Add to your dynamic-plugins.default.yaml: +---- +. Update your `dynamic-plugins.default.yaml` file, as follows: ++ +[source,json] +.`src/index.ts` file fragment +---- backstage-community.plugin-my-plugin: translationResources: - importName: myPluginTranslations ref: myPluginTranslationRef -For local Backstage app development: -// In your app's App.tsx -import { myPluginTranslations } from "@my-org/backstage-plugin-my-plugin"; - - -const app = createApp({ - apis, - __experimentalTranslations: { - availableLanguages: ["en", "de", "fr"], - resources: [myPluginTranslations], - }, -}); ---- -. Testing Your Translations -+ +.Verification +To verify your translations, create a test mock file. For example: + +.`src/test-utils/mockTranslations.ts` Test mock file [source,json] ---- -Create test mocks (src/test-utils/mockTranslations.ts): import { myPluginMessages } from "../translations/ref"; - function flattenMessages(obj: any, prefix = ""): Record { const flattened: Record = {}; for (const key in obj) { @@ -248,10 +220,8 @@ function flattenMessages(obj: any, prefix = ""): Record { return flattened; } - const flattenedMessages = flattenMessages(myPluginMessages); - export const mockT = (key: string, params?: any) => { let message = flattenedMessages[key] || key; if (params) { @@ -265,37 +235,17 @@ export const mockT = (key: string, params?: any) => { return message; }; - export const mockUseTranslation = () => ({ t: mockT }); -Update your tests: -import { mockUseTranslation } from "../test-utils/mockTranslations"; +---- +.Update your tests +[source,json] +---- +import { mockUseTranslation } from "../test-utils/mockTranslations"; jest.mock("../hooks/useTranslation", () => ({ useTranslation: mockUseTranslation, })); ----- // Your test code... -== Best practices -* Never modify original English strings - Keep them exactly as they were -* Use flat dot notation in translation files (e.g., 'page.title') -* Use nested objects in the reference file for TypeScript support -* Test with mocks to ensure translations work correctly -* Add all languages to your app configuration - -== Common Patterns -Use Case Pattern Example -Simple text t('key') t('page.title') -With variables t('key', {param}) t('table.topN', {count: '5'}) -Dynamic keys t(config.titleKey as any) t('cards.overview.title' as any) - -== Validation Checklist -* All hardcoded strings replaced with translation calls -* Translation files created for all target languages -* Translation resources exported from src/index.ts -* For RHDH: Dynamic plugins configuration updated with translationResources -* For local app: App configuration updated with available languages -* Tests updated with translation mocks -* Language switching works in the UI -* Fallback to English works for missing translations \ No newline at end of file +---- \ No newline at end of file diff --git a/modules/customizing-the-appearance/ref-best-practices-for-localization.adoc b/modules/customizing-the-appearance/ref-best-practices-for-localization.adoc new file mode 100644 index 0000000000..7518d18632 --- /dev/null +++ b/modules/customizing-the-appearance/ref-best-practices-for-localization.adoc @@ -0,0 +1,34 @@ +:_mod-docs-content-type: REFERENCE + +[id="ref-best-practices-for-localization_{context}"] += Best practices for implementing localization support for custom plugins in {product-very-short} +When you add localization support to your {product-very-short} plugins the following best practices help ensure that you establish a robust, type-safe, and future-proof localization workflow, separating the immutable source text from the organized key structure and ensuring reliable deployment across all targeted languages: + +Do not modify original English strings:: This preserves the source of truth for all translators, preventing unexpected changes that would invalidate existing translations and ensuring consistency across all versions. + +Use flat dot notation in translation files:: Flat dot notation, for example `page.title`, follows the standard `i18next` library convention, which optimizes runtime lookups and keeps the actual translation values concise and easy to manage for translation services. + +Use nested objects in the reference file for TypeScript support:: This allows the TypeScript compiler to enforce structural type checking on your translation keys, catching errors during development rather than at runtime. + +Test with mocks to ensure translations work correctly:: This isolates the translation logic, guaranteeing the correct keys are passed and rendered without relying on a full environment setup or external translation files during unit testing. + +Add all languages to your application configuration:: This ensures that the {product-very-short} application initializes and loads all necessary language resources at startup, making the locales immediately available for users to select in the UI. + +.Common patterns + +[cols="20%,35%,45%", frame="all", options="header"] +|=== +| Use case | Pattern | Example + +| Simple text +| `t('key')` +| `t('page.title')` + +| With variables +| `t('key', {param})` +| `t('table.topN', {count: '5'})` + +| Dynamic keys +| `t(config.titleKey as any)` +| `t('cards.overview.title' as any)` +|=== \ No newline at end of file From 348bc29f886302d89984b067c2d056a56520e7fa Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 14 Oct 2025 23:49:50 +0100 Subject: [PATCH 04/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- .../con-language-persistence.adoc | 2 +- .../proc-overriding-translations.adoc | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/customizing-the-appearance/con-language-persistence.adoc b/modules/customizing-the-appearance/con-language-persistence.adoc index 8087ff8fc2..f41733fd18 100644 --- a/modules/customizing-the-appearance/con-language-persistence.adoc +++ b/modules/customizing-the-appearance/con-language-persistence.adoc @@ -13,7 +13,7 @@ Default language selection uses the following priority order: . *Fallback priority*: If neither browser preferences nor configuration provide a match, defaults to `en`. -Red Hat Developer Hub automatically saves and restores user language settings across browser sessions. This feature is enabled by default and uses database storage. To opt-out and use browser storage instead, add the following to your `app-config.yaml`: +Red Hat Developer Hub automatically saves and restores user language settings across browser sessions. This feature is enabled by default and uses database storage. To opt-out and use browser storage instead, add the following to your `{my-app-config-file}` configuration file: [source,yaml,subs="+quotes"] ---- userSettings: diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index bda680cf9c..8afe1c6f67 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -2,13 +2,14 @@ [id="prov-overriding-translations_{context}"] = Overriding translations -In {product-very-short} 1.8, English and French are the supported languages. You can implement transalations for other languages by using an `allTranslations.json` file to override existing translation strings and updating the `i18n` section of your `{my-app-config-file}` configuration file. +In {product-very-short} 1.8, English and French are the supported languages. You can implement transalations for other languages by using an `allTranslations.json` file to override the existing translation strings, and updating the `i18n` section of your `{my-app-config-file}` configuration file to include the translation override file. .Prerequisitea * You have enabled localization in your {product-very-short} application. .Procedure -. Download the translation strings. +. In the top user menu, go to *Settings* > *General*. +. Click on the download link in the *Translations* panel to download the default English translation strings. . Create the JSON file containing all your translations, for example: + [id=i18n-enable] @@ -37,7 +38,7 @@ In {product-very-short} 1.8, English and French are the supported languages. You } } ---- -. Login to your cluster and create a config map for your json translations: +. Login to your cluster and create a config map for your translations override strings: + [source,bash] ---- From fe7f1d5fd50988999d959b423c85dcb7526328b2 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:06:12 +0000 Subject: [PATCH 05/54] Update modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc Co-authored-by: Debsmita Santra --- .../proc-adding-localization-to-custom-plugins.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc index 945cd1df01..29f5ff871e 100644 --- a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc +++ b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc @@ -176,7 +176,7 @@ const CardComponent = ({ config }) => { . Export the translation resources + [source,json] -.`src/index.ts` file fragment +.`src/alpha.ts` file fragment ---- // Export your plugin export { myPlugin } from "./plugin"; From a0336d1b7a1868dc8762ac744338b50168890e64 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:13:54 +0000 Subject: [PATCH 06/54] Update modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc Co-authored-by: Debsmita Santra --- .../proc-adding-localization-to-custom-plugins.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc index 29f5ff871e..057a714b0f 100644 --- a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc +++ b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc @@ -188,12 +188,13 @@ export { myPluginTranslations, myPluginTranslationRef } from "./translations"; . Update your `dynamic-plugins.default.yaml` file, as follows: + [source,json] -.`src/index.ts` file fragment +.`dynamic-plugins.default.yaml` file fragment ---- backstage-community.plugin-my-plugin: translationResources: - importName: myPluginTranslations ref: myPluginTranslationRef + module: Alpha ---- .Verification From 31ea4395a9f9b9e8a3afcbb59fcbf75d03ef4633 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:15:26 +0000 Subject: [PATCH 07/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Debsmita Santra --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 8afe1c6f67..7768ae452c 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -2,7 +2,7 @@ [id="prov-overriding-translations_{context}"] = Overriding translations -In {product-very-short} 1.8, English and French are the supported languages. You can implement transalations for other languages by using an `allTranslations.json` file to override the existing translation strings, and updating the `i18n` section of your `{my-app-config-file}` configuration file to include the translation override file. +In {product-very-short} 1.8, English and French are the supported languages. You can implement translations for other languages by using an `allTranslations.json` file to override the existing translation strings, and updating the `i18n` section of your `{my-app-config-file}` configuration file to include the translation override file. .Prerequisitea * You have enabled localization in your {product-very-short} application. From 4a64905507321f423469f360bd4ac844d701d86f Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:26:50 +0000 Subject: [PATCH 08/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- .../proc-customize-rhdh-language.adoc | 2 -- .../proc-overriding-translations.adoc | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/modules/customizing-the-appearance/proc-customize-rhdh-language.adoc b/modules/customizing-the-appearance/proc-customize-rhdh-language.adoc index c0df299ad7..a64e242f67 100644 --- a/modules/customizing-the-appearance/proc-customize-rhdh-language.adoc +++ b/modules/customizing-the-appearance/proc-customize-rhdh-language.adoc @@ -9,8 +9,6 @@ The language settings of {product-very-short} use English by default. You can ch * English * French -The default language is English, which automatically sets the light or dark theme based on your system preferences. - [NOTE] ==== English and French are the supported languages in {product-very-short} 1.8. You can add other languages in the the `i18n` section of your `{my-app-config-file}` configuration file. diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 7768ae452c..77fc620f95 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -2,14 +2,15 @@ [id="prov-overriding-translations_{context}"] = Overriding translations -In {product-very-short} 1.8, English and French are the supported languages. You can implement translations for other languages by using an `allTranslations.json` file to override the existing translation strings, and updating the `i18n` section of your `{my-app-config-file}` configuration file to include the translation override file. +In {product-very-short} 1.8, English and French are the supported languages. You can implement translations for other languages by using a JSON file to override the existing translation strings, and updating the `i18n` section of your `{my-app-config-file}` configuration file to include the JSON translation override file. .Prerequisitea * You have enabled localization in your {product-very-short} application. .Procedure -. In the top user menu, go to *Settings* > *General*. -. Click on the download link in the *Translations* panel to download the default English translation strings. +// This feature is not being included in 1.8 +// . In the top user menu, go to *Settings* > *General*. +// . Click on the download link in the *Translations* panel to download the default English translation strings. . Create the JSON file containing all your translations, for example: + [id=i18n-enable] @@ -17,25 +18,28 @@ In {product-very-short} 1.8, English and French are the supported languages. You [source,json] ---- { - "plugin.npm.translation-ref": { - "en": { - "infoCard.title": "NPM Packet JSON {{packageName}}" - }, - "de": { - "infoCard.title": "NPM Pakettt JSON {{packageName}}" + "plugin.global-floating-action-button": { + "fr": { + "fab.quay.label": "QUAY French JSON", + "fab.quay.tooltip": "QUAY french tooltip JSON", + "fab.rbac.label": "RBAC French JSON", + "fab.rbac.tooltip": "RBAC french tooltip JSON" }, - "zh": { - "infoCard.title": "NPM 包 JSON {{packageName}}" + "en": { + "fab.quay.label": "QUAY EN JSON", + "fab.rbac.label": "RBAC EN JSON", + "fab.rbac.tooltip": "RBAC EN tooltip JSON" } }, - "catalog-import": { - "en": { - "defaultImportPage.headerTitle": "Import an existing Git repository JSON" + "plugin.global-header": { + "fr": { + "applicationLauncher.developerHub": "Developer Hub French JSON" }, - "de": { - "defaultImportPage.headerTitle": "Ein vorhandenes Git-Repository importieren JSON" + "en": { + "applicationLauncher.developerHub": "Developer Hub EN JSON" } } + } ---- . Login to your cluster and create a config map for your translations override strings: From fead28bae0d0c4c959c07ee807347ebb725c9259 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:13:49 +0000 Subject: [PATCH 09/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- .../proc-enabling-localization-in-rhdh.adoc | 2 +- .../proc-overriding-translations.adoc | 42 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc index 5be7cc5254..b37961c882 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc @@ -4,7 +4,7 @@ = Enabling the localization framework in {product-short} Enabling localization enhances accessibility, improves the user experience for a global audience, and assists organizations in meeting language requirements in specific regions. -The language settings of {product-very-short} use English by default. In {product-very-short} {product-version}, you can choose to use one of the following supported languages: +The language settings of {product} ({product-very-short}) use English by default. In {product-very-short} {product-version}, you can choose to use one of the following supported languages: * English (en) * French (fr) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 77fc620f95..a55242c320 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -2,9 +2,9 @@ [id="prov-overriding-translations_{context}"] = Overriding translations -In {product-very-short} 1.8, English and French are the supported languages. You can implement translations for other languages by using a JSON file to override the existing translation strings, and updating the `i18n` section of your `{my-app-config-file}` configuration file to include the JSON translation override file. +In {product-very-short} 1.8, English and French are the supported languages. You can implement translations for other languages by using a JSON file to override the existing translation strings, and by updating the `i18n` section of your `{my-app-config-file}` configuration file to include the JSON translation override file. -.Prerequisitea +.Prerequisites * You have enabled localization in your {product-very-short} application. .Procedure @@ -46,30 +46,30 @@ In {product-very-short} 1.8, English and French are the supported languages. You + [source,bash] ---- -oc create configmap all-translations --from-file=//allTranslations.json +oc create configmap all-translations \ + --from-file=//allTranslations.json ---- -. Update your Developer hub helm chart to mount the above config map in the app’s filesystem +. Update your {product-short} `Backstage` Helm chart to mount in the {product-short} filesystem your files from the `all-translations` config map: -.. In the helm chart -> Root Schema -> Backstage chart schema -> Backstage parameters -> Backstage container additional volume mounts +.. In the {product-short} Helm chart, go to *Root Schema* → *Backstage chart schema* → *Backstage parameters* → *Backstage container additional volume mounts*. -.. Select `Add Backstage container additional volume mounts` and add the following -+ -[source,yaml] ----- -MountPath: /opt/app-root/src/translation -Name: all-translations ----- +.. Select *Add Backstage container additional volume mounts* and add the following values: -.. Add the translations to the `Backstage container additional volumes` in the helm chart -+ -[source,yaml] ----- -name: all-translations -configMap: - defaultMode: 420 - name: all-translations ----- +mountPath:: +`/opt/app-root/src/translation` +Name:: +`all-translations` + +.. Add the translations to the *Backstage container additional volumes* in the {product-short} Helm chart: + +name:: +`all-translations` +configMap:: +defaultMode::: + `420` +name::: +`all-translations` . Update the `i18n` section to your custom {product-short} `{my-app-config-file}` configuration file to include the translation override file: + From 0ac0c5332a360cc608aaf99b88c58cf3c8aeb38a Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:09:42 +0000 Subject: [PATCH 10/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- .../proc-overriding-translations.adoc | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index a55242c320..6be3fdbe18 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -50,18 +50,36 @@ oc create configmap all-translations \ --from-file=//allTranslations.json ---- -. Update your {product-short} `Backstage` Helm chart to mount in the {product-short} filesystem your files from the `all-translations` config map: +. Update your deployment configuration based on your installation method: -.. In the {product-short} Helm chart, go to *Root Schema* → *Backstage chart schema* → *Backstage parameters* → *Backstage container additional volume mounts*. +.. For an Operator-installed {product-very-short} instance, update your `Backstage` custom resource (CR). +... In the `spec.application.extraFiles` section, add the translations custom app configuration as shown in the following example: ++ +.Backstage custom resource fragment +[source,yaml,subs="+quotes"] +---- +apiVersion: rhdh.redhat.com/v1alpha3 +kind: Backstage +spec: + application: + extraFiles: + mountPath: /opt/app-root/src/translation + configMaps: + - name: all-translations +---- + +.. For an Helm-installed {product-very-short} instance, update your {product-short} `Backstage` Helm chart to mount in the {product-short} filesystem your files from the `all-translations` config map: + +... In the {product-short} Helm chart, go to *Root Schema* → *Backstage chart schema* → *Backstage parameters* → *Backstage container additional volume mounts*. -.. Select *Add Backstage container additional volume mounts* and add the following values: +... Select *Add Backstage container additional volume mounts* and add the following values: mountPath:: `/opt/app-root/src/translation` Name:: `all-translations` -.. Add the translations to the *Backstage container additional volumes* in the {product-short} Helm chart: +... Add the translations to the *Backstage container additional volumes* in the {product-short} Helm chart: name:: `all-translations` From 7128efcfa839c87262c8f4b5f644b8dae40d39b9 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:28:12 +0000 Subject: [PATCH 11/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 6be3fdbe18..a231ab7f39 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -68,7 +68,7 @@ spec: - name: all-translations ---- -.. For an Helm-installed {product-very-short} instance, update your {product-short} `Backstage` Helm chart to mount in the {product-short} filesystem your files from the `all-translations` config map: +.. For a Helm-installed {product-very-short} instance, update your {product-short} `Backstage` Helm chart to mount in the {product-short} filesystem your files from the `all-translations` config map: ... In the {product-short} Helm chart, go to *Root Schema* → *Backstage chart schema* → *Backstage parameters* → *Backstage container additional volume mounts*. From 9e9d9b61b69731e5ecd465b172da9f3f75e083a6 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:29:48 +0000 Subject: [PATCH 12/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Karthik Jeeyar --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index a231ab7f39..f2cc48b587 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -63,7 +63,7 @@ kind: Backstage spec: application: extraFiles: - mountPath: /opt/app-root/src/translation + mountPath: /opt/app-root/src/translations configMaps: - name: all-translations ---- From 8a80b351a38bdd855413844cfcaeb25b3fa9516f Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:30:16 +0000 Subject: [PATCH 13/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Karthik Jeeyar --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index f2cc48b587..4a75b9048e 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -75,7 +75,7 @@ spec: ... Select *Add Backstage container additional volume mounts* and add the following values: mountPath:: -`/opt/app-root/src/translation` +`/opt/app-root/src/translations` Name:: `all-translations` From c27782cc5acbb1474df30738beeef302ce47d2ef Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:30:36 +0000 Subject: [PATCH 14/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Karthik Jeeyar --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 4a75b9048e..0ea4b8d38c 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -76,7 +76,7 @@ spec: mountPath:: `/opt/app-root/src/translations` -Name:: +name:: `all-translations` ... Add the translations to the *Backstage container additional volumes* in the {product-short} Helm chart: From a357528dbe82ca791598b095ac63e7657cb8cfbf Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:31:04 +0000 Subject: [PATCH 15/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Karthik Jeeyar --- .../proc-overriding-translations.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 0ea4b8d38c..7c3c8b1822 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -101,7 +101,6 @@ i18n: - fr defaultLocale: en # Optional. Defaults to `en` if not specified. overrides: # List of JSON translation files applied in order (last file wins). Each file may override/add translations for one or more plugins/locales - - /.json - - /.json + - /opt/app-root/src/translations/all-translations.json ---- From 851d31352af2c94cf0bbc3a2fb5255b1626c2f16 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:31:24 +0000 Subject: [PATCH 16/54] Update modules/customizing-the-appearance/con-language-persistence.adoc Co-authored-by: Priyanka Abel --- .../customizing-the-appearance/con-language-persistence.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/con-language-persistence.adoc b/modules/customizing-the-appearance/con-language-persistence.adoc index f41733fd18..df79b56914 100644 --- a/modules/customizing-the-appearance/con-language-persistence.adoc +++ b/modules/customizing-the-appearance/con-language-persistence.adoc @@ -13,7 +13,7 @@ Default language selection uses the following priority order: . *Fallback priority*: If neither browser preferences nor configuration provide a match, defaults to `en`. -Red Hat Developer Hub automatically saves and restores user language settings across browser sessions. This feature is enabled by default and uses database storage. To opt-out and use browser storage instead, add the following to your `{my-app-config-file}` configuration file: +{product} automatically saves and restores user language settings across browser sessions. This feature is enabled by default and uses database storage. To opt-out and use browser storage instead, add the following to your `{my-app-config-file}` configuration file: [source,yaml,subs="+quotes"] ---- userSettings: From 863e698bff7145338e3d698d076df5f8a9abf844 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:31:46 +0000 Subject: [PATCH 17/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Priyanka Abel --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 7c3c8b1822..b5410f3a72 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -52,7 +52,7 @@ oc create configmap all-translations \ . Update your deployment configuration based on your installation method: -.. For an Operator-installed {product-very-short} instance, update your `Backstage` custom resource (CR). +.. For an Operator-installed {product-very-short} instance, update your `{product-custom-resource-type}` custom resource (CR). ... In the `spec.application.extraFiles` section, add the translations custom app configuration as shown in the following example: + .Backstage custom resource fragment From a854520aa4cf8e1213e2226aadf749c5a89dc557 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:33:28 +0000 Subject: [PATCH 18/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Priyanka Abel --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index b5410f3a72..4eb9aa9921 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -11,7 +11,7 @@ In {product-very-short} 1.8, English and French are the supported languages. You // This feature is not being included in 1.8 // . In the top user menu, go to *Settings* > *General*. // . Click on the download link in the *Translations* panel to download the default English translation strings. -. Create the JSON file containing all your translations, for example: +. Create the JSON file containing all your translation as shown in the following example: + [id=i18n-enable] .`allTranslations.json` fragment with translation string overrides From 1d2299b0ad8ad5e732c572c49b31f613d76e2a9a Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:50:30 +0000 Subject: [PATCH 19/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Priyanka Abel --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 4eb9aa9921..d8f1914577 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -68,7 +68,7 @@ spec: - name: all-translations ---- -.. For a Helm-installed {product-very-short} instance, update your {product-short} `Backstage` Helm chart to mount in the {product-short} filesystem your files from the `all-translations` config map: +.. For a Helm-installed {product-very-short} instance, update your {product-short} `{backstage}` Helm chart to mount in the {product-short} filesystem your files from the `all-translations` config map: ... In the {product-short} Helm chart, go to *Root Schema* → *Backstage chart schema* → *Backstage parameters* → *Backstage container additional volume mounts*. From f80edecfa69dd197d365704523932a29ed59507e Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:52:04 +0000 Subject: [PATCH 20/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Priyanka Abel --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index d8f1914577..623823d7c0 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -42,7 +42,7 @@ In {product-very-short} 1.8, English and French are the supported languages. You } ---- -. Login to your cluster and create a config map for your translations override strings: +. Log in to your cluster and create a config map for your translations override strings: + [source,bash] ---- From b4acbb659c9c9f2b9243290d86b76e5e3961a508 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:52:35 +0000 Subject: [PATCH 21/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Priyanka Abel --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 623823d7c0..47f5a76511 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -72,7 +72,7 @@ spec: ... In the {product-short} Helm chart, go to *Root Schema* → *Backstage chart schema* → *Backstage parameters* → *Backstage container additional volume mounts*. -... Select *Add Backstage container additional volume mounts* and add the following values: +... Select *Add {backstage} container additional volume mounts* and add the following values: mountPath:: `/opt/app-root/src/translations` From 27f76d48eb23739b6200a1f1d9de6fd05dfd3345 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:52:58 +0000 Subject: [PATCH 22/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Priyanka Abel --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 47f5a76511..f94795f00d 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -79,7 +79,7 @@ mountPath:: name:: `all-translations` -... Add the translations to the *Backstage container additional volumes* in the {product-short} Helm chart: +... Add the translations to the *{backstage} container additional volumes* in the {product-short} Helm chart: name:: `all-translations` From c2d345ed1cac3089af2897bb9b9669c81a3dc39e Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:53:19 +0000 Subject: [PATCH 23/54] Update modules/customizing-the-appearance/ref-best-practices-for-localization.adoc Co-authored-by: Priyanka Abel --- .../ref-best-practices-for-localization.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/ref-best-practices-for-localization.adoc b/modules/customizing-the-appearance/ref-best-practices-for-localization.adoc index 7518d18632..a8e1b36646 100644 --- a/modules/customizing-the-appearance/ref-best-practices-for-localization.adoc +++ b/modules/customizing-the-appearance/ref-best-practices-for-localization.adoc @@ -2,7 +2,7 @@ [id="ref-best-practices-for-localization_{context}"] = Best practices for implementing localization support for custom plugins in {product-very-short} -When you add localization support to your {product-very-short} plugins the following best practices help ensure that you establish a robust, type-safe, and future-proof localization workflow, separating the immutable source text from the organized key structure and ensuring reliable deployment across all targeted languages: +When you add localization support to your {product-very-short} plugins, the following best practices help ensure that you establish a robust, type-safe, and future-proof localization workflow, separating the immutable source text from the organized key structure, and ensuring reliable deployment across all targeted languages: Do not modify original English strings:: This preserves the source of truth for all translators, preventing unexpected changes that would invalidate existing translations and ensuring consistency across all versions. From 744683bbc996c59dd5d617b3701d8357fef4c0a2 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:53:38 +0000 Subject: [PATCH 24/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Priyanka Abel --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index f94795f00d..a67d9cde90 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -89,7 +89,7 @@ defaultMode::: name::: `all-translations` -. Update the `i18n` section to your custom {product-short} `{my-app-config-file}` configuration file to include the translation override file: +. Update the `i18n` section to your custom {product-short} `{my-app-config-file}` configuration file to include the following translation override file: + [id=i18n-override] .`{my-app-config-file}` fragment with localization `i18n` fields From a32ba21615983564946dffe6500a30e54ca552e6 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:54:11 +0000 Subject: [PATCH 25/54] Update modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc Co-authored-by: Priyanka Abel --- .../proc-adding-localization-to-custom-plugins.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc index 057a714b0f..cfd33bc50e 100644 --- a/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc +++ b/modules/customizing-the-appearance/proc-adding-localization-to-custom-plugins.adoc @@ -105,7 +105,7 @@ import { myPluginTranslationRef } from "../translations"; export const useTranslation = () => useTranslationRef(myPluginTranslationRef); ---- -. Update your plugin components to replace hardcoded strings with translation calls. For example: +. Update your plugin components to replace hard-coded strings with translation calls as shown in the following example: + .Before (hardcoded): [source,json] From 0285a12a9dc0599d3198516a20305bf4012500da Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:01:31 +0000 Subject: [PATCH 26/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- assemblies/assembly-localization-in-rhdh.adoc | 4 + ...-enabling-localization-in-quickstarts.adoc | 79 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc index 8466ea8a45..3b9fa3a34c 100644 --- a/assemblies/assembly-localization-in-rhdh.adoc +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -11,6 +11,10 @@ include::modules/customizing-the-appearance/proc-select-rhdh-language.adoc[level include::modules/customizing-the-appearance/con-language-persistence.adoc[leveloffset=+2] +include::modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc[leveloffset=+1] + +//include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+1] + == Localization support for custom plugins include::modules/customizing-the-appearance/ref-best-practices-for-localization.adoc[leveloffset=+2] diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc new file mode 100644 index 0000000000..1f08ab510f --- /dev/null +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc @@ -0,0 +1,79 @@ +:_mod-docs-content-type: CONCEPT + +[id="proc-enabling-localization-in-quickstarts_{context}"] += Enabling Quickstart localization in {product-very-short} + +You can enable translation key support for Quickstart titles, descriptions, and CTAs, so that users can onboard in their preferred language. In {product-short}, all existing and newly created Quickstart steps support localization using dedicated translation keys (`titleKey`, `descriptionKey`, `cta.textKey`). + +[NOTE] +If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the Quickstart configuration (`title`, `description`, `text`). If no translation key is defined at all, the original text is displayed. + +.Prerequisites +* You have enabled localization in your {product-very-short} application. + +.Procedure + +. For *all* Quickstart steps (both existing and new) in your configuration file, you must define both the original text and the new localization keys. For example, in the `quickstart` section of your custom `{my-app-config-file}` file, add the `titleKey`, `descriptionKey`, and `textKey` values, as follows: ++ +.`{my-app-config-file}` fragment +[source,yaml,subs="+quotes"] +---- +app: + quickstart: + # Existing Quickstart steps should also be updated with keys + - title: 'Setup Authentication' <1> + titleKey: steps.setupAuth.title <2> + description: 'Learn the basics of navigating the Developer Hub interface' <3> + descriptionKey: steps.setupAuth.description <4> + icon: 'home' + cta: + text: 'Get Started' <5> + textKey: steps.setupAuth.ctaTitle <6> + link: '/catalog' +# ... +---- +<1> (Mandatory) Fallback for the title. +<2> Key for the translated title. +<3> (Mandatory) Fallback for the description. +<4> Key for the translated description. +<5> (Mandatory) Fallback for the CTA text. +<6> Key for the translated CTA text. + +. In your `dynamic-plugins.yaml` file, add the `translationResources` section to your `red-hat-developer-hub-backstage-plugin-quickstart` configuration, as follows: ++ +.`{my-app-config-file}` fragment +[source,yaml,subs="+quotes"] +---- +plugins: + - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-quickstart + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-quickstart: + # translationResources definition is required for translations to work + translationResources: + - importName: quickstartTranslations <1> + ref: quickstartTranslationRef <2> + # ... other configurations like mountPoints ... +---- +<1> Name used to reference the import. +<2> Reference to the resource definition. +. In your translation file, map the keys from the first step to the localized strings for each supported language. ++ +.`allTranslations.json` fragment +[source,yaml,subs="+quotes"] +---- +"plugin.quickstart": { + "en": { + "steps.setupAuth.title": "Manage plugins EN", + "steps.setupAuth.description": "EN Browse and install extensions to add features, connect with external tools, and customize your experience.", + "steps.setupAuth.ctaTitle": "Start" + }, + "fr": { + "steps.setupAuth.title": "Gérer les plugins FR", + "steps.setupAuth.description": "FR Parcourez et installez des extensions pour ajouter des fonctionnalités, vous connecter à des outils externes et personnaliser votre expérience.", + "steps.setupAuth.ctaTitle": "Commencer" + } +} +---- \ No newline at end of file From 7bc89c6bb2736452301e7e0b6d043c94262c23ad Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Fri, 31 Oct 2025 15:25:58 +0000 Subject: [PATCH 27/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- ...nabling-localization-in-sidebar-items.adoc | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc new file mode 100644 index 0000000000..30f26891e3 --- /dev/null +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -0,0 +1,64 @@ +:_mod-docs-content-type: CONCEPT + +[id="proc-enabling-localization-in-sidebar-items_{context}"] += Enabling Sidebar items localization in {product-very-short} + +You can enable translation key support for Quickstart titles, descriptions, and CTAs, so that users can onboard in their preferred language. In {product-short}, all existing and newly created Quickstart steps support localization using dedicated translation keys (`titleKey`, `descriptionKey`, `cta.textKey`). + +[NOTE] +If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the Quickstart configuration (`title`, `description`, `text`). If no translation key is defined at all, the original text is displayed. + +.Prerequisites +* You have enabled localization in your {product-very-short} application. + +.Procedure + +. For *all* Quickstart steps (both existing and new) in your configuration file, you must define both the original text and the new localization keys. For example, in the `quickstart` section of your `{my-app-config-file}` file, add the `titleKey`, `descriptionKey`, and `textKey`, as follows: ++ +.`{my-app-config-file}` fragment +[source,yaml,subs="+quotes"] +---- +quickstart: + # Existing Quickstart steps should also be updated with keys + - title: 'Setup Authentication' # MANDATORY: Serves as the fallback text + titleKey: steps.setupAuth.title # Key for the translated title + description: 'Learn the basics of navigating the Developer Hub interface' # MANDATORY: Fallback description + descriptionKey: steps.setupAuth.description # Key for the translated description + icon: 'home' + cta: + text: 'Get Started' # MANDATORY: Fallback CTA text + textKey: steps.setupAuth.ctaTitle # Key for the translated CTA text + link: '/catalog' +---- +. In your {product-very-short} configuration file, to import your translation file, add the `translationResources` section to `dynamicPlugins:frontend:red-hat-developer-hub.backstage-plugin-quickstart` section: ++ +.`{my-app-config-file}` fragment +[source,yaml,subs="+quotes"] +---- +dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-quickstart: + # translationResources definition is required for translations to work + translationResources: + - importName: quickstartTranslations # Name used to reference the import + ref: quickstartTranslationRef # Reference to the resource definition + # ... other configurations like mountPoints ... +---- +. In your translation file, map the keys from the first step to the localized strings for each supported language. ++ +.`allTranslations.json` fragment +[source,yaml,subs="+quotes"] +---- +"plugin.quickstart": { + "en": { + "steps.setupAuth.title": "Manage plugins EN", + "steps.setupAuth.description": "EN Browse and install extensions to add features, connect with external tools, and customize your experience.", + "steps.setupAuth.ctaTitle": "Start" + }, + "fr": { + "steps.setupAuth.title": "Gérer les plugins FR", + "steps.setupAuth.description": "FR Parcourez et installez des extensions pour ajouter des fonctionnalités, vous connecter à des outils externes et personnaliser votre expérience.", + "steps.setupAuth.ctaTitle": "Commencer" + } +} +---- \ No newline at end of file From c7461be8cc6384efeca022b9e8fa890655d03042 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:41:32 +0000 Subject: [PATCH 28/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- assemblies/assembly-localization-in-rhdh.adoc | 2 +- ...nabling-localization-in-sidebar-items.adoc | 65 +++++++------------ 2 files changed, 25 insertions(+), 42 deletions(-) diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc index 3b9fa3a34c..07dd74cd92 100644 --- a/assemblies/assembly-localization-in-rhdh.adoc +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -13,7 +13,7 @@ include::modules/customizing-the-appearance/con-language-persistence.adoc[levelo include::modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc[leveloffset=+1] -//include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+1] +include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+1] == Localization support for custom plugins diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc index 30f26891e3..dda5b89744 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -1,64 +1,47 @@ :_mod-docs-content-type: CONCEPT -[id="proc-enabling-localization-in-sidebar-items_{context}"] -= Enabling Sidebar items localization in {product-very-short} +[id="proc-enabling-localization-in-sidebar-menu-items_{context}"] += Enabling sidebar menu items localization in {product-very-short} -You can enable translation key support for Quickstart titles, descriptions, and CTAs, so that users can onboard in their preferred language. In {product-short}, all existing and newly created Quickstart steps support localization using dedicated translation keys (`titleKey`, `descriptionKey`, `cta.textKey`). +You can enable translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using dedicated translation keys (`titleKey`, `descriptionKey`, `cta.textKey`). [NOTE] -If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the Quickstart configuration (`title`, `description`, `text`). If no translation key is defined at all, the original text is displayed. +If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the sidebar menu items configuration (`title`, `description`, `text`). If no translation key is defined at all, the original text is displayed. .Prerequisites * You have enabled localization in your {product-very-short} application. .Procedure -. For *all* Quickstart steps (both existing and new) in your configuration file, you must define both the original text and the new localization keys. For example, in the `quickstart` section of your `{my-app-config-file}` file, add the `titleKey`, `descriptionKey`, and `textKey`, as follows: +. For *all* sidebar menu items (both existing and new) in your configuration file, you must define both the original text and the new localization keys. For example, in the `dynamicPlugins.frontend.default.main-menu-items.menuItems.default.home` section of your `{my-app-config-file}` file, add the `titleKey`, as follows: + -.`{my-app-config-file}` fragment -[source,yaml,subs="+quotes"] ----- -quickstart: - # Existing Quickstart steps should also be updated with keys - - title: 'Setup Authentication' # MANDATORY: Serves as the fallback text - titleKey: steps.setupAuth.title # Key for the translated title - description: 'Learn the basics of navigating the Developer Hub interface' # MANDATORY: Fallback description - descriptionKey: steps.setupAuth.description # Key for the translated description - icon: 'home' - cta: - text: 'Get Started' # MANDATORY: Fallback CTA text - textKey: steps.setupAuth.ctaTitle # Key for the translated CTA text - link: '/catalog' ----- -. In your {product-very-short} configuration file, to import your translation file, add the `translationResources` section to `dynamicPlugins:frontend:red-hat-developer-hub.backstage-plugin-quickstart` section: -+ -.`{my-app-config-file}` fragment +.Example `{my-app-config-file}` fragment [source,yaml,subs="+quotes"] ---- dynamicPlugins: frontend: - red-hat-developer-hub.backstage-plugin-quickstart: - # translationResources definition is required for translations to work - translationResources: - - importName: quickstartTranslations # Name used to reference the import - ref: quickstartTranslationRef # Reference to the resource definition - # ... other configurations like mountPoints ... + default.main-menu-items: + menuItems: + default.home: + title: Home + titleKey: menuItem.home + icon: home + priority: 100 + enabled: true ---- -. In your translation file, map the keys from the first step to the localized strings for each supported language. +. In your translation file, map the `titleKey` from the first step to the localized strings for each supported language. + -.`allTranslations.json` fragment +.Example `allTranslations.json` fragment [source,yaml,subs="+quotes"] ---- -"plugin.quickstart": { - "en": { - "steps.setupAuth.title": "Manage plugins EN", - "steps.setupAuth.description": "EN Browse and install extensions to add features, connect with external tools, and customize your experience.", - "steps.setupAuth.ctaTitle": "Start" - }, - "fr": { - "steps.setupAuth.title": "Gérer les plugins FR", - "steps.setupAuth.description": "FR Parcourez et installez des extensions pour ajouter des fonctionnalités, vous connecter à des outils externes et personnaliser votre expérience.", - "steps.setupAuth.ctaTitle": "Commencer" +{ + "rhdh": { + "en": { + "menuItem.home": "Home" + }, + "fr": { + "menuItem.home": "Accueil" + } } } ---- \ No newline at end of file From 064d75b41c686259febc015a782146a43c986ae2 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sun, 2 Nov 2025 11:01:32 +0000 Subject: [PATCH 29/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- ...-configuring-a-floating-action-button.adoc | 4 ++ .../assembly-configuring-the-quickstarts.adoc | 2 + .../assembly-customizing-the-appearance.adoc | 2 + assemblies/assembly-localization-in-rhdh.adoc | 6 ++- ...upport-for-the-floating-action-button.adoc | 51 +++++++++++++++++++ ...ing-action-button-as-a-dynamic-plugin.adoc | 37 ++++++++++++++ ...ref-floating-action-button-parameters.adoc | 12 +++++ ...nabling-localization-in-sidebar-items.adoc | 4 +- 8 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc diff --git a/assemblies/assembly-configuring-a-floating-action-button.adoc b/assemblies/assembly-configuring-a-floating-action-button.adoc index 319cb61067..d2d36469d2 100644 --- a/assemblies/assembly-configuring-a-floating-action-button.adoc +++ b/assemblies/assembly-configuring-a-floating-action-button.adoc @@ -8,4 +8,8 @@ You can use the floating action button plugin to configure any action as a float include::modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc[leveloffset=+1] +// Localization +include::modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc[leveloffset=+1] +// END Localization + include::modules/configuring-a-floating-action-button/ref-floating-action-button-parameters.adoc[leveloffset=+1] diff --git a/assemblies/assembly-configuring-the-quickstarts.adoc b/assemblies/assembly-configuring-the-quickstarts.adoc index 8d5801327a..59f87c0a84 100644 --- a/assemblies/assembly-configuring-the-quickstarts.adoc +++ b/assemblies/assembly-configuring-the-quickstarts.adoc @@ -11,3 +11,5 @@ include::modules/configuring-the-quickstarts/proc-customize-rhdh-quickstart.adoc include::modules/configuring-the-quickstarts/proc-disabling-rhdh-quickstart.adoc[leveloffset=+2] include::modules/configuring-the-quickstarts/proc-starting-and-completing-modules-in-quickstarts.adoc[leveloffset=+1] + +include::modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc[leveloffset=+1] diff --git a/assemblies/assembly-customizing-the-appearance.adoc b/assemblies/assembly-customizing-the-appearance.adoc index 2c69959e63..c91ff6b7c9 100644 --- a/assemblies/assembly-customizing-the-appearance.adoc +++ b/assemblies/assembly-customizing-the-appearance.adoc @@ -36,6 +36,8 @@ include::modules/customizing-the-appearance/proc-configuring-dynamic-plugin-menu include::modules/customizing-the-appearance/proc-modifying-or-adding-rhdh-custom-menuitem.adoc[leveloffset=+2] +include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+2] + include::modules/customizing-the-appearance/proc-customizing-entity-tab-titles.adoc[leveloffset=+1] diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc index 07dd74cd92..150261b037 100644 --- a/assemblies/assembly-localization-in-rhdh.adoc +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -11,9 +11,11 @@ include::modules/customizing-the-appearance/proc-select-rhdh-language.adoc[level include::modules/customizing-the-appearance/con-language-persistence.adoc[leveloffset=+2] -include::modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc[leveloffset=+1] +// include::modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc[leveloffset=+1] -include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+1] +// include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+1] + +//include::modules/customizing-the-appearance/proc-enabling-localization-in-floating-action-button.adoc[leveloffset=+1] == Localization support for custom plugins diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc new file mode 100644 index 0000000000..fc5b10c824 --- /dev/null +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -0,0 +1,51 @@ +[id="proc-enabling-localization-in-floating-action-button_{context}"] += Enabling floating action button localization in {product-very-short} + +You can enable translation key support for floating action buttons, so that users can onboard in their preferred language. In {product-short}, all existing and newly created floating action buttons support localization using dedicated translation keys. + +The Global Floating Action Button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. + +== Built-in translation keys +The plugin provides built-in translation keys organized under the `fab` namespace: + +* `fab.create.label` - "Create" +* `fab.create.tooltip` - "Create entity" +* `fab.docs.label` - "Docs" +* `fab.docs.tooltip` - "Documentation" +* `fab.apis.label` - "APIs" +* `fab.apis.tooltip` - "API Documentation" +* `fab.github.label` - "GitHub" +* `fab.github.tooltip` - "GitHub Repository" +* `fab.bulkImport.label` - "Bulk Import" +* `fab.bulkImport.tooltip` - "Register multiple repositories in bulk" +* `fab.quay.label` - "Quay" +* `fab.quay.tooltip` - "Quay Container Registry" + +== Supported languages +The plugin includes translations for: + +* English (default) +// * German (de) +* French (fr) +// * Spanish (es) + +== Ensuring backward compatibility when providing translation support +To ensure backward compatibility while providing translation support when available, the following order is used to resolve string translations: + +. If the `labelKey` is provided, the plugin will attempt to resolve the translation key +. If the translation key is found, it will be used as the label +. If the translation key is not found, the plugin will fall back to the label property + +[NOTE] +The same logic applies to `toolTipKey` and `toolTip`. + +== Internal translation implementation +The plugin uses a centralized translation system where: + +* The `useTranslation()` hook is called in components that render floating action buttons to ensure proper translation context initialization +* The translation function (`t`) is passed down to child components that need to resolve translation keys +* This internal architecture prevents infinite re-render loops and ensures stable component rendering +* All components that use `CustomFab` must provide the translation function as a prop + +[NOTE] +When extending or modifying the plugin components, ensure that the `useTranslation()` hook is called in parent components and the `t` prop is passed to `CustomFab` instances to maintain proper translation functionality and prevent rendering issues. \ No newline at end of file diff --git a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc index 4193f14924..409d51e84d 100644 --- a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc +++ b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc @@ -203,3 +203,40 @@ To configure a floating action button as a dynamic plugin, complete any of the f text: Bulk import ---- `frontend:mountPoints:importName`:: Enter the import name with an associated component to the mount point. + += Translation support +The Global Floating Action Button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. + +.Example using translation keys in dynamic configuration +[source,yaml] +---- +- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-floating-action-button + disabled: false + pluginConfig: + dynamicPlugins: + frontend: + red-hat-developer-hub.backstage-plugin-global-floating-action-button: + mountPoints: + - mountPoint: application/listener + importName: DynamicGlobalFloatingActionButton + - mountPoint: global.floatingactionbutton/config + importName: NullComponent + config: + icon: github + label: 'GitHub' # Fallback text + labelKey: 'fab.github.label' # Translation key + toolTip: 'GitHub Repository' # Fallback text + toolTipKey: 'fab.github.tooltip' # Translation key + to: https://github.com/redhat-developer/rhdh-plugins + - mountPoint: global.floatingactionbutton/config + importName: NullComponent + config: + color: 'success' + icon: search + label: 'Create' # Fallback text + labelKey: 'fab.create.label' # Translation key + toolTip: 'Create entity' # Fallback text + toolTipKey: 'fab.create.tooltip' # Translation key + to: '/create' + showLabel: true +---- \ No newline at end of file diff --git a/modules/configuring-a-floating-action-button/ref-floating-action-button-parameters.adoc b/modules/configuring-a-floating-action-button/ref-floating-action-button-parameters.adoc index 1f2c3d54e4..d6be21b37c 100644 --- a/modules/configuring-a-floating-action-button/ref-floating-action-button-parameters.adoc +++ b/modules/configuring-a-floating-action-button/ref-floating-action-button-parameters.adoc @@ -21,6 +21,12 @@ Use the parameters as shown in the following table to configure your floating ac | Not applicable | Yes +| `labelKey` +| Translation key for the label. If provided, will be used instead of label when translations are available. +| `String` +| Not applicable +| No + | `icon` | Icon of the floating action button. Recommended to use filled icons from the link:https://fonts.google.com/icons[Material Design library]. You can also use an svg icon. For example: `` | `String`, `React.ReactElement`, `SVG image icon`, `HTML image icon` @@ -63,6 +69,12 @@ Use the parameters as shown in the following table to configure your floating ac | Not applicable | No +| `toolTipKey` +| Translation key for the tooltip. If provided, will be used instead of toolTip when translations are available. +| `String` +| Not applicable +| No + | `priority` | Order of the floating action buttons displayed in the submenu. A larger value means higher priority. | `number` diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc index dda5b89744..a929bab8d7 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -3,10 +3,10 @@ [id="proc-enabling-localization-in-sidebar-menu-items_{context}"] = Enabling sidebar menu items localization in {product-very-short} -You can enable translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using dedicated translation keys (`titleKey`, `descriptionKey`, `cta.textKey`). +You can enable translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using the `titleKey` translation key. [NOTE] -If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the sidebar menu items configuration (`title`, `description`, `text`). If no translation key is defined at all, the original text is displayed. +If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the sidebar menu items configuration (`title`). If no translation key is defined at all, the original text is displayed. .Prerequisites * You have enabled localization in your {product-very-short} application. From 63b80696b49fe39782c2a2d715e0e7c653219dff Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sun, 2 Nov 2025 11:33:57 +0000 Subject: [PATCH 30/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- assemblies/assembly-customizing-the-appearance.adoc | 4 ++-- .../proc-enabling-localization-in-sidebar-items.adoc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assemblies/assembly-customizing-the-appearance.adoc b/assemblies/assembly-customizing-the-appearance.adoc index c91ff6b7c9..04358576b8 100644 --- a/assemblies/assembly-customizing-the-appearance.adoc +++ b/assemblies/assembly-customizing-the-appearance.adoc @@ -32,12 +32,12 @@ include::modules/customizing-the-appearance/con-about-rhdh-sidebar-menuitems.ado include::modules/customizing-the-appearance/proc-customize-rhdh-sidebar-menuitems.adoc[leveloffset=+2] +include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+2] + include::modules/customizing-the-appearance/proc-configuring-dynamic-plugin-menuitem.adoc[leveloffset=+2] include::modules/customizing-the-appearance/proc-modifying-or-adding-rhdh-custom-menuitem.adoc[leveloffset=+2] -include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+2] - include::modules/customizing-the-appearance/proc-customizing-entity-tab-titles.adoc[leveloffset=+1] diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc index a929bab8d7..40b868d2a5 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -1,7 +1,7 @@ :_mod-docs-content-type: CONCEPT -[id="proc-enabling-localization-in-sidebar-menu-items_{context}"] -= Enabling sidebar menu items localization in {product-very-short} +[id="proc-translating-the-sidebar-menu-items_{context}"] += Translating the sidebar menu items in {product-very-short} You can enable translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using the `titleKey` translation key. From b8fa956acce293527c4ebfa5c2ec68aeefd20d65 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sun, 2 Nov 2025 11:47:31 +0000 Subject: [PATCH 31/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- .../proc-enabling-localization-in-sidebar-items.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc index 40b868d2a5..a929bab8d7 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -1,7 +1,7 @@ :_mod-docs-content-type: CONCEPT -[id="proc-translating-the-sidebar-menu-items_{context}"] -= Translating the sidebar menu items in {product-very-short} +[id="proc-enabling-localization-in-sidebar-menu-items_{context}"] += Enabling sidebar menu items localization in {product-very-short} You can enable translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using the `titleKey` translation key. From a6cde1fc746c5ef6320dae19a80766aaa0689d73 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:05:28 +0000 Subject: [PATCH 32/54] Update modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- .../proc-enabling-localization-in-sidebar-items.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc index a929bab8d7..adb1d510b3 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -35,7 +35,7 @@ dynamicPlugins: [source,yaml,subs="+quotes"] ---- { - "rhdh": { + "{product-very-short}": { "en": { "menuItem.home": "Home" }, From cac8ecdb9e82c9cfc968ee6b58374ea40ea3bb32 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:08:51 +0000 Subject: [PATCH 33/54] Update modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...con-localization-support-for-the-floating-action-button.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc index fc5b10c824..b2adb84fdc 100644 --- a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -3,7 +3,7 @@ You can enable translation key support for floating action buttons, so that users can onboard in their preferred language. In {product-short}, all existing and newly created floating action buttons support localization using dedicated translation keys. -The Global Floating Action Button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. +The *Global Floating Action* button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. == Built-in translation keys The plugin provides built-in translation keys organized under the `fab` namespace: From b4339278642ce4a84ee29fa28493ecd5c08a63de Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:09:26 +0000 Subject: [PATCH 34/54] Update modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...on-localization-support-for-the-floating-action-button.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc index b2adb84fdc..0953c6e9a2 100644 --- a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -5,8 +5,7 @@ You can enable translation key support for floating action buttons, so that user The *Global Floating Action* button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. -== Built-in translation keys -The plugin provides built-in translation keys organized under the `fab` namespace: +The plugin provides the following built-in translation keys organized under the `fab` namespace: * `fab.create.label` - "Create" * `fab.create.tooltip` - "Create entity" From 5467d90b38ddc1965d2a852caa78c4c707c9c2c1 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:10:19 +0000 Subject: [PATCH 35/54] Update modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- .../proc-enabling-localization-in-quickstarts.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc index 1f08ab510f..06618f3893 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc @@ -6,7 +6,9 @@ You can enable translation key support for Quickstart titles, descriptions, and CTAs, so that users can onboard in their preferred language. In {product-short}, all existing and newly created Quickstart steps support localization using dedicated translation keys (`titleKey`, `descriptionKey`, `cta.textKey`). [NOTE] +==== If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the Quickstart configuration (`title`, `description`, `text`). If no translation key is defined at all, the original text is displayed. +==== .Prerequisites * You have enabled localization in your {product-very-short} application. From fb5e8a4f781d592460bc4f77656ff141fb155711 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:10:52 +0000 Subject: [PATCH 36/54] Update modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...-enabling-localization-in-quickstarts.adoc | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc index 06618f3893..76de3b8dd8 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc @@ -23,23 +23,26 @@ If a translation key is present but the corresponding localized string is missin app: quickstart: # Existing Quickstart steps should also be updated with keys - - title: 'Setup Authentication' <1> - titleKey: steps.setupAuth.title <2> - description: 'Learn the basics of navigating the Developer Hub interface' <3> - descriptionKey: steps.setupAuth.description <4> + - title: 'Setup Authentication' + titleKey: steps.setupAuth.title + description: 'Learn the basics of navigating the Developer Hub interface' + descriptionKey: steps.setupAuth.description icon: 'home' cta: - text: 'Get Started' <5> - textKey: steps.setupAuth.ctaTitle <6> + text: 'Get Started' + textKey: steps.setupAuth.ctaTitle link: '/catalog' # ... ---- -<1> (Mandatory) Fallback for the title. -<2> Key for the translated title. -<3> (Mandatory) Fallback for the description. -<4> Key for the translated description. -<5> (Mandatory) Fallback for the CTA text. -<6> Key for the translated CTA text. ++ +where: + +`title`:: (Mandatory) Fallback for the title. +`titleKey`:: Key for the translated title. +`description`:: (Mandatory) Fallback for the description. +`descriptionKey`:: Key for the translated description. +`text`:: (Mandatory) Fallback for the CTA text. +`textKey`:: Key for the translated CTA text. . In your `dynamic-plugins.yaml` file, add the `translationResources` section to your `red-hat-developer-hub-backstage-plugin-quickstart` configuration, as follows: + From 8d43fec67592e2054acb115b24aefd6d15cd3aa2 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:11:24 +0000 Subject: [PATCH 37/54] Update modules/customizing-the-appearance/proc-overriding-translations.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- .../proc-overriding-translations.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index a67d9cde90..f055905a4d 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -55,7 +55,7 @@ oc create configmap all-translations \ .. For an Operator-installed {product-very-short} instance, update your `{product-custom-resource-type}` custom resource (CR). ... In the `spec.application.extraFiles` section, add the translations custom app configuration as shown in the following example: + -.Backstage custom resource fragment +.{product-custom-resource-type} custom resource fragment [source,yaml,subs="+quotes"] ---- apiVersion: rhdh.redhat.com/v1alpha3 From fab49904c4f64a493661c51c222134b977810f10 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:11:38 +0000 Subject: [PATCH 38/54] Update modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- .../proc-enabling-localization-in-sidebar-items.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc index adb1d510b3..0560d1ffbc 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -6,7 +6,9 @@ You can enable translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using the `titleKey` translation key. [NOTE] +==== If a translation key is present but the corresponding localized string is missing, the system defaults to the original text defined in the sidebar menu items configuration (`title`). If no translation key is defined at all, the original text is displayed. +==== .Prerequisites * You have enabled localization in your {product-very-short} application. From 0ed67ed2d912434d5dcfc6691863ff53136a6f5a Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:12:41 +0000 Subject: [PATCH 39/54] Update modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...on-localization-support-for-the-floating-action-button.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc index 0953c6e9a2..162e3c0282 100644 --- a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -20,8 +20,7 @@ The plugin provides the following built-in translation keys organized under the * `fab.quay.label` - "Quay" * `fab.quay.tooltip` - "Quay Container Registry" -== Supported languages -The plugin includes translations for: +The plugin includes translations for the following supported languages: * English (default) // * German (de) From 6d9b8571b719c6c2e1bddfa79ff842234de7b21b Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:13:16 +0000 Subject: [PATCH 40/54] Update modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- .../proc-enabling-localization-in-quickstarts.adoc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc index 76de3b8dd8..52033fbb39 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc @@ -58,12 +58,17 @@ plugins: red-hat-developer-hub.backstage-plugin-quickstart: # translationResources definition is required for translations to work translationResources: - - importName: quickstartTranslations <1> - ref: quickstartTranslationRef <2> + - importName: quickstartTranslations + ref: quickstartTranslationRef # ... other configurations like mountPoints ... ---- -<1> Name used to reference the import. -<2> Reference to the resource definition. ++ +where: + +importName:: +Enter the name used to reference the import. +ref:: +Reference to the resource definition. . In your translation file, map the keys from the first step to the localized strings for each supported language. + .`allTranslations.json` fragment From 7e76a5f3e3f73ab7d25a3e4b1f9078e4ba5c4aaf Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:16:54 +0000 Subject: [PATCH 41/54] Update modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...con-localization-support-for-the-floating-action-button.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc index 162e3c0282..6468082b49 100644 --- a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -35,7 +35,9 @@ To ensure backward compatibility while providing translation support when availa . If the translation key is not found, the plugin will fall back to the label property [NOTE] +==== The same logic applies to `toolTipKey` and `toolTip`. +==== == Internal translation implementation The plugin uses a centralized translation system where: From 7e262498feb03f12a06f32bf9e64ccf9ffd115e3 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:19:57 +0000 Subject: [PATCH 42/54] Update modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- .../con-localization-support-for-the-floating-action-button.adoc | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc index 6468082b49..3bb2fe40e0 100644 --- a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -27,7 +27,6 @@ The plugin includes translations for the following supported languages: * French (fr) // * Spanish (es) -== Ensuring backward compatibility when providing translation support To ensure backward compatibility while providing translation support when available, the following order is used to resolve string translations: . If the `labelKey` is provided, the plugin will attempt to resolve the translation key From 4afcc344b7392fed4ba4c4707aa29924a67b670a Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:20:37 +0000 Subject: [PATCH 43/54] Update modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...n-localization-support-for-the-floating-action-button.adoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc index 3bb2fe40e0..59721cb286 100644 --- a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -47,4 +47,6 @@ The plugin uses a centralized translation system where: * All components that use `CustomFab` must provide the translation function as a prop [NOTE] -When extending or modifying the plugin components, ensure that the `useTranslation()` hook is called in parent components and the `t` prop is passed to `CustomFab` instances to maintain proper translation functionality and prevent rendering issues. \ No newline at end of file +==== +When extending or modifying the plugin components, ensure that the `useTranslation()` hook is called in parent components and the `t` prop is passed to `CustomFab` instances to maintain proper translation functionality and prevent rendering issues. +==== \ No newline at end of file From 5550874a6d182c1b01c6408323eed33fb4210c11 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:24:41 +0000 Subject: [PATCH 44/54] Update modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...-configuring-floating-action-button-as-a-dynamic-plugin.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc index 409d51e84d..839085be18 100644 --- a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc +++ b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc @@ -205,7 +205,7 @@ To configure a floating action button as a dynamic plugin, complete any of the f `frontend:mountPoints:importName`:: Enter the import name with an associated component to the mount point. = Translation support -The Global Floating Action Button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. +The *Global Floating Action* button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. .Example using translation keys in dynamic configuration [source,yaml] From 959a98839b6053e800e4c90d0ecae153d346edaf Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:25:20 +0000 Subject: [PATCH 45/54] Update modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- ...-configuring-floating-action-button-as-a-dynamic-plugin.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc index 839085be18..d3163cee3a 100644 --- a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc +++ b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc @@ -207,7 +207,7 @@ To configure a floating action button as a dynamic plugin, complete any of the f = Translation support The *Global Floating Action* button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. -.Example using translation keys in dynamic configuration +Example for using translation keys in dynamic configuration: [source,yaml] ---- - package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-floating-action-button From 629fd9ffd22def8470ab0ff0011641a87187e861 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 16:25:52 +0000 Subject: [PATCH 46/54] Update modules/customizing-the-appearance/con-language-persistence.adoc Co-authored-by: Judith Magak <124673476+jmagak@users.noreply.github.com> --- .../con-language-persistence.adoc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/customizing-the-appearance/con-language-persistence.adoc b/modules/customizing-the-appearance/con-language-persistence.adoc index df79b56914..cd22f24fe5 100644 --- a/modules/customizing-the-appearance/con-language-persistence.adoc +++ b/modules/customizing-the-appearance/con-language-persistence.adoc @@ -17,7 +17,11 @@ Default language selection uses the following priority order: [source,yaml,subs="+quotes"] ---- userSettings: - persistence: browser # <1> + persistence: browser ---- -<1> To opt-out and use browser local storage, set this value to `browser`. Optionally, set this value to `database` to persist across browsers and devices. This the default setting and does not require this configuration to be set. ++ +where: + +userSettings:persistence:: +Enter `browser` to opt-out and use browser local storage. Optionally, set this value to `database` to persist across browsers and devices. This the default setting and does not require this configuration to be set. From eee8f7fdfdac2187688da5a4aed585d82cde5b3d Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:19:57 +0000 Subject: [PATCH 47/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- ...ization-support-for-the-floating-action-button.adoc | 10 +++++++++- ...ing-floating-action-button-as-a-dynamic-plugin.adoc | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc index 59721cb286..6794473a93 100644 --- a/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc +++ b/modules/configuring-a-floating-action-button/con-localization-support-for-the-floating-action-button.adoc @@ -3,7 +3,7 @@ You can enable translation key support for floating action buttons, so that users can onboard in their preferred language. In {product-short}, all existing and newly created floating action buttons support localization using dedicated translation keys. -The *Global Floating Action* button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. +The Global Floating Action Button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. The plugin provides the following built-in translation keys organized under the `fab` namespace: @@ -27,6 +27,14 @@ The plugin includes translations for the following supported languages: * French (fr) // * Spanish (es) +// [NOTE] +// ==== +// To add localization support for a new floating action button item, you can add any arbitrary key name for the `labelKey` and `toolTipKey` properties and provide corresponding translations for those keys. + +// If you add a new floating action button item, you can add localization support by adding an arbitary label key and tool tip key + +// ==== + To ensure backward compatibility while providing translation support when available, the following order is used to resolve string translations: . If the `labelKey` is provided, the plugin will attempt to resolve the translation key diff --git a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc index d3163cee3a..a523dc2c5e 100644 --- a/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc +++ b/modules/configuring-a-floating-action-button/proc-configuring-floating-action-button-as-a-dynamic-plugin.adoc @@ -205,7 +205,7 @@ To configure a floating action button as a dynamic plugin, complete any of the f `frontend:mountPoints:importName`:: Enter the import name with an associated component to the mount point. = Translation support -The *Global Floating Action* button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. +The Global Floating Action Button plugin supports internationalization (i18n) through translation keys. You can use `labelKey` and `toolTipKey` properties to provide translation keys instead of static text. Example for using translation keys in dynamic configuration: [source,yaml] @@ -216,6 +216,9 @@ Example for using translation keys in dynamic configuration: dynamicPlugins: frontend: red-hat-developer-hub.backstage-plugin-global-floating-action-button: + translationResources: + - importName: globalFloatingActionButtonTranslations + ref: globalFloatingActionButtonTranslationRef mountPoints: - mountPoint: application/listener importName: DynamicGlobalFloatingActionButton From 4e5f01e3929a85db2f6c85f4a23b5754836e081a Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Fri, 7 Nov 2025 10:39:59 +0000 Subject: [PATCH 48/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- ...enabling-localization-in-sidebar-items.adoc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc index 0560d1ffbc..175bfcd6df 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc @@ -3,7 +3,7 @@ [id="proc-enabling-localization-in-sidebar-menu-items_{context}"] = Enabling sidebar menu items localization in {product-very-short} -You can enable translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using the `titleKey` translation key. +You can add translation key support for sidebar menu items, so that users can onboard in their preferred language. In {product-short}, all existing and newly created sidebar menu items support localization using the `titleKey` translation key. [NOTE] ==== @@ -15,7 +15,7 @@ If a translation key is present but the corresponding localized string is missin .Procedure -. For *all* sidebar menu items (both existing and new) in your configuration file, you must define both the original text and the new localization keys. For example, in the `dynamicPlugins.frontend.default.main-menu-items.menuItems.default.home` section of your `{my-app-config-file}` file, add the `titleKey`, as follows: +. For sidebar menu items in your configuration file, you must define both the original text and the new localization keys. For example, in the `dynamicPlugins.frontend.default.main-menu-items.menuItems.default.favorites` section of your `{my-app-config-file}` file, add the `titleKey`, as follows: + .Example `{my-app-config-file}` fragment [source,yaml,subs="+quotes"] @@ -24,10 +24,10 @@ dynamicPlugins: frontend: default.main-menu-items: menuItems: - default.home: - title: Home - titleKey: menuItem.home - icon: home + default.favorites: + title: Favorites + titleKey: menuItem.favorites + icon: favorite priority: 100 enabled: true ---- @@ -37,12 +37,12 @@ dynamicPlugins: [source,yaml,subs="+quotes"] ---- { - "{product-very-short}": { + "rhdh": { "en": { - "menuItem.home": "Home" + "menuItem.favorites": "Favorites" }, "fr": { - "menuItem.home": "Accueil" + "menuItem.favorites": "Favoris" } } } From 59085491410300957942344711af7117314d3709 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:04:03 +0000 Subject: [PATCH 49/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- assemblies/assembly-localization-in-rhdh.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc index 150261b037..8a4b0442fd 100644 --- a/assemblies/assembly-localization-in-rhdh.adoc +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -5,12 +5,12 @@ include::modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc[leveloffset=+1] -include::modules/customizing-the-appearance/proc-overriding-translations.adoc[leveloffset=+2] - include::modules/customizing-the-appearance/proc-select-rhdh-language.adoc[leveloffset=+1] include::modules/customizing-the-appearance/con-language-persistence.adoc[leveloffset=+2] +include::modules/customizing-the-appearance/proc-overriding-translations.adoc[leveloffset=+1] + // include::modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc[leveloffset=+1] // include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+1] From 1cdf2844c33a6e8a49452fef67ef1962165ae9b4 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:29:38 +0000 Subject: [PATCH 50/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- assemblies/assembly-localization-in-rhdh.adoc | 6 +-- .../proc-overriding-translations.adoc | 42 ++++++++++++++----- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc index 8a4b0442fd..2f3e848cd4 100644 --- a/assemblies/assembly-localization-in-rhdh.adoc +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -9,15 +9,15 @@ include::modules/customizing-the-appearance/proc-select-rhdh-language.adoc[level include::modules/customizing-the-appearance/con-language-persistence.adoc[leveloffset=+2] -include::modules/customizing-the-appearance/proc-overriding-translations.adoc[leveloffset=+1] - // include::modules/customizing-the-appearance/proc-enabling-localization-in-quickstarts.adoc[leveloffset=+1] // include::modules/customizing-the-appearance/proc-enabling-localization-in-sidebar-items.adoc[leveloffset=+1] //include::modules/customizing-the-appearance/proc-enabling-localization-in-floating-action-button.adoc[leveloffset=+1] -== Localization support for custom plugins +== Localization support for plugins + +include::modules/customizing-the-appearance/proc-overriding-translations.adoc[leveloffset=+2] include::modules/customizing-the-appearance/ref-best-practices-for-localization.adoc[leveloffset=+2] diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index f055905a4d..c207821f9a 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -2,7 +2,14 @@ [id="prov-overriding-translations_{context}"] = Overriding translations -In {product-very-short} 1.8, English and French are the supported languages. You can implement translations for other languages by using a JSON file to override the existing translation strings, and by updating the `i18n` section of your `{my-app-config-file}` configuration file to include the JSON translation override file. +//In {product-very-short} 1.8, English and French are enabled by default. You can implement translations for other languages by using a JSON file to override the existing translation strings, and by updating the `i18n` section of your `{my-app-config-file}` configuration file to include the JSON translation override file. + +//In {product-very-short} 1.8, English and French are enabled by default. You can implement translations for other languages by adding the language translation strings to a file that overrides the existing translation strings. You must also update the `i18n` section of your `{my-app-config-file}` configuration file to add the new `locale` and your translation override file. + +//In {product-very-short} 1.8, English and French are enabled by default. You can add a translation strings file to your {product-short} deployment that you can use to override the default translation strings, or to implement translations for other languages. + +In {product-very-short} 1.8, you can override plugin translation strings without modifying the plugin source code. + .Prerequisites * You have enabled localization in your {product-very-short} application. @@ -11,7 +18,7 @@ In {product-very-short} 1.8, English and French are the supported languages. You // This feature is not being included in 1.8 // . In the top user menu, go to *Settings* > *General*. // . Click on the download link in the *Translations* panel to download the default English translation strings. -. Create the JSON file containing all your translation as shown in the following example: +. Create a translation stings file containing all your translation as shown in the following example: + [id=i18n-enable] .`allTranslations.json` fragment with translation string overrides @@ -19,27 +26,26 @@ In {product-very-short} 1.8, English and French are the supported languages. You ---- { "plugin.global-floating-action-button": { + "en": { + "fab.quay.label": "QUAY EN JSON", + "fab.rbac.label": "RBAC EN JSON", + "fab.rbac.tooltip": "RBAC EN tooltip JSON" + }, "fr": { "fab.quay.label": "QUAY French JSON", "fab.quay.tooltip": "QUAY french tooltip JSON", "fab.rbac.label": "RBAC French JSON", "fab.rbac.tooltip": "RBAC french tooltip JSON" - }, - "en": { - "fab.quay.label": "QUAY EN JSON", - "fab.rbac.label": "RBAC EN JSON", - "fab.rbac.tooltip": "RBAC EN tooltip JSON" } }, "plugin.global-header": { - "fr": { - "applicationLauncher.developerHub": "Developer Hub French JSON" - }, "en": { "applicationLauncher.developerHub": "Developer Hub EN JSON" + }, + "fr": { + "applicationLauncher.developerHub": "Developer Hub French JSON" } } - } ---- . Log in to your cluster and create a config map for your translations override strings: @@ -104,3 +110,17 @@ i18n: - /opt/app-root/src/translations/all-translations.json ---- +.Additional resources +// * link:{customizing-book-link}#configuring-templates[Enabling floating button localization in {product-short}] +// * link:{customizing-book-link}#configuring-templates[Enabling Quickstart localization in {product-short}] +// * link:{customizing-book-link}#configuring-templates[Enabling sidebar menu items localization in {product-short}] + +{context} + +* xref:proc-enabling-localization-in-floating-action-button_configuring-a-floating-action-button[Enabling floating button localization in {product-short}] +* xref:proc-enabling-localization-in-quickstarts_customizing-the-quickstarts[Enabling Quickstart localization in {product-short}] +* xref:proc-enabling-localization-in-sidebar-menu-items_customizing-appearance[Enabling sidebar menu items localization in {product-short}] + +// * xref:proc-enabling-localization-in-floating-action-button_{context}[fggdsg] +// * xref:proc-enabling-localization-in-quickstarts_{context}[fggdsg] +// * xref:proc-enabling-localization-in-sidebar-menu-items_{context}[fggdsg] \ No newline at end of file From 2b314199d7ae085e91d3ba7728e72a6a56b4c00a Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sat, 8 Nov 2025 19:27:03 +0000 Subject: [PATCH 51/54] RHIDP-8635-1 - Comprehensive documentation for developers on adding localization support to custom plugins --- .../customizing-the-appearance/con-language-persistence.adoc | 2 +- .../proc-overriding-translations.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/customizing-the-appearance/con-language-persistence.adoc b/modules/customizing-the-appearance/con-language-persistence.adoc index cd22f24fe5..9c0e28ba50 100644 --- a/modules/customizing-the-appearance/con-language-persistence.adoc +++ b/modules/customizing-the-appearance/con-language-persistence.adoc @@ -19,7 +19,7 @@ Default language selection uses the following priority order: userSettings: persistence: browser ---- -+ + where: userSettings:persistence:: diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index c207821f9a..602c019e97 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -58,7 +58,7 @@ oc create configmap all-translations \ . Update your deployment configuration based on your installation method: -.. For an Operator-installed {product-very-short} instance, update your `{product-custom-resource-type}` custom resource (CR). +.. For an Operator-installed {product-very-short} instance, update your `{product-custom-resource-type}` custom resource (CR). For more information about configuring a CR, see link:https://docs.redhat.com/en/documentation/red_hat_developer_hub/{product-version}/html/configuring_red_hat_developer_hub/provisioning-and-using-your-custom-configuration#using-the-operator-to-run-rhdh-with-your-custom-configuration[Using the Red Hat Developer Hub Operator to run Developer Hub with your custom configuration]. ... In the `spec.application.extraFiles` section, add the translations custom app configuration as shown in the following example: + .{product-custom-resource-type} custom resource fragment From a9120d4ea24c80bad39a55f3310e9128bcdb5c9a Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sat, 8 Nov 2025 19:42:52 +0000 Subject: [PATCH 52/54] RHIDP-8635-1 - [testday] Fix typo in localisation docs --- .../customizing-the-appearance/con-language-persistence.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/customizing-the-appearance/con-language-persistence.adoc b/modules/customizing-the-appearance/con-language-persistence.adoc index 9c0e28ba50..5e6097f3d3 100644 --- a/modules/customizing-the-appearance/con-language-persistence.adoc +++ b/modules/customizing-the-appearance/con-language-persistence.adoc @@ -23,5 +23,5 @@ userSettings: where: userSettings:persistence:: -Enter `browser` to opt-out and use browser local storage. Optionally, set this value to `database` to persist across browsers and devices. This the default setting and does not require this configuration to be set. +Enter `browser` to opt-out and use browser local storage. Optionally, set this value to `database` to persist across browsers and devices. This is the default setting and does not require this configuration to be set. From 3b42cd3d12e64ba6c80cd8b175ebb5d9dc56beb8 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sat, 8 Nov 2025 20:09:43 +0000 Subject: [PATCH 53/54] RHIDP-8635-1 - [testday] Localization: Add link to oc installation --- .../proc-overriding-translations.adoc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index 602c019e97..f335b73e3d 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -2,17 +2,11 @@ [id="prov-overriding-translations_{context}"] = Overriding translations -//In {product-very-short} 1.8, English and French are enabled by default. You can implement translations for other languages by using a JSON file to override the existing translation strings, and by updating the `i18n` section of your `{my-app-config-file}` configuration file to include the JSON translation override file. - -//In {product-very-short} 1.8, English and French are enabled by default. You can implement translations for other languages by adding the language translation strings to a file that overrides the existing translation strings. You must also update the `i18n` section of your `{my-app-config-file}` configuration file to add the new `locale` and your translation override file. - -//In {product-very-short} 1.8, English and French are enabled by default. You can add a translation strings file to your {product-short} deployment that you can use to override the default translation strings, or to implement translations for other languages. - In {product-very-short} 1.8, you can override plugin translation strings without modifying the plugin source code. - .Prerequisites * You have enabled localization in your {product-very-short} application. +* For an Operator-installed {product-very-short} instance, you have installed the {openshift-cli}. For more information about installing `oc`, see {ocp-docs-link}/html/cli_tools/openshift-cli-oc#installing-openshift-cli[Installing the OpenShift CLI]. .Procedure // This feature is not being included in 1.8 @@ -58,7 +52,7 @@ oc create configmap all-translations \ . Update your deployment configuration based on your installation method: -.. For an Operator-installed {product-very-short} instance, update your `{product-custom-resource-type}` custom resource (CR). For more information about configuring a CR, see link:https://docs.redhat.com/en/documentation/red_hat_developer_hub/{product-version}/html/configuring_red_hat_developer_hub/provisioning-and-using-your-custom-configuration#using-the-operator-to-run-rhdh-with-your-custom-configuration[Using the Red Hat Developer Hub Operator to run Developer Hub with your custom configuration]. +.. For an Operator-installed {product-very-short} instance, update your `{product-custom-resource-type}` custom resource (CR). For more information about configuring a CR, see link::https://docs.redhat.com/en/documentation/red_hat_developer_hub/{product-version}/html/configuring_red_hat_developer_hub/provisioning-and-using-your-custom-configuration#using-the-operator-to-run-rhdh-with-your-custom-configuration[Using the Red Hat Developer Hub Operator to run Developer Hub with your custom configuration]. ... In the `spec.application.extraFiles` section, add the translations custom app configuration as shown in the following example: + .{product-custom-resource-type} custom resource fragment From 6ef43d33e69f09ae6352f83e2b8b2b7130d80665 Mon Sep 17 00:00:00 2001 From: Gerry-Forde <63045020+Gerry-Forde@users.noreply.github.com> Date: Sat, 8 Nov 2025 20:52:36 +0000 Subject: [PATCH 54/54] RHIDP-8634-2 - [testday] Localization: Update enablement and language selection --- assemblies/assembly-localization-in-rhdh.adoc | 1 + .../proc-enabling-localization-in-rhdh.adoc | 2 ++ .../proc-overriding-translations.adoc | 2 +- .../customizing-the-appearance/proc-select-rhdh-language.adoc | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/assemblies/assembly-localization-in-rhdh.adoc b/assemblies/assembly-localization-in-rhdh.adoc index 2f3e848cd4..8785703aef 100644 --- a/assemblies/assembly-localization-in-rhdh.adoc +++ b/assemblies/assembly-localization-in-rhdh.adoc @@ -1,4 +1,5 @@ :_mod-docs-content-type: ASSEMBLY +:context: assembly-localization-in-rhdh [id="assembly-localization-in-rhdh_{context}"] = Localization in {product} diff --git a/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc b/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc index b37961c882..50c1c05076 100644 --- a/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc +++ b/modules/customizing-the-appearance/proc-enabling-localization-in-rhdh.adoc @@ -18,9 +18,11 @@ The language settings of {product} ({product-very-short}) use English by default .`{my-app-config-file}` fragment with localization `i18n` fields [source,yaml,subs="+quotes"] ---- +... i18n: locales: # List of supported locales. Must include `en`, otherwise the translation framework will fail to load. - en - fr defaultLocale: en # Optional. Defaults to `en` if not specified. +... ---- \ No newline at end of file diff --git a/modules/customizing-the-appearance/proc-overriding-translations.adoc b/modules/customizing-the-appearance/proc-overriding-translations.adoc index f335b73e3d..e5264a0319 100644 --- a/modules/customizing-the-appearance/proc-overriding-translations.adoc +++ b/modules/customizing-the-appearance/proc-overriding-translations.adoc @@ -12,7 +12,7 @@ In {product-very-short} 1.8, you can override plugin translation strings without // This feature is not being included in 1.8 // . In the top user menu, go to *Settings* > *General*. // . Click on the download link in the *Translations* panel to download the default English translation strings. -. Create a translation stings file containing all your translation as shown in the following example: +. Create a JSON file containing the translation strings that you want to override, as shown in the following example: + [id=i18n-enable] .`allTranslations.json` fragment with translation string overrides diff --git a/modules/customizing-the-appearance/proc-select-rhdh-language.adoc b/modules/customizing-the-appearance/proc-select-rhdh-language.adoc index dfa6c335d1..3bb91ca3fc 100644 --- a/modules/customizing-the-appearance/proc-select-rhdh-language.adoc +++ b/modules/customizing-the-appearance/proc-select-rhdh-language.adoc @@ -11,10 +11,11 @@ You can choose to use one of the following supported languages: .Prerequisites * You are logged in to the {product-short} web console. +* You have xref:proc-enabling-localization-in-rhdh_{context}[enabled the localization framework] in your {product-very-short} instance. .Procedure -. From the {product-short} web console, click *Settings*. +. From the {product-short} web console, click the down arrow next to your profile name, then click *Settings*. . From the *Appearance* panel, click the language dropdown to select your language of choice. + image::rhdh/customize-language-dropdown.png[] \ No newline at end of file