core-plugin-api: refactor translation ref to be opaque + fix some types
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -11,8 +11,8 @@ import { ReactNode } from 'react';
|
||||
// @alpha (undocumented)
|
||||
export type AppTranslationApi = {
|
||||
getI18n(): i18n;
|
||||
addResourcesByRef<Messages extends Record<string, string>>(
|
||||
translationRef: TranslationRef<Messages>,
|
||||
addResourcesByRef<TMessages extends Record<string, string>>(
|
||||
translationRef: TranslationRef<TMessages>,
|
||||
): void;
|
||||
};
|
||||
|
||||
@@ -20,11 +20,14 @@ export type AppTranslationApi = {
|
||||
export const appTranslationApiRef: ApiRef<AppTranslationApi>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const createTranslationRef: <
|
||||
Messages extends Record<keyof Messages, string> = {},
|
||||
export function createTranslationRef<
|
||||
TMessages extends {
|
||||
[key in string]: string;
|
||||
},
|
||||
TId extends string = string,
|
||||
>(
|
||||
config: TranslationRefConfig<Messages>,
|
||||
) => TranslationRef<Messages>;
|
||||
config: TranslationRefOptions<TMessages, TId>,
|
||||
): TranslationRef<TMessages, TId>;
|
||||
|
||||
// @alpha
|
||||
export interface PluginOptionsProviderProps {
|
||||
@@ -38,75 +41,45 @@ export interface PluginOptionsProviderProps {
|
||||
export const PluginProvider: (props: PluginOptionsProviderProps) => JSX.Element;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type TranslationOptions<
|
||||
Messages extends Record<keyof Messages, string> = Record<string, string>,
|
||||
> = Messages;
|
||||
export interface TranslationOptions {}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface TranslationRef<
|
||||
Messages extends Record<keyof Messages, string> = Record<string, string>,
|
||||
TMessages extends {
|
||||
[key in string]: string;
|
||||
} = {
|
||||
[key in string]: string;
|
||||
},
|
||||
TId extends string = string,
|
||||
> {
|
||||
// (undocumented)
|
||||
getDefaultMessages(): Messages;
|
||||
$$type: '@backstage/TranslationRef';
|
||||
// (undocumented)
|
||||
getId(): string;
|
||||
id: TId;
|
||||
// (undocumented)
|
||||
getLazyResources():
|
||||
| Record<
|
||||
string,
|
||||
() => Promise<{
|
||||
messages: Messages;
|
||||
}>
|
||||
>
|
||||
| undefined;
|
||||
// (undocumented)
|
||||
getResources(): Record<string, Messages> | undefined;
|
||||
T: TMessages;
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface TranslationRefConfig<
|
||||
Messages extends Record<keyof Messages, string>,
|
||||
export interface TranslationRefOptions<
|
||||
TMessages extends {
|
||||
[key in string]: string;
|
||||
},
|
||||
TId extends string = string,
|
||||
> {
|
||||
// (undocumented)
|
||||
id: string;
|
||||
id: TId;
|
||||
// (undocumented)
|
||||
lazyResources?: Record<
|
||||
string,
|
||||
() => Promise<{
|
||||
messages: Messages;
|
||||
messages: TMessages;
|
||||
}>
|
||||
>;
|
||||
// (undocumented)
|
||||
messages: Messages;
|
||||
messages: TMessages;
|
||||
// (undocumented)
|
||||
resources?: Record<string, Messages>;
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export class TranslationRefImpl<Messages extends Record<keyof Messages, string>>
|
||||
implements TranslationRef<Messages>
|
||||
{
|
||||
// (undocumented)
|
||||
static create<Messages extends Record<keyof Messages, string>>(
|
||||
config: TranslationRefConfig<Messages>,
|
||||
): TranslationRefImpl<Messages>;
|
||||
// (undocumented)
|
||||
getDefaultMessages(): Messages;
|
||||
// (undocumented)
|
||||
getId(): string;
|
||||
// (undocumented)
|
||||
getLazyResources():
|
||||
| Record<
|
||||
string,
|
||||
() => Promise<{
|
||||
messages: Messages;
|
||||
}>
|
||||
>
|
||||
| undefined;
|
||||
// (undocumented)
|
||||
getResources(): Record<string, Messages> | undefined;
|
||||
// (undocumented)
|
||||
toString(): string;
|
||||
resources?: Record<string, TMessages>;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
@@ -116,13 +89,15 @@ export function usePluginOptions<
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const useTranslationRef: <
|
||||
Messages extends Record<keyof Messages, string>,
|
||||
TMessages extends {
|
||||
[x: string]: string;
|
||||
},
|
||||
>(
|
||||
translationRef: TranslationRef<Messages>,
|
||||
) => <Tkey extends keyof Messages>(
|
||||
key: Tkey,
|
||||
translationRef: TranslationRef<TMessages, string>,
|
||||
) => <TKey extends keyof TMessages & string>(
|
||||
key: TKey,
|
||||
options?: TranslationOptions,
|
||||
) => Messages[Tkey];
|
||||
) => TMessages[TKey];
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -22,8 +22,8 @@ import { ApiRef, createApiRef } from '@backstage/core-plugin-api';
|
||||
export type AppTranslationApi = {
|
||||
getI18n(): i18n;
|
||||
|
||||
addResourcesByRef<Messages extends Record<string, string>>(
|
||||
translationRef: TranslationRef<Messages>,
|
||||
addResourcesByRef<TMessages extends Record<string, string>>(
|
||||
translationRef: TranslationRef<TMessages>,
|
||||
): void;
|
||||
};
|
||||
|
||||
@@ -31,5 +31,5 @@ export type AppTranslationApi = {
|
||||
* @alpha
|
||||
*/
|
||||
export const appTranslationApiRef: ApiRef<AppTranslationApi> = createApiRef({
|
||||
id: 'core.apptranslation',
|
||||
id: 'core.translation',
|
||||
});
|
||||
|
||||
@@ -13,30 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TranslationRefImpl, createTranslationRef } from './TranslationRef';
|
||||
import {
|
||||
createTranslationRef,
|
||||
toInternalTranslationRef,
|
||||
} from './TranslationRef';
|
||||
|
||||
describe('TranslationRefImpl', () => {
|
||||
it('should create a TranslationRef instance', () => {
|
||||
const config = {
|
||||
id: 'testId',
|
||||
messages: { key: 'value' },
|
||||
};
|
||||
|
||||
const translationRef = TranslationRefImpl.create(config);
|
||||
|
||||
expect(translationRef.getId()).toBe('testId');
|
||||
expect(translationRef.getDefaultMessages()).toEqual({ key: 'value' });
|
||||
});
|
||||
|
||||
it('should create a TranslationRef instance using the factory function', () => {
|
||||
const config = {
|
||||
id: 'testId',
|
||||
messages: { key: 'value' },
|
||||
};
|
||||
|
||||
const translationRef = createTranslationRef(config);
|
||||
const translationRef = toInternalTranslationRef(
|
||||
createTranslationRef(config),
|
||||
);
|
||||
|
||||
expect(translationRef.getId()).toBe('testId');
|
||||
expect(translationRef.id).toBe('testId');
|
||||
expect(translationRef.getDefaultMessages()).toEqual({ key: 'value' });
|
||||
});
|
||||
|
||||
@@ -49,7 +42,9 @@ describe('TranslationRefImpl', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const translationRef = TranslationRefImpl.create(config);
|
||||
const translationRef = toInternalTranslationRef(
|
||||
createTranslationRef(config),
|
||||
);
|
||||
|
||||
const lazyResources = translationRef.getLazyResources();
|
||||
|
||||
|
||||
@@ -14,48 +14,109 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TranslationRef, TranslationRefConfig } from './types';
|
||||
|
||||
/** @alpha */
|
||||
export class TranslationRefImpl<Messages extends Record<keyof Messages, string>>
|
||||
implements TranslationRef<Messages>
|
||||
{
|
||||
static create<Messages extends Record<keyof Messages, string>>(
|
||||
config: TranslationRefConfig<Messages>,
|
||||
) {
|
||||
return new TranslationRefImpl(config);
|
||||
}
|
||||
export interface TranslationRef<
|
||||
TMessages extends { [key in string]: string } = { [key in string]: string },
|
||||
TId extends string = string,
|
||||
> {
|
||||
$$type: '@backstage/TranslationRef';
|
||||
|
||||
getId() {
|
||||
return this.config.id;
|
||||
}
|
||||
id: TId;
|
||||
|
||||
getDefaultMessages(): Messages {
|
||||
return this.config.messages;
|
||||
}
|
||||
T: TMessages;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
type AnyMessages = { [key in string]: string };
|
||||
|
||||
/** @internal */
|
||||
export interface InternalTranslationRef<
|
||||
TMessages extends { [key in string]: string } = { [key in string]: string },
|
||||
TId extends string = string,
|
||||
> extends TranslationRef<TMessages, TId> {
|
||||
version: 'v1';
|
||||
|
||||
getDefaultMessages(): AnyMessages;
|
||||
|
||||
getResources(): Record<string, AnyMessages> | undefined;
|
||||
|
||||
getLazyResources():
|
||||
| Record<string, () => Promise<{ messages: Messages }>>
|
||||
| undefined {
|
||||
return this.config.lazyResources;
|
||||
}
|
||||
|
||||
getResources(): Record<string, Messages> | undefined {
|
||||
return this.config.resources;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `TranslationRef(${this.getId()})`;
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly config: TranslationRefConfig<Messages>,
|
||||
) {}
|
||||
| Record<string, () => Promise<{ messages: AnyMessages }>>
|
||||
| undefined;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export const createTranslationRef = <
|
||||
Messages extends Record<keyof Messages, string> = {},
|
||||
export interface TranslationRefOptions<
|
||||
TMessages extends { [key in string]: string },
|
||||
TId extends string = string,
|
||||
> {
|
||||
id: TId;
|
||||
messages: TMessages;
|
||||
lazyResources?: Record<string, () => Promise<{ messages: TMessages }>>;
|
||||
resources?: Record<string, TMessages>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
class TranslationRefImpl<
|
||||
TMessages extends { [key in string]: string },
|
||||
TId extends string = string,
|
||||
> implements InternalTranslationRef<TMessages, TId>
|
||||
{
|
||||
constructor(
|
||||
private readonly options: TranslationRefOptions<TMessages, TId>,
|
||||
) {}
|
||||
|
||||
$$type = '@backstage/TranslationRef' as const;
|
||||
|
||||
version = 'v1' as const;
|
||||
|
||||
get id(): TId {
|
||||
return this.options.id;
|
||||
}
|
||||
|
||||
get T(): never {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
getDefaultMessages(): AnyMessages {
|
||||
return this.options.messages;
|
||||
}
|
||||
|
||||
getLazyResources():
|
||||
| Record<string, () => Promise<{ messages: AnyMessages }>>
|
||||
| undefined {
|
||||
return this.options.lazyResources;
|
||||
}
|
||||
|
||||
getResources(): Record<string, AnyMessages> | undefined {
|
||||
return this.options.resources;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `TranslationRef{id=${this.id}}`;
|
||||
}
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export function createTranslationRef<
|
||||
TMessages extends { [key in string]: string },
|
||||
TId extends string = string,
|
||||
>(
|
||||
config: TranslationRefConfig<Messages>,
|
||||
): TranslationRef<Messages> => TranslationRefImpl.create(config);
|
||||
config: TranslationRefOptions<TMessages, TId>,
|
||||
): TranslationRef<TMessages, TId> {
|
||||
return new TranslationRefImpl(config);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function toInternalTranslationRef(
|
||||
ref: TranslationRef,
|
||||
): InternalTranslationRef {
|
||||
const r = ref as InternalTranslationRef;
|
||||
if (r.$$type !== '@backstage/TranslationRef') {
|
||||
throw new Error(`Invalid translation ref, bad type '${r.$$type}'`);
|
||||
}
|
||||
if (r.version !== 'v1') {
|
||||
throw new Error(`Invalid translation ref, bad version '${r.version}'`);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './TranslationRef';
|
||||
export * from './types';
|
||||
export * from './useTranslationRef';
|
||||
export {
|
||||
type TranslationRef,
|
||||
type TranslationRefOptions,
|
||||
createTranslationRef,
|
||||
} from './TranslationRef';
|
||||
export {
|
||||
type TranslationOptions,
|
||||
useTranslationRef,
|
||||
} from './useTranslationRef';
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** @alpha */
|
||||
export interface TranslationRefConfig<
|
||||
Messages extends Record<keyof Messages, string>,
|
||||
> {
|
||||
id: string;
|
||||
messages: Messages;
|
||||
lazyResources?: Record<string, () => Promise<{ messages: Messages }>>;
|
||||
resources?: Record<string, Messages>;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export interface TranslationRef<
|
||||
Messages extends Record<keyof Messages, string> = Record<string, string>,
|
||||
> {
|
||||
getId(): string;
|
||||
|
||||
getDefaultMessages(): Messages;
|
||||
|
||||
getResources(): Record<string, Messages> | undefined;
|
||||
|
||||
getLazyResources():
|
||||
| Record<string, () => Promise<{ messages: Messages }>>
|
||||
| undefined;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export type TranslationOptions<
|
||||
Messages extends Record<keyof Messages, string> = Record<string, string>,
|
||||
> = Messages;
|
||||
@@ -16,26 +16,33 @@
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { TranslationOptions, TranslationRef } from './types';
|
||||
import { useApi } from '../apis';
|
||||
import { appTranslationApiRef } from '../apis/alpha';
|
||||
import { toInternalTranslationRef, TranslationRef } from './TranslationRef';
|
||||
|
||||
/** @alpha */
|
||||
export interface TranslationOptions {
|
||||
/* no options supported for now */
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export const useTranslationRef = <
|
||||
Messages extends Record<keyof Messages, string>,
|
||||
TMessages extends { [key in string]: string },
|
||||
>(
|
||||
translationRef: TranslationRef<Messages>,
|
||||
translationRef: TranslationRef<TMessages>,
|
||||
) => {
|
||||
const appTranslationApi = useApi(appTranslationApiRef);
|
||||
|
||||
appTranslationApi.addResourcesByRef(translationRef);
|
||||
|
||||
const { t } = useTranslation(translationRef.getId());
|
||||
const internalRef = toInternalTranslationRef(translationRef);
|
||||
|
||||
const defaulteMessage = translationRef.getDefaultMessages();
|
||||
const { t } = useTranslation(internalRef.id);
|
||||
|
||||
return <Tkey extends keyof Messages>(
|
||||
key: Tkey,
|
||||
const defaultMessages = internalRef.getDefaultMessages();
|
||||
|
||||
return <TKey extends keyof TMessages & string>(
|
||||
key: TKey,
|
||||
options?: TranslationOptions,
|
||||
): Messages[Tkey] => t(key as string, defaulteMessage[key], options);
|
||||
): TMessages[TKey] => t(key, defaultMessages[key], options);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user