All formats

Template format, version 1

A template defines which fields items of one category have. Templates are UTF-8 JSON documents. Built-in templates ship in Packages/KuriosaKit/Sources/KuriosaCore/Resources/Templates/; every template used by a library is copied into its template table, so a library is self-contained.

Document

{
  "id": "builtin.watch",
  "version": 1,
  "name": { "en": "Watches", "de": "Uhren", "fr": "Montres", "it": "Orologi", "es": "Relojes" },
  "icon": "watch.analog",
  "sections": [ { "id": "identity", "label": { "en": "Identity", "…": "…" } } ],
  "fields": [ { "key": "brand", "type": "text", "label": { "en": "Brand" }, "section": "identity", "identifying": true } ]
}
KeyMeaning
idStable ID. Built-in: builtin.<name>. User templates: custom.<uuid>.
versionContent version; the app replaces stored built-ins with higher versions.
name, label, suffixLocalized text: object of language code → text; en is required and the fallback.
iconSF Symbol name (Android maps it to its own icon set).
sectionsField groups in display order.
fieldsFields in display order.
highlightsOptional: up to three key facts shown on gallery cards and lists, in this order: visible, non-sensitive field keys, and @count for the count of a collection that counts (quantity below; issue #55). @count cannot be a field key; it is left out while the count is removed (removedStandardFields) and comes back with it. Without it, the first three visible, non-sensitive fields are used; a list with only @count shows the count only.
basedOnOptional: for a customized copy, the ID of the template it was copied from.
quantityOptional: the collection's pieces are counted (issue #41, decision 0031), and what one unit is called: {"one": <localized>, "other": <localized>}, for example {"one": {"en": "bottle"}, "other": {"en": "bottles"}}; both are optional (the app then says "piece" and "pieces"), {} counts pieces. Without it, nothing is counted. The count comes from the item's journal (item.purchase_quantity and item.events, see catalog-schema.md). Built-ins: wine, spirits, and inks (bottles), trading cards (copies), ammunition (rounds), coins, banknotes, stamps, sealed products, and the blank template (pieces).
suggestionsOptional: the sub-collections this category suggests (issue #86, decision 0041), in the order they are offered; see "Suggested sub-collections" below. [] suggests nothing on purpose.
removedStandardFieldsOptional: standard fields this collection does not show or edit (issue #24): any of quantity (only with quantity above), purchase_date, purchase_price, source, estimated_value, condition, place, tags, files (the documents; photos stay), notes, in this order. The name cannot be removed. Values stay on the items, so adding a field back shows them again. Without it, all standard fields are shown.
showcaseOptional: how the objects are shown on their showcase stage: strap (watches, bracelets; fills the stage height, the ends fade), standing (bottles, figurines; stands on a plinth line), wide (cars, knives in profile), lying (coins, stamps, art on paper; on a tray, front and back side by side), or passepartout (a photo inside a mat). Without it, the photo's shape decides: a photo that is not cut out goes into a passepartout, a cut-out wider than 1.6 × its height is wide, every other cut-out stands. lying only ever comes from the template. A customized copy without its own mode uses the mode of the template it is basedOn. Built-ins: watches and straps strap; coins, banknotes, stamps, trading cards, comics, and records lying; wine, spirits, perfume, inks, handbags, books, and toys standing; cars, knives, firearms, pens, scale models, and optics wide; the others none.

Built-in templates

builtin.<name> with <name>.json in the resources. "New collection" offers them in groups (TemplateGroup, issue #86), in this order:

GroupTemplates
Wear and carrywatch, jewelry, sneaker, handbag, pen, knife
Coins and papercoin, banknote, stamp, trading_card, comic, book
Art, music, and playart, record, instrument, toy, scale_model, video_game
Drinks and scentswine, spirits, perfume
Gear and naturecar, camera, mineral, firearm, ammunition
Start emptyblank ("Custom collection")

Offered only as suggested sub-collections, never in "New collection": strap, optic, lens, ink, console, sealed_product. wishlist holds the wishlist's own fields (below). The group names are app text (String Catalog), not template data.

Suggested sub-collections (decision 0041)

A template may name the sub-collections it suggests, for example in builtin.firearm:

"suggestions": [
  { "template": "builtin.ammunition" },
  { "template": "builtin.optic" },
  { "template": "builtin.blank", "name": { "en": "Accessories", "de": "Zubehör", "…": "…" }, "icon": "tray.2" }
]
KeyMeaning
templateOptional: the built-in template the sub-collection is made from. Without it, the sub-collection gets the fields of the collection it sits in (a coin collection's "Medals"). A template the app does not know is not offered.
nameOptional, localized: its name; without it, the template's name. Required when template is absent.
iconOptional: its SF Symbol; without it, the template's. Required when template is absent.

Rules (reimplement exactly):

Field

KeyTypeDefaultMeaning
keystring–Storage key, [a-z][a-z0-9_]*, unique, never renamed once shipped
typestring–text, number, date, currency, dropdown, multiple_choice, rating, checkbox, url, quantity, measurement, relation, calculation
labellocalized–Display label
sectionstringfirst sectionSection ID
requiredboolfalseA value must be present
sensitiveboolfalseHidden by default, excluded from exports, blurred in privacy mode
identifyingboolfalseUsed by the duplicate check
optionsarray–{id, label} choices for dropdown and multiple_choice, units for quantity; required for all three
measurementstring–length, mass, or volume; required for measurement
unitHintobject–{"metric": "<unit>", "imperial": "<unit>"} preferred display units
ratingMaxint51…10
suffixlocalized–Label after a number (for example h, kW)
multilineboolfalseMulti-line text
examplelocalized–A typical value for text and number fields, shown as a quiet prompt in an empty field ("e.g. 79030N"). Never on sensitive fields. May be English only when it is a name or a number.
formulaobject–Required for calculation: {"left": <operand>, "operation": "add" \| "subtract" \| "multiply" \| "divide", "right": <operand>}; see below
hiddenboolfalseNot shown or edited; values stay on items. Fields are never deleted.

Older templates may have countsDown on a quantity field (issue #26); readers ignore it. The collection's count (quantity above) replaced it (decision 0031).

Options ({id, label, hidden}): a hidden option is not offered for new values but still displays on items that use it.

Multiple choice fields (decision 0042)

A multiple_choice field holds any number of its options (issue #87), a dropdown exactly one. Rules (reimplement exactly):

Unit identifiers: mm cm m km in ft mi (length), g kg ct gr oz ozt lb (mass), ml cl l fl_oz gal (volume). Conversion factors to the canonical unit are defined in KuriosaCore/Values/Units.swift (DisplayUnit.canonicalFactor).

Calculation fields (decision 0025)

A calculation field shows a value worked out from two others, for example "Price per round = Purchase price ÷ Rounds":

{ "key": "price_per_round", "type": "calculation", "label": { "en": "Price per round" },
  "formula": { "left": { "item": "purchasePrice" }, "operation": "divide", "right": { "field": "rounds" } } }

An operand is an object with exactly one key:

Rules (reimplement exactly):

Validation

TemplateValidator enforces: valid key format, unique keys, English labels, known sections, non-empty unique options for dropdown/multiple_choice/quantity, a kind for measurement and hints of the same kind, ratingMax in 1…10, for calculation a formula whose fields exist and can be calculated with, no circle, neither required nor identifying, and sensitive when it uses a sensitive field, and for suggestions a non-empty template and icon where given and English in name.

Customizing (decision 0014)

The wishlist template (decision 0017)

builtin.wishlist holds the fields every wish gets on top of its collection's fields. It ships with one field, shop_url (url, "Link to buy"), and is not offered as a collection template. It is customized like any template; the copy in use is named by library_meta.wishlist_template_id. Its values are stored in item.wish_fields.

Why translations live in the template

Templates are data and must stay portable to Android and shareable later, so their labels carry their own translations instead of living in the app's String Catalog. User-made templates usually have only one language; the English key doubles as the fallback.