Some translation platforms restrict the character set or length of translation keys. IDs like user.profile.edit.title may not be accepted as-is. Hashing and shortening IDs at build time sidesteps these constraints.
Code
function toHex(buffer: ArrayBuffer): string { return [...new Uint8Array(buffer)] .map((b) => b.toString(16).padStart(2, '0')) .join('')}
export function createHashStore(alg: string = 'SHA-256') { const contextHash = new Map<string, string>() const msgIdHash = new Map<string, string>()
async function digestContext(context: string): Promise<string> { const data = new TextEncoder().encode(context) const buf = await crypto.subtle.digest(alg, data) return toHex(buf).slice(0, 7) }
async function digestMsgId(msgId: string): Promise<string> { const data = new TextEncoder().encode(msgId) const buf = await crypto.subtle.digest(alg, data) return toHex(buf).slice(0, 10) }
async function saveContext(context: string): Promise<string> { const cached = contextHash.get(context) if (cached) return cached const hash = await digestContext(context) contextHash.set(context, hash) return hash }
async function saveMsgId(msgId: string): Promise<string> { const cached = msgIdHash.get(msgId) if (cached) return cached const hash = await digestMsgId(msgId) msgIdHash.set(msgId, hash) return hash }
return { contextHash, msgIdHash, saveContext, saveMsgId }}Using the Web Crypto API (crypto.subtle.digest) makes this universal — it runs in browsers, Node.js, Deno, and Bun without modification. Valid values for alg are 'SHA-1', 'SHA-256', 'SHA-384', and 'SHA-512'.
How It Works
saveContext and saveMsgId return hashes while caching results in a Map. For the same input, the hash is computed only once. Contexts (namespaces) are trimmed to the first 7 characters, message IDs to 10. Contexts tend to be fewer in number and carry less collision risk, so a shorter prefix is sufficient.
Usage in Practice
Integrate into a build plugin (Vite plugin, custom code transform) to replace message IDs in source code with their hashes. At runtime, the translation table is keyed by hash rather than the original string.
To change the hashing algorithm, pass a different alg to createHashStore. Adjust truncation length via the slice argument.
hsb.horse