UJG / UJG 1.0 Release Candidate 3 /
GitHub
Table of Contents
  • 1Overview
  • 2Terminology
  • 3Locale
  • 4MessageMeta
  • 5Message
  • 6Attachment Model
  • 7Normative Artifacts
    • 7.1Ontology
    • 7.2JSON-LD Context
    • 7.3Validation
  • 8Examples
    • 8.1Shared-Topology State-Preserving Switches
    • 8.2Locale Switch Affordance Example
    • 8.3Combined JSON Example
    • 8.4Opaque Runtime Hints
W3C Community Group Draft Report

Localization

draft

Status of this Document

This report was published by the User Journey Graph Community Group . It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups .

Status
CG-DRAFT
Version
1.0-rc3
Published
2026-11-09
Last Update
2026-11-09
Editors
  • Seva Dolgopolov
Group
User Journey Graph Community Group
Repository
View Source
License
W3C-Software-and-Document

1. Overview

This optional module defines a graph-native vocabulary for attaching localization metadata to UJG nodes. It lets a producer bind any addressable node to a reusable MessageMeta resource through l10n:copyRef, and model concrete locale-specific text as addressable Message nodes.

This module is optional. Its properties annotate Graph resources and do not themselves change topology, traversal rules, or import resolution. A producer may nevertheless model different Graph Journeys, States, or affordances when content structure genuinely differs by locale; Graph owns those topology differences.

2. Terminology

  • Locale: An addressable locale resource identified by a language tag.

  • MessageMeta: An addressable message identity and shared metadata resource.

  • Message: An addressable locale-specific value for one MessageMeta and one Locale.

3. Locale

A Locale identifies one locale used by localization metadata or locale-specific messages.

classDiagram
  class Locale {
    id
    localeCode
  }

Example JSON node:

json
{
  "@type": "l10n:Locale",
  "@id": "urn:l10n:locale:en",
  "l10n:localeCode": "en"
}
{
  "@type": "l10n:Locale",
  "@id": "urn:l10n:locale:en",
  "l10n:localeCode": "en"
}

4. MessageMeta

A MessageMeta identifies a reusable copy contract. The MessageMeta IRI is the stable message identity. UJG nodes reference a message meta node with l10n:copyRef.

classDiagram
  class Locale
  class MessageMeta {
    id
    argumentNames
    defaultLocaleRef
    fallbackLocaleRefs
    rtl
  }
  MessageMeta --> Locale : defaultLocaleRef
  MessageMeta --> "0..*" Locale : fallbackLocaleRefs

Example JSON node:

json
{
  "@type": "l10n:MessageMeta",
  "@id": "urn:l10n:message-meta:order-confirmation-title",
  "l10n:argumentNames": ["orderNumber"],
  "l10n:defaultLocaleRef": "urn:l10n:locale:en",
  "l10n:fallbackLocaleRefs": ["urn:l10n:locale:en", "urn:l10n:locale:de"],
  "l10n:rtl": false
}
{
  "@type": "l10n:MessageMeta",
  "@id": "urn:l10n:message-meta:order-confirmation-title",
  "l10n:argumentNames": ["orderNumber"],
  "l10n:defaultLocaleRef": "urn:l10n:locale:en",
  "l10n:fallbackLocaleRefs": ["urn:l10n:locale:en", "urn:l10n:locale:de"],
  "l10n:rtl": false
}

5. Message

A Message carries one locale-specific string value for one MessageMeta and one Locale. The value is an opaque string from the perspective of Localization conformance.

classDiagram
  class MessageMeta
  class Locale
  class Message {
    id
    messageMetaRef
    localeRef
    value
  }
  Message --> MessageMeta : messageMetaRef
  Message --> Locale : localeRef

Example JSON node:

json
{
  "@type": "l10n:Message",
  "@id": "urn:l10n:message:order-confirmation-title:en",
  "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
  "l10n:localeRef": "urn:l10n:locale:en",
  "l10n:value": "Order ${orderNumber} confirmed"
}
{
  "@type": "l10n:Message",
  "@id": "urn:l10n:message:order-confirmation-title:en",
  "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
  "l10n:localeRef": "urn:l10n:locale:en",
  "l10n:value": "Order ${orderNumber} confirmed"
}

6. Attachment Model

The module introduces real JSON-LD terms and RDF edges for localization attachment:

  • l10n:copyRef links any UJG node to a MessageMeta.

  • l10n:messageMetaRef links a Message to the MessageMeta it realizes.

  • l10n:localeRef links a Message to its Locale.

  • l10n:defaultLocaleRef and l10n:fallbackLocaleRefs link MessageMeta to fallback locale resources.

  • l10n:targetLocaleRef declares the requested Locale associated with an OutgoingTransition.

classDiagram
  class Node {
    copyRef
  }
  class MessageMeta
  class Locale
  class OutgoingTransition {
    targetLocaleRef
  }
  Node --> MessageMeta : copyRef
  OutgoingTransition --> Locale : targetLocaleRef

Example JSON nodes:

json
[
  {
    "@type": "Journey",
    "@id": "urn:ujg:journey:order-confirmation",
    "defaultEntryRef": "urn:ujg:entry:order-confirmation",
    "entryRefs": ["urn:ujg:entry:order-confirmation"],
    "stateRefs": ["urn:ujg:state:order-confirmation"]
  },
  {
    "@type": "JourneyEntry",
    "@id": "urn:ujg:entry:order-confirmation",
    "stateRef": "urn:ujg:state:order-confirmation"
  },
  {
    "@type": "State",
    "@id": "urn:ujg:state:order-confirmation",
    "label": "Order confirmation",
    "outgoingTransitionRefs": ["urn:example:ot:language-english"],
    "l10n:copyRef": "urn:l10n:message-meta:order-confirmation-title"
  },
  {
    "@type": "l10n:Locale",
    "@id": "urn:l10n:locale:en",
    "l10n:localeCode": "en"
  },
  {
    "@type": "l10n:MessageMeta",
    "@id": "urn:l10n:message-meta:order-confirmation-title",
    "l10n:argumentNames": ["orderNumber"],
    "l10n:defaultLocaleRef": "urn:l10n:locale:en"
  },
  {
    "@type": "l10n:Message",
    "@id": "urn:l10n:message:order-confirmation-title:en",
    "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
    "l10n:localeRef": "urn:l10n:locale:en",
    "l10n:value": "Order ${orderNumber} confirmed"
  },
  {
    "@type": "OutgoingTransition",
    "@id": "urn:example:ot:language-english",
    "label": "English",
    "toEntryRef": "urn:ujg:entry:order-confirmation",
    "l10n:targetLocaleRef": "urn:l10n:locale:en"
  }
]
[
  {
    "@type": "Journey",
    "@id": "urn:ujg:journey:order-confirmation",
    "defaultEntryRef": "urn:ujg:entry:order-confirmation",
    "entryRefs": ["urn:ujg:entry:order-confirmation"],
    "stateRefs": ["urn:ujg:state:order-confirmation"]
  },
  {
    "@type": "JourneyEntry",
    "@id": "urn:ujg:entry:order-confirmation",
    "stateRef": "urn:ujg:state:order-confirmation"
  },
  {
    "@type": "State",
    "@id": "urn:ujg:state:order-confirmation",
    "label": "Order confirmation",
    "outgoingTransitionRefs": ["urn:example:ot:language-english"],
    "l10n:copyRef": "urn:l10n:message-meta:order-confirmation-title"
  },
  {
    "@type": "l10n:Locale",
    "@id": "urn:l10n:locale:en",
    "l10n:localeCode": "en"
  },
  {
    "@type": "l10n:MessageMeta",
    "@id": "urn:l10n:message-meta:order-confirmation-title",
    "l10n:argumentNames": ["orderNumber"],
    "l10n:defaultLocaleRef": "urn:l10n:locale:en"
  },
  {
    "@type": "l10n:Message",
    "@id": "urn:l10n:message:order-confirmation-title:en",
    "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
    "l10n:localeRef": "urn:l10n:locale:en",
    "l10n:value": "Order ${orderNumber} confirmed"
  },
  {
    "@type": "OutgoingTransition",
    "@id": "urn:example:ot:language-english",
    "label": "English",
    "toEntryRef": "urn:ujg:entry:order-confirmation",
    "l10n:targetLocaleRef": "urn:l10n:locale:en"
  }
]

Here the locale switch explicitly re-enters the same Journey and State through its entry contract. Localization supplies the requested locale; Graph supplies the destination.

The module also defines non-reference properties l10n:localeCode, l10n:argumentNames, l10n:rtl, and l10n:value.

7. Normative Artifacts

This module is published through the following artifacts:

  • l10n.ttl: ontology, published at https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n

  • l10n.context.jsonld: JSON-LD term mappings, published at https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld

  • l10n.shape.ttl: SHACL validation rules, published at https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.shape

Examples in this page compose the shared baseline context https://ujg.specs.openuji.org/tr/1.0-rc3/ns/context.jsonld with the Localization context.

Non-goals:

  • This module does not define locale negotiation, runtime translation loading, JavaScript evaluation, ICU formatting behavior, pluralization, or runtime interpolation APIs.

  • This module does not introduce new traversal semantics beyond [UJG Graph].

  • This module does not replace opaque vendor-private hints carried in [UJG Core] extensions.

7.1. Ontology

The normative Localization ontology is defined below and is published at https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n. It is the authoritative structural definition for Locale, MessageMeta, Message, and the properties declared by this module.

turtle
@prefix ujg: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph#> .
@prefix ujgl10n: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .

<https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#> a owl:Ontology ;
  rdfs:label "UJG Localization 1.0 Vocabulary"@en ;
  dct:description "UJG Localization ontology declaration" .

### Classes

ujgl10n:Locale a owl:Class ;
  rdfs:subClassOf ujg:Node .

ujgl10n:MessageMeta a owl:Class ;
  rdfs:subClassOf ujg:Node .

ujgl10n:Message a owl:Class ;
  rdfs:subClassOf ujg:Node .

### Properties

ujgl10n:copyRef a owl:ObjectProperty ;
  rdfs:domain ujg:Node ;
  rdfs:range ujgl10n:MessageMeta .

ujgl10n:localeCode a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:Locale ;
  rdfs:range xsd:language .

ujgl10n:argumentNames a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range xsd:string .

ujgl10n:defaultLocaleRef a owl:ObjectProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range ujgl10n:Locale .

ujgl10n:fallbackLocaleRefs a owl:ObjectProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range ujgl10n:Locale .

ujgl10n:rtl a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range xsd:boolean .

ujgl10n:messageMetaRef a owl:ObjectProperty ;
  rdfs:domain ujgl10n:Message ;
  rdfs:range ujgl10n:MessageMeta .

ujgl10n:localeRef a owl:ObjectProperty ;
  rdfs:domain ujgl10n:Message ;
  rdfs:range ujgl10n:Locale .

ujgl10n:value a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:Message ;
  rdfs:range xsd:string .

ujgl10n:targetLocaleRef a owl:ObjectProperty ;
  rdfs:domain ujggraph:OutgoingTransition ;
  rdfs:range ujgl10n:Locale .
@prefix ujg: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph#> .
@prefix ujgl10n: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .

<https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#> a owl:Ontology ;
  rdfs:label "UJG Localization 1.0 Vocabulary"@en ;
  dct:description "UJG Localization ontology declaration" .

### Classes

ujgl10n:Locale a owl:Class ;
  rdfs:subClassOf ujg:Node .

ujgl10n:MessageMeta a owl:Class ;
  rdfs:subClassOf ujg:Node .

ujgl10n:Message a owl:Class ;
  rdfs:subClassOf ujg:Node .

### Properties

ujgl10n:copyRef a owl:ObjectProperty ;
  rdfs:domain ujg:Node ;
  rdfs:range ujgl10n:MessageMeta .

ujgl10n:localeCode a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:Locale ;
  rdfs:range xsd:language .

ujgl10n:argumentNames a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range xsd:string .

ujgl10n:defaultLocaleRef a owl:ObjectProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range ujgl10n:Locale .

ujgl10n:fallbackLocaleRefs a owl:ObjectProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range ujgl10n:Locale .

ujgl10n:rtl a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:MessageMeta ;
  rdfs:range xsd:boolean .

ujgl10n:messageMetaRef a owl:ObjectProperty ;
  rdfs:domain ujgl10n:Message ;
  rdfs:range ujgl10n:MessageMeta .

ujgl10n:localeRef a owl:ObjectProperty ;
  rdfs:domain ujgl10n:Message ;
  rdfs:range ujgl10n:Locale .

ujgl10n:value a owl:DatatypeProperty ;
  rdfs:domain ujgl10n:Message ;
  rdfs:range xsd:string .

ujgl10n:targetLocaleRef a owl:ObjectProperty ;
  rdfs:domain ujggraph:OutgoingTransition ;
  rdfs:range ujgl10n:Locale .

7.2. JSON-LD Context

The normative Localization JSON-LD context is defined below and is published at https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld. It provides the compact JSON-LD term mappings and coercions for Localization-specific properties and classes.

json-ld
{
  "@context": {
    "@version": 1.1,

    "ujgl10n": "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#",
    "l10n": "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#",
    "xsd": "http://www.w3.org/2001/XMLSchema#",

    "Locale": "ujgl10n:Locale",
    "MessageMeta": "ujgl10n:MessageMeta",
    "Message": "ujgl10n:Message",

    "copyRef": {
      "@id": "ujgl10n:copyRef",
      "@type": "@id"
    },
    "l10n:copyRef": {
      "@id": "ujgl10n:copyRef",
      "@type": "@id"
    },
    "localeCode": {
      "@id": "ujgl10n:localeCode",
      "@type": "xsd:language"
    },
    "l10n:localeCode": {
      "@id": "ujgl10n:localeCode",
      "@type": "xsd:language"
    },
    "argumentNames": {
      "@id": "ujgl10n:argumentNames",
      "@container": "@set"
    },
    "l10n:argumentNames": {
      "@id": "ujgl10n:argumentNames",
      "@container": "@set"
    },
    "defaultLocaleRef": {
      "@id": "ujgl10n:defaultLocaleRef",
      "@type": "@id"
    },
    "l10n:defaultLocaleRef": {
      "@id": "ujgl10n:defaultLocaleRef",
      "@type": "@id"
    },
    "fallbackLocaleRefs": {
      "@id": "ujgl10n:fallbackLocaleRefs",
      "@type": "@id",
      "@container": "@set"
    },
    "l10n:fallbackLocaleRefs": {
      "@id": "ujgl10n:fallbackLocaleRefs",
      "@type": "@id",
      "@container": "@set"
    },
    "rtl": "ujgl10n:rtl",
    "l10n:rtl": "ujgl10n:rtl",
    "messageMetaRef": {
      "@id": "ujgl10n:messageMetaRef",
      "@type": "@id"
    },
    "l10n:messageMetaRef": {
      "@id": "ujgl10n:messageMetaRef",
      "@type": "@id"
    },
    "localeRef": {
      "@id": "ujgl10n:localeRef",
      "@type": "@id"
    },
    "l10n:localeRef": {
      "@id": "ujgl10n:localeRef",
      "@type": "@id"
    },
    "value": "ujgl10n:value",
    "l10n:value": "ujgl10n:value",
    "targetLocaleRef": {
      "@id": "ujgl10n:targetLocaleRef",
      "@type": "@id"
    },
    "l10n:targetLocaleRef": {
      "@id": "ujgl10n:targetLocaleRef",
      "@type": "@id"
    }
  }
}
{
  "@context": {
    "@version": 1.1,

    "ujgl10n": "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#",
    "l10n": "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#",
    "xsd": "http://www.w3.org/2001/XMLSchema#",

    "Locale": "ujgl10n:Locale",
    "MessageMeta": "ujgl10n:MessageMeta",
    "Message": "ujgl10n:Message",

    "copyRef": {
      "@id": "ujgl10n:copyRef",
      "@type": "@id"
    },
    "l10n:copyRef": {
      "@id": "ujgl10n:copyRef",
      "@type": "@id"
    },
    "localeCode": {
      "@id": "ujgl10n:localeCode",
      "@type": "xsd:language"
    },
    "l10n:localeCode": {
      "@id": "ujgl10n:localeCode",
      "@type": "xsd:language"
    },
    "argumentNames": {
      "@id": "ujgl10n:argumentNames",
      "@container": "@set"
    },
    "l10n:argumentNames": {
      "@id": "ujgl10n:argumentNames",
      "@container": "@set"
    },
    "defaultLocaleRef": {
      "@id": "ujgl10n:defaultLocaleRef",
      "@type": "@id"
    },
    "l10n:defaultLocaleRef": {
      "@id": "ujgl10n:defaultLocaleRef",
      "@type": "@id"
    },
    "fallbackLocaleRefs": {
      "@id": "ujgl10n:fallbackLocaleRefs",
      "@type": "@id",
      "@container": "@set"
    },
    "l10n:fallbackLocaleRefs": {
      "@id": "ujgl10n:fallbackLocaleRefs",
      "@type": "@id",
      "@container": "@set"
    },
    "rtl": "ujgl10n:rtl",
    "l10n:rtl": "ujgl10n:rtl",
    "messageMetaRef": {
      "@id": "ujgl10n:messageMetaRef",
      "@type": "@id"
    },
    "l10n:messageMetaRef": {
      "@id": "ujgl10n:messageMetaRef",
      "@type": "@id"
    },
    "localeRef": {
      "@id": "ujgl10n:localeRef",
      "@type": "@id"
    },
    "l10n:localeRef": {
      "@id": "ujgl10n:localeRef",
      "@type": "@id"
    },
    "value": "ujgl10n:value",
    "l10n:value": "ujgl10n:value",
    "targetLocaleRef": {
      "@id": "ujgl10n:targetLocaleRef",
      "@type": "@id"
    },
    "l10n:targetLocaleRef": {
      "@id": "ujgl10n:targetLocaleRef",
      "@type": "@id"
    }
  }
}

7.3. Validation

The normative Localization SHACL shape is defined below and is published at https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.shape. It is the authoritative validation artifact for Localization structural constraints.

turtle
@prefix ujg: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph#> .
@prefix ujgl10n: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#> .
@prefix ujgl10nshape: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.shape#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ujgl10nshape:NodeCopyShape a sh:NodeShape ;
  sh:targetClass ujg:Node ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:copyRef ;
    sh:class ujgl10n:MessageMeta ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
  ] .

ujgl10nshape:LocaleShape a sh:NodeShape ;
  sh:targetClass ujgl10n:Locale ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:localeCode ;
    sh:datatype xsd:language ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

ujgl10nshape:MessageMetaShape a sh:NodeShape ;
  sh:targetClass ujgl10n:MessageMeta ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:argumentNames ;
    sh:datatype xsd:string ;
  ] ;

  sh:property [
    sh:path ujgl10n:defaultLocaleRef ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
  ] ;

  sh:property [
    sh:path ujgl10n:fallbackLocaleRefs ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
  ] ;

  sh:property [
    sh:path ujgl10n:rtl ;
    sh:datatype xsd:boolean ;
    sh:maxCount 1 ;
  ] .

ujgl10nshape:MessageShape a sh:NodeShape ;
  sh:targetClass ujgl10n:Message ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:messageMetaRef ;
    sh:class ujgl10n:MessageMeta ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;

  sh:property [
    sh:path ujgl10n:localeRef ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;

  sh:property [
    sh:path ujgl10n:value ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;

  sh:sparql [
    a sh:SPARQLConstraint ;
    sh:message "No two Message nodes may share the same messageMetaRef and localeRef pair." ;
    sh:select """
      SELECT $this
      WHERE {
        $this ujgl10n:messageMetaRef ?meta ;
              ujgl10n:localeRef ?locale .
        ?other a ujgl10n:Message ;
               ujgl10n:messageMetaRef ?meta ;
               ujgl10n:localeRef ?locale .
        FILTER (?other != $this)
      }
    """ ;
  ] .

ujgl10nshape:OutgoingTransitionLocaleShape a sh:NodeShape ;
  sh:targetClass ujggraph:OutgoingTransition ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:targetLocaleRef ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
  ] .
@prefix ujg: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core#> .
@prefix ujggraph: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph#> .
@prefix ujgl10n: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n#> .
@prefix ujgl10nshape: <https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.shape#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ujgl10nshape:NodeCopyShape a sh:NodeShape ;
  sh:targetClass ujg:Node ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:copyRef ;
    sh:class ujgl10n:MessageMeta ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
  ] .

ujgl10nshape:LocaleShape a sh:NodeShape ;
  sh:targetClass ujgl10n:Locale ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:localeCode ;
    sh:datatype xsd:language ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

ujgl10nshape:MessageMetaShape a sh:NodeShape ;
  sh:targetClass ujgl10n:MessageMeta ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:argumentNames ;
    sh:datatype xsd:string ;
  ] ;

  sh:property [
    sh:path ujgl10n:defaultLocaleRef ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
  ] ;

  sh:property [
    sh:path ujgl10n:fallbackLocaleRefs ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
  ] ;

  sh:property [
    sh:path ujgl10n:rtl ;
    sh:datatype xsd:boolean ;
    sh:maxCount 1 ;
  ] .

ujgl10nshape:MessageShape a sh:NodeShape ;
  sh:targetClass ujgl10n:Message ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:messageMetaRef ;
    sh:class ujgl10n:MessageMeta ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;

  sh:property [
    sh:path ujgl10n:localeRef ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;

  sh:property [
    sh:path ujgl10n:value ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;

  sh:sparql [
    a sh:SPARQLConstraint ;
    sh:message "No two Message nodes may share the same messageMetaRef and localeRef pair." ;
    sh:select """
      SELECT $this
      WHERE {
        $this ujgl10n:messageMetaRef ?meta ;
              ujgl10n:localeRef ?locale .
        ?other a ujgl10n:Message ;
               ujgl10n:messageMetaRef ?meta ;
               ujgl10n:localeRef ?locale .
        FILTER (?other != $this)
      }
    """ ;
  ] .

ujgl10nshape:OutgoingTransitionLocaleShape a sh:NodeShape ;
  sh:targetClass ujggraph:OutgoingTransition ;
  sh:nodeKind sh:IRI ;

  sh:property [
    sh:path ujgl10n:targetLocaleRef ;
    sh:class ujgl10n:Locale ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
  ] .

The rules below define the remaining module semantics beyond the structural constraints captured by the SHACL shape.

  1. Attachment only: Localization properties MUST NOT change Graph validity, graph traversal behavior, import resolution, or core node identity. Locale-dependent topology MAY be modeled explicitly with distinct Graph Journeys, Entries, States, and Transitions.

  2. Message identity: A MessageMeta IRI identifies the reusable message contract. Producers MUST use that IRI as the shared message identity.

  3. Locale identity: Locale references use Locale IRIs. A Locale.localeCode value identifies the language tag represented by that locale resource.

  4. Message uniqueness: Producers MUST NOT create more than one Message for the same messageMetaRef and localeRef pair.

  5. Opaque value: Message.value is a string value. Consumers MUST NOT infer UJG graph edges, message dependencies, JavaScript execution, ICU behavior, or cycle constraints from the string.

  6. Locale target metadata: l10n:targetLocaleRef declares the requested locale associated with an outgoing affordance. It does not itself define graph traversal behavior. The host OutgoingTransition's toEntryRef selects the destination JourneyEntry.

  7. Shared-topology state preservation: When a locale switch must preserve whichever of several States is active, each source State MUST directly reference a state-specific OutgoingTransition. Each transition's toEntryRef MUST target the JourneyEntry for the corresponding destination State.

  8. Fixed group destinations: An OutgoingTransitionGroup MAY contain a locale switch only when its one fixed destination entry is correct for every effective source. Consumers MUST NOT reinterpret a grouped transition's toEntryRef as relative to the active State.

  9. Locale topology: When locales share topology, a state-scoped locale-switch OutgoingTransition may target an entry whose stateRef is the same State where that affordance is available. When content structure differs by locale, it may target an entry owned by a locale-specific Journey with different local topology.

  10. No locale inference: Consumers MUST NOT infer a Journey, JourneyEntry, or State from targetLocaleRef; the Graph reference remains explicit and authoritative.

  11. Graceful degradation: A consumer that does not implement this module MAY ignore Localization semantics, but it SHOULD preserve recognized JSON-LD data during read-transform-write when possible.

  12. Private runtime hints: Platform-specific i18n loader configuration that is not intended for shared queryability or validation SHOULD remain in Core extensions.


8. Examples

8.1. Shared-Topology State-Preserving Switches

A state-preserving switch is source-specific because each OutgoingTransition has one fixed toEntryRef. In this shared-topology checkout Journey, switching to English from Form re-enters Form, while switching from Success re-enters Success:

json
{
  "@context": [
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld"
  ],
  "@type": "UJGDocument",
  "@id": "https://example.com/ujg/l10n/shared-topology-switch.jsonld",
  "nodes": [
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:en",
      "l10n:localeCode": "en"
    },
    {
      "@type": "Journey",
      "@id": "urn:journey:checkout",
      "defaultEntryRef": "urn:entry:checkout-form",
      "entryRefs": [
        "urn:entry:checkout-form",
        "urn:entry:checkout-success"
      ],
      "stateRefs": [
        "urn:state:checkout-form",
        "urn:state:checkout-success"
      ],
      "transitionRefs": ["urn:transition:checkout-submit"]
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:checkout-form",
      "stateRef": "urn:state:checkout-form"
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:checkout-success",
      "stateRef": "urn:state:checkout-success"
    },
    {
      "@type": "State",
      "@id": "urn:state:checkout-form",
      "label": "Form",
      "outgoingTransitionRefs": ["urn:ot:english-from-form"]
    },
    {
      "@type": "State",
      "@id": "urn:state:checkout-success",
      "label": "Success",
      "outgoingTransitionRefs": ["urn:ot:english-from-success"]
    },
    {
      "@type": "Transition",
      "@id": "urn:transition:checkout-submit",
      "from": "urn:state:checkout-form",
      "to": "urn:state:checkout-success"
    },
    {
      "@type": "OutgoingTransition",
      "@id": "urn:ot:english-from-form",
      "label": "English",
      "toEntryRef": "urn:entry:checkout-form",
      "l10n:targetLocaleRef": "urn:l10n:locale:en"
    },
    {
      "@type": "OutgoingTransition",
      "@id": "urn:ot:english-from-success",
      "label": "English",
      "toEntryRef": "urn:entry:checkout-success",
      "l10n:targetLocaleRef": "urn:l10n:locale:en"
    }
  ]
}
{
  "@context": [
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld"
  ],
  "@type": "UJGDocument",
  "@id": "https://example.com/ujg/l10n/shared-topology-switch.jsonld",
  "nodes": [
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:en",
      "l10n:localeCode": "en"
    },
    {
      "@type": "Journey",
      "@id": "urn:journey:checkout",
      "defaultEntryRef": "urn:entry:checkout-form",
      "entryRefs": [
        "urn:entry:checkout-form",
        "urn:entry:checkout-success"
      ],
      "stateRefs": [
        "urn:state:checkout-form",
        "urn:state:checkout-success"
      ],
      "transitionRefs": ["urn:transition:checkout-submit"]
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:checkout-form",
      "stateRef": "urn:state:checkout-form"
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:checkout-success",
      "stateRef": "urn:state:checkout-success"
    },
    {
      "@type": "State",
      "@id": "urn:state:checkout-form",
      "label": "Form",
      "outgoingTransitionRefs": ["urn:ot:english-from-form"]
    },
    {
      "@type": "State",
      "@id": "urn:state:checkout-success",
      "label": "Success",
      "outgoingTransitionRefs": ["urn:ot:english-from-success"]
    },
    {
      "@type": "Transition",
      "@id": "urn:transition:checkout-submit",
      "from": "urn:state:checkout-form",
      "to": "urn:state:checkout-success"
    },
    {
      "@type": "OutgoingTransition",
      "@id": "urn:ot:english-from-form",
      "label": "English",
      "toEntryRef": "urn:entry:checkout-form",
      "l10n:targetLocaleRef": "urn:l10n:locale:en"
    },
    {
      "@type": "OutgoingTransition",
      "@id": "urn:ot:english-from-success",
      "label": "English",
      "toEntryRef": "urn:entry:checkout-success",
      "l10n:targetLocaleRef": "urn:l10n:locale:en"
    }
  ]
}

The two locale-switch affordances may look identical in the interface, but they are different Graph edges with different destination entries. A Journey-level OutgoingTransitionGroup would be wrong for this state-preserving behavior: it would inject one transition with one fixed entry into both States. A group remains appropriate when every source intentionally enters the same locale landing entry.

8.2. Locale Switch Affordance Example

json
{
  "@context": [
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld"
  ],
  "@type": "UJGDocument",
  "@id": "https://example.com/ujg/l10n/locale-switch.jsonld",
  "nodes": [
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:en",
      "l10n:localeCode": "en"
    },
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:de",
      "l10n:localeCode": "de"
    },
    {
      "@type": "Journey",
      "@id": "urn:journey:confirmation-en",
      "defaultEntryRef": "urn:entry:confirmation-en",
      "entryRefs": ["urn:entry:confirmation-en"],
      "stateRefs": ["urn:state:confirmation-en"]
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:confirmation-en",
      "stateRef": "urn:state:confirmation-en"
    },
    {
      "@type": "State",
      "@id": "urn:state:confirmation-en",
      "label": "English confirmation",
      "outgoingTransitionRefs": ["urn:example:ot:language-german"]
    },
    {
      "@type": "Journey",
      "@id": "urn:journey:confirmation-de",
      "defaultEntryRef": "urn:entry:confirmation-de",
      "entryRefs": ["urn:entry:confirmation-de"],
      "stateRefs": ["urn:state:summary-de", "urn:state:legal-notice-de"],
      "transitionRefs": ["urn:transition:summary-to-legal-notice-de"]
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:confirmation-de",
      "stateRef": "urn:state:summary-de"
    },
    {
      "@type": "State",
      "@id": "urn:state:summary-de",
      "label": "German order summary"
    },
    {
      "@type": "State",
      "@id": "urn:state:legal-notice-de",
      "label": "German legal notice"
    },
    {
      "@type": "Transition",
      "@id": "urn:transition:summary-to-legal-notice-de",
      "from": "urn:state:summary-de",
      "to": "urn:state:legal-notice-de"
    },
    {
      "@type": "OutgoingTransition",
      "@id": "urn:example:ot:language-german",
      "label": "Deutsch",
      "toEntryRef": "urn:entry:confirmation-de",
      "l10n:targetLocaleRef": "urn:l10n:locale:de"
    }
  ]
}
{
  "@context": [
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/core.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/graph.context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld"
  ],
  "@type": "UJGDocument",
  "@id": "https://example.com/ujg/l10n/locale-switch.jsonld",
  "nodes": [
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:en",
      "l10n:localeCode": "en"
    },
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:de",
      "l10n:localeCode": "de"
    },
    {
      "@type": "Journey",
      "@id": "urn:journey:confirmation-en",
      "defaultEntryRef": "urn:entry:confirmation-en",
      "entryRefs": ["urn:entry:confirmation-en"],
      "stateRefs": ["urn:state:confirmation-en"]
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:confirmation-en",
      "stateRef": "urn:state:confirmation-en"
    },
    {
      "@type": "State",
      "@id": "urn:state:confirmation-en",
      "label": "English confirmation",
      "outgoingTransitionRefs": ["urn:example:ot:language-german"]
    },
    {
      "@type": "Journey",
      "@id": "urn:journey:confirmation-de",
      "defaultEntryRef": "urn:entry:confirmation-de",
      "entryRefs": ["urn:entry:confirmation-de"],
      "stateRefs": ["urn:state:summary-de", "urn:state:legal-notice-de"],
      "transitionRefs": ["urn:transition:summary-to-legal-notice-de"]
    },
    {
      "@type": "JourneyEntry",
      "@id": "urn:entry:confirmation-de",
      "stateRef": "urn:state:summary-de"
    },
    {
      "@type": "State",
      "@id": "urn:state:summary-de",
      "label": "German order summary"
    },
    {
      "@type": "State",
      "@id": "urn:state:legal-notice-de",
      "label": "German legal notice"
    },
    {
      "@type": "Transition",
      "@id": "urn:transition:summary-to-legal-notice-de",
      "from": "urn:state:summary-de",
      "to": "urn:state:legal-notice-de"
    },
    {
      "@type": "OutgoingTransition",
      "@id": "urn:example:ot:language-german",
      "label": "Deutsch",
      "toEntryRef": "urn:entry:confirmation-de",
      "l10n:targetLocaleRef": "urn:l10n:locale:de"
    }
  ]
}

The English Journey has one confirmation State. The German Journey has a summary followed by a separate legal-notice State. The locale switch therefore targets the German entry contract rather than assuming that both locales share a State or topology.

8.3. Combined JSON Example

json
{
  "@context": [
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld"
  ],
  "@id": "https://example.com/ujg/l10n/checkout.jsonld",
  "@type": "UJGDocument",
  "nodes": [
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:en",
      "l10n:localeCode": "en"
    },
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:de",
      "l10n:localeCode": "de"
    },
    {
      "@type": "State",
      "@id": "urn:ujg:state:order-confirmation",
      "label": "Order confirmation",
      "l10n:copyRef": "urn:l10n:message-meta:order-confirmation-title"
    },
    {
      "@type": "l10n:MessageMeta",
      "@id": "urn:l10n:message-meta:order-confirmation-title",
      "l10n:argumentNames": ["orderNumber"],
      "l10n:defaultLocaleRef": "urn:l10n:locale:en",
      "l10n:fallbackLocaleRefs": ["urn:l10n:locale:en", "urn:l10n:locale:de"],
      "l10n:rtl": false
    },
    {
      "@type": "l10n:Message",
      "@id": "urn:l10n:message:order-confirmation-title:en",
      "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
      "l10n:localeRef": "urn:l10n:locale:en",
      "l10n:value": "Order ${orderNumber} confirmed"
    },
    {
      "@type": "l10n:Message",
      "@id": "urn:l10n:message:order-confirmation-title:de",
      "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
      "l10n:localeRef": "urn:l10n:locale:de",
      "l10n:value": "Bestellung ${orderNumber} bestaetigt"
    }
  ]
}
{
  "@context": [
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/context.jsonld",
    "https://ujg.specs.openuji.org/tr/1.0-rc3/ns/l10n.context.jsonld"
  ],
  "@id": "https://example.com/ujg/l10n/checkout.jsonld",
  "@type": "UJGDocument",
  "nodes": [
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:en",
      "l10n:localeCode": "en"
    },
    {
      "@type": "l10n:Locale",
      "@id": "urn:l10n:locale:de",
      "l10n:localeCode": "de"
    },
    {
      "@type": "State",
      "@id": "urn:ujg:state:order-confirmation",
      "label": "Order confirmation",
      "l10n:copyRef": "urn:l10n:message-meta:order-confirmation-title"
    },
    {
      "@type": "l10n:MessageMeta",
      "@id": "urn:l10n:message-meta:order-confirmation-title",
      "l10n:argumentNames": ["orderNumber"],
      "l10n:defaultLocaleRef": "urn:l10n:locale:en",
      "l10n:fallbackLocaleRefs": ["urn:l10n:locale:en", "urn:l10n:locale:de"],
      "l10n:rtl": false
    },
    {
      "@type": "l10n:Message",
      "@id": "urn:l10n:message:order-confirmation-title:en",
      "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
      "l10n:localeRef": "urn:l10n:locale:en",
      "l10n:value": "Order ${orderNumber} confirmed"
    },
    {
      "@type": "l10n:Message",
      "@id": "urn:l10n:message:order-confirmation-title:de",
      "l10n:messageMetaRef": "urn:l10n:message-meta:order-confirmation-title",
      "l10n:localeRef": "urn:l10n:locale:de",
      "l10n:value": "Bestellung ${orderNumber} bestaetigt"
    }
  ]
}

8.4. Opaque Runtime Hints

json
{
  "@id": "urn:l10n:message-meta:order-confirmation-title",
  "@type": "l10n:MessageMeta",
  "extensions": {
    "com.acme.i18n-runtime": {
      "resourceFile": "checkout/confirmation.json",
      "bundleFormat": "icu"
    }
  }
}
{
  "@id": "urn:l10n:message-meta:order-confirmation-title",
  "@type": "l10n:MessageMeta",
  "extensions": {
    "com.acme.i18n-runtime": {
      "resourceFile": "checkout/confirmation.json",
      "bundleFormat": "icu"
    }
  }
}

Copyright © 2026 the Contributors to the ujg/modules/l10n/1.0-rc3, published by the User Journey Graph Community Group under the W3C Community Contributor License Agreement (CLA) . A human-readable summary is available.

GitHub Matrix room

generated by Speculator