Skip to content

i18n#3

Merged
Erik05Master merged 2 commits into
masterfrom
i18n
Jul 8, 2026
Merged

i18n#3
Erik05Master merged 2 commits into
masterfrom
i18n

Conversation

@Erik05Master

@Erik05Master Erik05Master commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Die App unterstützt jetzt mehrere Sprachen mit dynamischer Sprachauswahl.
    • Viele Bereiche wurden lokalisiert, darunter Navigation, Dashboard, Inventar, Einstellungen, Benachrichtigungen, Nutzerverwaltung und API-Keys.
  • Bug Fixes

    • Fehlermeldungen und Bestätigungsdialoge erscheinen jetzt konsistent in der gewählten Sprache.
    • Spracheinstellungen werden automatisch validiert und auf eine unterstützte Standardsprache zurückgesetzt.
    • E-Mail-, Discord- und Formulartexte wurden vereinheitlicht und sind nun übersetzbar.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Erik05Master, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a3d1152e-0e29-43b9-ad98-fb8e04520b73

📥 Commits

Reviewing files that changed from the base of the PR and between 5f4f689 and d1edf97.

📒 Files selected for processing (1)
  • app/routes/shop_rules.py

Walkthrough

Die PR führt ein vollständiges i18n-System ein (app/i18n.py mit YAML-Übersetzungen für Deutsch/Englisch), verdrahtet es in der App-Initialisierung (Locale-Erkennung, Jinja-Global t, Kontext-Globals), und ersetzt sämtliche fest kodierten Texte in Backend-Services, Routen und über 30 Templates durch übersetzbare t(...)-Aufrufe.

Changes

i18n-Einführung

Layer / File(s) Summary
i18n-Kerninfrastruktur und App-Wiring
app/i18n.py, app/__init__.py
Neues Modul mit get_locale()/t()/DEFAULT_LOCALE/SUPPORTED_LOCALES; App lädt Locale pro Request, registriert t als Jinja-Global und stellt current_locale/available_locales bereit.
Backend-Services lokalisiert
app/alert_service.py, app/notification_service.py, app/settings_service.py
Alert-/Notification-Nachrichten, E-Mail-/Discord-Inhalte und Betreffzeilen nutzen t(...); neuer Default app.language: "en".
Routen: Flash-/Validierungsmeldungen
app/routes/api_keys.py, auth.py, inventory.py, manufacturers.py, prints.py, settings.py, shop_rules.py, users.py
Alle Flash-, Validierungs- und Fehlermeldungen in Routen ersetzen Hardcoded-Strings durch t(...); settings.py speichert zusätzlich die gewählte Sprache.
Auth-, Fehler- und Basis-Templates
app/templates/auth/*.html, base.html, errors/*.html
Login/Setup, Navigation und 403/404/500-Seiten nutzen current_locale und t(...).
Dashboard- und API-Keys-Templates
app/templates/dashboard/index.html, api_keys/index.html
KPI-, Alert-, Scheduler- und Token-Texte inkl. Plural-Logik übersetzt.
Inventory-Templates
app/templates/inventory/*.html
Detail-, Listen- und Formularvorlagen (Spool/Filament/Purchase/Shoplink/Snapshot) vollständig lokalisiert.
Manufacturers- und Prints-Templates
app/templates/manufacturers/*.html, prints/*.html
Listen, Details und Formulare lokalisiert.
Settings-, Shop-Rules- und Users-Templates
app/templates/settings/index.html, shop_rules/*.html, users/*.html
Sektionen, Regel-Editor/Test-UI und Benutzerverwaltung lokalisiert.
Übersetzungsdateien
app/translations/de.yaml, en.yaml
Neue vollständige deutsche und englische Übersetzungskataloge für die gesamte Anwendung.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

  • SytxLabs/SpoolBeacon#1: Ändert dieselbe API-Keys-Code-Basis (app/routes/api_keys.py, app/templates/api_keys/index.html), die hier auf t(...)-Übersetzungsschlüssel umgestellt wird.

Poem

Ein Hase hoppelt durch den Code,
übersetzt jede Fehlermeldung en gros. 🐇
Von "Save" zu "Speichern", ganz geschwind,
jetzt spricht die App wie's Nutzer sind.
DE und EN, Seite an Seite –
ich hopple weiter, voller Freude! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive Der Titel beschreibt das Thema nur sehr allgemein und nennt keine konkrete Änderung im umfangreichen i18n-Umfang. Wähle einen präzisen Titel, z. B. „App auf i18n-Übersetzungen umstellen“ oder „Internationalisierung für UI, Fehler und Benachrichtigungen hinzufügen“.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch i18n

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Erik05Master
Erik05Master merged commit 333b176 into master Jul 8, 2026
3 of 4 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
app/routes/settings.py (1)

215-216: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hardcodierte englische Strings in test_email() nicht lokalisiert.

subject und body in test_email() sind noch fest kodierte englische Strings, während der HTML-Body bereits lokalisiert wird. Nutzer mit deutscher Spracheinstellung sehen eine englische Betreffzeile und Klartext-E-Mail.

🌐 Vorgeschlagene Lokalisierung
-    subject="SpoolBeacon Test Email",
-    body="SpoolBeacon test message: email integration is working.",
+    subject=t("settings.email_test.subject"),
+    body=t("settings.email_test.body"),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/settings.py` around lines 215 - 216, The test_email() response
still hardcodes English text in the email subject and plain-text body while the
HTML body is localized. Update the subject and body values in test_email() to
use the same localization pattern as the HTML content, using the existing
translation mechanism and related message keys so the email is fully localized
for the user’s language.
app/templates/inventory/spool_label.html (1)

2-2: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

lang-Attribut ist hartkodiert auf "en".

Das lang="en" im <html>-Tag sollte die aktuelle Sprache des Nutzers widerspiegeln. Da dieses Template nicht base.html erweitert, muss die Locale direkt referenziert werden. Der Stack-Kontext erwähnt, dass current_locale als Kontext-Global verfügbar ist.

Alternativ kann get_locale() verwendet werden, falls es als Jinja-Global registriert ist.

🛡️ Proposed fix
-<html lang="en" class="h-full">
+<html lang="{{ current_locale }}" class="h-full">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/inventory/spool_label.html` at line 2, The `<html>` tag in the
spool label template is hardcoded with `lang="en"`, so update the
`spool_label.html` markup to derive the language from the active locale instead
of a fixed value. Use the available locale source mentioned in the stack
context, such as `current_locale`, or `get_locale()` if that Jinja global is
registered, and wire it directly into the `html` language attribute so the
template reflects the user’s current language.
app/templates/inventory/snapshot_history.html (1)

55-56: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hardcodiertes Datumsformat ist nicht lokalisierbar.

strftime('%d.%m.%Y') verwendet das deutsche Datumsformat (TT.MM.JJJJ). Für englischsprachige Nutzer sollte das Format %m/%d/%Y oder ein lokalisiertes Äquivalent verwendet werden. Da dieser PR die Internationalisierung einführt, sollte auch die Datumsformatierung lokalisierbar sein.

Eine Möglichkeit wäre, das Format über die YAML-Übersetzungsdateien bereitzustellen und strftime damit zu parametrisieren, z. B. {{ snap.captured_at.strftime(t('common.date_format')) }}.

🛡️ Vorschlag zur Lokalisierung des Datumsformats
- <span class="text-gray-300">{{ snap.captured_at.strftime('%d.%m.%Y') }}</span>
+ <span class="text-gray-300">{{ snap.captured_at.strftime(t('common.date_format')) }}</span>

Und in den YAML-Dateien ergänzen:

# de.yaml
common:
  date_format: "%d.%m.%Y"

# en.yaml
common:
  date_format: "%m/%d/%Y"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/inventory/snapshot_history.html` around lines 55 - 56, The date
shown in the snapshot history template is hardcoded with a German-only format,
so make the date formatting localization-aware in the snapshot rendering path.
Update the `snap.captured_at.strftime(...)` usage in `snapshot_history.html` to
read the format string from translations (for example via
`t('common.date_format')`), and add matching localized `common.date_format`
values in the YAML language files so `snap.captured_at` renders correctly for
each locale.
🧹 Nitpick comments (4)
app/__init__.py (1)

46-56: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

DB-Abfrage bei jedem Request für Locale-Erkennung — Setting ändert sich selten.

load_locale fragt bei jedem nicht-static Request die Datenbank nach app.language ab. Da sich die Spracheinstellung selten ändert, fügt dies jeder Anfrage eine unnötige Datenbankabfrage hinzu. Ein einfacher TTL-Cache oder ein In-Memory-Cache würde die Latenz reduzieren.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/__init__.py` around lines 46 - 56, The load_locale before_request hook is
doing a database lookup on every non-static request for app.language, which adds
avoidable latency. Update the load_locale logic to use a small TTL or in-memory
cache for the locale value instead of querying through get_db and session.scalar
each time, while still falling back to DEFAULT_LOCALE when the cached or loaded
value is not in SUPPORTED_LOCALES.
app/routes/inventory.py (1)

1288-1298: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Konkatenation übersetzter Textfragmente vermeiden.

msg wird aus bis zu drei separat übersetzten Teilstrings zusammengesetzt (price_captured + optional price_availability_suffix + optional target_reached_suffix). Das ist ein klassisches i18n-Antipattern: Satzbau und Wortstellung unterscheiden sich je nach Sprache, wodurch die konkatenierte Nachricht in anderen Locales grammatikalisch falsch oder unpassend formatiert sein kann.

Empfehlung: vollständige Satz-Templates mit optionalen Platzhaltern in der YAML definieren (z. B. via bedingter Formulierung oder separaten, vollständigen Varianten je Kombination), statt Fragmente zur Laufzeit anzuhängen.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/inventory.py` around lines 1288 - 1298, Die Flash-Nachricht in
inventory.py wird derzeit aus mehreren übersetzten Fragmenten zusammengesetzt,
was für i18n problematisch ist. Passe die Logik rund um die Nachrichtenerzeugung
in der Flash-Branch so an, dass statt des schrittweisen Anhängens von
inventory.flash.price_captured, inventory.flash.price_availability_suffix und
inventory.flash.target_reached_suffix vollständige, locale-spezifische
Satzvorlagen verwendet werden. Lege dafür in den Übersetzungen passende
komplette Varianten mit Platzhaltern bzw. kombinationsabhängigen Texten an und
rufe in der aktuellen Flash-Logik nur die jeweils fertige Nachricht ab.
app/templates/settings/index.html (1)

325-327: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

confirm()-Dialoge sollten | tojson verwenden

Beide confirm()-Aufrufe betten Übersetzungstexte in einfachen Anführungszeichen in JS-Strings innerhalb von HTML-Attributen ein. Bei Apostrophen in der Übersetzung bricht der JS-String. Verwende | tojson für robustes Escaping.

♻️ Vorgeschlagene Fixes
-                        onclick="return confirm('{{ t('settings.inventory.spool_code.regenerate_confirm') }}')"
+                        onclick="return confirm({{ t('settings.inventory.spool_code.regenerate_confirm') | tojson }})"
-                            onclick="return confirm('{{ t('settings.backup.import.confirm') }}')"
+                            onclick="return confirm({{ t('settings.backup.import.confirm') | tojson }})"

Also applies to: 377-377

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/settings/index.html` around lines 325 - 327, The confirm()
handlers in the settings template are interpolating translated text directly
into JavaScript strings, which can break when the translation contains
apostrophes. Update the affected onclick confirm calls in the settings page
template to use the translation filter with tojson so the message is safely
escaped in the HTML attribute. Apply the same fix to both confirm() usages
referenced in this template and keep the existing confirm behavior unchanged.
app/templates/prints/index.html (1)

57-57: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

confirm()-Dialog sollte | tojson verwenden

Der Übersetzungstext wird in einfachen Anführungszeichen in einen JS-String innerhalb eines HTML-Attributs eingebettet. Wenn die Übersetzung Apostrophe enthält (z. B. "Don't delete"), bricht der JS-String. Verwende | tojson für robustes Escaping – dieser Pattern wird in rule_form.html:368 bereits korrekt angewendet.

♻️ Vorgeschlagener Fix
-                          onsubmit="return confirm('{{ t('prints.index.delete_confirm') }}')">
+                          onsubmit="return confirm({{ t('prints.index.delete_confirm') | tojson }})">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/prints/index.html` at line 57, The onsubmit confirm handler in
the prints index template embeds a translated string directly into a JavaScript
string, which can break when the text contains apostrophes. Update the confirm
call in the template to use the same escaping pattern already used in
rule_form.html by applying tojson to the translation output, and keep the change
localized to the onsubmit attribute so the confirm dialog remains functionally
identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/i18n.py`:
- Around line 48-56: The t() translation helper can crash requests when a YAML
string contains format placeholders that are missing from kwargs. Update t() to
handle formatting errors safely around value.format(**kwargs), using a fallback
behavior (for example returning the unformatted translation or a safe
error-tolerant render) instead of letting KeyError propagate. Keep the change
localized to t() and preserve the existing locale lookup/fallback behavior in
_lookup/_load.
- Around line 41-45: Scheduled price-warning notifications are always rendered
with the fallback locale because get_locale() in app/i18n.py defaults to
DEFAULT_LOCALE when no request context exists. Update the scheduler flow in
app/scheduler.py and the downstream calls in
price_check_service/alert_service/notification_service to carry the user or job
locale explicitly, or set g.locale before rendering, so the notification
templates use the intended language instead of always English.

In `@app/templates/dashboard/index.html`:
- Around line 2-8: The Scheduler-Status date display in the dashboard template
is using a hardcoded German strftime format, which should be localized. Update
the relevant datetime rendering in the template using the existing translation
helper t() and a locale-specific format key (for example under
common.datetime_format) instead of embedding '%d.%m. %H:%M'. Apply the same
localized formatting anywhere in the dashboard template where the current
hardcoded date format is used, so the output depends on the active locale.

In `@app/templates/inventory/detail.html`:
- Around line 514-526: The `onsubmit` handler in `inventory/detail.html` is
building a JavaScript string from `t('inventory.detail.confirm_delete_spool',
code=spool.spool_code)` in a single-quoted `confirm()` call, which can break out
when `spool_code` contains quotes. Fix it by making the delete confirmation
message safe for JavaScript context, ideally by using a JSON-encoded/JS-escaped
value instead of direct template interpolation, and keep the change localized to
the delete form’s `confirm()` usage.

In `@app/templates/inventory/spool_form.html`:
- Line 19: The confirm() message is being interpolated into a JavaScript string
inside an HTML attribute via t(), which allows JS-context injection when values
like spool.spool_code contain quotes. Update the confirm_delete usage in the
spool_form template to pass the translated string through the JS-safe tojson
filter, and apply the same fix to the matching confirm() calls in the
manufacturers index and manufacturer_detail templates that use mfr.name and
manufacturer.name.

In `@app/templates/shop_rules/index.html`:
- Around line 94-97: The delete confirmation in the shop_rules index template is
unsafe because rule.domain is being interpolated directly into a JavaScript
string inside the onsubmit confirm() call. Update the confirm text generation
around t('shop_rules.index.delete_confirm', domain=rule.domain) so the domain is
passed through a JS-safe escaping/serialization step (not just HTML
autoescaping), or move the string construction out of inline JS entirely. Keep
the delete form behavior the same, but ensure the value used in confirm() cannot
break out of the string and trigger XSS.

In `@app/templates/users/index.html`:
- Around line 78-80: The onsubmit confirm text in the users delete form is being
inserted into a single-quoted JavaScript string without escaping, so a username
can break the handler and inject script. Update the template around the delete
form in users/index.html to serialize the translated confirm message as a safe
JS string literal before passing it to confirm(), using the existing
t('users.index.delete_confirm', username=user.username) value in a way that
escapes quotes and other special characters.

In `@app/translations/de.yaml`:
- Around line 494-495: The validation strings in the translation entries are
incomplete and cut off before the actual format description; update the
`invalid_color_hex` and `invalid_color_hex_2` messages in the translation files
so they include the full color format placeholder (for example the hex format
text) and proper sentence ending. Make the same correction in both `de.yaml` and
the matching `en.yaml` keys so the messages are consistent across locales.

In `@app/translations/en.yaml`:
- Around line 494-495: The translation entries for invalid_color_hex and
invalid_color_hex_2 are incomplete and stop mid-sentence. Update the strings in
en.yaml so they explicitly include the expected hex color format (for example,
mention `#RRGGBB`) and keep the wording consistent with the surrounding validation
messages. Locate the fix via the invalid_color_hex and invalid_color_hex_2 keys.

---

Outside diff comments:
In `@app/routes/settings.py`:
- Around line 215-216: The test_email() response still hardcodes English text in
the email subject and plain-text body while the HTML body is localized. Update
the subject and body values in test_email() to use the same localization pattern
as the HTML content, using the existing translation mechanism and related
message keys so the email is fully localized for the user’s language.

In `@app/templates/inventory/snapshot_history.html`:
- Around line 55-56: The date shown in the snapshot history template is
hardcoded with a German-only format, so make the date formatting
localization-aware in the snapshot rendering path. Update the
`snap.captured_at.strftime(...)` usage in `snapshot_history.html` to read the
format string from translations (for example via `t('common.date_format')`), and
add matching localized `common.date_format` values in the YAML language files so
`snap.captured_at` renders correctly for each locale.

In `@app/templates/inventory/spool_label.html`:
- Line 2: The `<html>` tag in the spool label template is hardcoded with
`lang="en"`, so update the `spool_label.html` markup to derive the language from
the active locale instead of a fixed value. Use the available locale source
mentioned in the stack context, such as `current_locale`, or `get_locale()` if
that Jinja global is registered, and wire it directly into the `html` language
attribute so the template reflects the user’s current language.

---

Nitpick comments:
In `@app/__init__.py`:
- Around line 46-56: The load_locale before_request hook is doing a database
lookup on every non-static request for app.language, which adds avoidable
latency. Update the load_locale logic to use a small TTL or in-memory cache for
the locale value instead of querying through get_db and session.scalar each
time, while still falling back to DEFAULT_LOCALE when the cached or loaded value
is not in SUPPORTED_LOCALES.

In `@app/routes/inventory.py`:
- Around line 1288-1298: Die Flash-Nachricht in inventory.py wird derzeit aus
mehreren übersetzten Fragmenten zusammengesetzt, was für i18n problematisch ist.
Passe die Logik rund um die Nachrichtenerzeugung in der Flash-Branch so an, dass
statt des schrittweisen Anhängens von inventory.flash.price_captured,
inventory.flash.price_availability_suffix und
inventory.flash.target_reached_suffix vollständige, locale-spezifische
Satzvorlagen verwendet werden. Lege dafür in den Übersetzungen passende
komplette Varianten mit Platzhaltern bzw. kombinationsabhängigen Texten an und
rufe in der aktuellen Flash-Logik nur die jeweils fertige Nachricht ab.

In `@app/templates/prints/index.html`:
- Line 57: The onsubmit confirm handler in the prints index template embeds a
translated string directly into a JavaScript string, which can break when the
text contains apostrophes. Update the confirm call in the template to use the
same escaping pattern already used in rule_form.html by applying tojson to the
translation output, and keep the change localized to the onsubmit attribute so
the confirm dialog remains functionally identical.

In `@app/templates/settings/index.html`:
- Around line 325-327: The confirm() handlers in the settings template are
interpolating translated text directly into JavaScript strings, which can break
when the translation contains apostrophes. Update the affected onclick confirm
calls in the settings page template to use the translation filter with tojson so
the message is safely escaped in the HTML attribute. Apply the same fix to both
confirm() usages referenced in this template and keep the existing confirm
behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c2da5a01-7369-491c-a223-ba312700b067

📥 Commits

Reviewing files that changed from the base of the PR and between a0682da and 5f4f689.

📒 Files selected for processing (42)
  • app/__init__.py
  • app/alert_service.py
  • app/i18n.py
  • app/notification_service.py
  • app/routes/api_keys.py
  • app/routes/auth.py
  • app/routes/inventory.py
  • app/routes/manufacturers.py
  • app/routes/prints.py
  • app/routes/settings.py
  • app/routes/shop_rules.py
  • app/routes/users.py
  • app/settings_service.py
  • app/templates/api_keys/index.html
  • app/templates/auth/login.html
  • app/templates/auth/setup.html
  • app/templates/base.html
  • app/templates/dashboard/index.html
  • app/templates/errors/403.html
  • app/templates/errors/404.html
  • app/templates/errors/500.html
  • app/templates/inventory/detail.html
  • app/templates/inventory/filament_form.html
  • app/templates/inventory/index.html
  • app/templates/inventory/purchase_form.html
  • app/templates/inventory/shoplink_form.html
  • app/templates/inventory/snapshot_form.html
  • app/templates/inventory/snapshot_history.html
  • app/templates/inventory/spool_form.html
  • app/templates/inventory/spool_label.html
  • app/templates/manufacturers/index.html
  • app/templates/manufacturers/manufacturer_detail.html
  • app/templates/manufacturers/manufacturer_form.html
  • app/templates/prints/index.html
  • app/templates/prints/print_form.html
  • app/templates/settings/index.html
  • app/templates/shop_rules/index.html
  • app/templates/shop_rules/rule_form.html
  • app/templates/users/index.html
  • app/templates/users/user_form.html
  • app/translations/de.yaml
  • app/translations/en.yaml

Comment thread app/i18n.py
Comment on lines +41 to +45
def get_locale() -> str:
try:
return g.locale
except (RuntimeError, AttributeError):
return DEFAULT_LOCALE

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Suche nach Scheduler-Aufrufen von notification_service-Funktionen
# um zu verifizieren, ob der Locale außerhalb des Request-Kontexts gesetzt wird.

# Suche nach Aufrufen von build_price_alert in alert_service.py oder scheduler.py
rg -n -C5 'build_price_alert_discord_embed\|build_price_alert_email_html\|send_discord\|send_email' --type py app/alert_service.py app/scheduler.py 2>/dev/null

# Prüfe, ob g.locale im Scheduler gesetzt wird
rg -n 'g\.locale\|get_locale\|set_locale\|locale' --type py app/scheduler.py 2>/dev/null

Repository: SytxLabs/SpoolBeacon

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Überblick über relevante Dateien
git ls-files | rg '^(app/|.*scheduler.*\.py$|.*notification_service.*\.py$|.*i18n\.py$)'

# Relevante Stellen in app/i18n.py anzeigen
if [ -f app/i18n.py ]; then
  echo "### app/i18n.py"
  cat -n app/i18n.py | sed -n '1,220p'
fi

# Relevante Stellen in Notification-/Scheduler-Dateien suchen
rg -n -C 4 'build_price_alert_discord_embed|build_price_alert_email_html|send_discord|send_email|get_locale|g\.locale|before_request|load_locale|locale' app 2>/dev/null

# Falls Dateien existieren, gezielt die relevanten Abschnitte anzeigen
for f in app/notification_service.py app/scheduler.py app/alert_service.py; do
  if [ -f "$f" ]; then
    echo "### $f"
    cat -n "$f" | sed -n '1,260p'
  fi
done

Repository: SytxLabs/SpoolBeacon

Length of output: 42394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Wer ruft die Alert-Benachrichtigungen auf?
rg -n -C 4 'dispatch_alert_notifications|maybe_create_alerts|build_price_alert_discord_embed|build_price_alert_email_html' app

# Den Preischeck-Pfad genauer ansehen
cat -n app/price_check_service.py | sed -n '1,260p'

Repository: SytxLabs/SpoolBeacon

Length of output: 17534


Geplante Preiswarnungen lokalisieren get_locale() fällt außerhalb eines Request-Kontexts immer auf DEFAULT_LOCALE zurück. Der Scheduler-Pfad (app/scheduler.pyapp/price_check_service.pyapp/alert_service.pyapp/notification_service.py) setzt keinen Locale-Kontext, ამიტომ die geplanten Discord-/E-Mail-Benachrichtigungen werden immer auf Englisch erzeugt. Den Locale im Scheduler-Pfad explizit mitführen oder vor dem Rendern setzen.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/i18n.py` around lines 41 - 45, Scheduled price-warning notifications are
always rendered with the fallback locale because get_locale() in app/i18n.py
defaults to DEFAULT_LOCALE when no request context exists. Update the scheduler
flow in app/scheduler.py and the downstream calls in
price_check_service/alert_service/notification_service to carry the user or job
locale explicitly, or set g.locale before rendering, so the notification
templates use the intended language instead of always English.

Comment thread app/i18n.py
Comment on lines +48 to +56
def t(key: str, **kwargs) -> str:
"""Translate `key` (dot-notation path into the yaml files) for the current request locale."""
locale = get_locale()
value = _lookup(_load(locale), key)
if value is None and locale != DEFAULT_LOCALE:
value = _lookup(_load(DEFAULT_LOCALE), key)
if value is None:
return key
return value.format(**kwargs) if kwargs else value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

str.format() ohne Fehlerbehandlung kann Requests zum Absturz bringen.

Wenn ein YAML-Übersetzungstext einen Platzhalter enthält, der nicht in den übergebenen kwargs steht (z. B. Tippfehler wie {nam} statt {name}), löst value.format(**kwargs) eine KeyError-Ausnahme aus. Da t() in Templates und Route-Handlern aufgerufen wird, führt dies zu einem 500-Fehler für den Endbenutzer.

🛡️ Vorgeschlagene Fehlerbehandlung
 def t(key: str, **kwargs) -> str:
     """Translate `key` (dot-notation path into the yaml files) for the current request locale."""
     locale = get_locale()
     value = _lookup(_load(locale), key)
     if value is None and locale != DEFAULT_LOCALE:
         value = _lookup(_load(DEFAULT_LOCALE), key)
     if value is None:
         return key
-    return value.format(**kwargs) if kwargs else value
+    if kwargs:
+        try:
+            return value.format(**kwargs)
+        except (KeyError, IndexError):
+            return value
+    return value
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def t(key: str, **kwargs) -> str:
"""Translate `key` (dot-notation path into the yaml files) for the current request locale."""
locale = get_locale()
value = _lookup(_load(locale), key)
if value is None and locale != DEFAULT_LOCALE:
value = _lookup(_load(DEFAULT_LOCALE), key)
if value is None:
return key
return value.format(**kwargs) if kwargs else value
def t(key: str, **kwargs) -> str:
"""Translate `key` (dot-notation path into the yaml files) for the current request locale."""
locale = get_locale()
value = _lookup(_load(locale), key)
if value is None and locale != DEFAULT_LOCALE:
value = _lookup(_load(DEFAULT_LOCALE), key)
if value is None:
return key
if kwargs:
try:
return value.format(**kwargs)
except (KeyError, IndexError):
return value
return value
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/i18n.py` around lines 48 - 56, The t() translation helper can crash
requests when a YAML string contains format placeholders that are missing from
kwargs. Update t() to handle formatting errors safely around
value.format(**kwargs), using a fallback behavior (for example returning the
unformatted translation or a safe error-tolerant render) instead of letting
KeyError propagate. Keep the change localized to t() and preserve the existing
locale lookup/fallback behavior in _lookup/_load.

Comment on lines +2 to +8
{% block title %}{{ t('dashboard.page_title') }} – {{ t('app.title') }}{% endblock %}

{% block content %}

<div class="mb-8">
<h1 class="page-title">Dashboard</h1>
<p class="page-subtitle">Filament inventory at a glance</p>
<h1 class="page-title">{{ t('dashboard.heading') }}</h1>
<p class="page-subtitle">{{ t('dashboard.subtitle') }}</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Datumsformat in Scheduler-Status nicht lokalisiert.

Die strftime('%d.%m. %H:%M')-Aufrufe auf den Zeilen 338 und 345 verwenden ein hartcodiertes deutsches Datumsformat. Für eine korrekte i18n-Implementierung sollte das Datumsformat locale-abhängig sein.

🌐 Vorschlag: Locale-abhängiges Datumsformat

Fügen Sie einen Übersetzungsschlüssel für das Datumsformat hinzu (z. B. in de.yaml/en.yaml):

# de.yaml
common:
  datetime_format: '%d.%m. %H:%M'

# en.yaml
common:
  datetime_format: '%m/%d/%Y %H:%M'

Verwendung im Template:

-            {{ t('dashboard.scheduler.next_run_label') }} <span class="text-gray-400 font-mono">{{ scheduler.next_run.strftime('%d.%m. %H:%M') }}</span>
+            {{ t('dashboard.scheduler.next_run_label') }} <span class="text-gray-400 font-mono">{{ scheduler.next_run.strftime(t('common.datetime_format')) }}</span>
-            {{ t('dashboard.scheduler.last_run_label') }} <span class="text-gray-400 font-mono">{{ scheduler.last_run.strftime('%d.%m. %H:%M') }}</span>
+            {{ t('dashboard.scheduler.last_run_label') }} <span class="text-gray-400 font-mono">{{ scheduler.last_run.strftime(t('common.datetime_format')) }}</span>

Also applies to: 17-17, 27-33, 42-48, 59-65, 77-77, 86-86, 112-112, 127-137, 162-162, 174-186, 195-212, 243-243, 255-255, 264-264, 274-280, 297-297, 307-307, 326-352

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/dashboard/index.html` around lines 2 - 8, The Scheduler-Status
date display in the dashboard template is using a hardcoded German strftime
format, which should be localized. Update the relevant datetime rendering in the
template using the existing translation helper t() and a locale-specific format
key (for example under common.datetime_format) instead of embedding '%d.%m.
%H:%M'. Apply the same localized formatting anywhere in the dashboard template
where the current hardcoded date format is used, so the output depends on the
active locale.

Comment on lines +514 to +526
<a href="/inventory/{{ product.id }}/spool/{{ spool.id }}/label" title="{{ t('inventory.detail.title_print_label') }}" target="_blank"
class="p-1.5 rounded-lg text-gray-500 hover:text-white hover:bg-gray-800 transition-colors">
{{ icon_qr() }}
</a>
{% if not is_viewer %}
<a href="/inventory/{{ product.id }}/spool/{{ spool.id }}/edit" title="Edit spool"
<a href="/inventory/{{ product.id }}/spool/{{ spool.id }}/edit" title="{{ t('inventory.detail.title_edit_spool') }}"
class="p-1.5 rounded-lg text-gray-500 hover:text-white hover:bg-gray-800 transition-colors">
{{ icon_pencil() }}
</a>
<form method="POST" action="/inventory/{{ product.id }}/spool/{{ spool.id }}/delete"
class="inline" onsubmit="return confirm('Delete spool {{ spool.spool_code }}? This cannot be undone.')">
class="inline" onsubmit="return confirm('{{ t('inventory.detail.confirm_delete_spool', code=spool.spool_code) }}')">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<button type="submit" title="Delete spool"
<button type="submit" title="{{ t('inventory.detail.title_delete_spool') }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Unsichere String-Interpolation im confirm()-Aufruf (Zeile 524).

spool.spool_code wird über t('inventory.detail.confirm_delete_spool', code=spool.spool_code) in einen einfach gequoteten JS-String innerhalb des onsubmit-Attributs eingesetzt. Jinja escaped zwar HTML-Sonderzeichen ('&#39;), aber der Browser dekodiert HTML-Entities im Attributwert, bevor der Inline-Handler als JS ausgeführt wird. Enthält spool_code ein ', bricht der String aus und ermöglicht Skript-Injection.

🛡️ Vorschlag: sichere Einbettung via `tojson`
                                 <form method="POST" action="/inventory/{{ product.id }}/spool/{{ spool.id }}/delete"
-                                      class="inline" onsubmit="return confirm('{{ t('inventory.detail.confirm_delete_spool', code=spool.spool_code) }}')">
+                                      class="inline" onsubmit="return confirm({{ t('inventory.detail.confirm_delete_spool', code=spool.spool_code)|tojson }})">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/inventory/detail.html` around lines 514 - 526, The `onsubmit`
handler in `inventory/detail.html` is building a JavaScript string from
`t('inventory.detail.confirm_delete_spool', code=spool.spool_code)` in a
single-quoted `confirm()` call, which can break out when `spool_code` contains
quotes. Fix it by making the delete confirmation message safe for JavaScript
context, ideally by using a JSON-encoded/JS-escaped value instead of direct
template interpolation, and keep the change localized to the delete form’s
`confirm()` usage.


<form id="form-delete-spool" method="POST" action="/inventory/{{ product.id }}/spool/{{ spool.id }}/delete"
onsubmit="return confirm('Delete spool {{ spool.spool_code }}? This cannot be undone.')">
onsubmit="return confirm('{{ t('inventory.spool_form.confirm_delete', code=spool.spool_code) }}')">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

XSS-Schwachstelle in confirm()-Dialog durch JS-Kontext-Injektion.

spool.spool_code wird über t() in einen JavaScript-String innerhalb eines HTML-Attributs interpoliert. Jinja-Autoescaping wandelt ' in &#39; um, aber der Browser dekodiert HTML-Entities in Attributwerten, bevor der JS-Code ausgeführt wird. Enthält spool.spool_code ein einfaches Anführungszeichen, bricht die JS-Syntax. Mit einem präparierten Wert wie '); alert('xss entsteht eine XSS-Lücke.

Dieselbe Schwachstelle exists auch in manufacturers/index.html (Line 76) und manufacturers/manufacturer_detail.html (Line 26) mit name=mfr.name bzw. name=manufacturer.name.

Fix: Den |tojson-Filter verwenden, um den String sicher für JS-Kontext zu escapen.

🔒 Proposed fix: Use |tojson for safe JS interpolation
-      onsubmit="return confirm('{{ t('inventory.spool_form.confirm_delete', code=spool.spool_code) }}')">
+      onsubmit="return confirm({{ t('inventory.spool_form.confirm_delete', code=spool.spool_code)|tojson }})">

|tojson erzeugt einen gültigen JS-String-Literal (z. B. "Delete SPOOL-001?") und escapet alle gefährlichen Zeichen korrekt für den JS-Kontext.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onsubmit="return confirm('{{ t('inventory.spool_form.confirm_delete', code=spool.spool_code) }}')">
onsubmit="return confirm({{ t('inventory.spool_form.confirm_delete', code=spool.spool_code)|tojson }})">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/inventory/spool_form.html` at line 19, The confirm() message is
being interpolated into a JavaScript string inside an HTML attribute via t(),
which allows JS-context injection when values like spool.spool_code contain
quotes. Update the confirm_delete usage in the spool_form template to pass the
translated string through the JS-safe tojson filter, and apply the same fix to
the matching confirm() calls in the manufacturers index and manufacturer_detail
templates that use mfr.name and manufacturer.name.

Comment on lines 94 to +97
<form method="POST" action="/shop-rules/{{ rule.id }}/delete" class="inline"
onsubmit="return confirm('Delete rule for {{ rule.domain }}?')">
onsubmit="return confirm('{{ t('shop_rules.index.delete_confirm', domain=rule.domain) }}')">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<button type="submit" title="Delete rule"
<button type="submit" title="{{ t('shop_rules.index.delete_rule_title') }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Stored XSS durch rule.domain in confirm()-Dialog

rule.domain ist Benutzereingabe und wird via t(..., domain=rule.domain) in einen JavaScript-String innerhalb eines HTML-Attributs interpoliert. Jinja2s Autoescaping schützt hier nicht: &#39; wird vom HTML-Parser zu ' dekodiert, bevor der JS-Interpreter es sieht. Ein Domain-Wert wie x');alert('y bricht den JS-String und ermöglicht Stored XSS – ein Angreifer kann eine Rule mit bösartiger Domain anlegen, die bei jedem Admin beim Löschen-Versuch XSS auslöst.

🔒 Vorgeschlagener Fix
-                              onsubmit="return confirm('{{ t('shop_rules.index.delete_confirm', domain=rule.domain) }}')">
+                              onsubmit="return confirm({{ t('shop_rules.index.delete_confirm', domain=rule.domain) | tojson }})">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/shop_rules/index.html` around lines 94 - 97, The delete
confirmation in the shop_rules index template is unsafe because rule.domain is
being interpolated directly into a JavaScript string inside the onsubmit
confirm() call. Update the confirm text generation around
t('shop_rules.index.delete_confirm', domain=rule.domain) so the domain is passed
through a JS-safe escaping/serialization step (not just HTML autoescaping), or
move the string construction out of inline JS entirely. Keep the delete form
behavior the same, but ensure the value used in confirm() cannot break out of
the string and trigger XSS.

Comment on lines +78 to +80
onsubmit="return confirm('{{ t('users.index.delete_confirm', username=user.username) }}')">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<button type="submit" title="Delete user"
<button type="submit" title="{{ t('users.index.delete_title') }}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Prüfen, ob der Username serverseitig validiert wird (z.B. Zeichen-Whitelist)
rg -n -A5 -B2 "username" app/routes/users.py 2>/dev/null

Repository: SytxLabs/SpoolBeacon

Length of output: 4053


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== users model =="
rg -n -A6 -B4 "class User|username" app/models.py app/models/*.py 2>/dev/null || true

echo
echo "== user form template =="
rg -n -A8 -B4 "username|maxlength|pattern" app/templates/users/user_form.html 2>/dev/null || true

echo
echo "== translation strings =="
rg -n -A3 -B1 "users.index.delete_confirm|delete_confirm" app 2>/dev/null || true

Repository: SytxLabs/SpoolBeacon

Length of output: 11145


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the template render path and any escaping helpers used for translations.
rg -n -A6 -B4 "def t\(|render_template\\(\"users/index.html\"|tojson|autoescape|Markup" app 2>/dev/null || true

Repository: SytxLabs/SpoolBeacon

Length of output: 4613


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [Path("app/templates/users/index.html"), Path("app/templates/users/user_form.html")]:
    if p.exists():
        print(f"== {p} ==")
        for i, line in enumerate(p.read_text().splitlines(), 1):
            if 1 <= i <= 130:
                if "delete_confirm" in line or "username" in line or "confirm(" in line or "tojson" in line:
                    print(f"{i}: {line}")
PY

Repository: SytxLabs/SpoolBeacon

Length of output: 1339


confirm()-Text als JS-Literal serialisieren
user.username landet ungefiltert in einem einfach gequoteten JS-String. Ein Username mit ' kann den Handler brechen und beim Absenden beliebigen JS-Code einschleusen.

Vorschlag
-                              onsubmit="return confirm('{{ t('users.index.delete_confirm', username=user.username) }}')">
+                              onsubmit="return confirm({{ t('users.index.delete_confirm', username=user.username)|tojson }})">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/templates/users/index.html` around lines 78 - 80, The onsubmit confirm
text in the users delete form is being inserted into a single-quoted JavaScript
string without escaping, so a username can break the handler and inject script.
Update the template around the delete form in users/index.html to serialize the
translated confirm message as a safe JS string literal before passing it to
confirm(), using the existing t('users.index.delete_confirm',
username=user.username) value in a way that escapes quotes and other special
characters.

Comment thread app/translations/de.yaml
Comment on lines +494 to +495
invalid_color_hex: Farbwert muss im Format
invalid_color_hex_2: Zweiter Farbwert muss im Format

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Originaltext der Validierungsmeldung vor der i18n-Umstellung suchen
rg -n "invalid_color_hex|Farbwert muss|Color value must" app/routes app/*.py 2>/dev/null

Repository: SytxLabs/SpoolBeacon

Length of output: 334


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== de.yaml around target lines =="
sed -n '486,502p' app/translations/de.yaml

echo
echo "== en.yaml matching section =="
rg -n -A3 -B3 "invalid_color_hex|invalid_color_hex_2" app/translations/en.yaml app/translations/de.yaml

echo
echo "== inventory validation context =="
sed -n '890,920p' app/routes/inventory.py

echo
echo "== search for color hex formatting text across repo =="
rg -n "RRGGBB|hex.*format|format.*hex|invalid_color_hex" app -g '!**/*.pyc'

Repository: SytxLabs/SpoolBeacon

Length of output: 4227


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== HEX regex definition =="
rg -n "_HEX_RE|color_hex" app/routes/inventory.py app -g '!**/*.pyc'

echo
echo "== surrounding lines near regex definition =="
sed -n '1,120p' app/routes/inventory.py

Repository: SytxLabs/SpoolBeacon

Length of output: 12068


Validierungsmeldungen vervollständigen
invalid_color_hex und invalid_color_hex_2 brechen mitten im Satz ab; hier fehlt die eigentliche Formatangabe (#RRGGBB) und der Satzschluss. Dasselbe gilt auch für app/translations/en.yaml.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/translations/de.yaml` around lines 494 - 495, The validation strings in
the translation entries are incomplete and cut off before the actual format
description; update the `invalid_color_hex` and `invalid_color_hex_2` messages
in the translation files so they include the full color format placeholder (for
example the hex format text) and proper sentence ending. Make the same
correction in both `de.yaml` and the matching `en.yaml` keys so the messages are
consistent across locales.

Comment thread app/translations/en.yaml
Comment on lines +494 to +495
invalid_color_hex: Color value must be in
invalid_color_hex_2: Second color value must be in

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== en.yaml excerpt ==\n'
sed -n '485,505p' app/translations/en.yaml

printf '\n== de.yaml excerpt ==\n'
sed -n '485,505p' app/translations/de.yaml

printf '\n== usages of invalid_color_hex keys ==\n'
rg -n "invalid_color_hex(_2)?|`#RRGGBB`|color value must be in|Second color value must be in" app -S

Repository: SytxLabs/SpoolBeacon

Length of output: 2996


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== app/routes/inventory.py around color validation ==\n'
sed -n '880,925p' app/routes/inventory.py

printf '\n== search for translation helper concatenation or key joining ==\n'
rg -n "invalid_color_hex_2|invalid_color_hex|format\)|t\\(\"inventory\\.validation\\." app -S

Repository: SytxLabs/SpoolBeacon

Length of output: 5583


Unvollständige Validierungsmeldungen ergänzen

app/translations/en.yaml bricht bei invalid_color_hex und invalid_color_hex_2 mitten im Satz ab. Die Meldungen sollten den erwarteten Farbcode ergänzen, z. B. #RRGGBB, sonst bleibt die Fehlermeldung im UI unvollständig.

Vorschlag
-    invalid_color_hex: Color value must be in
-    invalid_color_hex_2: Second color value must be in
+    invalid_color_hex: Color value must be in `#RRGGBB` format.
+    invalid_color_hex_2: Second color value must be in `#RRGGBB` format.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
invalid_color_hex: Color value must be in
invalid_color_hex_2: Second color value must be in
invalid_color_hex: Color value must be in `#RRGGBB` format.
invalid_color_hex_2: Second color value must be in `#RRGGBB` format.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/translations/en.yaml` around lines 494 - 495, The translation entries for
invalid_color_hex and invalid_color_hex_2 are incomplete and stop mid-sentence.
Update the strings in en.yaml so they explicitly include the expected hex color
format (for example, mention `#RRGGBB`) and keep the wording consistent with the
surrounding validation messages. Locate the fix via the invalid_color_hex and
invalid_color_hex_2 keys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant