ferrocat-icu 0.13.0

Compact, Rust-native ICU MessageFormat parsing primitives for ferrocat.
Documentation

ferrocat-icu

Compact, Rust-native rich-message parsing and diagnostics for ferrocat, based on ICU MessageFormat v1.

This crate is for messages that contain runtime values: placeholders, number/date/time formatting, plural logic, select branches, or rich-text tags. It helps catalog tooling understand those structures before translations ship.

Add it with:

cargo add ferrocat-icu

Use this crate when you want the ICU-specific surface directly:

  • parse_icu / parse_icu_with_options for parsing
  • validate_icu for lightweight validation
  • analyze_icu for structured argument, formatter, plural, select, and tag summaries
  • compare_icu_messages for source/translation compatibility diagnostics
  • validate_icu_formatter_support for mapping consumer runtime support policies to diagnostics
  • normalize_message_metadata for progressive source-side metadata around msgid + msgctxt
  • extract_argument_names and extract_tag_names when tags should not be mixed with data arguments
  • extract_variables, has_plural, has_select, and related helpers for AST inspection
use ferrocat_icu::{IcuCompatibilityOptions, compare_icu_messages, parse_icu};

fn main() -> Result<(), ferrocat_icu::IcuParseError> {
    let source = parse_icu("Hello {name}, you have {count, number, integer} files.")?;
    let translation = parse_icu("Hallo, du hast {count, number, integer} Dateien.")?;
    let report = compare_icu_messages(
        &source,
        &translation,
        &IcuCompatibilityOptions::default(),
    );

    assert!(report.has_errors());
    Ok(())
}

Semantic metadata stays small for simple messages and expands only when the message or extractor knows more:

use ferrocat_icu::{
    MessageArgumentKind, MessageMetadataInput, normalize_message_metadata,
};

fn main() -> Result<(), ferrocat_icu::IcuParseError> {
    let metadata = normalize_message_metadata(MessageMetadataInput::new(
        "{count, plural, one {One item} other {# items}}",
    ))?;

    assert_eq!(
        metadata.args.get("count").map(|argument| argument.kind),
        Some(MessageArgumentKind::Number)
    );
    assert!(metadata.selectors.contains_key("count"));
    Ok(())
}

If you want the combined public entry point instead, use ferrocat.