diff --git a/goldens/public-api/angular_devkit/build_angular/src/index.md b/goldens/public-api/angular_devkit/build_angular/src/index.md index 1565e61b9b6a..30a86d584b93 100644 --- a/goldens/public-api/angular_devkit/build_angular/src/index.md +++ b/goldens/public-api/angular_devkit/build_angular/src/index.md @@ -43,7 +43,8 @@ export interface BrowserBuilderOptions { deployUrl?: string; extractLicenses?: boolean; fileReplacements?: FileReplacement[]; - i18nMissingTranslation?: I18NMissingTranslation; + i18nDuplicateTranslation?: I18NTranslation; + i18nMissingTranslation?: I18NTranslation; index: IndexUnion; inlineStyleLanguage?: InlineStyleLanguage; localize?: Localize; @@ -259,7 +260,8 @@ export interface ServerBuilderOptions { externalDependencies?: string[]; extractLicenses?: boolean; fileReplacements?: FileReplacement_3[]; - i18nMissingTranslation?: I18NMissingTranslation_2; + i18nDuplicateTranslation?: I18NTranslation_2; + i18nMissingTranslation?: I18NTranslation_2; inlineStyleLanguage?: InlineStyleLanguage_3; localize?: Localize_2; main: string; diff --git a/packages/angular_devkit/build_angular/src/builders/browser/schema.json b/packages/angular_devkit/build_angular/src/builders/browser/schema.json index 54bbdd52bade..4e15f47faa9f 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser/schema.json +++ b/packages/angular_devkit/build_angular/src/builders/browser/schema.json @@ -220,6 +220,12 @@ "enum": ["warning", "error", "ignore"], "default": "warning" }, + "i18nDuplicateTranslation": { + "type": "string", + "description": "How to handle duplicate translations for i18n.", + "enum": ["warning", "error", "ignore"], + "default": "warning" + }, "localize": { "description": "Translate the bundles in one or more locales.", "oneOf": [ diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/index.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/index.ts index 208c095bac90..bacccc93dc61 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/index.ts @@ -366,14 +366,22 @@ async function setupLocalize( compiler.hooks.thisCompilation.tap('build-angular', (compilation) => { if (i18n.shouldInline && i18nLoaderOptions.translation === undefined) { // Reload translations - loadTranslations(locale, localeDescription, context.workspaceRoot, loader, { - warn(message) { - addWarning(compilation, message); - }, - error(message) { - addError(compilation, message); + loadTranslations( + locale, + localeDescription, + context.workspaceRoot, + loader, + { + warn(message) { + addWarning(compilation, message); + }, + error(message) { + addError(compilation, message); + }, }, - }); + undefined, + browserOptions.i18nDuplicateTranslation, + ); i18nLoaderOptions.translation = localeDescription.translation; } diff --git a/packages/angular_devkit/build_angular/src/builders/server/schema.json b/packages/angular_devkit/build_angular/src/builders/server/schema.json index 0de18853499c..d4c68ecf16df 100644 --- a/packages/angular_devkit/build_angular/src/builders/server/schema.json +++ b/packages/angular_devkit/build_angular/src/builders/server/schema.json @@ -133,6 +133,12 @@ "enum": ["warning", "error", "ignore"], "default": "warning" }, + "i18nDuplicateTranslation": { + "type": "string", + "description": "How to handle duplicate translations for i18n.", + "enum": ["warning", "error", "ignore"], + "default": "warning" + }, "localize": { "description": "Translate the bundles in one or more locales.", "oneOf": [ diff --git a/packages/angular_devkit/build_angular/src/utils/build-options.ts b/packages/angular_devkit/build_angular/src/utils/build-options.ts index 68ef798bdf67..70715e8c2b7d 100644 --- a/packages/angular_devkit/build_angular/src/utils/build-options.ts +++ b/packages/angular_devkit/build_angular/src/utils/build-options.ts @@ -13,7 +13,7 @@ import { Budget, CrossOrigin, ExtraEntryPoint, - I18NMissingTranslation, + I18NTranslation, IndexUnion, InlineStyleLanguage, Localize, @@ -38,7 +38,7 @@ export interface BuildOptions { verbose?: boolean; progress?: boolean; localize?: Localize; - i18nMissingTranslation?: I18NMissingTranslation; + i18nMissingTranslation?: I18NTranslation; bundleDependencies?: boolean; externalDependencies?: string[]; watch?: boolean; diff --git a/packages/angular_devkit/build_angular/src/utils/i18n-options.ts b/packages/angular_devkit/build_angular/src/utils/i18n-options.ts index da6652062b25..0b249cb850d6 100644 --- a/packages/angular_devkit/build_angular/src/utils/i18n-options.ts +++ b/packages/angular_devkit/build_angular/src/utils/i18n-options.ts @@ -12,7 +12,7 @@ import fs from 'fs'; import module from 'module'; import os from 'os'; import path from 'path'; -import { Schema as BrowserBuilderSchema } from '../builders/browser/schema'; +import { Schema as BrowserBuilderSchema, I18NTranslation } from '../builders/browser/schema'; import { Schema as ServerBuilderSchema } from '../builders/server/schema'; import { readTsconfig } from '../utils/read-tsconfig'; import { TranslationLoader, createTranslationLoader } from './load-translations'; @@ -233,6 +233,7 @@ export async function configureI18nBuild 1 && tsConfig.options.enableI18nLegacyMessageIdFormat !== false) { @@ -282,6 +283,7 @@ export function loadTranslations( loader: TranslationLoader, logger: { warn: (message: string) => void; error: (message: string) => void }, usedFormats?: Set, + duplicateTranslation?: I18NTranslation, ) { for (const file of desc.files) { const loadResult = loader(path.join(workspaceRoot, file.path)); @@ -308,9 +310,18 @@ export function loadTranslations( // Merge translations for (const [id, message] of Object.entries(loadResult.translations)) { if (desc.translation[id] !== undefined) { - logger.warn( - `WARNING [${file.path}]: Duplicate translations for message '${id}' when merging`, - ); + const duplicateTranslationMessage = `[${file.path}]: Duplicate translations for message '${id}' when merging.`; + switch (duplicateTranslation) { + case I18NTranslation.Ignore: + break; + case I18NTranslation.Error: + logger.error(`ERROR ${duplicateTranslationMessage}`); + break; + case I18NTranslation.Warning: + default: + logger.warn(`WARNING ${duplicateTranslationMessage}`); + break; + } } desc.translation[id] = message; } diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-merging.ts b/tests/legacy-cli/e2e/tests/i18n/ivy-localize-merging.ts index 622d12d5bff8..a336f76c422b 100644 --- a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-merging.ts +++ b/tests/legacy-cli/e2e/tests/i18n/ivy-localize-merging.ts @@ -8,22 +8,20 @@ import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; +import { expectToFail } from '../../utils/utils'; import { setupI18nConfig } from './setup'; -export default async function() { +export default async function () { // Setup i18n tests and config. await setupI18nConfig(); // Update angular.json - await updateJsonFile('angular.json', workspaceJson => { + await updateJsonFile('angular.json', (workspaceJson) => { const appProject = workspaceJson.projects['test-project']; // tslint:disable-next-line: no-any const i18n: Record = appProject.i18n; - i18n.locales['fr'] = [ - i18n.locales['fr'], - i18n.locales['fr'], - ] + i18n.locales['fr'] = [i18n.locales['fr'], i18n.locales['fr']]; appProject.architect['build'].options.localize = ['fr']; }); @@ -32,5 +30,18 @@ export default async function() { throw new Error('duplicate translations warning not shown'); } + await updateJsonFile('angular.json', (workspaceJson) => { + const appProject = workspaceJson.projects['test-project']; + appProject.architect['build'].options.i18nDuplicateTranslation = 'error'; + }); + await expectToFail(() => ng('build')); + await updateJsonFile('angular.json', (workspaceJson) => { + const appProject = workspaceJson.projects['test-project']; + appProject.architect['build'].options.i18nDuplicateTranslation = 'ignore'; + }); + const { stderr: err2 } = await ng('build'); + if (err2.includes('Duplicate translations for message')) { + throw new Error('duplicate translations message not ignore'); + } }