# Release v1.2.0 — Hot Reload + Change-Event Registry
**Date:** 2026-05-20
**Compare:** `v1.1.0...v1.2.0`
## Summary
Two new opt-in features, both off by default — your existing `1.1.0`
dependency line continues to work unchanged.
- **`registry`** — wires `lang-lib` to `registry-io 1.0`. Install
handlers via `Lang::on_change` that fire whenever a locale is loaded,
reloaded, or unloaded. Sub-microsecond dispatch per handler. A panic
in one handler does not affect siblings.
- **`hot-reload`** — implies `registry`. Adds `Lang::watch` /
`Lang::unwatch`, which subscribe to filesystem events on the configured
locales directory and atomically reload changed `<locale>.toml` files.
Cross-platform via the `notify = "6"` crate (inotify / FSEvents /
ReadDirectoryChangesW). Per-file events are debounced (~150 ms) so
atomic-rename writes coalesce into a single reload.
## Added
### Feature flags
- `registry = ["dep:registry-io"]`
- `hot-reload = ["dep:notify", "registry"]` (implies `registry`)
### New public types
- `lang_lib::LangChangeEvent { locale: &'static str, kind: ChangeKind }`
(`registry` feature)
- `lang_lib::ChangeKind` — variants: `Loaded`, `Reloaded`, `Unloaded`,
`FileMissing`, `ParseFailed`
- `lang_lib::HandlerId` — re-export of `registry_io::HandlerId` so
callers don't need a direct dep on `registry-io`
- `lang_lib::WatchError` (`hot-reload` feature) — variants: `Io`,
`AlreadyRunning`
### New public functions on `Lang`
- `Lang::on_change<F>(handler: F) -> HandlerId` (`registry` feature)
- `Lang::off_change(id: HandlerId) -> bool` (`registry` feature)
- `Lang::watch(dir: impl AsRef<Path>) -> Result<(), WatchError>`
(`hot-reload` feature)
- `Lang::unwatch()` (`hot-reload` feature)
### Tests & examples
- `tests/registry.rs` — five cases covering Loaded / Reloaded /
Unloaded emission, no-op unload of an absent locale, and `off_change`
stop semantics.
- `tests/watch.rs` — two smoke tests covering modify→reload round-trip
and `unwatch` clean-up. Gated behind `--features hot-reload`.
- `examples/hot_reload.rs` — runnable demo of the watcher feeding live
changes into `t!`.
### CI
- `actions/upload-artifact@v4` → `@v7` (Node 20 → Node 24 actions
deprecation fix).
- Quality matrix exercises `cargo test --no-default-features`,
`cargo test --features registry`, and `cargo check --example
hot_reload --features hot-reload`.
## Changed
- README install snippet bumped to `1.2.0`; added an optional-features
install snippet and feature bullets.
- CHANGELOG `[1.2.0]` section.
## Removed
- Nothing.
## Fixed
- Nothing functional from `1.1.0`. The only fix is the CI Node 20
deprecation warning on the benchmarks workflow.
## Compatibility
No code changes are required when upgrading from `1.1.0`. The default
feature set is unchanged: the `registry` and `hot-reload` modules and
APIs are only compiled when the corresponding feature is enabled.
## Migration / Adoption
### Subscribing to change events programmatically
```toml
[dependencies]
lang-lib = { version = "1.2.0", features = ["registry"] }
```
```rust
use lang_lib::{ChangeKind, Lang};
let id = Lang::on_change(|event| {
match event.kind {
ChangeKind::Loaded => println!("loaded {}", event.locale),
ChangeKind::Reloaded => println!("reloaded {}", event.locale),
ChangeKind::Unloaded => println!("unloaded {}", event.locale),
_ => {}
}
});
Lang::load("en")?;
let _ = Lang::off_change(id);
```
### Watching the filesystem
```toml
[dependencies]
lang-lib = { version = "1.2.0", features = ["hot-reload"] }
```
```rust
use lang_lib::Lang;
Lang::set_path("locales");
Lang::load("en")?;
Lang::watch("locales")?;
// ... application runs; edits to locales/*.toml are picked up automatically ...
Lang::unwatch();
```
## Memory Note
The interner introduced in `1.1.0` is still append-only. Hot reload
inside this release does **not** add new pressure on the interner
because the value strings parsed from new file contents are deduped
against any identical strings already interned. Translation files that
churn through completely fresh values on every reload would grow the
interner over time; in practice locale files mostly tweak existing
strings rather than replace them wholesale.
A future release may add a generational interner if real-world hot
reload pressure exposes the growth as a problem.
## Next
Open backlog items (post-`1.2.0`):
- **Bench numbers committed.** Capture criterion + dhat baseline numbers
on representative hardware and commit them under `benches/baseline/`.
- **String interpolation / placeholders.** `t!("welcome", name: "John")`
with run-time template substitution.
- **Multiple locale directories.** Priority-ordered search path.
---
**Full Changelog:** https://github.com/jamesgober/lang-lib/compare/v1.1.0...v1.2.0