Skip to content

Commit 8b23e64

Browse files
alan-agius4dgp1130
authored andcommitted
fix(@schematics/angular): migrate project to use new i18n options
1 parent c5732f8 commit 8b23e64

File tree

2 files changed

+152
-15
lines changed

2 files changed

+152
-15
lines changed

packages/schematics/angular/migrations/update-9/update-workspace-config.ts

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import { JsonAstObject } from '@angular-devkit/core';
99
import { Rule, Tree, UpdateRecorder } from '@angular-devkit/schematics';
1010
import { getWorkspacePath } from '../../utility/config';
11+
import { NodeDependencyType, addPackageJsonDependency, getPackageJsonDependency } from '../../utility/dependencies';
1112
import {
1213
appendValueInAstArray,
1314
findPropertyInAstObject,
1415
insertPropertyInAstObjectInOrder,
1516
removePropertyInAstObject,
1617
} from '../../utility/json-utils';
18+
import { latestVersions } from '../../utility/latest-versions';
1719
import { Builders } from '../../utility/workspace-models';
1820
import { getAllOptions, getProjectTarget, getTargets, getWorkspace, isIvyEnabled } from './utils';
1921

@@ -28,29 +30,41 @@ export function updateWorkspaceConfig(): Rule {
2830
const workspace = getWorkspace(tree);
2931
const recorder = tree.beginUpdate(workspacePath);
3032

31-
for (const { target } of getTargets(workspace, 'build', Builders.Browser)) {
33+
for (const { target, project } of getTargets(workspace, 'build', Builders.Browser)) {
3234
updateStyleOrScriptOption('styles', recorder, target);
3335
updateStyleOrScriptOption('scripts', recorder, target);
3436
addAnyComponentStyleBudget(recorder, target);
3537
updateAotOption(tree, recorder, target);
38+
addBuilderI18NOptions(recorder, target, project);
3639
}
3740

38-
for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
41+
for (const { target, project } of getTargets(workspace, 'test', Builders.Karma)) {
3942
updateStyleOrScriptOption('styles', recorder, target);
4043
updateStyleOrScriptOption('scripts', recorder, target);
44+
addBuilderI18NOptions(recorder, target, project);
4145
}
4246

4347
for (const { target } of getTargets(workspace, 'server', Builders.Server)) {
4448
updateOptimizationOption(recorder, target);
4549
}
4650

51+
for (const { target, project } of getTargets(workspace, 'extract-i18n', Builders.ExtractI18n)) {
52+
addProjectI18NOptions(recorder, tree, target, project);
53+
removeExtracti18nDeprecatedOptions(recorder, target);
54+
}
55+
4756
tree.commitUpdate(recorder);
4857

4958
return tree;
5059
};
5160
}
5261

53-
function addProjectI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject, projectConfig: JsonAstObject) {
62+
function addProjectI18NOptions(
63+
recorder: UpdateRecorder,
64+
tree: Tree,
65+
builderConfig: JsonAstObject,
66+
projectConfig: JsonAstObject,
67+
) {
5468
const browserConfig = getProjectTarget(projectConfig, 'build', Builders.Browser);
5569
if (!browserConfig || browserConfig.kind !== 'object') {
5670
return;
@@ -86,33 +100,101 @@ function addProjectI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstO
86100
// Get sourceLocale from extract-i18n builder
87101
const i18nOptions = getAllOptions(builderConfig);
88102
const sourceLocale = i18nOptions
89-
.map(o => {
90-
const sourceLocale = findPropertyInAstObject(o, 'i18nLocale');
103+
.map(o => {
104+
const sourceLocale = findPropertyInAstObject(o, 'i18nLocale');
91105

92-
return sourceLocale && sourceLocale.value;
93-
})
94-
.find(x => !!x);
106+
return sourceLocale && sourceLocale.value;
107+
})
108+
.find(x => !!x);
95109

96110
// Add i18n project configuration
97111
insertPropertyInAstObjectInOrder(recorder, projectConfig, 'i18n', {
98112
locales,
99113
// tslint:disable-next-line: no-any
100114
sourceLocale: sourceLocale as any,
101115
}, 6);
116+
117+
// Add @angular/localize if not already a dependency
118+
if (!getPackageJsonDependency(tree, '@angular/localize')) {
119+
addPackageJsonDependency(tree, {
120+
name: '@angular/localize',
121+
version: latestVersions.Angular,
122+
type: NodeDependencyType.Default,
123+
});
124+
}
102125
}
103126
}
104127

105-
function addBuilderI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject) {
128+
function addBuilderI18NOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject, projectConfig: JsonAstObject) {
106129
const options = getAllOptions(builderConfig);
107130

131+
let hasi18n = false;
108132
for (const option of options) {
109133
const localeId = findPropertyInAstObject(option, 'i18nLocale');
110-
if (!localeId || localeId.kind !== 'string') {
111-
continue;
134+
if (localeId && localeId.kind === 'string') {
135+
// add new localize option
136+
insertPropertyInAstObjectInOrder(recorder, option, 'localize', [localeId.value], 12);
137+
removePropertyInAstObject(recorder, option, 'i18nLocale');
138+
}
139+
140+
const i18nFile = findPropertyInAstObject(option, 'i18nFile');
141+
if (i18nFile) {
142+
removePropertyInAstObject(recorder, option, 'i18nFile');
143+
}
144+
145+
const i18nFormat = findPropertyInAstObject(option, 'i18nFormat');
146+
if (i18nFormat) {
147+
removePropertyInAstObject(recorder, option, 'i18nFormat');
112148
}
113149

114-
// add new localize option
115-
insertPropertyInAstObjectInOrder(recorder, option, 'localize', [localeId.value], 12);
150+
hasi18n = !!(hasi18n || i18nFormat || i18nFile || localeId);
151+
}
152+
153+
if (hasi18n) {
154+
const options = findPropertyInAstObject(builderConfig, 'options');
155+
if (!options || options.kind !== 'object') {
156+
return;
157+
}
158+
159+
// Don't add localize option of it's already present in the main options
160+
if (findPropertyInAstObject(options, 'i18nLocale') || findPropertyInAstObject(options, 'localize')) {
161+
return;
162+
}
163+
164+
// Get sourceLocale from extract-i18n builder
165+
const extractI18nConfig = getProjectTarget(projectConfig, 'extract-i18n', Builders.ExtractI18n);
166+
let sourceLocale: string | undefined;
167+
168+
if (extractI18nConfig && extractI18nConfig.kind === 'object') {
169+
const i18nOptions = getAllOptions(extractI18nConfig);
170+
sourceLocale = i18nOptions
171+
.map(o => {
172+
const sourceLocale = findPropertyInAstObject(o, 'i18nLocale');
173+
174+
return sourceLocale && sourceLocale.value;
175+
})
176+
.find(x => !!x) as string;
177+
}
178+
179+
insertPropertyInAstObjectInOrder(recorder, options, 'localize', [sourceLocale || 'en-US'], 12);
180+
}
181+
}
182+
183+
function removeExtracti18nDeprecatedOptions(recorder: UpdateRecorder, builderConfig: JsonAstObject) {
184+
const options = getAllOptions(builderConfig);
185+
186+
for (const option of options) {
187+
// deprecated options
188+
removePropertyInAstObject(recorder, option, 'i18nLocale');
189+
const i18nFormat = option.properties.find(({ key }) => key.value === 'i18nFormat');
190+
191+
if (i18nFormat) {
192+
// i18nFormat has been changed to format
193+
const key = i18nFormat.key;
194+
const offset = key.start.offset + 1;
195+
recorder.remove(offset, key.value.length);
196+
recorder.insertLeft(offset, 'format');
197+
}
116198
}
117199
}
118200

@@ -122,7 +204,6 @@ function updateAotOption(tree: Tree, recorder: UpdateRecorder, builderConfig: Js
122204
return;
123205
}
124206

125-
126207
const tsConfig = findPropertyInAstObject(options, 'tsConfig');
127208
// Do not add aot option if the users already opted out from Ivy.
128209
if (tsConfig && tsConfig.kind === 'string' && !isIvyEnabled(tree, tsConfig.value)) {

packages/schematics/angular/migrations/update-9/update-workspace-config_spec.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import { EmptyTree } from '@angular-devkit/schematics';
1010
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
11+
import { latestVersions } from '../../utility/latest-versions';
1112
import { WorkspaceTargets } from '../../utility/workspace-models';
1213
import { ANY_COMPONENT_STYLE_BUDGET } from './update-workspace-config';
1314

@@ -297,7 +298,7 @@ describe('Migration to version 9', () => {
297298
});
298299
});
299300

300-
xdescribe('i18n configuration', () => {
301+
describe('i18n configuration', () => {
301302
function getI18NConfig(localId: string): object {
302303
return {
303304
outputPath: `dist/my-project-${localId}/`,
@@ -308,6 +309,17 @@ describe('Migration to version 9', () => {
308309
}
309310

310311
describe('when i18n builder options are set', () => {
312+
it(`should add '@angular/localize' as a dependency`, async () => {
313+
const config = getWorkspaceTargets(tree);
314+
config.build.options = getI18NConfig('fr');
315+
config.build.configurations.de = getI18NConfig('de');
316+
updateWorkspaceTargets(tree, config);
317+
318+
const tree2 = await schematicRunner.runSchematicAsync('workspace-version-9', {}, tree.branch()).toPromise();
319+
const { dependencies } = JSON.parse(tree2.readContent('/package.json'));
320+
expect(dependencies['@angular/localize']).toBe(latestVersions.Angular);
321+
});
322+
311323
it(`should add 'localize' option in configuration`, async () => {
312324
let config = getWorkspaceTargets(tree);
313325
config.build.options.aot = false;
@@ -321,6 +333,50 @@ describe('Migration to version 9', () => {
321333
expect(config.configurations.de.localize).toEqual(['de']);
322334
});
323335

336+
it(`should add 'localize' option in main options`, async () => {
337+
let config = getWorkspaceTargets(tree);
338+
config.build.options.aot = false;
339+
config.build.configurations.de = getI18NConfig('de');
340+
config['extract-i18n'].options.i18nFormat = 'xmb';
341+
config['extract-i18n'].options.i18nLocale = 'en-GB';
342+
updateWorkspaceTargets(tree, config);
343+
344+
const tree2 = await schematicRunner.runSchematicAsync('workspace-version-9', {}, tree.branch()).toPromise();
345+
config = getWorkspaceTargets(tree2).build;
346+
expect(config.options.localize).toEqual(['en-GB']);
347+
expect(config.configurations.de.localize).toEqual(['de']);
348+
});
349+
350+
it('should remove deprecated i18n options', async () => {
351+
let config = getWorkspaceTargets(tree);
352+
config.build.options.aot = false;
353+
config.build.options = getI18NConfig('fr');
354+
config.build.configurations.de = getI18NConfig('de');
355+
updateWorkspaceTargets(tree, config);
356+
357+
const tree2 = await schematicRunner.runSchematicAsync('workspace-version-9', {}, tree.branch()).toPromise();
358+
config = getWorkspaceTargets(tree2).build;
359+
expect(config.options.i18nFormat).toBeUndefined();
360+
expect(config.options.i18nFile).toBeUndefined();
361+
expect(config.options.i18nLocale).toBeUndefined();
362+
expect(config.configurations.de.i18nFormat).toBeUndefined();
363+
expect(config.configurations.de.i18nFile).toBeUndefined();
364+
expect(config.configurations.de.i18nLocale).toBeUndefined();
365+
});
366+
367+
it('should remove deprecated extract-i18n options', async () => {
368+
let config = getWorkspaceTargets(tree);
369+
config['extract-i18n'].options.i18nFormat = 'xmb';
370+
config['extract-i18n'].options.i18nLocale = 'en-GB';
371+
updateWorkspaceTargets(tree, config);
372+
373+
const tree2 = await schematicRunner.runSchematicAsync('workspace-version-9', {}, tree.branch()).toPromise();
374+
config = getWorkspaceTargets(tree2)['extract-i18n'];
375+
expect(config.options.i18nFormat).toBeUndefined();
376+
expect(config.options.i18nLocale).toBeUndefined();
377+
expect(config.options.format).toBe('xmb');
378+
});
379+
324380
it(`should add i18n 'sourceLocale' project config when 'extract-i18n' 'i18nLocale' is defined`, async () => {
325381
const config = getWorkspaceTargets(tree);
326382
config.build.options.aot = false;

0 commit comments

Comments
 (0)