skip to content

In Angular's i18n metadata meaning|description@@customId, what does each part do, and which parts affect the message ID?

level: middleimportance: should knowfreq 38%

answer

  1. one part only informs the translator
  2. one part splits identical text
  3. one part replaces the hash
  4. text plus meaning feed the generated ID

basics

~20 s

The description is context for translators and never changes the message ID. The meaning disambiguates identical text and is hashed into the generated ID with the text. A custom @@id replaces the generated ID entirely.

solid answer

~40 s

The metadata goes in the `i18n` or `i18n-<attr>` value, or between colons at the start of a `$localize` string. The **description** tells the translator where and how the text is used; it is not part of the generated ID. The **meaning** says what the text means in this context; the generated ID is computed from the message text plus the meaning, so the same word with two meanings, such as "Order" the noun and "Order" the verb, becomes two translation units, while the same text with the same meaning and different descriptions is extracted once. **`@@customId`** replaces the generated ID: it stays the same when the text changes, and it must be unique, because two different texts sharing one custom ID get extracted once and share one translation.

code

html · 7 lines
html
<!-- same text, different meanings: two translation units -->
<a routerLink="/orders" i18n="noun|Link to the list of past purchases">Order</a>
<button type="submit" i18n="verb|Checkout button that places the order@@checkoutPlaceOrder">Order</button>

<!-- same text and meaning, different descriptions: extracted once -->
<h2 i18n="cart heading|Shown above the item list">Your cart</h2>
<h2 i18n="cart heading|Shown in the mini-cart drawer">Your cart</h2>

go deeper

for a junior

Recall the order of the parts, meaning then pipe then description then @@ and the ID, and that a bare value is a description.

for a middle

Explain that the generated ID hashes the text plus the meaning, so descriptions never change it, meanings split homographs, and a custom ID replaces the hash.

for a senior

Weigh custom IDs against generated ones: stable IDs through wording edits versus translations silently drifting from rewritten source text, and enforcing uniqueness.

for a principal

Frame an ID policy for a large codebase: who owns custom ID naming, how it maps to the translation vendor's system, and how mixing policies raises cost.

## Where the metadata goes Every Angular i18n marker accepts the same optional metadata string: ```text {meaning}|{description}@@{custom_id} ``` - In a template: `<h2 i18n="cart heading|Title above the cart list@@cartHeading">Your cart</h2>` - On an attribute: `<button i18n-title="Tooltip on the remove button" title="Remove item">` - In code, between colons before the text: ``$localize`:cart heading|Title above the cart list@@cartHeading:Your cart` `` Each part is optional. `$localize` documents the partial forms explicitly: ``:meaning|:text``, ``:description:text``, ``:@@id:text``. A bare value with no `|` and no `@@` is a **description**. ## The three parts | Part | Purpose | Part of the generated ID? | | --- | --- | --- | | Description | Context for the translator: where the text appears, length limits, tone | No | | Meaning | The sense of the text in this context, so identical text can be translated differently | Yes, hashed with the text | | `@@customId` | A developer-chosen ID used instead of the generated one | Replaces it | ### Description The description is the part translators rely on most: "Button label on the cart summary, max 12 characters" turns an ambiguous word into an answerable request. It travels into the translation file as a note, but the Angular compiler's ID computation takes only the message text and the meaning. Consequence: two elements with the same text and the same meaning but different descriptions are **extracted once**, and that single translation is merged back everywhere the text appears. ### Meaning The meaning exists for **homographs**, words spelled the same with different senses. In a shop, "Order" is a noun on the order-history link and a verb on the checkout button; many languages use different words for the two. Giving them different meanings produces two translation units: ```html <a i18n="noun|Link to past purchases">Order</a> <button i18n="verb|Checkout button that places the order">Order</button> ``` Because the meaning is hashed into the ID, **changing a meaning changes the generated ID** just as changing the text does. Meanings also let you force consistency: every message marked `site header` with the same text resolves to one translation. ### `@@customId` With `@@`, the extractor uses your ID instead of hashing. Reasons teams choose it: 1. A translation-management system requires a particular ID format. 2. The team wants IDs that encode context, such as `cart.summary.heading`. 3. A wording tweak in the source language should not create a new, untranslated unit. The cost is the mirror image of point 3: since the ID does not change when the text changes, an existing translation can silently drift out of sync with a rewritten source string. Two further rules: - Custom IDs must be **unique**. If two different texts use the same `@@id`, the extractor keeps only the first (and reports a `Duplicate messages with id` diagnostic, a warning by default), and Angular shows that one translation in place of both. - When a custom ID is present, it is the ID; the meaning no longer participates in identifying the unit. ## How the generated ID is computed In `@angular/compiler`, the message-ID functions (`computeMsgId(msg, meaning)` and the older SHA-1 based digest) fingerprint the serialized message, including its placeholders, and fold in the meaning when one is present. The description is not an input to either. That single fact answers most interview follow-ups: - Edit the description: same ID, the translator just sees better context. - Edit the meaning: new ID, a new unit to translate. - Edit the text: new ID, unless a custom ID pins it. ## Picking what to write - Always write a **description** for anything short or ambiguous: button labels, single words, abbreviations. - Add a **meaning** only when the same text must translate differently, or must be forced to translate identically across screens. - Use **custom IDs** as a deliberate team policy, not ad hoc; a mix of generated and custom IDs is hard to reason about. How IDs behave across releases and how translation files are kept in sync after extraction is a separate workflow concern; the metadata above is what the marking step contributes to it.

  • In an Angular i18n attribute value with no | and no @@, which part is it?
    It is a description. `i18n="Title above the cart list"` gives the translator context and does not affect the generated ID. To supply only a meaning you write `meaning|` with the pipe; in `$localize` that is ``:meaning|:text``.
  • Why do placeholders matter to the generated ID?
    The ID is a fingerprint of the serialized message, and placeholders such as `INTERPOLATION` or a named `{$itemCount}` are part of that serialization. Renaming a placeholder or moving an element inside the marked text changes the message and therefore its generated ID, even when the visible words are the same.

saying these in an interview costs you the question

  • Changing the description changes the message ID
  • The meaning is only a comment for translators
  • A custom ID updates automatically when the text changes
  • Reusing one custom ID for different texts keeps both translations
  • Identical text always produces one translation unit