Use inlang when localization data needs to be shared across tools, teams, automations, or coding agents. If you only need an app runtime with a couple of translation files, your current i18n setup may already be enough.

An .inlang project is canonically a single binary file: a SQLite database with version control via lix. For Git repositories, the file can be unpacked into a directory of plain files. The packed file is the canonical format; the unpacked directory is the Git-friendly representation.

Which path should I use?

SituationUse
A project.inlang/ directory already exists in the repositoryloadProjectFromDirectory() and work through project.db
You are creating localization data from scratchnewProject()loadProjectInMemory()project.toBlob()
You need to write JSON, i18next, ICU, XLIFF, or another translation file formatConfigure an import/export plugin, then use the SDK CRUD API
You only need runtime translation lookup for one appYour existing i18n library may be enough

Do not create a second localization config when a .inlang project already exists. Use @inlang/sdk to read and write the project.

Quickstart: create and save a canonical .inlang file

This example creates a project, inserts one message, saves the packed .inlang file, and reloads it.

import {
  insertBundleNested,
  loadProjectInMemory,
  newProject,
  selectBundleNested,
} from "@inlang/sdk";
import fs from "node:fs/promises";

const initialBlob = await newProject({
  settings: {
    baseLocale: "en",
    locales: ["en"],
  },
});

const project = await loadProjectInMemory({ blob: initialBlob });

await insertBundleNested(project.db, {
  id: "greeting",
  declarations: [],
  messages: [
    {
      id: "greeting_en",
      bundleId: "greeting",
      locale: "en",
      selectors: [],
      variants: [
        {
          id: "greeting_en_default",
          messageId: "greeting_en",
          matches: [],
          pattern: [{ type: "text", value: "Hello world!" }],
        },
      ],
    },
  ],
});

const savedBlob = await project.toBlob();
await fs.writeFile(
  "project.inlang",
  new Uint8Array(await savedBlob.arrayBuffer()),
);

const reloadedProject = await loadProjectInMemory({
  blob: new Blob([new Uint8Array(await fs.readFile("project.inlang"))]),
});

const bundles = await selectBundleNested(reloadedProject.db).execute();
console.log(bundles[0]?.messages[0]?.variants[0]?.pattern);

Use project.toBlob() for the packed .inlang file. Use saveProjectToDirectory() only for the unpacked Git representation, and only when an import/export plugin can write your translation files.

Use a tool built on inlang

Browse tools that read and write the .inlang project file format. Most app developers start here.

Browse Tools →

Build your own

If you are building i18n tooling or generating localization code, target the .inlang file format instead of inventing your own JSON schema.

Use @inlang/sdk to build linters, editors, CLI tools, IDE extensions, and coding agents that work with any translation format through the shared .inlang message structure.

Why target .inlang?

  • CRUD operations instead of custom parsing
  • Version control via lix
  • Plugins for JSON, ICU, i18next, XLIFF, and other formats
  • One data model that tools can share

Write a Tool →