Back to blog

What Is a Source File: Understanding Code & Management

2026-06-14 10 min read
What Is a Source File: Understanding Code & Management

Meta description: Confused about whether a file belongs in Git or .gitignore? Here's what a source file is in Django, and how it affects builds and localization.

You run python manage.py makemessages, Git lights up, and suddenly locale/ is full of files you didn't have five minutes ago. Your first instinct is often the wrong one. .po looks generated, so maybe it belongs next to .pyc in .gitignore.

That mistake causes a lot of churn.

If you treat translation files like throwaway build output, you lose review history, create mismatches between code and translations, and make deploys harder to reproduce. If you treat actual build artifacts like source, you commit noise and drift into your repo. Knowing what is a source file is how you draw that line.

Source File or Build Artifact? The .gitignore Dilemma

The common Django version of this problem looks like this:

python manage.py makemessages --locale fr
git status

Now you see changes like:

locale/fr/LC_MESSAGES/django.po

At the same time, your repo may also have things you already know to ignore:

__pycache__/
*.pyc

Those two categories aren't the same, even if a command created both at some point in your workflow.

What belongs in Git

A practical rule is to ask one question first. Which file is the editable record your team intends to maintain?

For Django projects, that usually includes:

What usually doesn't belong in Git is tool output your team can rebuild from those files.

Practical rule: If your teammate fixes a bug by opening the file in an editor and committing the change, it's probably source. If a tool regenerates it from another file, it's probably a build artifact.

Where teams get burned

The bad workflow is easy to spot. Someone updates text in templates, forgets to commit the matching .po changes, and another developer pulls the branch and can't rebuild the same localization state. Or someone commits generated caches and every pull request turns into noise.

A clean .gitignore isn't about aesthetics. It's about preserving the files your team authors, and dropping the files your tools can recreate.

What Is a Source File Anyway?

A source file is the original, editable file from which another version is derived. In historical research, libraries and universities use the same idea for primary sources. They define them as original materials created by a participant or witness, such as letters, diaries, official documents, photographs, recordings, and artifacts, because those preserve evidence closest to the event itself, as described in Baylor University's guide to primary and source materials.

For software, keep the same mental model. A source file is the version humans maintain. A derived file is the version tools produce.

A diagram illustrating the typical directory structure and core source files found within a Django web project.

The working definition that helps in Django

Most definitions online get muddy because the term changes by domain. One explanation points out that source file gets blurred across programming files, design masters like PSD files, and academic submission files like LaTeX. For developers, the useful distinction is the one between what you write and what your tools build, especially in localization workflows, as noted in this discussion of the term's domain-specific meaning.

That distinction is the one you need in day-to-day Django work.

File Role Edit by hand Keep in Git
app/models.py Python source Yes Yes
templates/base.html Template source Yes Yes
locale/fr/LC_MESSAGES/django.po Translation source Yes Yes
__pycache__/views.cpython-*.pyc Python bytecode No No
locale/fr/LC_MESSAGES/django.mo Compiled translation binary No Usually no

The test that actually works

Use this when you're unsure:

That last point matters because teams often confuse "created by a command" with "generated junk." makemessages creates .po files, but those files become your maintained translation source after generation.

Your Django Project Is Full of Source Files

You feel this fast on a real team. A pull request touches models.py, a template, and locale/fr/LC_MESSAGES/django.po. All three deserve review because all three change application behavior.

A Django codebase has more source files than many developers first assume. Python modules are the obvious ones, but templates, management command code, fixture definitions your team maintains by hand, and translation catalogs also sit on the authored side of the line. They are the files people read, edit, review, and blame in Git when something breaks.

A four-step infographic explaining the Django localization workflow process from code scanning to deployment.

The source files you work on every week

In a typical Django app, these usually count as source files:

The practical test is simple. If a developer or translator is expected to change the file and submit that change through code review, treat it like source.

A small example:

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

class Invoice(models.Model):
    status = models.CharField(
        max_length=32,
        verbose_name=_("status"),
    )

That _('status') call starts in Python source, but it does not stay there. Django's i18n tooling extracts that string into a translation catalog, and the catalog becomes another maintained source file in the same feature's lifecycle.

Why .po files belong in the same category

Teams sometimes treat .po files like exports because makemessages created the first version. That breaks down in practice. After extraction, the .po file becomes the place where translation decisions live, where reviewers catch bad phrasing, and where merge conflicts show two people changed the same user-facing string.

Here's a realistic example:

msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Language: fr\n"

#: billing/templates/billing/invoice.html:18
#, python-format
msgid "Hello %(name)s, your invoice is ready."
msgstr "Bonjour %(name)s, votre facture est prête."

#: billing/models.py:42
msgid "status"
msgstr "statut"

#: billing/templates/billing/summary.html:12
#, python-format
msgid "%(count)s invoice"
msgid_plural "%(count)s invoices"
msgstr[0] "%(count)s facture"
msgstr[1] "%(count)s factures"

This file is source because your team edits it on purpose. It is readable, diffable, and reviewable. If you want a quick refresher on the structure, this guide to the PO file format in Django localization workflows is a useful reference.

That distinction matters in Git. A noisy .po diff may still contain the actual business change, such as a revised legal label or a pluralization fix for a billing screen. If reviewers dismiss it as generated output, bad translations ship.

A quick visual helps if you're explaining this to someone else on the team.

Your app runs from maintained inputs. In Django, that includes code, templates, and translation catalogs.

The Localization Loop From .po Source to .mo Binary

A common Django mistake shows up right after a translation edit. Someone updates django.po, reloads the page, still sees the old string, and starts questioning the template, the locale middleware, or the deployment. The missing step is usually simpler. Django is still reading the compiled catalog.

A checklist illustrating six best practices for maintaining healthy and organized source files in software development projects.

The workflow has two distinct file types. .po is the source your team reads and edits. .mo is the machine-oriented output Django loads at runtime. That split matters because it affects what you review in Git, what you regenerate in CI, and what you should never hand-edit on a server.

The loop in commands

python manage.py makemessages --locale fr
python manage.py compilemessages

If you're using an automated translator in the middle, the flow becomes:

python manage.py makemessages --locale fr
python manage.py translate --locale fr
python manage.py compilemessages

makemessages extracts translatable strings into .po files. A translator, developer, or automation step updates the entries. compilemessages then turns those text catalogs into .mo files for runtime use.

The compiler analogy is useful, but the practical rule is what matters day to day. Edit the human-readable source. Build the runtime artifact. Ship the built result your deployment process expects.

For teams automating this pipeline, it helps to know the exact structure of headers, references, and plural forms in a PO file format guide for Django localization workflows. Badly formed entries often surface late, usually when compilemessages runs in CI instead of on a laptop.

I handle translation assets with the same discipline used for buildable infrastructure definitions. The source belongs in version control, the generated output belongs in the build pipeline, and the result should be reproducible on any machine. The same reasoning behind infrastructure as code best practices applies here.

Keep the boundary clean: edit .po, build .mo, run the app. Don't edit .mo by hand, and don't expect translation changes to appear before recompiling.

How This Impacts Your Git History and CI/CD Pipeline

Once you treat .po files as source, a lot of workflow decisions stop being fuzzy.

Translation changes belong in pull requests. Reviewers can see string changes next to the code that introduced them. Releases stay reproducible because the repo contains the source state for code and localization together. One source on editable masters notes that source files often stay within a limited chain of people because recipients can alter the work, but for team development the right move is to commit those sources with clear ownership rules so the project can be rebuilt consistently, as discussed in this glossary entry on source file handoff and control.

What works in practice

A sane pipeline usually looks like this:

That approach lines up well with the same discipline you already use for infra definitions. If your team cares about reproducibility, drift control, and reviewable changes, the principles in these infrastructure as code best practices apply almost one-to-one to localization assets.

What doesn't work

A few patterns create pain fast:

If you want your team to review generated changes cleanly, it also helps to tighten up commit hygiene. This article on commands in Git is a good reminder that localization diffs deserve the same discipline as code diffs.

For automation, one valid option is TranslateBot. It runs as a Django management command and writes translations back into .po files, which fits naturally into a repo-first workflow.

Best Practices for Healthy Source Files

You don't need a grand system here. You need habits that prevent broken translations and messy diffs.

An infographic showing ten best practices for maintaining organized, scalable, and collaborative design source files.

Keep the text safe

Translation files are still source code in the sense that bad edits can break runtime behavior.

Bad:

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

Good:

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

Give translators context

Short strings are where quality falls apart. "Open", "Close", "Archive", "Post" can all mean different things depending on the screen.

Use translator comments and message context where needed:

from django.utils.translation import gettext_lazy as _
from django.utils.translation import pgettext_lazy

button_label = pgettext_lazy("verb for opening a document", "Open")
status_label = _("Archived")

You can also add comments near marked strings so extraction keeps useful context.

Keep files reviewable

The healthiest translation source files are boring to review.

The practical takeaway is small but important. Treat django.po with the same care you give models.py. It's source. It deserves review, ownership, and a clean path through CI.


If your team wants to stop copy-pasting strings into portals, TranslateBot fits neatly into the Django workflow you already have. Run makemessages, fill or update .po entries with a management command, review the diff in Git, then compile for deploy.

Stop editing .po files manually

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