Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<img src="https://img.shields.io/badge/Version-v1.24.1-6366F1" alt="v1.24.1">
<img src="https://img.shields.io/badge/Storage-IndexedDB_v8-F59E0B" alt="IndexedDB v8">
<img src="https://img.shields.io/badge/PWA-v3.0-5BB974?logo=pwa" alt="PWA v3.0">
<img src="https://img.shields.io/badge/i18n-19_locales-2854_keys-0EA5E9" alt="i18n 19 locales — 2854 keys">
<img src="https://img.shields.io/badge/i18n-19_locales-2859_keys-0EA5E9" alt="i18n 19 locales — 2859 keys">
<img src="https://img.shields.io/badge/Tests-5807%2B_%2F_527_files-22C55E" alt="5807+ tests / 527 files">
<img src="https://img.shields.io/codecov/c/github/qnbs/WorldScript-Studio?logo=codecov&label=Coverage" alt="Codecov Coverage">
<img src="https://img.shields.io/badge/License-MIT-22C55E" alt="License MIT">
Expand Down Expand Up @@ -351,7 +351,7 @@ One-click encrypted export of your entire project library from **Settings → Da

### 🌐 Full Multi-Language Support

Shipped UI locales with **2854 i18n keys** across all 19 languages — zero hardcoded user-facing strings:
Shipped UI locales with **2859 i18n keys** across all 19 languages — zero hardcoded user-facing strings:

- 🇩🇪 **German** (Deutsch)
- 🇬🇧 **English**
Expand Down Expand Up @@ -460,7 +460,7 @@ The Settings → AI panel shows a live GPU status badge with adapter details and
| **PDF Export** | jsPDF | Client-side, configurable PDF document generation |
| **Document Export** | docx + jszip | Word-compatible `.docx` generation (lazy-loaded) |
| **PWA** | Service Worker + Web App Manifest v3 | Offline support, installability, Workbox chunking |
| **i18n** | Custom React Context (`I18nContext.tsx`) | 2854 keys × 19 locales (de/en/es/fr/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta); EN fallback; `localStorage` persistence |
| **i18n** | Custom React Context (`I18nContext.tsx`) | 2859 keys × 19 locales (de/en/es/fr/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta); EN fallback; `localStorage` persistence |
| **Testing** | Vitest 4.x (5807+ tests / 527 files) + Playwright E2E | Unit/integration + cross-browser E2E; Stryker mutation (manual workflow) |
| **Code Quality** | Biome (lint + format) + TypeScript 7 (tsgo) strict | `--error-on-warnings` in CI; zero `any` policy |
| **Visualization** | Force-directed graph | Interactive character relationship network |
Expand Down Expand Up @@ -659,7 +659,7 @@ The main pipeline is [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Opt
**Current test metrics (2026-07-28):**
- **5807+ unit tests** across **527 test files** — all passing
- Coverage thresholds: lines ≥ 74 · branches ≥ 60 · functions ≥ 67 · statements ≥ 72 — enforced in CI (see Codecov badge for live metrics)
- i18n: **2854 keys × 19 locales** (en/de/fr/es/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta)
- i18n: **2859 keys × 19 locales** (en/de/fr/es/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta)

**CI-cloud-first workflow (recommended):** On constrained hardware run **`pnpm run lint && pnpm run i18n:check && pnpm run typecheck`** locally, then push and let CI handle coverage, E2E, Lighthouse, and Stryker. Authoritative numbers come from CI artifacts (Codecov, JUnit). After CI goes green, update the README badges and `AUDIT.md` quality-gate line from the reported metrics. See **[`docs/CI.md`](docs/CI.md) § Cloud CI-first vs local development** for the full post-merge doc-update checklist.

Expand Down
240 changes: 168 additions & 72 deletions components/settings/OpenRouterSection.tsx

Large diffs are not rendered by default.

19 changes: 12 additions & 7 deletions locales/ar/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,16 @@
"settings.ai.temperature.creative": "2 – إبداعي",
"settings.ai.temperature.precise": "0 – دقيق",
"settings.ai.testConnection": "اختبار الاتصال",
"settings.ai.testError.noApiKey": "لم يتم تعيين مفتاح API لـ {{provider}}",
"settings.ai.testError.httpError": "HTTP {{status}}",
"settings.ai.testError.timeout": "انتهت مهلة الاتصال ({{url}})",
"settings.ai.testError.unreachable": "غير قابل للوصول ({{url}})",
"settings.ai.testError.pluginUnavailable": "شبكة الخادم المحلي غير متوفرة: فشل تحميل مكوّن HTTP لسطح المكتب.",
"settings.ai.testError.desktopRequired": "تتوفر Ollama والخوادم المحلية المتوافقة مع OpenAI فقط في تطبيق سطح المكتب. تحظر المتصفحات الاتصالات المباشرة من صفحات الويب بـ localhost (CORS وPrivate Network Access).",
"settings.ai.testError.backendProxyRequired": "يتطلب Claude وكيل خلفية (قيود CORS)",
"settings.ai.testError.desktopRequired": "تتوفر Ollama والخوادم المحلية المتوافقة مع OpenAI فقط في تطبيق سطح المكتب. تحظر المتصفحات الاتصالات المباشرة من صفحات الويب بـ localhost (CORS وPrivate Network Access).",
"settings.ai.testError.httpError": "HTTP {{status}}",
"settings.ai.testError.noApiKey": "لم يتم تعيين مفتاح API لـ {{provider}}",
"settings.ai.testError.noWebgpu": "WebGPU غير متوفر في هذا المتصفح — يحتاج WebLLM إلى WebGPU (جرّب Chrome/Edge أو فعّل العلامات).",
"settings.ai.testError.unknownProvider": "مزوّد غير معروف",
"settings.ai.testError.pluginUnavailable": "شبكة الخادم المحلي غير متوفرة: فشل تحميل مكوّن HTTP لسطح المكتب.",
"settings.ai.testError.timeout": "انتهت مهلة الاتصال ({{url}})",
"settings.ai.testError.unexpected": "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى.",
"settings.ai.testError.unknownProvider": "مزوّد غير معروف",
"settings.ai.testError.unreachable": "غير قابل للوصول ({{url}})",
"settings.ai.title": "إعداد الذكاء الاصطناعي",
"settings.ai.transformers.hint": "يشغّل أي نموذج HuggingFace متوافق مع Xenova محليًا عبر WebAssembly أو WebGPU.",
"settings.ai.transformers.modelId": "معرّف النموذج ‏(HuggingFace)",
Expand Down Expand Up @@ -599,6 +599,7 @@
"settings.openRouter.enabledHint": "When enabled, OpenRouter is preferred for cloud AI calls unless the active mode forbids cloud.",
"settings.openRouter.errors.generic": "An unexpected OpenRouter error occurred",
"settings.openRouter.freeModel.deepseekR1": "DeepSeek R1 (free)",
"settings.openRouter.freeModel.fallbackNotice": "يتم عرض قائمة احتياطية ثابتة — كتالوج OpenRouter المباشر غير متاح حاليًا.",
"settings.openRouter.freeModel.gemma327b": "Gemma 3 27B IT (free)",
"settings.openRouter.freeModel.llama3370b": "Llama 3.3 70B Instruct (free)",
"settings.openRouter.freeModel.mistral7b": "Mistral 7B Instruct (free)",
Expand All @@ -621,14 +622,18 @@
"settings.openRouter.modelFetch.failed": "Could not load model catalog",
"settings.openRouter.modelFetch.loading": "Loading model catalog…",
"settings.openRouter.modelPlaceholder": "deepseek/deepseek-r1:free",
"settings.openRouter.modelUnavailable": "هذا النموذج غير موجود في الكتالوج الحالي.",
"settings.openRouter.paidModelNote": "Paid models require credits on your OpenRouter account.",
"settings.openRouter.policyBlocked.localOnly": "OpenRouter is blocked because “Local storage only” is on. Turn it off in Privacy & Data to allow cloud AI.",
"settings.openRouter.policyBlocked.mode": "OpenRouter is unavailable while the AI mode is Local or Eco. Switch to Hybrid or Cloud in AI & Models to use it.",
"settings.openRouter.preferredModel": "Preferred Model",
"settings.openRouter.refreshModels": "تحديث النماذج",
"settings.openRouter.refreshModelsAriaLabel": "تحديث كتالوج نماذج OpenRouter",
"settings.openRouter.resetCircuit": "Reset",
"settings.openRouter.resetCircuitAria": "Reset OpenRouter circuit breaker",
"settings.openRouter.rpmLabel": "Requests / min (last 60 s)",
"settings.openRouter.saveKey": "Save",
"settings.openRouter.switchToAvailableModel": "التبديل إلى نموذج مجاني متاح",
"settings.openRouter.testConnection": "Test connection",
"settings.openRouter.testConnectionFailed": "Connection failed",
"settings.openRouter.testConnectionOk": "Connection successful",
Expand Down
19 changes: 12 additions & 7 deletions locales/de/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,16 @@
"settings.ai.temperature.creative": "2 – Kreativ",
"settings.ai.temperature.precise": "0 – Präzise",
"settings.ai.testConnection": "Verbindung testen",
"settings.ai.testError.noApiKey": "Kein {{provider}} API-Schlüssel gesetzt",
"settings.ai.testError.httpError": "HTTP {{status}}",
"settings.ai.testError.timeout": "Zeitüberschreitung bei der Verbindung ({{url}})",
"settings.ai.testError.unreachable": "Nicht erreichbar ({{url}})",
"settings.ai.testError.pluginUnavailable": "Lokale Serververnetzung nicht verfügbar: Das Desktop-HTTP-Plugin konnte nicht geladen werden.",
"settings.ai.testError.desktopRequired": "Ollama und lokale OpenAI-kompatible Server sind nur in der Desktop-App verfügbar. Browser blockieren direkte Verbindungen von Webseiten zu localhost (CORS und Private Network Access).",
"settings.ai.testError.backendProxyRequired": "Claude benötigt einen Backend-Proxy (CORS-Einschränkung)",
"settings.ai.testError.desktopRequired": "Ollama und lokale OpenAI-kompatible Server sind nur in der Desktop-App verfügbar. Browser blockieren direkte Verbindungen von Webseiten zu localhost (CORS und Private Network Access).",
"settings.ai.testError.httpError": "HTTP {{status}}",
"settings.ai.testError.noApiKey": "Kein {{provider}} API-Schlüssel gesetzt",
"settings.ai.testError.noWebgpu": "WebGPU in diesem Browser nicht verfügbar — WebLLM benötigt WebGPU (Chrome/Edge verwenden oder Flags aktivieren).",
"settings.ai.testError.unknownProvider": "Unbekannter Anbieter",
"settings.ai.testError.pluginUnavailable": "Lokale Serververnetzung nicht verfügbar: Das Desktop-HTTP-Plugin konnte nicht geladen werden.",
"settings.ai.testError.timeout": "Zeitüberschreitung bei der Verbindung ({{url}})",
"settings.ai.testError.unexpected": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
"settings.ai.testError.unknownProvider": "Unbekannter Anbieter",
"settings.ai.testError.unreachable": "Nicht erreichbar ({{url}})",
"settings.ai.title": "KI-Konfiguration",
"settings.ai.transformers.hint": "Führt jedes Xenova-kompatible HuggingFace-Modell lokal via WebAssembly oder WebGPU aus.",
"settings.ai.transformers.modelId": "Modell-ID (HuggingFace)",
Expand Down Expand Up @@ -599,6 +599,7 @@
"settings.openRouter.enabledHint": "Wenn aktiviert, wird OpenRouter für Cloud-KI-Aufrufe bevorzugt, sofern der aktive Modus Cloud nicht verbietet.",
"settings.openRouter.errors.generic": "Ein unerwarteter OpenRouter-Fehler ist aufgetreten",
"settings.openRouter.freeModel.deepseekR1": "DeepSeek R1 (gratis)",
"settings.openRouter.freeModel.fallbackNotice": "Es wird die statische Fallback-Liste angezeigt — der Live-Modellkatalog von OpenRouter ist gerade nicht verfügbar.",
"settings.openRouter.freeModel.gemma327b": "Gemma 3 27B IT (gratis)",
"settings.openRouter.freeModel.llama3370b": "Llama 3.3 70B Instruct (gratis)",
"settings.openRouter.freeModel.mistral7b": "Mistral 7B Instruct (gratis)",
Expand All @@ -621,14 +622,18 @@
"settings.openRouter.modelFetch.failed": "Modellkatalog konnte nicht geladen werden",
"settings.openRouter.modelFetch.loading": "Modellkatalog wird geladen …",
"settings.openRouter.modelPlaceholder": "Modell auswählen oder suchen",
"settings.openRouter.modelUnavailable": "Dieses Modell ist nicht im aktuellen Katalog enthalten.",
"settings.openRouter.paidModelNote": "Kostenpflichtige Modelle benötigen Guthaben auf deinem OpenRouter-Konto.",
"settings.openRouter.policyBlocked.localOnly": "OpenRouter ist blockiert, weil „Nur lokaler Speicher“ aktiviert ist. Deaktiviere es unter „Datenschutz & Daten“, um Cloud-KI zu erlauben.",
"settings.openRouter.policyBlocked.mode": "OpenRouter ist im KI-Modus „Lokal“ oder „Eco“ nicht verfügbar. Wechsle unter „KI & Modelle“ zu Hybrid oder Cloud, um es zu nutzen.",
"settings.openRouter.preferredModel": "Bevorzugtes Modell",
"settings.openRouter.refreshModels": "Modelle aktualisieren",
"settings.openRouter.refreshModelsAriaLabel": "OpenRouter-Modellkatalog aktualisieren",
"settings.openRouter.resetCircuit": "Zurücksetzen",
"settings.openRouter.resetCircuitAria": "OpenRouter-Schutzschalter zurücksetzen",
"settings.openRouter.rpmLabel": "Anfragen / Min (letzte 60 s)",
"settings.openRouter.saveKey": "Speichern",
"settings.openRouter.switchToAvailableModel": "Zu einem verfügbaren Free-Modell wechseln",
"settings.openRouter.testConnection": "Verbindung testen",
"settings.openRouter.testConnectionFailed": "Verbindung fehlgeschlagen",
"settings.openRouter.testConnectionOk": "Verbindung erfolgreich",
Expand Down
2 changes: 1 addition & 1 deletion locales/el/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@
"proforge.progress.metric.totalTime": "Συνολικός χρόνος",
"proforge.progress.metric.totalTokens": "Σύνολο Μαρκών",
"proforge.progress.noneRunning": "Δεν λειτουργεί αγωγός.",
"proforge.progress.stageDetails": "Λεπτομέρειες σκηνής: {{stage}}",
"proforge.progress.stageDetails": "Λεπτομέρειες σταδίου: {{stage}}",
"proforge.progress.statusLabel": "Κατάσταση",
"proforge.progress.totals": "Σύνολα αγωγών",
"proforge.review.accept": "Αποδέχομαι",
Expand Down
Loading
Loading