diff --git a/.vscode/settings.json b/.vscode/settings.json index 64f9dc9..90264de 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,5 +17,8 @@ "[json]": { "editor.defaultFormatter": "denoland.vscode-deno" }, + "[jsonc]": { + "editor.defaultFormatter": "denoland.vscode-deno" + }, "editor.formatOnSave": true } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c26a5cc..24f1e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. ## [unreleased] +### 🚀 Features + +- *(vscode)* Add JSONC formatter configuration - ([c7af1fb](https://git.0xmax42.io/maxp/systemd-timer/commit/c7af1fb6caa46c22b84229745067d05bf60b6f64)) +- *(i18n)* Support loading JSONC translation files - ([4ac5dd4](https://git.0xmax42.io/maxp/systemd-timer/commit/4ac5dd4c88324f99cb6827283ad85bb9718abbeb)) +- *(config)* Add @std/jsonc dependency - ([8f1cb3f](https://git.0xmax42.io/maxp/systemd-timer/commit/8f1cb3fad71ead365d93087963ddb6c7202a9b4f)) + +### 🎨 Styling + +- *(i18n)* Add comments for clarity and rename files - ([5226269](https://git.0xmax42.io/maxp/systemd-timer/commit/5226269ec2a0b76dfa30ac8d614c3789ff3a837b)) + ### 🧪 Testing - *(fs)* Update test descriptions and comments to English - ([c4f4614](https://git.0xmax42.io/maxp/systemd-timer/commit/c4f4614a2daee68f9b33b9676106214c65a1a427)) diff --git a/deno.jsonc b/deno.jsonc index 71ffc2b..5132c0c 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -23,6 +23,7 @@ }, "exclude": [], "imports": { - "@cliffy/command": "jsr:@cliffy/command@1.0.0-rc.7" + "@cliffy/command": "jsr:@cliffy/command@1.0.0-rc.7", + "@std/jsonc": "jsr:@std/jsonc@^1.0.2" } } \ No newline at end of file diff --git a/deno.lock b/deno.lock index 4bf34d8..b1161da 100644 --- a/deno.lock +++ b/deno.lock @@ -6,6 +6,8 @@ "jsr:@cliffy/internal@1.0.0-rc.7": "1.0.0-rc.7", "jsr:@cliffy/table@1.0.0-rc.7": "1.0.0-rc.7", "jsr:@std/fmt@~1.0.2": "1.0.7", + "jsr:@std/json@^1.0.2": "1.0.2", + "jsr:@std/jsonc@^1.0.2": "1.0.2", "jsr:@std/text@~1.0.7": "1.0.13" }, "jsr": { @@ -37,6 +39,15 @@ "@std/fmt@1.0.7": { "integrity": "2a727c043d8df62cd0b819b3fb709b64dd622e42c3b1bb817ea7e6cc606360fb" }, + "@std/json@1.0.2": { + "integrity": "d9e5497801c15fb679f55a2c01c7794ad7a5dfda4dd1bebab5e409cb5e0d34d4" + }, + "@std/jsonc@1.0.2": { + "integrity": "909605dae3af22bd75b1cbda8d64a32cf1fd2cf6efa3f9e224aba6d22c0f44c7", + "dependencies": [ + "jsr:@std/json" + ] + }, "@std/text@1.0.13": { "integrity": "2191c90e6e667b0c3b7dea1cd082137580a93b3c136bad597c0212d5fe006eb1" } @@ -173,7 +184,8 @@ }, "workspace": { "dependencies": [ - "jsr:@cliffy/command@1.0.0-rc.7" + "jsr:@cliffy/command@1.0.0-rc.7", + "jsr:@std/jsonc@^1.0.2" ] } } diff --git a/src/i18n/de.json b/src/i18n/de.jsonc similarity index 95% rename from src/i18n/de.json rename to src/i18n/de.jsonc index 0b7f7f6..12e6590 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.jsonc @@ -1,6 +1,8 @@ { + // General "cli_description": "CLI-Tool zum Erzeugen von systemd .timer und .service Units", "cli_create_description": "Erzeugt eine systemd .service und .timer Unit", + // Options "option_name": "Name der Unit-Dateien (optional, wird sonst aus dem Exec generiert)", "option_exec": "Kommando, das durch systemd ausgeführt werden soll", "option_calendar": "OnCalendar-Ausdruck für den Timer", @@ -14,9 +16,11 @@ "option_environment": "Environment-Variablen im Format KEY=VALUE", "option_logfile": "Dateipfad für Log-Ausgabe (stdout/stderr)", "option_dry_run": "Gibt die Unit-Dateien nur aus, ohne sie zu schreiben", + // Messages "unit_written_service": "Service Unit geschrieben in: {path}", "unit_written_timer": "Timer Unit geschrieben in: {path}", "hint_header": "\nℹ️ Hinweis:", + // Error messages "error_write_units": "Fehler beim Schreiben der Units:", "rollback_failed": "Rollback fehlgeschlagen:" } diff --git a/src/i18n/en.json b/src/i18n/en.jsonc similarity index 95% rename from src/i18n/en.json rename to src/i18n/en.jsonc index b5d14b0..16cca89 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.jsonc @@ -1,6 +1,8 @@ { + // General "cli_description": "CLI tool for generating systemd .timer and .service units", "cli_create_description": "Generates a systemd .service and .timer unit", + // Options "option_name": "Name of the unit files (optional, otherwise derived from the exec command)", "option_exec": "Command to be executed by systemd", "option_calendar": "OnCalendar expression for the timer", @@ -14,9 +16,11 @@ "option_environment": "Environment variables in the format KEY=VALUE", "option_logfile": "File path for log output (stdout/stderr)", "option_dry_run": "Only outputs the unit files without writing them", + // Messages "unit_written_service": "Service unit written to: {path}", "unit_written_timer": "Timer unit written to: {path}", "hint_header": "\nℹ️ Note:", + // Error messages "error_write_units": "Error while writing unit files:", "rollback_failed": "Rollback failed:" } diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 7cce973..4e19c9f 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -1,3 +1,5 @@ +import { parse as parseJsonc } from '@std/jsonc'; + /** * Initializes the i18n module by loading * the appropriate locale file based on the system language. @@ -15,29 +17,35 @@ let translations: Record = {}; /** * 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. * * @param locale - The language code (e.g., 'de', 'en') to load * @returns Promise that resolves once the translations have been loaded */ export async function loadLocale(locale: string): Promise { - try { - const localeUrl = new URL(`./${locale}.json`, import.meta.url); - const file = await Deno.readTextFile(localeUrl); - translations = JSON.parse(file); - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - console.warn( - `Locale '${locale}' not found – falling back to 'en'.`, - ); - if (locale !== 'en') { - await loadLocale('en'); + const extensions = ['jsonc', 'json']; + for (const ext of extensions) { + try { + const localeUrl = new URL(`./${locale}.${ext}`, import.meta.url); + const raw = await Deno.readTextFile(localeUrl); + // parseJsonc tolerates both pure JSON and JSONC, so we can use it for either. + translations = parseJsonc(raw) as Record; + return; + } catch (err) { + if (err instanceof Deno.errors.NotFound) { + // Continue with next extension. + continue; } - } else { - console.error('Error loading translation file:', err); + console.error(`Error parsing locale '${locale}.${ext}':`, err); + break; } } + if (locale !== 'en') { + console.warn(`Locale '${locale}' not found – falling back to 'en'.`); + await loadLocale('en'); + } } /**