diff --git a/aio/tools/transforms/angular-api-package/index.js b/aio/tools/transforms/angular-api-package/index.js index 555ecd0645..79eef89996 100644 --- a/aio/tools/transforms/angular-api-package/index.js +++ b/aio/tools/transforms/angular-api-package/index.js @@ -86,7 +86,7 @@ module.exports = readTypeScriptModules.ignoreExportsMatching = [/^_|^ɵɵ|^VERSION$/]; readTypeScriptModules.hidePrivateMembers = true; - // NOTE: This list should be in sync with tools/public_api_guard/BUILD.bazel + // NOTE: This list should be in sync with the folders/files in `goldens/public-api`. readTypeScriptModules.sourceFiles = [ 'animations/index.ts', 'animations/browser/index.ts', @@ -101,6 +101,8 @@ module.exports = 'core/testing/index.ts', 'elements/index.ts', 'forms/index.ts', + 'localize/index.ts', + 'localize/init/index.ts', 'platform-browser/index.ts', 'platform-browser/animations/index.ts', 'platform-browser/testing/index.ts', diff --git a/packages/localize/PACKAGE.md b/packages/localize/PACKAGE.md new file mode 100644 index 0000000000..a08e9ec121 --- /dev/null +++ b/packages/localize/PACKAGE.md @@ -0,0 +1,56 @@ +The `@angular/localize` package contains helpers and tools for localizing your application. + +You should install this package using `ng add @angular/localize` if you need to tag text in your +application that you want to be translatable. + +The approach is based around the concept of tagging strings in code with a [template literal tag handler][tagged-templates] +called `$localize`. The idea is that strings that need to be translated are “marked” using this tag: + +```ts +const message = $localize`Hello, World!`; +``` + +--- + +This `$localize` identifier can be a real function that can do the translation at runtime, in the browser. +But, significantly, it is also a global identifier that survives minification. +This means it can act simply as a marker in the code that a static post-processing tool can use to replace +the original text with translated text before the code is deployed. + +For example, the following code: + +```ts +warning = $localize`${this.process} is not right`; +``` + +could be replaced with: + +```ts +warning = "" + this.process + ", ce n'est pas bon."; +``` + +The result is that all references to `$localize` are removed, and there is **zero runtime cost** to rendering +the translated text. + +--- + +The Angular template compiler also generates `$localize` tagged strings rather than doing the translation itself. +For example, the following template: + +```html +

Hello, World!

+``` + +would be compiled to something like: + +```ts +ɵɵelementStart(0, "h1"); //

+ɵɵi18n(1, $localize`Hello, World!`); // Hello, World! +ɵɵelementEnd(); //

+``` + +This means that after the Angular compiler has completed its work, all the template text marked with `i18n` +attributes have been converted to `$localize` tagged strings, which can be processed just like any other +tagged string. + +[tagged-templates]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates diff --git a/packages/localize/init/PACKAGE.md b/packages/localize/init/PACKAGE.md new file mode 100644 index 0000000000..581750ff4d --- /dev/null +++ b/packages/localize/init/PACKAGE.md @@ -0,0 +1,2 @@ +The `@angular/localize` package exposes the `$localize` function in the global namespace, which can +be used to tag i18n messages in your code that needs to be translated. diff --git a/packages/localize/init/index.ts b/packages/localize/init/index.ts index db1495a29d..e29960f8f3 100644 --- a/packages/localize/init/index.ts +++ b/packages/localize/init/index.ts @@ -7,7 +7,7 @@ */ import {$localize, _global, LocalizeFn} from '../src/localize'; -export {LocalizeFn, TranslateFn} from '../src/localize'; +export {$localize, LocalizeFn, TranslateFn} from '../src/localize'; // Attach $localize to the global context, as a side-effect of this module. _global.$localize = $localize; @@ -37,7 +37,7 @@ declare global { * ``` * * This format is the same as that used for `i18n` markers in Angular templates. See the - * [Angular 18n guide](guide/i18n#template-translations). + * [Angular 18n guide](guide/i18n#mark-text-for-translations). * * **Naming placeholders** * diff --git a/packages/localize/src/localize/src/localize.ts b/packages/localize/src/localize/src/localize.ts index 071de81bd0..e6bb9ab66d 100644 --- a/packages/localize/src/localize/src/localize.ts +++ b/packages/localize/src/localize/src/localize.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +/** @nodoc */ export interface LocalizeFn { (messageParts: TemplateStringsArray, ...expressions: readonly any[]): string; @@ -38,6 +39,7 @@ export interface LocalizeFn { locale?: string; } +/** @nodoc */ export interface TranslateFn { (messageParts: TemplateStringsArray, expressions: readonly any[]): [TemplateStringsArray, readonly any[]]; @@ -66,7 +68,7 @@ export interface TranslateFn { * ``` * * This format is the same as that used for `i18n` markers in Angular templates. See the - * [Angular 18n guide](guide/i18n#template-translations). + * [Angular 18n guide](guide/i18n#mark-text-for-translations). * * **Naming placeholders** * @@ -130,9 +132,13 @@ export interface TranslateFn { * 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. + * + * @globalApi + * @publicApi */ export const $localize: LocalizeFn = function( messageParts: TemplateStringsArray, ...expressions: readonly any[]) { diff --git a/packages/localize/src/translate.ts b/packages/localize/src/translate.ts index 1fb323c5cf..838df1aedd 100644 --- a/packages/localize/src/translate.ts +++ b/packages/localize/src/translate.ts @@ -17,21 +17,43 @@ import {MessageId, ParsedTranslation, parseTranslation, TargetMessage, translate declare const $localize: LocalizeFn&{TRANSLATIONS: Record}; /** - * Load translations for `$localize`. + * Load translations for use by `$localize`, if doing runtime translation. * - * The given `translations` are processed and added to a lookup based on their `MessageId`. - * A new translation will overwrite a previous translation if it has the same `MessageId`. + * If the `$localize` tagged strings are not going to be replaced at compiled time, it is possible + * to load a set of translations that will be applied to the `$localize` tagged strings at runtime, + * in the browser. * - * * If a message is generated by the Angular compiler from an `i18n` marker in a template, the - * `MessageId` is passed through to the `$localize` call as a custom `MessageId`. The `MessageId` - * will match what is extracted into translation files. + * Loading a new translation will overwrite a previous translation if it has the same `MessageId`. * - * * If the translation is from a call to `$localize` in application code, and no custom `MessageId` - * is provided, then the `MessageId` can be generated by passing the tagged string message-parts - * to the `parseMessage()` function (not currently public API). + * Note that `$localize` messages are only processed once, when the tagged string is first + * encountered, and does not provide dynamic language changing without refreshing the browser. + * Loading new translations later in the application life-cycle will not change the translated text + * of messages that have already been translated. * + * The message IDs and translations are in the same format as that rendered to "simple JSON" + * translation files when extracting messages. In particular, placeholders in messages are rendered + * using the `{$PLACEHOLDER_NAME}` syntax. For example the message from the following template: + * + * ```html + *
preinner-preboldinner-postpost
+ * ``` + * + * would have the following form in the `translations` map: + * + * ```ts + * { + * "2932901491976224757": + * "pre{$START_TAG_SPAN}inner-pre{$START_BOLD_TEXT}bold{$CLOSE_BOLD_TEXT}inner-post{$CLOSE_TAG_SPAN}post" + * } + * ``` + * + * @param translations A map from message ID to translated message. + * + * These messages are processed and added to a lookup based on their `MessageId`. + * + * @see `clearTranslations()` for removing translations loaded using this function. + * @see `$localize` for tagging messages as needing to be translated. * @publicApi - * */ export function loadTranslations(translations: Record) { // Ensure the translate function exists @@ -47,7 +69,12 @@ export function loadTranslations(translations: Record) } /** - * Remove all translations for `$localize`. + * Remove all translations for `$localize`, if doing runtime translation. + * + * All translations that had been loading into memory using `loadTranslations()` will be removed. + * + * @see `loadTranslations()` for loading translations at runtime. + * @see `$localize` for tagging messages as needing to be translated. * * @publicApi */