What is an unpacked project?
An unpacked project is the Git-friendly representation of an .inlang file. The canonical .inlang format is a single binary file: a SQLite database with version control via lix. The unpacked directory exists so changes can be reviewed alongside code.
Messages, variants, and locale data live in the .inlang database. In unpacked Git projects, settings.json is the only tracked project file by default; translation files such as messages/en.json live outside project.inlang/ and are connected through plugins.
project.inlang/
├── settings.json
├── .gitignore
├── README.md
├── .meta.json
└── cache/
Packed vs Unpacked
Packed (.inlang file) | Unpacked (directory) | |
|---|---|---|
| Format | Canonical single binary file | Git-friendly directory representation |
| Git-friendly | No (binary) | Yes (diffable, mergeable) |
| Portable | Yes (one file to share) | No |
| Use case | Sharing, backups, tools like Fink | Storing in git repos |
Why does it exist?
Storing inlang projects in git
Most codebases use git for version control. Developers want their translations co-located with their code — not in a separate system.
Git doesn't handle binary files well
An .inlang file is binary. Git can store binary files, but you lose:
- Readable diffs — Binary changes show as "file changed", not what changed
- Merge conflict resolution — Git can't merge binary files
- Code review — Teammates can't review translation changes in PRs
An unpacked project solves this for the project configuration. The generated .gitignore keeps settings.json in Git and ignores generated/cache files. Translation files are stored outside project.inlang/ according to plugin configuration.
How to use it
Loading a project
Use loadProjectFromDirectory() to load an unpacked project:
import { loadProjectFromDirectory } from "@inlang/sdk";
import fs from "node:fs";
const project = await loadProjectFromDirectory({
path: "./project.inlang",
fs: fs,
});
// Query translations
const messages = await project.db
.selectFrom("message")
.selectAll()
.execute();
Saving a project
Use saveProjectToDirectory() to save changes back to disk:
import { saveProjectToDirectory } from "@inlang/sdk";
import fs from "node:fs/promises";
await saveProjectToDirectory({
fs: fs,
project: project,
path: "./project.inlang",
});
File synchronization
You can enable automatic syncing between the filesystem and the in-memory database:
const project = await loadProjectFromDirectory({
path: "./project.inlang",
fs: fs,
syncInterval: 1000, // sync every second
});
When syncInterval is set, changes to files on disk are automatically imported, and changes to the database are automatically exported.
Directory structure
| File | Purpose |
|---|---|
settings.json | Project configuration (locales, plugins, plugin settings). This is the only file kept in Git by default. |
.gitignore | Auto-created, ignores everything except settings.json (including itself) |
README.md | Auto-created, explains the folder to coding agents (gitignored) |
.meta.json | Auto-created SDK metadata (gitignored) |
cache/ | Cached plugin modules, usually under cache/plugins/ (gitignored) |
Translation files (like messages/en.json) are managed by plugins and stored relative to your project based on plugin configuration.
Next steps
- CRUD API — Query and modify translations after loading
- Architecture — Understand how storage fits in the architecture
- Writing a Tool — Build a tool that loads unpacked projects