{"record":{"id":"c105a57c61866c3e","repo":"angular/angular-cli","slug":"error-c105a5","errorCode":null,"errorMessage":"*","messagePattern":"\\*","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/build_angular/src/builders/extract-i18n/builder.ts","lineNumber":132,"sourceCode":"      return path.relative(from, to);\n    },\n  };\n  const duplicateTranslationBehavior = normalizedOptions.i18nOptions.duplicateTranslationBehavior;\n  const diagnostics = checkDuplicateMessages(\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    checkFileSystem as any,\n    extractionResult.messages,\n    duplicateTranslationBehavior,\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    extractionResult.basePath as any,\n  );\n  if (diagnostics.messages.length > 0 && duplicateTranslationBehavior !== 'ignore') {\n    if (duplicateTranslationBehavior === 'error') {\n      context.logger.error(`Extraction Failed: ${diagnostics.formatDiagnostics('')}`);\n\n      return { success: false };\n    } else {\n      context.logger.warn(diagnostics.formatDiagnostics(''));\n    }\n  }\n\n  // Serialize all extracted messages\n  const serializer = await createSerializer(\n    localizeToolsModule,\n    normalizedOptions.format,\n    normalizedOptions.i18nOptions.sourceLocale,\n    extractionResult.basePath,\n    extractionResult.useLegacyIds,\n    diagnostics,\n  );\n\n  assert(serializer);\n\n  const content = serializer.serialize(extractionResult.messages);\n\n  // Ensure directory exists","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/build_angular/src/builders/extract-i18n/builder.ts#L114-L150","documentation":"During i18n extraction (extract-i18n builder), the localization diagnostics collector found duplicate translation message IDs. If duplicateTranslationBehavior is 'error', the builder logs 'Extraction Failed' with the formatted diagnostics and returns success:false; if 'warning' (default), it only logs the diagnostics as a warning.","triggerScenarios":"Running `ng extract-i18n` when two or more source messages produce the same i18n ID (explicit id attributes or auto-generated ids colliding) and duplicateTranslationBehavior is not 'ignore'.","commonSituations":"Two templates share an explicit i18n id with different source text; copy-pasted i18n attributes; auto-generated ids colliding after refactoring templates.","solutions":["Fix the duplicate i18n ids reported in the diagnostics output so each id maps to one source text.","Set duplicateTranslationBehavior to 'warning' in the extract-i18n options to only warn instead of failing.","Set duplicateTranslationBehavior to 'ignore' to suppress duplicates entirely (not recommended for translation integrity)."],"exampleFix":"// angular.json (extract-i18n options)\n// before\n\"duplicateTranslationBehavior\": \"error\"\n// after: fix ids in templates, e.g. give each a unique explicit id\n// <span i18n=\"@@greeting\">Hello</span>  vs  <span i18n=\"@@farewell\">Goodbye</span>","handlingStrategy":"validation","validationCode":"// Deduplicate explicit i18n ids before extraction:\nconst ids = [...text.matchAll(/i18n=\"@@([^\"]+)\"/g)].map(m => m[1]);\nconst dupes = ids.filter((id, i) => ids.indexOf(id) !== i);\nif (dupes.length) throw new Error(`Duplicate i18n ids: ${[...new Set(dupes)].join(', ')}`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Enforce unique explicit i18n ids via a lint rule (e.g., custom ESLint check or grep in CI).","Set duplicateTranslationBehavior to 'error' in CI so duplicates fail builds early.","Review i18n attributes when copy-pasting templates."],"tags":["i18n","angular","duplicate-ids"],"backgroundTag":"duplicate-translation-id","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}