Skip to content

Commit c9b7aa5

Browse files
feat(global-fab): add localization (#1574)
* feat(localization): add localization for fab Signed-off-by: its-mitesh-kumar <[email protected]> * updating changeset Signed-off-by: its-mitesh-kumar <[email protected]> * updating api report Signed-off-by: its-mitesh-kumar <[email protected]> * fixing test Signed-off-by: its-mitesh-kumar <[email protected]> * fixing yatn tsc issue Signed-off-by: its-mitesh-kumar <[email protected]> --------- Signed-off-by: its-mitesh-kumar <[email protected]>
1 parent 97011bd commit c9b7aa5

File tree

23 files changed

+577
-20
lines changed

23 files changed

+577
-20
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-global-floating-action-button': minor
3+
---
4+
5+
Adding localization support for `label` and `tooltip`

workspaces/global-floating-action-button/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"lint": "backstage-cli repo lint --since origin/main",
2121
"lint:all": "backstage-cli repo lint",
2222
"prettier:check": "prettier --check .",
23+
"prettier:fix": "prettier --write .",
2324
"new": "backstage-cli new --scope @red-hat-developer-hub",
2425
"postinstall": "cd ../../ && yarn install"
2526
},

workspaces/global-floating-action-button/packages/app/src/App.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { apis } from './apis';
5252
import { entityPage } from './components/catalog/EntityPage';
5353
import { Root } from './components/Root';
5454
import { searchPage } from './components/search/SearchPage';
55+
import { globalFloatingActionButtonTranslations } from '@red-hat-developer-hub/backstage-plugin-global-floating-action-button';
5556

5657
const app = createApp({
5758
apis,
@@ -90,6 +91,10 @@ const app = createApp({
9091
),
9192
},
9293
themes: getThemes(),
94+
__experimentalTranslations: {
95+
availableLanguages: ['en', 'de', 'fr', 'es'],
96+
resources: [globalFloatingActionButtonTranslations],
97+
},
9398
});
9499

95100
const routes = (

workspaces/global-floating-action-button/packages/app/src/components/Root/Root.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,43 +93,57 @@ export const Root = ({ children }: PropsWithChildren<{}>) => {
9393
color: 'success',
9494
icon: <CreateComponentIcon />,
9595
label: 'Create',
96+
labelKey: 'fab.create.label',
97+
showLabel: true,
9698
toolTip: 'Create entity',
99+
toolTipKey: 'fab.create.tooltip',
97100
to: '/create',
98101
},
99102
{
100103
slot: Slot.BOTTOM_LEFT,
101104
icon: <ExtensionIcon />,
102105
label: 'APIs',
106+
labelKey: 'fab.apis.label',
103107
toolTip: 'APIs',
108+
toolTipKey: 'fab.apis.tooltip',
104109
to: '/api-docs',
105110
},
106111
{
107112
slot: Slot.BOTTOM_LEFT,
108113
icon: <LibraryBooks />,
109-
showLabel: true,
110114
label: 'Docs',
115+
labelKey: 'fab.docs.label',
116+
showLabel: true,
111117
toolTip: 'Docs',
118+
toolTipKey: 'fab.docs.tooltip',
112119
to: '/docs',
113120
},
114121
{
115122
icon: <SearchIcon />,
116123
label: 'Search',
124+
labelKey: 'fab.search.label',
117125
toolTip: 'Search',
126+
toolTipKey: 'fab.search.tooltip',
118127
onClick: toggleModal,
119128
},
120129
{
121130
icon: <GitHubIcon />,
122131
label: 'RHDH plugins',
132+
labelKey: 'fab.github.label',
123133
showLabel: true,
124134
toolTip: 'RHDH plugins',
135+
toolTipKey: 'fab.github.tooltip',
125136
to: 'https://github.com/redhat-developer/rhdh-plugins',
126137
visibleOnPaths: ['/catalog'],
127138
},
128139
{
129140
color: 'success',
130141
icon: <UserSettingsSignInAvatar />,
131142
label: 'Settings',
143+
labelKey: 'fab.settings.label',
144+
showLabel: true,
132145
toolTip: 'Settings',
146+
toolTipKey: 'fab.settings.tooltip',
133147
to: '/settings',
134148
excludeOnPaths: ['/settings'],
135149
},

workspaces/global-floating-action-button/plugins/global-floating-action-button/README.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,17 +251,147 @@ The sections below are relevant for static plugins. If the plugin is expected to
251251
| ------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
252252
| **slot** | `enum` | The position where the fab will be placed. Valid values: `PAGE_END`, `BOTTOM_LEFT`. | [optional] default to `PAGE_END`. |
253253
| **label** | `String` | A name for your action button. | required |
254+
| **labelKey** | `String` | Translation key for the label. If provided, will be used instead of label when translations are available. | optional |
254255
| **icon** | `String`<br>`React.ReactElement`<br>`SVG image icon`<br>`HTML image icon` | An icon for your floating button. Recommended to use **filled** icons from the [Material Design library](https://fonts.google.com/icons) | optional |
255256
| **showLabel** | `Boolean` | To display the label next to your icon. | optional |
256257
| **size** | `'small'`<br>`'medium'`<br>`'large'` | A name for your action button. | [optional] default to `'medium'` |
257258
| **color** | `'default'`<br>`'error'`<br>`'info'`<br>`'inherit'`<br>`'primary'`<br>`'secondary'`<br>`'success'`<br>`'warning'` | The color of the component. It supports both default and custom theme colors, which can be added as shown in the [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). | [optional] default to `'default'`. |
258259
| **onClick** | `React.MouseEventHandler` | the action to be performed on `onClick`. | optional |
259260
| **to** | `String` | Specify an href if the action button should open a internal/external link. | optional |
260261
| **toolTip** | `String` | The text to appear on hover. | optional |
262+
| **toolTipKey** | `String` | Translation key for the tooltip. If provided, will be used instead of toolTip when translations are available. | optional |
261263
| **priority** | `number` | When multiple sub-menu actions are displayed, the button can be prioritized to position either at the top or the bottom. | optional |
262264
| **visibleOnPaths** | `string[]` | The action button will appear only on the specified paths and will remain hidden on all other paths. | [optional] default to displaying on all paths. |
263265
| **excludeOnPaths** | `string[]` | The action button will be hidden only on the specified paths and will appear on all other paths. | [optional] default to displaying on all paths. |
264266

267+
### Translation Support
268+
269+
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.
270+
271+
#### Using Translation Keys in Dynamic Configuration
272+
273+
```yaml title="dynamic-plugins.yaml"
274+
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-floating-action-button
275+
disabled: false
276+
pluginConfig:
277+
dynamicPlugins:
278+
frontend:
279+
red-hat-developer-hub.backstage-plugin-global-floating-action-button:
280+
mountPoints:
281+
- mountPoint: application/listener
282+
importName: DynamicGlobalFloatingActionButton
283+
- mountPoint: global.floatingactionbutton/config
284+
importName: NullComponent
285+
config:
286+
icon: github
287+
label: 'GitHub' # Fallback text
288+
labelKey: 'fab.github.label' # Translation key
289+
toolTip: 'GitHub Repository' # Fallback text
290+
toolTipKey: 'fab.github.tooltip' # Translation key
291+
to: https://github.com/redhat-developer/rhdh-plugins
292+
- mountPoint: global.floatingactionbutton/config
293+
importName: NullComponent
294+
config:
295+
color: 'success'
296+
icon: search
297+
label: 'Create' # Fallback text
298+
labelKey: 'fab.create.label' # Translation key
299+
toolTip: 'Create entity' # Fallback text
300+
toolTipKey: 'fab.create.tooltip' # Translation key
301+
to: '/create'
302+
showLabel: true
303+
```
304+
305+
#### Using Translation Keys in Static Configuration
306+
307+
```tsx title="packages/app/src/components/Root/Root.tsx"
308+
import {
309+
GlobalFloatingActionButton,
310+
Slot,
311+
} from '@red-hat-developer-hub/backstage-plugin-global-floating-action-button';
312+
313+
export const Root = ({ children }: PropsWithChildren<{}>) => (
314+
<SidebarPage>
315+
{/* ... */}
316+
<GlobalFloatingActionButton
317+
floatingButtons={[
318+
{
319+
color: 'success',
320+
icon: <CreateComponentIcon />,
321+
label: 'Create', // Fallback text
322+
labelKey: 'fab.create.label', // Translation key
323+
toolTip: 'Create entity', // Fallback text
324+
toolTipKey: 'fab.create.tooltip', // Translation key
325+
to: '/create',
326+
},
327+
{
328+
slot: Slot.BOTTOM_LEFT,
329+
icon: <LibraryBooks />,
330+
label: 'Docs', // Fallback text
331+
labelKey: 'fab.docs.label', // Translation key
332+
toolTip: 'Documentation', // Fallback text
333+
toolTipKey: 'fab.docs.tooltip', // Translation key
334+
to: '/docs',
335+
},
336+
]}
337+
/>
338+
{/* ... */}
339+
</SidebarPage>
340+
);
341+
```
342+
343+
#### Translation Setup
344+
345+
The plugin automatically registers its translations when loaded. The translation system is built into the plugin configuration and will be available when the plugin is installed.
346+
347+
For dynamic plugins, translations are automatically loaded with the plugin. For static installations, the translations are registered through the plugin's `__experimentalTranslations` configuration.
348+
349+
#### Built-in Translation Keys
350+
351+
The plugin provides built-in translation keys organized under the `fab` namespace:
352+
353+
- `fab.create.label` - "Create"
354+
- `fab.create.tooltip` - "Create entity"
355+
- `fab.docs.label` - "Docs"
356+
- `fab.docs.tooltip` - "Documentation"
357+
- `fab.apis.label` - "APIs"
358+
- `fab.apis.tooltip` - "API Documentation"
359+
- `fab.github.label` - "GitHub"
360+
- `fab.github.tooltip` - "GitHub Repository"
361+
- `fab.bulkImport.label` - "Bulk Import"
362+
- `fab.bulkImport.tooltip` - "Register multiple repositories in bulk"
363+
- `fab.quay.label` - "Quay"
364+
- `fab.quay.tooltip` - "Quay Container Registry"
365+
366+
#### Supported Languages
367+
368+
The plugin includes translations for:
369+
370+
- **English** (default)
371+
- **German** (de)
372+
- **French** (fr)
373+
- **Spanish** (es)
374+
375+
#### How Translation Resolution Works
376+
377+
1. If `labelKey` is provided, the plugin will attempt to resolve the translation key
378+
2. If the translation key is found, it will be used as the label
379+
3. If the translation key is not found, the plugin will fall back to the `label` property
380+
4. The same logic applies to `toolTipKey` and `toolTip`
381+
382+
This ensures backward compatibility while providing translation support when available.
383+
384+
#### Internal Translation Implementation
385+
386+
The plugin uses a centralized translation system where:
387+
388+
- The `useTranslation()` hook is called in components that render floating action buttons to ensure proper translation context initialization
389+
- The translation function (`t`) is passed down to child components that need to resolve translation keys
390+
- This internal architecture prevents infinite re-render loops and ensures stable component rendering
391+
- All components that use `CustomFab` must provide the translation function as a prop
392+
393+
**Note for Developers**: 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.
394+
265395
**NOTE**
266396

267397
If multiple floating button actions are assigned to the same `Slot`, they will appear as sub-menu options within the floating button.

workspaces/global-floating-action-button/plugins/global-floating-action-button/dev/index.tsx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,38 @@ import {
2828
DynamicGlobalFloatingActionButton,
2929
FloatingActionButton,
3030
Slot,
31+
globalFloatingActionButtonPlugin,
32+
globalFloatingActionButtonTranslations,
3133
} from '../src';
3234

3335
const mockFloatingButtons: FloatingActionButton[] = [
3436
{
3537
color: 'success',
3638
icon: <GitIcon />,
37-
label: 'Git repo',
39+
label: 'GitHub',
40+
labelKey: 'fab.github.label',
3841
showLabel: true,
3942
to: 'https://github.com/xyz',
40-
toolTip: 'Git',
43+
toolTip: 'GitHub Repository',
44+
toolTipKey: 'fab.github.tooltip',
4145
},
4246

4347
{
4448
color: 'info',
4549
label: 'Quay',
50+
labelKey: 'fab.quay.label',
4651
to: 'https://quay.io',
47-
toolTip: 'Quay',
52+
toolTip: 'Quay Container Registry',
53+
toolTipKey: 'fab.quay.tooltip',
4854
icon: '<svg viewBox="0 0 250 300" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M200.134 0l55.555 117.514-55.555 117.518h-47.295l55.555-117.518L152.84 0h47.295zM110.08 99.836l20.056-38.092-2.29-8.868L102.847 0H55.552l48.647 102.898 5.881-3.062zm17.766 74.433l-17.333-39.034-6.314-3.101-48.647 102.898h47.295l25-52.88v-7.883z" fill="#40B4E5"/><path d="M152.842 235.032L97.287 117.514 152.842 0h47.295l-55.555 117.514 55.555 117.518h-47.295zm-97.287 0L0 117.514 55.555 0h47.296L47.295 117.514l55.556 117.518H55.555z" fill="#003764"/></svg>',
4955
},
5056
{
5157
color: 'success',
52-
label: 'Quay',
53-
to: 'https://quay.io',
54-
toolTip: 'Quay',
58+
label: 'Docs',
59+
labelKey: 'fab.docs.label',
60+
to: '/docs',
61+
toolTip: 'Documentation',
62+
toolTipKey: 'fab.docs.tooltip',
5563
},
5664
{
5765
slot: Slot.BOTTOM_LEFT,
@@ -104,19 +112,23 @@ const createPage = ({
104112
config: {
105113
color: 'success',
106114
icon: 'github',
107-
label: 'DP: Git repo',
115+
label: 'DP: GitHub',
116+
labelKey: 'fab.github.label',
108117
showLabel: true,
109118
to: 'https://github.com/xyz',
110-
toolTip: 'Git',
119+
toolTip: 'GitHub Repository',
120+
toolTipKey: 'fab.github.tooltip',
111121
},
112122
},
113123
{
114124
staticJSXContent: null,
115125
config: {
116126
color: 'info',
117127
label: 'Quay',
128+
labelKey: 'fab.quay.label',
118129
to: 'https://quay.io',
119-
toolTip: 'Quay',
130+
toolTip: 'Quay Container Registry',
131+
toolTipKey: 'fab.quay.tooltip',
120132
icon: '<svg viewBox="0 0 250 300" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M200.134 0l55.555 117.514-55.555 117.518h-47.295l55.555-117.518L152.84 0h47.295zM110.08 99.836l20.056-38.092-2.29-8.868L102.847 0H55.552l48.647 102.898 5.881-3.062zm17.766 74.433l-17.333-39.034-6.314-3.101-48.647 102.898h47.295l25-52.88v-7.883z" fill="#40B4E5"/><path d="M152.842 235.032L97.287 117.514 152.842 0h47.295l-55.555 117.514 55.555 117.518h-47.295zm-97.287 0L0 117.514 55.555 0h47.296L47.295 117.514l55.556 117.518H55.555z" fill="#003764"/></svg>',
121133
},
122134
},
@@ -126,8 +138,10 @@ const createPage = ({
126138
slot: 'bottom-left',
127139
color: 'success',
128140
label: 'Github',
141+
labelKey: 'fab.github.label',
129142
icon: 'github',
130143
toolTip: 'Github',
144+
toolTipKey: 'fab.github.tooltip',
131145
to: 'https://github.com/xyz',
132146
priority: 200,
133147
visibleOnPaths: ['/test-global-floating-action-3'],
@@ -156,6 +170,10 @@ const createPage = ({
156170
};
157171

158172
createDevApp()
173+
.registerPlugin(globalFloatingActionButtonPlugin)
174+
.addTranslationResource(globalFloatingActionButtonTranslations)
175+
.setAvailableLanguages(['en', 'de', 'fr', 'es'])
176+
.setDefaultLanguage('en')
159177
.addPage({
160178
element: <ExampleComponent floatingButtons={mockFloatingButtons} />,
161179
title: 'Page 1',

workspaces/global-floating-action-button/plugins/global-floating-action-button/report.api.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import { BackstagePlugin } from '@backstage/core-plugin-api';
99
import { JSX as JSX_2 } from 'react/jsx-runtime';
10+
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
11+
import { TranslationResource } from '@backstage/core-plugin-api/alpha';
1012

1113
// @public
1214
export const DynamicGlobalFloatingActionButton: React.ComponentType;
@@ -27,6 +29,7 @@ export type FABMountPoint = {
2729
export type FloatingActionButton = {
2830
slot?: Slot;
2931
label: string;
32+
labelKey?: string;
3033
showLabel?: boolean;
3134
icon?: string | React.ReactElement;
3235
size?: 'small' | 'medium' | 'large';
@@ -43,6 +46,7 @@ export type FloatingActionButton = {
4346
onClick?: React.MouseEventHandler;
4447
to?: string;
4548
toolTip?: string;
49+
toolTipKey?: string;
4650
priority?: number;
4751
visibleOnPaths?: string[];
4852
excludeOnPaths?: string[];
@@ -64,6 +68,29 @@ export const GlobalFloatingActionButton: ({
6468
// @public
6569
export const globalFloatingActionButtonPlugin: BackstagePlugin<{}, {}, {}>;
6670

71+
// @public
72+
export const globalFloatingActionButtonTranslationRef: TranslationRef<
73+
'plugin.global-floating-action-button',
74+
{
75+
readonly 'fab.menu.tooltip': string;
76+
readonly 'fab.github.label': string;
77+
readonly 'fab.github.tooltip': string;
78+
readonly 'fab.create.label': string;
79+
readonly 'fab.create.tooltip': string;
80+
readonly 'fab.docs.label': string;
81+
readonly 'fab.docs.tooltip': string;
82+
readonly 'fab.apis.label': string;
83+
readonly 'fab.apis.tooltip': string;
84+
readonly 'fab.bulkImport.label': string;
85+
readonly 'fab.bulkImport.tooltip': string;
86+
readonly 'fab.quay.label': string;
87+
readonly 'fab.quay.tooltip': string;
88+
}
89+
>;
90+
91+
// @public
92+
export const globalFloatingActionButtonTranslations: TranslationResource<'plugin.global-floating-action-button'>;
93+
6794
// @public
6895
export const NullComponent: React.ComponentType;
6996

0 commit comments

Comments
 (0)