ADR 005 — Dynamic key rebuild, const map, and partial prefixes
Status: Accepted
Context
Translation calls with a template literal first argument often look “dynamic” because of `${…}`, even when most of the key path is static and only a few segments depend on runtime values or unresolved bindings.
Static analysis cannot execute user code, but many codebases repeat patterns such as:
- A file-level
const NS = 'some.namespace'andt(`${NS}.page.title`). - A long dotted path with a runtime-only segment in the middle:
t(`app.section.${id}.label`).
Without special handling, every such call is classified as non-literal, which over-reports “dynamic” usage and weakens trust in validate and locales dynamic.
Decision
-
Const string map — Reuse the same
buildConstStringMap(packages/cli/src/core/constmap/build.ts) that powersexactLiteralKeys: collectconst Name = 'value'pairs from the current file (or merged scan text). -
Full rebuild — For template inner text, run
resolveKeyPlaceholders(packages/cli/src/core/constmap/resolve.ts). If every${Ident}refers to a known const and the result has no remaining${…}, treat the call as fully static and do not emit a dynamic site. -
Partial prefix — If rebuild fails, compute
resolvedPrefix: the substring before the first${…}that cannot be resolved (or is not a simple identifier), then substitute known${Ident}values in that prefix only. Expose this onDynamicKeySitefor tooling and future cleanup alignment. Require at least one.in the prefix so single-segment roots stay conservative. -
Extension-only providers — Language-specific analysis stays in
packages/cli/src/core/extractor/dynamic/providers/; source discovery remains extension-based, not folder-name heuristics. -
Comments — Comment regions are detected separately; call sites inside comments use kind
commented.
The Why
- Same map for literals and dynamic filtering avoids drift between
validateand dynamic reporting. - Rebuild turns a large class of “looks dynamic, is actually constant” calls into zero dynamic noise.
- Partial prefix documents what part of the key path is still trustworthy when the full key is unknown—without claiming a false static key.
- No runtime execution preserves safety; we only substitute simple identifiers present in the const map.
Consequences
DynamicKeySitemay includeresolvedPrefixfortemplate_interpolation.@zamdevio/i18nprune/coreexportstryRebuildTemplateKeyFromConstsandtryResolveTemplatePrefixBeforeUnknownfor scripts that mirror CLI rules.- Large projects that rely on namespace
consts should see fewer reported dynamic sites when those templates become statically rebuildable.
Follow-up (not in this ADR)
- Optional const aliases resolved via imported key objects (requires config or analysis of imports).
- String-array patterns (
const keys = ['a.b', …]) andt(k)loops—shared with cleanup used-key logic. - Optional ripgrep verification for high-assurance CI.