Navigation: OWL ontology checker · OWL ontology publishing guide · API docs

How to Publish an OWL Ontology

A checklist for publishing OWL ontologies. It explains askwol’s checks and additional publishing practices.

By the TDCC-NES Ontology Engineers (Kathrin Füllenbach and Dani Metilli). Last reviewed: .

Contents Checks askwol runs (same order as the report) 1. Ontology basics 2. Namespaces & reuse 3. Term structure 4. Term documentation 5. Logic Additional best practices (no automated check)
Want a worked example? The askwol sample ontology illustrates the publishing conventions below. Load it from the home page (the sample ontology button) to view its report.

Two tags mark where a rule comes from, and two colored boxes flag extra context:

Spec what an OWL, RDF, RDFS, or other W3C/IETF specification defines or requires.

TDCC guideline a recommendation from the TDCC ontology engineers that goes beyond what any specification mandates. A W3C Note (such as Cool URIs, cited below) is informative, not normative, so guidance drawn from one counts as a TDCC guideline here too.

A green box like this one is a tip or extra detail.
An amber box like this one is a warning or common pitfall to watch out for.

Every finding in an askwol report carries one of three severity levels. The report counts individual findings, not checks: ten classes without a label are ten recommendations.

✗ Error a parsing failure or a logical or structural problem (for example an inconsistent individual, an unsatisfiable class, or a property whose domain or range is of the wrong kind). An unsatisfiable class does not make the ontology as a whole inconsistent, but nothing can be an instance of it.

⚠ Warning a likely mistake or a serious interoperability problem (for example a namespace or import that does not resolve, a reused term that does not exist in its vocabulary, mixed http/https IRIs, an undefined internal term, or an unrecognised datatype).

💡 Recommendation ontology publishing best practice (for example ontology metadata, an open licence, labels and comments, or naming conventions).

1. Ontology basics #

1.1 Ontology metadata #

Give the ontology itself good metadata

Your ontology is itself a published research object. It should say what it is, who made it, which version it is, and under which licence it can be reused.

Spec rdf:type owl:Ontology and owl:versionIRI/owl:versionInfo come from OWL 2; dcterms:title, dcterms:description, dcterms:creator, dcterms:created, dcterms:modified, and dcterms:publisher are defined by the DCMI Metadata Terms vocabulary, not OWL itself. Licensing gets its own dedicated check; see Open licence below.

TDCC guideline Neither spec says any of these properties is mandatory on an ontology header. The core / additional grouping below is this guide’s own editorial judgement of what a well-published ontology needs. askwol reports every missing property, in either group, as a recommendation.

askwol evaluates SHACL shapes for the ontology header and checks these properties:

Pre-2010 ontologies (e.g. FOAF, OWL-Time) often use the older Dublin Core Elements (dc:) properties instead of DCMI Terms; askwol accepts either, since they carry the same meaning. One nuance: Elements 1.1’s single, generic dc:date property doesn’t distinguish created from modified, so askwol accepts it for either (or both) of the two additional date checks above.

Fill in these metadata so that both humans and machines can understand the provenance and reuse conditions of your ontology.

Versioning #

Version information is part of good ontology metadata. Spec OWL 2 defines owl:versionIRI to identify a particular release, and owl:versionInfo to hold “a string that describes the IRI’s version,” with no format required.

TDCC guideline This guide recommends Semantic Versioning (MAJOR.MINOR.PATCH) as a practical convention, not an OWL requirement: bump major when you remove or redefine a term, minor when you add terms without breaking existing ones, and patch for fixes such as corrected labels or comments.

<http://example.org/my-ontology> a owl:Ontology ;
    owl:versionIRI <http://example.org/my-ontology/2.0.0> ;
    owl:versionInfo "2.0.0" .

Some ontologies version by date instead, especially W3C specifications on a fixed release schedule: PROV-O’s owl:versionIRI is http://www.w3.org/ns/prov-o-20130430. A date is simpler than semver but only says when something changed, not whether it is safe to upgrade. Pick one scheme per ontology and stick to it.

1.2 Resolving ontology imports #

Spec To include another ontology’s axioms in yours, use OWL 2 owl:imports in the ontology header. Merely referencing a vocabulary’s terms does not require an import:

<https://example.org/my-ontology> a owl:Ontology ;
    owl:imports <http://xmlns.com/foaf/0.1/> .

This tells reasoners and tools where your external terms are defined, so they can load the imported ontology and reason over it together with yours.

TDCC guideline Deciding which vocabularies deserve a formal import is a modelling judgement call askwol does not second-guess. What it does check is whether every owl:imports target you already declared is actually reachable and provides parseable RDF. This check is askwol’s own addition; OWL 2 does not require import targets to be checked at authoring time.

A broken import (a dead link, a renamed URL, a server that no longer serves RDF) degrades reasoning for anyone who loads your ontology, with nothing reporting the failure unless a tool checks it explicitly. askwol reports an import that does not resolve as a warning.
askwol checks direct imports only; nested imports are not followed. Each target must provide parseable RDF. It never merges their content into the checks that run on your ontology. Every other check, including the reasoner, runs on your ontology document alone.

1.3 Choosing hash or slash IRIs #

What askwol checks: every term defined inside your ontology’s own namespace is classified as either hash style (http://example.org/ont#Person) or slash style (http://example.org/ont/Person), per declared owl:Ontology base IRI. A base IRI whose defined terms all use the same pattern is fine; one that mixes both is reported as a warning. An ontology that legitimately bundles more than one base IRI (a rare pattern, e.g. the W3C PROV family) may use a different, consistent style for each without being flagged.

Mixing hash and slash in the same vocabulary is almost always accidental. It breaks naive prefix-based namespace splitting and confuses consumers about whether /Person is the same term as #Person.

Hash vs. slash, in plain terms #

TDCC guideline Neither RDF nor OWL requires hash or slash IRIs; both are valid (the W3C “Cool URIs for the Semantic Web” Interest Group Note describes both). They differ in how the identifier behaves over HTTP and how the vocabulary scales.

Recommendation #

TDCC guideline If in doubt, go with slash: it scales. Use hash only for a vocabulary you know will stay small. The Cool URIs note concludes:

“Hash URIs should be preferred for rather small and stable sets of resources that evolve together. The ideal case are RDF Schema vocabularies and OWL ontologies. […] 303 URIs may also be used for [large] data sets, making neater-looking URIs.”

Either way, pick one per base IRI and don’t mix styles within it. Use a domain you control, or a persistent ID service like w3id.org or purl.org, so your IRIs survive domain changes.

1.4 IRI scheme (http vs https) #

Use http or https, but be consistent per namespace

What askwol checks: every IRI used in the ontology (in subject, predicate, or object position, plus every bound namespace) is grouped by namespace (the base IRI up to the last / or #), not just by host, so two unrelated vocabularies that happen to share a host are never compared to each other. A namespace that appears under exactly one URI scheme is fine; one referenced under both http:// and https:// is reported as a warning. The report lists the conflicting namespaces with examples of each scheme so you can pick one canonical form and migrate the others.

Spec http://example.org/ont/Person and https://example.org/ont/Person are different IRIs: RDF compares IRIs by their exact strings, per RDF 1.1 Concepts. Mixing schemes can break joins that rely on exact IRI matches, including SPARQL and any tool that does string comparison on URIs.

The legacy problem #

Most foundational vocabularies were minted before HTTPS was ubiquitous. OWL, RDFS, FOAF, Dublin Core, SKOS and PROV-O all use http://. Changing them would break billions of existing triples, so they stay on http.

Do not “fix” http://xmlns.com/foaf/0.1/name to https://. You would be creating a different IRI that matches nothing in other people’s data.

For your own ontology #

Rule of thumb: copy-paste the namespace IRI from the vocabulary’s own ontology file. Don’t retype it, don’t “upgrade” the scheme.

1.5 Open licence #

Adopt an open licence

TDCC guideline Your ontology should be released under an open licence; this guide recommends the DCMI property dcterms:license. To meet the Linked Open Data principles, the licence must satisfy the Open Definition: “anyone can freely access, use, modify, and share [the ontology] for any purpose (subject, at most, to requirements that preserve provenance and openness).”

A licence is open if it’s equivalent to the public domain, or at most adds attribution or share-alike restrictions; non-commercial or no-derivatives clauses are not open. For maximum reuse, this guide recommends one of the two Creative Commons licences below.

  1. Creative Commons Zero (CC0) (public domain); IRI: https://creativecommons.org/publicdomain/zero/1.0/
  2. Creative Commons Attribution (CC BY) (requires attribution); IRI: https://creativecommons.org/licenses/by/4.0/

askwol also recognises other licences that meet the Open Definition but are less suitable for ontologies, for the reasons below.

Declare it with dcterms:license in the ontology header:

<https://example.org/my-ontology> a owl:Ontology ;
    dcterms:license <https://creativecommons.org/publicdomain/zero/1.0/> .
askwol checks dcterms:license and schema:license values on ontology headers against known licences. A missing, unrecognised, non-open, non-preferred or multiple licence declaration is a recommendation. Unrecognised means openness could not be established; it does not mean non-open.

2. Namespaces & reuse #

2.1 Making ontology namespaces resolvable #

A namespace is the URI that a prefix expands to. In @prefix foaf: <http://xmlns.com/foaf/0.1/> the prefix is foaf and the namespace is http://xmlns.com/foaf/0.1/. This check looks at those namespace URIs; the short prefix aliases themselves are covered by Unused prefixes below.

TDCC guideline Every namespace URI should return something useful when fetched with HTTP. Ideally it returns RDF (content-negotiated), so tools can discover term definitions automatically. Neither RDF nor OWL requires a namespace to be dereferenceable; this expectation comes from the Linked Data principles and the W3C Note linked below.

Good: http://xmlns.com/foaf/0.1/ returns RDF when asked with Accept: application/rdf+xml.
Bad: A namespace that returns 404 or a generic HTML page with no RDF link.

If you host your own ontology, configure your server to support content negotiation, serving RDF to machines and HTML to browsers; see Server configuration below for the details. askwol reports a namespace that does not resolve as a warning.

2.2 Unused prefixes #

Keep your prefixes clean

Spec Each @prefix line binds a short prefix to a namespace URI. TDCC guideline Turtle itself does not forbid declaring a prefix you never use; this check is about good hygiene: only declare prefixes you actually use. Leftover @prefix declarations clutter the file and confuse readers; they suggest a dependency that doesn’t exist.

@prefix dct: <http://purl.org/dc/terms/> .              # used below
@prefix geo: <http://www.opengis.net/ont/geosparql#> .  # unused, remove it
askwol reports detected prefix bindings whose namespace has no matching IRI use as a recommendation. Header and literal datatype IRIs count as usage. Parsing cannot reliably identify explicit declarations of built-in prefixes or distinguish aliases for the same namespace.

2.3 External term definitions #

Make sure reused terms actually exist

TDCC guideline Don’t reinvent the wheel. Before defining a new term, check if an established vocabulary already covers it. No spec requires reuse, but the wider Semantic Web interoperates far better when ontologies share terms:

When reusing a term, use the exact IRI from the source vocabulary. A typo like foaf:nme instead of foaf:name breaks interoperability.
askwol checks external terms used as subjects in your document against the retrieved vocabulary and reports terms not found there as warnings. This catches typos like foaf:nme, made-up terms like owl:MadeUpClass, and namespace hijacking (asserting a brand-new term such as foaf:MadeUpConcept under someone else’s namespace instead of your own). Terms from your own namespace are checked separately (see Internal term definitions).
A term can also exist but be deprecated by the vocabulary that defines it. Spec owl:deprecated is an OWL 2 annotation property, and owl:DeprecatedClass/owl:DeprecatedProperty come from the OWL 1 Reference (used by e.g. VIVO). TDCC guideline A vs:term_status of “deprecated”/“archaic” is a community convention documented in an editor’s draft and used by e.g. FOAF. askwol checks for all three and flags a deprecated reused term as a warning, so you don’t quietly build on a term the source vocabulary is phasing out.

3. Term structure #

3.1 Internal term definitions #

Define every term you use in your own namespace

TDCC guideline Every term you use from your own namespace should also be defined there. Don’t just use a term; define it. RDF’s open-world assumption actually permits referencing an IRI with no triples about it at all; askwol still flags it, because within your own namespace an undefined term is almost always an authoring mistake, not an intentional stub.

<#Person> a owl:Class ;
    rdfs:label "Person"@en ;
    rdfs:comment "A human being."@en .
If you reference ex:Persom but never define it, that’s probably a typo. askwol catches these.
askwol treats a term as defined when it appears as the subject of at least one triple, and as referenced when it appears as a predicate or object. A term in your own namespace that is referenced but never defined is reported as a warning. Internal namespaces are inferred from ontology IRIs using hash/slash boundaries; a separate term namespace cannot reliably be inferred. External vocabulary terms are covered by the External term definitions check instead. Some ontologies re-declare a reused term as boilerplate (e.g. rdfs:label a owl:AnnotationProperty, as PROV-O and FOAF do). askwol correctly recognises this as reuse of an existing term, keeping your own namespace scoped to what you actually define. New terms always belong in your own namespace: the External term definitions check confirms that anything asserted under an external prefix, like foaf:MadeUpConcept, genuinely exists there already, catching namespace hijacking along the way.

3.2 Term inventory & naming #

Categorise your terms and name them consistently

Spec OWL 2 defines six entity types: classes, datatypes, object properties, data properties, annotation properties, and named individuals. askwol lists each internal term with the category it detected, so you can spot a term that was never typed (rdf:type missing) or typed as the wrong kind of thing.

<#Person>   a owl:Class .
<#hasParent> a owl:ObjectProperty .
<#birthDate> a owl:DatatypeProperty .

Naming conventions #

TDCC guideline Capitalisation is a community convention, not an OWL or RDFS rule; nothing stops you from naming a class person or a property HasName. Consistent casing helps distinguish classes from properties. askwol reports a mismatch as a recommendation.

Some vocabularies use stable, coded identifiers instead: CIDOC CRM’s properties look like P2_has_type (the number is the permanent identifier; the wording after it can change between versions), and Wikidata’s are fully opaque, like P19, deliberately avoiding any one language’s naming bias. askwol recognises this pattern, an uppercase letter directly followed by a digit, and does not flag it.

Object property names can make the relationship’s direction clear: hasWife, wifeOf, lovedBy. This is a readability convention, not something askwol enforces; askwol only checks the case of the first letter, via SHACL shapes.
Mixing conventions (a lowercase class like person, or an uppercase property like HasName, with no digit following the capital) makes an ontology harder to read and to reuse.
A term marked deprecated (see External term definitions) is exempt from naming checks: an old term kept around for backward compatibility shouldn’t need renaming on its way out.

3.3 Domains & ranges #

Give properties a domain and a range

Spec An rdfs:domain says what kind of subject a property applies to; an rdfs:range says what kind of value it takes. TDCC guideline RDFS does not require either one; declaring both is this guide’s recommendation because it makes a property self-documenting and lets tools reason about it.

<#hasParent> a owl:ObjectProperty ;
    rdfs:domain <#Person> ;
    rdfs:range  <#Person> .  # a class

<#birthDate> a owl:DatatypeProperty ;
    rdfs:domain <#Person> ;
    rdfs:range  xsd:date .   # a datatype

Spec In OWL 2 DL, an object property ranges over classes and a datatype property over datatypes. Getting that wrong puts the ontology outside OWL 2 DL, so askwol reports it as an error. A domain or range that is simply missing is only a recommendation.

Spec In OWL, a domain or range is not a constraint that rejects bad data; a reasoner instead infers new facts from it. Stating rdfs:domain :Person on :birthDate tells a reasoner that anything with a :birthDate is a :Person. Pick domains and ranges that are actually true of every use.
askwol only looks at the rdfs:domain and rdfs:range declared directly on a property, not ones inherited from a super-property. A reasoner does follow that inheritance, so this check can occasionally flag something a reasoner would treat as fine. Kind checks use selected named class/datatype evidence; anonymous expressions are not evaluated. Checked against SHACL shapes. Deprecated properties (see External term definitions) are exempt.

3.4 Checking OWL datatypes #

Spec RDF typed literals use datatype IRIs, commonly from XSD built-ins (xsd:string, xsd:integer, xsd:date, xsd:boolean, …) or rdf:langString. A datatype property can range over rdfs:Literal or a specific datatype. Custom datatypes can be declared with rdfs:Datatype. RDF itself places no closed list on datatype IRIs; any IRI is legal there. Spec OWL 2 has a specific datatype map and restrictions on datatype definitions; recognising an IRI or finding a declaration alone does not establish OWL 2 DL conformance. askwol checks datatype IRIs against its built-in list and local declarations, reporting unrecognised IRIs as warnings. It does not validate literal values or establish OWL profile conformance.

<#age> rdfs:range xsd:nonNegativeInteger .
<#born> rdfs:range xsd:date .
"42"^^xsd:integer
A misspelled datatype (xsd:stirng, xsd:dateTiem) is treated as a brand-new, unknown datatype. Filters and validators that expect the real datatype then skip your values. askwol lists every datatype it sees and flags the ones it does not recognise.

3.5 Non-ontology terms #

Keep concept schemes separate from the schema

TDCC guideline An OWL ontology can contain both schema definitions and named individuals. Most named individuals are fine to keep in the same file. SKOS concepts, the members of a controlled vocabulary or thesaurus, are the one case worth pulling out. A concept scheme is typically much larger than the schema and grows on its own schedule: new terms get added without ever touching a class or property definition. This guide recommends keeping SKOS concepts in their own SKOS concept scheme, separate from the ontology file, so schema and data can each evolve and be reused independently. Mixing OWL and SKOS is valid; separation is a TDCC organisation preference.

<#Dataset> a owl:Class .     # schema, belongs here
<#biology> a skos:Concept .  # concept, belongs in a SKOS scheme
<#Monday> a <#DayOfWeek> .   # named individual, fine to keep here
askwol only reports skos:Concept instances defined in your own namespace, as recommendations; a full concept scheme has usually outgrown the schema file and is better managed on its own. Named individuals are left alone: many ontologies deliberately define a small, fixed set of them alongside their schema (days of week, units of measure, as OWL-Time does), a legitimate modelling pattern rather than accidental instance data. External terms are ignored either way. Checked against SHACL shapes.

4. Term documentation #

4.1 Labels #

Give every class and property a human-readable label

TDCC guideline Every class and property you define should carry a human-readable rdfs:label: a short name a person can read. RDFS defines what the property means but does not require any resource to have one; this guide recommends adding one to everything you define.

<#hasMother> a owl:ObjectProperty ;
    rdfs:label "has mother"@en ;
    rdfs:domain <#Person> ;
    rdfs:range <#Person> .
Use language tags (@en, @de) to support multilingual ontologies. Consider skos:prefLabel and skos:altLabel for richer labelling.
askwol uses SHACL shapes to check that each internally defined class and property has an rdfs:label. Reused external vocabulary terms, and terms you have marked deprecated yourself, are ignored. A missing label is reported as a recommendation.

4.2 Comments #

Give every class and property a clear description

TDCC guideline Every class and property you define should also carry an rdfs:comment: a brief description of what it means. As with labels, RDFS defines the property but does not require you to use it; this guide recommends it for every term you define.

<#hasMother> a owl:ObjectProperty ;
    rdfs:label "has mother"@en ;
    rdfs:comment "Relates a person to their biological mother."@en ;
    rdfs:domain <#Person> ;
    rdfs:range <#Person> .
A good comment states the intended meaning and, where helpful, how the term should (and should not) be used.
askwol uses SHACL shapes to check that each internally defined class and property has an rdfs:comment. Reused external vocabulary terms, and terms you have marked deprecated yourself, are ignored. A missing comment is reported as a recommendation.
An inverse property (owl:inverseOf) doesn’t need its own comment when its partner already has one: the relationship is documented either way.

4.3 Language tag consistency #

Use language tags consistently

TDCC guideline If your ontology includes human-readable labels and descriptions, tag every literal that carries natural-language text with a BCP 47 language tag (@en, @nl, @de, …), producing an RDF 1.1 language-tagged string.

:Person a owl:Class ;
    rdfs:label "person"@en ,
               "persoon"@nl ;
    rdfs:comment "A human being."@en ,
                    "Een menselijk wezen."@nl .

Consistency rules #

TDCC guideline Neither RDF nor BCP 47 requires every subject to carry the same set of languages; askwol checks for it anyway, because an inconsistency here is usually a missed translation, not a deliberate choice:

If a literal has no language tag, SPARQL filters like FILTER(LANG(?label) = "en") won’t match it. The triple is still in your data, but any app or query that filters by language will skip it, as if it doesn’t exist.
askwol checks rdfs:label, rdfs:comment, skos:prefLabel, skos:definition, and other standard annotation properties for tag consistency on terms defined in your own namespace. Reused external vocabulary terms, and terms you have marked deprecated yourself, are ignored.

5. Logic #

5.1 Reasoner checks #

Check logical consistency

Spec OWL is a logical language. OWL 2 Direct Semantics formally defines when an ontology is consistent and when a class is satisfiable; a reasoner can derive consequences from your axioms and detect contradictions you didn’t intend. askwol displays a summary and two types of logical finding in the Reasoner checks section of the report:

A functional object property with two object IRIs may imply that they name the same individual. It is a contradiction only if the objects must be different. For a functional datatype property, two unequal data values contradict functionality. askwol does not check all these cases.

Each inconsistent individual and each unsatisfiable class is reported as an error.

TDCC guideline askwol runs a lightweight OWL RL/RDF reasoner on the current ontology only; it does not follow owl:imports. This is an implementation tradeoff, not a spec requirement. The results cover selected class membership and named-class satisfiability patterns, not all logical contradictions. RDF-based rules can differ from OWL Direct Semantics for class/individual punning. For deeper checks (against imports, with HermiT or Pellet), use a desktop tool like Protégé.
This is the only check that reasons, and only over its own private copy of the graph. Every other check, including Domains & ranges, runs on the ontology as asserted; reasoning first could change some of those results.

Additional best practices #

Validate early and often #

Run askwol on your ontology during development, not just before release.

Integrate validation into your CI pipeline: askwol check my-ontology.ttl

Fix RDF syntax errors #

askwol stops at the first blocking parse error; ontology checks are not run. Check that the selected format matches the file contents. A reported location marks where the parser stopped; the cause may be earlier. Correct the error and try again. Further errors may then appear.

For syntax feedback while editing:

FormatTools
TurtleTurtle Language Server for VS Code.
RDF/XMLXML Language Support by Red Hat for VS Code checks XML syntax, not all RDF/XML rules.
JSON-LDA strict JSON editor, then the JSON-LD Playground. Valid JSON is not necessarily valid JSON-LD.
N-Triples / N3Mentor for VS Code checks syntax as you edit.

These tools may also stop at a blocking error. askwol does not repair the file or collect all syntax errors.

Serving ontology files with content negotiation #

Once published, your ontology’s IRI should resolve: a client that dereferences it must get the RDF back. Two server-side settings make that work.

GitHub file pages and raw files. A GitHub /blob/ URL opens a file viewer that returns HTML, even when its path ends in .owl or .ttl. Ontology tools need the file contents. Open the file on GitHub, select Raw, and copy its URL. A filename extension alone does not establish what a URL returns. Elsewhere, a published ontology IRI may legitimately show HTML in a browser while serving RDF to tools through content negotiation.

A raw file URL is a retrieval location. Keep the ontology, namespace, and term IRIs published by the vocabulary owner; do not replace these identifiers with a GitHub download URL.

Relative identifiers need a base IRI. A term such as <#Person> resolves against the document’s base. Declare @base in Turtle or xml:base in RDF/XML, or supply the intended base through the upload settings, API base_uri, or CLI --base-uri. Without a declared base, a URL uses its final retrieval location; an uploaded or local file can produce file:// identifiers tied to that file location. Choose the intended identifiers explicitly before publishing.

Some tools discover RDF through links in HTML pages or known alternative locations. Other tools may not follow those links. Ask Wol discloses this discovery and warns when an owl:imports target relies on it. Configure the declared import IRI to serve RDF directly, through content negotiation, or through an HTTP redirect.

Keep the namespace IRI and the document URL consistent. In the askwol sample ontology, terms live under https://lod-4tu.tudelft.nl/dataset#, and dereferencing https://lod-4tu.tudelft.nl/dataset (no fragment) returns the ontology itself, directly or via redirect.
Static file servers often default to text/plain for .ttl. Set the MIME type explicitly in your web server or reverse proxy so tools can reliably recognise the RDF.

Documentation #

Generate human-readable documentation

Good rdfs:label and rdfs:comment annotations pay off once turned into a browsable HTML page: a documentation generator reads your Turtle file and produces one, ready to serve alongside the RDF via content negotiation.

pyLODE is a lightweight Python tool: a single command turns a Turtle file into one static HTML page that needs no server or JavaScript to view (example output).

python3 -m venv .venv && source .venv/bin/activate
python -m pip install pylode
pylode my-ontology.ttl -o my-ontology.html

WIDOCO is a Java-based documentation generator that produces a multi-page bundle: metadata extraction, an optional WebVOWL diagram, and a ready-to-deploy .htaccess for content negotiation.

java -jar widoco.jar -ontFile my-ontology.ttl -outFolder docs \
    -getOntologyMetadata -rewriteAll -uniteSections -webVowl -htaccess
WIDOCO’s WebVOWL view needs a web server to load; open the page over HTTP (python3 -m http.server 8080) rather than from the file system.

For W3C specification-style output, ReSpec renders technical documents in the browser, and OntoReSpec generates the markup it needs from an RDF ontology or data model (example). OntoReSpec is early-stage, with no stable release yet.