lang-lib 1.3.0

A lightweight, high-performance localization library for Rust. Loads TOML language files, supports runtime locale switching, configurable paths, and automatic fallback chains.
# Release v1.3.0 — REPS-Compliant Hot Reload + Full API Documentation
**Date:** 2026-05-20
**Compare:** `v1.2.0...v1.3.0`

## Summary

Two things land in `1.3.0`:

1. **Hot reload is now genuinely REPS-compliant on memory.** The
   append-only interner growth caveat shipped with `1.2.0` is gone.
   Reloading `<locale>.toml` files no longer leaks string values
   into a permanent pool.
2. **The documentation deficit is closed.** `docs/API.md` and
   `docs/PROJECT-GUIDELINES.md` were empty; both are now complete
   references with examples for every public item.

The public API is source-compatible with `1.2.0`. Bump the version
and you're done.

## The memory-safety fix

The zero-allocation read path in `1.1.0` / `1.2.0` works by handing
out `Cow::Borrowed(&'static str)` references that point into a
process-wide interner. In the default build that's perfectly safe:
locale files are loaded at startup and the interner stops growing.
But under the `hot-reload` feature, every reload that introduced
fresh string values would leak them — the interner is append-only.

`1.3.0` resolves this by **making the value-storage strategy
compile-time conditional** on the `hot-reload` feature:

| Build         | Value storage           | Hit-path return                        | Reclaims on reload? |
|---------------|-------------------------|-----------------------------------------|---------------------|
| Default       | Interned `&'static str` | `Cow::Borrowed(&'static str)` — 0 alloc | N/A (no reloads)    |
| `hot-reload`  | `Arc<str>`              | `Cow::Owned(String)` — 1 alloc          | **Yes.**            |

The feature flag now genuinely encodes its trade-off:

- "I load locales at startup, then serve traffic" → default build,
  zero-allocation reads, leak doesn't matter because growth is
  impossible.
- "My service reloads files at runtime" → `hot-reload` build, pay
  one allocation per read, get clean memory lifecycle.

Both modes remain lock-free and `Send + Sync`. Concurrent translate
calls never contend on a lock in either build.

## New: `Lang::translate_arc` opt-in (hot-reload only)

For `hot-reload` users who want to avoid the per-call `String`
allocation on the hit path, `1.3.0` adds:

```rust,ignore
let value: Arc<str> = Lang::translate_arc("greeting", Some("en"), None);
println!("{value}");
```

This is zero-allocation on hit (just a refcount bump on the
underlying `Arc<str>`) and one allocation on miss paths (constructs
a fresh `Arc<str>` from the inline fallback or the key).

`Translator` gets the matching methods:

```rust,ignore
let t = Translator::new("en");
let value = t.translate_arc("greeting");
let value_or_default = t.translate_arc_with_fallback("missing", "Default");
```

**Trade-off:** many threads cloning the same `Arc<str>`
simultaneously contend on its refcount cache line. If your service
has 64+ threads all hitting the same hot key (like a homepage
title), the default `Cow::Owned` path is actually more consistent
under load. Use `translate_arc` when you've measured the
allocation cost as a hot spot and your access pattern spreads
across many keys.

## Documentation overhaul

### `docs/API.md` (new)

Full API reference written from scratch. Every public item is
documented with parameters, return types, the feature gate it lives
under, and at least one worked example. Sections:

- Crate root (item table)
- `Lang` (configuration / loading / querying / translation /
  registry / watcher subsections)
- `Translator`
- `LangError`
- `t!` macro
- Request helpers (`resolve_accept_language` /
  `resolve_accept_language_owned`)
- `LangChangeEvent` / `ChangeKind` / `HandlerId` (registry feature)
- `WatchError` (hot-reload feature)
- Feature flags matrix
- MSRV, runtime dependencies, stability contract
- Memory model (the dual-storage explanation)
- Performance pointers
- Quick example + hot-reload example

### `docs/PROJECT-GUIDELINES.md` (new)

Operational guidelines: loading model, per-request locale handling,
feature-flag selection, fallback chain policy, contribution flow,
CI gates, engineering discipline. Pairs with `docs/API.md` so users
can find both "what's the surface?" and "how do I use it well?"
without reading the source.

### Other updates

- `README.md` — install snippets bumped to `1.3.0`; clarified
  storage trade-off in optional-features section; new Documentation
  section pointing at `docs/API.md` and `docs/PROJECT-GUIDELINES.md`;
  added `examples/hot_reload.rs` to the example list with its
  feature requirement.
- `BENCHMARKS.md` — documents the `translate_hit_concurrent`
  benchmark and the concurrency stress tests added in `1.1.0` /
  `1.2.0`.
- `CHANGELOG.md` — full `[1.3.0]` section.

## Added

- `Lang::translate_arc(key, locale, fallback) -> Arc<str>` (hot-reload feature)
- `Translator::translate_arc(&self, key) -> Arc<str>` (hot-reload feature)
- `Translator::translate_arc_with_fallback(&self, key, fallback) -> Arc<str>` (hot-reload feature)
- `tests/watch.rs::hot_reload_storage_drops_arc_str_on_reload` — explicit refcount-probe reclaim test
- `docs/API.md` — authoritative public API reference
- `docs/PROJECT-GUIDELINES.md` — production patterns + contributor flow

## Changed

- Internal value-storage strategy now compile-time conditional on
  `hot-reload`. Public API shape unchanged.
- `src/loader.rs` produces `FxHashMap<&'static str, StoredValue>`,
  where `StoredValue` is `&'static str` in default builds and
  `Arc<str>` in `hot-reload` builds.
- README, BENCHMARKS, and CHANGELOG aligned with the actual
  `1.3.0` surface and behavior.

## Removed

- Nothing.

## Fixed

- **Append-only interner growth under `hot-reload`.** The headline
  caveat of `1.2.0` is closed.

## Compatibility

No breakage. The public API is source-compatible with `1.2.0`:

```toml
[dependencies]
lang-lib = "1.3.0"
```

`Lang::translate` still returns `Cow<'a, str>`. Anything that
worked on a `Cow<'_, str>` in `1.2.0` continues to work in `1.3.0`.
`Translator` is still `Copy`. All `1.2.0` feature flags retain
their behavior. The new `translate_arc` methods are additive and
gated on `hot-reload`.

## Migration from 1.2.0

Just bump the version. If you opt into `hot-reload` and want to
keep zero-allocation reads, switch to the new `translate_arc` API
(and read the trade-off in the API doc first).

## Next

Open backlog:

- Capture criterion + dhat baseline numbers on representative
  hardware and commit them under `benches/baseline/`.
- String interpolation / placeholders (`t!("welcome", name: "John")`).
- Multiple locale directories with priority-ordered search.

---

**Full Changelog:** https://github.com/jamesgober/lang-lib/compare/v1.2.0...v1.3.0