Skip to content

feat(i18n): add server-side i18n for notification agents#2731

Open
fallenbagel wants to merge 11 commits intodevelopfrom
fallenbagel/feat/server-i18n
Open

feat(i18n): add server-side i18n for notification agents#2731
fallenbagel wants to merge 11 commits intodevelopfrom
fallenbagel/feat/server-i18n

Conversation

@fallenbagel
Copy link
Copy Markdown
Collaborator

@fallenbagel fallenbagel commented Mar 20, 2026

Description

This adds server-side internationalization to all notification agents.

Previously, all notification strings were hardcoded in English. Now each agent resolves translations at send time based on the recipient's display language setting or, for system-level agents like Discord, Slack, Gotify, and ntfy, a configurable locale in the agent's notification settings.

Discord additionally supports a "Use Notification Recipient Locale" toggle that sends notifications in the requesting user's language instead of the channel-wide setting.

The implementation uses @formatjs/intl, the same ICU MessageFormat engine that powers the frontend's react-intl, so translators on Weblate see identical syntax across both components. A new extraction script is now added that generates the server locale source file for Weblate integration. The build script now copies locale JSON files into the production dist directory, and existing installations will have new default settings merged and persisted on first startup after upgrading.

How Has This Been Tested?

Screenshots / Logs (if applicable)

image

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • New Features

    • Notifications localized across channels with per-agent "Notification Language"; Discord adds "Use Notification Recipient Locale" and locale selectors in notification settings. Test notifications respect selected locale.
  • Chores

    • Server now builds and serves translation files and includes server-side message extraction tooling; added runtime i18n dependency.
  • Documentation

    • Added docs for Notification Language and Discord recipient-locale behavior.

@fallenbagel fallenbagel requested a review from Copilot March 20, 2026 00:29
@fallenbagel fallenbagel requested a review from a team as a code owner March 20, 2026 00:29
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Add server-side i18n runtime, message extraction tooling and English locale; initialize i18n at startup; propagate per-recipient locales through notification agents and frontend settings; extend CI/check script and build to include server extraction; add runtime dependency and new extraction script.

Changes

Cohort / File(s) Summary
I18n runtime & types
server/lib/i18n/index.ts, server/types/languages.ts, server/types/languages.d.ts
Add server i18n initializer (initI18n, getIntl, defineMessages); move availableLocales/AvailableLocale into a typed TS module and remove old .d.ts.
Server extraction & locale files
server/lib/i18n/extractMessages.ts, server/lib/i18n/locale/en.json, server/lib/i18n/globalMessages.ts
Add extractor script that writes en.json, add English locale file and a shared globalMessages catalog used by agents.
Startup integration
server/index.ts
Call initI18n() during server startup after settings are loaded.
Notification agents (bulk)
server/lib/notifications/agents/*
discord.ts, email.ts, gotify.ts, ntfy.ts, pushbullet.ts, pushover.ts, slack.ts, telegram.ts, webpush.ts
Replace hardcoded English strings with intl.formatMessage(...) using getIntl/globalMessages; add per-recipient locale handling and adjust internal payload/signature usage where needed.
Email & Webpush (major localized changes)
server/lib/notifications/agents/email.ts, server/lib/notifications/agents/webpush.ts
Email body generation refactored to use i18n (new locale parameter); webpush now builds per-recipient payloads and eager-loads user settings to obtain locales.
Settings & defaults
server/lib/settings/index.ts
Add locale: AvailableLocale to multiple agent options and useUserLocale: boolean for Discord; adjust migration/merge logic to detect changes.
Frontend settings UI
src/components/Settings/Notifications/...
NotificationsDiscord.tsx, NotificationsGotify/index.tsx, NotificationsNtfy/index.tsx, NotificationsSlack/index.tsx
Add locale select to multiple agent settings forms and a useUserLocale toggle for Discord; include fields in save/test payloads.
Build, CI & checks
.github/workflows/ci.yml, bin/check-i18n.js, package.json
CI guidance broadened to mention server extraction; i18n checker now validates client and server en.json via per-target backups and extract commands; add i18n:extract:server script, copy server locale JSON into server build, and add @formatjs/intl dependency.
Docs & UI strings
docs/using-seerr/notifications/*.md, src/i18n/globalMessages.ts, src/i18n/locale/en.json
Document notification language options and recipient-locale behavior; add UI translation keys notificationLocale and useUserLocale.
Test notification route
server/routes/settings/notifications.ts
Localize test notification subject/body using requesting user's locale.
I18n checker script
bin/check-i18n.js
Refactor to iterate extraction targets, create per-target backups, run target-specific extract commands, compare pre/post en.json, and exit non-zero on mismatch.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Server
    participant Agent as NotificationAgent
    participant I18n as server/lib/i18n
    participant LocaleFile as server/lib/i18n/locale/en.json

    User->>Server: trigger event (includes notifyUser/recipients)
    Server->>Agent: create/send notification
    Agent->>Server: fetch settings & recipient info
    Agent->>I18n: getIntl(recipientLocale?)
    I18n->>LocaleFile: load locale JSON (fallback to en)
    LocaleFile-->>I18n: messages
    I18n-->>Agent: IntlInstance
    Agent->>Agent: formatMessage(...) per recipient
    Agent->>User: send localized notification
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #1576 — Missing translation for email notification — this PR adds server email templates and localizes email generation, addressing the missing translation.
  • #1706 — Translation of request statuses in Telegram notifications — this PR localizes request-status strings used in Telegram agent notifications.

Possibly related PRs

Suggested reviewers

  • gauthier-th
  • 0xSysR3ll
  • M0NsTeRRR

Poem

🐰
I hopped through keys both big and small,
Turned lone strings into messages for all,
Now pings and mails will speak each tongue,
A rabbit's patchwork—i18n done!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: adding server-side i18n support for notification agents.
Linked Issues check ✅ Passed The PR fully addresses both linked issues: #1576 (email notification translations) and #1706 (Telegram notification status translations) by implementing comprehensive server-side i18n for all notification agents with localized messages and recipient locale support.
Out of Scope Changes check ✅ Passed All changes are directly related to the PR objectives. The i18n infrastructure, notification agent updates, settings extensions, UI controls, and documentation are all within scope for implementing server-side i18n for notification agents.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@github-actions github-actions bot added the i18n-out-of-sync User forgot to run pnpm i18n:extract label Mar 20, 2026
@github-actions
Copy link
Copy Markdown

The i18n check failed because translation messages are out of sync.

This usually happens when you've added or modified translation strings in your code but haven't updated the translation file.

Please run pnpm i18n:extract and/or pnpm i18n:extract:server and commit the changes.

@cypress
Copy link
Copy Markdown

cypress bot commented Mar 20, 2026

seerr    Run #3319

Run Properties:  status check passed Passed #3319  •  git commit f5ec45b1b9: feat(i18n): add server-side i18n for notification agents
Project seerr
Branch Review fallenbagel/feat/server-i18n
Run status status check passed Passed #3319
Run duration 02m 22s
Commit git commit f5ec45b1b9: feat(i18n): add server-side i18n for notification agents
Committer fallenbagel
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 32
View all changes introduced in this branch ↗︎

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a server-side i18n layer for notification agents so outbound notifications can be localized per recipient (or per-agent configured locale for channel/system agents), aligning message formatting with the frontend’s ICU MessageFormat tooling.

Changes:

  • Introduces a server i18n runtime (@formatjs/intl) with locale JSON loading, message descriptor helpers, and an extraction script for Weblate.
  • Adds per-agent “Notification Language” settings in the UI (Slack, Gotify, ntfy) and Discord’s “Use Notification Recipient Locale” toggle.
  • Updates multiple notification agents (email/webpush/telegram/discord/slack/etc.) to format user-facing strings via server i18n.

Reviewed changes

Copilot reviewed 24 out of 27 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/i18n/globalMessages.ts Adds a new UI label for notification locale selection.
src/components/Settings/Notifications/NotificationsSlack/index.tsx Adds locale option to Slack notification settings UI and payload.
src/components/Settings/Notifications/NotificationsNtfy/index.tsx Adds locale option to ntfy notification settings UI and payload.
src/components/Settings/Notifications/NotificationsGotify/index.tsx Adds locale option to Gotify notification settings UI and payload.
src/components/Settings/Notifications/NotificationsDiscord.tsx Adds locale + “use recipient locale” toggle to Discord settings UI and payload.
server/types/languages.ts Replaces the locale union with a const list + derived AvailableLocale type.
server/types/languages.d.ts Removes the previous AvailableLocale declaration file.
server/routes/settings/notifications.ts Localizes test notification subject/body using server i18n.
server/lib/settings/index.ts Adds locale fields to agent settings defaults and types; persists merged defaults on load.
server/lib/notifications/agents/webpush.ts Converts webpush message strings/labels to server i18n; adds per-subscription locale handling.
server/lib/notifications/agents/telegram.ts Converts Telegram message labels/status/link text to server i18n with per-user locale.
server/lib/notifications/agents/slack.ts Converts Slack block field labels/status/link text to server i18n using agent locale.
server/lib/notifications/agents/pushover.ts Converts Pushover message labels/status/link text to server i18n with per-user locale.
server/lib/notifications/agents/pushbullet.ts Converts Pushbullet message labels/status text to server i18n with per-user locale.
server/lib/notifications/agents/ntfy.ts Converts ntfy message labels/status text to server i18n using agent locale.
server/lib/notifications/agents/gotify.ts Converts Gotify message labels/status/link text to server i18n using agent locale.
server/lib/notifications/agents/email.ts Converts email body copy and issue text to server i18n with per-user locale.
server/lib/notifications/agents/discord.ts Converts Discord embed field labels/status text to server i18n; supports per-recipient locale toggle.
server/lib/i18n/locale/en.json Adds server-side extracted English message catalog.
server/lib/i18n/index.ts Adds server i18n initialization and message descriptor helper functions.
server/lib/i18n/globalMessages.ts Defines shared server-side notification message descriptors.
server/lib/i18n/extractMessages.ts Adds server extraction script to generate server/lib/i18n/locale/en.json.
server/index.ts Initializes server i18n during startup.
package.json Adds i18n:extract:server script and @formatjs/intl dependency; copies server locale JSON files into dist.
pnpm-lock.yaml Locks @formatjs/intl and related dependencies.
bin/check-i18n.js Extends i18n sync check to validate both frontend and server extractions.
.github/workflows/ci.yml Updates CI messaging to reference server extraction script as well.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions github-actions bot removed the i18n-out-of-sync User forgot to run pnpm i18n:extract label Mar 20, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (1)
src/components/Settings/Notifications/NotificationsNtfy/index.tsx (1)

164-177: ⚠️ Potential issue | 🟠 Major

Remove unused locale configuration from test notification request.

The frontend sends options.locale in the test POST body, but the backend's sendTestNotification handler ignores this value entirely. The function uses only the authenticated user's locale (user.settings?.locale), not the configured locale from the request options. This means the locale selector in the UI has no effect on test notifications—they will always render in the admin's language, not the agent's configured locale.

Remove the locale field from the test request body to avoid misleading users, or update the backend handler to accept and use the locale parameter for test notifications.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Settings/Notifications/NotificationsNtfy/index.tsx` around
lines 164 - 177, The test request body currently includes options.locale but the
backend sendTestNotification handler ignores that and uses
user.settings?.locale; remove the misleading locale field from the frontend test
POST call (the axios.post to '/api/v1/settings/notifications/ntfy/test' in
NotificationsNtfy component) so the UI doesn't send options.locale, or
alternatively update the backend sendTestNotification to read and use the
incoming options.locale instead of user.settings?.locale; choose one approach
and implement it consistently so the locale selector actually affects test
notifications or is not sent.
🧹 Nitpick comments (9)
server/lib/i18n/extractMessages.ts (2)

44-46: Consider filtering by file extension.

The getFiles function returns all files in ./server/, but extractMessages will attempt to read and regex-match every file type (including .json, .md, images, etc.). While the regex won't match non-TypeScript files, this adds unnecessary I/O overhead.

♻️ Suggested filter
 async function processMessages(dir: string): Promise<string> {
   const files = await getFiles(dir);
-  const extractedMessagesGroups = await Promise.all(files.map(extractMessages));
+  const tsFiles = files.filter(f => /\.[jt]sx?$/.test(f));
+  const extractedMessagesGroups = await Promise.all(tsFiles.map(extractMessages));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/i18n/extractMessages.ts` around lines 44 - 46, The processMessages
function calls getFiles and passes every file under the dir to extractMessages,
causing unnecessary I/O on non-source files; update processMessages to filter
the files list returned by getFiles by allowed extensions (e.g., .ts, .tsx, .js,
.jsx) before calling extractMessages so only relevant source files are
processed, leaving getFiles and extractMessages unchanged and using the existing
variables files and extractedMessagesGroups to wire the filtered list through.

19-19: Regex unnecessarily strict and fragile with message formats.

The regex /defineMessages\(\n?\s*'(.+?)',\n?\s*\{([\s\S]+?)\}\n?\);/ will fail to extract messages if any value contains a } character (e.g., template strings with embedded braces). While current messages use only simple placeholder syntax like {userName}, {mediaType}, etc., the extractor is brittle for future-proofing. Consider using a more robust parsing approach (e.g., AST parsing with a TypeScript parser) rather than regex-based extraction.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/i18n/extractMessages.ts` at line 19, The current const regex in
extractMessages.ts that matches defineMessages(...) is too fragile (it will
break if message values contain '}' or complex templates); replace the
regex-based extraction with an AST-based parse: parse the source file (using
TypeScript compiler API or `@babel/parser/`@typescript-eslint/parser), locate
CallExpressions named defineMessages, read the first argument (the message
namespace key) and the object literal second argument to safely extract keys and
string/value nodes, and then produce the same output structure; update the code
that referenced the const regex to instead call the new AST extraction routine
so defineMessages parsing is robust against embedded braces and complex
expressions.
src/components/Settings/Notifications/NotificationsNtfy/index.tsx (1)

340-345: Priority labels are hardcoded in English.

The priority dropdown options ("Minimum", "Low", "Default", "High", "Urgent") are hardcoded in English, while the "Notification Language" selector is immediately below. For consistency with the i18n work in this PR, consider localizing these labels using intl.formatMessage().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Settings/Notifications/NotificationsNtfy/index.tsx` around
lines 340 - 345, Replace the hardcoded English priority labels inside the
<Field> options (values 1–5) with localized strings by calling
intl.formatMessage(...) for each label; update the five option children to use
intl.formatMessage({ id: 'notifications.priority.minimum' }) etc. (or existing
message IDs), ensuring the component imports/uses the useIntl hook (intl) where
the <Field> is rendered so labels reflect the selected "Notification Language".
server/lib/notifications/agents/slack.ts (1)

68-71: Redundant getSettings() call on line 71.

Similar to the ntfy agent, line 68 stores this.getSettings() in settings, but line 71 calls getSettings().notifications.agents.slack again. Since this.getSettings() returns the same Slack agent settings, the embedPoster can be accessed directly from settings.

♻️ Suggested simplification
   public buildEmbed(
     type: Notification,
     payload: NotificationPayload
   ): SlackBlockEmbed {
     const settings = this.getSettings();
     const intl = getIntl(settings.options.locale);
     const { applicationUrl, applicationTitle } = getSettings().main;
-    const { embedPoster } = getSettings().notifications.agents.slack;
+    const { embedPoster } = settings;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/slack.ts` around lines 68 - 71, The redundant
getSettings() call should be removed: use the already-cached settings from const
settings = this.getSettings() to read notifications.agents.slack.embedPoster
instead of calling getSettings() again; update the code that sets embedPoster to
reference settings.notifications.agents.slack.embedPoster (or settings.options/
main where appropriate) so all settings come from the single settings variable.
server/lib/notifications/agents/ntfy.ts (2)

28-34: Redundant getSettings() call on line 31.

Line 28 already stores this.getSettings() in settings, but line 31 calls getSettings().notifications.agents.ntfy again to get embedPoster. Since this.getSettings() returns the same ntfy agent settings, this is redundant.

♻️ Suggested simplification
   private buildPayload(type: Notification, payload: NotificationPayload) {
     const settings = this.getSettings();
     const intl = getIntl(settings.options.locale as AvailableLocale);
     const { applicationUrl } = getSettings().main;
-    const { embedPoster } = getSettings().notifications.agents.ntfy;
+    const { embedPoster } = settings;

     const topic = settings.options.topic;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/ntfy.ts` around lines 28 - 34, The code
redundantly calls getSettings() again to read embedPoster; instead use the
already-stored settings variable (from const settings = this.getSettings()) to
access settings.notifications.agents.ntfy.embedPoster, and remove the extra
getSettings() invocation so embedPoster is assigned from settings rather than
calling getSettings() a second time.

71-71: IssueTypeName is not localized.

The issue type name (e.g., "Video", "Audio", "Subtitle", "Other") comes from the IssueTypeName constant and remains in English. This is consistent with other notification agents in this PR, but may be worth adding to the i18n catalog in a follow-up if full localization is desired.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/ntfy.ts` at line 71, The IssueTypeName lookup
is not localized; replace the direct use of
IssueTypeName[payload.issue.issueType] when building message (the line appending
to message using intl.formatMessage(globalMessages.issueType)) with a localized
string: add per-issue-type keys to the i18n catalog (e.g., messages for
Video/Audio/Subtitle/Other), then map payload.issue.issueType to
intl.formatMessage(...) for the corresponding key (or use a small helper that
converts IssueTypeName enum values to intl keys and returns a localized
fallback). Ensure you reference IssueTypeName and payload.issue.issueType when
creating the mapping and use intl.formatMessage to produce the localized issue
type in the notification message.
server/lib/notifications/agents/pushbullet.ts (1)

132-132: System notifications use default English locale.

Similar to Telegram, Pushbullet's system notifications always use the default English locale. Consider adding a locale option to NotificationAgentPushbullet.options for consistency with Discord, Slack, Gotify, and Ntfy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/pushbullet.ts` at line 132, Add a locale
option to NotificationAgentPushbullet.options and ensure system notifications
use it: update the NotificationAgentPushbullet.options type to include an
optional locale (e.g., string), read this.options.locale when constructing
messages, and pass that locale into getNotificationPayload or assign it on the
notificationPayload object before sending (the line creating notificationPayload
via this.getNotificationPayload(type, payload)). Ensure default behavior remains
unchanged if locale is undefined so other paths still work.
server/lib/notifications/agents/telegram.ts (1)

199-199: System notifications use default English locale.

Unlike Discord, Slack, Gotify, and Ntfy agents which have a configurable locale option for channel/system notifications, Telegram's system notification (sent to chatId) always uses the default English locale since no locale is passed to getNotificationPayload().

Consider adding a locale option to NotificationAgentTelegram.options for consistency with other agents, or document this as intentional behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/telegram.ts` at line 199,
System/system-channel Telegram notifications call getNotificationPayload(type,
payload) without passing a locale, causing English-only messages; update
NotificationAgentTelegram to accept a locale option on
NotificationAgentTelegram.options (consistent with Slack/Discord/Gotify/Ntfy)
and pass that locale into getNotificationPayload(type, payload, locale) when
sending to chatId, falling back to default config or request-level locale if not
provided; ensure the option name matches other agents (e.g., "locale") and
update the send flow that constructs notificationPayload to forward the
configured/derived locale.
server/lib/notifications/agents/webpush.ts (1)

291-296: Consider adding a locale validation helper for type safety.

The as AvailableLocale cast is technically unsafe since UserSettings.locale is typed as string (and defaults to empty string). While getIntl() gracefully handles invalid values by falling back to 'en', explicit validation would improve type safety.

This is not a bug since the runtime handles it correctly, but a shared helper could be beneficial across all notification agents:

// In server/lib/i18n/index.ts
export function toAvailableLocale(locale?: string): AvailableLocale | undefined {
  return availableLocales.includes(locale as AvailableLocale) ? locale as AvailableLocale : undefined;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/webpush.ts` around lines 291 - 296, Add a
small validation helper (toAvailableLocale) in your i18n module that accepts a
string and returns AvailableLocale | undefined (matching availableLocales), then
replace the unsafe cast in webpush.ts: when building pushSubs (where pushSubs
and notifySubs are used and payload.notifyUser?.settings?.locale is read) call
toAvailableLocale(payload.notifyUser?.settings?.locale) and pass that result (or
let getIntl() handle undefined fallback) instead of using "as AvailableLocale";
update imports to reference toAvailableLocale and keep getIntl usage unchanged
so invalid locales safely become undefined and default to 'en' at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/lib/i18n/index.ts`:
- Around line 34-36: Ensure the English locale is present at startup and fail
fast: in initI18n() after loading locales validate that intls.get('en') exists
(or intls.has('en')) and if not throw a clear Error like "English locale failed
to initialize" so the process won't silently continue; additionally make
getIntl(locale?: AvailableLocale): IntlInstance stop using the non-null
assertion and either return the found IntlInstance or throw an explanatory Error
referencing getIntl and AvailableLocale/IntlInstance when both the requested
locale and fallback 'en' are missing.

In `@server/lib/notifications/agents/discord.ts`:
- Line 182: The code is using the hardcoded IssueTypeName mapping for the issue
type value (IssueTypeName[payload.issue.issueType]) which is not localized;
replace that usage with a localized string via the existing intl.formatMessage
flow (e.g., derive a message id from the issue type and call
intl.formatMessage(...) or introduce an IssueTypeMessageMap that maps
payload.issue.issueType to message descriptors used by intl.formatMessage), and
update the value expression in the Discord payload to call intl.formatMessage
with that descriptor so the issue type label matches other localized labels.

In `@server/lib/notifications/agents/gotify.ts`:
- Line 93: The message currently uses the hardcoded English enum lookup
IssueTypeName[payload.issue.issueType]; change this to use localized strings via
intl.formatMessage instead (either by mapping IssueType enum values to message
IDs in globalMessages like
globalMessages.issueTypeName.video/audio/subtitle/other or by creating a small
helper function such as getLocalizedIssueType(issueType) that switches on
payload.issue.issueType and returns intl.formatMessage(...) for each case).
Update the code that builds the notification (the line using
IssueTypeName[payload.issue.issueType]) to call that helper or the appropriate
intl.formatMessage key so the issue type is localized consistently with the
surrounding labels.

In `@server/lib/notifications/agents/pushbullet.ts`:
- Line 92: The notification currently appends a hardcoded English issue type via
IssueTypeName[payload.issue.issueType]; replace this with a localized string by
mapping the IssueTypeName enum to message descriptors and calling
intl.formatMessage for the chosen descriptor. Update the pushbullet.ts
construction where body is appended (the line using IssueTypeName and
payload.issue.issueType) to use
intl.formatMessage(issueTypeMessages[payload.issue.issueType]) or similar, add a
new issueTypeMessages map of message descriptors keyed by the same IssueTypeName
enum values, and reuse globalMessages.issueType for the label so the issue type
itself is localized via intl.formatMessage.

In `@server/lib/notifications/agents/pushover.ts`:
- Around line 265-269: The code unsafely casts
payload.notifyUser.settings?.locale to AvailableLocale when calling
getNotificationPayload; instead, validate or normalize the locale before passing
it (e.g., use a helper like normalizeLocale/validateLocale that returns a safe
AvailableLocale or a default such as 'en'), or let getNotificationPayload accept
a string and perform the validation there; update the call site around
getNotificationPayload to pass the validated/fallback locale (and reference
payload.notifyUser.settings?.locale and AvailableLocale when making the change).

In `@server/lib/notifications/agents/telegram.ts`:
- Around line 131-133: The code uses IssueTypeName[payload.issue.issueType]
(hardcoded English) inside the message assembly; change it to use the intl
system: create a mapping of IssueType -> MessageDescriptor (e.g.,
IssueTypeMessageDefs or IssueTypeMessageIds) and replace
IssueTypeName[payload.issue.issueType] with
intl.formatMessage(IssueTypeMessageDefs[payload.issue.issueType]) (then pass
that into this.escapeText as before). Update the message build in telegram.ts
where message is concatenated (the line using this.escapeText and
intl.formatMessage(globalMessages.issueType)) to use the new
IssueTypeMessageDefs map and ensure the keys match payload.issue.issueType.

In `@src/components/Settings/Notifications/NotificationsSlack/index.tsx`:
- Line 67: The locale field in the NotificationsSlack component is set to
data.options.locale without a fallback, which can lead to an undefined default
for existing installs; update the locale assignment (in the object construction
inside NotificationsSlack / wherever locale: data.options.locale is used) to use
a fallback like data?.options.locale ?? 'en' (or data?.options.locale || 'en')
so the select always has a default value.

---

Outside diff comments:
In `@src/components/Settings/Notifications/NotificationsNtfy/index.tsx`:
- Around line 164-177: The test request body currently includes options.locale
but the backend sendTestNotification handler ignores that and uses
user.settings?.locale; remove the misleading locale field from the frontend test
POST call (the axios.post to '/api/v1/settings/notifications/ntfy/test' in
NotificationsNtfy component) so the UI doesn't send options.locale, or
alternatively update the backend sendTestNotification to read and use the
incoming options.locale instead of user.settings?.locale; choose one approach
and implement it consistently so the locale selector actually affects test
notifications or is not sent.

---

Nitpick comments:
In `@server/lib/i18n/extractMessages.ts`:
- Around line 44-46: The processMessages function calls getFiles and passes
every file under the dir to extractMessages, causing unnecessary I/O on
non-source files; update processMessages to filter the files list returned by
getFiles by allowed extensions (e.g., .ts, .tsx, .js, .jsx) before calling
extractMessages so only relevant source files are processed, leaving getFiles
and extractMessages unchanged and using the existing variables files and
extractedMessagesGroups to wire the filtered list through.
- Line 19: The current const regex in extractMessages.ts that matches
defineMessages(...) is too fragile (it will break if message values contain '}'
or complex templates); replace the regex-based extraction with an AST-based
parse: parse the source file (using TypeScript compiler API or
`@babel/parser/`@typescript-eslint/parser), locate CallExpressions named
defineMessages, read the first argument (the message namespace key) and the
object literal second argument to safely extract keys and string/value nodes,
and then produce the same output structure; update the code that referenced the
const regex to instead call the new AST extraction routine so defineMessages
parsing is robust against embedded braces and complex expressions.

In `@server/lib/notifications/agents/ntfy.ts`:
- Around line 28-34: The code redundantly calls getSettings() again to read
embedPoster; instead use the already-stored settings variable (from const
settings = this.getSettings()) to access
settings.notifications.agents.ntfy.embedPoster, and remove the extra
getSettings() invocation so embedPoster is assigned from settings rather than
calling getSettings() a second time.
- Line 71: The IssueTypeName lookup is not localized; replace the direct use of
IssueTypeName[payload.issue.issueType] when building message (the line appending
to message using intl.formatMessage(globalMessages.issueType)) with a localized
string: add per-issue-type keys to the i18n catalog (e.g., messages for
Video/Audio/Subtitle/Other), then map payload.issue.issueType to
intl.formatMessage(...) for the corresponding key (or use a small helper that
converts IssueTypeName enum values to intl keys and returns a localized
fallback). Ensure you reference IssueTypeName and payload.issue.issueType when
creating the mapping and use intl.formatMessage to produce the localized issue
type in the notification message.

In `@server/lib/notifications/agents/pushbullet.ts`:
- Line 132: Add a locale option to NotificationAgentPushbullet.options and
ensure system notifications use it: update the
NotificationAgentPushbullet.options type to include an optional locale (e.g.,
string), read this.options.locale when constructing messages, and pass that
locale into getNotificationPayload or assign it on the notificationPayload
object before sending (the line creating notificationPayload via
this.getNotificationPayload(type, payload)). Ensure default behavior remains
unchanged if locale is undefined so other paths still work.

In `@server/lib/notifications/agents/slack.ts`:
- Around line 68-71: The redundant getSettings() call should be removed: use the
already-cached settings from const settings = this.getSettings() to read
notifications.agents.slack.embedPoster instead of calling getSettings() again;
update the code that sets embedPoster to reference
settings.notifications.agents.slack.embedPoster (or settings.options/ main where
appropriate) so all settings come from the single settings variable.

In `@server/lib/notifications/agents/telegram.ts`:
- Line 199: System/system-channel Telegram notifications call
getNotificationPayload(type, payload) without passing a locale, causing
English-only messages; update NotificationAgentTelegram to accept a locale
option on NotificationAgentTelegram.options (consistent with
Slack/Discord/Gotify/Ntfy) and pass that locale into
getNotificationPayload(type, payload, locale) when sending to chatId, falling
back to default config or request-level locale if not provided; ensure the
option name matches other agents (e.g., "locale") and update the send flow that
constructs notificationPayload to forward the configured/derived locale.

In `@server/lib/notifications/agents/webpush.ts`:
- Around line 291-296: Add a small validation helper (toAvailableLocale) in your
i18n module that accepts a string and returns AvailableLocale | undefined
(matching availableLocales), then replace the unsafe cast in webpush.ts: when
building pushSubs (where pushSubs and notifySubs are used and
payload.notifyUser?.settings?.locale is read) call
toAvailableLocale(payload.notifyUser?.settings?.locale) and pass that result (or
let getIntl() handle undefined fallback) instead of using "as AvailableLocale";
update imports to reference toAvailableLocale and keep getIntl usage unchanged
so invalid locales safely become undefined and default to 'en' at runtime.

In `@src/components/Settings/Notifications/NotificationsNtfy/index.tsx`:
- Around line 340-345: Replace the hardcoded English priority labels inside the
<Field> options (values 1–5) with localized strings by calling
intl.formatMessage(...) for each label; update the five option children to use
intl.formatMessage({ id: 'notifications.priority.minimum' }) etc. (or existing
message IDs), ensuring the component imports/uses the useIntl hook (intl) where
the <Field> is rendered so labels reflect the selected "Notification Language".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b64fa953-41d9-4315-b64a-f2e7194f8aba

📥 Commits

Reviewing files that changed from the base of the PR and between 25e376c and d6a715c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • .github/workflows/ci.yml
  • bin/check-i18n.js
  • package.json
  • server/index.ts
  • server/lib/i18n/extractMessages.ts
  • server/lib/i18n/globalMessages.ts
  • server/lib/i18n/index.ts
  • server/lib/i18n/locale/en.json
  • server/lib/notifications/agents/discord.ts
  • server/lib/notifications/agents/email.ts
  • server/lib/notifications/agents/gotify.ts
  • server/lib/notifications/agents/ntfy.ts
  • server/lib/notifications/agents/pushbullet.ts
  • server/lib/notifications/agents/pushover.ts
  • server/lib/notifications/agents/slack.ts
  • server/lib/notifications/agents/telegram.ts
  • server/lib/notifications/agents/webpush.ts
  • server/lib/settings/index.ts
  • server/routes/settings/notifications.ts
  • server/types/languages.d.ts
  • server/types/languages.ts
  • src/components/Settings/Notifications/NotificationsDiscord.tsx
  • src/components/Settings/Notifications/NotificationsGotify/index.tsx
  • src/components/Settings/Notifications/NotificationsNtfy/index.tsx
  • src/components/Settings/Notifications/NotificationsSlack/index.tsx
  • src/i18n/globalMessages.ts
💤 Files with no reviewable changes (1)
  • server/types/languages.d.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
server/lib/notifications/agents/webpush.ts (2)

291-296: Type cast may mask empty string locale.

The locale field in UserSettings defaults to an empty string (per the entity definition). While getIntl has a defensive fallback that handles this at runtime, the as AvailableLocale cast bypasses type safety. Consider using a helper that normalizes empty strings to undefined:

💡 Suggested approach
// Helper at module level or in i18n module
const toLocale = (locale?: string): AvailableLocale | undefined =>
  locale ? (locale as AvailableLocale) : undefined;

// Usage
pushSubs.push(
  ...notifySubs.map((sub) => ({
    sub,
    locale: toLocale(payload.notifyUser?.settings?.locale),
  }))
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/webpush.ts` around lines 291 - 296, The
current mapping that pushes notifySubs uses a direct cast (as AvailableLocale)
on payload.notifyUser?.settings?.locale which can mask empty-string locales;
create a small normalizer (e.g., toLocale) that takes a possibly-empty string
and returns AvailableLocale | undefined (returning undefined for
empty/null/undefined), then replace the cast in the pushSubs.push mapping to
call toLocale(payload.notifyUser?.settings?.locale); ensure getIntl continues to
receive undefined for missing locales rather than an empty string.

32-33: Consider consolidating with globalMessages definitions.

These messages differ from globalMessages.viewIssue and globalMessages.viewMedia (which include {applicationTitle}). This creates duplicate translation keys for the same concept. If the simpler versions without the application title are intentional for WebPush (e.g., due to space constraints), consider documenting this or adding a comment explaining the divergence.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/webpush.ts` around lines 32 - 33, The
webpush-specific message entries viewIssue and viewMedia duplicate
globalMessages.viewIssue and globalMessages.viewMedia (which include
{applicationTitle}); either consolidate by removing these local keys and
referencing globalMessages.viewIssue/globalMessages.viewMedia where webpush.ts
uses them, or if the shorter forms are intentional (space constraints), add a
comment above the viewIssue/viewMedia definitions explaining why they diverge
from globalMessages and link to the global keys for translators; update any
places that read these keys to use the chosen source to avoid duplicate
translation keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/lib/notifications/agents/webpush.ts`:
- Around line 388-396: The Promise.all call is not actually waiting for sends
because webPushNotification is not awaited; update the pushSubs mapping so each
async callback returns/awaits the webPushNotification promise (e.g., inside the
pushSubs.map callback that currently builds notificationPayload call await
webPushNotification(sub, notificationPayload) or return
webPushNotification(...)) so Promise.all waits for all web push sends to
complete; reference functions/ids: pushSubs, webPushNotification,
getNotificationPayload, and the surrounding Promise.all.
- Around line 343-365: The Promise.all call currently calls webPushNotification
inside an async map callback but doesn't await or return its promise, causing
fire-and-forget behavior; update the callback in the allSubs.map used by
Promise.all so it returns/awaits the promise from webPushNotification(sub,
notificationBadgePayload) (i.e., ensure the async callback either uses "return
await webPushNotification(...)" or simply "return webPushNotification(...)" so
Promise.all waits for all web push operations to complete), keeping the
surrounding logic that builds notificationBadgePayload via
this.getNotificationPayload unchanged.

---

Nitpick comments:
In `@server/lib/notifications/agents/webpush.ts`:
- Around line 291-296: The current mapping that pushes notifySubs uses a direct
cast (as AvailableLocale) on payload.notifyUser?.settings?.locale which can mask
empty-string locales; create a small normalizer (e.g., toLocale) that takes a
possibly-empty string and returns AvailableLocale | undefined (returning
undefined for empty/null/undefined), then replace the cast in the pushSubs.push
mapping to call toLocale(payload.notifyUser?.settings?.locale); ensure getIntl
continues to receive undefined for missing locales rather than an empty string.
- Around line 32-33: The webpush-specific message entries viewIssue and
viewMedia duplicate globalMessages.viewIssue and globalMessages.viewMedia (which
include {applicationTitle}); either consolidate by removing these local keys and
referencing globalMessages.viewIssue/globalMessages.viewMedia where webpush.ts
uses them, or if the shorter forms are intentional (space constraints), add a
comment above the viewIssue/viewMedia definitions explaining why they diverge
from globalMessages and link to the global keys for translators; update any
places that read these keys to use the chosen source to avoid duplicate
translation keys.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 209dc48e-06f5-4766-a784-9252cb0ec26b

📥 Commits

Reviewing files that changed from the base of the PR and between 3392d30 and 64d45a2.

📒 Files selected for processing (3)
  • server/lib/notifications/agents/webpush.ts
  • src/components/Settings/Notifications/NotificationsNtfy/index.tsx
  • src/components/Settings/Notifications/NotificationsSlack/index.tsx

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
server/lib/i18n/extractMessages.ts (2)

71-73: Unhandled promise rejection may cause silent failures.

saveMessages() returns a promise that is neither awaited nor has error handling. If extraction or file writing fails, the error won't be caught, potentially causing the script to exit silently without proper error reporting.

♻️ Proposed fix: Add error handling
-saveMessages();
+saveMessages().catch((err) => {
+  console.error('Failed to extract messages:', err);
+  process.exit(1);
+});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/i18n/extractMessages.ts` around lines 71 - 73, The call to
saveMessages() is fire-and-forget which can hide extraction/file write errors;
change the module to handle the returned promise from saveMessages (either await
it inside an async IIFE or attach a .catch handler) and log or rethrow the error
so failures are visible; ensure you use the existing logging/error utilities (or
console.error) and exit non‑zero on failure so saveMessages() errors are not
swallowed.

19-20: Regex design assumes one defineMessages call per file, which holds true in practice.

The regex on line 19 lacks the global (g) flag, meaning content.match(regex) returns only the first match. While this design choice creates a vulnerability to multiple defineMessages calls in a single file, verification shows no TypeScript files in the ./server directory currently contain multiple calls. If this pattern is guaranteed by design or convention, the code is safe as-is; otherwise, adding the global flag would make the extraction more robust.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/i18n/extractMessages.ts` around lines 19 - 20, The current regex
declaration (regex) used to extract defineMessages only matches the first
occurrence because content.match(regex) is not global; update the extraction to
support multiple defineMessages by making the regex global or using an iteration
method (e.g., RegExp.prototype.exec in a loop) so all defineMessages(...) blocks
are captured, and adjust subsequent logic that uses match to handle multiple
matches; refer to the regex variable and the defineMessages usage in this file
(extractMessages.ts) when implementing the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/lib/i18n/extractMessages.ts`:
- Around line 44-61: processMessages currently builds the final JSON by
iterating namespaces in extraction order (extractedMessagesGroups), which
depends on filesystem order; make the output deterministic by sorting the final
result keys before stringifying—after populating result in processMessages
(variable name result, function processMessages, and loop over
extractedMessagesGroups) compute a sorted list of Object.keys(result).sort() and
produce the JSON using that deterministic order (or use JSON.stringify with a
replacer that orders keys) so the produced string is stable across runs.

---

Nitpick comments:
In `@server/lib/i18n/extractMessages.ts`:
- Around line 71-73: The call to saveMessages() is fire-and-forget which can
hide extraction/file write errors; change the module to handle the returned
promise from saveMessages (either await it inside an async IIFE or attach a
.catch handler) and log or rethrow the error so failures are visible; ensure you
use the existing logging/error utilities (or console.error) and exit non‑zero on
failure so saveMessages() errors are not swallowed.
- Around line 19-20: The current regex declaration (regex) used to extract
defineMessages only matches the first occurrence because content.match(regex) is
not global; update the extraction to support multiple defineMessages by making
the regex global or using an iteration method (e.g., RegExp.prototype.exec in a
loop) so all defineMessages(...) blocks are captured, and adjust subsequent
logic that uses match to handle multiple matches; refer to the regex variable
and the defineMessages usage in this file (extractMessages.ts) when implementing
the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 58bdf53b-e121-4ca2-a8f6-a2dc244e9f9d

📥 Commits

Reviewing files that changed from the base of the PR and between 64d45a2 and 5d291b1.

📒 Files selected for processing (5)
  • server/lib/i18n/extractMessages.ts
  • server/lib/i18n/index.ts
  • server/lib/notifications/agents/ntfy.ts
  • server/lib/notifications/agents/slack.ts
  • server/lib/notifications/agents/webpush.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/lib/notifications/agents/ntfy.ts
  • server/lib/notifications/agents/slack.ts
  • server/lib/i18n/index.ts

@github-actions github-actions bot added the i18n-out-of-sync User forgot to run pnpm i18n:extract label Mar 20, 2026
@github-actions
Copy link
Copy Markdown

The i18n check failed because translation messages are out of sync.

This usually happens when you've added or modified translation strings in your code but haven't updated the translation file.

Please run pnpm i18n:extract and/or pnpm i18n:extract:server and commit the changes.

@github-actions github-actions bot removed the i18n-out-of-sync User forgot to run pnpm i18n:extract label Mar 20, 2026
@fallenbagel fallenbagel requested a review from Copilot March 20, 2026 01:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 30 out of 33 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
server/lib/i18n/extractMessages.ts (2)

4-13: Consider using Array.flat() for cleaner flattening.

The Array.prototype.concat(...files) pattern works, but flat() is more idiomatic:

-  return Array.prototype.concat(...files);
+  return files.flat();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/i18n/extractMessages.ts` around lines 4 - 13, In getFiles, replace
the final flatten step that uses Array.prototype.concat(...files) with the more
idiomatic files.flat() (depth 1) to flatten the Promise.all result; ensure the
files variable (from Promise.all in getFiles) is treated as (string |
string[])[] so calling files.flat() yields string[] — update the return to use
files.flat() to produce the flattened list of file paths.

19-21: Consider using matchAll() for future-proofing if multiple defineMessages calls may be added to any file.

While the current code uses .match() which returns only the first match (ignoring subsequent defineMessages calls), no server files currently contain multiple calls, so this is not an active issue. However, the suggested refactoring using matchAll() with the global flag and returning an array of results would provide defensive programming and prevent silent failures if multiple definitions are added in the future.

Additionally, the silent error swallowing in the catch block (line 38) should log or handle failures explicitly to aid debugging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/i18n/extractMessages.ts` around lines 19 - 21, The current
extraction uses content.match with a single-match regex for defineMessages which
will miss multiple definitions; change the logic in extractMessages to use
content.matchAll with a global regex (modify the regex to include the /g flag)
and iterate over all matches to build and return an array of extracted message
blocks instead of a single match, referencing the existing defineMessages
pattern and regex variable; also replace the silent catch in the try/catch that
currently swallows errors with an explicit error handling call (e.g.,
processLogger.error or console.error) that logs the caught error and context so
failures in parsing are visible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/lib/i18n/extractMessages.ts`:
- Line 71: The call to saveMessages() is unhandled and may reject silently;
modify the code that invokes saveMessages() (the saveMessages function call) to
properly handle rejections by awaiting it inside a try/catch or by returning its
promise and attaching a .catch handler; in the catch block log the error (use
the existing logger/processLogger) and rethrow or exit with a non‑zero status so
CI sees the failure.
- Around line 37-39: The catch block in server/lib/i18n/extractMessages.ts is
silently swallowing JSON parse errors and returning null; modify the catch to
capture the error (e.g., catch (err)) and log meaningful context before
returning null — include the error message and stack plus the file identifier
and/or contents used for parsing (e.g., filePath or fileContents variables used
in the surrounding function) via your project logger or console.error so callers
can diagnose which file and content caused the failure.

---

Nitpick comments:
In `@server/lib/i18n/extractMessages.ts`:
- Around line 4-13: In getFiles, replace the final flatten step that uses
Array.prototype.concat(...files) with the more idiomatic files.flat() (depth 1)
to flatten the Promise.all result; ensure the files variable (from Promise.all
in getFiles) is treated as (string | string[])[] so calling files.flat() yields
string[] — update the return to use files.flat() to produce the flattened list
of file paths.
- Around line 19-21: The current extraction uses content.match with a
single-match regex for defineMessages which will miss multiple definitions;
change the logic in extractMessages to use content.matchAll with a global regex
(modify the regex to include the /g flag) and iterate over all matches to build
and return an array of extracted message blocks instead of a single match,
referencing the existing defineMessages pattern and regex variable; also replace
the silent catch in the try/catch that currently swallows errors with an
explicit error handling call (e.g., processLogger.error or console.error) that
logs the caught error and context so failures in parsing are visible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53df06a9-fac4-4399-9dfe-71b5bfed6c61

📥 Commits

Reviewing files that changed from the base of the PR and between 5d291b1 and d059903.

📒 Files selected for processing (3)
  • package.json
  • server/lib/i18n/extractMessages.ts
  • server/lib/i18n/locale/en.json
✅ Files skipped from review due to trivial changes (1)
  • server/lib/i18n/locale/en.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json

@github-actions github-actions bot added the merge conflict Cannot merge due to merge conflicts label Mar 28, 2026
@github-actions
Copy link
Copy Markdown

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

@seerr-automation-bot seerr-automation-bot added this to the v3.2.0 milestone Mar 28, 2026
@fallenbagel fallenbagel force-pushed the fallenbagel/feat/server-i18n branch from 329f904 to f5ec45b Compare March 28, 2026 19:25
@github-actions github-actions bot removed the merge conflict Cannot merge due to merge conflicts label Mar 28, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
server/lib/notifications/agents/email.ts (1)

19-21: Consider using globalMessages for shared message definitions.

The issueType and issue messages (lines 20-21) duplicate definitions that exist in globalMessages as issueTypeName and issue. While functionally correct, consolidating to use globalMessages would reduce duplication and ensure consistency across agents.

♻️ Optional consolidation
 const messages = defineMessages('notifications.agents.email', {
-  issueType: '{type} issue',
-  issue: 'issue',
   pendingRequest:
     'A new request for the following {mediaType} is pending approval:',
   // ... rest of messages
 });

Then use globalMessages.issueTypeName and globalMessages.issue in the buildMessage() method (lines 203-208), similar to how webpush.ts does it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/lib/notifications/agents/email.ts` around lines 19 - 21, Replace the
duplicated local messages in defineMessages('notifications.agents.email', {
issueType, issue }) with references to the shared globalMessages: remove or stop
exporting the local issueType/issue entries and import/use
globalMessages.issueTypeName and globalMessages.issue inside the buildMessage()
implementation of the Email agent (function/method buildMessage in email.ts);
ensure any code that previously referenced messages.issueType or messages.issue
now references globalMessages.issueTypeName and globalMessages.issue to keep
naming consistent and eliminate duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/lib/notifications/agents/email.ts`:
- Around line 19-21: Replace the duplicated local messages in
defineMessages('notifications.agents.email', { issueType, issue }) with
references to the shared globalMessages: remove or stop exporting the local
issueType/issue entries and import/use globalMessages.issueTypeName and
globalMessages.issue inside the buildMessage() implementation of the Email agent
(function/method buildMessage in email.ts); ensure any code that previously
referenced messages.issueType or messages.issue now references
globalMessages.issueTypeName and globalMessages.issue to keep naming consistent
and eliminate duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7575fcb5-cc6e-41b3-903a-b49e7cfe301c

📥 Commits

Reviewing files that changed from the base of the PR and between 329f904 and f5ec45b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • .github/workflows/ci.yml
  • bin/check-i18n.js
  • docs/using-seerr/notifications/discord.md
  • docs/using-seerr/notifications/email.md
  • docs/using-seerr/notifications/gotify.md
  • docs/using-seerr/notifications/ntfy.md
  • docs/using-seerr/notifications/slack.md
  • package.json
  • server/index.ts
  • server/lib/i18n/extractMessages.ts
  • server/lib/i18n/globalMessages.ts
  • server/lib/i18n/index.ts
  • server/lib/i18n/locale/en.json
  • server/lib/notifications/agents/discord.ts
  • server/lib/notifications/agents/email.ts
  • server/lib/notifications/agents/gotify.ts
  • server/lib/notifications/agents/ntfy.ts
  • server/lib/notifications/agents/pushbullet.ts
  • server/lib/notifications/agents/pushover.ts
  • server/lib/notifications/agents/slack.ts
  • server/lib/notifications/agents/telegram.ts
  • server/lib/notifications/agents/webpush.ts
  • server/lib/settings/index.ts
  • server/routes/settings/notifications.ts
  • server/types/languages.d.ts
  • server/types/languages.ts
  • src/components/Settings/Notifications/NotificationsDiscord.tsx
  • src/components/Settings/Notifications/NotificationsGotify/index.tsx
  • src/components/Settings/Notifications/NotificationsNtfy/index.tsx
  • src/components/Settings/Notifications/NotificationsSlack/index.tsx
  • src/i18n/globalMessages.ts
  • src/i18n/locale/en.json
💤 Files with no reviewable changes (1)
  • server/types/languages.d.ts
✅ Files skipped from review due to trivial changes (14)
  • docs/using-seerr/notifications/ntfy.md
  • docs/using-seerr/notifications/slack.md
  • docs/using-seerr/notifications/discord.md
  • docs/using-seerr/notifications/gotify.md
  • src/i18n/globalMessages.ts
  • server/index.ts
  • docs/using-seerr/notifications/email.md
  • server/routes/settings/notifications.ts
  • server/types/languages.ts
  • src/components/Settings/Notifications/NotificationsDiscord.tsx
  • server/lib/i18n/globalMessages.ts
  • .github/workflows/ci.yml
  • server/lib/i18n/locale/en.json
  • src/components/Settings/Notifications/NotificationsSlack/index.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/components/Settings/Notifications/NotificationsGotify/index.tsx
  • server/lib/notifications/agents/ntfy.ts
  • server/lib/notifications/agents/pushbullet.ts
  • src/components/Settings/Notifications/NotificationsNtfy/index.tsx
  • bin/check-i18n.js
  • package.json
  • server/lib/notifications/agents/discord.ts
  • server/lib/settings/index.ts
  • server/lib/i18n/index.ts

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.

Translation of request statuses in Telegram notifications Missing translation for email notification

4 participants