Pure MoonBit GNU gettext catalog parser, compiler, and runtime lookup library.
Dependencies
moon add YanFangNie/moongettextmoon check --target all --deny-warn --warn-list +73
moon test --target all --deny-warn --warn-list +73
moon run examples/basic///|
test "parse and normalize a PO entry" {
let source =
#|#. Shown on the launch screen
#|#: src/main.mbt:12
#|msgctxt "button"
#|msgid "Open"
#|msgstr "Ouvrir"
#|
let document = @moongettext.parse_po(source)
assert_eq(document.entries.length(), 1)
assert_true(
document.find_entry("Open", context="button") is Some(entry) &&
entry.translation(0) == Some("Ouvrir"),
)
let reparsed = @moongettext.parse_po(@moongettext.write_po(document))
assert_true(reparsed == document)
}///|
test "context and plural-aware lookup" {
let document = @moongettext.PoFile::new([
@moongettext.PoEntry::singular(
"",
translation=(
#|Content-Type: text/plain; charset=UTF-8
#|Language: fr
#|Plural-Forms: nplurals=2; plural=(n > 1);
#|
),
),
@moongettext.PoEntry::singular(
"Open",
translation="Ouvrir",
context="button",
),
@moongettext.PoEntry::plural("file", "files", ["fichier", "fichiers"]),
])
let catalog = @moongettext.Catalog::from_po(document)
assert_eq(catalog.pgettext("button", "Open"), "Ouvrir")
assert_eq(catalog.ngettext("file", "files", 1), "fichier")
assert_eq(catalog.ngettext("file", "files", 3), "fichiers")
assert_eq(catalog.gettext("missing"), "missing")
}///|
test "fallback catalog" {
let primary = @moongettext.Catalog::from_po(
@moongettext.PoFile::new([
@moongettext.PoEntry::singular("Hello", translation="Salut"),
]),
)
let fallback = @moongettext.Catalog::from_po(
@moongettext.PoFile::new([
@moongettext.PoEntry::singular("Open", translation="Ouvrir"),
]),
)
assert_eq(primary.gettext("Hello", fallback~), "Salut")
assert_eq(primary.gettext("Open", fallback~), "Ouvrir")
assert_eq(primary.gettext("Unknown", fallback~), "Unknown")
}///|
test "compile and load big-endian MO" {
let source =
#|msgid ""
#|msgstr "Content-Type: text/plain; charset=UTF-8\n"
#|
#|msgid "Save"
#|msgstr "Enregistrer"
#|
let po = @moongettext.parse_po(source)
let bytes = @moongettext.compile_mo_checked(po, endian=Big)
assert_true(@moongettext.mo_endian(bytes) == Big)
let loaded = @moongettext.Catalog::from_mo(bytes)
assert_eq(loaded.gettext("Save"), "Enregistrer")
}///|
test "Russian three-form expression" {
let rule = @moongettext.parse_plural_forms(
"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;",
)
assert_eq(rule.select(1), 0)
assert_eq(rule.select(2), 1)
assert_eq(rule.select(5), 2)
assert_eq(rule.select(21), 0)
}///|
test "structured validation report" {
let invalid = @moongettext.PoFile::new([
@moongettext.PoEntry::plural("item", "", [""]),
])
let issues = @moongettext.validate_po(invalid)
assert_true(@moongettext.validation_has_errors(issues))
let report = @moongettext.validation_report(issues)
assert_true(report.contains("empty-plural-id"))
}///|
test "merge template with translated PO" {
let template = @moongettext.PoFile::new([
@moongettext.PoEntry::singular("Open"),
@moongettext.PoEntry::singular("New"),
])
let existing = @moongettext.PoFile::new([
@moongettext.PoEntry::singular("Open", translation="Ouvrir"),
@moongettext.PoEntry::singular("Old", translation="Ancien"),
])
let merged = @moongettext.merge_template(template, existing)
assert_true(merged.stats == { matched: 1, added: 1, obsoleted: 1 })
assert_eq(merged.document.entries[0].translations[0], "Ouvrir")
assert_true(merged.document.entries[2].obsolete)
}moon run cmd/main -- demo
moon run cmd/main -- validate examples/fr.po
moon run cmd/main -- normalize examples/fr.po
moon run cmd/main -- compile examples/fr.po _build/fr.mo
moon run cmd/main -- compile examples/fr.po _build/fr-be.mo --big-endian
moon run cmd/main -- inspect _build/fr.momoon run examples/basicOuvrir
0: fichier
1: fichier
2: fichiers
5: fichiers| Area | Entry points |
|---|---|
| PO/POT | parse_po, write_po, write_pot, PoFile::as_template |
| MO | compile_mo, compile_mo_checked, parse_mo, mo_endian |
| Plurals | parse_plural_forms, evaluate_plural_expression, PluralRule::select |
| Runtime | Catalog::from_po, Catalog::from_mo, gettext, pgettext, ngettext, npgettext |
| Merge | merge_template, MergeResult, MergeStats |
| Validation | validate_po, validate_po_strict, validation_report |
| Query | find_entry, flags, source_references, statistics, metadata |
model / escape shared PO model and string escaping
po_parser / po_writer semantic PO/POT codec
plural_lexer / plural_parser Plural-Forms grammar and evaluator
mo_reader / mo_writer strict MO revision 0 codec
catalog indexed runtime lookup and fallback
validation / query / merge tooling APIs
cmd/main native file CLI
examples/basic backend-neutral runnable examplemoon version --all
moon fmt --check
moon info --target all
moon check --target all --deny-warn --warn-list +73
moon test --target all --deny-warn --warn-list +73
moon build --target native
moon run examples/basic
moon run cmd/main -- validate examples/fr.po
moon package --frozen
git diff --exit-codefn Catalog::from_po(document : PoFile, default_rule? : PluralRule, include_fuzzy? : Bool) -> Catalog raise GettextErrorfn Catalog::ngettext(self : Catalog, singular : String, plural : String, n : Int, fallback? : Catalog) -> String raise GettextErrorfn Catalog::npgettext(self : Catalog, context : String, singular : String, plural : String, n : Int, fallback? : Catalog) -> String raise GettextErrorpub(all) struct ValidationIssue {
severity : IssueSeverity
entry : Int?
code : String
message : String
} derive(Eq, Debug)fn escape_po_string(input : String) -> Stringfn merge_template(template : PoFile, existing : PoFile, keep_obsolete? : Bool) -> MergeResult raise GettextErrorPure MoonBit GNU gettext catalog parser, compiler, and runtime lookup library.
Dependencies