An open API service indexing awesome lists of open source software.

https://github.com/stackoverprof/say-dictionary

[NPM] Simple and LLM-friendly i18n solution.
https://github.com/stackoverprof/say-dictionary

Last synced: 4 days ago
JSON representation

[NPM] Simple and LLM-friendly i18n solution.

Awesome Lists containing this project

README

          

# say-dictionary

URL-based i18n with configurable languages and a CLI for extracting translation keys.

## Why?

The dictionary format is designed to be **LLM-friendly**. Just hand your `dictionary.json` to an AI and ask it to translate — done. No complex tooling, no translation service integrations, no manual key mapping.

## Installation

```bash
npm install say-dictionary
# or
pnpm add say-dictionary
```

## Usage

### 1. Initialize (once in your entry point)

```ts
// root.tsx or app entry
import { init } from 'say-dictionary';
import dictionary from './dictionary.json';

init(dictionary);
```

### 2. Use anywhere

```ts
import { say, getLanguage, setLanguage } from 'say-dictionary';

// Get translated text
say("Order Now"); // Returns "Order Now" or "Panta núna" based on URL

// ICU formatting (only when vars are provided)
say("You have {count, plural, one {# pizza} other {# pizzas}}", {
count: 2,
});

// Get current language from URL (null if no language prefix)
getLanguage(); // "is" or null

// Navigate to different language
setLanguage('is'); // Redirects to /is/current-path
```

## Dictionary Format

```json
{
"Order Now": { "en": "Order Now", "is": "Panta núna" },
"Welcome": { "en": "Welcome", "is": "Velkomin" },
"You have {count, plural, one {# pizza} other {# pizzas}}": {
"is": "Þú átt {count, plural, one {# pizzur} other {# pizzur}}"
}
}
```

Languages are automatically detected from the dictionary keys.
ICU message formatting is **opt-in** and only runs when you pass variables to
`say(key, vars)`. If a translation is missing for the current language, the key
itself is treated as the ICU message.

Example:

```ts
say(
"Today is the {day, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} of March",
{ day: 3 }
);
// "Today is the 3rd of March"

say("Event on {date, date, ::MMMM d}", { date: new Date("2026-03-02") });
// "Event on March 2"
```

## URL Structure

The language is detected from the first path segment:

- `/is/about` → Icelandic (`say()` returns translation)
- `/about` → No language prefix (`say()` returns the key itself)

## SSR (Next.js, Remix, etc.)

For SSR frameworks, use `ssrLang()` to prevent hydration mismatch:

```tsx
// app/[lang]/page.tsx
export default async function Page({ params }) {
const { lang } = await params;
return ;
}

// app/page.tsx
"use client";
import { ssrLang, say } from 'say-dictionary';

export default function Home({ lang }: { lang?: string }) {
ssrLang(lang ?? null);
return

{say("Hello")}

;
}
```

This is optional — only needed for SSR. Client-side apps (Vite, CRA) work without it.

## CLI

Extract translation keys from your source files:

```bash
npx say-dictionary extract -l en,is -i ./app -o ./dictionary.json
```

Options:

- `-l, --lang` - Comma-separated list of languages
- `-i, --in` - Source directory to scan
- `-o, --out` - Output dictionary file

Output `dictionary.json`:

```json
{
"Order Now": { "en": "Order Now", "is": "" },
"Welcome": { "en": "Welcome", "is": "" }
}
```

**The first language (`en`) gets the key as its value.** Hand this to an LLM to fill in the translations.

## License

MIT