Skip to content

Commit 83e7003

Browse files
Move i18n key specification from UI Schema options to UI Schema element
The 'i18n' base key is no longer specified as part of the UI Schema `options` but moved up to the UI Schema element itself. The new 'Internationalizable' interface can be extended by internationalizable UI Schema elements. For now this is the 'ControlELement'.
1 parent a3d2ac0 commit 83e7003

File tree

4 files changed

+61
-5
lines changed

4 files changed

+61
-5
lines changed

packages/core/src/i18n/i18nUtil.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ErrorObject } from 'ajv';
2-
import { UISchemaElement } from '../models';
2+
import { isInternationalized, UISchemaElement } from '../models';
33
import { getControlPath } from '../reducers';
44
import { formatErrorMessage } from '../util';
55
import { i18nJsonSchema, ErrorTranslator, Translator } from './i18nTypes';
@@ -8,7 +8,10 @@ export const getI18nKeyPrefixBySchema = (
88
schema: i18nJsonSchema | undefined,
99
uischema: UISchemaElement | undefined
1010
): string | undefined => {
11-
return uischema?.options?.i18n ?? schema?.i18n ?? undefined;
11+
if (isInternationalized(uischema)) {
12+
return uischema.i18n;
13+
}
14+
return schema?.i18n ?? undefined;
1215
};
1316

1417
/**

packages/core/src/models/uischema.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ export interface Scopable {
3636
scope: string;
3737
}
3838

39+
/**
40+
* Interface for describing an UI schema element that can provide an internationalization base key.
41+
* If defined, this key is suffixed to derive applicable message keys for the UI schema element.
42+
* For example, such suffixes are `.label` or `.description` to derive the corresponding message keys for a control element.
43+
*/
44+
export interface Internationalizable {
45+
i18n?: string;
46+
}
47+
3948
/**
4049
* A rule that may be attached to any UI schema element.
4150
*/
@@ -207,7 +216,7 @@ export interface LabelElement extends UISchemaElement {
207216
* A control element. The scope property of the control determines
208217
* to which part of the schema the control should be bound.
209218
*/
210-
export interface ControlElement extends UISchemaElement, Scopable {
219+
export interface ControlElement extends UISchemaElement, Scopable, Internationalizable {
211220
type: 'Control';
212221
/**
213222
* An optional label that will be associated with the control
@@ -244,6 +253,10 @@ export interface Categorization extends UISchemaElement {
244253
elements: (Category | Categorization)[];
245254
}
246255

256+
export const isInternationalized = (element: unknown): element is Required<Internationalizable> => {
257+
return typeof element === 'object' && element !== null && typeof (element as Internationalizable).i18n === 'string';
258+
}
259+
247260
export const isGroup = (layout: Layout): layout is GroupLayout =>
248261
layout.type === 'Group';
249262

packages/core/test/i18n/i18nUtil.test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*/
2525
import test from 'ava';
2626

27-
import { transformPathToI18nPrefix } from '../../src';
27+
import { ControlElement, getI18nKeyPrefixBySchema, i18nJsonSchema, transformPathToI18nPrefix } from '../../src';
2828

2929
test('transformPathToI18nPrefix returns root when empty', t => {
3030
t.is(transformPathToI18nPrefix(''), 'root');
@@ -46,3 +46,43 @@ test('transformPathToI18nPrefix removes array indices', t => {
4646
t.is(transformPathToI18nPrefix('foo1.23.b2ar3.1.5.foo'), 'foo1.b2ar3.foo');
4747
t.is(transformPathToI18nPrefix('3'), 'root');
4848
});
49+
50+
test('getI18nKeyPrefixBySchema gets key from uischema over schema', t => {
51+
const control: ControlElement = {
52+
type: 'Control',
53+
scope: '#/properties/foo',
54+
i18n: 'controlFoo'
55+
};
56+
const schema: i18nJsonSchema = {
57+
type: 'string',
58+
i18n: 'schemaFoo'
59+
}
60+
t.is(getI18nKeyPrefixBySchema(schema, control), 'controlFoo');
61+
});
62+
63+
test('getI18nKeyPrefixBySchema gets schema key for missing uischema key', t => {
64+
const control: ControlElement = {
65+
type: 'Control',
66+
scope: '#/properties/foo',
67+
};
68+
const schema: i18nJsonSchema = {
69+
type: 'string',
70+
i18n: 'schemaFoo'
71+
}
72+
t.is(getI18nKeyPrefixBySchema(schema, control), 'schemaFoo');
73+
});
74+
75+
test('getI18nKeyPrefixBySchema returns undefined for missing uischema and schema keys', t => {
76+
const control: ControlElement = {
77+
type: 'Control',
78+
scope: '#/properties/foo',
79+
};
80+
const schema: i18nJsonSchema = {
81+
type: 'string',
82+
}
83+
t.is(getI18nKeyPrefixBySchema(schema, control), undefined);
84+
});
85+
86+
test('getI18nKeyPrefixBySchema returns undefined for undefined parameters', t => {
87+
t.is(getI18nKeyPrefixBySchema(undefined, undefined), undefined);
88+
});

packages/core/test/util/renderer.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,7 +1344,7 @@ test('mapStateToControlProps - i18n - translation via UI Schema i18n key', t =>
13441344
};
13451345
const state: JsonFormsState = createState(coreUISchema);
13461346
state.jsonforms.i18n = defaultJsonFormsI18nState;
1347-
ownProps.uischema = {...ownProps.uischema, options: {i18n: 'my-key'}};
1347+
ownProps.uischema = {...ownProps.uischema, i18n: 'my-key'};
13481348
state.jsonforms.i18n.translate = (key: string, defaultMessage: string | undefined) => {
13491349
switch(key){
13501350
case 'my-key.label': return 'my label';

0 commit comments

Comments
 (0)