Skip to content

Best practices

Best Practices — Multilingual Catalogs

A multilingual catalog publishes a separate metadata tree for each language. The catalog uses one tree as its source and derives the other trees from it. alternate links connect equivalent catalogs and collections across the trees.

Use the STAC Language extension on each translated catalog and collection. It identifies the document's language and gives clients the information they need for a language selector.

The examples below use Romanian as the source language, with Russian and English translations. Core defines the required structure in Alternate-Language Trees.

The Basic Layout

Keep the source-language catalog at the root. Put each translation in a directory named with its RFC 5646 language tag:

national-geodata/
├── catalog.json              # Romanian source
├── README.md
├── AGENTS.md
├── cadastru/
│   ├── collection.json
│   ├── README.md
│   └── AGENTS.md
├── en/
│   ├── catalog.json
│   ├── README.md
│   ├── AGENTS.md             # may stay in the source language
│   └── cadastru/
│       ├── collection.json
│       ├── README.md
│       └── AGENTS.md
└── ru/
    └── ...

Use the same IDs and directory names in every tree. The file en/cadastru/collection.json is the English version of cadastru/collection.json, not a new collection with a new ID.

Only the metadata is duplicated. Data assets remain in the source tree, and translated metadata refers to them with relative hrefs.

Language Fields

Every translated catalog and collection declares the Language extension and its own language:

{
  "stac_extensions": [
    "https://schemas.portolan-sdi.org/portolan/v0.2.0/schema.json",
    "https://stac-extensions.github.io/language/v1.0.0/schema.json"
  ],
  "language": { "code": "ro", "name": "Română", "alternate": "Romanian" },
  "languages": [
    { "code": "ru", "name": "Русский", "alternate": "Russian" },
    { "code": "en", "name": "English" }
  ]
}

language describes the current document. languages lists the other available languages and must not repeat the current one.

Use an RFC 5646 tag for code. Write name in the language it names, such as Română or Русский. The optional alternate field gives a familiar name in another language. For a right-to-left language, add "dir": "rtl".

Each root catalog links to every other root catalog:

{
  "rel": "alternate",
  "href": "./en/catalog.json",
  "type": "application/json",
  "title": "Engleză",
  "hreflang": "en"
}

The type matters because an alternate link can also point to HTML or another media type. The hreflang identifies the target language. The title names that language for the current reader, so a Romanian document uses Engleză and an English document uses Romanian.

Keep these hrefs relative. From the English root, the Romanian root is ../catalog.json and the Russian root is ../ru/catalog.json.

Add the same links to equivalent collections. This lets a reader switch languages without losing their place in the catalog.

Do not use child to link a translated root. The translation represents the same catalog; it is not a subcatalog.

A language tree does not need to reproduce every item. A translated collection can link to an item in the source tree and identify its language with hreflang:

{
  "rel": "item",
  "href": "../../cadastru/parcele/parcele.json",
  "type": "application/geo+json",
  "title": "Cadastral parcels, 2024",
  "hreflang": "ro"
}

Translate the link title even when the target item remains in the source language. Clients can show the title without fetching the item.

Use hreflang on the describedby and agents links too. Both point at files in the same directory. The value names the language of the file it points at.

What to Translate

Translate the root catalog, the collections, and their README.md files. These carry the descriptions that people use to understand and find the data. The AGENTS.md files may stay in the source language, see Agent Guidance.

Translate the column descriptions under table:columns as well. A client shows them beside the column names, so they read as prose to the user. Keep the column names themselves unchanged.

Translate items only when their prose warrants it. Items often contain identifiers, geometries, times, and asset hrefs, so duplicating thousands of them may add little value.

Keep these values identical in every language:

  • Collection IDs, item IDs, and asset keys
  • Data column names
  • Style layer IDs
  • CRS identifiers
  • License identifiers

They are identifiers or properties of shared data. Stable values also let clients match equivalent objects across trees.

Agent Guidance

Every catalog and collection MUST carry an AGENTS.md, and the agents link MUST point at the sibling file. Core states this under AGENTS.md. A translated node is a catalog or a collection, so it carries the file too. rashid check reports PTL-FIL-001 and PTL-FIL-002 when it does not.

The language of that file is a separate choice. Technical guidance describes the data rather than the prose, so it may stay in the source language. Mark the link with the language of the file, not the language of the tree:

{
  "rel": "agents",
  "href": "./AGENTS.md",
  "type": "text/markdown",
  "title": "Guidance for AI agents",
  "hreflang": "ro"
}

Translate AGENTS.md when the guidance itself is about the language. The term glossary below is one example, because a translating agent reads it.

One Source Language

Choose one source language and treat every other tree as derived. Use a language that the publisher can check against the upstream material. In this example, the publisher chooses Romanian because the upstream material is in Romanian.

Apply corrections to the source tree first, then regenerate the affected translations. A direct edit to a derived tree will disappear during the next regeneration.

Record the source language and the regeneration command in the root AGENTS.md.

A Glossary for Important Terms

General translation tools often choose reasonable but inconsistent words. A short glossary prevents that drift and protects distinctions that matter in the data. Keep it in the root AGENTS.md:

## Translation

Source language: Romanian (`ro`). Edit `catalog.json`, `collection.json`,
`README.md`, and `AGENTS.md` in the root tree. Regenerate `en/` and `ru/`.

Use these terms. Do not substitute a synonym.

| Concept          | ro                   | ru                  | en               |
| ---------------- | -------------------- | ------------------- | ---------------- |
| cadastral parcel | parcelă cadastrală   | кадастровый участок | cadastral parcel |
| land cover       | acoperirea terenului | земельный покров    | land cover       |
| geodetic network | rețea geodezică      | геодезическая сеть  | geodetic network |

Keep the agency names and abbreviations AGCC and INGEOCAD unchanged.

Include terms that affect search or meaning. For example, Spanish uses riesgo for risk and peligro for hazard. Those terms are related, but they are not interchangeable in flood metadata.

Keep Translations Current

Use the source-tree diff to decide what to regenerate. If one collection description changes, update that collection in each translated tree. There is no need to translate the whole catalog again.

CI can catch omissions. Fail the check when a source metadata file changes without a corresponding change in every translated tree.

Check the Result

Run rashid check on the catalog. It validates each language tree and the links between them.

Some warnings use English-language heuristics. PTL-FIL-005 looks for license and provenance text in a collection README.md. PTL-TTL-002 checks whether a title looks like prose. Review these warnings instead of assuming they apply equally to every language.

Finally, open the catalog in STAC Browser. Check that every language appears in the selector and opens the equivalent catalog or collection.