szs

    PO/POT catalog checking for MoonBit, with format and HTML/XML contracts, cross-file consistency, and bounded plural analysis.

    gettext
    po
    pot
    localization
    validation
    Download zip
    Version
    0.2.2
    License
    Apache-2.0
    Last updated
    15 days ago
    Downloads
    60

    No dependencies

    #CatalogGuard

    MoonBit 编写的 PO/POT 翻译目录检查库与命令行工具。项目由 2111950632 维护,面向 MoonBit 文档、应用本地化和 GitHub CI 中的翻译检查。

    当前版本 0.2.2。继续修复终审复查发现的忽略上下文漏报及 HTML 数字字符引用误报/漏报。跨文件索引为每项比较保留至多五个见证,独立比较复数源文、同复数源文的译文及格式 flags。两个朴素参考分别穷举 768 个正向和 729 个逆向小项目;另有 960 个生成标记输入及 350 个多文件项目检查随标准验证和 CI 运行。功能边界保持显式,旧版成果按版本保留。

    0.2.0 针对第二轮初审新增 HTML/XML 标记结构和跨文件一致性检查,与原有三类格式合同组成可嵌入 MoonBit 的翻译质量检查核心。新增功能、与固定版本 Translate Toolkit 的实际对照、合法与错误案例见 项目质量检查边界。PO/POT、目录校验、复数和模板处理与 moongettext 存在重叠,不申报为独有能力;格式标准和兼容终点仍见 格式合同说明。

    解析、格式与标记合同、跨文件分组比较、复数求值、模板对比、配置校验、报告生成均在 MoonBit 中实现;Node.js 入口负责参数传递、目录遍历、UTF-8 文件读取和输出。核心不执行宿主语言 eval,不需要第三方运行时包。

    #快速运行

    安装 MoonBit 工具链 和 Node.js 20 或更新版本,在项目目录运行:

    moon build --target js --release node cli.mjs check examples/zh_CN.po --template examples/messages.pot --strict node cli.mjs examples/project-quality/valid --recursive --markup html --consistency --strict --json

    预期输出:3 条有效消息,3 条已翻译,0 个错误、0 个警告。故意出错的例子用于演示诊断和失败退出码:

    node cli.mjs check examples/broken.po --json node cli.mjs inspect examples/zh_CN.po --json node cli.mjs stats examples/zh_CN.po --json node cli.mjs plural 'nplurals=2; plural=(n!=1);' node cli.mjs rules

    依次展示正常、错误、JSON 和复数规则四项演示:npm run demo。录屏步骤见 演示提纲。

    PowerShell、macOS 和 Linux 均可使用上面的命令。目录路径包含空格时加引号。PowerShell 查看上一条程序的退出码用 $LASTEXITCODE。

    下载带 dist/szs-core.js 的运行包后,只需 Node.js 即可执行 node cli.mjs ...,无需再次编译。GitHub 源码不提交构建产物;从源码运行时先构建。要制作运行包:

    moon build --target js --release node scripts/package.mjs

    同目录存在本地构建时,CLI 使用 release/debug 中最近构建的版本;无本地构建时加载运行包的 dist 核心,避免重新构建后仍运行旧发布文件。

    #检查范围

    功能本版本行为
    PO/POT 解析UTF-8、BOM、CRLF、多行字符串、字节转义、上下文、注释、引用、flags、obsolete
    源位置文件名、条目索引、从 1 开始的行列;字符串续行纳入范围;end 为排他位置
    目录与 metadata重复 context/msgid、缺失字段、头字段、charset、日期、空翻译、fuzzy、NUL、换行边界
    格式合同显式 c-format、python-format、python-brace-format 的参数身份和类型;C 参数序号、星号宽度精度;Python 命名字段
    HTML/XML 标记显式选择片段模式;配对与嵌套、祖先关系、数量、属性存在与受保护值;允许同级子树、属性顺序及可翻译值变化
    跨文件项目检查按语言/上下文/源文建索引,报告译文、复数源文和格式标记冲突,提供双方位置;可选逆向同译文审阅
    复数受限语法解析、短路求值、除零与整数溢出、索引范围、翻译槽位完整性、有限区间覆盖
    POT 对比精确匹配 context/msgid,报告缺失、多余、plural 来源变化、format flags 变化
    配置合法性校验、空翻译/fuzzy 严重性、严格模式、资源限制;部分审阅规则可关闭或改级
    输出文本、版本 1 JSON、目录结构、按 context 和源文件统计的翻译工作量
    规范化输出规范 PO 文本,保留条目内注释及 decoded 字符串,不修复翻译含义

    退出码:0 通过所选策略,1 检查失败,2 参数、配置、文件读取或规范化拒绝。默认 warning 不导致失败;--strict 使 warning 导致失败。inspect 只检查解析,不代表所有翻译规则通过。stats 仍运行检查,并返回相同的策略退出码。

    node cli.mjs check examples/broken.po --config examples/strict.json --json node cli.mjs normalize examples/zh_CN.po --output normalized.po

    --output 仅写入新文件,拒绝覆盖。省略该参数时输出到终端。通过 - 从标准输入读取。.pot 扩展名自动启用模板模式,也可用 --pot。

    #支持边界

    • 只接受 UTF-8;不读 MO,不自动转换编码,不提取源代码,不执行译文格式化,不做机器翻译或语义评分。
    • 根据显式 flags 检查格式。普通文本中的 % 或 {} 不触发推测检查;possible-* 和未支持的格式 flags 有提示。no-* 表示不检查对应格式,矛盾 flags 报错。
    • 标记和跨文件检查默认关闭,分别用 --markup html|xml、--consistency 启用。CGM-1 是受限平衡片段合同,不是完整 HTML5/XML 验证器;范围外构造明确失败。语言、上下文隔离与冲突审阅策略详见项目质量检查边界。
    • C printf 目标为 C11/POSIX 静态参数合同,另支持列明的 GNU 扩展。Python 目标为 3.12 的 str 百分号和 str.format 内建规格;支持 Unicode 名称、数字身份、自动路径和一层嵌套依赖。同一参数的不同合法用途记录为集合,可重排但须保留。合同语义和未覆盖范围见格式合同说明,不等同于完整格式化执行器。
    • 复数是受检的有符号 32 位整数语义,仅测试 n=0..plural_max,不证明所有整数输入都安全或所有形式都可达。默认上限 1000,可设 0..100000;嵌套、token 和操作预算有上限。报告包含实际检查进度。
    • 头字段诊断指向所在 msgstr 范围,不猜测 decoded metadata 的原始精确行。源引用按空白分词;包含空白的复杂文件名不是完整支持范围。
    • obsolete 条目保留但不检查。规范化拒绝解析诊断、无法关联的末尾注释及不能安全输出的结构,避免静默丢失数据。顺序和换行布局会规范化;不是逐字节往返。

    #MoonBit 库 API

    包名为 2111950632/szs。主要接口见 生成的公开接口 与 公开 API 测试:

    let catalog = @szs.parse_catalog(source, file="zh_CN.po")
    let report = @szs.check_catalog(catalog)
    let json = @szs.report_json(report)

    还提供原始字节入口、复数 parser/evaluator、格式合同、POT 对比、规范化和工作量统计。0.2.0 新 API 包括 markup_contract、check_markup_catalog、check_consistency、parse_project_policy 和 check_project。旧 Options、Policy 与 check_catalog 保持兼容。包名继续使用 2111950632/szs;版本对应的线上/发布验收另附交付记录。发布步骤见 发布说明。

    #验证

    moon check --target all --deny-warn moon test --target js --deny-warn moon test --target wasm-gc --deny-warn moon info moon fmt --check moon build --target js --release --deny-warn node --test tests/integration.mjs node scripts/count-source.mjs

    开发时工具链为 moon 0.1.20261012、moonc v0.10.15+943464bfa,Node.js 24.19.0。CI 记录实际工具链版本,使用官方当前工具链;工具链更新可能需要显式升级 API,失败不会被跳过。

    安装 Python 3.9 或更新版本后,npm run verify 按顺序完成核心检查、两种后端测试、CLI 测试、打包和独立 consumer 验证。它从 moon package 的真实压缩包提取源码,在单独项目中调用 API;需要 Python 标准库,不需要 polib。SZS_MOON、SZS_PYTHON 可指定本机工具路径。

    装好 Python 3.12、tests/quality-requirements.txt 中的固定参考工具和 GNU gettext 后,可执行 npm run verify:independent 加上独立对照。依赖缺失或网络下载失败会停止验证,不会跳过后宣称成功。CI 同时保存工具版本、示例报告、源码统计与包供下载。

    独立比对使用 polib 1.2.0 和固定提交的 MoonBit 官方中日文 PO 文件;只比较明确支持的解析字段。外部语料不计入项目代码、不打包分发:

    python -m pip install polib==1.2.0 node scripts/fetch-fixtures.mjs python tests/oracle.py tests/external/moonbit-docs-0.po tests/external/moonbit-docs-1.po # 安装 GNU gettext 后,可添加 --gnu 比较正常/异常样例的结果

    源码统计把生产代码、测试、生成接口、外部语料和 Node 入口分开。非空非整行注释的行数包含结构语法,不冒充主办方认证的有效代码行。

    #参赛与原创说明

    本项目从个人仓库的 README 初始提交继续开发。PO/POT 检查已有 GNU gettext、polib、Translate Toolkit 等成熟工具;本项目的价值是 MoonBit 检查核心、清楚的诊断与支持边界、可嵌入 API 和可重复运行的工程入口,不宣称全球首创。

    MoonBit 实现为本项目新编写,未复制 GNU gettext、polib 或 Translate Toolkit 的实现代码。使用 AI 辅助开发,由仓库作者负责审核、验证和提交。参考与外部测试来源见 来源说明。代码按 Apache-2.0 授权。

    比赛资格、申报截止与发布要求,以 比赛官网 和主办方最新章程为准。仓库中有 申报书;填写报名表和 Mooncakes 账号认证需由参赛者完成。

    版本修复见 更新记录,本次初审补充见 复审说明。

    Catalog

    pub(all) struct Catalog {
    file : String
    entries : Array[Entry]
    diagnostics : Array[Diagnostic]
    } derive(
    Debug
    )

    Catalog::header

    fn Catalog::header(self : Catalog) -> Entry?

    Catalog::to_repr

    CatalogHeader

    pub(all) struct CatalogHeader {
    fields : Array[HeaderField]
    diagnostics : Array[Diagnostic]
    } derive(
    Debug
    )

    CatalogHeader::get

    fn CatalogHeader::get(self : CatalogHeader, name : String) -> String?

    Comment

    pub(all) struct Comment {
    kind : String
    text : String
    span : Span
    } derive(Eq,
    Debug
    )

    Comment::equal

    fn Comment::equal(Comment, Comment) -> Bool

    Comment::not_equal

    fn Comment::not_equal(x : Comment, y : Comment) -> Bool

    Comment::to_repr

    ConsistencyOptions

    pub(all) struct ConsistencyOptions {
    enabled : Bool
    ignore_context : Bool
    inverse : Bool
    max_entries : Int
    } derive(
    Debug
    )

    ContextSummary

    pub(all) struct ContextSummary {
    context : String?
    active : Int
    translated : Int
    fuzzy : Int
    plural : Int
    characters : Int
    } derive(Eq,
    Debug
    )

    ContextSummary::equal

    ContextSummary::not_equal

    fn ContextSummary::not_equal(x : ContextSummary, y : ContextSummary) -> Bool

    Diagnostic

    pub(all) struct Diagnostic {
    code : String
    severity : Severity
    message : String
    file : String
    span : Span
    entry : Int?
    related : Span?
    hint : String
    } derive(Eq,
    Debug
    )

    Diagnostic::equal

    fn Diagnostic::equal(Diagnostic, Diagnostic) -> Bool

    Diagnostic::not_equal

    fn Diagnostic::not_equal(x : Diagnostic, y : Diagnostic) -> Bool

    Entry

    pub(all) struct Entry {
    context : String?
    id : String
    plural : String?
    translations : Array[Translation]
    flags : Array[String]
    comments : Array[Comment]
    references : Array[String]
    obsolete : Bool
    span : Span
    id_span : Span
    plural_span : Span?
    } derive(Eq,
    Debug
    )

    Entry::equal

    fn Entry::equal(Entry, Entry) -> Bool

    Entry::has_flag

    fn Entry::has_flag(self : Entry, flag : String) -> Bool

    Entry::is_header

    fn Entry::is_header(self : Entry) -> Bool

    Entry::is_translated

    fn Entry::is_translated(self : Entry) -> Bool

    Entry::not_equal

    fn Entry::not_equal(x : Entry, y : Entry) -> Bool

    Entry::to_repr

    Entry::translation

    fn Entry::translation(self : Entry, index : Int) -> String?

    FormatArgument

    pub(all) struct FormatArgument {
    name : String
    kind : String
    offset : Int
    } derive(Eq,
    Debug
    )

    FormatArgument::equal

    FormatArgument::not_equal

    fn FormatArgument::not_equal(x : FormatArgument, y : FormatArgument) -> Bool

    FormatContract

    pub(all) struct FormatContract {
    arguments : Array[FormatArgument]
    style : String
    } derive(Eq,
    Debug
    )

    FormatContract::equal

    FormatContract::not_equal

    fn FormatContract::not_equal(x : FormatContract, y : FormatContract) -> Bool

    FormatDifference

    pub(all) struct FormatDifference {
    code : String
    message : String
    argument : String
    } derive(Eq,
    Debug
    )

    FormatDifference::equal

    FormatDifference::not_equal

    fn FormatDifference::not_equal(x : FormatDifference, y : FormatDifference) -> Bool

    HeaderField

    pub(all) struct HeaderField {
    name : String
    value : String
    span : Span
    } derive(Eq,
    Debug
    )

    HeaderField::equal

    fn HeaderField::equal(HeaderField, HeaderField) -> Bool

    HeaderField::not_equal

    fn HeaderField::not_equal(x : HeaderField, y : HeaderField) -> Bool

    MarkupContract

    pub(all) struct MarkupContract {
    mode : String
    nodes : Int
    signature : String
    } derive(Eq,
    Debug
    )

    MarkupContract::equal

    MarkupContract::not_equal

    fn MarkupContract::not_equal(x : MarkupContract, y : MarkupContract) -> Bool

    MarkupError

    pub(all) struct MarkupError {
    offset : Int
    message : String
    } derive(Eq,
    Debug
    )

    MarkupError::equal

    fn MarkupError::equal(MarkupError, MarkupError) -> Bool

    MarkupError::not_equal

    fn MarkupError::not_equal(x : MarkupError, y : MarkupError) -> Bool

    MarkupOptions

    pub(all) struct MarkupOptions {
    mode : String
    translatable_attributes : Array[String]
    max_chars : Int
    max_nodes : Int
    max_depth : Int
    } derive(
    Debug
    )

    Options

    pub(all) struct Options {
    empty_severity : Severity
    fuzzy_severity : Severity
    strict : Bool
    plural_max : Int
    expression_depth : Int
    expression_budget : Int
    max_entries : Int
    max_line_chars : Int
    } derive(Eq,
    Debug
    )

    Options::equal

    fn Options::equal(Options, Options) -> Bool

    Options::not_equal

    fn Options::not_equal(x : Options, y : Options) -> Bool

    Options::to_repr

    PluralCoverage

    pub(all) struct PluralCoverage {
    maximum : Int
    checked_through : Int
    reached : Array[Int]
    examples : Array[Int]
    failures : Array[String]
    } derive(Eq,
    Debug
    )

    PluralCoverage::equal

    PluralCoverage::not_equal

    fn PluralCoverage::not_equal(x : PluralCoverage, y : PluralCoverage) -> Bool

    PluralExpr

    pub(all) enum PluralExpr {
    Constant(Int)
    Count
    Unary(String, PluralExpr)
    Binary(String, PluralExpr, PluralExpr)
    Conditional(PluralExpr, PluralExpr, PluralExpr)
    } derive(Eq,
    Debug
    )

    Parsed gettext plural expression. Arithmetic is checked signed 32-bit.

    PluralExpr::equal

    fn PluralExpr::equal(PluralExpr, PluralExpr) -> Bool

    PluralExpr::not_equal

    fn PluralExpr::not_equal(x : PluralExpr, y : PluralExpr) -> Bool

    PluralRule

    pub(all) struct PluralRule {
    nplurals : Int
    expression : String
    tree : PluralExpr
    } derive(Eq,
    Debug
    )

    PluralRule::equal

    fn PluralRule::equal(PluralRule, PluralRule) -> Bool

    PluralRule::not_equal

    fn PluralRule::not_equal(x : PluralRule, y : PluralRule) -> Bool

    Policy

    pub(all) struct Policy {
    options : Options
    overrides : Map[String, Severity]
    disabled : Array[String]
    } derive(
    Debug
    )

    Policy::to_repr

    Position

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

    Position::equal

    fn Position::equal(Position, Position) -> Bool

    Position::not_equal

    fn Position::not_equal(x : Position, y : Position) -> Bool

    Position::to_repr

    ProjectFinding

    pub(all) struct ProjectFinding {
    file_index : Int
    diagnostic : Diagnostic
    related_file : String?
    related_span : Span?
    related_entry : Int?
    } derive(
    Debug
    )

    ProjectPolicy

    pub(all) struct ProjectPolicy {
    policy : Policy
    markup : MarkupOptions
    consistency : ConsistencyOptions
    } derive(
    Debug
    )

    ProjectReport

    pub(all) struct ProjectReport {
    reports : Array[Report]
    findings : Array[ProjectFinding]
    exit_code : Int
    } derive(
    Debug
    )

    Reference

    pub(all) struct Reference {
    path : String
    line : Int?
    } derive(Eq,
    Debug
    )

    Reference::equal

    fn Reference::equal(Reference, Reference) -> Bool

    Reference::not_equal

    fn Reference::not_equal(x : Reference, y : Reference) -> Bool

    Report

    pub(all) struct Report {
    catalog : Catalog
    diagnostics : Array[Diagnostic]
    stats : Statistics
    exit_code : Int
    plural_checked_through : Int?
    } derive(
    Debug
    )

    Report::to_repr

    RuleDefinition

    pub(all) struct RuleDefinition {
    code : String
    category : String
    summary : String
    configurable : Bool
    } derive(Eq,
    Debug
    )

    RuleDefinition::equal

    RuleDefinition::not_equal

    fn RuleDefinition::not_equal(x : RuleDefinition, y : RuleDefinition) -> Bool

    Severity

    pub(all) enum Severity {
    Error
    Warning
    Info
    } derive(Eq,
    Debug
    )

    Diagnostic severity. Warning policy can be promoted by the caller.

    Severity::equal

    fn Severity::equal(Severity, Severity) -> Bool

    Explicit trait-method exports preserve the API on the October 2026 compiler.

    Severity::not_equal

    fn Severity::not_equal(x : Severity, y : Severity) -> Bool

    Explicit trait-method exports preserve the API on the October 2026 compiler.

    Severity::to_repr

    Span

    pub(all) struct Span {
    start : Position
    end : Position
    } derive(Eq,
    Debug
    )

    Span::equal

    fn Span::equal(Span, Span) -> Bool

    Span::not_equal

    fn Span::not_equal(x : Span, y : Span) -> Bool

    Span::to_repr

    Statistics

    pub(all) struct Statistics {
    total : Int
    active : Int
    obsolete : Int
    translated : Int
    untranslated : Int
    fuzzy : Int
    plural : Int
    errors : Int
    warnings : Int
    infos : Int
    } derive(Eq,
    Debug
    )

    Statistics::equal

    fn Statistics::equal(Statistics, Statistics) -> Bool

    Statistics::not_equal

    fn Statistics::not_equal(x : Statistics, y : Statistics) -> Bool

    TemplateDelta

    pub(all) struct TemplateDelta {
    missing : Array[String]
    extra : Array[String]
    changed_plural : Array[String]
    changed_flags : Array[String]
    diagnostics : Array[Diagnostic]
    } derive(
    Debug
    )

    Translation

    pub(all) struct Translation {
    index : Int
    value : String
    span : Span
    } derive(Eq,
    Debug
    )

    Translation::equal

    fn Translation::equal(Translation, Translation) -> Bool

    Translation::not_equal

    fn Translation::not_equal(x : Translation, y : Translation) -> Bool

    apply_policy

    fn apply_policy(report : Report, policy : Policy) -> Report

    brace_contract

    fn brace_contract(text : String) -> Result[FormatContract, String]

    Check Python str.format brace syntax; escaped {{ and }} do not consume args.

    catalog_json

    fn catalog_json(catalog : Catalog) -> Json

    catalog_summary_json

    fn catalog_summary_json(catalog : Catalog) -> Json

    check_against_template

    fn check_against_template(template : Catalog, translation : Catalog, options? : Options) -> Report

    check_bytes

    fn check_bytes(bytes : Bytes, file? : String, options? : Options, template? : Bool) -> Report

    check_catalog

    fn check_catalog(catalog : Catalog, options? : Options, template? : Bool) -> Report

    Validate a parsed catalog. Obsolete entries are retained but not checked.

    check_consistency

    fn check_consistency(catalogs : Array[Catalog], options? : ConsistencyOptions) -> Array[ProjectFinding]

    Compare exact translated strings across separate catalogs in the same locale.

    check_markup_catalog

    fn check_markup_catalog(catalog : Catalog, options? : MarkupOptions) -> Array[Diagnostic]

    check_project

    fn check_project(catalogs : Array[Catalog], configuration? : ProjectPolicy) -> ProjectReport

    Validate target catalogs and apply opt-in markup/project checks and policy.

    check_text

    fn check_text(text : String, file? : String, options? : Options, template? : Bool) -> Report

    compare_contracts

    fn compare_contracts(source : FormatContract, target : FormatContract) -> Array[FormatDifference]

    Compare argument identities and types, allowing reordered numbered arguments.

    compare_template

    fn compare_template(template : Catalog, translation : Catalog) -> TemplateDelta

    Match exact context/id keys; this does not guess fuzzy matches or edit files.

    context_summaries

    fn context_summaries(catalog : Catalog) -> Array[ContextSummary]

    Group workload by exact context, retaining null versus empty context.

    default_consistency_options

    fn default_consistency_options() -> ConsistencyOptions

    default_markup_options

    fn default_markup_options() -> MarkupOptions

    default_options

    fn default_options() -> Options

    default_policy

    fn default_policy() -> Policy

    default_project_policy

    fn default_project_policy() -> ProjectPolicy

    diagnostic_json

    fn diagnostic_json(diagnostic : Diagnostic) -> Json

    dispatch

    fn dispatch(request : String) -> String

    Host bridge: JSON in, JSON out. All checking stays inside MoonBit.

    entry_json

    fn entry_json(entry : Entry) -> Json

    evaluate_plural

    fn evaluate_plural(expression : PluralExpr, n : Int, budget? : Int, maximum_depth? : Int) -> Result[Int, String]

    Evaluate without using a host language's eval or executing arbitrary code.

    format_contract

    fn format_contract(style : String, text : String) -> Result[FormatContract, String]

    markup_contract

    fn markup_contract(text : String, options? : MarkupOptions) -> Result[MarkupContract, MarkupError]

    CGM-1 balanced fragments, not an HTML browser tree builder or XML validator.

    options_from_json

    fn options_from_json(base : Options, value : Json) -> Result[Options, String]

    parse_catalog

    fn parse_catalog(text : String, file? : String, options? : Options) -> Catalog

    Parse a catalog without throwing. Malformed entries produce diagnostics.

    parse_catalog_bytes

    fn parse_catalog_bytes(bytes : Bytes, file? : String, options? : Options) -> Catalog

    Decode a complete UTF-8 catalog for library clients that receive raw bytes.

    parse_plural_expression

    fn parse_plural_expression(text : String, maximum_depth? : Int) -> Result[PluralExpr, String]

    Parse only the expression, without running it.

    parse_plural_rule

    fn parse_plural_rule(text : String, maximum_depth? : Int) -> Result[PluralRule, String]

    Parse nplurals=...; plural=...;. Unknown or duplicate assignments fail.

    parse_policy

    fn parse_policy(text : String) -> Result[Policy, String]

    Only review-policy findings can be disabled or reclassified.

    parse_project_policy

    fn parse_project_policy(text : String) -> Result[ProjectPolicy, String]

    Extends the version-1 policy without changing parse_policy or Options.

    parse_reference

    fn parse_reference(value : String) -> Reference

    Split a terminal :line without confusing a Windows drive letter.

    parse_severity

    fn parse_severity(value : String) -> Severity?

    plural_coverage

    fn plural_coverage(rule : PluralRule, maximum? : Int, budget? : Int, maximum_depth? : Int) -> Result[PluralCoverage, String]

    Exhaustive only over the stated finite interval; this is not a proof for all n.

    printf_contract

    fn printf_contract(text : String) -> Result[FormatContract, String]

    Extract typed printf arguments, including positional width and precision.

    project_finding_json

    fn project_finding_json(finding : ProjectFinding) -> Json

    python_percent_contract

    fn python_percent_contract(text : String) -> Result[FormatContract, String]

    Python percent formatting uses mapping names or sequential arguments.

    quote_po

    fn quote_po(text : String) -> String

    Encode a string as a PO quoted literal, preserving Unicode code points.

    read_header

    fn read_header(catalog : Catalog) -> CatalogHeader

    Header spans point to the containing msgstr, not guessed decoded line positions.

    report_json

    fn report_json(report : Report) -> Json

    report_text

    fn report_text(report : Report) -> String

    rule_definitions

    fn rule_definitions() -> Array[RuleDefinition]

    Discoverable rule inventory for editors, CI tools, and config validation.

    serialize_catalog

    fn serialize_catalog(catalog : Catalog) -> Result[String, String]

    Canonical syntax only. Refuses parse errors; no automatic translation fixes.

    severity_name

    fn severity_name(severity : Severity) -> String

    statistics

    fn statistics(catalog : Catalog, diagnostics : Array[Diagnostic]) -> Statistics

    statistics_json

    fn statistics_json(stats : Statistics) -> Json

    validate_header

    fn validate_header(catalog : Catalog, header : CatalogHeader, template? : Bool) -> Array[Diagnostic]

    validate_markup_options

    fn validate_markup_options(options : MarkupOptions) -> Array[String]

    validate_options

    fn validate_options(options : Options) -> Array[String]