{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://veydrin.codeberg.page/protocols/vlds/vlds-v1.0.schema.json",
  "title": "VLDS v1.0 - Veydrin Language Data Standard",
  "description": "A single language-learning lesson: an ordered set of items, each with a source-language cue, one target-language variant per declared dialect (or a shared variant), a register, and optional open-licensed audio carrying attribution, a text alternative, and provenance.",
  "type": "object",
  "required": ["vlds_version", "id", "language", "title", "dialects", "items"],
  "properties": {
    "vlds_version": { "type": "string", "pattern": "^1\\.[0-9]+$", "description": "VLDS specification version this lesson conforms to. Consumers MUST accept any version with a matching major number." },
    "id": { "type": "string", "pattern": "\\S", "description": "Stable lesson identifier, unique within its pack." },
    "language": { "type": "string", "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$", "description": "BCP 47 tag of the target language taught, e.g. \"cy\". A language with no ISO code uses a private-use tag such as \"qaa\"." },
    "title": { "$ref": "#/$defs/lang_map", "description": "Lesson title, one entry per source language." },
    "dialects": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/dialect" }, "description": "Ordered dialects this lesson provides. A language with no dialect axis declares a single dialect; the recommended id is \"std\"." },
    "voices": { "type": "array", "items": { "$ref": "#/$defs/voice" }, "description": "Optional declared voices. When present, audio keys MUST be among these ids." },
    "items": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/item" } }
  },
  "patternProperties": { "^_": {} },
  "additionalProperties": false,
  "$defs": {
    "lang_map": {
      "type": "object",
      "minProperties": 1,
      "propertyNames": { "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$" },
      "additionalProperties": { "type": "string", "pattern": "\\S" },
      "description": "A map of BCP 47 tag to a non-empty NFC string."
    },
    "open_license": {
      "enum": [
        "CC0-1.0",
        "CC-BY-1.0", "CC-BY-2.0", "CC-BY-2.0-FR", "CC-BY-2.5", "CC-BY-3.0", "CC-BY-4.0",
        "CC-BY-SA-1.0", "CC-BY-SA-2.0", "CC-BY-SA-2.5", "CC-BY-SA-3.0", "CC-BY-SA-4.0"
      ],
      "description": "SPDX id of an open licence: CC0, or a CC-BY / CC-BY-SA version (including the ported forms real corpora use, such as Tatoeba's CC-BY-2.0-FR). NonCommercial and NoDerivatives are never permitted."
    },
    "attribution": {
      "type": "object",
      "required": ["name"],
      "properties": {
        "name": { "type": "string", "pattern": "\\S", "description": "Name to credit (person, pseudonym, or organization)." },
        "url": { "type": "string", "pattern": "\\S", "description": "Source URL of the original work." },
        "title": { "type": "string", "pattern": "\\S" }
      },
      "patternProperties": { "^_": {} },
      "additionalProperties": false
    },
    "dialect": {
      "type": "object",
      "required": ["id", "label"],
      "properties": {
        "id": { "type": "string", "pattern": "\\S", "description": "Pack-chosen dialect token, e.g. \"north\". Use \"std\" for a language with no dialect axis. Unique within the lesson." },
        "tag": { "type": "string", "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$", "description": "Optional single BCP 47 tag for the dialect. Private-use subtags are permitted but not interoperable." },
        "label": { "$ref": "#/$defs/lang_map" }
      },
      "patternProperties": { "^_": {} },
      "additionalProperties": false
    },
    "voice": {
      "type": "object",
      "required": ["id"],
      "properties": {
        "id": { "type": "string", "pattern": "\\S" },
        "label": { "$ref": "#/$defs/lang_map" },
        "gender": { "enum": ["female", "male", "neutral"] }
      },
      "patternProperties": { "^_": {} },
      "additionalProperties": false
    },
    "pronunciation": {
      "type": "object",
      "properties": {
        "ipa": { "type": "string", "pattern": "\\S", "description": "IPA transcription (BCP 47 fonipa). Phonemic transcription in / / unless a broader phonetic [ ] form is intended." },
        "respell": { "type": "string", "pattern": "\\S", "description": "A respelling aid (an English-speaker \"sounds-like\", say) for the item's primary source language. Non-normative; a future version may key it by source language." }
      },
      "patternProperties": { "^_": {} },
      "additionalProperties": false
    },
    "audio_clip": {
      "type": "object",
      "required": ["src", "license", "source_type"],
      "properties": {
        "src": { "type": "string", "pattern": "\\S", "description": "App-relative path or URL. A missing clip degrades to text; it is not a schema violation to omit the clip object entirely." },
        "license": { "$ref": "#/$defs/open_license" },
        "attribution": { "$ref": "#/$defs/attribution", "description": "REQUIRED when license is any CC-BY variant; optional for CC0-1.0." },
        "transcript": { "type": "string", "pattern": "\\S", "description": "Text alternative for the audio (WCAG 2.2 SC 1.2.1). A conformant consumer that finds no transcript MUST use the variant text as the transcript." },
        "source_type": { "enum": ["human", "synthetic"], "description": "human = IPTC digitalCapture; synthetic = IPTC trainedAlgorithmicMedia. Machine-detectable provenance." },
        "generated_by": { "type": "string", "pattern": "\\S", "description": "For synthetic audio: engine, voice, and version." },
        "provisional": { "type": "boolean", "description": "SHOULD be true when the audio is synthetic and not yet human-reviewed." },
        "modified": { "type": "boolean" },
        "modification_note": { "type": "string", "pattern": "\\S" }
      },
      "patternProperties": { "^_": {} },
      "additionalProperties": false,
      "allOf": [
        { "if": { "properties": { "license": { "pattern": "^CC-BY" } } }, "then": { "required": ["attribution"] } },
        { "if": { "required": ["modified"], "properties": { "modified": { "const": true } } }, "then": { "required": ["modification_note"] } }
      ]
    },
    "variant": {
      "type": "object",
      "required": ["text"],
      "properties": {
        "text": { "type": "string", "pattern": "\\S", "description": "The phrase to say, in Unicode NFC." },
        "pronunciation": { "$ref": "#/$defs/pronunciation" },
        "attribution": { "$ref": "#/$defs/attribution", "description": "Credit for the text. REQUIRED when the text license is a CC-BY variant (e.g. text ingested from Tatoeba)." },
        "license": { "$ref": "#/$defs/open_license", "description": "Licence of the text when it differs from the pack's content licence (e.g. an ingested sentence)." },
        "source": { "type": "string", "pattern": "\\S", "description": "URI of an upstream record this text comes from or corresponds to, e.g. a Wikidata Lexeme sense or a Tatoeba sentence." },
        "audio": {
          "type": "object",
          "minProperties": 1,
          "additionalProperties": { "$ref": "#/$defs/audio_clip" },
          "description": "Map of declared voice id to an audio clip."
        }
      },
      "patternProperties": { "^_": {} },
      "additionalProperties": false,
      "allOf": [
        { "if": { "properties": { "license": { "pattern": "^CC-BY" } }, "required": ["license"] }, "then": { "required": ["attribution"] } }
      ]
    },
    "item": {
      "type": "object",
      "required": ["id", "cue", "register"],
      "properties": {
        "id": { "type": "string", "pattern": "\\S", "description": "Item identifier, unique within the lesson." },
        "cue": { "$ref": "#/$defs/lang_map", "description": "The prompt, in one or more source languages." },
        "cue_audio": { "$ref": "#/$defs/audio_clip" },
        "shared": { "$ref": "#/$defs/variant", "description": "The variant for any declared dialect that has no entry in targets. Use it for phrases that do not vary by dialect, to avoid duplicating identical variants." },
        "targets": {
          "type": "object",
          "additionalProperties": { "$ref": "#/$defs/variant" },
          "description": "Map of dialect id to that dialect's variant. Keys MUST be declared dialect ids. Every declared dialect MUST be covered by either a targets entry or the item's shared variant."
        },
        "register": { "type": "string", "pattern": "\\S", "description": "Register of this item. An open string so any language can name its own registers; \"formal\" and \"colloquial\" are the recommended defaults, mapped to LexInfo register individuals." }
      },
      "patternProperties": { "^_": {} },
      "additionalProperties": false
    }
  }
}
