diff --git a/spec/README.md b/spec/README.md index 9626f7242b..f37e333543 100644 --- a/spec/README.md +++ b/spec/README.md @@ -12,6 +12,7 @@ 1. [Registry](registry.md) 1. [`registry.dtd`](registry.dtd) 1. [Formatting](formatting.md) +1. [Interchange data model](data-model/README.md) ## Introduction diff --git a/spec/data-model.md b/spec/data-model/README.md similarity index 93% rename from spec/data-model.md rename to spec/data-model/README.md index b78925fa64..672a630884 100644 --- a/spec/data-model.md +++ b/spec/data-model/README.md @@ -12,8 +12,12 @@ or for other operations to be performed on or with messages in this representati Implementations are not required to use this data model for their internal representation of messages. To ensure compatibility across all platforms, -this interchange data model is defined in terms of JSON-compatible values -using TypeScript syntax for their definition. +this interchange data model is defined here using TypeScript notation. +Two equivalent definitions of the data model are also provided: +- [`message.json`](./message.json) is a JSON Schema definition, + for use with message data encoded as JSON or compatible formats, such as YAML. +- [`message.dtd`](./message.dtd) is a document type definition (DTD), + for use with message data encoded as XML. ## Messages diff --git a/spec/data-model/message.dtd b/spec/data-model/message.dtd new file mode 100644 index 0000000000..ad3dcdbd93 --- /dev/null +++ b/spec/data-model/message.dtd @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/data-model/message.json b/spec/data-model/message.json new file mode 100644 index 0000000000..5d2f64920f --- /dev/null +++ b/spec/data-model/message.json @@ -0,0 +1,162 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://github.com/unicode-org/message-format-wg/blob/main/spec/data-model/message.json", + + "oneOf": [{ "$ref": "#/$defs/message" }, { "$ref": "#/$defs/select" }], + + "$defs": { + "literal": { + "type": "object", + "properties": { + "type": { "const": "literal" }, + "quoted": { "type": "boolean" }, + "value": { "type": "string" } + }, + "required": ["type", "quoted", "value"] + }, + "variable": { + "type": "object", + "properties": { + "type": { "const": "variable" }, + "name": { "type": "string" } + }, + "required": ["type", "name"] + }, + "value": { + "oneOf": [{ "$ref": "#/$defs/literal" }, { "$ref": "#/$defs/variable" }] + }, + + "function": { + "type": "object", + "properties": { + "type": { "const": "function" }, + "kind": { "enum": ["open", "close", "value"] }, + "name": { "type": "string" }, + "operand": { "$ref": "#/$defs/value" }, + "options": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "value": { "$ref": "#/$defs/value" } + }, + "required": ["name", "value"] + } + } + }, + "required": ["type", "kind", "name"] + }, + "reserved": { + "type": "object", + "properties": { + "type": { "const": "reserved" }, + "sigil": { + "enum": ["!", "@", "#", "%", "^", "&", "*", "<", ">", "?", "~"] + }, + "source": { "type": "string" }, + "operand": { "$ref": "#/$defs/value" } + }, + "required": ["type", "sigil", "source"] + }, + + "text": { + "type": "object", + "properties": { + "type": { "const": "text" }, + "value": { "type": "string" } + }, + "required": ["type", "value"] + }, + "expression": { + "type": "object", + "properties": { + "type": { "const": "expression" }, + "body": { + "oneOf": [ + { "$ref": "#/$defs/literal" }, + { "$ref": "#/$defs/variable" }, + { "$ref": "#/$defs/function" }, + { "$ref": "#/$defs/reserved" } + ] + } + }, + "required": ["type", "body"] + }, + "pattern": { + "type": "object", + "properties": { + "body": { + "type": "array", + "items": { + "oneOf": [ + { "$ref": "#/$defs/text" }, + { "$ref": "#/$defs/expression" } + ] + } + } + }, + "required": ["body"] + }, + + "declarations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "value": { "$ref": "#/$defs/expression" } + }, + "required": ["target", "value"] + } + }, + "variant-key": { + "oneOf": [ + { "$ref": "#/$defs/literal" }, + { + "type": "object", + "properties": { + "type": { "const": "*" }, + "value": { "type": "string" } + }, + "required": ["type"] + } + ] + }, + "message": { + "type": "object", + "properties": { + "type": { "const": "message" }, + "declarations": { "$ref": "#/$defs/declarations" }, + "pattern": { "$ref": "#/$defs/pattern" } + }, + "required": ["type", "declarations", "pattern"] + }, + "select": { + "type": "object", + "properties": { + "type": { "const": "select" }, + "declarations": { "$ref": "#/$defs/declarations" }, + "selectors": { + "type": "array", + "items": { "$ref": "#/$defs/expression" } + }, + "variants": { + "type": "array", + "items": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "items": { "$ref": "#/$defs/variant-key" } + }, + "value": { "$ref": "#/$defs/pattern" } + }, + "required": ["keys", "value"] + } + } + }, + "required": ["type", "declarations", "selectors", "variants"] + } + } +}