File formats

Built-in formats, how plurals and placeholders round-trip, and how unknown formats are learned.

A format turns file bytes into entries (key, value, comment, plural forms) and entries back into a file. Built-in formats are code; learned formats are declarative templates the AI writes. Both run through the same interface, so everything else (dedupe, translation, review, pull) works the same for every file.

Built-in formats

FormatIdExtensionsLocales per file
Xcode String Catalogxcstrings.xcstringsall
Flutter ARBarb.arbone
JSON (key/value, i18next)json.jsonone
YAML (Rails i18n)yaml.yml .yamlone
Apple .stringsios-strings.stringsone
Apple .stringsdictios-stringsdict.stringsdictone
Android strings.xmlandroid-xml.xmlone
Gettext PO/POTgettext-po.po .potone
XLIFF 1.2 / 2.0xliff.xlf .xliffall
Java .propertiesproperties.propertiesone
.NET RESXresx.resxone
CSV / TSV (multi-locale)csv.csv .tsvall

Detection and locales

push_file scores every format against the file name and content and picks the most confident. Pass format to force one, or call detect_format to see the candidates without storing anything. The locale of a single-locale file comes from its path (locales/es.json, es.lproj, values-pt-rBR, messages_de.properties) or from locale. Paths are stored as patterns such as locales/{locale}.json, so one file record covers every locale.

What survives a round trip

  • Plurals are read into CLDR categories (zero, one, two, few, many, other) and written back in the format's own structure.
  • Comments become the string's context, which the translator sees, and are written back where the format has them.
  • Placeholders (printf, braces, ICU arguments, template literals, HTML tags) are checked on every translation. A translation that changes them is rejected with placeholder_mismatch or held for review.
  • Layout: serializers start from the file as last imported, so ordering, headers and format-specific attributes are kept where the format allows.

Formats it has never seen

When no format is confident about a file, it is not rejected. Instead:

  1. The file is stored with status needs_format and a learn_format job starts. You can also start one yourself with a hint about the structure.
  2. The AI reads a sample and writes a declarative template: where keys, values, comments and plurals are. The template is validated against your file; one that reads nothing is rejected.
  3. A learn_format task asks a reviewer to keep, retire or retry_with_hint.
  4. Kept templates are stored in your workspace and read every similar file from then on, with no model call.
npx @meterapp/glossa detect_format --json '{"filename":"strings.ts","content":"…"}'npx @meterapp/glossa learn_format --json '{"project":"web","path":"i18n/strings.ts","content":"…","hint":"TS object; keys are property names"}'npx @meterapp/glossa list_formats