L Ling

Generated from docs/*.md

Ling CLI Usage

Use ling-scan and ling-lint to generate and validate translation files.

CLI Usage

Ling ships two CLI commands:

  • ling-scan - scan source files and update translation dictionaries
  • ling-lint - report missing translations compared to a default language

If you installed @orderofchaos/ling, the commands are already available. Otherwise install the standalone CLI package:

pnpm add -D @orderofchaos/ling-cli

ling-scan

# Default source directory: ./src
pnpm ling-scan

# Scan a specific directory
pnpm ling-scan ./app

The scanner looks for:

  1. initI18nModule("Namespace")
  2. t("Literal key")

It updates translation files under src/i18n/translations by default.

Existing translated values are preserved. New namespaces and phrases are appended.

ling-lint

# Default language is en if omitted
pnpm ling-lint

# Use a specific default language
pnpm ling-lint ru

# Specify both default language and translations directory
pnpm ling-lint ru ./src/i18n/translations

# Path-only form: use en and lint a custom directory
pnpm ling-lint ./src/i18n/translations

Missing translations are reported when:

  • a namespace is missing in a non-default language
  • a key is missing in a non-default language
  • a translation value is still identical to the original key

Configuration File

Create ling.config.json in the project root:

{
  "outDir": "src/i18n/translations",
  "locales": ["en", "ru"],
  "originalLocale": "en"
}

| Option | Default | Description | | --- | --- | --- | | outDir | src/i18n/translations | Output directory for generated files | | locales | ["ru", "en"] | Locales generated by ling-scan | | originalLocale | "ru" | Source language metadata stored in the config |

Example Workflow

// src/components/Header.tsx
import { initI18nModule } from "@orderofchaos/ling";

const { useI18n } = initI18nModule("Header");

export function Header() {
  const { t } = useI18n();

  return (
    <header>
      <h1>{t("Welcome to our app")}</h1>
      <p>{t("Navigate using the menu below")}</p>
    </header>
  );
}

After pnpm ling-scan src, Ling generates files like:

// src/i18n/translations/en.ts
export const en = {
  Header: {
    "Navigate using the menu below": "Navigate using the menu below",
    "Welcome to our app": "Welcome to our app",
  },
};

export default en;

Programmatic API

import {
  scanDirectory,
  scanFile,
  findMissingTranslations,
} from "@orderofchaos/ling-cli";

const scanResult = scanDirectory("./src", {
  extensions: ["ts", "tsx"],
});

const singleFile = scanFile("./src/components/Header.tsx");
const lintResult = findMissingTranslations(translations, "en");

CI Example

name: i18n

on:
  pull_request:
  push:
    branches: [master]

jobs:
  lint-translations:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: "24"
      - run: corepack enable
      - run: corepack prepare pnpm@10.13.1 --activate
      - run: pnpm install --frozen-lockfile
      - run: pnpm ling-lint en src/i18n/translations
{
  "scripts": {
    "i18n:scan": "ling-scan src",
    "i18n:lint": "ling-lint en src/i18n/translations",
    "i18n:check": "pnpm i18n:scan && pnpm i18n:lint"
  }
}