From 6aa1bd26386a50a43e3f68db34c1e44061ef63ee Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 20 Jan 2025 09:46:46 +0100 Subject: [PATCH 1/3] BUGFIX: Forgiving `I18nRegistry.translate` for strings with colons The following node type configuration ```yaml placeholder: 'ClientEval: node.properties.tagName' ``` currently yields an error which is caught by the error boundary for properties: > Error: TranslationAddress must adhere to format "{packageKey}:{sourceName}:{transUnitId}". Got "ClientEval: node.properties.tagName" instead. The newly added strictness in https://github.com/neos/neos-ui/pull/3804 introduces this regression here. Previously the function `getTranslationAddress` would just attempt to spit a string and expect three items to be returned: https://github.com/neos/neos-ui/blob/2cecfcf136aafcd9e3f72537f513eb81b01e993b/packages/neos-ui-i18n/src/registry/I18nRegistry.js#L7 The deconstruction would already cause an error in PHP but not in JavaScript: ``` const [packageKey, sourceName, id] = 'ClientEval: node.properties.tagName'.split(':') ``` Returns ``` packageKey: "ClientEval" sourceName: " node.properties.tagName" id: undefined ``` The previous forgiveness needs to be reintroduced as its easy to create errors with strings containing colons at any position which is definitely likely for placeholders. Imagine: `placeholder: "The title of the blog:"`. --- .../src/model/TranslationAddress.spec.ts | 19 +++++++++++++++++++ .../src/model/TranslationAddress.ts | 15 ++++++++++++--- .../registry/getTranslationAddress.spec.ts | 13 +++++++++++++ .../src/registry/getTranslationAddress.ts | 8 ++++++-- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/packages/neos-ui-i18n/src/model/TranslationAddress.spec.ts b/packages/neos-ui-i18n/src/model/TranslationAddress.spec.ts index f10ae30a7f..4474814571 100644 --- a/packages/neos-ui-i18n/src/model/TranslationAddress.spec.ts +++ b/packages/neos-ui-i18n/src/model/TranslationAddress.spec.ts @@ -41,4 +41,23 @@ describe('TranslationAddress', () => { .becauseStringDoesNotAdhereToExpectedFormat('foo bar') ); }); + + it('can be try created from string', () => { + const translationAddress = TranslationAddress.tryFromString( + 'Some.Package:SomeSource:some.transunit.id' + ); + + expect(translationAddress).not.toBeNull(); + expect(translationAddress?.id).toBe('some.transunit.id'); + expect(translationAddress?.sourceName).toBe('SomeSource'); + expect(translationAddress?.packageKey).toBe('Some.Package'); + expect(translationAddress?.fullyQualified).toBe('Some.Package:SomeSource:some.transunit.id'); + }); + + it('try with invalid string returns null', () => { + expect(TranslationAddress.tryFromString('foo bar')).toBeNull(); + expect(TranslationAddress.tryFromString('something:')).toBeNull(); + // error in placeholder https://github.com/neos/neos-ui/pull/3907 + expect(TranslationAddress.tryFromString('ClientEval: node.properties.tagName')).toBeNull(); + }); }); diff --git a/packages/neos-ui-i18n/src/model/TranslationAddress.ts b/packages/neos-ui-i18n/src/model/TranslationAddress.ts index a563b3889b..067ea1a7ba 100644 --- a/packages/neos-ui-i18n/src/model/TranslationAddress.ts +++ b/packages/neos-ui-i18n/src/model/TranslationAddress.ts @@ -24,17 +24,26 @@ export class TranslationAddress { }): TranslationAddress => new TranslationAddress(props.id, props.sourceName, props.packageKey, `${props.packageKey}:${props.sourceName}:${props.id}`); - public static fromString = (string: string): TranslationAddress => { + public static tryFromString = (string: string): TranslationAddress|null => { const parts = string.split(TRANSLATION_ADDRESS_SEPARATOR); if (parts.length !== 3) { - throw TranslationAddressIsInvalid - .becauseStringDoesNotAdhereToExpectedFormat(string); + return null; } const [packageKey, sourceName, id] = parts; return new TranslationAddress(id, sourceName, packageKey, string); } + + public static fromString = (string: string): TranslationAddress => { + const translationAddress = TranslationAddress.tryFromString(string); + if (translationAddress === null) { + throw TranslationAddressIsInvalid + .becauseStringDoesNotAdhereToExpectedFormat(string); + } + + return translationAddress; + } } export class TranslationAddressIsInvalid extends Error { diff --git a/packages/neos-ui-i18n/src/registry/getTranslationAddress.spec.ts b/packages/neos-ui-i18n/src/registry/getTranslationAddress.spec.ts index 3707e20377..e5f546fde2 100644 --- a/packages/neos-ui-i18n/src/registry/getTranslationAddress.spec.ts +++ b/packages/neos-ui-i18n/src/registry/getTranslationAddress.spec.ts @@ -31,4 +31,17 @@ describe('getTranslationAddress', () => { expect(translationAddress.sourceName).toBe('SomeSource'); expect(translationAddress.packageKey).toBe('Some.Package'); }); + + it('try with invalid string returns null', () => { + // error in placeholder https://github.com/neos/neos-ui/pull/3907 + const translationAddress = getTranslationAddress( + 'ClientEval: node.properties.tagName', + 'Some.Package', + 'SomeSource' + ); + + expect(translationAddress.id).toBe('ClientEval: node.properties.tagName'); + expect(translationAddress.sourceName).toBe('SomeSource'); + expect(translationAddress.packageKey).toBe('Some.Package'); + }); }); diff --git a/packages/neos-ui-i18n/src/registry/getTranslationAddress.ts b/packages/neos-ui-i18n/src/registry/getTranslationAddress.ts index 4a3a742824..8cee85e924 100644 --- a/packages/neos-ui-i18n/src/registry/getTranslationAddress.ts +++ b/packages/neos-ui-i18n/src/registry/getTranslationAddress.ts @@ -9,6 +9,9 @@ */ import {TranslationAddress} from '../model'; +/** + * @deprecated legacy implementation for I18nRegistry we want to use the TranslationAddress instead. + */ export function getTranslationAddress( fullyQualifiedTransUnitId: string ): TranslationAddress; @@ -22,8 +25,9 @@ export function getTranslationAddress( packageKey?: string, sourceName?: string ) { - if (id && id.indexOf(':') !== -1) { - return TranslationAddress.fromString(id); + const fullyQualifiedTranslationAddress = TranslationAddress.tryFromString(id); + if (fullyQualifiedTranslationAddress !== null) { + return fullyQualifiedTranslationAddress; } if (packageKey === undefined) { From 82219fb04bf48ca678902a846f77c0d60613eda4 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:57:09 +0100 Subject: [PATCH 2/3] TASK: Apply php storm suggestions to new i18n code --- packages/neos-ui-i18n/README.md | 28 ++++++------- packages/neos-ui-i18n/src/component/I18n.tsx | 2 +- .../neos-ui-i18n/src/global/globals.spec.ts | 6 +-- packages/neos-ui-i18n/src/global/globals.ts | 10 ++--- .../neos-ui-i18n/src/registry/I18nRegistry.ts | 42 +++++++++---------- packages/neos-ui-i18n/src/translate.ts | 4 +- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/packages/neos-ui-i18n/README.md b/packages/neos-ui-i18n/README.md index 0fd7260786..931f44876a 100644 --- a/packages/neos-ui-i18n/README.md +++ b/packages/neos-ui-i18n/README.md @@ -22,7 +22,7 @@ Neos: // ... ``` -At the beginning of the UI bootstrapping process, translations are loaded from an enpoint (see: [`\Neos\Neos\Controller\Backend\BackendController->xliffAsJsonAction()`](https://neos.github.io/neos/9.0/Neos/Neos/Controller/Backend/BackendController.html#method_xliffAsJsonAction)) and are available afterwards via the `translate` function exposed by this package. +At the beginning of the UI bootstrapping process, translations are loaded from an endpoint (see: [`\Neos\Neos\Controller\Backend\BackendController->xliffAsJsonAction()`](https://neos.github.io/neos/9.0/Neos/Neos/Controller/Backend/BackendController.html#method_xliffAsJsonAction)) and are available afterwards via the `translate` function exposed by this package. ## API @@ -90,18 +90,18 @@ Copy {source} to {target} For numerically indexed placeholders, you can pass an array of strings to the `parameters` argument of `translate`. For named parameters, you can pass an object with string values and keys identifying the parameters. -Translations may also have plural forms. `translate` uses the [`Intl` Web API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) to pick the currect plural form for the current `Locale` based on the given `quantity`. +Translations may also have plural forms. `translate` uses the [`Intl` Web API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) to pick the correct plural form for the current `Locale` based on the given `quantity`. -Fallbacks can also provide plural forms, but will always treated as if we're in locale `en-US`, so you can only provide two different plural forms. +Fallbacks can also provide plural forms, but will always be treated as if we're in locale `en-US`, so you can only provide two different plural forms. #### Arguments -| Name | Description | -|-|-| -| `fullyQualifiedTranslationAddressAsString` | The translation address for the translation to use, e.g.: `"Neos.Neos.Ui:Main:errorBoundary.title"` | -| `fallback` | The string to return, if no translation can be found under the given address. If a tuple of two strings is passed here, these will be treated as singular and plural forms of the translation. | -| `parameters` | Values to replace placeholders in the translation with. This can be passed as an array of strings (to replace numerically indexed placeholders) or as a `Record` (to replace named placeholders) | -| `quantity` | The quantity is used to determine which plural form (if any) to use for the translation | +| Name | Description | +|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `fullyQualifiedTranslationAddressAsString` | The translation address for the translation to use, e.g.: `"Neos.Neos.Ui:Main:errorBoundary.title"` | +| `fallback` | The string to return, if no translation can be found under the given address. If a tuple of two strings is passed here, these will be treated as singular and plural forms of the translation. | +| `parameters` | Values to replace placeholders in the translation with. This can be passed as an array of strings (to replace numerically indexed placeholders) or as a `Record` (to replace named placeholders) | +| `quantity` | The quantity is used to determine which plural form (if any) to use for the translation | #### Examples @@ -168,7 +168,7 @@ async function initializeI18n(): Promise; This function loads the translations from the translations endpoint and makes them available globally. It must be run exactly once before any call to `translate`. -The exact URL of the translations endpoint is discoverd via the DOM. The document needs to have a link tag with the id `neos-ui-uri:/neos/xliff.json`, with the following attributes: +The exact URL of the translations endpoint is discovered via the DOM. The document needs to have a link tag with the id `neos-ui-uri:/neos/xliff.json`, with the following attributes: ```html getPluralForms()`](https://neos.github.io/flow/9.0/Neos/Flow/I18n/Cldr/Reader/PluralsReader.html#method_getPluralForms) is expected, e.g.: `one,other` for `de-DE`, or `zero,one,two,few,many` for `ar-EG` | -| `translations` | The XLIFF translations in their JSON-serialized form | +| `translations` | The XLIFF translations in their JSON-serialized form | ##### `TranslationsDTO` diff --git a/packages/neos-ui-i18n/src/component/I18n.tsx b/packages/neos-ui-i18n/src/component/I18n.tsx index e14b6d353a..34c34a7c64 100644 --- a/packages/neos-ui-i18n/src/component/I18n.tsx +++ b/packages/neos-ui-i18n/src/component/I18n.tsx @@ -30,7 +30,7 @@ interface I18nProps { } /** - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead */ export class I18n extends React.PureComponent { public render(): JSX.Element { diff --git a/packages/neos-ui-i18n/src/global/globals.spec.ts b/packages/neos-ui-i18n/src/global/globals.spec.ts index 8623ed1bb0..a5ef1420d1 100644 --- a/packages/neos-ui-i18n/src/global/globals.spec.ts +++ b/packages/neos-ui-i18n/src/global/globals.spec.ts @@ -7,7 +7,7 @@ * information, please view the LICENSE file which was distributed with this * source code. */ -import {GlobalsRuntimeContraintViolation, requireGlobals, setGlobals, unsetGlobals} from './globals'; +import {GlobalsRuntimeConstraintViolation, requireGlobals, setGlobals, unsetGlobals} from './globals'; describe('globals', () => { afterEach(() => { @@ -17,7 +17,7 @@ describe('globals', () => { test('requireGlobals throws when globals are not initialized yet', () => { expect(() => requireGlobals()) .toThrow( - GlobalsRuntimeContraintViolation + GlobalsRuntimeConstraintViolation .becauseGlobalsWereRequiredButHaveNotBeenSetYet() ); }); @@ -31,7 +31,7 @@ describe('globals', () => { setGlobals('foo' as any); expect(() => setGlobals('bar' as any)) .toThrow( - GlobalsRuntimeContraintViolation + GlobalsRuntimeConstraintViolation .becauseGlobalsWereAttemptedToBeSetMoreThanOnce() ); }); diff --git a/packages/neos-ui-i18n/src/global/globals.ts b/packages/neos-ui-i18n/src/global/globals.ts index d926933f6e..7e1540625c 100644 --- a/packages/neos-ui-i18n/src/global/globals.ts +++ b/packages/neos-ui-i18n/src/global/globals.ts @@ -18,7 +18,7 @@ export const globals = { export function requireGlobals(): NonNullable<(typeof globals)['current']> { if (globals.current === null) { - throw GlobalsRuntimeContraintViolation + throw GlobalsRuntimeConstraintViolation .becauseGlobalsWereRequiredButHaveNotBeenSetYet(); } @@ -31,7 +31,7 @@ export function setGlobals(value: NonNullable<(typeof globals)['current']>) { return; } - throw GlobalsRuntimeContraintViolation + throw GlobalsRuntimeConstraintViolation .becauseGlobalsWereAttemptedToBeSetMoreThanOnce(); } @@ -39,20 +39,20 @@ export function unsetGlobals() { globals.current = null; } -export class GlobalsRuntimeContraintViolation extends Error { +export class GlobalsRuntimeConstraintViolation extends Error { private constructor(message: string) { super(message); } public static becauseGlobalsWereRequiredButHaveNotBeenSetYet = () => - new GlobalsRuntimeContraintViolation( + new GlobalsRuntimeConstraintViolation( 'Globals for "@neos-project/neos-ui-i18n" are not available,' + ' because they have not been initialized yet. Make sure to run' + ' `loadI18n` or `setupI18n` (for testing).' ); public static becauseGlobalsWereAttemptedToBeSetMoreThanOnce = () => - new GlobalsRuntimeContraintViolation( + new GlobalsRuntimeConstraintViolation( 'Globals for "@neos-project/neos-ui-i18n" have already been set. ' + ' Make sure to only run one of `loadI18n` or `setupI18n` (for' + ' testing). Neither function must ever be called more than' diff --git a/packages/neos-ui-i18n/src/registry/I18nRegistry.ts b/packages/neos-ui-i18n/src/registry/I18nRegistry.ts index b97b6ba2fc..cc102b1c14 100644 --- a/packages/neos-ui-i18n/src/registry/I18nRegistry.ts +++ b/packages/neos-ui-i18n/src/registry/I18nRegistry.ts @@ -20,11 +20,11 @@ import type {LegacyParameters} from './LegacyParameters'; const errorCache: Record = {}; /** - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead */ export class I18nRegistry extends SynchronousRegistry { /** - * Retrieves a the translation string that is identified by the given + * Retrieves the translation string that is identified by the given * identifier. If it is a fully qualified translation address (a string * following the pattern "{Package.Key:SourceName:actual.trans.unit.id}"), * then the translation will be looked up in the respective package and @@ -34,13 +34,13 @@ export class I18nRegistry extends SynchronousRegistry { * If no translation string can be found for the given id, the fully * qualified translation address will be returned. * - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead * @param {string} transUnitIdOrFullyQualifiedTranslationAddress A trans-unit id or a fully qualified translation address */ translate(transUnitIdOrFullyQualifiedTranslationAddress: string): string; /** - * Retrieves a the translation string that is identified by the given + * Retrieves the translation string that is identified by the given * identifier. If it is a fully qualified translation address (a string * following the pattern "{Package.Key:SourceName:actual.trans.unit.id}"), * then the translation will be looked up in the respective package and @@ -50,14 +50,14 @@ export class I18nRegistry extends SynchronousRegistry { * If no translation string can be found for the given id, the given * fallback value will be returned. * - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead * @param {string} transUnitIdOrFullyQualifiedTranslationAddress A trans-unit id or a fully qualified translation address * @param {string} fallback The string that shall be displayed, when no translation string could be found. */ translate(transUnitIdOrFullyQualifiedTranslationAddress: string, fallback: string): string; /** - * Retrieves a the translation string that is identified by the given + * Retrieves the translation string that is identified by the given * identifier. If it is a fully qualified translation address (a string * following the pattern "{Package.Key:SourceName:actual.trans.unit.id}"), * then the translation will be looked up in the respective package and @@ -68,11 +68,11 @@ export class I18nRegistry extends SynchronousRegistry { * fallback value will be returned. If no fallback value has been given, * the fully qualified translation address will be returned. * - * If a translation string was found and it contains substition placeholders + * If a translation string was found, and it contains substitution placeholders * (e.g.: "{0}", or "{somePlaceholder}"), the placeholders will be replaced * with the corresponding values that were passed as parameters. * - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead * @param {string} transUnitIdOrFullyQualifiedTranslationAddress The fully qualified translation address, that follows the format "{Package.Key:SourceName:trans.unit.id}" * @param {undefined|string} fallback The string that shall be displayed, when no translation string could be found. * @param {LegacyParameters} parameters The values to replace substitution placeholders with in the translation string @@ -84,7 +84,7 @@ export class I18nRegistry extends SynchronousRegistry { ): string; /** - * Retrieves a the translation string that is identified by the given + * Retrieves the translation string that is identified by the given * trans-unit id. The translation file will be looked up inside the package * identified by the given package key. The file itself will be the Main.xlf * in that package's resource translations. @@ -93,11 +93,11 @@ export class I18nRegistry extends SynchronousRegistry { * value will be returned. If no fallback value has been given, the fully * qualified translation address will be returned. * - * If a translation string was found and it contains substition placeholders + * If a translation string was found, and it contains substitution placeholders * (e.g.: "{0}", or "{somePlaceholder}"), the placeholders will be replaced * with the corresponding values that were passed as parameters. * - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead * @param {string} transUnitId The trans-unit id * @param {undefined|string} fallback The string that shall be displayed, when no translation string could be found. * @param {LegacyParameters} parameters The values to replace substitution placeholders with in the translation string @@ -111,7 +111,7 @@ export class I18nRegistry extends SynchronousRegistry { ): string; /** - * Retrieves a the translation string that is identified by the given + * Retrieves the translation string that is identified by the given * trans-unit id. The translation file will be looked up inside the package * identified by the given package key. The file itself will be the *.xlf file * in that package's resource translations that is identified by the given @@ -121,11 +121,11 @@ export class I18nRegistry extends SynchronousRegistry { * value will be returned. If no fallback value has been given, the fully * qualified translation address will be returned. * - * If a translation string was found and it contains substition placeholders + * If a translation string was found, and it contains substitution placeholders * (e.g.: "{0}", or "{somePlaceholder}"), the placeholders will be replaced * with the corresponding values that were passed as parameters. * - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead * @param {string} transUnitId The trans-unit id * @param {undefined|string} fallback The string that shall be displayed, when no translation string could be found. * @param {LegacyParameters} parameters The values to replace substitution placeholders with in the translation string @@ -141,7 +141,7 @@ export class I18nRegistry extends SynchronousRegistry { ): string; /** - * Retrieves a the translation string that is identified by the given + * Retrieves the translation string that is identified by the given * trans-unit id. The translation file will be looked up inside the package * identified by the given package key. The file itself will be the *.xlf file * in that package's resource translations that is identified by the given @@ -155,11 +155,11 @@ export class I18nRegistry extends SynchronousRegistry { * plural form, then the plural form will be used. If the quantity equals 1 * or is smaller than 1, the singular form will be used. * - * If a translation string was found and it contains substition placeholders + * If a translation string was found, and it contains substitution placeholders * (e.g.: "{0}", or "{somePlaceholder}"), the placeholders will be replaced * with the corresponding values that were passed as parameters. * - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead * @param {string} transUnitId The trans-unit id * @param {undefined|string} fallback The string that shall be displayed, when no translation string could be found. * @param {LegacyParameters} parameters The values to replace substitution placeholders with in the translation string @@ -184,10 +184,10 @@ export class I18nRegistry extends SynchronousRegistry { quantity: number = 0 ) { const fallback = explicitlyProvidedFallback || transUnitIdOrFullyQualifiedTranslationAddress; - const translationAddess = getTranslationAddress(transUnitIdOrFullyQualifiedTranslationAddress, explicitlyProvidedPackageKey, explicitlyProvidedSourceName); - const translation = this.getTranslation(translationAddess); + const translationAddress = getTranslationAddress(transUnitIdOrFullyQualifiedTranslationAddress, explicitlyProvidedPackageKey, explicitlyProvidedSourceName); + const translation = this.getTranslation(translationAddress); if (translation === null) { - this.logTranslationNotFound(translationAddess, fallback); + this.logTranslationNotFound(translationAddress, fallback); return fallback; } @@ -209,6 +209,6 @@ export class I18nRegistry extends SynchronousRegistry { } /** - * @deprecated Use `import {tranlsate} from '@neos-project/neos-ui-i18n'` instead + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead */ export const i18nRegistry = new I18nRegistry('The i18n registry'); diff --git a/packages/neos-ui-i18n/src/translate.ts b/packages/neos-ui-i18n/src/translate.ts index 82c1a7dc82..4db4870abd 100644 --- a/packages/neos-ui-i18n/src/translate.ts +++ b/packages/neos-ui-i18n/src/translate.ts @@ -12,7 +12,7 @@ import {TranslationAddress, type Parameters} from './model'; import {substitutePlaceholders} from './registry'; /** - * Retrieves a the translation string that is identified by the given fully + * Retrieves the translation string that is identified by the given fully * qualified translation address (a string following the pattern * "{Package.Key:SourceName:actual.trans.unit.id}"), then the translation will * be looked up in the respective package and *.xlf file. @@ -20,7 +20,7 @@ import {substitutePlaceholders} from './registry'; * If no translation string can be found for the given address, the given * fallback value will be returned. * - * If a translation string was found and it contains substition placeholders + * If a translation string was found, and it contains substitution placeholders * (e.g.: "{0}", or "{somePlaceholder}"), the placeholders will be replaced * with the corresponding values that were passed as parameters. * From 5520eb488f9267e08c87b1244d88bc456d4a1c10 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 22 Jan 2025 17:10:08 +0100 Subject: [PATCH 3/3] BUGFIX: Reintroduce graceful handling of empty parameter in `translate()` the use of ``` i18nRegistry.translate(ui?.help?.message) ``` in https://github.com/neos/neos-ui/blob/a60961417e7ad49a22b75d48dbe4b2542a25ae5b/packages/neos-ui/src/Containers/Modals/SelectNodeType/nodeTypeItem.js#L60 brings the Ui currently to a fatal crash: > Message: undefined is not an object (evaluating 'string5.split') This change reintroduces the old behaviour by returning a possible fallback instead or `undefined`. --- .../neos-ui-i18n/src/registry/I18nRegistry.spec.ts | 8 ++++++++ packages/neos-ui-i18n/src/registry/I18nRegistry.ts | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/neos-ui-i18n/src/registry/I18nRegistry.spec.ts b/packages/neos-ui-i18n/src/registry/I18nRegistry.spec.ts index 0c926dd8bb..d850ed487e 100644 --- a/packages/neos-ui-i18n/src/registry/I18nRegistry.spec.ts +++ b/packages/neos-ui-i18n/src/registry/I18nRegistry.spec.ts @@ -111,3 +111,11 @@ test(` expect(actual).toBe('Singular Translation'); }); + +test(` + Host > Containers > I18n: Returns undefined if no id is specified`, () => { + const registry = new I18nRegistry(''); + const actual = registry.translate(undefined); + + expect(actual).toBe(undefined); +}); diff --git a/packages/neos-ui-i18n/src/registry/I18nRegistry.ts b/packages/neos-ui-i18n/src/registry/I18nRegistry.ts index cc102b1c14..57eae6e8c4 100644 --- a/packages/neos-ui-i18n/src/registry/I18nRegistry.ts +++ b/packages/neos-ui-i18n/src/registry/I18nRegistry.ts @@ -23,6 +23,11 @@ const errorCache: Record = {}; * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead */ export class I18nRegistry extends SynchronousRegistry { + /** + * @deprecated Use `import {translate} from '@neos-project/neos-ui-i18n'` instead + */ + translate(emptyAddress: undefined): undefined; + /** * Retrieves the translation string that is identified by the given * identifier. If it is a fully qualified translation address (a string @@ -176,13 +181,17 @@ export class I18nRegistry extends SynchronousRegistry { ): string; translate( - transUnitIdOrFullyQualifiedTranslationAddress: string, + transUnitIdOrFullyQualifiedTranslationAddress?: string, explicitlyProvidedFallback?: string, parameters?: LegacyParameters, explicitlyProvidedPackageKey: string = 'Neos.Neos', explicitlyProvidedSourceName: string = 'Main', quantity: number = 0 ) { + if (typeof transUnitIdOrFullyQualifiedTranslationAddress !== 'string' || transUnitIdOrFullyQualifiedTranslationAddress === '') { + // legacy behaviour to guard against undefined + return explicitlyProvidedFallback; + } const fallback = explicitlyProvidedFallback || transUnitIdOrFullyQualifiedTranslationAddress; const translationAddress = getTranslationAddress(transUnitIdOrFullyQualifiedTranslationAddress, explicitlyProvidedPackageKey, explicitlyProvidedSourceName); const translation = this.getTranslation(translationAddress);