feat: content localisation - #796
Conversation
…superpowers Shell scripts checked out with CRLF on Windows (core.autocrlf=true) broke the Docker entrypoint at container start. Force LF for all text files at the git layer, matching the existing .editorconfig and Prettier settings.
Add a workflow-level concurrency group keyed by workflow + ref with cancel-in-progress, so pushing a new commit cancels the still-running test job for the previous commit on that branch/PR.
…mments, maintenances
… them Five manage-monitor cards (MonitorTypeCard, UptimeSettingsCard, StatusHistoryDaysCard, MonitorSharingOptionsCard, DangerZoneCard) spread the page's monitor state into storeMonitorData payloads without a translations key, so it arrived as undefined. CreateUpdateMonitor serialized that unconditionally to null, silently wiping authored monitor translations whenever one of those cards was saved. Add resolveTranslationsForUpdate (src/lib/server/content-i18n.ts), a pure keep-on-undefined resolver matching the pattern already used by UpdateIncident/UpdateMaintenance: an omitted translations key preserves whatever is stored, while an explicit object or null always replaces it. Wire it into CreateUpdateMonitor's update branch and the standalone UpdateMonitor controller, fetching the existing raw value only when the incoming payload actually omits translations. The create path keeps its prior undefined-to-null semantics. Also retype the local MonitorInput interface's translations field to unknown (was inherited string | null from MonitorRecordInsert), matching IncidentInput/CreateMaintenanceInput, since real callers pass objects, serialized strings, undefined, or null - never just string | null. MonitorRecord/MonitorRecordInsert, which describe actual DB rows, are unchanged.
cancelEditComment() and cancelAddComment() reset commentText but left commentTranslations holding the last-edited values, relying on the next startEditComment/startAddComment call to reset it first. Clear it in both cancel paths too, so the safety invariant holds locally rather than depending on every future entry path.
📝 WalkthroughWalkthroughThis PR adds localized content translations for monitors, incidents, comments, and maintenances across storage, APIs, management forms, and rendered pages. It also adds Vitest browser/server testing, CI execution, database migration support, and repository configuration updates. ChangesContent translations
Test infrastructure and repository configuration
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Manager
participant ManageAPI
participant Controller
participant Database
participant PublicPage
Manager->>ManageAPI: submit translated content
ManageAPI->>Controller: forward translation payload
Controller->>Database: validate and store serialized translations
Database-->>PublicPage: return translation fields
PublicPage->>PublicPage: resolve active-locale content with fallback
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
| Filename | Overview |
|---|---|
| src/lib/server/content-i18n.ts | Adds validation, serialization, and preserve-on-undefined update behavior. |
| src/lib/content-i18n.ts | Adds tolerant parsing and locale-aware field resolution. |
| src/lib/components/manage/TranslationsButton.svelte | Adds a reusable editor for translated entity fields. |
| src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte | Localizes visible content, but translated-only descriptions can remain hidden and metadata stays untranslated. |
| src/routes/(kener)/monitors/[monitor_tag]/+page.svelte | Localizes visible content, but translated-only descriptions can remain hidden and metadata stays untranslated. |
| src/routes/(kener)/incidents/[incident_id]/+page.svelte | Localizes visible incident content while retaining base-language page metadata. |
| migrations/20260716120000_add_content_translations.ts | Adds guarded nullable translation columns through Knex. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Management translation editor] --> B[Validation and serialization]
B --> C[(Translation JSON columns)]
C --> D[Query and parsing]
D --> E[Locale resolver]
E --> F[Incident pages]
E --> G[Maintenance pages]
E --> H[Monitor pages]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart LR
A[Management translation editor] --> B[Validation and serialization]
B --> C[(Translation JSON columns)]
C --> D[Query and parsing]
D --> E[Locale resolver]
E --> F[Incident pages]
E --> G[Maintenance pages]
E --> H[Monitor pages]
Reviews (1): Last reviewed commit: "fix(monitors): look up preserved transla..." | Re-trigger Greptile
| <div class="prose prose-sm dark:prose-invert max-w-none min-w-0 overflow-x-auto p-4 wrap-break-word"> | ||
| <SveltePurify html={mdToHTML(data.maintenance.description)} /> | ||
| <SveltePurify | ||
| html={mdToHTML($lt(data.maintenance.translations, "description", data.maintenance.description))} |
There was a problem hiding this comment.
Translated Description Remains Hidden
When the optional base description is empty but the current locale has a description, the surrounding block still tests data.maintenance.description and skips this localized output. Visitors therefore see no description despite a valid translation being stored.
Context Used: CLAUDE.md (source)
| let descriptionExpanded = $state(false); | ||
| let showInlineEvents = $derived(data.eventDisplaySettings?.showInlineEvents === true); | ||
| const monitorName = $derived($lt(data.monitorTranslations, "name", data.monitorName)); | ||
| const monitorDescription = $derived($lt(data.monitorTranslations, "description", data.monitorDescription ?? "")); |
There was a problem hiding this comment.
Translated Description Remains Hidden
monitorDescription can resolve to translated text when the optional base description is empty, but the template still renders the section only when data.monitorDescription is truthy. A translated-only description is therefore omitted from the public monitor page.
Context Used: CLAUDE.md (source)
| <h1> | ||
| <Item.Title class="text-3xl wrap-break-word">{data.incident.title}</Item.Title> | ||
| <Item.Title class="text-3xl wrap-break-word" | ||
| >{$lt(data.incident.translations, "title", data.incident.title)}</Item.Title |
There was a problem hiding this comment.
Localized Content Has Base Metadata
The visible title and comments now use $lt, but the page title and social metadata still use the base incident title and first comment. For a non-default locale, browser tabs, search descriptions, and shared-link previews disagree with the rendered page.
Context Used: CLAUDE.md (source)
| <h1> | ||
| <Item.Title class="text-3xl wrap-break-word">{data.maintenance.title}</Item.Title> | ||
| <Item.Title class="text-3xl wrap-break-word" | ||
| >{$lt(data.maintenance.translations, "title", data.maintenance.title)}</Item.Title |
There was a problem hiding this comment.
Localized Content Has Base Metadata
The heading and description now resolve translations, while <svelte:head> still uses the base title and description. Visitors using another locale receive default-language browser and social-preview metadata for otherwise localized content.
Context Used: CLAUDE.md (source)
| const monitorName = $derived($lt(data.monitorTranslations, "name", data.monitorName)); | ||
| const monitorDescription = $derived($lt(data.monitorTranslations, "description", data.monitorDescription ?? "")); |
There was a problem hiding this comment.
Localized Content Has Base Metadata
These derived values localize the visible monitor content, but <svelte:head> still reads data.monitorName and data.monitorDescription. Browser titles and shared-link previews therefore remain in the default language.
Context Used: CLAUDE.md (source)
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/routes/(kener)/incidents/[incident_id]/+page.svelte (1)
22-28: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse translated strings for SEO and page metadata.
The
<title>and<meta>tags currently render the raw, untranslated fallback strings (e.g.,data.incident.titleordata.maintenance.title). To ensure that browser tab titles and social media previews match the localized page language currently in view, apply the$lttranslation helper to these properties.
src/routes/(kener)/incidents/[incident_id]/+page.svelte#L22-L28: Update<title>and<meta>properties (og:title,description,og:description) to use the$lthelper for evaluating translations ondata.incident.titleanddata.comments[0].comment.src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte#L67-L73: Update<title>and<meta>properties (og:title,description,og:description) to use the$lthelper for evaluating translations ondata.maintenance.titleanddata.maintenance.description.💡 Proposed fixes
For
src/routes/(kener)/incidents/[incident_id]/+page.svelte:- <title>{data.incident.title + " - " + data.siteName}</title> - <meta property="og:title" content={data.incident.title + " - " + data.siteName} /> + <title>{$lt(data.incident.translations, "title", data.incident.title) + " - " + data.siteName}</title> + <meta property="og:title" content={$lt(data.incident.translations, "title", data.incident.title) + " - " + data.siteName} /> <meta property="og:type" content="article" /> <meta name="twitter:card" content="summary_large_image" /> {`#if` data.comments.length > 0} - <meta name="description" content={data.comments[0].comment} /> - <meta property="og:description" content={data.comments[0].comment} /> + <meta name="description" content={$lt(data.comments[0].translations, "comment", data.comments[0].comment)} /> + <meta property="og:description" content={$lt(data.comments[0].translations, "comment", data.comments[0].comment)} /> {/if}For
src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte:- <title>{data.maintenance.title + " - " + data.siteName}</title> - <meta property="og:title" content={data.maintenance.title + " - " + data.siteName} /> + <title>{$lt(data.maintenance.translations, "title", data.maintenance.title) + " - " + data.siteName}</title> + <meta property="og:title" content={$lt(data.maintenance.translations, "title", data.maintenance.title) + " - " + data.siteName} /> <meta property="og:type" content="article" /> <meta name="twitter:card" content="summary_large_image" /> {`#if` data.maintenance.description} - <meta name="description" content={data.maintenance.description} /> - <meta property="og:description" content={data.maintenance.description} /> + <meta name="description" content={$lt(data.maintenance.translations, "description", data.maintenance.description)} /> + <meta property="og:description" content={$lt(data.maintenance.translations, "description", data.maintenance.description)} /> {/if}🤖 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 `@src/routes/`(kener)/incidents/[incident_id]/+page.svelte around lines 22 - 28, Update the incident metadata in src/routes/(kener)/incidents/[incident_id]/+page.svelte at lines 22-28 to evaluate data.incident.title and data.comments[0].comment through the $lt translation helper for the title, og:title, description, and og:description. Apply the same change in src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte at lines 67-73, using $lt for data.maintenance.title and data.maintenance.description in the corresponding metadata properties.src/routes/(api)/api/v4/monitors/+server.ts (1)
118-144: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winAuthenticate both monitor mutation endpoints with
VerifyAPIKey.The new translation writes are handled without the route-level Bearer-token authentication required for API endpoints.
src/routes/(api)/api/v4/monitors/+server.ts#L118-L144: authenticate before validating or inserting the monitor.src/routes/(api)/api/v4/monitors/[monitor_tag]/+server.ts#L144-L159: authenticate before applying the monitor update.As per coding guidelines,
src/routes/(api)/**/*.tsmust use API authentication viaVerifyAPIKey()imported from$lib/server/controllers/apiController.🤖 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 `@src/routes/`(api)/api/v4/monitors/+server.ts around lines 118 - 144, Authenticate both monitor mutation handlers with VerifyAPIKey from $lib/server/controllers/apiController before validation or database changes. Update src/routes/(api)/api/v4/monitors/+server.ts lines 118-144 and src/routes/(api)/api/v4/monitors/[monitor_tag]/+server.ts lines 144-159; preserve the existing request handling after successful authentication.Source: Coding guidelines
src/lib/server/controllers/monitorsController.ts (1)
283-303: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winDo not retain the source monitor’s translated name.
The clone receives
newName, butsource.translationsmay still contain the source"name"in every locale. Public localized views will therefore show the old monitor name. Remove translatednameentries or require translated names for the clone; translated descriptions can still be copied.🤖 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 `@src/lib/server/controllers/monitorsController.ts` around lines 283 - 303, Update the monitor clone construction in the controller’s db.insertMonitor call so source.translations does not retain localized “name” entries when using newNameTrimmed. Remove or replace translated name values while preserving translated descriptions and other translation data.
🤖 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 @.github/workflows/test.yml:
- Line 21: Update the workflow’s actions/checkout, actions/setup-node, and
actions/cache uses entries to reference audited immutable commit SHAs instead of
mutable version tags, while retaining the corresponding action version in an
inline comment for readability.
In `@CLAUDE.md`:
- Line 25: Update the component test setup note in CLAUDE.md to include a
Linux-specific dependency installation step using `npx playwright install
--with-deps chromium`, while retaining the existing one-time Chromium
installation guidance for other environments.
In `@src/lib/client/manage-locales.ts`:
- Around line 15-23: Update the locale-discovery request before response.json()
in the manage-locales flow to validate response.ok and reject failed HTTP
responses, including 401, 403, and 500 statuses, instead of falling back to the
English-only configuration. Preserve the existing parsing and locale handling
for successful responses.
In `@static/api-references/v4.json`:
- Around line 1860-1871: Update the Incident, IncidentComment, and Maintenance
schema definitions in v4.json to include translations and is_global wherever
those API payloads and request models support them, including
CreateIncidentRequest and UpdateMaintenanceRequest. Reuse the existing Monitor
translations definition and the established is_global type, while preserving
strict additionalProperties validation.
---
Outside diff comments:
In `@src/lib/server/controllers/monitorsController.ts`:
- Around line 283-303: Update the monitor clone construction in the controller’s
db.insertMonitor call so source.translations does not retain localized “name”
entries when using newNameTrimmed. Remove or replace translated name values
while preserving translated descriptions and other translation data.
In `@src/routes/`(api)/api/v4/monitors/+server.ts:
- Around line 118-144: Authenticate both monitor mutation handlers with
VerifyAPIKey from $lib/server/controllers/apiController before validation or
database changes. Update src/routes/(api)/api/v4/monitors/+server.ts lines
118-144 and src/routes/(api)/api/v4/monitors/[monitor_tag]/+server.ts lines
144-159; preserve the existing request handling after successful authentication.
In `@src/routes/`(kener)/incidents/[incident_id]/+page.svelte:
- Around line 22-28: Update the incident metadata in
src/routes/(kener)/incidents/[incident_id]/+page.svelte at lines 22-28 to
evaluate data.incident.title and data.comments[0].comment through the $lt
translation helper for the title, og:title, description, and og:description.
Apply the same change in
src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte at lines 67-73,
using $lt for data.maintenance.title and data.maintenance.description in the
corresponding metadata properties.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: f9fad65b-2f99-4ba2-ba3d-397128ebb0c9
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (49)
.gitattributes.github/workflows/test.yml.gitignoreCLAUDE.mdmigrations/20260716120000_add_content_translations.tspackage.jsonsrc/lib/client/manage-locales.tssrc/lib/components/CopyButton.svelte.test.tssrc/lib/components/IncidentItem.sveltesrc/lib/components/MaintenanceItem.sveltesrc/lib/components/MonitorBar.sveltesrc/lib/components/StatusBarCalendar.svelte.test.tssrc/lib/components/manage/TranslationsButton.sveltesrc/lib/content-i18n.test.tssrc/lib/content-i18n.tssrc/lib/server/api-server/monitor-bar/get.tssrc/lib/server/api-server/monitor-bar/shared.tssrc/lib/server/content-i18n.test.tssrc/lib/server/content-i18n.tssrc/lib/server/controllers/incidentController.tssrc/lib/server/controllers/maintenanceController.tssrc/lib/server/controllers/monitorsController.tssrc/lib/server/db/repositories/incidents.tssrc/lib/server/db/repositories/maintenances.tssrc/lib/server/db/repositories/monitors.tssrc/lib/server/tool.test.tssrc/lib/server/types/db.tssrc/lib/stores/i18n.test.tssrc/lib/stores/i18n.tssrc/lib/types/api.tssrc/lib/types/common.tssrc/routes/(api)/api/v4/incidents/[incident_id]/+server.tssrc/routes/(api)/api/v4/incidents/[incident_id]/comments/+server.tssrc/routes/(api)/api/v4/incidents/[incident_id]/comments/[comment_id]/+server.tssrc/routes/(api)/api/v4/monitors/+server.tssrc/routes/(api)/api/v4/monitors/[monitor_tag]/+server.tssrc/routes/(kener)/incidents/[incident_id]/+page.sveltesrc/routes/(kener)/maintenances/[maintenance_id]/+page.server.tssrc/routes/(kener)/maintenances/[maintenance_id]/+page.sveltesrc/routes/(kener)/monitors/[monitor_tag]/+page.server.tssrc/routes/(kener)/monitors/[monitor_tag]/+page.sveltesrc/routes/(manage)/manage/api/+server.tssrc/routes/(manage)/manage/app/incidents/[incident_id]/+page.sveltesrc/routes/(manage)/manage/app/maintenances/[id]/+page.sveltesrc/routes/(manage)/manage/app/monitors/[tag]/+page.sveltesrc/routes/(manage)/manage/app/monitors/[tag]/components/GeneralSettingsCard.sveltestatic/api-references/v4.jsonvite.config.tsvitest-setup-client.ts
| contents: read | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4.2.2 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- .github/workflows/test.yml ---\n'
nl -ba .github/workflows/test.yml | sed -n '1,120p'
printf '\n--- search for pinning policy / workflow comments ---\n'
rg -n --hidden -S "pinning|action-pinning|checkout@|setup-node@|actions/cache@" .github/workflows .github README.md . | sed -n '1,200p'Repository: rajnandan1/kener
Length of output: 245
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- .github/workflows/test.yml ---'
nl -ba .github/workflows/test.yml | sed -n '1,120p'
printf '\n%s\n' '--- search for pinning policy / workflow comments ---'
rg -n --hidden -S "pinning|action-pinning|checkout@|setup-node@|actions/cache@" .github/workflows .github README.md . | sed -n '1,200p'Repository: rajnandan1/kener
Length of output: 229
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- .github/workflows/test.yml ---'
python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/test.yml')
for i, line in enumerate(p.read_text().splitlines(), 1):
if i <= 120:
print(f"{i:4d}: {line}")
PY
printf '\n%s\n' '--- search for pinning policy / workflow references ---'
python3 - <<'PY'
from pathlib import Path
import re
targets = [Path('.github/workflows/test.yml'), Path('.github')]
for path in targets:
if path.is_file():
text = path.read_text(errors='ignore')
for pat in ['checkout@', 'setup-node@', 'actions/cache@', 'pinning', 'action-pinning']:
if pat in text:
print(f"{path}: contains {pat}")
else:
for p in path.rglob('*'):
if p.is_file() and p.suffix in {'.yml', '.yaml', '.md', '.txt'}:
try:
text = p.read_text(errors='ignore')
except Exception:
continue
if any(pat in text for pat in ['pinning', 'action-pinning', 'checkout@', 'setup-node@', 'actions/cache@']):
print(p)
PYRepository: rajnandan1/kener
Length of output: 2067
Pin the workflow actions to commit SHAs. actions/checkout, actions/setup-node, and actions/cache are still referenced by mutable tags, so they can change without a repo diff. Pin each to an audited SHA and keep the version in a comment.
Also applies to: 26-26, 39-39
🧰 Tools
🪛 zizmor (1.26.1)
[error] 21-21: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 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 @.github/workflows/test.yml at line 21, Update the workflow’s
actions/checkout, actions/setup-node, and actions/cache uses entries to
reference audited immutable commit SHAs instead of mutable version tags, while
retaining the corresponding action version in an inline comment for readability.
Source: Linters/SAST tools
| npm run test:watch # Watch mode | ||
| ``` | ||
|
|
||
| Component tests need a one-time `npx playwright install chromium`. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## FILES\n'
git ls-files CLAUDE.md package.json .github/workflows/test.yml .claude/skills/documentation-writer/SKILL.md
for f in CLAUDE.md package.json .github/workflows/test.yml .claude/skills/documentation-writer/SKILL.md; do
if [ -f "$f" ]; then
printf '\n## %s (%s lines)\n' "$f" "$(wc -l < "$f")"
fi
done
printf '\n## CLAUDE.md excerpt\n'
sed -n '1,80p' CLAUDE.md
printf '\n## package.json excerpt\n'
sed -n '1,240p' package.json
printf '\n## test workflow excerpt\n'
sed -n '1,240p' .github/workflows/test.yml
if [ -f .claude/skills/documentation-writer/SKILL.md ]; then
printf '\n## documentation skill excerpt\n'
sed -n '1,220p' .claude/skills/documentation-writer/SKILL.md
fiRepository: rajnandan1/kener
Length of output: 14842
🌐 Web query:
Playwright install --with-deps chromium Linux system dependencies official docs
💡 Result:
To install the Chromium browser along with its required Linux system dependencies using Playwright, you can use the following command: npx playwright install --with-deps chromium [1][2] This command performs two actions: it downloads the Chromium browser binary and automatically installs the necessary operating system-level packages required to run it on your Linux environment [1][2]. Key points regarding this process: - Usage: This command is particularly useful in Continuous Integration (CI) environments where you need to ensure the environment is correctly configured to run browser automation [1][3]. - Targeted Installation: You can install dependencies for a specific browser (like chromium) as shown above, or you can run npx playwright install-deps to install system dependencies for all browsers supported by your current Playwright version [1][2]. - Proxy Configuration: If your Linux environment requires a proxy, you must run the command as a root user (e.g., using sudo). If not run as root, Playwright's attempt to elevate privileges may prevent it from correctly passing environment variables like HTTPS_PROXY to the system's package manager [1][4]. - Underlying Mechanism: Playwright uses internal tooling to identify missing shared libraries (via ldd) and maps them to the appropriate package names for your specific Linux distribution, ensuring the browser can launch successfully [5]. For official information regarding supported operating systems and further details on browser management, refer to the official Playwright documentation on browsers [1][6].
Citations:
- 1: https://playwright.dev/docs/browsers
- 2: https://github.com/microsoft/playwright/blob/main/docs/src/browsers.md
- 3: https://playwright.dev/docs/ci
- 4: https://github.com/microsoft/playwright/blob/4d289016/docs/src/browsers.md
- 5: https://fossies.org/linux/playwright/utils/linux-browser-dependencies/README.md
- 6: https://playwright.dev/docs/intro
Document the Linux Playwright dependency step npx playwright install chromium still misses the Linux system packages needed to run component tests on clean machines; CI already uses npx playwright install --with-deps chromium. Add a Linux-specific note here.
🤖 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 `@CLAUDE.md` at line 25, Update the component test setup note in CLAUDE.md to
include a Linux-specific dependency installation step using `npx playwright
install --with-deps chromium`, while retaining the existing one-time Chromium
installation guidance for other environments.
| const response = await fetch(clientResolver(resolve, "/manage/api"), { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ action: "getAllSiteData" }), | ||
| }); | ||
| const result = await response.json(); | ||
| const names = new Map(availableLocalesList.map((l) => [l.code, l.name])); | ||
| const defaultLocale: string = result?.i18n?.defaultLocale || "en"; | ||
| const enabled: { code: string; name?: string; selected?: boolean }[] = result?.i18n?.locales || []; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Reject failed locale-discovery responses.
A 401/403/500 response is currently treated as a valid English-only configuration, silently hiding translation controls. Check response.ok before parsing.
Proposed fix
const response = await fetch(clientResolver(resolve, "/manage/api"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "getAllSiteData" }),
});
+ if (!response.ok) {
+ throw new Error(`Failed to fetch translatable locales (${response.status})`);
+ }
const result = await response.json();📝 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.
| const response = await fetch(clientResolver(resolve, "/manage/api"), { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ action: "getAllSiteData" }), | |
| }); | |
| const result = await response.json(); | |
| const names = new Map(availableLocalesList.map((l) => [l.code, l.name])); | |
| const defaultLocale: string = result?.i18n?.defaultLocale || "en"; | |
| const enabled: { code: string; name?: string; selected?: boolean }[] = result?.i18n?.locales || []; | |
| const response = await fetch(clientResolver(resolve, "/manage/api"), { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ action: "getAllSiteData" }), | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`Failed to fetch translatable locales (${response.status})`); | |
| } | |
| const result = await response.json(); | |
| const names = new Map(availableLocalesList.map((l) => [l.code, l.name])); | |
| const defaultLocale: string = result?.i18n?.defaultLocale || "en"; | |
| const enabled: { code: string; name?: string; selected?: boolean }[] = result?.i18n?.locales || []; |
🤖 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 `@src/lib/client/manage-locales.ts` around lines 15 - 23, Update the
locale-discovery request before response.json() in the manage-locales flow to
validate response.ok and reject failed HTTP responses, including 401, 403, and
500 statuses, instead of falling back to the English-only configuration.
Preserve the existing parsing and locale handling for successful responses.
| "translations": { | ||
| "oneOf": [ | ||
| { | ||
| "type": "object", | ||
| "additionalProperties": true | ||
| }, | ||
| { | ||
| "type": "null" | ||
| } | ||
| ], | ||
| "description": "Per-locale content translations, keyed by locale code, e.g. {\"de\": {\"name\": \"...\"}}" | ||
| }, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Add translations and is_global to Incident, IncidentComment, and Maintenance schemas.
While translations has been correctly added to the Monitor request and response schemas, it is missing from the Incident, IncidentComment, and Maintenance schemas (e.g., CreateIncidentRequest, UpdateMaintenanceRequest).
Additionally, the is_global field has been added to incident and maintenance API payloads in this PR but is missing from their respective request schemas here. Since these request schemas enforce "additionalProperties": false, omitting these properties will cause strict API validators and generated SDKs to reject requests containing them.
🤖 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 `@static/api-references/v4.json` around lines 1860 - 1871, Update the Incident,
IncidentComment, and Maintenance schema definitions in v4.json to include
translations and is_global wherever those API payloads and request models
support them, including CreateIncidentRequest and UpdateMaintenanceRequest.
Reuse the existing Monitor translations definition and the established is_global
type, while preserving strict additionalProperties validation.
Adds a option for all user visible texts to enter texts in all (in internationalization configured) languages.
Example:

Summary by CodeRabbit