Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
19e7af3
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 14, 2025
9a243fa
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 14, 2025
d902569
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 14, 2025
348bc29
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 14, 2025
2822628
Merge branch 'redhat-developer:main' into RHIDP-8635-1
Gerry-Forde Oct 28, 2025
fe7f1d5
Update modules/customizing-the-appearance/proc-adding-localization-to…
Gerry-Forde Oct 28, 2025
a0336d1
Update modules/customizing-the-appearance/proc-adding-localization-to…
Gerry-Forde Oct 28, 2025
31ea439
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 28, 2025
4a64905
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 28, 2025
fead28b
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 28, 2025
0ac0c53
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 28, 2025
7128efc
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 28, 2025
9e9d9b6
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
8a80b35
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
c27782c
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
a357528
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
851d313
Update modules/customizing-the-appearance/con-language-persistence.adoc
Gerry-Forde Oct 30, 2025
863e698
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
a854520
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
1d2299b
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
f80edec
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
b4acbb6
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
27f76d4
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
c2d345e
Update modules/customizing-the-appearance/ref-best-practices-for-loca…
Gerry-Forde Oct 30, 2025
744683b
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Oct 30, 2025
a32ba21
Update modules/customizing-the-appearance/proc-adding-localization-to…
Gerry-Forde Oct 30, 2025
0285a12
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 31, 2025
77316ca
Merge branch 'main' into RHIDP-8635-1
Gerry-Forde Oct 31, 2025
7bc89c6
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 31, 2025
c7461be
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Oct 31, 2025
064d75b
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 2, 2025
91c380a
Merge branch 'main' into RHIDP-8635-1
Gerry-Forde Nov 2, 2025
63b8069
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 2, 2025
b8fa956
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 2, 2025
a6cde1f
Update modules/customizing-the-appearance/proc-enabling-localization-…
Gerry-Forde Nov 6, 2025
cac8ecd
Update modules/configuring-a-floating-action-button/con-localization-…
Gerry-Forde Nov 6, 2025
b433927
Update modules/configuring-a-floating-action-button/con-localization-…
Gerry-Forde Nov 6, 2025
5467d90
Update modules/customizing-the-appearance/proc-enabling-localization-…
Gerry-Forde Nov 6, 2025
fb5e8a4
Update modules/customizing-the-appearance/proc-enabling-localization-…
Gerry-Forde Nov 6, 2025
8d43fec
Update modules/customizing-the-appearance/proc-overriding-translation…
Gerry-Forde Nov 6, 2025
fab4990
Update modules/customizing-the-appearance/proc-enabling-localization-…
Gerry-Forde Nov 6, 2025
0ed67ed
Update modules/configuring-a-floating-action-button/con-localization-…
Gerry-Forde Nov 6, 2025
6d9b857
Update modules/customizing-the-appearance/proc-enabling-localization-…
Gerry-Forde Nov 6, 2025
7e76a5f
Update modules/configuring-a-floating-action-button/con-localization-…
Gerry-Forde Nov 6, 2025
7e26249
Update modules/configuring-a-floating-action-button/con-localization-…
Gerry-Forde Nov 6, 2025
4afcc34
Update modules/configuring-a-floating-action-button/con-localization-…
Gerry-Forde Nov 6, 2025
5550874
Update modules/configuring-a-floating-action-button/proc-configuring-…
Gerry-Forde Nov 6, 2025
959a988
Update modules/configuring-a-floating-action-button/proc-configuring-…
Gerry-Forde Nov 6, 2025
629fd9f
Update modules/customizing-the-appearance/con-language-persistence.adoc
Gerry-Forde Nov 6, 2025
f0cb2d3
Merge branch 'main' into RHIDP-8635-1
Gerry-Forde Nov 6, 2025
eee8f7f
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 6, 2025
1784af9
Merge branch 'main' into RHIDP-8635-1
Gerry-Forde Nov 6, 2025
ad0722c
Merge branch 'redhat-developer:main' into RHIDP-8635-1
Gerry-Forde Nov 7, 2025
4e5f01e
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 7, 2025
5908549
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 8, 2025
20814c7
Merge branch 'main' into RHIDP-8635-1
Gerry-Forde Nov 8, 2025
1cdf284
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 8, 2025
2b31419
RHIDP-8635-1 - Comprehensive documentation for developers on adding l…
Gerry-Forde Nov 8, 2025
a9120d4
RHIDP-8635-1 - [testday] Fix typo in localisation docs
Gerry-Forde Nov 8, 2025
3b42cd3
RHIDP-8635-1 - [testday] Localization: Add link to oc installation
Gerry-Forde Nov 8, 2025
6ef43d3
RHIDP-8634-2 - [testday] Localization: Update enablement and language…
Gerry-Forde Nov 8, 2025
57f79de
RHIDP-8635-1 - Localization typo link fix
Gerry-Forde Nov 10, 2025
a2be32a
RHIDP-8635-1 - Localization: Update package.json file
Gerry-Forde Nov 10, 2025
2aa55fa
Merge branch 'main' into RHIDP-8635-1
Gerry-Forde Nov 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions assemblies/assembly-localization-in-rhdh.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
:_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]

== Localization support for custom plugins
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
== Localization support for custom plugins

I suggest creating a separate assembly, Localization Support for Custom Plugins, for these modules, unless we’re fine keeping them in the same assembly. Otherwise, we can address this after the release.”


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]

Binary file added images/rhdh/customize-language-dropdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions modules/customizing-the-appearance/con-language-persistence.adoc
Original file line number Diff line number Diff line change
@@ -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 `{my-app-config-file}` configuration file:
[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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
:_mod-docs-content-type: PROCEDURE

[id="proc-adding-localization-to-custom-plugins_{context}"]
= 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:
+
.`src/translations/ref.ts` English reference
[source,json]
----
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 translation
[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 translation
[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 file, as follows:
+
.`src/hooks/useTranslation.ts` Translation hooks
[source,json]
----
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. For example:
+
.Before (hardcoded):
[source,json]
----
const MyComponent = () => {
return (
<div>
<h1>My Plugin</h1>
<button>Export CSV</button>
</div>
);
};
----
+
.After (translated):
[source,json]
----
import { useTranslation } from '../hooks/useTranslation';

const MyComponent = () => {
const { t } = useTranslation();

return (
<div>
<h1>{t('page.title')}</h1>
<button>{t('common.exportCSV')}</button>
</div>
);
};
----

. (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]
----
// 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 (
<div>
<h2>{t(config.titleKey as any)}</h2>
{/* Use 'as any' for dynamic keys */}
</div>
);
};
----

. Export the translation resources
+
[source,json]
.`src/alpha.ts` file fragment
----
// Export your plugin
export { myPlugin } from "./plugin";

// Export translation resources for RHDH
export { myPluginTranslations, myPluginTranslationRef } from "./translations";
----

. Update your `dynamic-plugins.default.yaml` file, as follows:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
. Update your `dynamic-plugins.default.yaml` file, as follows:
. Update your `dynamic-plugins-default.yaml` file, as shown in the following example

(I think it's dynamic-plugins-default.yaml... I'm not fully sure but I think the file names wouldn't have two periods. Please check this)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+
[source,json]
.`dynamic-plugins.default.yaml` file fragment
----
backstage-community.plugin-my-plugin:
translationResources:
- importName: myPluginTranslations
ref: myPluginTranslationRef
module: Alpha
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@debsmita1 in your earlier review, I see you have suggested to add Alpha module instead of regular export from src/index, I thought about this earlier when I created this gist but I preferred src/index.ts over Alpha module export as it avoids some more changes in the package.json like scalprum, exports and typeVersions.

If in case if we go with this Alpha module approach, then we must also include the changes required in package.json

change in plugin/package.json:

  "exports": {
    ".": "./src/index.ts",
    "./alpha": "./src/alpha.ts",
    "./package.json": "./package.json"
  },
  "typesVersions": {
    "*": {
      "alpha": [
        "src/alpha.ts"
      ],
      "package.json": [
        "package.json"
      ]
    }
  },
  "scalprum": {
    "name": "backstage-community.backstage-plugin-test",
    "exposedModules": {
      "Alpha": "./src/alpha.ts",
      "PluginRoot": "./src/index.ts",
    }
  },

I recommend, lets skip all this and use export from src/index.ts to keep it simple, I would love to hear your thoughts on this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karthikjeeyar This has been added to the marketplace wrapper's package.json in rhdh

"exports": {
    ".": "./src/index.ts",
    "./alpha": "./src/alpha.ts",
    "./package.json": "./package.json"
  },
  "typesVersions": {
    "*": {
      "alpha": [
        "src/alpha.ts"
      ],
      "package.json": [
        "package.json"
      ]
    }
  },
"scalprum": {
    "name": "red-hat-developer-hub.backstage-plugin-marketplace",
    "exposedModules": {
      "PluginRoot": "./src/index.ts",
      "Alpha": "./src/alpha.ts"
    }
  },

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we have handled this in our plugins via wrappers, but this documentation section talks about how to add localization for the user's custom plugins (which may reside in backstage-community or their own repo).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this document only covers how to add translations to the plugin and not how to integrate them into RHDH, then we can follow the approach used in this package.json: https://github.com/backstage/community-plugins/blob/main/workspaces/linguist/package.json

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @debsmita1 and @karthikjeeyar, could you please confirm what should be updated at line 197?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@debsmita1 I think above mentioned package.json isn't the right link.

@Gerry-Forde To export Alpha module we need to also add this changes to the plugin's package.json file - https://github.com/backstage/community-plugins/blob/main/workspaces/linguist/plugins/linguist/package.json#L24-L40

----

.Verification
To verify your translations, create a test mock file. For example:

.`src/test-utils/mockTranslations.ts` Test mock file
[source,json]
----
import { myPluginMessages } from "../translations/ref";

function flattenMessages(obj: any, prefix = ""): Record<string, string> {
const flattened: Record<string, string> = {};
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
[source,json]
----
import { mockUseTranslation } from "../test-utils/mockTranslations";

jest.mock("../hooks/useTranslation", () => ({
useTranslation: mockUseTranslation,
}));

// Your test code...
----
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
:_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

[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[]
Original file line number Diff line number Diff line change
@@ -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} ({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.
----
Loading