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> {
try { const extensions = ['jsonc', 'json'];
const localeUrl = new URL(`./${locale}.json`, import.meta.url); for (const ext of extensions) {
const file = await Deno.readTextFile(localeUrl); try {
translations = JSON.parse(file); const localeUrl = new URL(`./${locale}.${ext}`, import.meta.url);
} catch (err) { const raw = await Deno.readTextFile(localeUrl);
if (err instanceof Deno.errors.NotFound) { // parseJsonc tolerates both pure JSON and JSONC, so we can use it for either.
console.warn( translations = parseJsonc(raw) as Record<string, string>;
`Locale '${locale}' not found – falling back to 'en'.`, return;
); } catch (err) {
if (locale !== 'en') { if (err instanceof Deno.errors.NotFound) {
await loadLocale('en'); // Continue with next extension.
continue;
} }
} else { console.error(`Error parsing locale '${locale}.${ext}':`, err);
console.error('Error loading translation file:', err); break;
} }
} }
if (locale !== 'en') {
console.warn(`Locale '${locale}' not found – falling back to 'en'.`);
await loadLocale('en');
}
} }
/** /**