Specification scope: Core with the Alternate Names and Units companion specifications.
An enum value is an identifier, not finished user-interface text.
PENDING_PAYMENT may be a good stable value in a schema. It is a poor label on
a German invoice, and a legacy API may insist on the code P. Replacing the
enum value for either consumer would make presentation and transport choices
part of the type’s identity.
The alternate-names extension uses altenums to keep that distinction in the
schema. The enum array holds the canonical values. Purpose maps attach
external or localized representations to them.
Keep the values boring and stable
The order-status schema keeps the canonical values separate from their mappings:
{
"$schema": "https://json-structure.org/meta/extended/v0/#",
"$id": "https://example.com/schemas/order-status",
"name": "OrderStatusDocument",
"$uses": ["JSONStructureAlternateNames"],
"$root": "#/definitions/OrderStatusRecord",
"definitions": {
"OrderStatus": {
"name": "OrderStatus",
"type": "string",
"enum": [
"PENDING_PAYMENT",
"PAID",
"SHIPPED",
"CANCELLED"
],
"altenums": {
"json": {
"PENDING_PAYMENT": "P",
"PAID": "D",
"SHIPPED": "S",
"CANCELLED": "X"
},
"erp": {
"PENDING_PAYMENT": "10",
"PAID": "20",
"SHIPPED": "30",
"CANCELLED": "90"
},
"lang:en": {
"PENDING_PAYMENT": "Pending payment",
"PAID": "Paid",
"SHIPPED": "Shipped",
"CANCELLED": "Cancelled"
},
"lang:de": {
"PENDING_PAYMENT": "Zahlung ausstehend",
"PAID": "Bezahlt",
"SHIPPED": "Versandt",
"CANCELLED": "Storniert"
}
}
},
"OrderStatusRecord": {
"name": "OrderStatusRecord",
"type": "object",
"properties": {
"orderId": { "type": "string" },
"status": {
"type": { "$ref": "#/definitions/OrderStatus" }
}
},
"required": ["orderId", "status"],
"additionalProperties": false
}
}
}
A JSON encoder that supports the reserved json purpose can represent the
canonical value SHIPPED as S:
{
"$schema": "https://example.com/schemas/order-status",
"orderId": "O-2026-1042",
"status": "S"
}
That encoded instance depends on an extension-aware processor applying the
mapping. The core enum still contains SHIPPED, not S. A processor that
ignores altenums sees only the canonical contract and must not be expected to
validate the mapped representation as though the mapping did not exist.
The map has two levels
The first level names a purpose. The second maps every canonical enum value to its alternate representation:
{
"lang:de": {
"PENDING_PAYMENT": "Zahlung ausstehend",
"PAID": "Bezahlt",
"SHIPPED": "Versandt",
"CANCELLED": "Storniert"
}
}
json is reserved for JSON encoding. Keys beginning with lang: are reserved
for localized alternatives. Their suffixes are language tags. Other keys, such
as erp, are permitted custom purposes, but the draft assigns no standard
behavior to them.
The mapping leaves the canonical value unchanged. The user may see
Versandt, while business logic continues to compare SHIPPED. Editors can
change the translation without changing the schema contract.
These are not unit symbols
The draft calls the mapped values alternate representations or symbols. That
does not make altenums the same feature as the units extension’s symbol and
symbols keywords.
altenums maps individual members of an enum. The units annotations attach a
presentation symbol such as € or °C to a schema element and may accompany a
number. They have different placement, shape, and purpose. An order status does
not acquire a symbol annotation merely because one external system encodes it
as S.
Policy stays outside the map
altenums does not select a locale or negotiate one. It does not define fallback
from lang:de-CH to lang:de. It does not say whether an ERP adapter is allowed
to accept both 30 and SHIPPED. Those are processor and application policies.
Nor does it turn labels into identities. Localized text can change for editorial reasons. Treat a canonical enum value change as a contract change.
One enum can serve a UI and several external systems without giving any of them ownership of its canonical values.