///|
pub(all) struct TemplateDelta {
missing : Array[String]
extra : Array[String]
changed_plural : Array[String]
changed_flags : Array[String]
diagnostics : Array[Diagnostic]
} derive(Debug)
///|
fn display_key(entry : Entry) -> String {
match entry.context {
None => entry.id
Some(context) => "[" + context + "] " + entry.id
}
}
///|
fn active_index(catalog : Catalog) -> Map[String, Int32] {
let index : Map[String, Int32] = Map([])
for i, entry in catalog.entries {
if !entry.obsolete && !entry.is_header() {
index.set(entry_key(entry), i)
}
}
index
}
///|
/// Match exact context/id keys; this does not guess fuzzy matches or edit files.
pub fn compare_template(
template : Catalog,
translation : Catalog,
) -> TemplateDelta {
let missing : Array[String] = []
let extra : Array[String] = []
let changed_plural : Array[String] = []
let changed_flags : Array[String] = []
let diagnostics : Array[Diagnostic] = []
let expected = active_index(template)
let actual = active_index(translation)
for entry in template.entries {
if entry.obsolete || entry.is_header() {
continue
}
let key = entry_key(entry)
match actual.get(key) {
None => {
missing.push(display_key(entry))
diagnostics.push(
issue(
"T001",
Error,
"Missing template entry: " + display_key(entry),
translation.file,
line_span(1, 1, 0),
hint="Template source: " +
template.file +
":" +
entry.id_span.start.line.to_string(),
),
)
}
Some(index) => {
let translated = translation.entries[index]
if entry.plural != translated.plural {
changed_plural.push(display_key(entry))
diagnostics.push(
issue(
"T003",
Error,
"Plural source differs from the template",
translation.file,
translated.plural_span.unwrap_or(translated.id_span),
entry=index,
hint="Template source: " +
template.file +
":" +
entry.id_span.start.line.to_string(),
),
)
}
let mut flags_differ = false
for flag in entry.flags {
if flag.has_suffix("-format") && !translated.has_flag(flag) {
flags_differ = true
}
}
for flag in translated.flags {
if flag.has_suffix("-format") && !entry.has_flag(flag) {
flags_differ = true
}
}
if flags_differ {
changed_flags.push(display_key(entry))
diagnostics.push(
issue(
"T004",
Error,
"Format flags differ from the template",
translation.file,
translated.span,
entry=index,
),
)
}
}
}
}
for index, entry in translation.entries {
if entry.obsolete || entry.is_header() {
continue
}
if expected.get(entry_key(entry)) is None {
extra.push(display_key(entry))
diagnostics.push(
issue(
"T002",
Warning,
"Entry is absent from the template: " + display_key(entry),
translation.file,
entry.id_span,
entry=index,
),
)
}
}
{ missing, extra, changed_plural, changed_flags, diagnostics, }
}
///|
pub fn check_against_template(
template : Catalog,
translation : Catalog,
options~ : Options = default_options(),
) -> Report {
let report = check_catalog(translation, options~)
let diagnostics = report.diagnostics.copy()
let template_report = check_catalog(template, options~, template=true)
for diagnostic in template_report.diagnostics {
diagnostics.push(diagnostic)
}
if template_report.stats.errors == 0 {
for diagnostic in compare_template(template, translation).diagnostics {
diagnostics.push(diagnostic)
}
}
report_with(translation, diagnostics, options, report.plural_checked_through)
}