Summary
When a consumer sets <keksmeister-banner lang="pl"> (or passes lang: 'pl' via config), the banner renders in German instead of Polish — even though the translation file keksmeister/i18n/pl ships in the same package. The fallback is completely silent: no console warning, no thrown error, the banner just looks German.
Same applies to every locale shipped in dist/i18n/* other than de and en: fr, es, it, nl, pl.
Repro
import { resolveTranslations } from 'keksmeister';
console.log(resolveTranslations('pl').banner.title);
// → "Cookie-Einstellungen" (German — expected: "Ustawienia plików cookie")
Same shape in the browser:
<keksmeister-banner lang="pl" privacy-url="/privacy"></keksmeister-banner>
Renders the German strings.
Root cause
src/i18n/index.ts:
const builtinTranslations = { de, en };
const customTranslations = {};
export function resolveTranslations(lang) {
if (!lang) return de;
if (typeof lang === 'object') return lang;
return customTranslations[lang] ?? builtinTranslations[lang] ?? de;
}
builtinTranslations is hardcoded to { de, en }. The pl.ts, fr.ts, es.ts, it.ts, nl.ts files exist in the package and export valid KeksmeisterTranslations, but the consumer is responsible for registerTranslation('pl', pl) before the banner reads its config.
The README mentions extending with custom translations as a follow-up sentence, but the foot-gun is: the wrong language doesn't fail — it silently renders in German. A consumer in production might never notice that their Polish/French users see a German cookie banner.
Suggested fixes (pick whichever fits the design)
- Auto-resolve bundled locales on demand. When
resolveTranslations(lang) does not find a registered table for a locale that does exist on disk under i18n/, dynamically import + register it before returning. Tree-shakable per-locale chunks; common path (de/en) stays inlined. This makes lang=\"pl\" Just Work for every locale the package already ships.
- Warn on silent fallback in dev. If
lang is provided, not found in either map, and the value isn't de, emit a console.warn (gated on import.meta.env.DEV or equivalent) along the lines of: "keksmeister: no translation registered for 'pl' — falling back to 'de'. Import and registerTranslation from 'keksmeister/i18n/pl' to enable Polish."
- Document the side-loading footgun. Add an explicit code block to the README showing the
registerTranslation flow for the locales shipped under i18n/. Currently the README only says "German and English built-in, extensible with custom translations" which reads as: the others need custom tables, not as: the others ship in the package and just need registering.
(1) is the user-friendliest. (2) is the cheapest. (3) at minimum closes the documentation gap.
Context
Hit while rolling out multilingual support on apps/homepage in footage.one/frontend. Worked around it consumer-side by side-loading the matching keksmeister/i18n/<lang> chunk and calling registerTranslation inside our CookieConsentService.init(). See footage.one/frontend@e4a3c2fa1 for the workaround. Filing this so the next consumer doesn't have to discover the same trap by eye-balling their Polish banner.
Verified against
keksmeister@0.4.0
Summary
When a consumer sets
<keksmeister-banner lang="pl">(or passeslang: 'pl'viaconfig), the banner renders in German instead of Polish — even though the translation filekeksmeister/i18n/plships in the same package. The fallback is completely silent: no console warning, no thrown error, the banner just looks German.Same applies to every locale shipped in
dist/i18n/*other thandeanden:fr,es,it,nl,pl.Repro
Same shape in the browser:
Renders the German strings.
Root cause
src/i18n/index.ts:builtinTranslationsis hardcoded to{ de, en }. Thepl.ts,fr.ts,es.ts,it.ts,nl.tsfiles exist in the package and export validKeksmeisterTranslations, but the consumer is responsible forregisterTranslation('pl', pl)before the banner reads its config.The README mentions extending with custom translations as a follow-up sentence, but the foot-gun is: the wrong language doesn't fail — it silently renders in German. A consumer in production might never notice that their Polish/French users see a German cookie banner.
Suggested fixes (pick whichever fits the design)
resolveTranslations(lang)does not find a registered table for a locale that does exist on disk underi18n/, dynamically import + register it before returning. Tree-shakable per-locale chunks; common path (de/en) stays inlined. This makeslang=\"pl\"Just Work for every locale the package already ships.langis provided, not found in either map, and the value isn'tde, emit aconsole.warn(gated onimport.meta.env.DEVor equivalent) along the lines of: "keksmeister: no translation registered for 'pl' — falling back to 'de'. Import and registerTranslation from 'keksmeister/i18n/pl' to enable Polish."registerTranslationflow for the locales shipped underi18n/. Currently the README only says "German and English built-in, extensible with custom translations" which reads as: the others need custom tables, not as: the others ship in the package and just need registering.(1) is the user-friendliest. (2) is the cheapest. (3) at minimum closes the documentation gap.
Context
Hit while rolling out multilingual support on
apps/homepageinfootage.one/frontend. Worked around it consumer-side by side-loading the matchingkeksmeister/i18n/<lang>chunk and callingregisterTranslationinside ourCookieConsentService.init(). See footage.one/frontend@e4a3c2fa1 for the workaround. Filing this so the next consumer doesn't have to discover the same trap by eye-balling their Polish banner.Verified against
keksmeister@0.4.0