/** * Standard PO file headers */ interface Headers { "Project-Id-Version": string; "Report-Msgid-Bugs-To": string; "POT-Creation-Date": string; "PO-Revision-Date": string; "Last-Translator": string; Language: string; "Language-Team": string; "Content-Type": string; "Content-Transfer-Encoding": string; "Plural-Forms": string; [name: string]: string; } /** * Result of parsing the Plural-Forms header */ interface ParsedPluralForms { nplurals: string | undefined; plural: string | undefined; } /** * Options for creating a new Item */ interface CreateItemOptions { nplurals?: number | string; } /** * Represents a single translation entry in a PO file. */ interface PoItem { /** The source string to translate */ msgid: string; /** Message context for disambiguation */ msgctxt: string | null; /** Source file references (e.g., "src/app.ts:42") */ references: string[]; /** Plural form of the source string */ msgid_plural: string | null; /** Translated string(s). Multiple entries for plural forms. */ msgstr: string[]; /** Translator comments (lines starting with #) */ comments: string[]; /** Automatically extracted comments (lines starting with #.) */ extractedComments: string[]; /** Flags like "fuzzy", "no-wrap", etc. */ flags: Record; /** * Custom metadata as key-value pairs. * * Useful for tool integration, tracking translation sources, timestamps, etc. * Serialized as `#@ key: value` comments in PO files. * * @example * metadata: { * origin: "LLM", * modified: "2024-01-15", * confidence: "0.95" * } */ metadata: Record; /** Whether this entry is marked as obsolete (#~) */ obsolete: boolean; /** Number of plural forms for this item's language */ nplurals: number; } /** * Represents a complete PO (Portable Object) file. */ interface PoFile { /** Translator comments at the top of the file */ comments: string[]; /** Extracted comments at the top of the file */ extractedComments: string[]; /** PO file headers (Content-Type, Language, etc.) */ headers: Partial; /** Order of headers as they appeared in the source file */ headerOrder: string[]; /** Translation entries */ items: PoItem[]; } /** * Internal parser state during PO file parsing */ interface ParserState { item: PoItem; context: "msgid" | "msgid_plural" | "msgstr" | "msgctxt" | null; plural: number; obsoleteCount: number; noCommentLineCount: number; } /** * Tracks which Intl formatters are used during compilation. * Used for generating formatter declarations in static code output. */ interface FormatterUsage { number: Set; date: Set; time: Set; list: Set; ago: Set; name: Set; } /** * Options for serializing PO files. * * These options control the output format when converting a PoFile object * back to a string. The defaults are optimized for compatibility with * translation platforms like Crowdin. */ interface SerializeOptions { /** * Maximum line width before folding long strings. * * When a string exceeds this length, it will be split across multiple lines. * Set to 0 to disable folding (strings will only break on actual newlines). * * @default 80 * * @example * // With foldLength: 40 * msgid "This is a long string that will be " * "folded across multiple lines" * * // With foldLength: 0 * msgid "This is a long string that stays on one line" */ foldLength?: number; /** * Use compact format for multiline strings. * * When true (default), multiline strings start with content on the first line: * ```po * msgid "First line\n" * "Second line" * ``` * * When false, uses GNU gettext's traditional format with an empty first line: * ```po * msgid "" * "First line\n" * "Second line" * ``` * * The compact format is recommended as it's compatible with translation * platforms like Crowdin that may strip empty first lines, avoiding * unnecessary diffs. Both formats are valid PO syntax. * * @default true * * @see https://github.com/lingui/js-lingui/issues/2235 */ compactMultiline?: boolean; } /** * Creates a new empty PO file structure with default headers. */ declare function createPoFile(): PoFile; /** * Parses a PO file string into a PoFile structure. */ declare function parsePo(data: string): PoFile; /** * Serializes a PoFile structure to a string. * * Accepts partial input - missing fields default to empty arrays/objects. * * @param po - The PO file structure to serialize (can be partial) * @param options - Serialization options for controlling output format * * @example * // Default: compact format, 80 char fold length (Crowdin-compatible) * const output = stringifyPo(po) * * @example * // Partial input - only headers and items required * const output = stringifyPo({ headers: myHeaders, items: myItems }) * * @example * // GNU gettext traditional format * const output = stringifyPo(po, { compactMultiline: false }) * * @example * // No line folding * const output = stringifyPo(po, { foldLength: 0 }) */ declare function stringifyPo(po: Partial, options?: SerializeOptions): string; /** * Creates a new translation item with default values. */ declare function createItem(options?: CreateItemOptions): PoItem; /** * Serializes an item to PO file format. * * @param item - The translation item to serialize * @param options - Serialization options for controlling output format */ declare function stringifyItem(item: PoItem, options?: SerializeOptions): string; /** * Default headers helper for creating PO files. */ /** * Options for creating default PO file headers. */ interface CreateHeadersOptions { /** * Target language code (e.g., "de", "fr", "en-US") */ language?: string; /** * Generator tool name * @default "pofile-ts" */ generator?: string; /** * Project name and version * @default "" */ projectIdVersion?: string; /** * Email for reporting msgid bugs * @default "" */ reportBugsTo?: string; /** * Translator name and email * @default "" */ lastTranslator?: string; /** * Translation team name * @default "" */ languageTeam?: string; /** * Plural forms expression (e.g., "nplurals=2; plural=(n != 1);") * If not provided but language is set, auto-generates from CLDR. * Set to `false` to explicitly omit the header. */ pluralForms?: string | false; /** * Custom headers to add or override */ custom?: Record; } /** * Formats a date in PO file format: "YYYY-MM-DD HH:MM+ZZZZ" * * @example * formatPoDate(new Date("2025-12-11T14:30:00+01:00")) * // → "2025-12-11 14:30+0100" */ declare function formatPoDate(date: Date): string; /** * Generates a Plural-Forms header string for a locale. * * Uses CLDR data via Intl.PluralRules to determine nplurals. * The plural expression is a simple fallback - for accurate runtime * plural selection, use `getPluralFunction(locale)` instead. * * @example * getPluralFormsHeader("de") // → "nplurals=2; plural=(n != 1);" * getPluralFormsHeader("pl") // → "nplurals=4; plural=(n != 1);" * getPluralFormsHeader("ar") // → "nplurals=6; plural=(n != 1);" */ declare function getPluralFormsHeader(language: string): string; /** * Creates default PO file headers with sensible defaults. * * If `language` is provided and `pluralForms` is not explicitly set, * automatically generates Plural-Forms from CLDR data. * * @example * const headers = createDefaultHeaders({ * language: "de", * generator: "my-tool", * }) * // → includes "Plural-Forms: nplurals=2; plural=(n != 1);" */ declare function createDefaultHeaders(options?: CreateHeadersOptions): Partial; /** * Utilities for parsing and formatting PO file references. * * References in PO files use the format: file:line * Example: "src/App.tsx:42" */ /** * A parsed source reference. */ interface SourceReference { /** File path (always uses forward slashes) */ file: string; /** Line number (optional) */ line?: number; } /** * Options for formatting references. */ interface FormatReferenceOptions { /** * Include line numbers in the output. * @default true */ includeLineNumbers?: boolean; } /** * Parses a PO file reference string into its components. * * Parses from right to find the line number, handling edge cases like * colons in file paths. * * @throws Error if the reference format is invalid * * @example * parseReference("src/App.tsx:42") * // → { file: "src/App.tsx", line: 42 } * * parseReference("src/App.tsx") * // → { file: "src/App.tsx" } */ declare function parseReference(reference: string): SourceReference; /** * Formats a source reference back to a string. * * @example * formatReference({ file: "src/App.tsx", line: 42 }) * // → "src/App.tsx:42" * * formatReference({ file: "src/App.tsx" }) * // → "src/App.tsx" * * formatReference({ file: "src/App.tsx", line: 42 }, { includeLineNumbers: false }) * // → "src/App.tsx" */ declare function formatReference(ref: SourceReference, options?: FormatReferenceOptions): string; /** * Normalizes a file path to use forward slashes (Unix-style). * * Always converts backslashes to forward slashes, regardless of platform. * This ensures consistent output in PO files. * * @example * normalizeFilePath("src\\components\\App.tsx") * // → "src/components/App.tsx" */ declare function normalizeFilePath(filePath: string): string; /** * Parses multiple references from a single string. * * References can be separated by spaces or commas. * * @throws Error if any reference format is invalid * * @example * parseReferences("src/App.tsx:42 src/utils.ts:10") * // → [{ file: "src/App.tsx", line: 42 }, { file: "src/utils.ts", line: 10 }] */ declare function parseReferences(references: string): SourceReference[]; /** * Formats multiple references to a string. * * @example * formatReferences([ * { file: "src/App.tsx", line: 42 }, * { file: "src/utils.ts", line: 10 } * ]) * // → "src/App.tsx:42 src/utils.ts:10" */ declare function formatReferences(refs: SourceReference[], options?: FormatReferenceOptions): string; /** * Creates a reference from a file path and optional line number. * * Validates that the path is relative and normalizes it. * * @throws Error if the path is absolute * * @example * createReference("src/App.tsx", 42) * // → { file: "src/App.tsx", line: 42 } */ declare function createReference(file: string, line?: number): SourceReference; /** * Catalog conversion helpers for working with simple key-value formats. * * Provides utilities to convert between a simple catalog format and PO items. */ /** * A single entry in the catalog. */ interface CatalogEntry { /** * The source message (msgid content). * Used when the catalog key is a generated ID rather than the source text. */ message?: string; /** * The translated string(s). * Use an array for plural forms: [singular, plural, ...] * Optional for extraction workflows where translations don't exist yet. */ translation?: string | string[]; /** * Source string for plural forms (msgid_plural). * Required when translation is an array. */ pluralSource?: string; /** * Message context for disambiguation (msgctxt). */ context?: string; /** * Translator comments. */ comments?: string[]; /** * Extracted comments (from source code). */ extractedComments?: string[]; /** * Source file references. */ origins?: SourceReference[]; /** * Whether this entry is obsolete. */ obsolete?: boolean; /** * Flags like "fuzzy". */ flags?: Record; } /** * A catalog is a record of message IDs to their entries. */ type Catalog = Record; /** * Options for converting catalog to items. */ interface CatalogToItemsOptions { /** * Include source references in the output. * @default true */ includeOrigins?: boolean; /** * Include line numbers in references. * @default true */ includeLineNumbers?: boolean; /** * Number of plural forms for the target language. * @default 2 */ nplurals?: number; } /** * Options for converting items to catalog. */ interface ItemsToCatalogOptions { /** * Use msgid as the catalog key (true) or use a custom key generator (false). * @default true */ useMsgidAsKey?: boolean; /** * Custom function to generate catalog keys from items. * Only used when useMsgidAsKey is false. */ keyGenerator?: (item: PoItem) => string; /** * Include origins in the catalog entries. * @default true */ includeOrigins?: boolean; } /** * Converts a catalog to PO items. * * @example * const items = catalogToItems({ * "Hello": { translation: "Hallo" }, * "greeting": { * message: "Hello {name}", * translation: "Hallo {name}", * context: "informal" * }, * "{count} item": { * translation: ["{count} Element", "{count} Elemente"], * pluralSource: "{count} items" * } * }) */ declare function catalogToItems(catalog: Catalog, options?: CatalogToItemsOptions): PoItem[]; /** * Converts PO items to a catalog. * * @example * const catalog = itemsToCatalog(items) * // → { "Hello": { translation: "Hallo", ... } } */ declare function itemsToCatalog(items: PoItem[], options?: ItemsToCatalogOptions): Catalog; /** * Merges two catalogs, with the second catalog taking precedence. * * Useful for merging extracted messages with existing translations. * * @example * const merged = mergeCatalogs(existingCatalog, newCatalog) */ declare function mergeCatalogs(base: Catalog, updates: Catalog): Catalog; /** * ICU Message Compiler * * Compiles ICU MessageFormat strings into executable JavaScript functions. * The compiled functions take a values object and return the formatted string. * * Features: * - Variables: {name} → values.name * - Plurals: {count, plural, one {# item} other {# items}} → CLDR plural rules * - Select: {gender, select, male {He} female {She} other {They}} * - Number/Date/Time: {n, number, percent}, {d, date, medium} → Intl formatters * - Tags: text → values.bold(children) for JSX support * * @example * const fn = compileIcu("{count, plural, one {# item} other {# items}}", { locale: "en" }) * fn({ count: 5 }) // → "5 items" */ /** * Options for compiling ICU messages. */ interface CompileIcuOptions { /** Locale for plural rules and Intl formatting */ locale: string; /** * Whether to throw on parse errors. * If false, returns a function that returns the original message. * @default true */ strict?: boolean; /** * Custom number format styles. * Keys are style names used in messages, values are Intl.NumberFormat options. * @example * numberStyles: { * bytes: { style: "unit", unit: "byte", unitDisplay: "narrow" }, * percent2: { style: "percent", minimumFractionDigits: 2 } * } * // Usage: {size, number, bytes} */ numberStyles?: Record; /** * Custom date format styles. * Keys are style names used in messages, values are Intl.DateTimeFormat options. * @example * dateStyles: { * monthYear: { month: "long", year: "numeric" }, * iso: { year: "numeric", month: "2-digit", day: "2-digit" } * } * // Usage: {d, date, monthYear} */ dateStyles?: Record; /** * Custom time format styles. * Keys are style names used in messages, values are Intl.DateTimeFormat options. * @example * timeStyles: { * precise: { hour: "2-digit", minute: "2-digit", second: "2-digit" }, * hourOnly: { hour: "numeric" } * } * // Usage: {t, time, precise} */ timeStyles?: Record; /** * Custom list format styles. * Keys are style names used in messages, values are Intl.ListFormat options. * @example * listStyles: { * narrow: { type: "conjunction", style: "narrow" }, * or: { type: "disjunction" } * } * // Usage: {items, list, narrow} */ listStyles?: Record; } /** * Values that can be passed to a compiled message function. */ type MessageValues = Record; /** * Return type of a compiled message function. * - string: when no tags are used * - (string | unknown)[]: when tags are used (for JSX support) */ type MessageResult = string | readonly unknown[]; /** * A compiled message function. */ type CompiledMessageFunction = (values?: MessageValues) => MessageResult; declare function compileIcu(message: string, options: CompileIcuOptions): CompiledMessageFunction; /** * Creates a pre-configured ICU compiler with custom styles. * * Use this factory when you want to define format styles once and reuse them * across your application. This avoids passing the same options to every * `compileIcu` call. * * @example * // Define once in your i18n config * export const compile = createIcuCompiler({ * locale: "de", * numberStyles: { * bytes: { style: "unit", unit: "byte", unitDisplay: "narrow" }, * filesize: { style: "unit", unit: "kilobyte", unitDisplay: "short" } * }, * dateStyles: { * iso: { year: "numeric", month: "2-digit", day: "2-digit" } * } * }) * * // Use everywhere * const msg = compile("{size, number, bytes}") * msg({ size: 1024 }) // → "1,024B" */ declare function createIcuCompiler(options: CompileIcuOptions): (message: string) => CompiledMessageFunction; /** * Catalog Compiler * * Compiles a PO catalog into optimized message functions. * Each message is compiled to a function that takes values and returns the formatted string. * * Uses messageId (8-char hash) as keys for minimal bundle size. * * @example * const po = PO.parse(poFileContent) * const catalog = itemsToCatalog(po.items) * const compiled = compileCatalog(catalog, { locale: "de" }) * * compiled.format("Xk9mLp", { name: "Sebastian" }) * // → "Hallo Sebastian!" */ /** * Options for compiling a catalog. */ interface CompileCatalogOptions { /** Locale for plural rules and Intl formatting */ locale: string; /** * Whether to use messageId (hash) as key. * If false, uses msgid as key. * @default true */ useMessageId?: boolean; /** * Whether to throw on parse errors. * If false, invalid messages return the original text. * @default false */ strict?: boolean; } /** * A compiled catalog with message lookup and formatting. */ interface CompiledCatalog { /** * Get a compiled message function by key (messageId or msgid). */ get(key: string): CompiledMessageFunction | undefined; /** * Format a message with values. * Returns the formatted string, or the key if not found. */ format(key: string, values?: MessageValues): MessageResult; /** * Check if a message exists. */ has(key: string): boolean; /** * Get all message keys. */ keys(): string[]; /** * Number of compiled messages. */ readonly size: number; /** * The locale this catalog was compiled for. */ readonly locale: string; } /** * Compiles a catalog into optimized message functions. * * @example * const compiled = compileCatalog(catalog, { locale: "de" }) * compiled.format("Xk9mLp", { name: "World" }) // → "Hallo World!" */ declare function compileCatalog(catalog: Catalog, options: CompileCatalogOptions): CompiledCatalog; /** * Options for generating compiled code. */ interface GenerateCodeOptions { /** Locale for plural rules and Intl formatting */ locale: string; /** * Whether to use messageId (hash) as key. * @default true */ useMessageId?: boolean; /** * Export name for the messages object. * @default "messages" */ exportName?: string; /** * Whether to generate TypeScript or JavaScript. * @default "typescript" */ format?: "typescript" | "javascript"; /** * Whether to include source comments with original msgid. * @default false */ includeSourceComments?: boolean; } /** * Generates JavaScript/TypeScript code for a compiled catalog. * * This can be used in build pipelines to generate static message files * that don't require runtime ICU parsing. * * @example * const code = generateCompiledCode(catalog, { locale: "de" }) * // Write to file: messages.de.ts * * // Generated code: * // const _nf = new Intl.NumberFormat("de") * // export const messages = { * // "Xk9mLp": (v) => `Hallo ${v?.name ?? "{name}"}!`, * // ... * // } */ declare function generateCompiledCode(catalog: Catalog, options: GenerateCodeOptions): string; /** * Message ID generation utilities. * * Provides functions to generate stable, content-based message IDs * from source strings. Uses SHA-256 hashing with Base64URL encoding * for compact, URL-safe identifiers. */ /** * Generates a message ID from content using SHA-256. * * This is an async function that works in both Node.js and browser environments. * The generated ID is an 8-character Base64URL string derived from the SHA-256 hash. * * @param message - The source message text * @param context - Optional message context for disambiguation * @returns An 8-character Base64URL ID * * @example * const id = await generateMessageId("Hello {name}") * // → "Kj9xMnPq" * * const idWithContext = await generateMessageId("Open", "menu.file") * // → "Xp2wLmNr" */ declare function generateMessageId(message: string, context?: string): Promise; /** * Generates a message ID synchronously (Node.js only). * * This function uses Node.js's crypto module and will not work in browsers. * Use `generateMessageId` for isomorphic code. * * @param message - The source message text * @param context - Optional message context for disambiguation * @returns An 8-character Base64URL ID * * @example * const id = generateMessageIdSync("Hello {name}") * // → "Kj9xMnPq" */ declare function generateMessageIdSync(message: string, context?: string): string; /** * Options for batch message ID generation. */ interface GenerateIdsOptions { /** * Whether to include the original message in the result. * @default false */ includeMessage?: boolean; } /** * Generates message IDs for multiple messages. * * @param messages - Array of messages (strings or { message, context } objects) * @returns Map of input to generated ID * * @example * const ids = await generateMessageIds([ * "Hello", * { message: "Open", context: "menu.file" } * ]) * // → Map { "Hello" => "a1b2c3", "Open" => "d4e5f6" } */ declare function generateMessageIds(messages: (string | { message: string; context?: string; })[]): Promise>; /** * CLDR Plural Categories and Locale Mappings * * Uses native Intl.PluralRules for plural selection. * * @see https://cldr.unicode.org/index/cldr-spec/plural-rules * @see https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html */ /** * Parses the Plural-Forms header value from a PO file. * Example: "nplurals=2; plural=(n != 1);" * * Note: The plural expression is a legacy Gettext format. * For runtime plural selection, use `getPluralFunction(locale)` instead. */ declare function parsePluralForms(pluralFormsString: string | undefined): ParsedPluralForms; /** * Returns the CLDR plural categories for a locale. * Uses native Intl.PluralRules for accurate, up-to-date CLDR data. * Categories are sorted in canonical CLDR order for consistency across ICU versions. * * @example * getPluralCategories("de") // → ["one", "other"] * getPluralCategories("pl") // → ["one", "few", "many", "other"] * getPluralCategories("ar") // → ["zero", "one", "two", "few", "many", "other"] */ declare function getPluralCategories(locale: string): readonly string[]; /** * Returns the number of plural forms for a locale. * * @example * getPluralCount("de") // → 2 * getPluralCount("pl") // → 4 * getPluralCount("ar") // → 6 */ declare function getPluralCount(locale: string): number; /** * Returns the plural selector function for a locale. * Uses native Intl.PluralRules for CLDR-compliant selection. * * @example * const selectPlural = getPluralFunction("de") * selectPlural(1) // → 0 (one) * selectPlural(5) // → 1 (other) */ declare function getPluralFunction(locale: string): (n: number) => number; /** * Comment processing utilities. * * Provides helpers for handling comments in PO files, * particularly for integration with build tools that extract * comments from source code. */ /** * Splits multiline comments into individual lines. * * Source code comments often contain newlines, but PO format expects * one comment per line. This helper normalizes comments for PO output. * * Features: * - Splits on newlines (\n, \r\n, \r) * - Trims whitespace from each line * - Filters out empty lines * - Flattens arrays (handles both single strings and arrays) * * @example * // Split a multiline comment * splitMultilineComments(["Line1\nLine2", "Line3"]) * // → ["Line1", "Line2", "Line3"] * * @example * // Handles whitespace * splitMultilineComments([" Line1\n Line2 "]) * // → ["Line1", "Line2"] * * @example * // Windows line endings * splitMultilineComments(["First\r\nSecond"]) * // → ["First", "Second"] * * @example * // Empty lines are filtered out * splitMultilineComments(["Line1\n\n\nLine2"]) * // → ["Line1", "Line2"] * * @example * // Single-line comments pass through unchanged * splitMultilineComments(["Simple comment"]) * // → ["Simple comment"] */ declare function splitMultilineComments(comments: string[]): string[]; /** * ICU MessageFormat AST types. * * Style types use Intl APIs directly where available (via ESNext.Intl lib). */ /** * Duration format style. * Note: Intl.DurationFormat is Baseline 2025 but not yet in TypeScript's lib. */ type IcuDurationStyle = "long" | "short" | "narrow" | "digital"; /** * Node types in the ICU MessageFormat AST. */ type IcuNodeType = "literal" | "argument" | "number" | "date" | "time" | "list" | "duration" | "ago" | "name" | "select" | "plural" | "pound" | "tag"; /** * Source location in the message string. */ interface IcuLocation { start: IcuPosition; end: IcuPosition; } interface IcuPosition { /** Offset in UTF-16 code units */ offset: number; /** 1-based line number */ line: number; /** 1-based column (in Unicode code points) */ column: number; } /** * Base interface for all AST nodes. */ interface IcuNodeBase { type: IcuNodeType; } /** * Literal text node. */ interface IcuLiteralNode extends IcuNodeBase { type: "literal"; value: string; } /** * Simple argument: {name} */ interface IcuArgumentNode extends IcuNodeBase { type: "argument"; value: string; } /** * Number format: {n, number} or {n, number, style} * * Style can be a named format (e.g. "currency", "percent") or a skeleton * (e.g. "::currency/EUR"). The parser treats both as opaque strings - * interpretation is up to the runtime. */ interface IcuNumberNode extends IcuNodeBase { type: "number"; value: string; style: string | null; } /** * Date format: {d, date} or {d, date, style} * * Style can be a named format (e.g. "short", "medium", "tablecell") or * a skeleton (e.g. "::yyyyMMdd"). The parser treats both as opaque strings. */ interface IcuDateNode extends IcuNodeBase { type: "date"; value: string; style: string | null; } /** * Time format: {t, time} or {t, time, style} * * Style can be a named format or skeleton. The parser treats both as opaque strings. */ interface IcuTimeNode extends IcuNodeBase { type: "time"; value: string; style: string | null; } /** * List format: {items, list} or {items, list, type} * * Formats arrays using Intl.ListFormat. * * @example * {items, list} → "Alice, Bob, and Charlie" * {items, list, disjunction} → "Alice, Bob, or Charlie" * {items, list, unit} → "Alice, Bob, Charlie" */ interface IcuListNode extends IcuNodeBase { type: "list"; value: string; /** List format type. Default: "conjunction" */ style: Intl.ListFormatType | null; } /** * Duration format: {d, duration} or {d, duration, style} * * Formats duration objects using Intl.DurationFormat. * * @example * {time, duration} → "2 hours, 30 minutes" * {time, duration, short} → "2 hr, 30 min" * {time, duration, narrow} → "2h 30m" */ interface IcuDurationNode extends IcuNodeBase { type: "duration"; value: string; /** Duration format style. Default: "long" */ style: IcuDurationStyle | null; } /** * Relative time format: {val, ago, unit} or {val, ago, unit style} * * Formats relative time using Intl.RelativeTimeFormat. * * @example * {days, ago, day} → "in 3 days" or "3 days ago" * {hours, ago, hour short} → "in 2 hr." */ interface IcuAgoNode extends IcuNodeBase { type: "ago"; value: string; /** Unit and optional style, e.g. "day", "hour short" */ style: IcuAgoStyle | null; } /** * Combined unit and style for ago format. * Format: "unit" or "unit style" (e.g. "day", "hour short") */ type IcuAgoStyle = Intl.RelativeTimeFormatUnit | `${Intl.RelativeTimeFormatUnit} ${Intl.RelativeTimeFormatStyle}`; /** * Display names format: {code, name, type} * * Formats codes to localized display names using Intl.DisplayNames. * * @example * {lang, name, language} → "English" (for "en") * {country, name, region} → "Germany" (for "DE") * {cur, name, currency} → "Euro" (for "EUR") */ interface IcuNameNode extends IcuNodeBase { type: "name"; value: string; /** Display names type. Default: "language" */ style: Intl.DisplayNamesType | null; } /** * Plural or selectordinal argument. */ interface IcuPluralNode extends IcuNodeBase { type: "plural"; value: string; options: Record; offset: number; pluralType: "cardinal" | "ordinal"; } /** * Select argument. */ interface IcuSelectNode extends IcuNodeBase { type: "select"; value: string; options: Record; } /** * Option in a plural/select. */ interface IcuPluralOption { value: IcuNode[]; } interface IcuSelectOption { value: IcuNode[]; } /** * The # symbol in plural, replaced with the count. */ interface IcuPoundNode extends IcuNodeBase { type: "pound"; } /** * XML-like tag: content */ interface IcuTagNode extends IcuNodeBase { type: "tag"; value: string; children: IcuNode[]; } /** * Union of all ICU AST node types. */ type IcuNode = IcuLiteralNode | IcuArgumentNode | IcuNumberNode | IcuDateNode | IcuTimeNode | IcuListNode | IcuDurationNode | IcuAgoNode | IcuNameNode | IcuSelectNode | IcuPluralNode | IcuPoundNode | IcuTagNode; /** * Parser options. */ interface IcuParserOptions { /** * Whether to treat HTML/XML tags as literal text. * When true, `text` is parsed as a literal string. * When false (default), it's parsed as a tag node. * @default false */ ignoreTag?: boolean; /** * Whether select/plural must have an 'other' clause. * @default true */ requiresOtherClause?: boolean; } /** * Parser error kinds. */ type IcuErrorKind = "SYNTAX_ERROR"; /** * Parser error. */ interface IcuParseError { kind: IcuErrorKind; message: string; location: IcuLocation; } /** * Parser result - either success with AST or error. */ type IcuParseResult = { success: true; ast: IcuNode[]; errors: []; } | { success: false; ast: null; errors: IcuParseError[]; }; /** * ICU MessageFormat v1 Parser. * * A minimal, zero-dependency parser for ICU MessageFormat strings. * Optimized for small bundle size (~3kb gzipped). * * Supported syntax: * - Simple arguments: {name} * - Formatted: {n, number}, {d, date, short}, {t, time, medium} * - Skeletons: {n, number, ::currency/EUR} (as opaque string) * - Plural: {n, plural, offset:1 =0 {...} one {...} other {...}} * - Select: {gender, select, male {...} female {...} other {...}} * - Selectordinal: {n, selectordinal, one {#st} two {#nd} ...} * - Tags: bold, <0>numbered * - Escaping: '' → literal ', '{text}' → literal text * * Extended format types (pofile-ts extensions): * - List: {items, list}, {items, list, disjunction} * - Duration: {d, duration}, {d, duration, short} * - Ago: {n, ago, day}, {n, ago, hour short} * - Name: {code, name, language}, {code, name, region} * * Trade-offs for bundle size / complexity: * - Modern JS only (no IE11 polyfills) * - No location tracking (typical messages are single-line anyway) * - Styles/skeletons stored as opaque strings (runtime handles interpretation) * - Quoting only escapes ICU special chars ({, }, <, >, #), not arbitrary text * * @see https://unicode-org.github.io/icu/userguide/format_parse/messages/ */ /** * ICU syntax error thrown during parsing. */ declare class IcuSyntaxError extends Error { readonly offset: number; constructor(message: string, offset: number); } /** * ICU MessageFormat Parser. */ declare class IcuParser { private pos; private readonly msg; private readonly ignoreTag; private readonly requiresOther; constructor(message: string, options?: IcuParserOptions); parse(): IcuNode[]; private parseMessage; private parseArgument; private parseFormattedArg; private parsePlural; private parseSelect; private parsePluralOptions; private parseSelectOptions; private parseTag; private parseLiteral; private parseStyle; private parseIdentifier; private parseTagName; private parseInteger; private skipWhitespace; /** Lookahead for identifier without consuming input */ private peekIdentifier; private expectChar; private error; } /** * Parse an ICU MessageFormat string. * * @example * const result = parseIcu("Hello {name}!") * if (result.success) { * console.log(result.ast) * } * * @example * const result = parseIcu("{count, plural, one {# item} other {# items}}") */ declare function parseIcu(message: string, options?: IcuParserOptions): IcuParseResult; /** * ICU MessageFormat conversion utilities. * * Converts between Gettext plural format and ICU MessageFormat. */ /** * Options for Gettext to ICU conversion. */ interface GettextToIcuOptions { /** * Target locale for determining plural categories. * Required to map msgstr indices to ICU plural keywords. */ locale: string; /** * Variable name to use in the ICU plural expression. * @default "count" */ pluralVariable?: string; /** * Replace `#` with the explicit variable reference `{varname}`. * Makes translations more readable in TMS tools. * @default true */ expandOctothorpe?: boolean; } /** * Options for converting an entire PO file to ICU format. */ interface NormalizeToIcuOptions extends GettextToIcuOptions { /** * Whether to modify items in-place or return copies. * @default false */ inPlace?: boolean; } /** * Converts a Gettext plural item to ICU MessageFormat. * * @example * const item = { * msgid: "One item", * msgid_plural: "{count} items", * msgstr: ["Ein Artikel", "{count} Artikel"] * } * * gettextToIcu(item, { locale: "de" }) * // → "{count, plural, one {Ein Artikel} other {{count} Artikel}}" * * @example * // Polish with 4 plural forms * const plItem = { * msgid: "One file", * msgid_plural: "{count} files", * msgstr: ["plik", "pliki", "plików", "pliki"] * } * * gettextToIcu(plItem, { locale: "pl" }) * // → "{count, plural, one {plik} few {pliki} many {plików} other {pliki}}" */ declare function gettextToIcu(item: PoItem, options: GettextToIcuOptions): string | null; /** * Checks if an item is a plural item (has msgid_plural). */ declare function isPluralItem(item: PoItem): boolean; /** * Normalizes a plural item to ICU format in-place. * The ICU string is stored in msgstr[0], and msgid_plural is cleared. * * @returns true if the item was converted, false otherwise */ declare function normalizeItemToIcu(item: PoItem, options: GettextToIcuOptions): boolean; /** * Normalizes all plural items in a PO file to ICU format. * * @example * const po = parsePo(content) * const normalized = normalizeToIcu(po, { locale: "de" }) * * // All plural items now have ICU in msgstr[0] * normalized.items[0].msgstr[0] * // → "{count, plural, one {Ein Artikel} other {{count} Artikel}}" */ declare function normalizeToIcu(po: PoFile, options: NormalizeToIcuOptions): PoFile; interface IcuToGettextOptions { /** * Replace `#` with the explicit variable reference `{varname}`. * Makes source strings more readable. * @default true */ expandOctothorpe?: boolean; } /** * Converts ICU plural back to source msgid/msgid_plural. * Extracts the first and last plural cases. * * @example * const icu = "{count, plural, one {# item} other {# items}}" * icuToGettextSource(icu) * // → { msgid: "{count} item", msgid_plural: "{count} items", pluralVariable: "count" } * * icuToGettextSource(icu, { expandOctothorpe: false }) * // → { msgid: "# item", msgid_plural: "# items", pluralVariable: "count" } */ declare function icuToGettextSource(icu: string, options?: IcuToGettextOptions): { msgid: string; msgid_plural: string; pluralVariable: string; } | null; /** * ICU MessageFormat utility functions. * * Convenience APIs for working with ICU messages. */ /** * Information about a variable in an ICU message. */ interface IcuVariable { /** Variable name */ name: string; /** Variable type: argument, number, date, time, plural, select */ type: "argument" | "number" | "date" | "time" | "plural" | "select"; /** Format style (for number/date/time) */ style?: string; } /** * Validation result for an ICU message. */ interface IcuValidationResult { /** Whether the message is valid */ valid: boolean; /** Validation errors (if any) */ errors: IcuParseError[]; } /** * Comparison result between source and translation variables. */ interface IcuVariableComparison { /** Variables in source but missing in translation */ missing: string[]; /** Variables in translation but not in source */ extra: string[]; /** Whether the variables match exactly */ isMatch: boolean; } /** * Extract all variable names from an ICU message. * * @example * extractVariables("Hello {name}, you have {count, plural, one {# msg} other {# msgs}}") * // → ["name", "count"] * * @example * extractVariables("{date, date, short} at {time, time}") * // → ["date", "time"] */ declare function extractVariables(message: string): string[]; /** * Extract variable information from an ICU message. * Returns detailed info about each variable including type and style. * * @example * extractVariableInfo("{price, number, currency}") * // → [{ name: "price", type: "number", style: "currency" }] */ declare function extractVariableInfo(message: string): IcuVariable[]; /** * Validate an ICU message string. * * @example * validateIcu("{count, plural, one {#} other {#}}") * // → { valid: true, errors: [] } * * @example * validateIcu("{unclosed") * // → { valid: false, errors: [{ kind: "EXPECT_ARGUMENT_CLOSING_BRACE", ... }] } */ declare function validateIcu(message: string, options?: IcuParserOptions): IcuValidationResult; /** * Compare variables between source and translation messages. * Useful for detecting missing or extra placeholders in translations. * * @example * compareVariables( * "Hello {name}, you have {count} messages", * "Hallo {name}, du hast {count} Nachrichten" * ) * // → { missing: [], extra: [], isMatch: true } * * @example * compareVariables( * "Hello {name}", * "Hallo {userName}" * ) * // → { missing: ["name"], extra: ["userName"], isMatch: false } */ declare function compareVariables(source: string, translation: string): IcuVariableComparison; /** * Check if a message contains ICU plural syntax (cardinal or ordinal). */ declare function hasPlural(message: string): boolean; /** * Check if a message contains ICU selectordinal syntax. * Note: selectordinal is internally stored as a plural node with pluralType: "ordinal". */ declare function hasSelectOrdinal(message: string): boolean; /** * Check if a message contains ICU select syntax. */ declare function hasSelect(message: string): boolean; /** * Check if a message contains any ICU syntax (variables, plural, select, etc.). * Returns false for plain text. */ declare function hasIcuSyntax(message: string): boolean; /** Default serialization options */ declare const DEFAULT_SERIALIZE_OPTIONS: Required; /** * Folds a string into multiple lines at word boundaries. * * @param text - The text to fold (already escaped) * @param maxLength - Maximum line length * @returns Array of folded line segments */ declare function foldLine(text: string, maxLength: number): string[]; /** * Formats a keyword and text into PO file lines. * * Handles: * 1. Multiline strings (containing \n characters) * 2. Long strings that need folding (when foldLength > 0) * 3. Compact vs traditional GNU gettext format * * @param keyword - The PO keyword (msgid, msgstr, etc.) * @param text - The text value * @param index - Optional plural index * @param options - Serialization options */ declare function formatKeyword(keyword: string, text: string, index?: number, options?: SerializeOptions): string[]; /** * Escapes special characters in a string for PO file format. * Handles bell, backspace, tab, vertical tab, form feed, carriage return, * double quotes, and backslashes. */ declare function escapeString(str: string): string; /** * Unescapes C-style escape sequences in a string. * Handles: \a \b \t \n \v \f \r \' \" \\ \? and octal/hex escapes. * Octal escapes can be 1-3 digits (e.g., \0, \77, \123). */ declare function unescapeString(str: string): string; /** * Extracts the string value from a PO line. * Removes the keyword prefix and surrounding quotes, then unescapes. */ declare function extractString(line: string): string; /** * Splits PO file content into header section and body lines. */ declare function splitHeaderAndBody(data: string): { headerSection: string; bodyLines: string[]; }; /** * Parses the header section and populates the PO file. */ declare function parseHeaders(headerSection: string, po: PoFile): void; /** * Parses item lines and populates the PO file. */ declare function parseItems(lines: string[], po: PoFile, nplurals: string | undefined): void; /** * Code Generation Utilities * * Internal module for generating JavaScript code from ICU AST. */ /** * Context for code generation. */ interface CodeGenContext { locale: string; formatters: FormatterUsage; pluralCategories: readonly string[]; /** Current plural variable for # substitution */ pluralVar: string | null; /** Current plural offset */ pluralOffset: number; needsPluralFn: boolean; hasTags: boolean; } /** * Result of generating message code. */ interface MessageCodeResult { code: string; formatters: FormatterUsage; needsPluralFn: boolean; hasTags: boolean; } /** * Creates a new code generation context. */ declare function createCodeGenContext(locale: string, pluralCategories: readonly string[]): CodeGenContext; /** * Generates code for an array of nodes. * Returns a template literal string for simple cases, or an array for JSX. */ declare function generateNodesCode(nodes: IcuNode[], ctx: CodeGenContext): string; /** * Generates code for a single node. */ declare function generateNodeCode(node: IcuNode, ctx: CodeGenContext): string; /** * Makes a variable name safe for use in generated code. */ declare function safeVarName(name: string): string; /** * Sanitizes a style string for use as a variable name suffix. */ declare function sanitizeStyle(style: string): string; /** * Escapes a string for use inside a template literal. */ declare function escapeTemplateString(str: string): string; /** * Escapes a string for use in a comment. */ declare function escapeComment(str: string): string; /** * Extracts the plural variable name from msgid or msgid_plural. * Looks for {varName} patterns and returns the first one found. * * @example * extractPluralVariable("{count} item", "{count} items") // → "count" * extractPluralVariable("One item", "{n} items") // → "n" * extractPluralVariable("One item", "Many items") // → null (use default) */ declare function extractPluralVariable(msgid: string, pluralSource?: string): string | null; /** * Gets Intl.NumberFormat options for a style. * Note: "currency" without skeleton is handled dynamically via _nf_currency(). */ declare function getNumberOptionsForStyle(style: string): Intl.NumberFormatOptions; /** * Generates the plural function code for a locale. * Uses native Intl.PluralRules for accurate CLDR-compliant plural selection. */ declare function generatePluralFunctionCode(locale: string, categories: readonly string[]): string; /** * Generates Intl formatter declarations. */ declare function generateFormatterDeclarations(locale: string, used: FormatterUsage): string | null; export { type Catalog, type CatalogEntry, type CatalogToItemsOptions, type CodeGenContext, type CompileCatalogOptions, type CompileIcuOptions, type CompiledCatalog, type CompiledMessageFunction, type CreateHeadersOptions, type CreateItemOptions, DEFAULT_SERIALIZE_OPTIONS, type FormatReferenceOptions, type GenerateCodeOptions, type GenerateIdsOptions, type GettextToIcuOptions, type Headers, type IcuAgoNode, type IcuAgoStyle, type IcuArgumentNode, type IcuDateNode, type IcuDurationNode, type IcuDurationStyle, type IcuListNode, type IcuLiteralNode, type IcuLocation, type IcuNameNode, type IcuNode, type IcuNumberNode, type IcuParseError, type IcuParseResult, IcuParser, type IcuParserOptions, type IcuPluralNode, type IcuPluralOption, type IcuPosition, type IcuPoundNode, type IcuSelectNode, type IcuSelectOption, IcuSyntaxError, type IcuTagNode, type IcuTimeNode, type IcuToGettextOptions, type IcuValidationResult, type IcuVariable, type IcuVariableComparison, type ItemsToCatalogOptions, type MessageCodeResult, type MessageResult, type MessageValues, type NormalizeToIcuOptions, type ParsedPluralForms, type ParserState, type PoFile, type PoItem, type SerializeOptions, type SourceReference, catalogToItems, compareVariables, compileCatalog, compileIcu, createCodeGenContext, createDefaultHeaders, createIcuCompiler, createItem, createPoFile, createReference, escapeComment, escapeString, escapeTemplateString, extractPluralVariable, extractString, extractVariableInfo, extractVariables, foldLine, formatKeyword, formatPoDate, formatReference, formatReferences, generateCompiledCode, generateFormatterDeclarations, generateMessageId, generateMessageIdSync, generateMessageIds, generateNodeCode, generateNodesCode, generatePluralFunctionCode, getNumberOptionsForStyle, getPluralCategories, getPluralCount, getPluralFormsHeader, getPluralFunction, gettextToIcu, hasIcuSyntax, hasPlural, hasSelect, hasSelectOrdinal, icuToGettextSource, isPluralItem, itemsToCatalog, mergeCatalogs, normalizeFilePath, normalizeItemToIcu, normalizeToIcu, parseHeaders, parseIcu, parseItems, parsePluralForms, parsePo, parseReference, parseReferences, safeVarName, sanitizeStyle, splitHeaderAndBody, splitMultilineComments, stringifyItem, stringifyPo, unescapeString, validateIcu };