moon_l10n

A cross-target ICU MessageFormat subset runtime and catalog linter for MoonBit.

moonbit
i18n
l10n
icu
localization
moon add Zhouz-z/moon_l10n@0.2.0
Download zip
Author
Version
0.2.0
License
MIT
Last updated
5 hours ago
Downloads
8

Dependencies

README

#MoonL10n

CI

MoonL10n is a cross-target MoonBit internationalization library and CLI. It implements the practical core of ICU MessageFormat: variables, select, cardinal plural, selectordinal, nested branches, locale-aware fallback, JSON catalogs, static linting, and deterministic validation reports.

The runtime has no file-system dependency and supports native, JavaScript, WebAssembly, and wasm-gc. File access is isolated in the CLI package.

#Installation

Add the latest Mooncakes release:

moon add Zhouz-z/moon_l10n

To reproduce this release after version 0.2.0 is published:

moon add Zhouz-z/moon_l10n@0.2.0

Import the package from moon.pkg:

///|
import {
"Zhouz-z/moon_l10n",
}

The default package alias is @moon_l10n. Run moon update after changing dependencies if the local package index is stale.

#Minimal library example

///|
test {
let message = @moon_l10n.parse_message(
"{count, plural, one {# file} other {# files}}",
)
let values : Map[String, @moon_l10n.Argument] = {
"count": @moon_l10n.Number(2),
}
inspect(@moon_l10n.format(message, values, "en"), content="2 files")
}

Load catalogs and use locale fallback:

///|
test {
let english = @moon_l10n.Catalog::from_json(
"en", "{\"files\":\"{count, plural, one {# file} other {# files}}\"}",
)
let translator = @moon_l10n.Translator::new("zh-CN", "en")
translator.add_catalog(english)
let result = translator.translate_detailed(
"files",
{ "count": @moon_l10n.Number(1) },
locale="zh-Hans-CN",
)
inspect(result.value(), content="1 file")
inspect(result.resolved_locale(), content="en")
}

Fallback formatting uses the locale of the catalog that supplied the message. For example, an English fallback selected from a Chinese request still applies English plural rules.

#Message syntax

Variables:

Hello {name}

Text selection:

{gender, select, female {She} male {He} other {They}}

Cardinal plural:

{count, plural, =0 {No files} one {# file} other {# files}}

Ordinal selection:

{position, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}

Choices may contain other choices. Within a plural or ordinal branch, # renders the active numeric argument. An inner plural temporarily replaces the outer # value; an inner select preserves it.

The standard formatter name is selectordinal. The parser rejects the common misspelling selectorordinal and reports a correction.

#Catalogs and fallback

A catalog is a JSON object whose values are MessageFormat templates:

{ "greeting": "Hello {name}", "files": "{count, plural, one {# file} other {# files}}" }

Catalog::from_json is strict and rejects malformed templates. Catalog::from_json_lenient preserves malformed templates so validators and translation editors can report all problems in one run.

Requested locales are canonicalized and expanded into parents. A request for zh-Hans-CN tries:

zh-Hans-CN -> zh-Hans -> zh -> configured default -> configured fallback

Translator::resolve and translate_detailed expose the requested locale, resolved catalog locale, template provenance, and whether fallback occurred.

#Linting

lint_catalogs(reference, translation) reports:

  • missing and extra keys;
  • malformed reference or translation templates with line and column;
  • missing and extra arguments;
  • text-versus-number argument usage changes;
  • select, plural, and selectordinal kind changes;
  • missing or unexpected selectors;
  • missing exact selectors such as =0;
  • plural categories expected by the target locale.

Reports have stable issue codes, severity, text output, JSON output, summary counts, and exit-code semantics. Catalog utilities also provide key diffs, coverage percentages, builders, deterministic JSON serialization, statistics, and multi-locale coverage matrices.

#CLI

Validate templates and semantic signatures:

moon run cmd/main -- validate examples/locales/en.json examples/locales/en.json en moon run cmd/main -- validate examples/locales/en.json examples/locales/ru.json ru --json

Compare catalog key sets:

moon run cmd/main -- diff examples/locales/en.json examples/locales/zh-CN.json zh-CN

Render one message:

moon run cmd/main -- render examples/locales/en.json en files count=3 moon run cmd/main -- render examples/locales/en.json en files count=1 --json

Exit code 0 means clean, 1 means warnings or extra-only differences, and 2 means errors or missing required content. render returns 0 on success.

#Locale coverage

Version 0.2.0 explicitly implements integer plural behavior for:

  • Chinese (zh, including zh-CN) and Japanese (ja): other;
  • English (en): cardinal one/other, ordinal one/two/few/other;
  • Russian (ru): one/few/many/other;
  • Arabic (ar): zero/one/two/few/many/other.

The locale layer also provides canonicalization, language/script/region parsing, parent fallback chains, text direction, negotiation, supported category metadata, and representative plural examples.

#ICU and CLDR scope

MoonL10n is an original MoonBit implementation informed by public internationalization specifications. It does not copy ICU or CLDR source code or data files.

References:

Version 0.2.0 intentionally does not implement:

  • date, time, number, or currency formatting;
  • gettext PO/POT catalogs;
  • plural offset;
  • decimal plural operands;
  • complete generated CLDR locale data;
  • ICU apostrophe quoting;
  • remote translation services.

Backslash escaping is used for literal {, }, #, and \ in this subset. These boundaries keep the runtime compact, deterministic, and cross-target.

#Validation

python scripts/count_effective_moonbit.py --minimum 4000 moon fmt --check moon check --target all --deny-warn moon build --target all --deny-warn moon test --target all --deny-warn moon info moon package

The effective-line check counts code-bearing lines in production MoonBit library packages. Tests, examples, the CLI executable, generated interfaces, comments, and build output are excluded.

GitHub Actions runs the same gates plus CLI smoke tests and uploads the Mooncakes zip artifact.

#License

MIT. See LICENSE.

#
MessageError

pub(all) suberror MessageError {
ParseError(position~ : Int, message~ : String)
InvalidCatalog(String)
InvalidLocale(String)
InvalidCommand(String)
EvaluationLimit(Int)
MissingArgument(String)
WrongArgumentType(String)
MissingChoice(String)
} derive(Eq,
Debug
)

#
Argument

pub(all) enum Argument {
Text(String)
Number(Int)
} derive(Eq,
Debug
)

#
Argument::as_number

fn Argument::as_number(self : Argument) -> Int raise MessageError

#
Argument::as_text

fn Argument::as_text(self : Argument) -> String

#
Argument::is_number

fn Argument::is_number(self : Argument) -> Bool

#
Argument::is_text

fn Argument::is_text(self : Argument) -> Bool

#
Argument::kind

fn Argument::kind(self : Argument) -> String

#
ArgumentRole

pub(all) enum ArgumentRole {
Interpolation
SelectSelector
CardinalSelector
OrdinalSelector
} derive(Eq,
Debug
)

Describes how an argument is consumed by a message.

#
ArgumentRole::expects_number

fn ArgumentRole::expects_number(self : ArgumentRole) -> Bool

#
ArgumentRole::name

fn ArgumentRole::name(self : ArgumentRole) -> String

#
ArgumentUse

pub(all) struct ArgumentUse {
name : String
roles : Array[ArgumentRole]
occurrences : Int
} derive(Eq,
Debug
)

A single use of an argument in a parsed message.

#
ArgumentUse::expects_number

fn ArgumentUse::expects_number(self : ArgumentUse) -> Bool

#
ArgumentUse::name

fn ArgumentUse::name(self : ArgumentUse) -> String

#
ArgumentUse::occurrences

fn ArgumentUse::occurrences(self : ArgumentUse) -> Int

#
ArgumentUse::roles

#
ArgumentUse::uses_role

fn ArgumentUse::uses_role(self : ArgumentUse, role : ArgumentRole) -> Bool

#
Catalog

pub struct Catalog {
locale : String
entries : Map[String, String]
} derive(
Debug
)

#
Catalog::analyses

fn Catalog::analyses(self : Catalog) -> Array[CatalogEntryAnalysis]

#
Catalog::contains

fn Catalog::contains(self : Catalog, key : String) -> Bool

#
Catalog::entries

fn Catalog::entries(self : Catalog) -> Map[String, String]

#
Catalog::entry_analysis

fn Catalog::entry_analysis(self : Catalog, key : String) -> CatalogEntryAnalysis?

#
Catalog::from_json

fn Catalog::from_json(locale : String, source : String) -> Catalog raise MessageError

#
Catalog::from_json_lenient

fn Catalog::from_json_lenient(locale : String, source : String) -> Catalog raise MessageError

Load a catalog without rejecting invalid message templates.

This is intended for validators and editors that need to report every malformed entry in one pass. Applications should normally use from_json.

#
Catalog::get

fn Catalog::get(self : Catalog, key : String) -> String?

#
Catalog::invalid_keys

fn Catalog::invalid_keys(self : Catalog) -> Array[String]

#
Catalog::is_empty

fn Catalog::is_empty(self : Catalog) -> Bool

#
Catalog::keys

fn Catalog::keys(self : Catalog) -> Array[String]

#
Catalog::length

fn Catalog::length(self : Catalog) -> Int

#
Catalog::locale

fn Catalog::locale(self : Catalog) -> String

#
Catalog::merge

fn Catalog::merge(self : Catalog, other : Catalog, policy? : CatalogMergePolicy) -> Catalog raise MessageError

#
Catalog::parsed

fn Catalog::parsed(self : Catalog, key : String) -> Message?

#
Catalog::stats

fn Catalog::stats(self : Catalog) -> CatalogStats

Compute aggregate catalog statistics.

#
Catalog::subset

fn Catalog::subset(self : Catalog, prefix : String) -> Catalog

#
Catalog::to_json

fn Catalog::to_json(self : Catalog, pretty? : Bool) -> String

Serialize a catalog deterministically with keys in lexical order.

#
Catalog::valid_keys

fn Catalog::valid_keys(self : Catalog) -> Array[String]

#
Catalog::values

fn Catalog::values(self : Catalog) -> Array[String]

#
Catalog::with_entry

fn Catalog::with_entry(self : Catalog, key : String, template : String) -> Catalog raise MessageError

#
Catalog::without_entry

fn Catalog::without_entry(self : Catalog, key : String) -> Catalog

#
CatalogBuilder

pub struct CatalogBuilder {
locale : String
entries : Map[String, String]
}

A builder for constructing catalogs without exposing mutable internals.

#
CatalogBuilder::build

fn CatalogBuilder::build(self : CatalogBuilder, validate? : Bool) -> Catalog raise MessageError

#
CatalogBuilder::clear

#
CatalogBuilder::contains

fn CatalogBuilder::contains(self : CatalogBuilder, key : String) -> Bool

#
CatalogBuilder::from_catalog

fn CatalogBuilder::from_catalog(catalog : Catalog) -> CatalogBuilder

#
CatalogBuilder::length

fn CatalogBuilder::length(self : CatalogBuilder) -> Int

#
CatalogBuilder::locale

fn CatalogBuilder::locale(self : CatalogBuilder) -> String

#
CatalogBuilder::new

fn CatalogBuilder::new(locale : String) -> CatalogBuilder

#
CatalogBuilder::remove

fn CatalogBuilder::remove(self : CatalogBuilder, key : String) -> CatalogBuilder

#
CatalogBuilder::set

fn CatalogBuilder::set(self : CatalogBuilder, key : String, template : String) -> CatalogBuilder

#
CatalogCoverage

pub(all) struct CatalogCoverage {
reference_keys : Int
translated_keys : Int
missing_keys : Int
extra_keys : Int
coverage_percent : Int
} derive(Eq,
Debug
)

Result of a catalog coverage calculation.

#
CatalogCoverage::coverage_percent

fn CatalogCoverage::coverage_percent(self : CatalogCoverage) -> Int

#
CatalogCoverage::extra_keys

fn CatalogCoverage::extra_keys(self : CatalogCoverage) -> Int

#
CatalogCoverage::missing_keys

fn CatalogCoverage::missing_keys(self : CatalogCoverage) -> Int

#
CatalogCoverage::reference_keys

fn CatalogCoverage::reference_keys(self : CatalogCoverage) -> Int

#
CatalogCoverage::summary

fn CatalogCoverage::summary(self : CatalogCoverage) -> String

#
CatalogCoverage::translated_keys

fn CatalogCoverage::translated_keys(self : CatalogCoverage) -> Int

#
CatalogDiff

pub(all) struct CatalogDiff {
reference_locale : String
translation_locale : String
shared_keys : Array[String]
missing_keys : Array[String]
extra_keys : Array[String]
} derive(Eq,
Debug
)

Key-level difference between two catalogs.

#
CatalogDiff::coverage

fn CatalogDiff::coverage(self : CatalogDiff) -> CatalogCoverage

#
CatalogDiff::extra_keys

fn CatalogDiff::extra_keys(self : CatalogDiff) -> Array[String]

#
CatalogDiff::is_equal

fn CatalogDiff::is_equal(self : CatalogDiff) -> Bool

#
CatalogDiff::missing_keys

fn CatalogDiff::missing_keys(self : CatalogDiff) -> Array[String]

#
CatalogDiff::reference_locale

fn CatalogDiff::reference_locale(self : CatalogDiff) -> String

#
CatalogDiff::shared_keys

fn CatalogDiff::shared_keys(self : CatalogDiff) -> Array[String]

#
CatalogDiff::translation_locale

fn CatalogDiff::translation_locale(self : CatalogDiff) -> String

#
CatalogEntryAnalysis

pub(all) struct CatalogEntryAnalysis {
key : String
template : String
valid : Bool
diagnostic : MessageDiagnostic?
message : MessageAnalysis?
} derive(
Debug
)

Analysis for a single catalog entry.

#
CatalogEntryAnalysis::diagnostic

#
CatalogEntryAnalysis::key

fn CatalogEntryAnalysis::key(self : CatalogEntryAnalysis) -> String

#
CatalogEntryAnalysis::message

#
CatalogEntryAnalysis::template

fn CatalogEntryAnalysis::template(self : CatalogEntryAnalysis) -> String

#
CatalogEntryAnalysis::valid

fn CatalogEntryAnalysis::valid(self : CatalogEntryAnalysis) -> Bool

#
CatalogMatrix

pub(all) struct CatalogMatrix {
reference_locale : String
locales : Array[String]
rows : Array[CatalogMatrixRow]
} derive(Eq,
Debug
)

Cross-locale catalog coverage matrix.

#
CatalogMatrix::complete

fn CatalogMatrix::complete(self : CatalogMatrix) -> Bool

#
CatalogMatrix::complete_rows

fn CatalogMatrix::complete_rows(self : CatalogMatrix) -> Array[CatalogMatrixRow]

#
CatalogMatrix::coverage_for

fn CatalogMatrix::coverage_for(self : CatalogMatrix, locale : String) -> CatalogCoverage?

#
CatalogMatrix::has_locale

fn CatalogMatrix::has_locale(self : CatalogMatrix, locale : String) -> Bool

#
CatalogMatrix::incomplete_rows

fn CatalogMatrix::incomplete_rows(self : CatalogMatrix) -> Array[CatalogMatrixRow]

#
CatalogMatrix::key_count

fn CatalogMatrix::key_count(self : CatalogMatrix) -> Int

#
CatalogMatrix::locale_count

fn CatalogMatrix::locale_count(self : CatalogMatrix) -> Int

#
CatalogMatrix::locales

fn CatalogMatrix::locales(self : CatalogMatrix) -> Array[String]

#
CatalogMatrix::missing_count

fn CatalogMatrix::missing_count(self : CatalogMatrix) -> Int

#
CatalogMatrix::missing_keys_for

fn CatalogMatrix::missing_keys_for(self : CatalogMatrix, locale : String) -> Array[String]

#
CatalogMatrix::present_keys_for

fn CatalogMatrix::present_keys_for(self : CatalogMatrix, locale : String) -> Array[String]

#
CatalogMatrix::reference_locale

fn CatalogMatrix::reference_locale(self : CatalogMatrix) -> String

#
CatalogMatrix::row

fn CatalogMatrix::row(self : CatalogMatrix, key : String) -> CatalogMatrixRow?

#
CatalogMatrix::rows

#
CatalogMatrix::to_json

fn CatalogMatrix::to_json(self : CatalogMatrix) -> String

#
CatalogMatrix::to_text

fn CatalogMatrix::to_text(self : CatalogMatrix) -> String

#
CatalogMatrixRow

pub(all) struct CatalogMatrixRow {
key : String
present_locales : Array[String]
missing_locales : Array[String]
} derive(Eq,
Debug
)

Presence information for one key across a catalog set.

#
CatalogMatrixRow::complete

fn CatalogMatrixRow::complete(self : CatalogMatrixRow) -> Bool

#
CatalogMatrixRow::coverage_percent

fn CatalogMatrixRow::coverage_percent(self : CatalogMatrixRow) -> Int

#
CatalogMatrixRow::key

fn CatalogMatrixRow::key(self : CatalogMatrixRow) -> String

#
CatalogMatrixRow::missing_locales

fn CatalogMatrixRow::missing_locales(self : CatalogMatrixRow) -> Array[String]

#
CatalogMatrixRow::present_locales

fn CatalogMatrixRow::present_locales(self : CatalogMatrixRow) -> Array[String]

#
CatalogMergePolicy

pub(all) enum CatalogMergePolicy {
KeepExisting
ReplaceExisting
RejectDuplicate
} derive(Eq,
Debug
)

How duplicate keys should be handled while combining catalogs.

#
CatalogStats

pub(all) struct CatalogStats {
locale : String
key_count : Int
template_count : Int
valid_template_count : Int
invalid_template_count : Int
argument_count : Int
choice_count : Int
} derive(Eq,
Debug
)

Summary information for one catalog.

#
CatalogStats::argument_count

fn CatalogStats::argument_count(self : CatalogStats) -> Int

#
CatalogStats::choice_count

fn CatalogStats::choice_count(self : CatalogStats) -> Int

#
CatalogStats::invalid_template_count

fn CatalogStats::invalid_template_count(self : CatalogStats) -> Int

#
CatalogStats::key_count

fn CatalogStats::key_count(self : CatalogStats) -> Int

#
CatalogStats::locale

fn CatalogStats::locale(self : CatalogStats) -> String

#
CatalogStats::summary

fn CatalogStats::summary(self : CatalogStats) -> String

#
CatalogStats::template_count

fn CatalogStats::template_count(self : CatalogStats) -> Int

#
CatalogStats::valid_template_count

fn CatalogStats::valid_template_count(self : CatalogStats) -> Int

#
ChoiceKind

pub(all) enum ChoiceKind {
Select
Plural
SelectOrdinal
} derive(Eq,
Debug
)

The kind of selector represented by a choice expression.

#
ChoiceKind::from_name

fn ChoiceKind::from_name(name : String) -> ChoiceKind?

#
ChoiceKind::is_numeric

fn ChoiceKind::is_numeric(self : ChoiceKind) -> Bool

#
ChoiceKind::name

fn ChoiceKind::name(self : ChoiceKind) -> String

#
ChoiceUse

pub(all) struct ChoiceUse {
argument : String
kind : ChoiceKind
selectors : Array[String]
depth : Int
ordinal : Int
} derive(Eq,
Debug
)

One choice expression found in a message.

#
ChoiceUse::argument

fn ChoiceUse::argument(self : ChoiceUse) -> String

#
ChoiceUse::category_selectors

fn ChoiceUse::category_selectors(self : ChoiceUse) -> Array[String]

#
ChoiceUse::depth

fn ChoiceUse::depth(self : ChoiceUse) -> Int

#
ChoiceUse::exact_selectors

fn ChoiceUse::exact_selectors(self : ChoiceUse) -> Array[String]

#
ChoiceUse::has_selector

fn ChoiceUse::has_selector(self : ChoiceUse, selector : String) -> Bool

#
ChoiceUse::identifier

fn ChoiceUse::identifier(self : ChoiceUse) -> String

#
ChoiceUse::kind

fn ChoiceUse::kind(self : ChoiceUse) -> ChoiceKind

#
ChoiceUse::ordinal

fn ChoiceUse::ordinal(self : ChoiceUse) -> Int

#
ChoiceUse::selectors

fn ChoiceUse::selectors(self : ChoiceUse) -> Array[String]

#
CliCommandKind

pub(all) enum CliCommandKind {
Validate
Diff
Render
Help
} derive(Eq,
Debug
)

Stable command names understood by the pure CLI parser.

#
CliCommandKind::name

fn CliCommandKind::name(self : CliCommandKind) -> String

#
CliOutput

pub(all) struct CliOutput {
exit_code : Int
stdout : String
stderr : String
} derive(Eq,
Debug
)

Result returned by the pure CLI execution layer.

#
CliOutput::display

fn CliOutput::display(self : CliOutput) -> String

#
CliOutput::exit_code

fn CliOutput::exit_code(self : CliOutput) -> Int

#
CliOutput::failed

fn CliOutput::failed(self : CliOutput) -> Bool

#
CliOutput::stderr

fn CliOutput::stderr(self : CliOutput) -> String

#
CliOutput::stdout

fn CliOutput::stdout(self : CliOutput) -> String

#
CliOutput::succeeded

fn CliOutput::succeeded(self : CliOutput) -> Bool

#
CliRequest

pub(all) struct CliRequest {
kind : CliCommandKind
paths : Array[String]
locale : String?
key : String?
arguments : Map[String, Argument]
json : Bool
} derive(Eq,
Debug
)

Parsed CLI request independent of file-system access.

#
CliRequest::arguments

fn CliRequest::arguments(self : CliRequest) -> Map[String, Argument]

#
CliRequest::json

fn CliRequest::json(self : CliRequest) -> Bool

#
CliRequest::key

fn CliRequest::key(self : CliRequest) -> String?

#
CliRequest::kind

#
CliRequest::locale

fn CliRequest::locale(self : CliRequest) -> String?

#
CliRequest::path

fn CliRequest::path(self : CliRequest, index : Int) -> String?

#
CliRequest::paths

fn CliRequest::paths(self : CliRequest) -> Array[String]

#
FormatEvent

pub(all) struct FormatEvent {
kind : FormatEventKind
depth : Int
path : String
argument : String?
selector : String?
output : String
} derive(Eq,
Debug
)

One deterministic event from message evaluation.

#
FormatEvent::argument

fn FormatEvent::argument(self : FormatEvent) -> String?

#
FormatEvent::depth

fn FormatEvent::depth(self : FormatEvent) -> Int

#
FormatEvent::display

fn FormatEvent::display(self : FormatEvent) -> String

#
FormatEvent::emits_output

fn FormatEvent::emits_output(self : FormatEvent) -> Bool

#
FormatEvent::has_argument

fn FormatEvent::has_argument(self : FormatEvent) -> Bool

#
FormatEvent::has_selector

fn FormatEvent::has_selector(self : FormatEvent) -> Bool

#
FormatEvent::is_choice

fn FormatEvent::is_choice(self : FormatEvent) -> Bool

#
FormatEvent::kind

#
FormatEvent::output

fn FormatEvent::output(self : FormatEvent) -> String

#
FormatEvent::path

fn FormatEvent::path(self : FormatEvent) -> String

#
FormatEvent::selector

fn FormatEvent::selector(self : FormatEvent) -> String?

#
FormatEventKind

pub(all) enum FormatEventKind {
EmitText
EmitArgument
EmitPound
SelectBranch
} derive(Eq,
Debug
)

Kind of evaluation event emitted by format_with_trace.

#
FormatEventKind::name

fn FormatEventKind::name(self : FormatEventKind) -> String

#
FormatOptions

pub(all) struct FormatOptions {
missing_argument : MissingArgumentBehavior
maximum_depth : Int
} derive(Eq,
Debug
)

Runtime options for message evaluation.

#
FormatOptions::default

fn FormatOptions::default() -> FormatOptions

#
FormatOptions::maximum_depth

fn FormatOptions::maximum_depth(self : FormatOptions) -> Int

#
FormatOptions::missing_argument

fn FormatOptions::missing_argument(self : FormatOptions) -> MissingArgumentBehavior

#
FormatOptions::permissive

fn FormatOptions::permissive() -> FormatOptions

#
FormattedMessage

pub(all) struct FormattedMessage {
value : String
events : Array[FormatEvent]
} derive(Eq,
Debug
)

Formatted value plus a deterministic evaluation trace.

#
FormattedMessage::choice_events

fn FormattedMessage::choice_events(self : FormattedMessage) -> Array[FormatEvent]

#
FormattedMessage::event_count

fn FormattedMessage::event_count(self : FormattedMessage) -> Int

#
FormattedMessage::events

#
FormattedMessage::events_for_argument

fn FormattedMessage::events_for_argument(self : FormattedMessage, argument : String) -> Array[FormatEvent]

#
FormattedMessage::maximum_depth

fn FormattedMessage::maximum_depth(self : FormattedMessage) -> Int

#
FormattedMessage::trace_text

fn FormattedMessage::trace_text(self : FormattedMessage) -> String

#
FormattedMessage::value

fn FormattedMessage::value(self : FormattedMessage) -> String

#
LintIssue

pub struct LintIssue {
severity : LintSeverity
code : String
key : String
message : String
argument : String?
selector : String?
} derive(Eq,
Debug
)

#
LintIssue::argument

fn LintIssue::argument(self : LintIssue) -> String?

#
LintIssue::code

fn LintIssue::code(self : LintIssue) -> String

#
LintIssue::display

fn LintIssue::display(self : LintIssue) -> String

#
LintIssue::key

fn LintIssue::key(self : LintIssue) -> String

#
LintIssue::message

fn LintIssue::message(self : LintIssue) -> String

#
LintIssue::selector

fn LintIssue::selector(self : LintIssue) -> String?

#
LintIssue::severity

fn LintIssue::severity(self : LintIssue) -> LintSeverity

#
LintOptions

pub(all) struct LintOptions {
report_extra_keys : Bool
report_extra_arguments : Bool
report_extra_selectors : Bool
require_locale_categories : Bool
} derive(Eq,
Debug
)

Policy switches for catalog linting.

#
LintOptions::default

fn LintOptions::default() -> LintOptions

#
LintOptions::minimal

fn LintOptions::minimal() -> LintOptions

#
LintOptions::report_extra_arguments

fn LintOptions::report_extra_arguments(self : LintOptions) -> Bool

#
LintOptions::report_extra_keys

fn LintOptions::report_extra_keys(self : LintOptions) -> Bool

#
LintOptions::report_extra_selectors

fn LintOptions::report_extra_selectors(self : LintOptions) -> Bool

#
LintOptions::require_locale_categories

fn LintOptions::require_locale_categories(self : LintOptions) -> Bool

#
LintReport

pub struct LintReport {
reference_locale : String
locale : String
issues : Array[LintIssue]
} derive(
Debug
)

#
LintReport::contains_code

fn LintReport::contains_code(self : LintReport, code : String) -> Bool

#
LintReport::exit_code

fn LintReport::exit_code(self : LintReport) -> Int

#
LintReport::has_errors

fn LintReport::has_errors(self : LintReport) -> Bool

#
LintReport::issues

fn LintReport::issues(self : LintReport) -> Array[LintIssue]

#
LintReport::issues_with_code

fn LintReport::issues_with_code(self : LintReport, code : String) -> Array[LintIssue]

#
LintReport::issues_with_severity

fn LintReport::issues_with_severity(self : LintReport, severity : LintSeverity) -> Array[LintIssue]

#
LintReport::locale

fn LintReport::locale(self : LintReport) -> String

#
LintReport::ok

fn LintReport::ok(self : LintReport) -> Bool

#
LintReport::reference_locale

fn LintReport::reference_locale(self : LintReport) -> String

#
LintReport::summary

fn LintReport::summary(self : LintReport) -> LintSummary

#
LintReport::to_json

fn LintReport::to_json(self : LintReport) -> String

#
LintReport::to_text

fn LintReport::to_text(self : LintReport) -> String

#
LintSeverity

pub(all) enum LintSeverity {
Error
Warning
Information
} derive(Eq,
Debug
)

Severity assigned to a catalog lint issue.

#
LintSeverity::name

fn LintSeverity::name(self : LintSeverity) -> String

#
LintSeverity::rank

fn LintSeverity::rank(self : LintSeverity) -> Int

#
LintSummary

pub(all) struct LintSummary {
errors : Int
warnings : Int
information : Int
total : Int
} derive(Eq,
Debug
)

Aggregate lint counts.

#
LintSummary::display

fn LintSummary::display(self : LintSummary) -> String

#
LintSummary::errors

fn LintSummary::errors(self : LintSummary) -> Int

#
LintSummary::information

fn LintSummary::information(self : LintSummary) -> Int

#
LintSummary::total

fn LintSummary::total(self : LintSummary) -> Int

#
LintSummary::warnings

fn LintSummary::warnings(self : LintSummary) -> Int

#
LocaleId

pub(all) struct LocaleId {
original : String
canonical : String
language : String
script : String?
region : String?
variants : Array[String]
} derive(Eq,
Debug
)

Parsed and normalized locale identifier.

#
LocaleId::canonical

fn LocaleId::canonical(self : LocaleId) -> String

#
LocaleId::fallback_chain

fn LocaleId::fallback_chain(self : LocaleId) -> Array[String]

Return the exact locale followed by progressively broader parent locales.

#
LocaleId::has_region

fn LocaleId::has_region(self : LocaleId) -> Bool

#
LocaleId::has_script

fn LocaleId::has_script(self : LocaleId) -> Bool

#
LocaleId::is_language

fn LocaleId::is_language(self : LocaleId, language : String) -> Bool

#
LocaleId::language

fn LocaleId::language(self : LocaleId) -> String

#
LocaleId::original

fn LocaleId::original(self : LocaleId) -> String

#
LocaleId::parse

fn LocaleId::parse(value : String) -> LocaleId raise MessageError

Parse and canonicalize a compact BCP-47-style locale identifier.

#
LocaleId::region

fn LocaleId::region(self : LocaleId) -> String?

#
LocaleId::script

fn LocaleId::script(self : LocaleId) -> String?

#
LocaleId::variants

fn LocaleId::variants(self : LocaleId) -> Array[String]

#
LocaleNegotiation

pub(all) struct LocaleNegotiation {
requested : String
candidates : Array[String]
available : Array[String]
matched : String?
used_default : Bool
} derive(Eq,
Debug
)

Result of negotiating requested and available locales.

#
LocaleNegotiation::available

fn LocaleNegotiation::available(self : LocaleNegotiation) -> Array[String]

#
LocaleNegotiation::candidates

fn LocaleNegotiation::candidates(self : LocaleNegotiation) -> Array[String]

#
LocaleNegotiation::matched

fn LocaleNegotiation::matched(self : LocaleNegotiation) -> String?

#
LocaleNegotiation::requested

fn LocaleNegotiation::requested(self : LocaleNegotiation) -> String

#
LocaleNegotiation::succeeded

fn LocaleNegotiation::succeeded(self : LocaleNegotiation) -> Bool

#
LocaleNegotiation::summary

fn LocaleNegotiation::summary(self : LocaleNegotiation) -> String

#
LocaleNegotiation::used_default

fn LocaleNegotiation::used_default(self : LocaleNegotiation) -> Bool

#
LocaleProfile

pub(all) struct LocaleProfile {
locale : LocaleId
direction : TextDirection
cardinal_family : PluralRuleFamily
ordinal_family : PluralRuleFamily
cardinal_categories : Array[String]
ordinal_categories : Array[String]
} derive(Eq,
Debug
)

Metadata used by the runtime and linter for a supported locale.

#
LocaleProfile::cardinal_categories

fn LocaleProfile::cardinal_categories(self : LocaleProfile) -> Array[String]

#
LocaleProfile::cardinal_family

fn LocaleProfile::cardinal_family(self : LocaleProfile) -> PluralRuleFamily

#
LocaleProfile::direction

fn LocaleProfile::direction(self : LocaleProfile) -> TextDirection

#
LocaleProfile::locale

fn LocaleProfile::locale(self : LocaleProfile) -> LocaleId

#
LocaleProfile::ordinal_categories

fn LocaleProfile::ordinal_categories(self : LocaleProfile) -> Array[String]

#
LocaleProfile::ordinal_family

fn LocaleProfile::ordinal_family(self : LocaleProfile) -> PluralRuleFamily

#
LocaleSupport

pub(all) struct LocaleSupport {
language : String
english_name : String
native_name : String
direction : TextDirection
cardinal_categories : Array[String]
ordinal_categories : Array[String]
} derive(Eq,
Debug
)

Human-facing metadata for one explicitly supported base locale.

#
LocaleSupport::cardinal_categories

fn LocaleSupport::cardinal_categories(self : LocaleSupport) -> Array[String]

#
LocaleSupport::direction

fn LocaleSupport::direction(self : LocaleSupport) -> TextDirection

#
LocaleSupport::english_name

fn LocaleSupport::english_name(self : LocaleSupport) -> String

#
LocaleSupport::language

fn LocaleSupport::language(self : LocaleSupport) -> String

#
LocaleSupport::native_name

fn LocaleSupport::native_name(self : LocaleSupport) -> String

#
LocaleSupport::ordinal_categories

fn LocaleSupport::ordinal_categories(self : LocaleSupport) -> Array[String]

#
LocaleSupport::summary

fn LocaleSupport::summary(self : LocaleSupport) -> String

#
Message

pub struct Message {
nodes : Array[MessageNode]
} derive(
Debug
)

#
Message::analyze

fn Message::analyze(self : Message) -> MessageAnalysis

Analyze the argument signature and structural complexity of a message.

#
Message::argument_uses

fn Message::argument_uses(self : Message) -> Array[ArgumentUse]

#
Message::ast_text

fn Message::ast_text(self : Message) -> String

Render a deterministic, human-readable AST tree.

#
Message::choice_uses

fn Message::choice_uses(self : Message) -> Array[ChoiceUse]

#
Message::from_nodes

fn Message::from_nodes(nodes : Array[MessageNode]) -> Message

Construct a message from AST nodes.

#
Message::maximum_depth

fn Message::maximum_depth(self : Message) -> Int

#
Message::node_count

fn Message::node_count(self : Message) -> Int

#
Message::nodes

fn Message::nodes(self : Message) -> Array[MessageNode]

#
Message::signature

fn Message::signature(self : Message) -> MessageSignature

Return a semantic signature for linting and tooling.

#
Message::to_template

fn Message::to_template(self : Message) -> String

Serialize an AST using MoonL10n's canonical spacing and escaping.

#
MessageAnalysis

pub(all) struct MessageAnalysis {
arguments : Array[ArgumentUse]
node_count : Int
text_node_count : Int
argument_node_count : Int
choice_node_count : Int
pound_node_count : Int
maximum_depth : Int
} derive(Eq,
Debug
)

Structural information collected from a parsed message.

#
MessageAnalysis::argument_node_count

fn MessageAnalysis::argument_node_count(self : MessageAnalysis) -> Int

#
MessageAnalysis::arguments

#
MessageAnalysis::choice_node_count

fn MessageAnalysis::choice_node_count(self : MessageAnalysis) -> Int

#
MessageAnalysis::maximum_depth

fn MessageAnalysis::maximum_depth(self : MessageAnalysis) -> Int

#
MessageAnalysis::node_count

fn MessageAnalysis::node_count(self : MessageAnalysis) -> Int

#
MessageAnalysis::pound_node_count

fn MessageAnalysis::pound_node_count(self : MessageAnalysis) -> Int

#
MessageAnalysis::text_node_count

fn MessageAnalysis::text_node_count(self : MessageAnalysis) -> Int

#
MessageDiagnostic

pub(all) struct MessageDiagnostic {
code : String
message : String
position : SourcePosition
} derive(Eq,
Debug
)

A stable, serializable parser diagnostic.

#
MessageDiagnostic::code

fn MessageDiagnostic::code(self : MessageDiagnostic) -> String

#
MessageDiagnostic::display

fn MessageDiagnostic::display(self : MessageDiagnostic) -> String

#
MessageDiagnostic::message

fn MessageDiagnostic::message(self : MessageDiagnostic) -> String

#
MessageDiagnostic::position

#
MessageNode

pub(all) enum MessageNode {
TextNode(String)
ArgumentNode(String)
PoundNode
ChoiceNode(String, String, Map[String, Array[MessageNode]])
} derive(
Debug
)

#
MessageSignature

pub(all) struct MessageSignature {
arguments : Array[ArgumentUse]
choices : Array[ChoiceUse]
} derive(Eq,
Debug
)

Full semantic signature used by catalog linting.

#
MessageSignature::argument

fn MessageSignature::argument(self : MessageSignature, name : String) -> ArgumentUse?

#
MessageSignature::argument_count

fn MessageSignature::argument_count(self : MessageSignature) -> Int

#
MessageSignature::argument_names

fn MessageSignature::argument_names(self : MessageSignature) -> Array[String]

Return sorted argument names used by a message.

#
MessageSignature::arguments

#
MessageSignature::choice_count

fn MessageSignature::choice_count(self : MessageSignature) -> Int

#
MessageSignature::choices

#
MessageSignature::choices_for_argument

fn MessageSignature::choices_for_argument(self : MessageSignature, name : String) -> Array[ChoiceUse]

#
MessageSignature::choices_of_kind

fn MessageSignature::choices_of_kind(self : MessageSignature, kind : ChoiceKind) -> Array[ChoiceUse]

#
MessageSignature::compatible_with

fn MessageSignature::compatible_with(self : MessageSignature, other : MessageSignature) -> Bool

Compare argument names and roles while ignoring occurrence counts.

#
MessageSignature::has_argument

fn MessageSignature::has_argument(self : MessageSignature, name : String) -> Bool

#
MessageSignature::summary

fn MessageSignature::summary(self : MessageSignature) -> String

Render a compact signature suitable for CLI diagnostics.

#
MissingArgumentBehavior

pub(all) enum MissingArgumentBehavior {
RaiseError
KeepPlaceholder
ReplaceWithEmpty
} derive(Eq,
Debug
)

Behavior used by the configurable formatter when an argument is absent.

#
MissingArgumentBehavior::name

#
PluralExamples

pub(all) struct PluralExamples {
category : String
values : Array[Int]
} derive(Eq,
Debug
)

Example values discovered for one plural category.

#
PluralExamples::category

fn PluralExamples::category(self : PluralExamples) -> String

#
PluralExamples::is_empty

fn PluralExamples::is_empty(self : PluralExamples) -> Bool

#
PluralExamples::summary

fn PluralExamples::summary(self : PluralExamples) -> String

#
PluralExamples::values

fn PluralExamples::values(self : PluralExamples) -> Array[Int]

#
PluralRuleFamily

pub(all) enum PluralRuleFamily {
OtherOnly
English
Russian
Arabic
} derive(Eq,
Debug
)

Plural rule set selected for a locale.

#
PluralRuleFamily::name

fn PluralRuleFamily::name(self : PluralRuleFamily) -> String

#
PluralSelection

pub(all) struct PluralSelection {
locale : String
value : Int
ordinal : Bool
category : String
exact_selector : String
selected_selector : String
used_exact : Bool
used_other : Bool
} derive(Eq,
Debug
)

Explanation of plural branch selection.

#
PluralSelection::category

fn PluralSelection::category(self : PluralSelection) -> String

#
PluralSelection::exact_selector

fn PluralSelection::exact_selector(self : PluralSelection) -> String

#
PluralSelection::locale

fn PluralSelection::locale(self : PluralSelection) -> String

#
PluralSelection::ordinal

fn PluralSelection::ordinal(self : PluralSelection) -> Bool

#
PluralSelection::selected_selector

fn PluralSelection::selected_selector(self : PluralSelection) -> String

#
PluralSelection::summary

fn PluralSelection::summary(self : PluralSelection) -> String

#
PluralSelection::used_exact

fn PluralSelection::used_exact(self : PluralSelection) -> Bool

#
PluralSelection::used_other

fn PluralSelection::used_other(self : PluralSelection) -> Bool

#
PluralSelection::value

fn PluralSelection::value(self : PluralSelection) -> Int

#
ResolvedMessage

pub(all) struct ResolvedMessage {
key : String
requested_locale : String
resolved_locale : String
template : String
used_fallback : Bool
} derive(Eq,
Debug
)

A message template together with the catalog that supplied it.

#
ResolvedMessage::key

fn ResolvedMessage::key(self : ResolvedMessage) -> String

#
ResolvedMessage::requested_locale

fn ResolvedMessage::requested_locale(self : ResolvedMessage) -> String

#
ResolvedMessage::resolved_locale

fn ResolvedMessage::resolved_locale(self : ResolvedMessage) -> String

#
ResolvedMessage::template

fn ResolvedMessage::template(self : ResolvedMessage) -> String

#
ResolvedMessage::used_fallback

fn ResolvedMessage::used_fallback(self : ResolvedMessage) -> Bool

#
SourcePosition

pub(all) struct SourcePosition {
offset : Int
line : Int
column : Int
} derive(Eq,
Debug
)

A one-based source location together with its zero-based character offset.

#
SourcePosition::column

fn SourcePosition::column(self : SourcePosition) -> Int

#
SourcePosition::display

fn SourcePosition::display(self : SourcePosition) -> String

#
SourcePosition::line

fn SourcePosition::line(self : SourcePosition) -> Int

#
SourcePosition::offset

fn SourcePosition::offset(self : SourcePosition) -> Int

#
SourceSpan

pub(all) struct SourceSpan {
start : SourcePosition
end : SourcePosition
} derive(Eq,
Debug
)

A half-open range in a message template.

#
SourceSpan::end

#
SourceSpan::length

fn SourceSpan::length(self : SourceSpan) -> Int

#
SourceSpan::start

fn SourceSpan::start(self : SourceSpan) -> SourcePosition

#
TextDirection

pub(all) enum TextDirection {
LeftToRight
RightToLeft
} derive(Eq,
Debug
)

Text direction associated with a locale.

#
TextDirection::name

fn TextDirection::name(self : TextDirection) -> String

#
TranslationResult

pub(all) struct TranslationResult {
key : String
requested_locale : String
resolved_locale : String
value : String
used_fallback : Bool
} derive(Eq,
Debug
)

Detailed result returned after translation and formatting.

#
TranslationResult::key

fn TranslationResult::key(self : TranslationResult) -> String

#
TranslationResult::requested_locale

fn TranslationResult::requested_locale(self : TranslationResult) -> String

#
TranslationResult::resolved_locale

fn TranslationResult::resolved_locale(self : TranslationResult) -> String

#
TranslationResult::used_fallback

fn TranslationResult::used_fallback(self : TranslationResult) -> Bool

#
TranslationResult::value

fn TranslationResult::value(self : TranslationResult) -> String

#
Translator

pub struct Translator {
default_locale : String
fallback_locale : String
catalogs : Map[String, Catalog]
} derive(
Debug
)

#
Translator::add_catalog

fn Translator::add_catalog(self : Translator, catalog : Catalog) -> Unit

#
Translator::available_locales

fn Translator::available_locales(self : Translator) -> Array[String]

#
Translator::catalog_count

fn Translator::catalog_count(self : Translator) -> Int

#
Translator::default_locale

fn Translator::default_locale(self : Translator) -> String

#
Translator::fallback_locale

fn Translator::fallback_locale(self : Translator) -> String

#
Translator::has_catalog

fn Translator::has_catalog(self : Translator, locale : String) -> Bool

#
Translator::new

fn Translator::new(default_locale : String, fallback_locale : String) -> Translator

#
Translator::resolve

fn Translator::resolve(self : Translator, key : String, locale? : String) -> ResolvedMessage raise MessageError

Resolve a key and preserve the locale of the catalog that supplied it.

#
Translator::translate

fn Translator::translate(self : Translator, key : String, arguments : Map[String, Argument], locale? : String) -> String raise MessageError

Look up and format a key, trying requested, default, then fallback locale.

#
Translator::translate_detailed

fn Translator::translate_detailed(self : Translator, key : String, arguments : Map[String, Argument], locale? : String) -> TranslationResult raise MessageError

Resolve, format, and return locale provenance for a translation.

#
canonicalize_locale

fn canonicalize_locale(value : String) -> String raise MessageError

Canonicalize a locale identifier.

#
catalog_matrix

fn catalog_matrix(reference : Catalog, translations : Array[Catalog]) -> CatalogMatrix

Build a key-presence matrix for a reference and multiple translations.

#
cli_diff_sources

fn cli_diff_sources(reference_source : String, translation_source : String, locale : String, json? : Bool) -> CliOutput raise MessageError

Compare the key sets of two in-memory catalog sources.

#
cli_render_source

fn cli_render_source(source : String, locale : String, key : String, arguments : Map[String, Argument], json? : Bool) -> CliOutput raise MessageError

Render one key from an in-memory catalog source.

#
cli_usage

fn cli_usage() -> String

Return the stable command-line help text.

#
cli_validate_sources

fn cli_validate_sources(reference_source : String, translation_source : String, locale : String, json? : Bool) -> CliOutput raise MessageError

Validate two in-memory catalog sources.

#
diagnose_message

fn diagnose_message(template : String) -> MessageDiagnostic?

Parse a message and return a diagnostic instead of raising on failure.

#
diagnostic_from_error

fn diagnostic_from_error(source : String, error : MessageError) -> MessageDiagnostic

Convert a parser error into a stable diagnostic with line and column.

#
diff_catalogs

fn diff_catalogs(reference : Catalog, translation : Catalog) -> CatalogDiff

Compare the key sets of two catalogs.

#
format

fn format(message : Message, arguments : Map[String, Argument], locale : String) -> String raise MessageError

Format a parsed message with text and integer arguments.

#
format_with_options

fn format_with_options(message : Message, arguments : Map[String, Argument], locale : String, options : FormatOptions) -> String raise MessageError

Format a message using explicit runtime options.

#
format_with_trace

fn format_with_trace(message : Message, arguments : Map[String, Argument], locale : String, options? : FormatOptions) -> FormattedMessage raise MessageError

Format a message and retain a deterministic evaluation trace.

#
is_supported_locale

fn is_supported_locale(locale : String) -> Bool

Return whether MoonL10n has an explicit plural rule for the locale.

#
lint_catalog_set

fn lint_catalog_set(reference : Catalog, translations : Array[Catalog]) -> Array[LintReport]

Lint multiple translations using one reference catalog.

#
lint_catalogs

fn lint_catalogs(reference : Catalog, translation : Catalog) -> LintReport

Compare a translation against the reference catalog.

#
lint_catalogs_with_options

fn lint_catalogs_with_options(reference : Catalog, translation : Catalog, options : LintOptions) -> LintReport

Compare a translation against the reference catalog with explicit policy.

#
locale_fallback_chain

fn locale_fallback_chain(locale : String) -> Array[String]

Build a fallback chain for a locale, tolerating non-canonical input.

#
locale_profile

fn locale_profile(locale : String) -> LocaleProfile raise MessageError

Return plural metadata for a locale.

#
locale_support

fn locale_support(locale : String) -> LocaleSupport?

#
locale_support_table

fn locale_support_table() -> Array[LocaleSupport]

Return human-facing metadata for every explicitly supported language.

#
missing_plural_categories

fn missing_plural_categories(locale : String, selectors : Array[String], ordinal? : Bool) -> Array[String]

Check whether selectors cover all categories declared for a locale.

#
negotiate_locale

fn negotiate_locale(requested : String, available : Array[String], default_locale? : String) -> LocaleNegotiation

Negotiate a requested locale against an available locale list.

#
observed_plural_categories

fn observed_plural_categories(locale : String, start : Int, end : Int, ordinal? : Bool) -> Array[String]

Return every category observed over an inclusive integer range.

#
ordinal_category

fn ordinal_category(locale : String, value : Int) -> String

#
parse_cli_argument

fn parse_cli_argument(value : String) -> (String, Argument) raise MessageError

Parse one name=value CLI argument.

#
parse_cli_request

fn parse_cli_request(args : Array[String]) -> CliRequest raise MessageError

Parse command arguments without accessing the file system.

The first element must be the command itself, not the executable path.

#
parse_message

fn parse_message(template : String) -> Message raise MessageError

Parse a message template using the supported ICU MessageFormat subset.

#
plural_category

fn plural_category(locale : String, value : Int) -> String

#
plural_examples

fn plural_examples(locale : String, start : Int, end : Int, ordinal? : Bool, limit_per_category? : Int) -> Array[PluralExamples]

Collect representative integer values for each plural category.

#
required_cardinal_categories

fn required_cardinal_categories(locale : String) -> Array[String]

Return the cardinal categories expected by the linter.

#
required_ordinal_categories

fn required_ordinal_categories(locale : String) -> Array[String]

Return the ordinal categories expected by the linter.

#
select_plural_branch

fn select_plural_branch(locale : String, value : Int, ordinal : Bool, selectors : Array[String]) -> PluralSelection raise MessageError

Explain how a numeric choice selects a branch.

#
source_position

fn source_position(source : String, offset : Int) -> SourcePosition

Convert a zero-based character offset into a one-based line and column.

#
source_span

fn source_span(source : String, start : Int, end : Int) -> SourceSpan

Create a half-open source span from two offsets.

#
supported_languages

fn supported_languages() -> Array[String]

Return all canonical base languages with explicit plural support.

#
supported_locales

fn supported_locales() -> Array[String]

Return all locale identifiers with explicit v0.2 plural support.