diff --git a/apps/showcase/src/components/showcase/localization/localization-pres.component.ts b/apps/showcase/src/components/showcase/localization/localization-pres.component.ts index ae2d72315e..96376a4583 100644 --- a/apps/showcase/src/components/showcase/localization/localization-pres.component.ts +++ b/apps/showcase/src/components/showcase/localization/localization-pres.component.ts @@ -31,6 +31,8 @@ export class LocalizationPresComponent implements Translatable { + const resolvedFilePath = path.resolve(path.dirname(this.filePath), file); + const data = JSON.parse(fs.readFileSync(resolvedFilePath, 'utf-8')); + return acc.concat(Object.keys(data)); + }, []); + + return name && type ? { + name, configName, contextName, isDynamicConfig: isDynamic, type, selector, templateUrl, linkableToRuleset, + ...(localizationKeys.length ? { localizationKeys } : {}) + } : undefined; } /** diff --git a/packages/@o3r/components/builders/component-extractor/helpers/component/component.extractor.ts b/packages/@o3r/components/builders/component-extractor/helpers/component/component.extractor.ts index 14baa26f2d..5bb753ff02 100644 --- a/packages/@o3r/components/builders/component-extractor/helpers/component/component.extractor.ts +++ b/packages/@o3r/components/builders/component-extractor/helpers/component/component.extractor.ts @@ -233,7 +233,8 @@ export class ComponentExtractor { type: parsedItemRef.component.type, context, config, - linkableToRuleset: parsedItemRef.component.linkableToRuleset + linkableToRuleset: parsedItemRef.component.linkableToRuleset, + localizationKeys: parsedItemRef.component.localizationKeys }; }); diff --git a/packages/@o3r/components/schemas/component.metadata.schema.json b/packages/@o3r/components/schemas/component.metadata.schema.json index ade57d53d0..d809d8a09d 100644 --- a/packages/@o3r/components/schemas/component.metadata.schema.json +++ b/packages/@o3r/components/schemas/component.metadata.schema.json @@ -125,6 +125,12 @@ "linkableToRuleset": { "type": "boolean", "description": "Determine if the component is activating a ruleset" + }, + "localizationKeys": { + "type": "array", + "items": { + "type": "string" + } } } } diff --git a/packages/@o3r/components/src/core/component.output.ts b/packages/@o3r/components/src/core/component.output.ts index a5a0f59c15..603c43a10d 100644 --- a/packages/@o3r/components/src/core/component.output.ts +++ b/packages/@o3r/components/src/core/component.output.ts @@ -77,6 +77,8 @@ export interface ComponentClassOutput extends Output { placeholders?: PlaceholderData[]; /** Determine if the component is activating a ruleset */ linkableToRuleset: boolean; + /** List of localization keys used in the component */ + localizationKeys?: string[]; } /** diff --git a/packages/@o3r/extractors/src/utils/index.ts b/packages/@o3r/extractors/src/utils/index.ts index b3d4284d43..88a2dbec72 100644 --- a/packages/@o3r/extractors/src/utils/index.ts +++ b/packages/@o3r/extractors/src/utils/index.ts @@ -1,5 +1,6 @@ export * from './common'; export * from './config-doc'; +export * from './localization'; export * from './parser-factory'; export * from './tsdoc'; export * from './validator'; diff --git a/packages/@o3r/extractors/src/utils/localization.ts b/packages/@o3r/extractors/src/utils/localization.ts new file mode 100644 index 0000000000..01c33655e3 --- /dev/null +++ b/packages/@o3r/extractors/src/utils/localization.ts @@ -0,0 +1,38 @@ +import * as ts from 'typescript'; + +const localizationDecoratorName = 'Localization'; + +/** + * Retrieve the localization json files from TS Code + * + * @param node TSNode of the angular component class + * @param source Ts file source + */ +export function getLocalizationFileFromAngularElement(node: ts.ClassDeclaration): string[] | undefined { + const localizationPaths: string[] = []; + node.forEachChild((item) => { + if (!ts.isPropertyDeclaration(item)) { + return; + } + + item.forEachChild((decorator) => { + if ( + !ts.isDecorator(decorator) + || !ts.isCallExpression(decorator.expression) + || !ts.isIdentifier(decorator.expression.expression) + || decorator.expression.expression.escapedText !== localizationDecoratorName + ) { + return; + } + + const firstArg = decorator.expression.arguments[0]; + if (!firstArg || !ts.isStringLiteral(firstArg)) { + return; + } + + localizationPaths.push(firstArg.text); + }); + }); + + return localizationPaths.length ? localizationPaths : undefined; +} diff --git a/packages/@o3r/localization/builders/helpers/localization.generator.ts b/packages/@o3r/localization/builders/helpers/localization.generator.ts index 7afa74d22d..5b68f02f86 100644 --- a/packages/@o3r/localization/builders/helpers/localization.generator.ts +++ b/packages/@o3r/localization/builders/helpers/localization.generator.ts @@ -1,5 +1,5 @@ import { logging } from '@angular-devkit/core'; -import { getLibraryCmsMetadata } from '@o3r/extractors'; +import { getLibraryCmsMetadata, getLocalizationFileFromAngularElement } from '@o3r/extractors'; import type { JSONLocalization, LocalizationMetadata } from '@o3r/localization'; import { O3rCliError } from '@o3r/schematics'; import * as fs from 'node:fs'; @@ -103,37 +103,6 @@ export class LocalizationExtractor { return angularItems.length ? angularItems : undefined; } - /** - * Retrieve the localization json file from TS Code - * - * @param node TSNode of the angular component class - * @param source Ts file source - */ - private getLocalizationFileFromAngularElement(node: ts.ClassDeclaration, source: ts.SourceFile): string[] | undefined { - const localizationPaths: string[] = []; - node.forEachChild((item) => { - if (!ts.isPropertyDeclaration(item)) { - return; - } - - item.forEachChild((decorator) => { - if (!ts.isDecorator(decorator)) { - return; - } - - const text = decorator.getText(source); - const result = /^@Localization *\( *['"](.*)['"] *\)/.exec(text); - if (!result || !result[1]) { - return; - } - - localizationPaths.push(result[1]); - }); - }); - - return localizationPaths.length ? localizationPaths : undefined; - } - /** * Get the list of referenced translation files * @@ -300,9 +269,9 @@ export class LocalizationExtractor { const localizationFiles = tsFiles .map((file) => ({file, source: program.getSourceFile(file)})) .map(({ file, source }) => ({ file, classes: source && this.getAngularClassNode(source), source})) - .filter(({ classes, source }) => !!classes && !!source) - .map(({file, classes, source}) => classes! - .map((classItem) => this.getLocalizationFileFromAngularElement(classItem, source!)) + .filter(({ classes }) => !!classes) + .map(({file, classes}) => classes! + .map((classItem) => getLocalizationFileFromAngularElement(classItem)) .filter((locFiles): locFiles is string[] => !!locFiles) .reduce((acc: string[], locFiles) => { acc.push(...locFiles.filter((f) => acc.indexOf(f) === -1));