CLI Usage
Ling ships two CLI commands:
ling-scan- scan source files and update translation dictionariesling-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:
initI18nModule("Namespace")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
Recommended Scripts
{
"scripts": {
"i18n:scan": "ling-scan src",
"i18n:lint": "ling-lint en src/i18n/translations",
"i18n:check": "pnpm i18n:scan && pnpm i18n:lint"
}
}