Language-Tagged RDF Strings with curl

Open inAnthropic

TerminusDB supports language-tagged strings through the rdf:langString data type. This lets you attach a language tag to any text value, which is useful for multilingual content, localized labels, or any field where the language of the text matters.

This guide walks through the full lifecycle: creating a schema with language-tagged fields, inserting documents with language values, and retrieving them — all using curl against the HTTP API.

Prerequisites

  • A running TerminusDB instance on localhost:6363
  • Default credentials admin:root (adjust to match your setup)
  • curl installed

What you will build

A Greeting class with two language-tagged fields, then insert and retrieve a multilingual greeting document.


Step 1: Create a database

curl -u admin:root -X POST "http://localhost:6363/api/db/admin/greetings" \
  -H "Content-Type: application/json" \
  -d '{"label": "Greetings DB", "comment": "Multilingual greetings"}'

You should see a success response:

Example: JSON
{"@type":"api:DbCreateResponse", "api:status":"api:success", ...}

Step 2: Define the schema

The schema needs two things to support rdf:langString:

  • Declare the rdf and rdfs prefixes in the @context so TerminusDB can resolve the type and property IRIs.
  • Use "rdf:langString" as the field type for any property that should hold language-tagged text.

Since we are replacing the default context document with one that includes custom prefixes, we need full_replace=true.

curl -u admin:root -X POST \
  "http://localhost:6363/api/document/admin/greetings?graph_type=schema&author=admin&message=Add+schema&full_replace=true" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "@type": "@context",
      "@base": "terminusdb:///data/",
      "@schema": "terminusdb:///schema#",
      "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
      "rdfs": "http://www.w3.org/2000/01/rdf-schema#"
    },
    {
      "@type": "Class",
      "@id": "Greeting",
      "rdfs:label": "rdf:langString",
      "message": "rdf:langString"
    }
  ]'

This creates a Greeting class with two language-tagged fields:

  • rdfs:label — a standard RDF label, useful for display names
  • message — a custom field for the greeting text

Both use rdf:langString, which means every value must include a language tag.

Step 3: Insert a document

Language-tagged values use the @lang and @value keys. The @lang value must be a valid IANA language tag such as en, fr, de, or es.

curl -u admin:root -X POST \
  "http://localhost:6363/api/document/admin/greetings?author=admin&message=Add+greeting" \
  -H "Content-Type: application/json" \
  -d '{
    "@type": "Greeting",
    "@id": "Greeting/hello",
    "rdfs:label": {"@lang": "en", "@value": "Hello World"},
    "message": {"@lang": "fr", "@value": "Bonjour le monde"}
  }'

The response returns the full document ID:

Example: JSON
["terminusdb:///data/Greeting/hello"]

What happens with invalid language tags?

TerminusDB validates language tags against the IANA registry. If you try a tag like "foobar", the insert will fail with an unknown_language_tag error. Standard subtags like en, en-GB, fr, de, zh-Hans all work.

Step 4: Retrieve the document

curl -u admin:root \
  "http://localhost:6363/api/document/admin/greetings?id=Greeting/hello&minimized=false"

The response preserves the language tags:

Example: JSON
{
  "@id": "Greeting/hello",
  "@type": "Greeting",
  "rdfs:label": {
    "@lang": "en",
    "@value": "Hello World"
  },
  "message": {
    "@lang": "fr",
    "@value": "Bonjour le monde"
  }
}

Step 5: Update the document

To change the language or text, replace the full document with PUT. Every field must be included — omitted fields are removed.

curl -u admin:root -X PUT \
  "http://localhost:6363/api/document/admin/greetings?author=admin&message=Update+greeting" \
  -H "Content-Type: application/json" \
  -d '{
    "@type": "Greeting",
    "@id": "Greeting/hello",
    "rdfs:label": {"@lang": "de", "@value": "Hallo Welt"},
    "message": {"@lang": "es", "@value": "Hola Mundo"}
  }'

Step 6: Retrieve all documents

To list all Greeting documents, omit the id parameter:

curl -u admin:root \
  "http://localhost:6363/api/document/admin/greetings?type=Greeting&as_list=true&minimized=false"

Multiple languages per field

The schema above uses mandatory (single-value) fields, so each field holds exactly one language-tagged string. If you need to store the same field in several languages — for example, a label in English, French, and German — use a Set cardinality instead.

Update the schema to use Set

curl -u admin:root -X POST \
  "http://localhost:6363/api/document/admin/greetings?graph_type=schema&author=admin&message=Update+schema&full_replace=true" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "@type": "@context",
      "@base": "terminusdb:///data/",
      "@schema": "terminusdb:///schema#",
      "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
      "rdfs": "http://www.w3.org/2000/01/rdf-schema#"
    },
    {
      "@type": "Class",
      "@id": "Greeting",
      "rdfs:label": {"@type": "Set", "@class": "rdf:langString"},
      "message": "rdf:langString"
    }
  ]'

Now rdfs:label accepts an array of language-tagged values while message remains single-valued.

Insert a document with multiple labels

curl -u admin:root -X POST \
  "http://localhost:6363/api/document/admin/greetings?author=admin&message=Add+multilingual" \
  -H "Content-Type: application/json" \
  -d '{
    "@type": "Greeting",
    "@id": "Greeting/world",
    "rdfs:label": [
      {"@lang": "en", "@value": "Hello World"},
      {"@lang": "fr", "@value": "Bonjour le monde"},
      {"@lang": "de", "@value": "Hallo Welt"}
    ],
    "message": {"@lang": "en", "@value": "A greeting in many languages"}
  }'

Retrieve and inspect

curl -u admin:root \
  "http://localhost:6363/api/document/admin/greetings?id=Greeting/world&minimized=false"

The Set field comes back as an array:

Example: JSON
{
  "@id": "Greeting/world",
  "@type": "Greeting",
  "rdfs:label": [
    {"@lang": "en", "@value": "Hello World"},
    {"@lang": "fr", "@value": "Bonjour le monde"},
    {"@lang": "de", "@value": "Hallo Welt"}
  ],
  "message": {
    "@lang": "en",
    "@value": "A greeting in many languages"
  }
}

Cardinality options for language strings

CardinalitySchema syntaxMultiple values?Retrieval shape
Mandatory"rdf:langString"No — array is rejectedSingle {@lang, @value} object
Optional{"@type":"Optional","@class":"rdf:langString"}NoSingle object or absent
Set{"@type":"Set","@class":"rdf:langString"}Yes (unordered)Array of objects
List{"@type":"List","@class":"rdf:langString"}Yes (ordered)Array preserving insertion order

Use Set when you want translations in any order, or List when insertion order matters (for example, a priority-ranked list of translations).

Cleanup

When you are done experimenting, delete the database:

curl -u admin:root -X DELETE "http://localhost:6363/api/db/admin/greetings"

Key points

  • Schema: Declare rdf and rdfs prefixes in @context, use "rdf:langString" as the field type
  • Values: Use {"@lang": "<tag>", "@value": "<text>"} for each language-tagged string
  • Validation: Language tags are validated against the IANA registry — invalid tags are rejected
  • Round-trip: Language tags are preserved exactly through insert and retrieval

Further reading

Was this helpful?