mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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
This commit is contained in:
committed by
Alex Rickabaugh
parent
acff032f4b
commit
881ecb0936
@@ -11,3 +11,106 @@
|
||||
// public_api_guard rules
|
||||
|
||||
export * from './localize';
|
||||
|
||||
// The global declaration must be in the index.d.ts as otherwise it will not be picked up when used
|
||||
// with
|
||||
// /// <reference types="@angular/localize" />
|
||||
|
||||
import {LocalizeFn} from './src/localize';
|
||||
|
||||
// `declare global` allows us to escape the current module and place types on the global namespace
|
||||
declare global {
|
||||
/**
|
||||
* Tag a template literal string for localization.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```ts
|
||||
* $localize `some string to localize`
|
||||
* ```
|
||||
*
|
||||
* **Providing meaning, description and id**
|
||||
*
|
||||
* You can optionally specify one or more of `meaning`, `description` and `id` for a localized
|
||||
* string by pre-pending it with a colon delimited block of the form:
|
||||
*
|
||||
* ```ts
|
||||
* $localize`:meaning|description@@id:source message text`;
|
||||
*
|
||||
* $localize`:meaning|:source message text`;
|
||||
* $localize`:description:source message text`;
|
||||
* $localize`:@@id:source message text`;
|
||||
* ```
|
||||
*
|
||||
* This format is the same as that used for `i18n` markers in Angular templates. See the
|
||||
* [Angular i18n guide](guide/i18n-common-prepare#mark-text-in-component-template).
|
||||
*
|
||||
* **Naming placeholders**
|
||||
*
|
||||
* If the template literal string contains expressions, then the expressions will be automatically
|
||||
* associated with placeholder names for you.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```ts
|
||||
* $localize `Hi ${name}! There are ${items.length} items.`;
|
||||
* ```
|
||||
*
|
||||
* will generate a message-source of `Hi {$PH}! There are {$PH_1} items`.
|
||||
*
|
||||
* The recommended practice is to name the placeholder associated with each expression though.
|
||||
*
|
||||
* Do this by providing the placeholder name wrapped in `:` characters directly after the
|
||||
* expression. These placeholder names are stripped out of the rendered localized string.
|
||||
*
|
||||
* For example, to name the `items.length` expression placeholder `itemCount` you write:
|
||||
*
|
||||
* ```ts
|
||||
* $localize `There are ${items.length}:itemCount: items`;
|
||||
* ```
|
||||
*
|
||||
* **Escaping colon markers**
|
||||
*
|
||||
* If you need to use a `:` character directly at the start of a tagged string that has no
|
||||
* metadata block, or directly after a substitution expression that has no name you must escape
|
||||
* the `:` by preceding it with a backslash:
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```ts
|
||||
* // message has a metadata block so no need to escape colon
|
||||
* $localize `:some description::this message starts with a colon (:)`;
|
||||
* // no metadata block so the colon must be escaped
|
||||
* $localize `\:this message starts with a colon (:)`;
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* // named substitution so no need to escape colon
|
||||
* $localize `${label}:label:: ${}`
|
||||
* // anonymous substitution so colon must be escaped
|
||||
* $localize `${label}\: ${}`
|
||||
* ```
|
||||
*
|
||||
* **Processing localized strings:**
|
||||
*
|
||||
* There are three scenarios:
|
||||
*
|
||||
* * **compile-time inlining**: the `$localize` tag is transformed at compile time by a
|
||||
* transpiler, removing the tag and replacing the template literal string with a translated
|
||||
* literal string from a collection of translations provided to the transpilation tool.
|
||||
*
|
||||
* * **run-time evaluation**: the `$localize` tag is a run-time function that replaces and
|
||||
* reorders the parts (static strings and expressions) of the template literal string with strings
|
||||
* from a collection of translations loaded at run-time.
|
||||
*
|
||||
* * **pass-through evaluation**: the `$localize` tag is a run-time function that simply evaluates
|
||||
* the original template literal string without applying any translations to the parts. This
|
||||
* version is used during development or where there is no need to translate the localized
|
||||
* template literals.
|
||||
*
|
||||
* @param messageParts a collection of the static parts of the template string.
|
||||
* @param expressions a collection of the values of each placeholder in the template string.
|
||||
* @returns the translated string, with the `messageParts` and `expressions` interleaved together.
|
||||
*/
|
||||
const $localize: LocalizeFn;
|
||||
}
|
||||
|
||||
@@ -7,107 +7,9 @@
|
||||
*/
|
||||
|
||||
// This file contains the public API of the `@angular/localize` entry-point
|
||||
import {LocalizeFn} from './src/localize';
|
||||
|
||||
export {clearTranslations, loadTranslations} from './src/translate';
|
||||
export {MessageId, TargetMessage} from './src/utils';
|
||||
|
||||
// Exports that are not part of the public API
|
||||
export * from './private';
|
||||
|
||||
// `declare global` allows us to escape the current module and place types on the global namespace
|
||||
declare global {
|
||||
/**
|
||||
* Tag a template literal string for localization.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```ts
|
||||
* $localize `some string to localize`
|
||||
* ```
|
||||
*
|
||||
* **Providing meaning, description and id**
|
||||
*
|
||||
* You can optionally specify one or more of `meaning`, `description` and `id` for a localized
|
||||
* string by pre-pending it with a colon delimited block of the form:
|
||||
*
|
||||
* ```ts
|
||||
* $localize`:meaning|description@@id:source message text`;
|
||||
*
|
||||
* $localize`:meaning|:source message text`;
|
||||
* $localize`:description:source message text`;
|
||||
* $localize`:@@id:source message text`;
|
||||
* ```
|
||||
*
|
||||
* This format is the same as that used for `i18n` markers in Angular templates. See the
|
||||
* [Angular i18n guide](guide/i18n-common-prepare#mark-text-in-component-template).
|
||||
*
|
||||
* **Naming placeholders**
|
||||
*
|
||||
* If the template literal string contains expressions, then the expressions will be automatically
|
||||
* associated with placeholder names for you.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```ts
|
||||
* $localize `Hi ${name}! There are ${items.length} items.`;
|
||||
* ```
|
||||
*
|
||||
* will generate a message-source of `Hi {$PH}! There are {$PH_1} items`.
|
||||
*
|
||||
* The recommended practice is to name the placeholder associated with each expression though.
|
||||
*
|
||||
* Do this by providing the placeholder name wrapped in `:` characters directly after the
|
||||
* expression. These placeholder names are stripped out of the rendered localized string.
|
||||
*
|
||||
* For example, to name the `items.length` expression placeholder `itemCount` you write:
|
||||
*
|
||||
* ```ts
|
||||
* $localize `There are ${items.length}:itemCount: items`;
|
||||
* ```
|
||||
*
|
||||
* **Escaping colon markers**
|
||||
*
|
||||
* If you need to use a `:` character directly at the start of a tagged string that has no
|
||||
* metadata block, or directly after a substitution expression that has no name you must escape
|
||||
* the `:` by preceding it with a backslash:
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```ts
|
||||
* // message has a metadata block so no need to escape colon
|
||||
* $localize `:some description::this message starts with a colon (:)`;
|
||||
* // no metadata block so the colon must be escaped
|
||||
* $localize `\:this message starts with a colon (:)`;
|
||||
* ```
|
||||
*
|
||||
* ```ts
|
||||
* // named substitution so no need to escape colon
|
||||
* $localize `${label}:label:: ${}`
|
||||
* // anonymous substitution so colon must be escaped
|
||||
* $localize `${label}\: ${}`
|
||||
* ```
|
||||
*
|
||||
* **Processing localized strings:**
|
||||
*
|
||||
* There are three scenarios:
|
||||
*
|
||||
* * **compile-time inlining**: the `$localize` tag is transformed at compile time by a
|
||||
* transpiler, removing the tag and replacing the template literal string with a translated
|
||||
* literal string from a collection of translations provided to the transpilation tool.
|
||||
*
|
||||
* * **run-time evaluation**: the `$localize` tag is a run-time function that replaces and
|
||||
* reorders the parts (static strings and expressions) of the template literal string with strings
|
||||
* from a collection of translations loaded at run-time.
|
||||
*
|
||||
* * **pass-through evaluation**: the `$localize` tag is a run-time function that simply evaluates
|
||||
* the original template literal string without applying any translations to the parts. This
|
||||
* version is used during development or where there is no need to translate the localized
|
||||
* template literals.
|
||||
*
|
||||
* @param messageParts a collection of the static parts of the template string.
|
||||
* @param expressions a collection of the values of each placeholder in the template string.
|
||||
* @returns the translated string, with the `messageParts` and `expressions` interleaved together.
|
||||
*/
|
||||
const $localize: LocalizeFn;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {Builders} from '@schematics/angular/utility/workspace-models';
|
||||
import {Schema} from './schema';
|
||||
|
||||
const localizeType = `@angular/localize`;
|
||||
const localizeTripleSlashType = `/// <reference types="@angular/localize" />`;
|
||||
|
||||
function addTypeScriptConfigTypes(projectName: string): Rule {
|
||||
return async (host: Tree) => {
|
||||
@@ -28,7 +29,7 @@ function addTypeScriptConfigTypes(projectName: string): Rule {
|
||||
}
|
||||
|
||||
// We add the root workspace tsconfig for better IDE support.
|
||||
const tsConfigFiles = new Set<string>(['tsconfig.json']);
|
||||
const tsConfigFiles = new Set<string>();
|
||||
for (const target of project.targets.values()) {
|
||||
switch (target.builder) {
|
||||
case Builders.Karma:
|
||||
@@ -41,6 +42,13 @@ function addTypeScriptConfigTypes(projectName: string): Rule {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (target.builder === Builders.Browser) {
|
||||
const value = target.options?.['main'];
|
||||
if (typeof value === 'string') {
|
||||
addTripleSlashType(host, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const typesJsonPath: JSONPath = ['compilerOptions', 'types'];
|
||||
@@ -68,6 +76,13 @@ function addTypeScriptConfigTypes(projectName: string): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function addTripleSlashType(host: Tree, path: string): void {
|
||||
const content = host.readText(path);
|
||||
if (!content.includes(localizeTripleSlashType)) {
|
||||
host.overwrite(path, localizeTripleSlashType + '\n\n' + content);
|
||||
}
|
||||
}
|
||||
|
||||
function moveToDependencies(host: Tree, context: SchematicContext): void {
|
||||
if (!host.exists('package.json')) {
|
||||
return;
|
||||
|
||||
@@ -16,7 +16,8 @@ interface TsConfig {
|
||||
}
|
||||
|
||||
describe('ng-add schematic', () => {
|
||||
const localizeType = '@angular/localize';
|
||||
const localizeTripleSlashType = `/// <reference types="@angular/localize" />`;
|
||||
|
||||
const defaultOptions = {project: 'demo'};
|
||||
const schematicRunner = new SchematicTestRunner(
|
||||
'@angular/localize', runfiles.resolvePackageRelative('../collection.json'));
|
||||
@@ -33,6 +34,11 @@ describe('ng-add schematic', () => {
|
||||
},
|
||||
}));
|
||||
|
||||
host.create('main.ts', `
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
`);
|
||||
|
||||
host.create('angular.json', JSON.stringify({
|
||||
version: 1,
|
||||
projects: {
|
||||
@@ -42,6 +48,7 @@ describe('ng-add schematic', () => {
|
||||
build: {
|
||||
builder: '@angular-devkit/build-angular:browser',
|
||||
options: {
|
||||
main: './main.ts',
|
||||
tsConfig: './tsconfig.app.json',
|
||||
},
|
||||
},
|
||||
@@ -69,36 +76,22 @@ describe('ng-add schematic', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
it(`should add '@angular/localize' in 'types' in the root level 'tsconfig.json'`, async () => {
|
||||
host.create('tsconfig.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
types: ['node'],
|
||||
},
|
||||
}));
|
||||
|
||||
it(`should add '@angular/localize' type reference in 'main.ts'`, async () => {
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {compilerOptions} = host.readJson('tsconfig.json') as TsConfig;
|
||||
const types = compilerOptions?.types;
|
||||
expect(types).toContain(localizeType);
|
||||
expect(types).toHaveSize(2);
|
||||
expect(host.readText('main.ts')).toContain(localizeTripleSlashType);
|
||||
});
|
||||
|
||||
it(`should not add '@angular/localize' in 'types' tsconfig when '@angular/localize/init' is present`,
|
||||
it(`should not add '@angular/localize' type reference in 'main.ts' if already present`,
|
||||
async () => {
|
||||
host.create('tsconfig.json', JSON.stringify({
|
||||
compilerOptions: {
|
||||
types: ['node', '@angular/localize/init'],
|
||||
},
|
||||
}));
|
||||
|
||||
const mainContentInput = `
|
||||
${localizeTripleSlashType}
|
||||
import { enableProdMode } from '@angular/core';
|
||||
`;
|
||||
host.overwrite('main.ts', mainContentInput);
|
||||
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
|
||||
const {compilerOptions} = host.readJson('tsconfig.json') as TsConfig;
|
||||
const types = compilerOptions?.types;
|
||||
expect(types).not.toContain(localizeType);
|
||||
expect(types).toHaveSize(2);
|
||||
expect(host.readText('main.ts')).toBe(mainContentInput);
|
||||
});
|
||||
|
||||
|
||||
it(`should not add '@angular/localize' in 'types' tsconfigs referenced in non official builders`,
|
||||
async () => {
|
||||
const tsConfig = JSON.stringify({
|
||||
|
||||
Reference in New Issue
Block a user