One Video, Two Subtitle Languages in Directus

If an English page and a Spanish page play the same product walkthrough, give editors a way to fix a subtitle without re-exporting the video. Keep one video file on the parent item and link one WebVTT file to each language record. The browser can then offer both languages on either page.

Set up the content model

In Settings → Data Model, create a collection called demos with a single-file field, video_file. On demos, add a field called translations using the Translations interface, which creates a one-to-many alias. This manual setup creates demos_translations and uses the languages collection. The shared video_file stays on demos. See the Directus content translations guide for the manual setup steps.

Open demos_translations in Data Model. Add a single-file field called subtitle_file and a text field called subtitle_label. Keep the file field optional so an editor can leave a track out until it is ready.

Keep the video file on the demo item. Each translation row owns its subtitle file link and label.

Use one row for English and one for Spanish. In this example the language keys are en and es. Link en.vtt and es.vtt to their respective rows and set the labels to English and Español. If your project uses locale codes such as en-US or es-ES, pass those codes through to the player. Keep the language code separate from the human-readable label so users can distinguish the tracks even when the rest of the page is in another language.

An English file can start with the cue below. Use the same time range in the Spanish file if its translation should appear at the same moment. Set both files’ cue times against the same video cut.

WEBVTT

00:00:00.400 --> 00:00:02.800
Move the marker toward the goal.

Return all available tracks with the video

For a demo item with ID 1, a REST request can fetch the required fields in one response. This example uses languages_code as the language relationship field and assumes that its stored value is the language code. If your project uses a different relationship or an ID unrelated to the code, fetch the related code and use it for srclang.

GET /items/demos/1?fields=video_file,translations.languages_code,translations.subtitle_file,translations.subtitle_label

Let the page pick the appropriate translation for its own text separately. If you filter this request to just the page language, a reader on the English page will never be offered Spanish subtitles. The Directus query parameter guide explains how to request specific nested fields.

Build the tracks in the player

Start with a <video controls> element. Once the item response has loaded, set its source and add one HTML <track> for each row with a linked file. The example below assumes a same-origin route to the Directus assets endpoint. data is the item object from the REST response.

const video = document.querySelector("video");
const assetUrl = id => "/assets/" + encodeURIComponent(id);
video.src = assetUrl(data.video_file);

for (const row of data.translations ?? []) {
  if (!row.languages_code || !row.subtitle_file) continue;
  const track = document.createElement("track");
  track.kind = "subtitles";
  track.srclang = row.languages_code;
  track.label = row.subtitle_label || row.languages_code;
  track.src = assetUrl(row.subtitle_file);
  video.append(track);
}

With a browser that exposes subtitle controls, these tracks can be selected there. If you build your own selector, create its choices from the same list and use each track’s mode to switch languages. The HTML track reference covers the attributes and the browser’s text-track API.

If the video plays but the subtitles do not appear, open the network panel and check the .vtt response. Confirm it starts with WEBVTT, has a text/vtt media type, and is readable to the same visitor who can read the item. A public parent item alone does not grant access to its related files. If the player and media are on different origins, set the appropriate crossorigin mode before assigning the source and configure CORS on the asset responses. Use a same-origin route for the smallest first test. The Directus file access guide describes asset routes and permissions.

Check both languages and one missing file

The local example uses one synthetic video and two WebVTT files over HTTP. Its sample JSON follows the shape of the query above. In Chrome 157, the English cue appeared during playback. Switching to Spanish displayed the matching Spanish cue, followed by the next Spanish cue as playback advanced. With the Spanish file field set to null, the menu offered only Off and English. These are browser results from the example data. Check your own Directus endpoint and permissions before publishing.

Chrome paused on an active Spanish cue in the local fixture demo.

Try the same cases on your own front end before release. Switch languages while a cue is active, let playback run into the next cue, then remove one file link and reload. If an editor only changes the Spanish subtitle file, the English track and the shared video can stay where they are.