or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cli-tools.mdcore-localization.mdindex.mdmessage-processing.mdruntime-translation.mdtranslation-files.md
tile.json

message-processing.mddocs/

Message Processing

Core utilities for parsing, processing, and manipulating localize messages, including metadata extraction, message ID computation, and message structure handling.

Capabilities

Parse Message

Parses a $localize tagged string into a structured format for translation or extraction.

/**
 * Parse a $localize tagged string into structure for translation/extraction.
 * Extracts metadata, placeholders, and substitutions from template literal.
 * 
 * @param messageParts - Template string array parts
 * @param expressions - Expression values (optional)
 * @param location - Source location info (optional)
 * @param messagePartLocations - Location info for each part (optional)
 * @param expressionLocations - Location info for expressions (optional)
 * @returns Parsed message structure with metadata and placeholders
 */
function parseMessage(
  messageParts: TemplateStringsArray,
  expressions?: readonly any[],
  location?: SourceLocation,
  messagePartLocations?: (SourceLocation | undefined)[],
  expressionLocations?: (SourceLocation | undefined)[]
): ParsedMessage;

interface ParsedMessage extends MessageMetadata {
  id: MessageId;
  substitutions: Record<string, any>;
  messageParts: string[];
  placeholderNames: string[];
  associatedMessageIds?: Record<string, MessageId>;
  substitutionLocations?: Record<string, SourceLocation | undefined>;
  messagePartLocations?: (SourceLocation | undefined)[];
  location?: SourceLocation;
}

Usage Example:

import { parseMessage } from "@angular/localize";

// Parse a template literal
const messageParts = ["Hello, ", "!"] as TemplateStringsArray;
messageParts.raw = ["Hello, ", "!"];
const expressions = ["Alice"];

const parsed = parseMessage(messageParts, expressions);
console.log(parsed);
// {
//   id: "2788806693285683417",
//   text: "Hello, {$PH}!",
//   substitutions: { PH: "Alice" },
//   messageParts: ["Hello, ", "!"],
//   placeholderNames: ["PH"],
//   meaning: "",
//   description: "",
//   ...
// }

Parse Metadata

Parses metadata block from the first part of a $localize tagged string.

/**
 * Parse metadata from message part (cooked + raw).
 * Extracts meaning, description, customId and legacyIds from metadata block.
 * Metadata is serialized delimited by |, @@, and ␟ respectively.
 * 
 * @param cooked - Cooked version of message part
 * @param raw - Raw version of message part  
 * @returns Object containing parsed metadata
 */
function parseMetadata(cooked: string, raw: string): MessageMetadata;

interface MessageMetadata {
  text: string;
  meaning?: string;
  description?: string;
  customId?: string;
  legacyIds?: string[];
  location?: SourceLocation;
}

Usage Examples:

import { parseMetadata } from "@angular/localize";

// Parse full metadata block
const metadata1 = parseMetadata(
  ":page-title|The main page title@@homepage-title:Welcome",
  ":page-title|The main page title@@homepage-title:Welcome"
);
console.log(metadata1);
// {
//   text: "Welcome",
//   meaning: "page-title", 
//   description: "The main page title",
//   customId: "homepage-title"
// }

// Parse with legacy IDs
const metadata2 = parseMetadata(
  ":@@new-id␟legacy-1␟legacy-2:Message",
  ":@@new-id␟legacy-1␟legacy-2:Message"  
);
console.log(metadata2);
// {
//   text: "Message",
//   customId: "new-id",
//   legacyIds: ["legacy-1", "legacy-2"]
// }

// No metadata block
const metadata3 = parseMetadata("Simple message", "Simple message");
console.log(metadata3);
// { text: "Simple message" }

Parse Placeholder

Parses placeholder metadata from message parts following substitutions.

/**
 * Parse placeholder metadata from message part (cooked + raw).
 * Extracts placeholderName and associatedMessageId from block.
 * Metadata is serialized delimited by @@.
 * 
 * @param cooked - Cooked version of message part
 * @param raw - Raw version of message part
 * @returns Object with placeholder metadata and remaining text
 */
function parsePlaceholder(
  cooked: string,
  raw: string
): {
  messagePart: string;
  placeholderName?: string;
  associatedMessageId?: string;
};

Usage Examples:

import { parsePlaceholder } from "@angular/localize";

// Named placeholder
const result1 = parsePlaceholder(":itemCount: items", ":itemCount: items");
console.log(result1);
// {
//   messagePart: " items",
//   placeholderName: "itemCount"
// }

// Placeholder with associated message ID
const result2 = parsePlaceholder(
  ":userName@@user-id: profile", 
  ":userName@@user-id: profile"
);
console.log(result2);
// {
//   messagePart: " profile",
//   placeholderName: "userName",
//   associatedMessageId: "user-id"
// }

// No placeholder metadata
const result3 = parsePlaceholder(" remaining", " remaining");
console.log(result3);
// { messagePart: " remaining" }

Compute Message ID

Computes a unique message ID hash from message string and meaning.

/**
 * Compute unique message ID from message string and meaning.
 * Uses same algorithm as Angular compiler for consistency.
 * 
 * @param messageString - The message text with placeholder syntax
 * @param meaning - Optional meaning to distinguish identical messages
 * @returns Computed message ID string
 */
function computeMsgId(messageString: string, meaning: string): string;

Usage Examples:

import { computeMsgId } from "@angular/localize";

// Simple message
const id1 = computeMsgId("Hello, World!", "");
console.log(id1); // "4286451273117902052"

// Message with placeholder
const id2 = computeMsgId("Hello, {$PH}!", "");
console.log(id2); // "2788806693285683417"

// Same message, different meaning
const id3 = computeMsgId("Home", "navigation");
const id4 = computeMsgId("Home", "location");
console.log(id3 !== id4); // true - different IDs

Split Block

Splits a message part into an optional metadata block and remaining text.

/**
 * Split message part into optional delimited block and remaining text.
 * Blocks are delimited by colon characters at start and end.
 * Handles escaped colons (\:) that should not be treated as delimiters.
 * 
 * @param cooked - Cooked version of message part
 * @param raw - Raw version of message part
 * @returns Object with block text (if exists) and remaining text
 * @throws Error if block is unterminated
 */
function splitBlock(cooked: string, raw: string): {
  text: string;
  block?: string;
};

Usage Examples:

import { splitBlock } from "@angular/localize";

// With metadata block
const result1 = splitBlock(":meaning|desc@@id:Message text", ":meaning|desc@@id:Message text");
console.log(result1);
// {
//   block: "meaning|desc@@id",
//   text: "Message text"
// }

// No metadata block
const result2 = splitBlock("Simple message", "Simple message");
console.log(result2);
// { text: "Simple message" }

// Escaped colon (not a block)
const result3 = splitBlock("\\:Not a block", "\\:Not a block");
console.log(result3);
// { text: ":Not a block" }

Find End of Block

Finds the end of a metadata block by locating the first non-escaped colon.

/**
 * Find end of marked block indicated by first non-escaped colon.
 * Handles escaped colons in raw string that shouldn't terminate block.
 * 
 * @param cooked - Cooked string with escape sequences processed
 * @param raw - Raw string with escape sequences in place
 * @returns Index of end of block marker
 * @throws Error if block is unterminated
 */
function findEndOfBlock(cooked: string, raw: string): number;

Usage Example:

import { findEndOfBlock } from "@angular/localize";

// Find end of metadata block
const endIndex = findEndOfBlock(
  ":meaning|description@@id:message",
  ":meaning|description@@id:message"
);
console.log(endIndex); // 22 (index of second colon)

// With escaped colon inside block
const endIndex2 = findEndOfBlock(
  ":desc with \\: colon:message",
  ":desc with \\: colon:message"  
);
console.log(endIndex2); // 19 (index after escaped colon)

Translation Processing

Make Parsed Translation

Creates a ParsedTranslation from message parts and placeholder names.

/**
 * Create ParsedTranslation from messageParts and placeholderNames.
 * Used internally for converting translation strings to usable format.
 * 
 * @param messageParts - Message parts to appear in ParsedTranslation
 * @param placeholderNames - Names of placeholders between parts
 * @returns ParsedTranslation structure
 */
function makeParsedTranslation(
  messageParts: string[],
  placeholderNames: string[]
): ParsedTranslation;

interface ParsedTranslation extends MessageMetadata {
  messageParts: TemplateStringsArray;
  placeholderNames: string[];
}

Parse Translation

Parses a target message string into a ParsedTranslation structure.

/**
 * Parse messageParts and placeholderNames from target message.
 * Used by loadTranslations() to convert strings to translation format.
 * 
 * @param messageString - Target message with {$placeholder} syntax
 * @returns ParsedTranslation structure ready for use
 */
function parseTranslation(messageString: TargetMessage): ParsedTranslation;

type TargetMessage = string;

Usage Example:

import { parseTranslation } from "@angular/localize";

const translation = parseTranslation("¡Hola, {$PH}!");
console.log(translation);
// {
//   text: "¡Hola, {$PH}!",
//   messageParts: ["¡Hola, ", "!"],
//   placeholderNames: ["PH"]
// }

Make Template Object

Creates the specialized TemplateStringsArray passed to tagged template functions.

/**
 * Create specialized array passed to tagged-string tag functions.
 * Adds raw property containing unprocessed string parts.
 * 
 * @param cooked - Message parts with escape codes processed
 * @param raw - Message parts with escaped codes as-is
 * @returns TemplateStringsArray for tagged template usage
 */
function makeTemplateObject(cooked: string[], raw: string[]): TemplateStringsArray;

Error Handling

Missing Translation Error

Error thrown when a translation is not found for a message.

class MissingTranslationError extends Error {
  private readonly type = 'MissingTranslationError';
  constructor(readonly parsedMessage: ParsedMessage);
}

/**
 * Type guard to check if error is MissingTranslationError.
 * 
 * @param e - Error to check
 * @returns True if error is MissingTranslationError
 */
function isMissingTranslationError(e: any): e is MissingTranslationError;

Usage Example:

import { isMissingTranslationError } from "@angular/localize";

try {
  // Some translation operation
  translateMessage(messageParts, substitutions);
} catch (error) {
  if (isMissingTranslationError(error)) {
    console.log(`Missing translation for: ${error.parsedMessage.id}`);
    console.log(`Message text: ${error.parsedMessage.text}`);
  } else {
    throw error;
  }
}

Constants

Block Processing Constants

/** Character marking start and end of metadata blocks */
const BLOCK_MARKER = ':';

/** Separator between meaning and description in metadata */
const MEANING_SEPARATOR = '|';

/** Separator between description and custom ID in metadata */
const ID_SEPARATOR = '@@';

/** Marker separating legacy IDs from other metadata */
const LEGACY_ID_INDICATOR = '\u241F';