feat(i18n): support loading JSONC translation files

- Adds support for `.jsonc` format alongside `.json` for locale files
- Prioritizes `.jsonc` format when both file types are available
This commit is contained in:
2025-05-30 11:44:09 +02:00
parent 8f1cb3fad7
commit 4ac5dd4c88

View File

@@ -1,3 +1,5 @@
import { parse as parseJsonc } from '@std/jsonc';
/** /**
* Initializes the i18n module by loading * Initializes the i18n module by loading
* the appropriate locale file based on the system language. * the appropriate locale file based on the system language.
@@ -15,29 +17,35 @@ let translations: Record<string, string> = {};
/** /**
* Loads the translation file for the specified locale. * Loads the translation file for the specified locale.
* *
* Expects a JSON file in the same directory named like `de.json` or `en.json`. * Accepts both `.jsonc` (JSON with comments) and plain `.json`.
* When both exist, `.jsonc` takes precedence.
* Falls back to English ('en') if the specified file does not exist. * Falls back to English ('en') if the specified file does not exist.
* *
* @param locale - The language code (e.g., 'de', 'en') to load * @param locale - The language code (e.g., 'de', 'en') to load
* @returns Promise that resolves once the translations have been loaded * @returns Promise that resolves once the translations have been loaded
*/ */
export async function loadLocale(locale: string): Promise<void> { export async function loadLocale(locale: string): Promise<void> {
const extensions = ['jsonc', 'json'];
for (const ext of extensions) {
try { try {
const localeUrl = new URL(`./${locale}.json`, import.meta.url); const localeUrl = new URL(`./${locale}.${ext}`, import.meta.url);
const file = await Deno.readTextFile(localeUrl); const raw = await Deno.readTextFile(localeUrl);
translations = JSON.parse(file); // parseJsonc tolerates both pure JSON and JSONC, so we can use it for either.
translations = parseJsonc(raw) as Record<string, string>;
return;
} catch (err) { } catch (err) {
if (err instanceof Deno.errors.NotFound) { if (err instanceof Deno.errors.NotFound) {
console.warn( // Continue with next extension.
`Locale '${locale}' not found – falling back to 'en'.`, continue;
); }
console.error(`Error parsing locale '${locale}.${ext}':`, err);
break;
}
}
if (locale !== 'en') { if (locale !== 'en') {
console.warn(`Locale '${locale}' not found – falling back to 'en'.`);
await loadLocale('en'); await loadLocale('en');
} }
} else {
console.error('Error loading translation file:', err);
}
}
} }
/** /**