cjpais/Handy · info
Missing metadata for locale "${code}" in languages.ts
Error message
Missing metadata for locale "${code}" in languages.ts What it means
At i18n bootstrap, import.meta.glob eagerly loads every src/i18n/locales/<code>/translation.json and turns each directory name into a supported language. This console.warn fires when such a discovered locale code has no matching entry in LANGUAGE_METADATA (src/i18n/languages.ts); the language still works but is listed in the UI with its raw code as both name and nativeName and sorts by that string.
Source
Thrown at src/i18n/index.ts:32
"./locales/*/translation.json",
{ eager: true },
);
// Build resources from discovered locale files
const resources: Record<string, { translation: Record<string, unknown> }> = {};
for (const [path, module] of Object.entries(localeModules)) {
const langCode = path.match(/\.\/locales\/(.+)\/translation\.json/)?.[1];
if (langCode) {
resources[langCode] = { translation: module.default };
}
}
// Build supported languages list from discovered locales + metadata
export const SUPPORTED_LANGUAGES = Object.keys(resources)
.map((code) => {
const meta = LANGUAGE_METADATA[code];
if (!meta) {
console.warn(`Missing metadata for locale "${code}" in languages.ts`);
return { code, name: code, nativeName: code, priority: undefined };
}
return {
code,
name: meta.name,
nativeName: meta.nativeName,
priority: meta.priority,
};
})
.sort((a, b) => {
// Sort by priority first (lower = higher), then alphabetically
if (a.priority !== undefined && b.priority !== undefined) {
return a.priority - b.priority;
}
if (a.priority !== undefined) return -1;
if (b.priority !== undefined) return 1;
return a.name.localeCompare(b.name);
});View on GitHub (pinned to c89b7bf389)
Solutions
- Add a metadata entry { name, nativeName, priority? } to LANGUAGE_METADATA in src/i18n/languages.ts keyed by the exact folder code
- Use canonical BCP-47 codes consistently between folder name and metadata key (case-sensitive match)
- Add a unit test asserting every SUPPORTED_LANGUAGES entry has name !== code, which catches any future occurrence
- Remove stray locale folders that are not intended to ship
Example fix
// before: locales/pt-BR/translation.json exists, languages.ts has no 'pt-BR' key
// -> console.warn(`Missing metadata for locale "pt-BR" in languages.ts`)
// after: src/i18n/languages.ts
export const LANGUAGE_METADATA: Record<string, LanguageMetadata> = {
// ...
"pt-BR": { name: "Portuguese (Brazil)", nativeName: "Português (Brasil)", priority: 6 },
}; Defensive patterns
Strategy: validation
Validate before calling
// Fail the build/test when a locale folder lacks metadata
import localeModules from "virtual:i18n-glob"; // or replicate the glob in a vitest test
const discovered = Object.keys(localeModules).map((p) => p.match(/\.\/locales\/(.+)\/translation\.json/)?.[1]);
const missing = discovered.filter((c) => c && !(c in LANGUAGE_METADATA));
if (missing.length) throw new Error(`Missing LANGUAGE_METADATA for: ${missing.join(", ")}`); Type guard
const isConfiguredLocale = (code: string): code is keyof typeof LANGUAGE_METADATA => Object.prototype.hasOwnProperty.call(LANGUAGE_METADATA, code);
Prevention
- Any PR adding src/i18n/locales/<code>/ must also add <code> to LANGUAGE_METADATA in the same PR
- Use exact-match BCP-47 codes between folder names and metadata keys (case-sensitive)
- Watch the console during first boot of a new locale — this warn is the only signal
When it happens
Trigger: Adding a new locale folder (e.g. src/i18n/locales/pt-BR/translation.json) without adding a pt-BR entry to LANGUAGE_METADATA; a folder whose code differs from the metadata key in case or hyphenation (zh-tw vs zh-TW); leftover experimental locale directories committed by accident.
Common situations: Translation contributions (the repo explicitly invites them via CONTRIBUTING_TRANSLATIONS.md) forgetting the metadata step; locale renames; copy-pasting a locale folder as a starting point.
Related errors
- English translations must exist
- Failed to sync language from settings:
- error while building tauri application
- No handler for setting: ${String(key)}
AI-assisted analysis of cjpais/Handy@c89b7bf389 (2026-08-17).
Data as JSON: /api/errors/0a2526b6aa32278d.
Report an issue: GitHub.