feat(cli): suporte i18n completo — 42 locales, --lang flag, config lang get/set/list (#2285)

feat(cli): suporte i18n completo — 42 locales, --lang flag, config lang get/set/list

- 42 locale files in bin/cli/locales/ (en + pt-BR fully translated, 29 with common/program, 11 scaffolds)
- --lang <code> global flag for per-execution override
- config lang get/set/list subcommands
- Locale persistence via ~/.omniroute/.env
- Path traversal protection via regex validation in normalize()
- Script generate-locales.mjs for scaffolding new locales
- Unit tests for lang commands + normalization security

Integrated into release/v3.8.0
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-05-15 13:14:14 -03:00
committed by GitHub
parent 0d09858526
commit 79d03575ee
51 changed files with 2344 additions and 17 deletions

View File

@@ -135,11 +135,25 @@ export const RETRY_DEFAULTS = {
## 6. Internationalization
- Every user-facing string goes through `t("module.key", vars)`.
- Catalogs live in `bin/cli/locales/{en,pt-BR}.json` (nested objects).
- Detection: `OMNIROUTE_LANG` overrides, otherwise `LC_ALL`, `LC_MESSAGES`,
`LANG`. Fallback: `en`.
- Missing keys return the key itself (no crash). PRs that add new strings
must update both `en` and `pt-BR` catalogs.
- Catalogs live in `bin/cli/locales/{locale}.json` (nested objects).
42 files ship out-of-the-box: `en`, `pt-BR`, and 40 additional locales.
11 locales are scaffold-only (empty `{}`); all keys fall back to `en` automatically.
- Detection order: `--lang` flag → `OMNIROUTE_LANG` env → `LC_ALL``LC_MESSAGES``LANG``en`.
- Locale persisted via `config lang set <code>` — saves `OMNIROUTE_LANG` to `~/.omniroute/.env`.
- Missing keys return the key itself (no crash).
- PRs that add new strings **must** update `en.json` and `pt-BR.json`.
Other locale files are best-effort; missing keys silently fall back to `en`.
- `normalize()` in `i18n.mjs` validates locale codes via `/^[a-zA-Z0-9-]+$/` to
prevent path traversal — never pass raw filesystem paths.
- Canonical locale list: `config/i18n.json` — source of truth used by both CLI and
dashboard i18n pipelines.
### Adding a new locale file
1. Add entry to `config/i18n.json` with `code`, `english`, `native`, `flag`.
2. Run `node bin/cli/scripts/generate-locales.mjs` — creates `bin/cli/locales/{code}.json`.
3. Fill in translations (or leave as `{}` for en-fallback scaffold).
4. The pre-commit hook `check-cli-i18n` will verify all `t()` keys exist in `en.json`.
## 7. Logs / output channels

View File

@@ -8,8 +8,7 @@ This directory contains the CLI runtime, helpers, and commands for the `omnirout
bin/cli/
├── CONVENTIONS.md ← normative design rules (read this first)
├── README.md ← this file
├── index.mjs ← central command router (will migrate to Commander in 1.1)
├── args.mjs ← legacy arg parser (replaced by Commander in 1.1)
├── program.mjs ← Commander setup — global flags, registerCommands()
├── api.mjs ← apiFetch() — all HTTP calls + retry/backoff
├── runtime.mjs ← withRuntime() — server-first / DB-fallback
├── i18n.mjs ← t() — i18n helper + locale detection
@@ -23,13 +22,16 @@ bin/cli/
├── provider-test.mjs ← testProviderApiKey()
├── settings-store.mjs ← DB CRUD for key_value settings
├── locales/
│ ├── en.json ← English strings
── pt-BR.json ← Portuguese (Brazil) strings
│ ├── en.json ← English strings (source of truth, 42+ locales)
── pt-BR.json ← Portuguese (Brazil) — fully translated
│ └── {locale}.json ← 40 additional locales (ar, az, de, es, fr, ja, zh-CN, …)
├── scripts/
│ └── generate-locales.mjs ← scaffold new locale files from config/i18n.json
└── commands/
├── setup.mjs
├── doctor.mjs
├── providers.mjs
├── config.mjs
├── config.mjs ← includes `config lang get/set/list`
├── status.mjs
├── logs.mjs
└── update.mjs
@@ -103,12 +105,42 @@ printError("Something went wrong");
process.exit(EXIT_CODES.SERVER_OFFLINE);
```
## Locale selection
The CLI displays text in the user's language. Detection order:
1. `--lang <code>` flag on the command line
2. `OMNIROUTE_LANG` environment variable
3. System env: `LC_ALL``LC_MESSAGES``LANG`
4. Fallback: `en`
**Set permanently:**
```bash
omniroute config lang set pt-BR # saves to ~/.omniroute/.env
omniroute config lang list # show all 42 available locales
omniroute config lang get # show currently active locale
```
**One-time override:**
```bash
omniroute --lang de providers list # run in German, not persisted
OMNIROUTE_LANG=ja omniroute status # same effect via env
```
**Adding a new locale**: add entry to `config/i18n.json`, then run:
```bash
node bin/cli/scripts/generate-locales.mjs
```
## Adding a new command
1. Create `bin/cli/commands/your-command.mjs`
2. Export `runYourCommand(argv, context)` (pre-1.1) or `registerYourCommand(program)` (post-1.1)
3. Register in `bin/cli/index.mjs` (pre-1.1) or `bin/cli/program.mjs` (post-1.1)
4. Add strings to both `locales/en.json` and `locales/pt-BR.json`
2. Export `registerYourCommand(program)` following the Commander pattern
3. Register in `bin/cli/commands/registry.mjs`
4. Add strings to `locales/en.json` and `locales/pt-BR.json`
5. Write test in `tests/unit/cli-your-command.test.ts`
See `CONVENTIONS.md` for exit codes, flag naming, output format, and destructive-action rules.

View File

@@ -2,6 +2,8 @@ import { printHeading, printInfo, printSuccess, printError } from "../io.mjs";
import { t } from "../i18n.mjs";
import path from "node:path";
import fs from "node:fs";
import { fileURLToPath } from "node:url";
import { resolveDataDir } from "../data-dir.mjs";
import { registerContexts } from "./contexts.mjs";
function ensureBackup(configPath) {
@@ -140,6 +142,106 @@ async function runConfigValidateCommand(toolId, opts = {}) {
return 0;
}
function loadI18nLocales() {
const cfgPath = path.join(
path.dirname(path.dirname(path.dirname(path.dirname(fileURLToPath(import.meta.url))))),
"config",
"i18n.json"
);
try {
return JSON.parse(fs.readFileSync(cfgPath, "utf8")).locales || [];
} catch {
return [];
}
}
function getCliEnvPath() {
return path.join(resolveDataDir(), ".env");
}
function upsertEnvLine(envPath, key, value) {
let content = "";
if (fs.existsSync(envPath)) content = fs.readFileSync(envPath, "utf8");
const lines = content.split("\n");
const idx = lines.findIndex((l) => l.trimStart().startsWith(`${key}=`));
const newLine = `${key}=${value}`;
if (idx >= 0) {
lines[idx] = newLine;
} else {
if (content && !content.endsWith("\n")) lines.push("");
lines.push(newLine);
}
const dir = path.dirname(envPath);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
const tmp = `${envPath}.tmp`;
fs.writeFileSync(tmp, lines.join("\n"), "utf8");
fs.renameSync(tmp, envPath);
}
export async function runConfigLangGetCommand(opts = {}) {
const { getLocale } = await import("../i18n.mjs");
const code = getLocale();
const locales = loadI18nLocales();
const entry = locales.find((l) => l.code === code);
const name = entry ? entry.english : code;
if (opts.output === "json" || opts.json) {
console.log(JSON.stringify({ code, name }, null, 2));
} else {
console.log(t("config.lang.current", { code, name }));
}
return 0;
}
export async function runConfigLangSetCommand(code, opts = {}) {
if (!code) {
console.error(t("config.lang.noCode"));
return 1;
}
const locales = loadI18nLocales();
const entry = locales.find((l) => l.code === code);
if (!entry) {
console.error(t("config.lang.unknown", { code }));
return 1;
}
const { getLocale, setLocale } = await import("../i18n.mjs");
const current = getLocale();
if (current === code && !opts.force) {
console.log(t("config.lang.alreadySet", { code }));
return 0;
}
const envPath = getCliEnvPath();
upsertEnvLine(envPath, "OMNIROUTE_LANG", code);
setLocale(code);
console.log(t("config.lang.saved", { code, name: entry.english }));
console.log(t("config.lang.envHint", { code }));
return 0;
}
export async function runConfigLangListCommand(opts = {}) {
const { getLocale } = await import("../i18n.mjs");
const current = getLocale();
const locales = loadI18nLocales();
if (opts.output === "json" || opts.json) {
console.log(
JSON.stringify(
locales.map((l) => ({ ...l, active: l.code === current })),
null,
2
)
);
return 0;
}
console.log(`\n\x1b[1m\x1b[36m${t("config.lang.listTitle")}\x1b[0m\n`);
for (const loc of locales) {
const active = loc.code === current ? " \x1b[32m◀ active\x1b[0m" : "";
console.log(
` ${loc.flag} ${loc.code.padEnd(8)} ${loc.english.padEnd(28)} ${loc.native}${active}`
);
}
console.log("");
return 0;
}
export function registerConfig(program) {
const config = program.command("config").description("Show or update CLI tool configuration");
@@ -190,6 +292,35 @@ export function registerConfig(program) {
if (exitCode !== 0) process.exit(exitCode);
});
// lang subgroup
const lang = config.command("lang").description(t("config.lang.description"));
lang
.command("get")
.description(t("config.lang.getDescription"))
.option("--json", t("common.jsonOpt"))
.action(async (opts, cmd) => {
const globalOpts = cmd.parent.parent.optsWithGlobals();
const exitCode = await runConfigLangGetCommand({ ...opts, output: globalOpts.output });
if (exitCode !== 0) process.exit(exitCode);
});
lang
.command("set <code>")
.description(t("config.lang.setDescription"))
.option("--force", "Set even if already active")
.action(async (code, opts, cmd) => {
const exitCode = await runConfigLangSetCommand(code, opts);
if (exitCode !== 0) process.exit(exitCode);
});
lang
.command("list")
.description(t("config.lang.listDescription"))
.option("--json", t("common.jsonOpt"))
.action(async (opts, cmd) => {
const globalOpts = cmd.parent.parent.optsWithGlobals();
const exitCode = await runConfigLangListCommand({ ...opts, output: globalOpts.output });
if (exitCode !== 0) process.exit(exitCode);
});
// Register contexts/profiles CRUD as a subgroup of config.
registerContexts(config);
}

View File

@@ -21,8 +21,8 @@ export function detectLocale() {
}
function normalize(raw) {
const stripped = String(raw).split(".")[0].replace("_", "-");
if (!stripped) return FALLBACK_LOCALE;
const stripped = String(raw).split(".")[0].replaceAll("_", "-");
if (!stripped || !/^[a-zA-Z0-9-]+$/.test(stripped)) return FALLBACK_LOCALE;
if (hasCatalog(stripped)) return stripped;
const base = stripped.split("-")[0];
if (hasCatalog(base)) return base;

29
bin/cli/locales/ar.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "خطأ: {message}",
"serverOffline": "خادم OmniRoute غير متصل. ابدأ بالأمر: omniroute serve",
"authRequired": "المصادقة مطلوبة. عيّن OMNIROUTE_API_KEY أو شغّل: omniroute setup",
"rateLimited": "تم تجاوز حد الطلبات. أعد المحاولة بعد {seconds} ثانية.",
"timeout": "انتهت مهلة الطلب بعد {ms}ms.",
"success": "تم.",
"yes": "نعم",
"no": "لا",
"confirm": "هل أنت متأكد؟ (نعم/لا)",
"dryRun": "[محاكاة] سيتم: {action}",
"cancelled": "تم الإلغاء.",
"jsonOpt": "إخراج بتنسيق JSON",
"yesOpt": "تخطي رسالة التأكيد"
},
"program": {
"description": "OmniRoute — جهاز توجيه الذكاء الاصطناعي مع التبديل التلقائي",
"version": "عرض الإصدار والخروج",
"output": "تنسيق الإخراج (table, json, jsonl, csv)",
"quiet": "إخفاء المخرجات غير الأساسية",
"no_color": "تعطيل الإخراج الملوّن",
"timeout": "مهلة طلب HTTP بالميلي ثانية",
"api_key": "مفتاح API لخادم OmniRoute",
"base_url": "عنوان URL الأساسي لخادم OmniRoute",
"context": "سياق/ملف تعريف الخادم المستخدم في هذا الأمر",
"lang": "تعيين لغة عرض CLI (يتجاوز OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/az.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Xəta: {message}",
"serverOffline": "OmniRoute serveri oflayndır. Başladın: omniroute serve",
"authRequired": "Autentifikasiya tələb olunur. OMNIROUTE_API_KEY təyin edin və ya işə salın: omniroute setup",
"rateLimited": "Sorğu limiti aşıldı. {seconds} saniyə sonra yenidən cəhd edin.",
"timeout": "Sorğunun vaxtı {ms}ms sonra bitdi.",
"success": "Tamamlandı.",
"yes": "bəli",
"no": "xeyr",
"confirm": "Əminsiniz? (bəli/xeyr)",
"dryRun": "[simulyasiya] ediləcəkdi: {action}",
"cancelled": "Ləğv edildi.",
"jsonOpt": "JSON formatında çıxış",
"yesOpt": "Təsdiq sorğusunu keç"
},
"program": {
"description": "OmniRoute — Avtomatik Fallback ilə Ağıllı AI Marşrutlaşdırıcısı",
"version": "Versiyasını çap et və çıx",
"output": ıxış formatı (table, json, jsonl, csv)",
"quiet": "Vacib olmayan çıxışı gizlət",
"no_color": "Rəngli çıxışı deaktiv et",
"timeout": "HTTP sorğusu üçün zaman aşımı (millisaniyə)",
"api_key": "OmniRoute serveri üçün API açarı",
"base_url": "OmniRoute server baza URL-i",
"context": "Bu əmr üçün server konteksti/profili",
"lang": "CLI ekran dilini təyin edin (OMNIROUTE_LANG-ı keçir)"
}
}

29
bin/cli/locales/bg.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Грешка: {message}",
"serverOffline": "Сървърът OmniRoute е офлайн. Стартирайте с: omniroute serve",
"authRequired": "Необходима е автентикация. Задайте OMNIROUTE_API_KEY или изпълнете: omniroute setup",
"rateLimited": "Превишен лимит на заявки. Опитайте след {seconds}с.",
"timeout": "Заявката изтече след {ms}ms.",
"success": "Готово.",
"yes": "да",
"no": "не",
"confirm": "Сигурни ли сте? (да/не)",
"dryRun": "[симулация] ще извърши: {action}",
"cancelled": "Отменено.",
"jsonOpt": "Изход като JSON",
"yesOpt": "Пропускане на потвърждение"
},
"program": {
"description": "OmniRoute — Интелигентен AI рутер с автоматично превключване",
"version": "Покажи версията и излез",
"output": "Формат на изхода (table, json, jsonl, csv)",
"quiet": "Потисни несъществена информация",
"no_color": "Деактивирай цветния изход",
"timeout": "Таймаут за HTTP заявки в милисекунди",
"api_key": "API ключ за сървъра OmniRoute",
"base_url": "Базов URL на сървъра OmniRoute",
"context": "Контекст/профил на сървъра за тази команда",
"lang": "Задай език на CLI (замества OMNIROUTE_LANG)"
}
}

1
bin/cli/locales/bn.json Normal file
View File

@@ -0,0 +1 @@
{}

29
bin/cli/locales/cs.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Chyba: {message}",
"serverOffline": "Server OmniRoute je offline. Spusťte: omniroute serve",
"authRequired": "Vyžaduje se ověření. Nastavte OMNIROUTE_API_KEY nebo spusťte: omniroute setup",
"rateLimited": "Překročen limit požadavků. Zkuste za {seconds}s.",
"timeout": "Požadavek vypršel po {ms}ms.",
"success": "Hotovo.",
"yes": "ano",
"no": "ne",
"confirm": "Jste si jisti? (ano/ne)",
"dryRun": "[simulace] by provedlo: {action}",
"cancelled": "Zrušeno.",
"jsonOpt": "Výstup jako JSON",
"yesOpt": "Přeskočit potvrzení"
},
"program": {
"description": "OmniRoute — Chytrý AI router s automatickým přepínáním",
"version": "Vypsat verzi a skončit",
"output": "Formát výstupu (table, json, jsonl, csv)",
"quiet": "Potlačit nepodstatný výstup",
"no_color": "Zakázat barevný výstup",
"timeout": "Časový limit HTTP požadavků v milisekundách",
"api_key": "API klíč pro server OmniRoute",
"base_url": "Základní URL serveru OmniRoute",
"context": "Kontext/profil serveru pro tento příkaz",
"lang": "Nastavit jazyk CLI (přepisuje OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/da.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Fejl: {message}",
"serverOffline": "OmniRoute-serveren er offline. Start med: omniroute serve",
"authRequired": "Godkendelse kræves. Sæt OMNIROUTE_API_KEY eller kør: omniroute setup",
"rateLimited": "Anmodningsgrænse overskredet. Prøv igen om {seconds}s.",
"timeout": "Anmodningen timed ud efter {ms}ms.",
"success": "Færdig.",
"yes": "ja",
"no": "nej",
"confirm": "Er du sikker? (ja/nej)",
"dryRun": "[simulering] ville: {action}",
"cancelled": "Annulleret.",
"jsonOpt": "Output som JSON",
"yesOpt": "Spring bekræftelse over"
},
"program": {
"description": "OmniRoute — Smart AI-router med automatisk fallback",
"version": "Vis version og afslut",
"output": "Outputformat (table, json, jsonl, csv)",
"quiet": "Undertryk ikke-essentielt output",
"no_color": "Deaktiver farvet output",
"timeout": "HTTP-anmodnings timeout i millisekunder",
"api_key": "API-nøgle til OmniRoute-serveren",
"base_url": "OmniRoute-serverens basis-URL",
"context": "Server-kontekst/profil til denne kommando",
"lang": "Angiv CLI-visningssprog (tilsidesætter OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/de.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Fehler: {message}",
"serverOffline": "OmniRoute-Server ist offline. Starten mit: omniroute serve",
"authRequired": "Authentifizierung erforderlich. OMNIROUTE_API_KEY setzen oder ausführen: omniroute setup",
"rateLimited": "Anfragelimit überschritten. Erneut versuchen in {seconds}s.",
"timeout": "Anfrage-Timeout nach {ms}ms.",
"success": "Fertig.",
"yes": "ja",
"no": "nein",
"confirm": "Sind Sie sicher? (ja/nein)",
"dryRun": "[Simulation] würde: {action}",
"cancelled": "Abgebrochen.",
"jsonOpt": "Ausgabe als JSON",
"yesOpt": "Bestätigung überspringen"
},
"program": {
"description": "OmniRoute — Intelligenter AI-Router mit automatischem Fallback",
"version": "Version ausgeben und beenden",
"output": "Ausgabeformat (table, json, jsonl, csv)",
"quiet": "Unwesentliche Ausgabe unterdrücken",
"no_color": "Farbige Ausgabe deaktivieren",
"timeout": "HTTP-Anfrage-Timeout in Millisekunden",
"api_key": "API-Schlüssel für den OmniRoute-Server",
"base_url": "OmniRoute-Server-Basis-URL",
"context": "Server-Kontext/Profil für diesen Befehl",
"lang": "CLI-Anzeigesprache festlegen (überschreibt OMNIROUTE_LANG)"
}
}

View File

@@ -776,7 +776,8 @@
"timeout": "HTTP request timeout in milliseconds",
"api_key": "API key for OmniRoute server",
"base_url": "OmniRoute server base URL",
"context": "Server context/profile to use for this command"
"context": "Server context/profile to use for this command",
"lang": "Set CLI display language (overrides OMNIROUTE_LANG)"
},
"files": {
"description": "Manage files (upload, list, get, download, delete)",
@@ -1154,6 +1155,19 @@
"config": {
"contexts": {
"description": "Manage server contexts/profiles (add, use, list, show, remove, rename, export, import)"
},
"lang": {
"description": "Manage CLI display language",
"getDescription": "Show the currently active language code",
"setDescription": "Set the display language and save it to config",
"listDescription": "List all available languages",
"listTitle": "Available Languages",
"current": "Language: {code} ({name})",
"saved": "Language set to {code} ({name}).",
"noCode": "Language code required. Run 'omniroute config lang list' to see available codes.",
"unknown": "Unknown language code: {code}. Run 'omniroute config lang list' to see available codes.",
"alreadySet": "Language is already set to {code}.",
"envHint": "Tip: you can also set OMNIROUTE_LANG={code} in your environment."
}
},
"completion": {

29
bin/cli/locales/es.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Error: {message}",
"serverOffline": "El servidor OmniRoute está offline. Inícielo con: omniroute serve",
"authRequired": "Autenticación requerida. Configure OMNIROUTE_API_KEY o ejecute: omniroute setup",
"rateLimited": "Límite de solicitudes excedido. Reintente en {seconds}s.",
"timeout": "Solicitud expiró después de {ms}ms.",
"success": "Listo.",
"yes": "sí",
"no": "no",
"confirm": "¿Está seguro? (sí/no)",
"dryRun": "[simulación] haría: {action}",
"cancelled": "Cancelado.",
"jsonOpt": "Salida como JSON",
"yesOpt": "Omitir confirmación"
},
"program": {
"description": "OmniRoute — Router de IA inteligente con fallback automático",
"version": "Mostrar versión y salir",
"output": "Formato de salida (table, json, jsonl, csv)",
"quiet": "Suprimir salida no esencial",
"no_color": "Deshabilitar salida en color",
"timeout": "Tiempo de espera de solicitudes HTTP en milisegundos",
"api_key": "Clave de API para el servidor OmniRoute",
"base_url": "URL base del servidor OmniRoute",
"context": "Contexto/perfil del servidor para este comando",
"lang": "Establecer idioma del CLI (reemplaza OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/fa.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "خطا: {message}",
"serverOffline": "سرور OmniRoute آفلاین است. با این دستور راه‌اندازی کنید: omniroute serve",
"authRequired": "احراز هویت لازم است. OMNIROUTE_API_KEY را تنظیم کنید یا اجرا کنید: omniroute setup",
"rateLimited": "محدودیت درخواست رسیده است. پس از {seconds} ثانیه دوباره تلاش کنید.",
"timeout": "درخواست پس از {ms}ms منقضی شد.",
"success": "انجام شد.",
"yes": "بله",
"no": "خیر",
"confirm": "مطمئنید؟ (بله/خیر)",
"dryRun": "[شبیه‌سازی] اقدام می‌شد: {action}",
"cancelled": "لغو شد.",
"jsonOpt": "خروجی به صورت JSON",
"yesOpt": "رد کردن تأیید"
},
"program": {
"description": "OmniRoute — روتر هوشمند هوش مصنوعی با fallback خودکار",
"version": "نمایش نسخه و خروج",
"output": "فرمت خروجی (table, json, jsonl, csv)",
"quiet": "حذف خروجی غیر ضروری",
"no_color": "غیرفعال کردن خروجی رنگی",
"timeout": "تایم‌اوت درخواست HTTP به میلی‌ثانیه",
"api_key": "کلید API برای سرور OmniRoute",
"base_url": "URL پایه سرور OmniRoute",
"context": "زمینه/پروفایل سرور برای این دستور",
"lang": "تنظیم زبان نمایش CLI (OMNIROUTE_LANG را نادیده می‌گیرد)"
}
}

29
bin/cli/locales/fi.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Virhe: {message}",
"serverOffline": "OmniRoute-palvelin on offline. Käynnistä komennolla: omniroute serve",
"authRequired": "Todennus vaaditaan. Aseta OMNIROUTE_API_KEY tai suorita: omniroute setup",
"rateLimited": "Pyyntöraja ylitetty. Yritä uudelleen {seconds}s kuluttua.",
"timeout": "Pyyntö aikakatkaistiin {ms}ms jälkeen.",
"success": "Valmis.",
"yes": "kyllä",
"no": "ei",
"confirm": "Oletko varma? (kyllä/ei)",
"dryRun": "[simulointi] tekisi: {action}",
"cancelled": "Peruutettu.",
"jsonOpt": "Tulosta JSON-muodossa",
"yesOpt": "Ohita vahvistuspyyntö"
},
"program": {
"description": "OmniRoute — Älykäs AI-reititin automaattisella fallbackilla",
"version": "Tulosta versio ja poistu",
"output": "Tulostusmuoto (table, json, jsonl, csv)",
"quiet": "Piilota epäolennaiset tulosteet",
"no_color": "Poista väritulostus käytöstä",
"timeout": "HTTP-pyyntöjen aikakatkaisu millisekunteina",
"api_key": "API-avain OmniRoute-palvelimelle",
"base_url": "OmniRoute-palvelimen perus-URL",
"context": "Palvelimen konteksti/profiili tälle komennolle",
"lang": "Aseta CLI-näyttökieli (ohittaa OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/fr.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Erreur : {message}",
"serverOffline": "Le serveur OmniRoute est hors ligne. Démarrez avec : omniroute serve",
"authRequired": "Authentification requise. Définissez OMNIROUTE_API_KEY ou exécutez : omniroute setup",
"rateLimited": "Limite de requêtes atteinte. Réessayez dans {seconds}s.",
"timeout": "La requête a expiré après {ms}ms.",
"success": "Terminé.",
"yes": "oui",
"no": "non",
"confirm": "Êtes-vous sûr ? (oui/non)",
"dryRun": "[simulation] ferait : {action}",
"cancelled": "Annulé.",
"jsonOpt": "Sortie au format JSON",
"yesOpt": "Ignorer la confirmation"
},
"program": {
"description": "OmniRoute — Routeur IA intelligent avec basculement automatique",
"version": "Afficher la version et quitter",
"output": "Format de sortie (table, json, jsonl, csv)",
"quiet": "Supprimer les sorties non essentielles",
"no_color": "Désactiver la sortie en couleur",
"timeout": "Délai d'expiration des requêtes HTTP en millisecondes",
"api_key": "Clé API pour le serveur OmniRoute",
"base_url": "URL de base du serveur OmniRoute",
"context": "Contexte/profil du serveur pour cette commande",
"lang": "Définir la langue d'affichage du CLI (remplace OMNIROUTE_LANG)"
}
}

1
bin/cli/locales/gu.json Normal file
View File

@@ -0,0 +1 @@
{}

1
bin/cli/locales/he.json Normal file
View File

@@ -0,0 +1 @@
{}

29
bin/cli/locales/hi.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "त्रुटि: {message}",
"serverOffline": "OmniRoute सर्वर ऑफलाइन है। शुरू करें: omniroute serve",
"authRequired": "प्रमाणीकरण आवश्यक है। OMNIROUTE_API_KEY सेट करें या चलाएं: omniroute setup",
"rateLimited": "अनुरोध सीमा पार हो गई। {seconds}s बाद पुनः प्रयास करें।",
"timeout": "{ms}ms के बाद अनुरोध समय समाप्त हुआ।",
"success": "पूर्ण।",
"yes": "हाँ",
"no": "नहीं",
"confirm": "क्या आप सुनिश्चित हैं? (हाँ/नहीं)",
"dryRun": "[अनुकरण] करेगा: {action}",
"cancelled": "रद्द किया गया।",
"jsonOpt": "JSON के रूप में आउटपुट",
"yesOpt": "पुष्टि छोड़ें"
},
"program": {
"description": "OmniRoute — ऑटो फॉलबैक के साथ स्मार्ट AI राउटर",
"version": "संस्करण प्रिंट करें और बाहर निकलें",
"output": "आउटपुट प्रारूप (table, json, jsonl, csv)",
"quiet": "गैर-आवश्यक आउटपुट दबाएं",
"no_color": "रंगीन आउटपुट अक्षम करें",
"timeout": "HTTP अनुरोध टाइमआउट मिलीसेकंड में",
"api_key": "OmniRoute सर्वर के लिए API कुंजी",
"base_url": "OmniRoute सर्वर का बेस URL",
"context": "इस कमांड के लिए सर्वर संदर्भ/प्रोफ़ाइल",
"lang": "CLI प्रदर्शन भाषा सेट करें (OMNIROUTE_LANG को ओवरराइड करता है)"
}
}

29
bin/cli/locales/hu.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Hiba: {message}",
"serverOffline": "Az OmniRoute szerver offline. Indítsa el: omniroute serve",
"authRequired": "Hitelesítés szükséges. Állítsa be az OMNIROUTE_API_KEY-t vagy futtassa: omniroute setup",
"rateLimited": "Kérési korlát túllépve. Próbálja újra {seconds}s múlva.",
"timeout": "A kérés {ms}ms után lejárt.",
"success": "Kész.",
"yes": "igen",
"no": "nem",
"confirm": "Biztos benne? (igen/nem)",
"dryRun": "[szimuláció] végrehajtaná: {action}",
"cancelled": "Törölve.",
"jsonOpt": "JSON formátumú kimenet",
"yesOpt": "Megerősítés kihagyása"
},
"program": {
"description": "OmniRoute — Intelligens AI útválasztó automatikus fallbackkel",
"version": "Verzió kiírása és kilépés",
"output": "Kimeneti formátum (table, json, jsonl, csv)",
"quiet": "Nem lényeges kimenet elnyomása",
"no_color": "Színes kimenet letiltása",
"timeout": "HTTP kérés időtúllépése ezredmásodpercben",
"api_key": "API kulcs az OmniRoute szerverhez",
"base_url": "Az OmniRoute szerver alap URL-je",
"context": "Szerverkontextus/profil ehhez a parancshoz",
"lang": "CLI megjelenítési nyelv beállítása (felülírja az OMNIROUTE_LANG-ot)"
}
}

29
bin/cli/locales/id.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Kesalahan: {message}",
"serverOffline": "Server OmniRoute sedang offline. Mulai dengan: omniroute serve",
"authRequired": "Autentikasi diperlukan. Setel OMNIROUTE_API_KEY atau jalankan: omniroute setup",
"rateLimited": "Batas permintaan terlampaui. Coba lagi dalam {seconds}d.",
"timeout": "Permintaan habis waktu setelah {ms}ms.",
"success": "Selesai.",
"yes": "ya",
"no": "tidak",
"confirm": "Apakah Anda yakin? (ya/tidak)",
"dryRun": "[simulasi] akan: {action}",
"cancelled": "Dibatalkan.",
"jsonOpt": "Keluaran sebagai JSON",
"yesOpt": "Lewati konfirmasi"
},
"program": {
"description": "OmniRoute — Router AI Cerdas dengan Fallback Otomatis",
"version": "Cetak versi dan keluar",
"output": "Format keluaran (table, json, jsonl, csv)",
"quiet": "Sembunyikan output yang tidak penting",
"no_color": "Nonaktifkan output berwarna",
"timeout": "Batas waktu permintaan HTTP dalam milidetik",
"api_key": "Kunci API untuk server OmniRoute",
"base_url": "URL dasar server OmniRoute",
"context": "Konteks/profil server untuk perintah ini",
"lang": "Atur bahasa tampilan CLI (menggantikan OMNIROUTE_LANG)"
}
}

1
bin/cli/locales/in.json Normal file
View File

@@ -0,0 +1 @@
{}

29
bin/cli/locales/it.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Errore: {message}",
"serverOffline": "Il server OmniRoute è offline. Avviarlo con: omniroute serve",
"authRequired": "Autenticazione richiesta. Impostare OMNIROUTE_API_KEY o eseguire: omniroute setup",
"rateLimited": "Limite di richieste superato. Riprovare tra {seconds}s.",
"timeout": "La richiesta è scaduta dopo {ms}ms.",
"success": "Completato.",
"yes": "sì",
"no": "no",
"confirm": "Sei sicuro? (sì/no)",
"dryRun": "[simulazione] eseguirebbe: {action}",
"cancelled": "Annullato.",
"jsonOpt": "Output come JSON",
"yesOpt": "Salta la conferma"
},
"program": {
"description": "OmniRoute — Router AI intelligente con fallback automatico",
"version": "Stampa la versione ed esci",
"output": "Formato di output (table, json, jsonl, csv)",
"quiet": "Sopprimi l'output non essenziale",
"no_color": "Disabilita l'output colorato",
"timeout": "Timeout delle richieste HTTP in millisecondi",
"api_key": "Chiave API per il server OmniRoute",
"base_url": "URL base del server OmniRoute",
"context": "Contesto/profilo del server per questo comando",
"lang": "Imposta la lingua di visualizzazione della CLI (sovrascrive OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/ja.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "エラー: {message}",
"serverOffline": "OmniRouteサーバーはオフラインです。起動: omniroute serve",
"authRequired": "認証が必要です。OMNIROUTE_API_KEYを設定するか実行してください: omniroute setup",
"rateLimited": "リクエスト制限を超えました。{seconds}秒後に再試行してください。",
"timeout": "{ms}ms後にリクエストがタイムアウトしました。",
"success": "完了。",
"yes": "はい",
"no": "いいえ",
"confirm": "よろしいですか?(はい/いいえ)",
"dryRun": "[シミュレーション] 実行予定: {action}",
"cancelled": "キャンセルしました。",
"jsonOpt": "JSON形式で出力",
"yesOpt": "確認をスキップ"
},
"program": {
"description": "OmniRoute — 自動フォールバック付きスマートAIルーター",
"version": "バージョンを表示して終了",
"output": "出力形式 (table, json, jsonl, csv)",
"quiet": "重要でない出力を抑制",
"no_color": "カラー出力を無効化",
"timeout": "HTTPリクエストタイムアウトミリ秒",
"api_key": "OmniRouteサーバーのAPIキー",
"base_url": "OmniRouteサーバーのベースURL",
"context": "このコマンドで使用するサーバーコンテキスト/プロファイル",
"lang": "CLI表示言語を設定OMNIROUTE_LANGを上書き"
}
}

29
bin/cli/locales/ko.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "오류: {message}",
"serverOffline": "OmniRoute 서버가 오프라인입니다. 시작: omniroute serve",
"authRequired": "인증이 필요합니다. OMNIROUTE_API_KEY를 설정하거나 실행하세요: omniroute setup",
"rateLimited": "요청 제한 초과. {seconds}초 후 다시 시도하세요.",
"timeout": "{ms}ms 후 요청 시간 초과.",
"success": "완료.",
"yes": "예",
"no": "아니오",
"confirm": "확실합니까? (예/아니오)",
"dryRun": "[시뮬레이션] 실행 예정: {action}",
"cancelled": "취소되었습니다.",
"jsonOpt": "JSON으로 출력",
"yesOpt": "확인 건너뛰기"
},
"program": {
"description": "OmniRoute — 자동 폴백 기능을 갖춘 스마트 AI 라우터",
"version": "버전 출력 후 종료",
"output": "출력 형식 (table, json, jsonl, csv)",
"quiet": "불필요한 출력 억제",
"no_color": "색상 출력 비활성화",
"timeout": "HTTP 요청 타임아웃(밀리초)",
"api_key": "OmniRoute 서버의 API 키",
"base_url": "OmniRoute 서버 기본 URL",
"context": "이 명령에 사용할 서버 컨텍스트/프로필",
"lang": "CLI 표시 언어 설정 (OMNIROUTE_LANG 재정의)"
}
}

1
bin/cli/locales/mr.json Normal file
View File

@@ -0,0 +1 @@
{}

1
bin/cli/locales/ms.json Normal file
View File

@@ -0,0 +1 @@
{}

29
bin/cli/locales/nl.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Fout: {message}",
"serverOffline": "OmniRoute-server is offline. Start met: omniroute serve",
"authRequired": "Authenticatie vereist. Stel OMNIROUTE_API_KEY in of voer uit: omniroute setup",
"rateLimited": "Verzoeklimiet overschreden. Probeer opnieuw na {seconds}s.",
"timeout": "Verzoek verlopen na {ms}ms.",
"success": "Klaar.",
"yes": "ja",
"no": "nee",
"confirm": "Weet u het zeker? (ja/nee)",
"dryRun": "[simulatie] zou: {action}",
"cancelled": "Geannuleerd.",
"jsonOpt": "Uitvoer als JSON",
"yesOpt": "Bevestiging overslaan"
},
"program": {
"description": "OmniRoute — Slimme AI-router met automatische fallback",
"version": "Versie afdrukken en afsluiten",
"output": "Uitvoerformaat (table, json, jsonl, csv)",
"quiet": "Niet-essentiële uitvoer onderdrukken",
"no_color": "Gekleurde uitvoer uitschakelen",
"timeout": "HTTP-verzoek timeout in milliseconden",
"api_key": "API-sleutel voor de OmniRoute-server",
"base_url": "Basis-URL van de OmniRoute-server",
"context": "Servercontext/profiel voor dit commando",
"lang": "CLI-weergavetaal instellen (overschrijft OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/no.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Feil: {message}",
"serverOffline": "OmniRoute-serveren er offline. Start med: omniroute serve",
"authRequired": "Autentisering kreves. Angi OMNIROUTE_API_KEY eller kjør: omniroute setup",
"rateLimited": "Forespørselgrense overskredet. Prøv igjen om {seconds}s.",
"timeout": "Forespørselen tidsavbrutt etter {ms}ms.",
"success": "Ferdig.",
"yes": "ja",
"no": "nei",
"confirm": "Er du sikker? (ja/nei)",
"dryRun": "[simulering] ville: {action}",
"cancelled": "Avbrutt.",
"jsonOpt": "Utdata som JSON",
"yesOpt": "Hopp over bekreftelse"
},
"program": {
"description": "OmniRoute — Smart AI-ruter med automatisk fallback",
"version": "Skriv ut versjon og avslutt",
"output": "Utdataformat (table, json, jsonl, csv)",
"quiet": "Undertrykk ikke-essensiell utdata",
"no_color": "Deaktiver farget utdata",
"timeout": "HTTP-forespørsel timeout i millisekunder",
"api_key": "API-nøkkel for OmniRoute-serveren",
"base_url": "OmniRoute-serverens basis-URL",
"context": "Serverkontekst/profil for denne kommandoen",
"lang": "Angi CLI-visningsspråk (overstyrer OMNIROUTE_LANG)"
}
}

1
bin/cli/locales/phi.json Normal file
View File

@@ -0,0 +1 @@
{}

29
bin/cli/locales/pl.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Błąd: {message}",
"serverOffline": "Serwer OmniRoute jest offline. Uruchom: omniroute serve",
"authRequired": "Wymagane uwierzytelnienie. Ustaw OMNIROUTE_API_KEY lub uruchom: omniroute setup",
"rateLimited": "Przekroczono limit żądań. Spróbuj ponownie za {seconds}s.",
"timeout": "Żądanie przekroczyło czas po {ms}ms.",
"success": "Gotowe.",
"yes": "tak",
"no": "nie",
"confirm": "Czy jesteś pewien? (tak/nie)",
"dryRun": "[symulacja] wykonałoby: {action}",
"cancelled": "Anulowano.",
"jsonOpt": "Wyjście jako JSON",
"yesOpt": "Pomiń potwierdzenie"
},
"program": {
"description": "OmniRoute — Inteligentny router AI z automatycznym fallbackiem",
"version": "Wydrukuj wersję i wyjdź",
"output": "Format wyjścia (table, json, jsonl, csv)",
"quiet": "Pomiń nieistotne wyjście",
"no_color": "Wyłącz kolorowe wyjście",
"timeout": "Limit czasu żądania HTTP w milisekundach",
"api_key": "Klucz API dla serwera OmniRoute",
"base_url": "Bazowy URL serwera OmniRoute",
"context": "Kontekst/profil serwera dla tego polecenia",
"lang": "Ustaw język wyświetlania CLI (nadpisuje OMNIROUTE_LANG)"
}
}

View File

@@ -776,7 +776,8 @@
"timeout": "Timeout de requisições HTTP em milissegundos",
"api_key": "Chave de API para o servidor OmniRoute",
"base_url": "URL base do servidor OmniRoute",
"context": "Contexto/perfil do servidor a usar neste comando"
"context": "Contexto/perfil do servidor a usar neste comando",
"lang": "Definir idioma de exibição do CLI (substitui OMNIROUTE_LANG)"
},
"files": {
"description": "Gerenciar arquivos (upload, listar, obter, baixar, deletar)",
@@ -1154,6 +1155,19 @@
"config": {
"contexts": {
"description": "Gerenciar contextos/perfis de servidor (add, use, list, show, remove, rename, export, import)"
},
"lang": {
"description": "Gerenciar idioma de exibição do CLI",
"getDescription": "Exibir o código de idioma atualmente ativo",
"setDescription": "Definir o idioma de exibição e salvar na configuração",
"listDescription": "Listar todos os idiomas disponíveis",
"listTitle": "Idiomas Disponíveis",
"current": "Idioma: {code} ({name})",
"saved": "Idioma definido como {code} ({name}).",
"noCode": "Código de idioma obrigatório. Execute 'omniroute config lang list' para ver os códigos disponíveis.",
"unknown": "Código de idioma desconhecido: {code}. Execute 'omniroute config lang list' para ver os códigos disponíveis.",
"alreadySet": "O idioma já está definido como {code}.",
"envHint": "Dica: você também pode definir OMNIROUTE_LANG={code} no seu ambiente."
}
},
"completion": {

29
bin/cli/locales/pt.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Erro: {message}",
"serverOffline": "O servidor OmniRoute está offline. Inicie com: omniroute serve",
"authRequired": "Autenticação necessária. Defina OMNIROUTE_API_KEY ou execute: omniroute setup",
"rateLimited": "Limite de pedidos atingido. Tente novamente em {seconds}s.",
"timeout": "O pedido expirou após {ms}ms.",
"success": "Concluído.",
"yes": "sim",
"no": "não",
"confirm": "Tem a certeza? (sim/não)",
"dryRun": "[simulação] faria: {action}",
"cancelled": "Cancelado.",
"jsonOpt": "Saída em JSON",
"yesOpt": "Ignorar confirmação"
},
"program": {
"description": "OmniRoute — Router de IA inteligente com fallback automático",
"version": "Mostrar versão e sair",
"output": "Formato de saída (table, json, jsonl, csv)",
"quiet": "Suprimir saída não essencial",
"no_color": "Desativar saída colorida",
"timeout": "Timeout de pedidos HTTP em milissegundos",
"api_key": "Chave de API para o servidor OmniRoute",
"base_url": "URL base do servidor OmniRoute",
"context": "Contexto/perfil do servidor para este comando",
"lang": "Definir idioma de apresentação do CLI (substitui OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/ro.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Eroare: {message}",
"serverOffline": "Serverul OmniRoute este offline. Porniți cu: omniroute serve",
"authRequired": "Autentificare necesară. Setați OMNIROUTE_API_KEY sau rulați: omniroute setup",
"rateLimited": "Limita de cereri depășită. Încercați din nou după {seconds}s.",
"timeout": "Cererea a expirat după {ms}ms.",
"success": "Gata.",
"yes": "da",
"no": "nu",
"confirm": "Sigur? (da/nu)",
"dryRun": "[simulare] ar face: {action}",
"cancelled": "Anulat.",
"jsonOpt": "Ieșire ca JSON",
"yesOpt": "Omite confirmarea"
},
"program": {
"description": "OmniRoute — Router AI inteligent cu fallback automat",
"version": "Afișează versiunea și ieși",
"output": "Format de ieșire (table, json, jsonl, csv)",
"quiet": "Suprimă ieșirile neesențiale",
"no_color": "Dezactivează ieșirea colorată",
"timeout": "Timeout cereri HTTP în milisecunde",
"api_key": "Cheie API pentru serverul OmniRoute",
"base_url": "URL de bază al serverului OmniRoute",
"context": "Contextul/profilul serverului pentru această comandă",
"lang": "Setează limba de afișare CLI (suprascrie OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/ru.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Ошибка: {message}",
"serverOffline": "Сервер OmniRoute отключён. Запустите: omniroute serve",
"authRequired": "Требуется аутентификация. Установите OMNIROUTE_API_KEY или выполните: omniroute setup",
"rateLimited": "Превышен лимит запросов. Повторите через {seconds}с.",
"timeout": "Запрос истёк через {ms}мс.",
"success": "Готово.",
"yes": "да",
"no": "нет",
"confirm": "Вы уверены? (да/нет)",
"dryRun": "[симуляция] выполнит: {action}",
"cancelled": "Отменено.",
"jsonOpt": "Вывод в формате JSON",
"yesOpt": "Пропустить подтверждение"
},
"program": {
"description": "OmniRoute — Умный AI-маршрутизатор с автоматическим переключением",
"version": "Вывести версию и выйти",
"output": "Формат вывода (table, json, jsonl, csv)",
"quiet": "Подавить несущественный вывод",
"no_color": "Отключить цветной вывод",
"timeout": "Таймаут HTTP-запросов в миллисекундах",
"api_key": "API-ключ для сервера OmniRoute",
"base_url": "Базовый URL сервера OmniRoute",
"context": "Контекст/профиль сервера для этой команды",
"lang": "Установить язык отображения CLI (переопределяет OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/sk.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Chyba: {message}",
"serverOffline": "Server OmniRoute je offline. Spustite: omniroute serve",
"authRequired": "Vyžaduje sa overenie. Nastavte OMNIROUTE_API_KEY alebo spustite: omniroute setup",
"rateLimited": "Prekročený limit požiadaviek. Skúste za {seconds}s.",
"timeout": "Požiadavka vypršala po {ms}ms.",
"success": "Hotovo.",
"yes": "áno",
"no": "nie",
"confirm": "Ste si istí? (áno/nie)",
"dryRun": "[simulácia] by vykonalo: {action}",
"cancelled": "Zrušené.",
"jsonOpt": "Výstup ako JSON",
"yesOpt": "Preskočiť potvrdenie"
},
"program": {
"description": "OmniRoute — Inteligentný AI router s automatickým prepínaním",
"version": "Vypísať verziu a skončiť",
"output": "Formát výstupu (table, json, jsonl, csv)",
"quiet": "Potlačiť nepodstatný výstup",
"no_color": "Zakázať farebný výstup",
"timeout": "Časový limit HTTP požiadaviek v milisekundách",
"api_key": "API kľúč pre server OmniRoute",
"base_url": "Základná URL servera OmniRoute",
"context": "Kontext/profil servera pre tento príkaz",
"lang": "Nastaviť jazyk zobrazenia CLI (prepíše OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/sv.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Fel: {message}",
"serverOffline": "OmniRoute-servern är offline. Starta med: omniroute serve",
"authRequired": "Autentisering krävs. Ange OMNIROUTE_API_KEY eller kör: omniroute setup",
"rateLimited": "Begäransgräns nådd. Försök igen om {seconds}s.",
"timeout": "Begäran tog slut efter {ms}ms.",
"success": "Klar.",
"yes": "ja",
"no": "nej",
"confirm": "Är du säker? (ja/nej)",
"dryRun": "[simulering] skulle: {action}",
"cancelled": "Avbruten.",
"jsonOpt": "Utdata som JSON",
"yesOpt": "Hoppa över bekräftelse"
},
"program": {
"description": "OmniRoute — Smart AI-router med automatisk fallback",
"version": "Skriv ut version och avsluta",
"output": "Utdataformat (table, json, jsonl, csv)",
"quiet": "Undertryck icke-väsentlig utdata",
"no_color": "Inaktivera färgad utdata",
"timeout": "HTTP-begärans timeout i millisekunder",
"api_key": "API-nyckel för OmniRoute-servern",
"base_url": "OmniRoute-serverns bas-URL",
"context": "Serverkontext/profil för det här kommandot",
"lang": "Ange CLI-visningsspråk (åsidosätter OMNIROUTE_LANG)"
}
}

1
bin/cli/locales/sw.json Normal file
View File

@@ -0,0 +1 @@
{}

1
bin/cli/locales/ta.json Normal file
View File

@@ -0,0 +1 @@
{}

1
bin/cli/locales/te.json Normal file
View File

@@ -0,0 +1 @@
{}

29
bin/cli/locales/th.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "ข้อผิดพลาด: {message}",
"serverOffline": "เซิร์ฟเวอร์ OmniRoute ออฟไลน์ เริ่มด้วย: omniroute serve",
"authRequired": "ต้องการการยืนยันตัวตน ตั้งค่า OMNIROUTE_API_KEY หรือรัน: omniroute setup",
"rateLimited": "เกินขีดจำกัดคำขอ ลองใหม่หลังจาก {seconds}วินาที",
"timeout": "คำขอหมดเวลาหลังจาก {ms}ms",
"success": "เสร็จสิ้น",
"yes": "ใช่",
"no": "ไม่",
"confirm": "คุณแน่ใจหรือไม่? (ใช่/ไม่)",
"dryRun": "[จำลอง] จะทำ: {action}",
"cancelled": "ยกเลิกแล้ว",
"jsonOpt": "ส่งออกเป็น JSON",
"yesOpt": "ข้ามการยืนยัน"
},
"program": {
"description": "OmniRoute — AI Router อัจฉริยะพร้อม Auto Fallback",
"version": "แสดงเวอร์ชันและออก",
"output": "รูปแบบเอาต์พุต (table, json, jsonl, csv)",
"quiet": "ซ่อนเอาต์พุตที่ไม่จำเป็น",
"no_color": "ปิดใช้งานเอาต์พุตสี",
"timeout": "หมดเวลา HTTP request ในมิลลิวินาที",
"api_key": "API Key สำหรับ OmniRoute server",
"base_url": "Base URL ของ OmniRoute server",
"context": "บริบท/โปรไฟล์ของเซิร์ฟเวอร์สำหรับคำสั่งนี้",
"lang": "ตั้งค่าภาษาแสดงผล CLI (แทนที่ OMNIROUTE_LANG)"
}
}

29
bin/cli/locales/tr.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Hata: {message}",
"serverOffline": "OmniRoute sunucusu çevrimdışı. Başlatın: omniroute serve",
"authRequired": "Kimlik doğrulama gerekli. OMNIROUTE_API_KEY ayarlayın veya çalıştırın: omniroute setup",
"rateLimited": "İstek limiti aşıldı. {seconds}s sonra tekrar deneyin.",
"timeout": "İstek {ms}ms sonra zaman aşımına uğradı.",
"success": "Tamamlandı.",
"yes": "evet",
"no": "hayır",
"confirm": "Emin misiniz? (evet/hayır)",
"dryRun": "[simülasyon] yapılacaktı: {action}",
"cancelled": "İptal edildi.",
"jsonOpt": "JSON olarak çıktı",
"yesOpt": "Onayı atla"
},
"program": {
"description": "OmniRoute — Otomatik Fallback ile Akıllı AI Yönlendirici",
"version": "Sürümü yazdır ve çık",
"output": ıktı formatı (table, json, jsonl, csv)",
"quiet": "Önemsiz çıktıyı gizle",
"no_color": "Renkli çıktıyı devre dışı bırak",
"timeout": "HTTP istek zaman aşımı (milisaniye)",
"api_key": "OmniRoute sunucusu için API anahtarı",
"base_url": "OmniRoute sunucusu temel URL'si",
"context": "Bu komut için sunucu bağlamı/profili",
"lang": "CLI görüntüleme dilini ayarla (OMNIROUTE_LANG'ı geçersiz kılar)"
}
}

View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Помилка: {message}",
"serverOffline": "Сервер OmniRoute відключено. Запустіть: omniroute serve",
"authRequired": "Потрібна автентифікація. Встановіть OMNIROUTE_API_KEY або виконайте: omniroute setup",
"rateLimited": "Перевищено ліміт запитів. Повторіть через {seconds}с.",
"timeout": "Запит завершився через {ms}мс.",
"success": "Готово.",
"yes": "так",
"no": "ні",
"confirm": "Ви впевнені? (так/ні)",
"dryRun": "[симуляція] виконає: {action}",
"cancelled": "Скасовано.",
"jsonOpt": "Вивести у форматі JSON",
"yesOpt": "Пропустити підтвердження"
},
"program": {
"description": "OmniRoute — Розумний AI-маршрутизатор з автоматичним перемиканням",
"version": "Вивести версію та вийти",
"output": "Формат виведення (table, json, jsonl, csv)",
"quiet": "Приховати несуттєвий вивід",
"no_color": "Вимкнути кольоровий вивід",
"timeout": "Тайм-аут HTTP-запитів у мілісекундах",
"api_key": "API-ключ для сервера OmniRoute",
"base_url": "Базовий URL сервера OmniRoute",
"context": "Контекст/профіль сервера для цієї команди",
"lang": "Встановити мову відображення CLI (замінює OMNIROUTE_LANG)"
}
}

1
bin/cli/locales/ur.json Normal file
View File

@@ -0,0 +1 @@
{}

29
bin/cli/locales/vi.json Normal file
View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "Lỗi: {message}",
"serverOffline": "Máy chủ OmniRoute đang offline. Khởi động với: omniroute serve",
"authRequired": "Cần xác thực. Đặt OMNIROUTE_API_KEY hoặc chạy: omniroute setup",
"rateLimited": "Đã vượt giới hạn yêu cầu. Thử lại sau {seconds}s.",
"timeout": "Yêu cầu hết thời gian sau {ms}ms.",
"success": "Xong.",
"yes": "có",
"no": "không",
"confirm": "Bạn có chắc không? (có/không)",
"dryRun": "[mô phỏng] sẽ: {action}",
"cancelled": "Đã hủy.",
"jsonOpt": "Xuất dưới dạng JSON",
"yesOpt": "Bỏ qua xác nhận"
},
"program": {
"description": "OmniRoute — Bộ định tuyến AI thông minh với tự động chuyển đổi dự phòng",
"version": "In phiên bản và thoát",
"output": "Định dạng đầu ra (table, json, jsonl, csv)",
"quiet": "Ẩn đầu ra không cần thiết",
"no_color": "Tắt đầu ra màu sắc",
"timeout": "Thời gian chờ yêu cầu HTTP tính bằng mili giây",
"api_key": "Khóa API cho máy chủ OmniRoute",
"base_url": "URL cơ sở của máy chủ OmniRoute",
"context": "Bối cảnh/hồ sơ máy chủ cho lệnh này",
"lang": "Đặt ngôn ngữ hiển thị CLI (ghi đè OMNIROUTE_LANG)"
}
}

View File

@@ -0,0 +1,29 @@
{
"common": {
"error": "错误:{message}",
"serverOffline": "OmniRoute 服务器已离线。请启动omniroute serve",
"authRequired": "需要认证。请设置 OMNIROUTE_API_KEY 或运行omniroute setup",
"rateLimited": "请求超出限制。请在 {seconds}s 后重试。",
"timeout": "请求在 {ms}ms 后超时。",
"success": "完成。",
"yes": "是",
"no": "否",
"confirm": "确定吗?(是/否)",
"dryRun": "【模拟】将执行:{action}",
"cancelled": "已取消。",
"jsonOpt": "以 JSON 格式输出",
"yesOpt": "跳过确认"
},
"program": {
"description": "OmniRoute — 具有自动故障转移的智能 AI 路由器",
"version": "打印版本并退出",
"output": "输出格式table, json, jsonl, csv",
"quiet": "禁止非必要输出",
"no_color": "禁用彩色输出",
"timeout": "HTTP 请求超时(毫秒)",
"api_key": "OmniRoute 服务器的 API 密钥",
"base_url": "OmniRoute 服务器的基础 URL",
"context": "此命令使用的服务器上下文/配置文件",
"lang": "设置 CLI 显示语言(覆盖 OMNIROUTE_LANG"
}
}

View File

@@ -31,6 +31,7 @@ export function createProgram() {
t("program.context") || "Server context/profile to use for this command"
).env("OMNIROUTE_CONTEXT")
)
.addOption(new Option("--lang <code>", t("program.lang")))
.showHelpAfterError(true)
.exitOverride();

View File

@@ -0,0 +1,911 @@
#!/usr/bin/env node
/**
* Generates scaffold locale files for all languages listed in config/i18n.json
* that don't yet have a corresponding file in bin/cli/locales/.
*
* For top-tier languages, a translated `common` + `program` section is included.
* All other keys fall back to `en` via i18n.mjs's existing fallback mechanism.
*
* Run: node bin/cli/scripts/generate-locales.mjs [--force]
*/
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, "..", "..", "..");
const LOCALES_DIR = join(__dirname, "..", "locales");
const I18N_CFG = join(ROOT, "config", "i18n.json");
const FORCE = process.argv.includes("--force");
const { locales } = JSON.parse(readFileSync(I18N_CFG, "utf8"));
// common + program translations for each language code.
// Keys that are absent fall back to en automatically.
const TRANSLATIONS = {
ar: {
common: {
error: "خطأ: {message}",
serverOffline: "خادم OmniRoute غير متصل. ابدأ بالأمر: omniroute serve",
authRequired: "المصادقة مطلوبة. عيّن OMNIROUTE_API_KEY أو شغّل: omniroute setup",
rateLimited: "تم تجاوز حد الطلبات. أعد المحاولة بعد {seconds} ثانية.",
timeout: "انتهت مهلة الطلب بعد {ms}ms.",
success: "تم.",
yes: "نعم",
no: "لا",
confirm: "هل أنت متأكد؟ (نعم/لا)",
dryRun: "[محاكاة] سيتم: {action}",
cancelled: "تم الإلغاء.",
jsonOpt: "إخراج بتنسيق JSON",
yesOpt: "تخطي رسالة التأكيد",
},
program: {
description: "OmniRoute — جهاز توجيه الذكاء الاصطناعي مع التبديل التلقائي",
version: "عرض الإصدار والخروج",
output: "تنسيق الإخراج (table, json, jsonl, csv)",
quiet: "إخفاء المخرجات غير الأساسية",
no_color: "تعطيل الإخراج الملوّن",
timeout: "مهلة طلب HTTP بالميلي ثانية",
api_key: "مفتاح API لخادم OmniRoute",
base_url: "عنوان URL الأساسي لخادم OmniRoute",
context: "سياق/ملف تعريف الخادم المستخدم في هذا الأمر",
lang: "تعيين لغة عرض CLI (يتجاوز OMNIROUTE_LANG)",
},
},
az: {
common: {
error: "Xəta: {message}",
serverOffline: "OmniRoute serveri oflayndır. Başladın: omniroute serve",
authRequired:
"Autentifikasiya tələb olunur. OMNIROUTE_API_KEY təyin edin və ya işə salın: omniroute setup",
rateLimited: "Sorğu limiti aşıldı. {seconds} saniyə sonra yenidən cəhd edin.",
timeout: "Sorğunun vaxtı {ms}ms sonra bitdi.",
success: "Tamamlandı.",
yes: "bəli",
no: "xeyr",
confirm: "Əminsiniz? (bəli/xeyr)",
dryRun: "[simulyasiya] ediləcəkdi: {action}",
cancelled: "Ləğv edildi.",
jsonOpt: "JSON formatında çıxış",
yesOpt: "Təsdiq sorğusunu keç",
},
program: {
description: "OmniRoute — Avtomatik Fallback ilə Ağıllı AI Marşrutlaşdırıcısı",
version: "Versiyasını çap et və çıx",
output: ıxış formatı (table, json, jsonl, csv)",
quiet: "Vacib olmayan çıxışı gizlət",
no_color: "Rəngli çıxışı deaktiv et",
timeout: "HTTP sorğusu üçün zaman aşımı (millisaniyə)",
api_key: "OmniRoute serveri üçün API açarı",
base_url: "OmniRoute server baza URL-i",
context: "Bu əmr üçün server konteksti/profili",
lang: "CLI ekran dilini təyin edin (OMNIROUTE_LANG-ı keçir)",
},
},
bg: {
common: {
error: "Грешка: {message}",
serverOffline: "Сървърът OmniRoute е офлайн. Стартирайте с: omniroute serve",
authRequired:
"Необходима е автентикация. Задайте OMNIROUTE_API_KEY или изпълнете: omniroute setup",
rateLimited: "Превишен лимит на заявки. Опитайте след {seconds}с.",
timeout: "Заявката изтече след {ms}ms.",
success: "Готово.",
yes: "да",
no: "не",
confirm: "Сигурни ли сте? (да/не)",
dryRun: "[симулация] ще извърши: {action}",
cancelled: "Отменено.",
jsonOpt: "Изход като JSON",
yesOpt: "Пропускане на потвърждение",
},
program: {
description: "OmniRoute — Интелигентен AI рутер с автоматично превключване",
version: "Покажи версията и излез",
output: "Формат на изхода (table, json, jsonl, csv)",
quiet: "Потисни несъществена информация",
no_color: "Деактивирай цветния изход",
timeout: "Таймаут за HTTP заявки в милисекунди",
api_key: "API ключ за сървъра OmniRoute",
base_url: "Базов URL на сървъра OmniRoute",
context: "Контекст/профил на сървъра за тази команда",
lang: "Задай език на CLI (замества OMNIROUTE_LANG)",
},
},
cs: {
common: {
error: "Chyba: {message}",
serverOffline: "Server OmniRoute je offline. Spusťte: omniroute serve",
authRequired: "Vyžaduje se ověření. Nastavte OMNIROUTE_API_KEY nebo spusťte: omniroute setup",
rateLimited: "Překročen limit požadavků. Zkuste za {seconds}s.",
timeout: "Požadavek vypršel po {ms}ms.",
success: "Hotovo.",
yes: "ano",
no: "ne",
confirm: "Jste si jisti? (ano/ne)",
dryRun: "[simulace] by provedlo: {action}",
cancelled: "Zrušeno.",
jsonOpt: "Výstup jako JSON",
yesOpt: "Přeskočit potvrzení",
},
program: {
description: "OmniRoute — Chytrý AI router s automatickým přepínáním",
version: "Vypsat verzi a skončit",
output: "Formát výstupu (table, json, jsonl, csv)",
quiet: "Potlačit nepodstatný výstup",
no_color: "Zakázat barevný výstup",
timeout: "Časový limit HTTP požadavků v milisekundách",
api_key: "API klíč pro server OmniRoute",
base_url: "Základní URL serveru OmniRoute",
context: "Kontext/profil serveru pro tento příkaz",
lang: "Nastavit jazyk CLI (přepisuje OMNIROUTE_LANG)",
},
},
da: {
common: {
error: "Fejl: {message}",
serverOffline: "OmniRoute-serveren er offline. Start med: omniroute serve",
authRequired: "Godkendelse kræves. Sæt OMNIROUTE_API_KEY eller kør: omniroute setup",
rateLimited: "Anmodningsgrænse overskredet. Prøv igen om {seconds}s.",
timeout: "Anmodningen timed ud efter {ms}ms.",
success: "Færdig.",
yes: "ja",
no: "nej",
confirm: "Er du sikker? (ja/nej)",
dryRun: "[simulering] ville: {action}",
cancelled: "Annulleret.",
jsonOpt: "Output som JSON",
yesOpt: "Spring bekræftelse over",
},
program: {
description: "OmniRoute — Smart AI-router med automatisk fallback",
version: "Vis version og afslut",
output: "Outputformat (table, json, jsonl, csv)",
quiet: "Undertryk ikke-essentielt output",
no_color: "Deaktiver farvet output",
timeout: "HTTP-anmodnings timeout i millisekunder",
api_key: "API-nøgle til OmniRoute-serveren",
base_url: "OmniRoute-serverens basis-URL",
context: "Server-kontekst/profil til denne kommando",
lang: "Angiv CLI-visningssprog (tilsidesætter OMNIROUTE_LANG)",
},
},
de: {
common: {
error: "Fehler: {message}",
serverOffline: "OmniRoute-Server ist offline. Starten mit: omniroute serve",
authRequired:
"Authentifizierung erforderlich. OMNIROUTE_API_KEY setzen oder ausführen: omniroute setup",
rateLimited: "Anfragelimit überschritten. Erneut versuchen in {seconds}s.",
timeout: "Anfrage-Timeout nach {ms}ms.",
success: "Fertig.",
yes: "ja",
no: "nein",
confirm: "Sind Sie sicher? (ja/nein)",
dryRun: "[Simulation] würde: {action}",
cancelled: "Abgebrochen.",
jsonOpt: "Ausgabe als JSON",
yesOpt: "Bestätigung überspringen",
},
program: {
description: "OmniRoute — Intelligenter AI-Router mit automatischem Fallback",
version: "Version ausgeben und beenden",
output: "Ausgabeformat (table, json, jsonl, csv)",
quiet: "Unwesentliche Ausgabe unterdrücken",
no_color: "Farbige Ausgabe deaktivieren",
timeout: "HTTP-Anfrage-Timeout in Millisekunden",
api_key: "API-Schlüssel für den OmniRoute-Server",
base_url: "OmniRoute-Server-Basis-URL",
context: "Server-Kontext/Profil für diesen Befehl",
lang: "CLI-Anzeigesprache festlegen (überschreibt OMNIROUTE_LANG)",
},
},
es: {
common: {
error: "Error: {message}",
serverOffline: "El servidor OmniRoute está offline. Inícielo con: omniroute serve",
authRequired:
"Autenticación requerida. Configure OMNIROUTE_API_KEY o ejecute: omniroute setup",
rateLimited: "Límite de solicitudes excedido. Reintente en {seconds}s.",
timeout: "Solicitud expiró después de {ms}ms.",
success: "Listo.",
yes: "sí",
no: "no",
confirm: "¿Está seguro? (sí/no)",
dryRun: "[simulación] haría: {action}",
cancelled: "Cancelado.",
jsonOpt: "Salida como JSON",
yesOpt: "Omitir confirmación",
},
program: {
description: "OmniRoute — Router de IA inteligente con fallback automático",
version: "Mostrar versión y salir",
output: "Formato de salida (table, json, jsonl, csv)",
quiet: "Suprimir salida no esencial",
no_color: "Deshabilitar salida en color",
timeout: "Tiempo de espera de solicitudes HTTP en milisegundos",
api_key: "Clave de API para el servidor OmniRoute",
base_url: "URL base del servidor OmniRoute",
context: "Contexto/perfil del servidor para este comando",
lang: "Establecer idioma del CLI (reemplaza OMNIROUTE_LANG)",
},
},
fa: {
common: {
error: "خطا: {message}",
serverOffline: "سرور OmniRoute آفلاین است. با این دستور راه‌اندازی کنید: omniroute serve",
authRequired:
"احراز هویت لازم است. OMNIROUTE_API_KEY را تنظیم کنید یا اجرا کنید: omniroute setup",
rateLimited: "محدودیت درخواست رسیده است. پس از {seconds} ثانیه دوباره تلاش کنید.",
timeout: "درخواست پس از {ms}ms منقضی شد.",
success: "انجام شد.",
yes: "بله",
no: "خیر",
confirm: "مطمئنید؟ (بله/خیر)",
dryRun: "[شبیه‌سازی] اقدام می‌شد: {action}",
cancelled: "لغو شد.",
jsonOpt: "خروجی به صورت JSON",
yesOpt: "رد کردن تأیید",
},
program: {
description: "OmniRoute — روتر هوشمند هوش مصنوعی با fallback خودکار",
version: "نمایش نسخه و خروج",
output: "فرمت خروجی (table, json, jsonl, csv)",
quiet: "حذف خروجی غیر ضروری",
no_color: "غیرفعال کردن خروجی رنگی",
timeout: "تایم‌اوت درخواست HTTP به میلی‌ثانیه",
api_key: "کلید API برای سرور OmniRoute",
base_url: "URL پایه سرور OmniRoute",
context: "زمینه/پروفایل سرور برای این دستور",
lang: "تنظیم زبان نمایش CLI (OMNIROUTE_LANG را نادیده می‌گیرد)",
},
},
fi: {
common: {
error: "Virhe: {message}",
serverOffline: "OmniRoute-palvelin on offline. Käynnistä komennolla: omniroute serve",
authRequired: "Todennus vaaditaan. Aseta OMNIROUTE_API_KEY tai suorita: omniroute setup",
rateLimited: "Pyyntöraja ylitetty. Yritä uudelleen {seconds}s kuluttua.",
timeout: "Pyyntö aikakatkaistiin {ms}ms jälkeen.",
success: "Valmis.",
yes: "kyllä",
no: "ei",
confirm: "Oletko varma? (kyllä/ei)",
dryRun: "[simulointi] tekisi: {action}",
cancelled: "Peruutettu.",
jsonOpt: "Tulosta JSON-muodossa",
yesOpt: "Ohita vahvistuspyyntö",
},
program: {
description: "OmniRoute — Älykäs AI-reititin automaattisella fallbackilla",
version: "Tulosta versio ja poistu",
output: "Tulostusmuoto (table, json, jsonl, csv)",
quiet: "Piilota epäolennaiset tulosteet",
no_color: "Poista väritulostus käytöstä",
timeout: "HTTP-pyyntöjen aikakatkaisu millisekunteina",
api_key: "API-avain OmniRoute-palvelimelle",
base_url: "OmniRoute-palvelimen perus-URL",
context: "Palvelimen konteksti/profiili tälle komennolle",
lang: "Aseta CLI-näyttökieli (ohittaa OMNIROUTE_LANG)",
},
},
fr: {
common: {
error: "Erreur : {message}",
serverOffline: "Le serveur OmniRoute est hors ligne. Démarrez avec : omniroute serve",
authRequired:
"Authentification requise. Définissez OMNIROUTE_API_KEY ou exécutez : omniroute setup",
rateLimited: "Limite de requêtes atteinte. Réessayez dans {seconds}s.",
timeout: "La requête a expiré après {ms}ms.",
success: "Terminé.",
yes: "oui",
no: "non",
confirm: "Êtes-vous sûr ? (oui/non)",
dryRun: "[simulation] ferait : {action}",
cancelled: "Annulé.",
jsonOpt: "Sortie au format JSON",
yesOpt: "Ignorer la confirmation",
},
program: {
description: "OmniRoute — Routeur IA intelligent avec basculement automatique",
version: "Afficher la version et quitter",
output: "Format de sortie (table, json, jsonl, csv)",
quiet: "Supprimer les sorties non essentielles",
no_color: "Désactiver la sortie en couleur",
timeout: "Délai d'expiration des requêtes HTTP en millisecondes",
api_key: "Clé API pour le serveur OmniRoute",
base_url: "URL de base du serveur OmniRoute",
context: "Contexte/profil du serveur pour cette commande",
lang: "Définir la langue d'affichage du CLI (remplace OMNIROUTE_LANG)",
},
},
hi: {
common: {
error: "त्रुटि: {message}",
serverOffline: "OmniRoute सर्वर ऑफलाइन है। शुरू करें: omniroute serve",
authRequired: "प्रमाणीकरण आवश्यक है। OMNIROUTE_API_KEY सेट करें या चलाएं: omniroute setup",
rateLimited: "अनुरोध सीमा पार हो गई। {seconds}s बाद पुनः प्रयास करें।",
timeout: "{ms}ms के बाद अनुरोध समय समाप्त हुआ।",
success: "पूर्ण।",
yes: "हाँ",
no: "नहीं",
confirm: "क्या आप सुनिश्चित हैं? (हाँ/नहीं)",
dryRun: "[अनुकरण] करेगा: {action}",
cancelled: "रद्द किया गया।",
jsonOpt: "JSON के रूप में आउटपुट",
yesOpt: "पुष्टि छोड़ें",
},
program: {
description: "OmniRoute — ऑटो फॉलबैक के साथ स्मार्ट AI राउटर",
version: "संस्करण प्रिंट करें और बाहर निकलें",
output: "आउटपुट प्रारूप (table, json, jsonl, csv)",
quiet: "गैर-आवश्यक आउटपुट दबाएं",
no_color: "रंगीन आउटपुट अक्षम करें",
timeout: "HTTP अनुरोध टाइमआउट मिलीसेकंड में",
api_key: "OmniRoute सर्वर के लिए API कुंजी",
base_url: "OmniRoute सर्वर का बेस URL",
context: "इस कमांड के लिए सर्वर संदर्भ/प्रोफ़ाइल",
lang: "CLI प्रदर्शन भाषा सेट करें (OMNIROUTE_LANG को ओवरराइड करता है)",
},
},
hu: {
common: {
error: "Hiba: {message}",
serverOffline: "Az OmniRoute szerver offline. Indítsa el: omniroute serve",
authRequired:
"Hitelesítés szükséges. Állítsa be az OMNIROUTE_API_KEY-t vagy futtassa: omniroute setup",
rateLimited: "Kérési korlát túllépve. Próbálja újra {seconds}s múlva.",
timeout: "A kérés {ms}ms után lejárt.",
success: "Kész.",
yes: "igen",
no: "nem",
confirm: "Biztos benne? (igen/nem)",
dryRun: "[szimuláció] végrehajtaná: {action}",
cancelled: "Törölve.",
jsonOpt: "JSON formátumú kimenet",
yesOpt: "Megerősítés kihagyása",
},
program: {
description: "OmniRoute — Intelligens AI útválasztó automatikus fallbackkel",
version: "Verzió kiírása és kilépés",
output: "Kimeneti formátum (table, json, jsonl, csv)",
quiet: "Nem lényeges kimenet elnyomása",
no_color: "Színes kimenet letiltása",
timeout: "HTTP kérés időtúllépése ezredmásodpercben",
api_key: "API kulcs az OmniRoute szerverhez",
base_url: "Az OmniRoute szerver alap URL-je",
context: "Szerverkontextus/profil ehhez a parancshoz",
lang: "CLI megjelenítési nyelv beállítása (felülírja az OMNIROUTE_LANG-ot)",
},
},
id: {
common: {
error: "Kesalahan: {message}",
serverOffline: "Server OmniRoute sedang offline. Mulai dengan: omniroute serve",
authRequired:
"Autentikasi diperlukan. Setel OMNIROUTE_API_KEY atau jalankan: omniroute setup",
rateLimited: "Batas permintaan terlampaui. Coba lagi dalam {seconds}d.",
timeout: "Permintaan habis waktu setelah {ms}ms.",
success: "Selesai.",
yes: "ya",
no: "tidak",
confirm: "Apakah Anda yakin? (ya/tidak)",
dryRun: "[simulasi] akan: {action}",
cancelled: "Dibatalkan.",
jsonOpt: "Keluaran sebagai JSON",
yesOpt: "Lewati konfirmasi",
},
program: {
description: "OmniRoute — Router AI Cerdas dengan Fallback Otomatis",
version: "Cetak versi dan keluar",
output: "Format keluaran (table, json, jsonl, csv)",
quiet: "Sembunyikan output yang tidak penting",
no_color: "Nonaktifkan output berwarna",
timeout: "Batas waktu permintaan HTTP dalam milidetik",
api_key: "Kunci API untuk server OmniRoute",
base_url: "URL dasar server OmniRoute",
context: "Konteks/profil server untuk perintah ini",
lang: "Atur bahasa tampilan CLI (menggantikan OMNIROUTE_LANG)",
},
},
it: {
common: {
error: "Errore: {message}",
serverOffline: "Il server OmniRoute è offline. Avviarlo con: omniroute serve",
authRequired:
"Autenticazione richiesta. Impostare OMNIROUTE_API_KEY o eseguire: omniroute setup",
rateLimited: "Limite di richieste superato. Riprovare tra {seconds}s.",
timeout: "La richiesta è scaduta dopo {ms}ms.",
success: "Completato.",
yes: "sì",
no: "no",
confirm: "Sei sicuro? (sì/no)",
dryRun: "[simulazione] eseguirebbe: {action}",
cancelled: "Annullato.",
jsonOpt: "Output come JSON",
yesOpt: "Salta la conferma",
},
program: {
description: "OmniRoute — Router AI intelligente con fallback automatico",
version: "Stampa la versione ed esci",
output: "Formato di output (table, json, jsonl, csv)",
quiet: "Sopprimi l'output non essenziale",
no_color: "Disabilita l'output colorato",
timeout: "Timeout delle richieste HTTP in millisecondi",
api_key: "Chiave API per il server OmniRoute",
base_url: "URL base del server OmniRoute",
context: "Contesto/profilo del server per questo comando",
lang: "Imposta la lingua di visualizzazione della CLI (sovrascrive OMNIROUTE_LANG)",
},
},
ja: {
common: {
error: "エラー: {message}",
serverOffline: "OmniRouteサーバーはオフラインです。起動: omniroute serve",
authRequired:
"認証が必要です。OMNIROUTE_API_KEYを設定するか実行してください: omniroute setup",
rateLimited: "リクエスト制限を超えました。{seconds}秒後に再試行してください。",
timeout: "{ms}ms後にリクエストがタイムアウトしました。",
success: "完了。",
yes: "はい",
no: "いいえ",
confirm: "よろしいですか?(はい/いいえ)",
dryRun: "[シミュレーション] 実行予定: {action}",
cancelled: "キャンセルしました。",
jsonOpt: "JSON形式で出力",
yesOpt: "確認をスキップ",
},
program: {
description: "OmniRoute — 自動フォールバック付きスマートAIルーター",
version: "バージョンを表示して終了",
output: "出力形式 (table, json, jsonl, csv)",
quiet: "重要でない出力を抑制",
no_color: "カラー出力を無効化",
timeout: "HTTPリクエストタイムアウトミリ秒",
api_key: "OmniRouteサーバーのAPIキー",
base_url: "OmniRouteサーバーのベースURL",
context: "このコマンドで使用するサーバーコンテキスト/プロファイル",
lang: "CLI表示言語を設定OMNIROUTE_LANGを上書き",
},
},
ko: {
common: {
error: "오류: {message}",
serverOffline: "OmniRoute 서버가 오프라인입니다. 시작: omniroute serve",
authRequired: "인증이 필요합니다. OMNIROUTE_API_KEY를 설정하거나 실행하세요: omniroute setup",
rateLimited: "요청 제한 초과. {seconds}초 후 다시 시도하세요.",
timeout: "{ms}ms 후 요청 시간 초과.",
success: "완료.",
yes: "예",
no: "아니오",
confirm: "확실합니까? (예/아니오)",
dryRun: "[시뮬레이션] 실행 예정: {action}",
cancelled: "취소되었습니다.",
jsonOpt: "JSON으로 출력",
yesOpt: "확인 건너뛰기",
},
program: {
description: "OmniRoute — 자동 폴백 기능을 갖춘 스마트 AI 라우터",
version: "버전 출력 후 종료",
output: "출력 형식 (table, json, jsonl, csv)",
quiet: "불필요한 출력 억제",
no_color: "색상 출력 비활성화",
timeout: "HTTP 요청 타임아웃(밀리초)",
api_key: "OmniRoute 서버의 API 키",
base_url: "OmniRoute 서버 기본 URL",
context: "이 명령에 사용할 서버 컨텍스트/프로필",
lang: "CLI 표시 언어 설정 (OMNIROUTE_LANG 재정의)",
},
},
nl: {
common: {
error: "Fout: {message}",
serverOffline: "OmniRoute-server is offline. Start met: omniroute serve",
authRequired: "Authenticatie vereist. Stel OMNIROUTE_API_KEY in of voer uit: omniroute setup",
rateLimited: "Verzoeklimiet overschreden. Probeer opnieuw na {seconds}s.",
timeout: "Verzoek verlopen na {ms}ms.",
success: "Klaar.",
yes: "ja",
no: "nee",
confirm: "Weet u het zeker? (ja/nee)",
dryRun: "[simulatie] zou: {action}",
cancelled: "Geannuleerd.",
jsonOpt: "Uitvoer als JSON",
yesOpt: "Bevestiging overslaan",
},
program: {
description: "OmniRoute — Slimme AI-router met automatische fallback",
version: "Versie afdrukken en afsluiten",
output: "Uitvoerformaat (table, json, jsonl, csv)",
quiet: "Niet-essentiële uitvoer onderdrukken",
no_color: "Gekleurde uitvoer uitschakelen",
timeout: "HTTP-verzoek timeout in milliseconden",
api_key: "API-sleutel voor de OmniRoute-server",
base_url: "Basis-URL van de OmniRoute-server",
context: "Servercontext/profiel voor dit commando",
lang: "CLI-weergavetaal instellen (overschrijft OMNIROUTE_LANG)",
},
},
no: {
common: {
error: "Feil: {message}",
serverOffline: "OmniRoute-serveren er offline. Start med: omniroute serve",
authRequired: "Autentisering kreves. Angi OMNIROUTE_API_KEY eller kjør: omniroute setup",
rateLimited: "Forespørselgrense overskredet. Prøv igjen om {seconds}s.",
timeout: "Forespørselen tidsavbrutt etter {ms}ms.",
success: "Ferdig.",
yes: "ja",
no: "nei",
confirm: "Er du sikker? (ja/nei)",
dryRun: "[simulering] ville: {action}",
cancelled: "Avbrutt.",
jsonOpt: "Utdata som JSON",
yesOpt: "Hopp over bekreftelse",
},
program: {
description: "OmniRoute — Smart AI-ruter med automatisk fallback",
version: "Skriv ut versjon og avslutt",
output: "Utdataformat (table, json, jsonl, csv)",
quiet: "Undertrykk ikke-essensiell utdata",
no_color: "Deaktiver farget utdata",
timeout: "HTTP-forespørsel timeout i millisekunder",
api_key: "API-nøkkel for OmniRoute-serveren",
base_url: "OmniRoute-serverens basis-URL",
context: "Serverkontekst/profil for denne kommandoen",
lang: "Angi CLI-visningsspråk (overstyrer OMNIROUTE_LANG)",
},
},
pl: {
common: {
error: "Błąd: {message}",
serverOffline: "Serwer OmniRoute jest offline. Uruchom: omniroute serve",
authRequired:
"Wymagane uwierzytelnienie. Ustaw OMNIROUTE_API_KEY lub uruchom: omniroute setup",
rateLimited: "Przekroczono limit żądań. Spróbuj ponownie za {seconds}s.",
timeout: "Żądanie przekroczyło czas po {ms}ms.",
success: "Gotowe.",
yes: "tak",
no: "nie",
confirm: "Czy jesteś pewien? (tak/nie)",
dryRun: "[symulacja] wykonałoby: {action}",
cancelled: "Anulowano.",
jsonOpt: "Wyjście jako JSON",
yesOpt: "Pomiń potwierdzenie",
},
program: {
description: "OmniRoute — Inteligentny router AI z automatycznym fallbackiem",
version: "Wydrukuj wersję i wyjdź",
output: "Format wyjścia (table, json, jsonl, csv)",
quiet: "Pomiń nieistotne wyjście",
no_color: "Wyłącz kolorowe wyjście",
timeout: "Limit czasu żądania HTTP w milisekundach",
api_key: "Klucz API dla serwera OmniRoute",
base_url: "Bazowy URL serwera OmniRoute",
context: "Kontekst/profil serwera dla tego polecenia",
lang: "Ustaw język wyświetlania CLI (nadpisuje OMNIROUTE_LANG)",
},
},
pt: {
common: {
error: "Erro: {message}",
serverOffline: "O servidor OmniRoute está offline. Inicie com: omniroute serve",
authRequired: "Autenticação necessária. Defina OMNIROUTE_API_KEY ou execute: omniroute setup",
rateLimited: "Limite de pedidos atingido. Tente novamente em {seconds}s.",
timeout: "O pedido expirou após {ms}ms.",
success: "Concluído.",
yes: "sim",
no: "não",
confirm: "Tem a certeza? (sim/não)",
dryRun: "[simulação] faria: {action}",
cancelled: "Cancelado.",
jsonOpt: "Saída em JSON",
yesOpt: "Ignorar confirmação",
},
program: {
description: "OmniRoute — Router de IA inteligente com fallback automático",
version: "Mostrar versão e sair",
output: "Formato de saída (table, json, jsonl, csv)",
quiet: "Suprimir saída não essencial",
no_color: "Desativar saída colorida",
timeout: "Timeout de pedidos HTTP em milissegundos",
api_key: "Chave de API para o servidor OmniRoute",
base_url: "URL base do servidor OmniRoute",
context: "Contexto/perfil do servidor para este comando",
lang: "Definir idioma de apresentação do CLI (substitui OMNIROUTE_LANG)",
},
},
ro: {
common: {
error: "Eroare: {message}",
serverOffline: "Serverul OmniRoute este offline. Porniți cu: omniroute serve",
authRequired: "Autentificare necesară. Setați OMNIROUTE_API_KEY sau rulați: omniroute setup",
rateLimited: "Limita de cereri depășită. Încercați din nou după {seconds}s.",
timeout: "Cererea a expirat după {ms}ms.",
success: "Gata.",
yes: "da",
no: "nu",
confirm: "Sigur? (da/nu)",
dryRun: "[simulare] ar face: {action}",
cancelled: "Anulat.",
jsonOpt: "Ieșire ca JSON",
yesOpt: "Omite confirmarea",
},
program: {
description: "OmniRoute — Router AI inteligent cu fallback automat",
version: "Afișează versiunea și ieși",
output: "Format de ieșire (table, json, jsonl, csv)",
quiet: "Suprimă ieșirile neesențiale",
no_color: "Dezactivează ieșirea colorată",
timeout: "Timeout cereri HTTP în milisecunde",
api_key: "Cheie API pentru serverul OmniRoute",
base_url: "URL de bază al serverului OmniRoute",
context: "Contextul/profilul serverului pentru această comandă",
lang: "Setează limba de afișare CLI (suprascrie OMNIROUTE_LANG)",
},
},
ru: {
common: {
error: "Ошибка: {message}",
serverOffline: "Сервер OmniRoute отключён. Запустите: omniroute serve",
authRequired:
"Требуется аутентификация. Установите OMNIROUTE_API_KEY или выполните: omniroute setup",
rateLimited: "Превышен лимит запросов. Повторите через {seconds}с.",
timeout: "Запрос истёк через {ms}мс.",
success: "Готово.",
yes: "да",
no: "нет",
confirm: "Вы уверены? (да/нет)",
dryRun: "[симуляция] выполнит: {action}",
cancelled: "Отменено.",
jsonOpt: "Вывод в формате JSON",
yesOpt: "Пропустить подтверждение",
},
program: {
description: "OmniRoute — Умный AI-маршрутизатор с автоматическим переключением",
version: "Вывести версию и выйти",
output: "Формат вывода (table, json, jsonl, csv)",
quiet: "Подавить несущественный вывод",
no_color: "Отключить цветной вывод",
timeout: "Таймаут HTTP-запросов в миллисекундах",
api_key: "API-ключ для сервера OmniRoute",
base_url: "Базовый URL сервера OmniRoute",
context: "Контекст/профиль сервера для этой команды",
lang: "Установить язык отображения CLI (переопределяет OMNIROUTE_LANG)",
},
},
sk: {
common: {
error: "Chyba: {message}",
serverOffline: "Server OmniRoute je offline. Spustite: omniroute serve",
authRequired:
"Vyžaduje sa overenie. Nastavte OMNIROUTE_API_KEY alebo spustite: omniroute setup",
rateLimited: "Prekročený limit požiadaviek. Skúste za {seconds}s.",
timeout: "Požiadavka vypršala po {ms}ms.",
success: "Hotovo.",
yes: "áno",
no: "nie",
confirm: "Ste si istí? (áno/nie)",
dryRun: "[simulácia] by vykonalo: {action}",
cancelled: "Zrušené.",
jsonOpt: "Výstup ako JSON",
yesOpt: "Preskočiť potvrdenie",
},
program: {
description: "OmniRoute — Inteligentný AI router s automatickým prepínaním",
version: "Vypísať verziu a skončiť",
output: "Formát výstupu (table, json, jsonl, csv)",
quiet: "Potlačiť nepodstatný výstup",
no_color: "Zakázať farebný výstup",
timeout: "Časový limit HTTP požiadaviek v milisekundách",
api_key: "API kľúč pre server OmniRoute",
base_url: "Základná URL servera OmniRoute",
context: "Kontext/profil servera pre tento príkaz",
lang: "Nastaviť jazyk zobrazenia CLI (prepíše OMNIROUTE_LANG)",
},
},
sv: {
common: {
error: "Fel: {message}",
serverOffline: "OmniRoute-servern är offline. Starta med: omniroute serve",
authRequired: "Autentisering krävs. Ange OMNIROUTE_API_KEY eller kör: omniroute setup",
rateLimited: "Begäransgräns nådd. Försök igen om {seconds}s.",
timeout: "Begäran tog slut efter {ms}ms.",
success: "Klar.",
yes: "ja",
no: "nej",
confirm: "Är du säker? (ja/nej)",
dryRun: "[simulering] skulle: {action}",
cancelled: "Avbruten.",
jsonOpt: "Utdata som JSON",
yesOpt: "Hoppa över bekräftelse",
},
program: {
description: "OmniRoute — Smart AI-router med automatisk fallback",
version: "Skriv ut version och avsluta",
output: "Utdataformat (table, json, jsonl, csv)",
quiet: "Undertryck icke-väsentlig utdata",
no_color: "Inaktivera färgad utdata",
timeout: "HTTP-begärans timeout i millisekunder",
api_key: "API-nyckel för OmniRoute-servern",
base_url: "OmniRoute-serverns bas-URL",
context: "Serverkontext/profil för det här kommandot",
lang: "Ange CLI-visningsspråk (åsidosätter OMNIROUTE_LANG)",
},
},
th: {
common: {
error: "ข้อผิดพลาด: {message}",
serverOffline: "เซิร์ฟเวอร์ OmniRoute ออฟไลน์ เริ่มด้วย: omniroute serve",
authRequired: "ต้องการการยืนยันตัวตน ตั้งค่า OMNIROUTE_API_KEY หรือรัน: omniroute setup",
rateLimited: "เกินขีดจำกัดคำขอ ลองใหม่หลังจาก {seconds}วินาที",
timeout: "คำขอหมดเวลาหลังจาก {ms}ms",
success: "เสร็จสิ้น",
yes: "ใช่",
no: "ไม่",
confirm: "คุณแน่ใจหรือไม่? (ใช่/ไม่)",
dryRun: "[จำลอง] จะทำ: {action}",
cancelled: "ยกเลิกแล้ว",
jsonOpt: "ส่งออกเป็น JSON",
yesOpt: "ข้ามการยืนยัน",
},
program: {
description: "OmniRoute — AI Router อัจฉริยะพร้อม Auto Fallback",
version: "แสดงเวอร์ชันและออก",
output: "รูปแบบเอาต์พุต (table, json, jsonl, csv)",
quiet: "ซ่อนเอาต์พุตที่ไม่จำเป็น",
no_color: "ปิดใช้งานเอาต์พุตสี",
timeout: "หมดเวลา HTTP request ในมิลลิวินาที",
api_key: "API Key สำหรับ OmniRoute server",
base_url: "Base URL ของ OmniRoute server",
context: "บริบท/โปรไฟล์ของเซิร์ฟเวอร์สำหรับคำสั่งนี้",
lang: "ตั้งค่าภาษาแสดงผล CLI (แทนที่ OMNIROUTE_LANG)",
},
},
tr: {
common: {
error: "Hata: {message}",
serverOffline: "OmniRoute sunucusu çevrimdışı. Başlatın: omniroute serve",
authRequired:
"Kimlik doğrulama gerekli. OMNIROUTE_API_KEY ayarlayın veya çalıştırın: omniroute setup",
rateLimited: "İstek limiti aşıldı. {seconds}s sonra tekrar deneyin.",
timeout: "İstek {ms}ms sonra zaman aşımına uğradı.",
success: "Tamamlandı.",
yes: "evet",
no: "hayır",
confirm: "Emin misiniz? (evet/hayır)",
dryRun: "[simülasyon] yapılacaktı: {action}",
cancelled: "İptal edildi.",
jsonOpt: "JSON olarak çıktı",
yesOpt: "Onayı atla",
},
program: {
description: "OmniRoute — Otomatik Fallback ile Akıllı AI Yönlendirici",
version: "Sürümü yazdır ve çık",
output: ıktı formatı (table, json, jsonl, csv)",
quiet: "Önemsiz çıktıyı gizle",
no_color: "Renkli çıktıyı devre dışı bırak",
timeout: "HTTP istek zaman aşımı (milisaniye)",
api_key: "OmniRoute sunucusu için API anahtarı",
base_url: "OmniRoute sunucusu temel URL'si",
context: "Bu komut için sunucu bağlamı/profili",
lang: "CLI görüntüleme dilini ayarla (OMNIROUTE_LANG'ı geçersiz kılar)",
},
},
"uk-UA": {
common: {
error: "Помилка: {message}",
serverOffline: "Сервер OmniRoute відключено. Запустіть: omniroute serve",
authRequired:
"Потрібна автентифікація. Встановіть OMNIROUTE_API_KEY або виконайте: omniroute setup",
rateLimited: "Перевищено ліміт запитів. Повторіть через {seconds}с.",
timeout: "Запит завершився через {ms}мс.",
success: "Готово.",
yes: "так",
no: "ні",
confirm: "Ви впевнені? (так/ні)",
dryRun: "[симуляція] виконає: {action}",
cancelled: "Скасовано.",
jsonOpt: "Вивести у форматі JSON",
yesOpt: "Пропустити підтвердження",
},
program: {
description: "OmniRoute — Розумний AI-маршрутизатор з автоматичним перемиканням",
version: "Вивести версію та вийти",
output: "Формат виведення (table, json, jsonl, csv)",
quiet: "Приховати несуттєвий вивід",
no_color: "Вимкнути кольоровий вивід",
timeout: "Тайм-аут HTTP-запитів у мілісекундах",
api_key: "API-ключ для сервера OmniRoute",
base_url: "Базовий URL сервера OmniRoute",
context: "Контекст/профіль сервера для цієї команди",
lang: "Встановити мову відображення CLI (замінює OMNIROUTE_LANG)",
},
},
vi: {
common: {
error: "Lỗi: {message}",
serverOffline: "Máy chủ OmniRoute đang offline. Khởi động với: omniroute serve",
authRequired: "Cần xác thực. Đặt OMNIROUTE_API_KEY hoặc chạy: omniroute setup",
rateLimited: "Đã vượt giới hạn yêu cầu. Thử lại sau {seconds}s.",
timeout: "Yêu cầu hết thời gian sau {ms}ms.",
success: "Xong.",
yes: "có",
no: "không",
confirm: "Bạn có chắc không? (có/không)",
dryRun: "[mô phỏng] sẽ: {action}",
cancelled: "Đã hủy.",
jsonOpt: "Xuất dưới dạng JSON",
yesOpt: "Bỏ qua xác nhận",
},
program: {
description: "OmniRoute — Bộ định tuyến AI thông minh với tự động chuyển đổi dự phòng",
version: "In phiên bản và thoát",
output: "Định dạng đầu ra (table, json, jsonl, csv)",
quiet: "Ẩn đầu ra không cần thiết",
no_color: "Tắt đầu ra màu sắc",
timeout: "Thời gian chờ yêu cầu HTTP tính bằng mili giây",
api_key: "Khóa API cho máy chủ OmniRoute",
base_url: "URL cơ sở của máy chủ OmniRoute",
context: "Bối cảnh/hồ sơ máy chủ cho lệnh này",
lang: "Đặt ngôn ngữ hiển thị CLI (ghi đè OMNIROUTE_LANG)",
},
},
"zh-CN": {
common: {
error: "错误:{message}",
serverOffline: "OmniRoute 服务器已离线。请启动omniroute serve",
authRequired: "需要认证。请设置 OMNIROUTE_API_KEY 或运行omniroute setup",
rateLimited: "请求超出限制。请在 {seconds}s 后重试。",
timeout: "请求在 {ms}ms 后超时。",
success: "完成。",
yes: "是",
no: "否",
confirm: "确定吗?(是/否)",
dryRun: "【模拟】将执行:{action}",
cancelled: "已取消。",
jsonOpt: "以 JSON 格式输出",
yesOpt: "跳过确认",
},
program: {
description: "OmniRoute — 具有自动故障转移的智能 AI 路由器",
version: "打印版本并退出",
output: "输出格式table, json, jsonl, csv",
quiet: "禁止非必要输出",
no_color: "禁用彩色输出",
timeout: "HTTP 请求超时(毫秒)",
api_key: "OmniRoute 服务器的 API 密钥",
base_url: "OmniRoute 服务器的基础 URL",
context: "此命令使用的服务器上下文/配置文件",
lang: "设置 CLI 显示语言(覆盖 OMNIROUTE_LANG",
},
},
};
// Languages with no translation in this script — will be created as empty objects
// All keys fall back to `en` via i18n.mjs's fallback mechanism.
const SCAFFOLD_ONLY = ["bn", "gu", "he", "in", "mr", "ms", "phi", "sw", "ta", "te", "ur"];
let created = 0;
let skipped = 0;
for (const locale of locales) {
const { code } = locale;
if (code === "en" || code === "pt-BR") {
skipped++;
continue;
}
const filePath = join(LOCALES_DIR, `${code}.json`);
if (existsSync(filePath) && !FORCE) {
skipped++;
continue;
}
const translations = TRANSLATIONS[code] || {};
const content = Object.keys(translations).length > 0 ? translations : {};
writeFileSync(filePath, JSON.stringify(content, null, 2) + "\n", "utf8");
console.log(`${code.padEnd(8)} ${locale.english}`);
created++;
}
console.log(`\nGenerated: ${created} | Skipped (already exist): ${skipped}`);

View File

@@ -67,6 +67,20 @@ function loadEnvFile() {
loadEnvFile();
// Apply --lang before Commander parses (program descriptions call t() during setup)
{
const langIdx = process.argv.findIndex((a) => a === "--lang");
const langArg = langIdx >= 0 ? process.argv[langIdx + 1] : null;
const langEnv = process.env.OMNIROUTE_LANG;
const chosen = langArg || langEnv;
if (chosen) {
const { setLocale } = await import(
pathToFileURL(join(ROOT, "bin", "cli", "i18n.mjs")).href
);
setLocale(chosen);
}
}
// Register update notifier — checks npm once per 24h, notifies on exit via stderr.
const _pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
const _notifier = updateNotifier({ pkg: _pkg, updateCheckInterval: 1000 * 60 * 60 * 24 });

View File

@@ -243,6 +243,75 @@ python3 scripts/i18n/i18n_autotranslate.py \
- Sends paragraphs to LLM with technical translation system prompt
- Supports all 30 languages
## CLI i18n
The `omniroute` CLI has its own i18n layer separate from the Next.js dashboard.
### How it works
- Every user-facing string in CLI commands goes through `t("module.key", vars)` from `bin/cli/i18n.mjs`.
- Catalogs are JSON files in `bin/cli/locales/` — 42 ship out-of-the-box.
- Locale falls back to `en` for any missing key, so partial translations are valid.
- The source of truth for available locales is `config/i18n.json` (shared with the dashboard).
### Locale selection
Detection order (first match wins):
| Priority | Source | Example |
| -------- | ------------------------ | --------------------------------------- |
| 1 | `--lang` flag | `omniroute --lang de status` |
| 2 | `OMNIROUTE_LANG` env var | `OMNIROUTE_LANG=ja omniroute providers` |
| 3 | `LC_ALL` system env | auto-detected from terminal locale |
| 4 | `LC_MESSAGES` system env | auto-detected from terminal locale |
| 5 | `LANG` system env | auto-detected from terminal locale |
| 6 | Fallback | `en` |
Locale codes with underscores (`pt_BR`) are normalized to hyphen form (`pt-BR`).
Locale codes are validated against `/^[a-zA-Z0-9-]+$/` — path traversal is rejected.
### Saving a language preference
```bash
# Set language and save to ~/.omniroute/.env (persists across sessions)
omniroute config lang set pt-BR
# View current language
omniroute config lang get
# List all 42 available languages
omniroute config lang list
# JSON output
omniroute config lang list --output json
```
The saved preference is written atomically to `~/.omniroute/.env` and is loaded by the
CLI bootstrap before any command runs.
### One-time override
```bash
# Override for one command only (not persisted)
omniroute --lang de providers list
```
Note: the `--lang` flag does NOT write to the env file — it only affects the current
invocation. Use `config lang set` to persist.
### Available locales
42 locale files ship in `bin/cli/locales/`. Full translations: `en`, `pt-BR`.
Scaffold-only (all keys fall back to `en`): `bn`, `gu`, `he`, `in`, `mr`, `ms`, `phi`, `sw`, `ta`, `te`, `ur`.
All other 29 locales have `common` + `program` keys translated.
### Adding a new CLI locale
1. Add the locale entry to `config/i18n.json`.
2. Run `node bin/cli/scripts/generate-locales.mjs` — creates the locale file.
3. Translate the keys (or leave as `{}` for en-fallback scaffold).
4. PRs must add strings to `en.json` and `pt-BR.json`; other files are best-effort.
## Validation & QA
### validate_translation.py

View File

@@ -0,0 +1,275 @@
import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, readFileSync, existsSync, writeFileSync, mkdirSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
let tmpDir: string;
let origDataDir: string | undefined;
let origOmniLang: string | undefined;
test.before(() => {
tmpDir = mkdtempSync(join(tmpdir(), "omniroute-lang-test-"));
origDataDir = process.env.DATA_DIR;
origOmniLang = process.env.OMNIROUTE_LANG;
process.env.DATA_DIR = tmpDir;
delete process.env.OMNIROUTE_LANG;
});
test.after(() => {
if (origDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = origDataDir;
if (origOmniLang === undefined) delete process.env.OMNIROUTE_LANG;
else process.env.OMNIROUTE_LANG = origOmniLang;
try {
rmSync(tmpDir, { recursive: true, force: true });
} catch {}
});
// ── i18n.mjs security ─────────────────────────────────────────────────────────
test("normalize rejeita path traversal com ../", async () => {
const { resetForTests, setLocale, getLocale } = await import("../../bin/cli/i18n.mjs");
resetForTests();
setLocale("../etc/passwd");
const locale = getLocale();
assert.equal(locale, "en", `Deveria ter fallback para en, obteve: ${locale}`);
resetForTests();
});
test("normalize rejeita código com caracteres especiais", async () => {
const { resetForTests, setLocale, getLocale } = await import("../../bin/cli/i18n.mjs");
resetForTests();
setLocale("pt;rm -rf /");
const locale = getLocale();
assert.equal(locale, "en", `Deveria ter fallback para en, obteve: ${locale}`);
resetForTests();
});
test("normalize aceita código válido com hífen (pt-BR)", async () => {
const { resetForTests, setLocale, getLocale } = await import("../../bin/cli/i18n.mjs");
resetForTests();
setLocale("pt-BR");
const locale = getLocale();
assert.equal(locale, "pt-BR");
resetForTests();
});
test("normalize converte underscore para hífen (pt_BR → pt-BR)", async () => {
const { resetForTests, setLocale, getLocale } = await import("../../bin/cli/i18n.mjs");
resetForTests();
setLocale("pt_BR");
const locale = getLocale();
assert.equal(locale, "pt-BR");
resetForTests();
});
// ── config lang get ────────────────────────────────────────────────────────────
test("runConfigLangGetCommand retorna 0 e imprime o locale ativo", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangGetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const output: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => output.push(args.join(" "));
try {
const code = await runConfigLangGetCommand({});
assert.equal(code, 0);
assert.ok(
output.some((l) => l.includes("en")),
`Esperava 'en' no output: ${output.join("|")}`
);
} finally {
console.log = origLog;
resetForTests();
}
});
test("runConfigLangGetCommand --json retorna objeto com code e name", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangGetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const chunks: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => chunks.push(args.join(" "));
try {
await runConfigLangGetCommand({ json: true });
const parsed = JSON.parse(chunks.join(""));
assert.ok("code" in parsed, "JSON deve ter campo code");
assert.ok("name" in parsed, "JSON deve ter campo name");
assert.equal(parsed.code, "en");
} finally {
console.log = origLog;
resetForTests();
}
});
// ── config lang list ───────────────────────────────────────────────────────────
test("runConfigLangListCommand --json lista locales com campo active", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangListCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const chunks: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => chunks.push(args.join(" "));
try {
const exitCode = await runConfigLangListCommand({ json: true });
assert.equal(exitCode, 0);
const arr = JSON.parse(chunks.join(""));
assert.ok(Array.isArray(arr), "Deve retornar array");
assert.ok(arr.length >= 2, "Deve ter ao menos en e pt-BR");
const enEntry = arr.find((l: any) => l.code === "en");
assert.ok(enEntry, "Deve ter entrada para en");
assert.ok("active" in enEntry, "Deve ter campo active");
assert.equal(enEntry.active, true, "en deve ser active quando locale for en");
} finally {
console.log = origLog;
resetForTests();
}
});
// ── config lang set ────────────────────────────────────────────────────────────
test("runConfigLangSetCommand salva locale no .env e chama setLocale imediatamente", async () => {
const { resetForTests, setLocale, getLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangSetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const chunks: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => chunks.push(args.join(" "));
try {
const exitCode = await runConfigLangSetCommand("pt-BR", {});
assert.equal(exitCode, 0);
const envPath = join(tmpDir, ".env");
assert.ok(existsSync(envPath), ".env deve existir após set");
const content = readFileSync(envPath, "utf8");
assert.ok(content.includes("OMNIROUTE_LANG=pt-BR"), "Deve persistir OMNIROUTE_LANG=pt-BR");
assert.equal(getLocale(), "pt-BR", "setLocale deve ter sido chamado imediatamente em-processo");
} finally {
console.log = origLog;
resetForTests();
}
});
test("runConfigLangSetCommand retorna 1 para código desconhecido", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangSetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const errors: string[] = [];
const origErr = console.error;
console.error = (...args: unknown[]) => errors.push(args.join(" "));
try {
const exitCode = await runConfigLangSetCommand("xx-NONEXISTENT", {});
assert.equal(exitCode, 1);
} finally {
console.error = origErr;
resetForTests();
}
});
test("runConfigLangSetCommand retorna 1 quando code não fornecido", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangSetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const errors: string[] = [];
const origErr = console.error;
console.error = (...args: unknown[]) => errors.push(args.join(" "));
try {
const exitCode = await runConfigLangSetCommand(undefined, {});
assert.equal(exitCode, 1);
} finally {
console.error = origErr;
resetForTests();
}
});
test("runConfigLangSetCommand retorna 0 quando já ativo (sem --force)", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangSetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const chunks: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => chunks.push(args.join(" "));
try {
const exitCode = await runConfigLangSetCommand("en", {});
assert.equal(exitCode, 0, "Deve retornar 0 mesmo quando locale já está ativo");
} finally {
console.log = origLog;
resetForTests();
}
});
test("runConfigLangSetCommand --force salva mesmo quando locale já ativo", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangSetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const chunks: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => chunks.push(args.join(" "));
try {
const exitCode = await runConfigLangSetCommand("en", { force: true });
assert.equal(exitCode, 0, "Com --force deve retornar 0");
const envPath = join(tmpDir, ".env");
if (existsSync(envPath)) {
const content = readFileSync(envPath, "utf8");
assert.ok(content.includes("OMNIROUTE_LANG=en"), "Deve ter gravado OMNIROUTE_LANG=en");
}
} finally {
console.log = origLog;
resetForTests();
}
});
// ── upsertEnvLine (testado indiretamente via set) ─────────────────────────────
test("runConfigLangSetCommand atualiza chave sem duplicar quando já existe", async () => {
const { resetForTests, setLocale } = await import("../../bin/cli/i18n.mjs");
const { runConfigLangSetCommand } = await import("../../bin/cli/commands/config.mjs");
resetForTests();
setLocale("en");
const envPath = join(tmpDir, ".env");
writeFileSync(envPath, "OMNIROUTE_LANG=de\n", "utf8");
const chunks: string[] = [];
const origLog = console.log;
console.log = (...args: unknown[]) => chunks.push(args.join(" "));
try {
const exitCode = await runConfigLangSetCommand("pt-BR", { force: true });
assert.equal(exitCode, 0);
const content = readFileSync(envPath, "utf8");
assert.ok(content.includes("OMNIROUTE_LANG=pt-BR"), "Deve ter atualizado para pt-BR");
const matches = content.match(/OMNIROUTE_LANG=/g);
assert.equal(matches?.length, 1, "Deve ter exatamente uma ocorrência de OMNIROUTE_LANG");
} finally {
console.log = origLog;
resetForTests();
}
});