Back to blog

File Format TS

2026-05-27 11 min read
File Format TS

Meta description: Found a mysterious .ts file in your Django project? Use terminal commands to identify it fast and route it into the right i18n workflow.

You pull a branch during a localization sprint, run git status, and there it is: labels.ts.

You open it expecting gettext content. Instead you get one of three bad surprises. Binary garbage. Frontend code. Or XML that looks like translations, but not the kind Django knows what to do with.

That's the problem with the file format TS. The extension is overloaded. It can mean TypeScript source, MPEG transport stream video, or a Qt translation source file, and a lot of guides only explain the video case. That leaves you guessing at the worst time, usually when your release already depends on getting strings into locale/<lang>_<REGION>/LC_MESSAGES/django.po and compiled before deploy. The ambiguity across those three file types is documented in this overview of TS file meanings from this video explanation.

The fix is to stop guessing. Identify the file from the terminal first, then route it to the right toolchain. That saves you from trying to “translate” a video container, feeding source code into a PO workflow, or ignoring a Qt translation asset that matters.

That Mysterious .ts File in Your Project

A .ts file showing up in a Django repo usually means one of three things.

Frontend code got added by a React, Vue, or build-tool change. A media artifact slipped in from a streaming or upload workflow. Or someone from another team exported translations from a Qt-based tool and checked them in next to your app code.

Only one of those belongs anywhere near your Django i18n pipeline.

I've seen teams lose time because they treat every .ts file like text they can convert later. That breaks fast. A TypeScript file is source code. An MPEG transport stream file is a media container. A Qt .ts file is the one case that is a translation asset, but it still isn't usable by Django as-is.

Practical rule: never decide what a .ts file is from the filename alone. Decide from its contents.

When you're working on localization, the confusion gets worse because .po and Qt .ts both contain translatable strings. They solve similar problems with different structures. If you don't identify the file first, you'll waste time writing the wrong conversion step, or worse, commit junk to your locale tree.

How to Identify Your .ts File Type

The first move is always the same. Ask the operating system what it thinks the file is, then inspect the opening lines.

How to Identify Your .ts File Type

Start with file and head

Run these commands from your project root:

file path/to/unknown.ts
head -n 20 path/to/unknown.ts

On macOS and Linux, file is usually enough to separate binary media from text. head confirms the text-based cases.

Here's the pattern I use:

file assets/labels.ts
head -n 10 assets/labels.ts

Likely outputs and what they mean:

file output pattern head output pattern What you have What to do next
MPEG transport stream unreadable or binary-looking data Video container Convert, move, or ignore
ASCII text / UTF-8 text import, export, interface, class TypeScript source Keep out of Django i18n
XML document / text <?xml version="1.0"?> and <!DOCTYPE TS> Qt translation file Convert into .po
UTF-8 text lines beginning with @, then @data Time-series dataset format Handle as ML data, not i18n

That fourth case catches people off guard. The .ts extension is also used by a formal time-series data format documented by sktime's .ts specification. It's a UTF-8 encoded format with a description block, metadata lines starting with @, and a dataset block marked by @data. It supports dataset metadata such as @problemName, @missing, @timestamps, @serieslength, and labels for regression or classification. That's useful in machine learning workflows, but it has nothing to do with Django translations.

Quick signatures you can trust

You don't need a long forensic process. You need a fast one.

Don't use file icons, IDE syntax highlighting, or somebody's commit message as your source of truth.

For teams, this is worth codifying in CI. A tiny shell check can fail the build when an unexpected .ts lands inside locale/ or a release package.

find . -name "*.ts" -print

That command won't identify the type on its own, but it gives you the audit list. From there, run file on anything suspicious before you decide whether it belongs in your repo, your asset pipeline, or your translation workflow.

Handling MPEG Transport Stream Video Files

If file says MPEG transport stream, you're dealing with media, not text.

Handling MPEG Transport Stream Video Files

MPEG-TS is a digital container used in broadcast ecosystems including DVB, ATSC, and IPTV, and it's common in streaming and Blu-ray workflows, as described in this overview of MPEG transport stream usage. In a Django codebase, that usually means one of two things. You have a real media asset from a video workflow, or you have a temporary artifact that never should have been committed.

What works

If you need the video, convert it to something friendlier for your app or review process:

ffmpeg -i input.ts -c copy output.mp4

That remuxes the streams into MP4 without re-encoding. If the output has compatibility issues, fall back to a full transcode:

ffmpeg -i input.ts -c:v libx264 -c:a aac output.mp4

If your app handles HLS or webcam pipelines, you'll run into TS segments more often. For that side of the stack, this guide to HLS encoding for live webcams is useful because it explains why these files keep showing up in streaming systems even when your product team only thinks in terms of “video uploads.”

What does not

Don't commit transport stream artifacts to the repo unless they are versioned test fixtures and you've agreed on that explicitly.

Don't put them in locale/, don't pass them through gettext tooling, and don't assume every downstream system will accept them. A more practical problem is pipeline breakage. Real-world examples show .ts files can cause packaging or upload failures in automated systems, including LMS and validator workflows, as discussed in this example of pipeline issues with .ts files.

A decent default .gitignore for media-heavy repos looks like this:

*.ts
!src/**/*.ts
!frontend/**/*.ts

That pattern is intentionally narrow. Blanket-ignore every .ts file and you'll hide TypeScript source too.

A related format question comes up a lot when teams also ship captions. If that's your case, this write-up on the subtitle file format landscape is worth keeping nearby because subtitle workflows and transport stream workflows often get mixed together in product discussions even though they belong to different toolchains.

Here's a quick visual refresher on media-side handling:

Managing TypeScript Source Code Files

If the file starts with import, export, type, interface, or a typed function signature, it's TypeScript. Treat it as code.

That sounds obvious, but I've seen backend teams drag .ts files into translation planning because they contain UI strings. The file itself is not the translation artifact. It's the place where strings originate. Your frontend i18n tool should extract those strings into JSON, ICU message catalogs, or whatever your JS stack uses.

Keep Django gettext in its lane

Django's makemessages is for Django templates and Python code. It is not your frontend extractor.

If your repo mixes Django and a TypeScript frontend, keep responsibilities clear:

Here's the backend side you want to preserve:

django-admin makemessages --locale=fr
python manage.py compilemessages

And here's a normal Django model label pattern that belongs in that workflow:

from django.db import models
from django.utils.translation import gettext_lazy as _

class Invoice(models.Model):
    customer_name = models.CharField(_("customer name"), max_length=255)

If your team is tempted to “translate the .ts file directly,” stop there. That usually means your frontend extraction is missing or inconsistent. This article on translating computer code is a useful sanity check because it draws the line between localizing user-facing strings and editing source files that happen to contain them.

Code files can contain translatable content. They are still code files.

Working with Qt Linguist Translation Files

The Qt case is the one that matters for Django localization.

If head shows XML and <!DOCTYPE TS>, you've found a Qt Linguist translation source file. It isn't TypeScript, and it isn't video. It's a translation asset from the Qt ecosystem.

Screenshot from https://doc.qt.io/qt-6/linguist-ts-file-format.html

What it looks like

A typical file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
  <context>
    <name>MainWindow</name>
    <message>
      <source>Hello, %(name)s</source>
      <translation>Bonjour, %(name)s</translation>
    </message>
    <message>
      <source>Save changes</source>
      <translation></translation>
    </message>
  </context>
</TS>

The parts to care about are:

That structure is close enough to gettext that conversion is practical. It is not close enough that Django can use it directly.

Why teams miss this case

A lot of “open TS file” guides stop at media playback. That's why localization teams get stuck when a designer, desktop team, or plugin export hands them a Qt .ts file. It looks translation-related, but it doesn't fit the Django path you already know.

The wrong response is to hand-edit XML and copy strings manually into .po. That loses context fast, increases review noise, and makes repeated imports miserable.

The better response is to treat Qt .ts as an upstream format. Convert it once, preserve placeholders, and bring it into the same Git-reviewed flow as the rest of your gettext catalogs.

The packet-based 188-byte structure you may associate with transport-stream .ts files belongs to the media format, not the Qt one. That detail matters because people often search “TS format” and get the wrong answer first. The packet size and error-tolerant delivery behavior are described in this overview of TS packet structure.

A quick inspection habit

Before converting, scan the file for placeholder patterns and repeated contexts:

grep -n "<source>" locale-designer-export.ts | head
grep -n "%(" locale-designer-export.ts
grep -n "{0}" locale-designer-export.ts

You're checking whether the strings line up with your Django placeholder conventions or whether you'll need extra review after conversion.

If the file is full of UI labels that already exist in your Django app, decide who owns the source of truth before importing anything. Blindly merging Qt-generated messages into django.po can create duplicate copy with slightly different wording.

A Practical Workflow for Qt .ts and Django i18n

Once you know the file is Qt translation XML, the job is mechanical. Convert it into gettext, put it where Django expects it, review it, compile it, and wire that into CI.

A Practical Workflow for Qt .ts and Django i18n

Convert first, then normalize placement

I prefer translate-toolkit for this kind of bridge work.

Install it in your environment:

python -m pip install translate-toolkit

Then convert Qt TS into XLIFF, and XLIFF into PO:

ts2xlf designer-export.ts designer-export.xlf
xlf2po designer-export.xlf django.po

Put the result where Django expects it:

mkdir -p locale/fr_FR/LC_MESSAGES
mv django.po locale/fr_FR/LC_MESSAGES/django.po

Then compile:

python manage.py compilemessages

If your app uses fr rather than fr_FR, align the directory name with your project's language setup before you commit. Don't keep both unless you mean to.

Review the PO before you trust it

A converted file still needs a fast review pass.

Check these items:

Here's the kind of output you want to inspect after conversion:

msgid "Hello, %(name)s"
msgstr "Bonjour, %(name)s"

msgid "Save changes"
msgstr ""

And if the string belongs in a Django template or Python callsite later, it should still map cleanly to normal gettext usage:

<h1>{% translate "Save changes" %}</h1>

Make CI reject ambiguous leftovers

One operational rule helps a lot. After conversion, don't leave the original Qt .ts file sitting in a package or deploy artifact unless you need it there.

Unexpected .ts files can break downstream systems. There are real cases where packaging validators or LMS-style upload pipelines reject artifacts that contain .ts files because of policy or file-type restrictions. That's documented in the earlier pipeline example, and it's the reason I add an explicit check in CI for stray extension-based artifacts after packaging.

A minimal check might be:

find build -name "*.ts" -print

If that prints anything in a Django deploy artifact, inspect it before shipping.

For teams that already keep translations in version control and want to automate filling missing msgstr values after conversion, one option is TranslateBot's guide on working with translations. The package itself fits into the normal Django flow by translating .po files from a management command instead of sending you through a separate portal. That's useful once the Qt file has already been turned into gettext.

Convert foreign translation formats at the boundary. Inside your Django repo, keep one canonical translation format.

Your Checklist for the Next .ts File

When the next .ts file lands in your project, don't guess.

That workflow is repeatable. More important, it keeps your localization pipeline boring, which is what you want right before a release.


If your team already uses Django gettext and you want to automate the last step after makemessages or a Qt-to-PO conversion, TranslateBot is one option. It runs inside your project, writes back to .po files, and keeps the reviewable Git-based workflow most Django teams already prefer.

Stop editing .po files manually

TranslateBot automates Django translations with AI. One command, all your languages, pennies per translation.