Merge pull request #21569 from backstage/rugvip/frontend-translations

frontend-plugin-api: re-export translation API and add createTranslationExtension
This commit is contained in:
Patrik Oldsberg
2023-12-05 10:39:33 +01:00
committed by GitHub
10 changed files with 279 additions and 10 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Add support for translation extensions.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': patch
---
Added translation APIs as well as `createTranslationExtension`.
@@ -18,6 +18,7 @@ import {
coreExtensionData,
createExtension,
createExtensionInput,
createTranslationExtension,
} from '@backstage/frontend-plugin-api';
export const Core = createExtension({
@@ -33,6 +34,9 @@ export const Core = createExtension({
components: createExtensionInput({
component: coreExtensionData.component,
}),
translations: createExtensionInput({
translation: createTranslationExtension.translationDataRef,
}),
root: createExtensionInput(
{
element: coreExtensionData.reactElement,
@@ -23,6 +23,7 @@ import {
ComponentRef,
componentsApiRef,
coreExtensionData,
createTranslationExtension,
ExtensionDataRef,
ExtensionOverrides,
RouteRef,
@@ -375,6 +376,16 @@ function createApiHolder(
?.map(e => e.instance?.getData(coreExtensionData.theme))
.filter((x): x is AppTheme => !!x) ?? [];
const translationResources =
tree.root.edges.attachments
.get('translations')
?.map(e =>
e.instance?.getData(createTranslationExtension.translationDataRef),
)
.filter(
(x): x is typeof createTranslationExtension.translationDataRef.T => !!x,
) ?? [];
for (const factory of [...defaultApis, ...pluginApis]) {
factoryRegistry.register('default', factory);
}
@@ -431,15 +442,6 @@ function createApiHolder(
factory: () => AppLanguageSelector.createWithStorage(),
});
factoryRegistry.register('default', {
api: translationApiRef,
deps: { languageApi: appLanguageApiRef },
factory: ({ languageApi }) =>
I18nextTranslationApi.create({
languageApi,
}),
});
factoryRegistry.register('static', {
api: configApiRef,
deps: {},
@@ -452,12 +454,13 @@ function createApiHolder(
factory: () => AppLanguageSelector.createWithStorage(),
});
factoryRegistry.register('default', {
factoryRegistry.register('static', {
api: translationApiRef,
deps: { languageApi: appLanguageApiRef },
factory: ({ languageApi }) =>
I18nextTranslationApi.create({
languageApi,
resources: translationResources,
}),
});
@@ -31,6 +31,9 @@ import { ConfigApi } from '@backstage/core-plugin-api';
import { configApiRef } from '@backstage/core-plugin-api';
import { createApiFactory } from '@backstage/core-plugin-api';
import { createApiRef } from '@backstage/core-plugin-api';
import { createTranslationMessages } from '@backstage/core-plugin-api/alpha';
import { createTranslationRef } from '@backstage/core-plugin-api/alpha';
import { createTranslationResource } from '@backstage/core-plugin-api/alpha';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { discoveryApiRef } from '@backstage/core-plugin-api';
import { ErrorApi } from '@backstage/core-plugin-api';
@@ -74,9 +77,16 @@ import { SignInPageProps } from '@backstage/core-plugin-api';
import { StorageApi } from '@backstage/core-plugin-api';
import { storageApiRef } from '@backstage/core-plugin-api';
import { StorageValueSnapshot } from '@backstage/core-plugin-api';
import { TranslationMessages } from '@backstage/core-plugin-api/alpha';
import { TranslationMessagesOptions } from '@backstage/core-plugin-api/alpha';
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
import { TranslationRefOptions } from '@backstage/core-plugin-api/alpha';
import { TranslationResource } from '@backstage/core-plugin-api/alpha';
import { TranslationResourceOptions } from '@backstage/core-plugin-api/alpha';
import { TypesToApiRefs } from '@backstage/core-plugin-api';
import { useApi } from '@backstage/core-plugin-api';
import { useApiHolder } from '@backstage/core-plugin-api';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { withApis } from '@backstage/core-plugin-api';
import { z } from 'zod';
import { ZodSchema } from 'zod';
@@ -628,6 +638,34 @@ export function createThemeExtension(
theme: AppTheme,
): ExtensionDefinition<never>;
// @public (undocumented)
export function createTranslationExtension(options: {
name?: string;
resource: TranslationResource | TranslationMessages;
}): ExtensionDefinition<never>;
// @public (undocumented)
export namespace createTranslationExtension {
const // (undocumented)
translationDataRef: ConfigurableExtensionDataRef<
| TranslationResource<string>
| TranslationMessages<
string,
{
[x: string]: string;
},
boolean
>,
{}
>;
}
export { createTranslationMessages };
export { createTranslationRef };
export { createTranslationResource };
export { DiscoveryApi };
export { discoveryApiRef };
@@ -946,6 +984,18 @@ export interface SubRouteRef<
readonly T: TParams;
}
export { TranslationMessages };
export { TranslationMessagesOptions };
export { TranslationRef };
export { TranslationRefOptions };
export { TranslationResource };
export { TranslationResourceOptions };
export { TypesToApiRefs };
// @public
@@ -973,5 +1023,7 @@ export function useRouteRefParams<Params extends AnyRouteRefParams>(
_routeRef: RouteRef<Params> | SubRouteRef<Params>,
): Params;
export { useTranslationRef };
export { withApis };
```
@@ -0,0 +1,128 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
createTranslationRef,
createTranslationMessages,
createTranslationResource,
} from '@backstage/core-plugin-api/alpha';
import { createTranslationExtension } from './createTranslationExtension';
const translationRef = createTranslationRef({
id: 'test',
messages: {
a: 'a',
b: 'b',
},
});
describe('createTranslationExtension', () => {
it('creates a translation message extension', () => {
const messages = createTranslationMessages({
ref: translationRef,
messages: {
a: 'A',
},
});
const extension = createTranslationExtension({ resource: messages });
expect(extension).toEqual({
$$type: '@backstage/ExtensionDefinition',
kind: 'translation',
namespace: 'test',
attachTo: { id: 'core', input: 'translations' },
disabled: false,
inputs: {},
output: {
resource: createTranslationExtension.translationDataRef,
},
factory: expect.any(Function),
});
expect(extension.factory({} as any)).toEqual({
resource: messages,
});
});
it('creates a translation resource extension', () => {
const resource = createTranslationResource({
ref: translationRef,
translations: {
sv: () =>
Promise.resolve({
default: createTranslationMessages({
ref: translationRef,
messages: {
a: 'Ä',
b: 'Ö',
},
}),
}),
},
});
const extension = createTranslationExtension({ resource });
expect(extension).toEqual({
$$type: '@backstage/ExtensionDefinition',
kind: 'translation',
namespace: 'test',
attachTo: { id: 'core', input: 'translations' },
disabled: false,
inputs: {},
output: {
resource: createTranslationExtension.translationDataRef,
},
factory: expect.any(Function),
});
expect(extension.factory({} as any)).toEqual({ resource });
});
it('creates a translation resource extension with a name', () => {
expect(
createTranslationExtension({
name: 'sv',
resource: createTranslationResource({
ref: translationRef,
translations: {
sv: () =>
Promise.resolve({
default: createTranslationMessages({
ref: translationRef,
messages: {
a: 'Ä',
b: 'Ö',
},
}),
}),
},
}),
}),
).toEqual({
$$type: '@backstage/ExtensionDefinition',
kind: 'translation',
namespace: 'test',
name: 'sv',
attachTo: { id: 'core', input: 'translations' },
disabled: false,
inputs: {},
output: {
resource: createTranslationExtension.translationDataRef,
},
factory: expect.any(Function),
});
});
});
@@ -0,0 +1,42 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TranslationMessages, TranslationResource } from '../translation';
import { createExtension, createExtensionDataRef } from '../wiring';
/** @public */
export function createTranslationExtension(options: {
name?: string;
resource: TranslationResource | TranslationMessages;
}) {
return createExtension({
kind: 'translation',
namespace: options.resource.id,
name: options.name,
attachTo: { id: 'core', input: 'translations' },
output: {
resource: createTranslationExtension.translationDataRef,
},
factory: () => ({ resource: options.resource }),
});
}
/** @public */
export namespace createTranslationExtension {
export const translationDataRef = createExtensionDataRef<
TranslationResource | TranslationMessages
>('core.translationResource');
}
@@ -20,3 +20,4 @@ export { createNavItemExtension } from './createNavItemExtension';
export { createSignInPageExtension } from './createSignInPageExtension';
export { createThemeExtension } from './createThemeExtension';
export { createComponentExtension } from './createComponentExtension';
export { createTranslationExtension } from './createTranslationExtension';
@@ -28,6 +28,7 @@ export * from './icons';
export * from './routing';
export * from './schema';
export * from './apis/system';
export * from './translation';
export * from './wiring';
export type {
@@ -0,0 +1,28 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
type TranslationMessages,
type TranslationMessagesOptions,
type TranslationResource,
type TranslationResourceOptions,
type TranslationRef,
type TranslationRefOptions,
createTranslationMessages,
createTranslationResource,
createTranslationRef,
useTranslationRef,
} from '@backstage/core-plugin-api/alpha';