Skip to content

Commit a1a8e91

Browse files
alan-agius4alxhub
authored andcommitted
fix(localize): add triple slash type reference on @angular/localize on `ng add (#48502)
This commits add a triple slash type reference to the `main.ts` of the project when running `ng add @angular/localize`. This is purely needed for IDE purposes so that `$localize` is available globally. For the compilations `@angular/localize` types are adding the the respective TS configs files. This commits also add better support for using `@angular/localize` in `/// <reference types="@angular/localize" />`. To support this we need to move the global definition outside of a barrel file and into the index.ts file. Without this change the `$localize` method will not available globally when using triple slash type references. Closes #48434 PR Close #48502
1 parent 2f4f063 commit a1a8e91

File tree

4 files changed

+136
-123
lines changed

4 files changed

+136
-123
lines changed

packages/localize/index.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,106 @@
1111
// public_api_guard rules
1212

1313
export * from './localize';
14+
15+
// The global declaration must be in the index.d.ts as otherwise it will not be picked up when used
16+
// with
17+
// /// <reference types="@angular/localize" />
18+
19+
import {LocalizeFn} from './src/localize';
20+
21+
// `declare global` allows us to escape the current module and place types on the global namespace
22+
declare global {
23+
/**
24+
* Tag a template literal string for localization.
25+
*
26+
* For example:
27+
*
28+
* ```ts
29+
* $localize `some string to localize`
30+
* ```
31+
*
32+
* **Providing meaning, description and id**
33+
*
34+
* You can optionally specify one or more of `meaning`, `description` and `id` for a localized
35+
* string by pre-pending it with a colon delimited block of the form:
36+
*
37+
* ```ts
38+
* $localize`:meaning|description@@id:source message text`;
39+
*
40+
* $localize`:meaning|:source message text`;
41+
* $localize`:description:source message text`;
42+
* $localize`:@@id:source message text`;
43+
* ```
44+
*
45+
* This format is the same as that used for `i18n` markers in Angular templates. See the
46+
* [Angular i18n guide](guide/i18n-common-prepare#mark-text-in-component-template).
47+
*
48+
* **Naming placeholders**
49+
*
50+
* If the template literal string contains expressions, then the expressions will be automatically
51+
* associated with placeholder names for you.
52+
*
53+
* For example:
54+
*
55+
* ```ts
56+
* $localize `Hi ${name}! There are ${items.length} items.`;
57+
* ```
58+
*
59+
* will generate a message-source of `Hi {$PH}! There are {$PH_1} items`.
60+
*
61+
* The recommended practice is to name the placeholder associated with each expression though.
62+
*
63+
* Do this by providing the placeholder name wrapped in `:` characters directly after the
64+
* expression. These placeholder names are stripped out of the rendered localized string.
65+
*
66+
* For example, to name the `items.length` expression placeholder `itemCount` you write:
67+
*
68+
* ```ts
69+
* $localize `There are ${items.length}:itemCount: items`;
70+
* ```
71+
*
72+
* **Escaping colon markers**
73+
*
74+
* If you need to use a `:` character directly at the start of a tagged string that has no
75+
* metadata block, or directly after a substitution expression that has no name you must escape
76+
* the `:` by preceding it with a backslash:
77+
*
78+
* For example:
79+
*
80+
* ```ts
81+
* // message has a metadata block so no need to escape colon
82+
* $localize `:some description::this message starts with a colon (:)`;
83+
* // no metadata block so the colon must be escaped
84+
* $localize `\:this message starts with a colon (:)`;
85+
* ```
86+
*
87+
* ```ts
88+
* // named substitution so no need to escape colon
89+
* $localize `${label}:label:: ${}`
90+
* // anonymous substitution so colon must be escaped
91+
* $localize `${label}\: ${}`
92+
* ```
93+
*
94+
* **Processing localized strings:**
95+
*
96+
* There are three scenarios:
97+
*
98+
* * **compile-time inlining**: the `$localize` tag is transformed at compile time by a
99+
* transpiler, removing the tag and replacing the template literal string with a translated
100+
* literal string from a collection of translations provided to the transpilation tool.
101+
*
102+
* * **run-time evaluation**: the `$localize` tag is a run-time function that replaces and
103+
* reorders the parts (static strings and expressions) of the template literal string with strings
104+
* from a collection of translations loaded at run-time.
105+
*
106+
* * **pass-through evaluation**: the `$localize` tag is a run-time function that simply evaluates
107+
* the original template literal string without applying any translations to the parts. This
108+
* version is used during development or where there is no need to translate the localized
109+
* template literals.
110+
*
111+
* @param messageParts a collection of the static parts of the template string.
112+
* @param expressions a collection of the values of each placeholder in the template string.
113+
* @returns the translated string, with the `messageParts` and `expressions` interleaved together.
114+
*/
115+
const $localize: LocalizeFn;
116+
}

packages/localize/localize.ts

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -7,107 +7,9 @@
77
*/
88

99
// This file contains the public API of the `@angular/localize` entry-point
10-
import {LocalizeFn} from './src/localize';
1110

1211
export {clearTranslations, loadTranslations} from './src/translate';
1312
export {MessageId, TargetMessage} from './src/utils';
1413

1514
// Exports that are not part of the public API
1615
export * from './private';
17-
18-
// `declare global` allows us to escape the current module and place types on the global namespace
19-
declare global {
20-
/**
21-
* Tag a template literal string for localization.
22-
*
23-
* For example:
24-
*
25-
* ```ts
26-
* $localize `some string to localize`
27-
* ```
28-
*
29-
* **Providing meaning, description and id**
30-
*
31-
* You can optionally specify one or more of `meaning`, `description` and `id` for a localized
32-
* string by pre-pending it with a colon delimited block of the form:
33-
*
34-
* ```ts
35-
* $localize`:meaning|description@@id:source message text`;
36-
*
37-
* $localize`:meaning|:source message text`;
38-
* $localize`:description:source message text`;
39-
* $localize`:@@id:source message text`;
40-
* ```
41-
*
42-
* This format is the same as that used for `i18n` markers in Angular templates. See the
43-
* [Angular i18n guide](guide/i18n-common-prepare#mark-text-in-component-template).
44-
*
45-
* **Naming placeholders**
46-
*
47-
* If the template literal string contains expressions, then the expressions will be automatically
48-
* associated with placeholder names for you.
49-
*
50-
* For example:
51-
*
52-
* ```ts
53-
* $localize `Hi ${name}! There are ${items.length} items.`;
54-
* ```
55-
*
56-
* will generate a message-source of `Hi {$PH}! There are {$PH_1} items`.
57-
*
58-
* The recommended practice is to name the placeholder associated with each expression though.
59-
*
60-
* Do this by providing the placeholder name wrapped in `:` characters directly after the
61-
* expression. These placeholder names are stripped out of the rendered localized string.
62-
*
63-
* For example, to name the `items.length` expression placeholder `itemCount` you write:
64-
*
65-
* ```ts
66-
* $localize `There are ${items.length}:itemCount: items`;
67-
* ```
68-
*
69-
* **Escaping colon markers**
70-
*
71-
* If you need to use a `:` character directly at the start of a tagged string that has no
72-
* metadata block, or directly after a substitution expression that has no name you must escape
73-
* the `:` by preceding it with a backslash:
74-
*
75-
* For example:
76-
*
77-
* ```ts
78-
* // message has a metadata block so no need to escape colon
79-
* $localize `:some description::this message starts with a colon (:)`;
80-
* // no metadata block so the colon must be escaped
81-
* $localize `\:this message starts with a colon (:)`;
82-
* ```
83-
*
84-
* ```ts
85-
* // named substitution so no need to escape colon
86-
* $localize `${label}:label:: ${}`
87-
* // anonymous substitution so colon must be escaped
88-
* $localize `${label}\: ${}`
89-
* ```
90-
*
91-
* **Processing localized strings:**
92-
*
93-
* There are three scenarios:
94-
*
95-
* * **compile-time inlining**: the `$localize` tag is transformed at compile time by a
96-
* transpiler, removing the tag and replacing the template literal string with a translated
97-
* literal string from a collection of translations provided to the transpilation tool.
98-
*
99-
* * **run-time evaluation**: the `$localize` tag is a run-time function that replaces and
100-
* reorders the parts (static strings and expressions) of the template literal string with strings
101-
* from a collection of translations loaded at run-time.
102-
*
103-
* * **pass-through evaluation**: the `$localize` tag is a run-time function that simply evaluates
104-
* the original template literal string without applying any translations to the parts. This
105-
* version is used during development or where there is no need to translate the localized
106-
* template literals.
107-
*
108-
* @param messageParts a collection of the static parts of the template string.
109-
* @param expressions a collection of the values of each placeholder in the template string.
110-
* @returns the translated string, with the `messageParts` and `expressions` interleaved together.
111-
*/
112-
const $localize: LocalizeFn;
113-
}

packages/localize/schematics/ng-add/index.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {Builders} from '@schematics/angular/utility/workspace-models';
1818
import {Schema} from './schema';
1919

2020
const localizeType = `@angular/localize`;
21+
const localizeTripleSlashType = `/// <reference types="@angular/localize" />`;
2122

2223
function addTypeScriptConfigTypes(projectName: string): Rule {
2324
return async (host: Tree) => {
@@ -28,7 +29,7 @@ function addTypeScriptConfigTypes(projectName: string): Rule {
2829
}
2930

3031
// We add the root workspace tsconfig for better IDE support.
31-
const tsConfigFiles = new Set<string>(['tsconfig.json']);
32+
const tsConfigFiles = new Set<string>();
3233
for (const target of project.targets.values()) {
3334
switch (target.builder) {
3435
case Builders.Karma:
@@ -41,6 +42,13 @@ function addTypeScriptConfigTypes(projectName: string): Rule {
4142

4243
break;
4344
}
45+
46+
if (target.builder === Builders.Browser) {
47+
const value = target.options?.['main'];
48+
if (typeof value === 'string') {
49+
addTripleSlashType(host, value);
50+
}
51+
}
4452
}
4553

4654
const typesJsonPath: JSONPath = ['compilerOptions', 'types'];
@@ -68,6 +76,13 @@ function addTypeScriptConfigTypes(projectName: string): Rule {
6876
};
6977
}
7078

79+
function addTripleSlashType(host: Tree, path: string): void {
80+
const content = host.readText(path);
81+
if (!content.includes(localizeTripleSlashType)) {
82+
host.overwrite(path, localizeTripleSlashType + '\n\n' + content);
83+
}
84+
}
85+
7186
function moveToDependencies(host: Tree, context: SchematicContext): void {
7287
if (!host.exists('package.json')) {
7388
return;

packages/localize/schematics/ng-add/index_spec.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ interface TsConfig {
1616
}
1717

1818
describe('ng-add schematic', () => {
19-
const localizeType = '@angular/localize';
19+
const localizeTripleSlashType = `/// <reference types="@angular/localize" />`;
20+
2021
const defaultOptions = {project: 'demo'};
2122
const schematicRunner = new SchematicTestRunner(
2223
'@angular/localize', runfiles.resolvePackageRelative('../collection.json'));
@@ -33,6 +34,11 @@ describe('ng-add schematic', () => {
3334
},
3435
}));
3536

37+
host.create('main.ts', `
38+
import { enableProdMode } from '@angular/core';
39+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
40+
`);
41+
3642
host.create('angular.json', JSON.stringify({
3743
version: 1,
3844
projects: {
@@ -42,6 +48,7 @@ describe('ng-add schematic', () => {
4248
build: {
4349
builder: '@angular-devkit/build-angular:browser',
4450
options: {
51+
main: './main.ts',
4552
tsConfig: './tsconfig.app.json',
4653
},
4754
},
@@ -69,36 +76,22 @@ describe('ng-add schematic', () => {
6976
}));
7077
});
7178

72-
it(`should add '@angular/localize' in 'types' in the root level 'tsconfig.json'`, async () => {
73-
host.create('tsconfig.json', JSON.stringify({
74-
compilerOptions: {
75-
types: ['node'],
76-
},
77-
}));
78-
79+
it(`should add '@angular/localize' type reference in 'main.ts'`, async () => {
7980
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
80-
const {compilerOptions} = host.readJson('tsconfig.json') as TsConfig;
81-
const types = compilerOptions?.types;
82-
expect(types).toContain(localizeType);
83-
expect(types).toHaveSize(2);
81+
expect(host.readText('main.ts')).toContain(localizeTripleSlashType);
8482
});
8583

86-
it(`should not add '@angular/localize' in 'types' tsconfig when '@angular/localize/init' is present`,
84+
it(`should not add '@angular/localize' type reference in 'main.ts' if already present`,
8785
async () => {
88-
host.create('tsconfig.json', JSON.stringify({
89-
compilerOptions: {
90-
types: ['node', '@angular/localize/init'],
91-
},
92-
}));
93-
86+
const mainContentInput = `
87+
${localizeTripleSlashType}
88+
import { enableProdMode } from '@angular/core';
89+
`;
90+
host.overwrite('main.ts', mainContentInput);
9491
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
95-
const {compilerOptions} = host.readJson('tsconfig.json') as TsConfig;
96-
const types = compilerOptions?.types;
97-
expect(types).not.toContain(localizeType);
98-
expect(types).toHaveSize(2);
92+
expect(host.readText('main.ts')).toBe(mainContentInput);
9993
});
10094

101-
10295
it(`should not add '@angular/localize' in 'types' tsconfigs referenced in non official builders`,
10396
async () => {
10497
const tsConfig = JSON.stringify({

0 commit comments

Comments
 (0)