# i18n-log

[![npm version](https://badge.fury.io/js/i18n-log.svg)](https://badge.fury.io/js/i18n-log)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A powerful linter for internationalization (i18n) in JavaScript/TypeScript projects. Works like ESLint but specifically for translation issues - detecting untranslated text, missing keys, unused translations, and more with configurable severity levels and CI/CD integration.

**🚀 Supports all major frameworks:** React, Vue, Angular, Svelte, Next.js, Nuxt, and more!

## Features

✅ **Framework Support**: React, Vue, Angular, Svelte, Next.js, Nuxt, and more
✅ **Advanced Variable Tracking**: Detects `useTranslations('namespace')` and similar patterns
✅ **Linter-Style Output**: Configurable rules, severity levels, and exit codes
✅ **Multiple Output Formats**: Stylish (default), JSON, compact for CI/CD integration
✅ **Real-time Analysis**: Fast AST-based parsing with comprehensive pattern detection
✅ **CI/CD Ready**: Proper exit codes, thresholds, and JSON reporting

## Prerequisites

- Node.js 18+ (ships with npm and npx)

## Install

```bash
# Install globally
npm install -g i18n-log

# Or as a dev dependency
npm install --save-dev i18n-log

# Or use with npx (no installation required)
npx i18n-log --help
```

## Quick Start

1. Initialise the project configuration:

   ```bash
   npx i18log init
   ```

   The wizard looks for existing i18n config files (e.g. `i18next.config.js`, `next-i18next.config.js`) and common translation directories such as `locales/` or `src/locales/`. Confirm a detected path or enter your own when prompted. A new `i18log.config.ts` file is created at the repo root.

2. Run the audit:

   ```bash
   npx i18log lint
   ```

   You will see a colourised summary plus the number of missing translations, lone keys, and hard-coded snippets. A JSON report is written to the location configured in `i18log.config.ts` (default `i18n-report.json`).

## CLI Reference

```text
Usage: i18log <command> [options]

Commands:
  lint                    Run the translation audit (default)
  init                    Create a config file with sensible defaults
  help                    Show the CLI reference

lint options:
  -c, --config <path>     Use a specific config file
  -o, --output <path>     Override the JSON report output path
  -f, --function <name>   Override the translation function name (default: t)
  -e, --extensions <list> Comma separated source extensions to scan
  -t, --translations <list>
                          Comma separated translation file or directory paths
      --ignore <list>     Comma separated glob patterns to ignore
      --no-prompt         Skip interactive prompts when no config is found
      --format <type>     Output format: stylish (default), json, compact
      --max-errors <n>    Maximum errors before failing (CI/CD)
      --max-warnings <n>  Maximum warnings before failing (CI/CD)
      --quiet             Only report errors, ignore warnings
  -h, --help              Show help

init options:
  -c, --config <path>     Where to write the generated config (default: i18log.config.ts)
  -y, --yes               Accept detected suggestions without prompting
  -h, --help              Show help
```

### Running From Source

When working on the CLI itself you can still run:

```bash
npm start            # ts-node src/cli.ts
npm run build        # emit dist/ for the published package
node dist/cli.js lint
```

## Lint Rules

i18n-log works as a linter with configurable rules and severity levels:

### Available Rules

- **missing-translation** (error) – `t('key')` usage without a matching translation entry
- **unused-translation** (warning) – Translation keys defined in JSON but never used in code
- **hardcoded-text** (warning) – JSX text nodes or string literals that should be translated
- **empty-translation** (warning) – Translation keys with empty values
- **duplicate-translation** (info) – Different keys with identical translation values
- **inconsistent-placeholders** (error) – Missing or mismatched placeholders across languages
- **missing-plural-forms** (warning) – Keys that likely need plural forms

### Exit Codes

- **0** – Success, no errors (warnings allowed unless `--max-warnings` is set)
- **1** – Linting errors found or threshold exceeded

Perfect for CI/CD pipelines:

```bash
# Fail on any error
npx i18log lint

# Fail if more than 10 warnings
npx i18log lint --max-warnings 10

# Only check for errors (ignore warnings)
npx i18log lint --quiet
```

## Output Formats

### Stylish (default)

Colored, human-readable output similar to ESLint:

```
src/components/Header.jsx
  12:8  error    Translation key "header.title" is not defined  (missing-translation)
  24:10 warning  Hardcoded text "Welcome" should be translated  (hardcoded-text)

✖ 2 problems (1 error, 1 warning)
```

### JSON

Machine-readable format for tooling integration:

```bash
npx i18log lint --format json > results.json
```

### Compact

One issue per line for easy parsing:

```bash
npx i18log lint --format compact
# src/App.jsx:26:54: warning: Hardcoded text "English" [hardcoded-text]
```

## Configuration

### Basic Config File

`npx i18log init` generates a typed config file:

```ts
// i18log.config.ts
import { defineConfig } from 'i18n-log';

export default defineConfig({
  translationPaths: ['public/locales'],
  sourceExtensions: ['.ts', '.tsx', '.js', '.jsx'],
  translationFunctionName: 't',
  outputPath: 'reports/i18n-report.json',
  ignorePaths: ['**/node_modules/**', '**/dist/**']
});
```

### Lint Configuration

Customize rules in `.i18nlintrc.json`:

```json
{
  "rules": {
    "missing-translation": "error",
    "unused-translation": "warning",
    "hardcoded-text": "warning",
    "empty-translation": "warning",
    "duplicate-translation": "info"
  },
  "ignoredKeys": [
    "debug.*",
    "internal.*"
  ],
  "maxErrors": 0,
  "maxWarnings": 50
}
```

The CLI automatically discovers config files at the project root.

## Supported Frameworks

i18n-log automatically detects and supports translation patterns from all major frameworks:

### React Ecosystem
- **Next.js intl**: `useTranslations('namespace')`, `useMessages()`, `useLocale()`
- **react-i18next**: `useTranslation()`, `useTranslation('namespace')`, `<Trans>`
- **react-intl/formatjs**: `useIntl()`, `intl.formatMessage({ id: 'key' })`
- **Lingui**: `useLingui()`, `_('key')`, `<Trans>`

### Vue Ecosystem
- **vue-i18n Composition API**: `useI18n()`, `t()`, `tc()`, `te()`, `d()`, `n()`
- **Vue Options API**: `this.$t()`, `this.$tc()`, `this.$te()`, `this.$d()`, `this.$n()`
- **Nuxt i18n**: `$t()`, `useI18n()`

### Angular Ecosystem
- **ngx-translate**: `TranslateService.get()`, `TranslateService.instant()`
- **Angular Localize**: `$localize\`key\``, `$localize\`:@@id:text\``

### Other Frameworks
- **Svelte**: `_('key')`, `$_('key')`, svelte-i18n patterns
- **Framework Agnostic**: `i18next.t()`, `polyglot.t()`, generic patterns

### Advanced Pattern Detection

i18n-log can detect complex patterns like:

```javascript
// Namespace tracking
const t = useTranslations('dashboard.settings');
t('title') // → Detected as 'dashboard.settings.title'

// Multiple translation functions
const tNav = useTranslations('navigation');
const tForms = useTranslations('forms.validation');

// Destructuring
const { t, tc, te } = useI18n();

// Conditional assignments
const t = isAdmin ? useTranslations('admin') : useTranslations('user');

// Member expressions
i18n.t('key'), this.$t('key'), intl.formatMessage({ id: 'key' })
```

## Development

Type-check and build the distributable:

```bash
npm run lint
npm run build
```

Generated artifacts are emitted to `dist/`.

### Testing with Example App

The `example/` folder contains a minimal React app for testing i18n-log:

```bash
# Build i18n-log first
npm run build

# Run the linter on the example app
cd example
npm install
npm run i18n:lint           # Default stylish output
npm run i18n:lint:json      # JSON format
npm run i18n:lint:quiet     # Errors only
npm run i18n:check          # Strict mode (fail on any warning)
```

The example demonstrates:
- React components with i18next
- Multiple languages (EN, ES, FR)
- Various translation patterns (nested keys, interpolation)
- Common issues (unused keys, hardcoded text)

### Local npm Alias

To experiment with the CLI without publishing, add an alias to your local `package.json` in the consuming project:

```json
{
  "devDependencies": {
    "i18n-log": "file:../path-to/i18n-log"
  }
}
```

After running `npm install`, `npx i18log` will resolve to your local checkout so you can verify changes end-to-end.

## Inspiration

The translation path discovery draws inspiration from the [i18n Ally](https://github.com/lokalise/i18n-ally) project.

## License

MIT
