File System
The @ilingo/fs package provides FSStore — an IStore adapter that lazy-loads translations from disk and persists set() calls back as JSON.
Install
npm install @ilingo/fsUsage
import { Ilingo } from 'ilingo';
import { FSStore } from '@ilingo/fs';
const ilingo = new Ilingo({
store: new FSStore({ directory: './language' }),
});Locale strings live in subdirectories — one per locale — with one file per group:
└── language
├── de
│ ├── app.json
│ └── forum.json
└── en
├── app.json
└── forum.jsonSupported file extensions
FSStore resolves <directory>/<locale>/<group>.<ext> across the following extensions, in order:
.ts, .mts, .js, .mjs, .cjs, .json, .conf
Loading is provided by locter — first match wins.
File formats
JSON
{
"greeting": "Hello, {{name}}!",
"nested": {
"deep": "Deep value"
},
"cart": {
"items": {
"@plural": {
"one": "{{count}} item",
"other": "{{count}} items"
}
}
}
}TypeScript / JavaScript (ESM)
export default {
greeting: 'Hello, {{name}}!',
};CommonJS
module.exports = {
greeting: 'Hello, {{name}}!',
};Multiple directories
Pass an array to layer translations. Later directories merge into earlier ones via smob:
const store = new FSStore({
directory: ['./language', './overrides'],
});For each (locale, group), files from both directories are loaded and merged. Later directories win on conflicts.
Persistence
FSStore.set(...) writes the updated group back to disk as JSON:
await store.set({
locale: 'en',
group: 'app',
key: 'greeting',
value: 'Hello, {{name}}!',
});
// → ./language/en/app.jsonBy default writes go to the first configured directory. Pass writeDirectory to split read and write paths:
const store = new FSStore({
directory: ['./language', './overrides'],
writeDirectory: './overrides',
});Writes are atomic — FSStore writes to a temporary file in the same directory then renames it over the target. The full merged record is serialised; sibling keys are preserved.
If the original source for a group was a .ts/.js/.cjs file, that file is left untouched. The new .json lives alongside it. On the next load, smob merges both — the newer JSON wins.
When to use
- Translation files are part of the codebase and edited by humans (translators, contributors).
- You want hot-reloading:
FSStorelazy-loads per group on first access, so dev-server restarts are not needed. - You need durable runtime edits (CMS-like flows): the persistence story round-trips cleanly.
For network-loaded translations, write a custom store — see Guide → Stores → Writing a custom store.