feat(bouncer-networks): protocol + store foundation (1/3)#214
feat(bouncer-networks): protocol + store foundation (1/3)#214ValwareIRC wants to merge 15 commits into
Conversation
Adds the soju.im/bouncer-networks protocol layer plus a typed store
reducer. No UI surface yet -- that's PR C. No connection-binding logic
either -- that's PR B. This PR is intentionally headless so it can
land cleanly and be reviewed on its own merits.
What's in:
- src/lib/bouncerAttrs.ts -- codec for the message-tag-style attribute
payload (key=value;key=value with \s \: \\ \r \n escapes). The
encode and decode are symmetric and tolerate the corner cases in
the spec (bare-key = empty value, attr=<empty> = deletion, repeated
semicolons).
- src/lib/irc/handlers/bouncer.ts -- protocol dispatcher for the
BOUNCER command (NETWORK / ADDNETWORK / CHANGENETWORK / DELNETWORK)
and the FAIL BOUNCER standard-replies variant. Decodes the spec
context shapes for INVALID_NETID, INVALID_ATTRIBUTE,
READ_ONLY_ATTRIBUTE, UNKNOWN_ATTRIBUTE, NEED_ATTRIBUTE.
- IRCClient: BOUNCER_NETWORK, BOUNCER_{ADD,CHANGE,DEL}NETWORK_OK and
BOUNCER_FAIL added to EventMap. bouncerListNetworks /
bouncerBind / bouncerAddNetwork / bouncerChangeNetwork /
bouncerDelNetwork added as send helpers.
- handlers/index.ts: BOUNCER routed to the new handler, FAIL routed
to handleBouncerFail when parv[0] === BOUNCER (same per-command
fan-out trick we use for FAIL METADATA).
- src/types/index.ts: BouncerNetwork + BouncerState types.
- src/store/handlers/bouncer.ts: reducer that upserts/merges networks,
treats deleted=true and attr=<empty> per spec, picks up the
soju.im/bouncer-networks and -notify caps on CAP ACK, watches
BOUNCER_NETID ISUPPORT, and flips state.listed=true at the end of
the soju.im/bouncer-networks BATCH.
- src/store/index.ts: bouncers: Record<serverId, BouncerState> in
state. Public actions bouncerListNetworks / bouncerAddNetwork /
bouncerChangeNetwork / bouncerDelNetwork that wrap the wire encode.
- CAP REQ list updated to ask for soju.im/bouncer-networks and
soju.im/bouncer-networks-notify.
What's NOT in (deliberate; comes in PR B / C):
- BOUNCER BIND lifecycle: connecting to a bouncer and opening child
connections per upstream network.
- UI: there is no surface yet. The store reducer covers add / change /
delete / list / errors so PR C can be a pure UI layer.
Coverage:
- tests/lib/bouncerAttrs.test.ts (10) -- encode/decode round-trips,
spec quirks (bare keys, repeated semicolons, unknown escapes).
- tests/protocol/bouncer.test.ts (9) -- BOUNCER NETWORK / ADD /
CHANGE / DEL dispatch, batch-tag carry-through,
case-insensitive subcommand match, FAIL BOUNCER variants for each
spec error code.
- tests/store/bouncer.test.ts (8) -- merge / partial-update / attr
deletion / network deletion / lastError / ISUPPORT BOUNCER_NETID /
batch-end listed-flag.
Full suite green (812 / 1 skipped), build clean.
|
PR changed again? Review this PR in Change Stack to compare snapshots and stay oriented. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Soju bouncer multi-network support: attribute codec, IRC BOUNCER and FAIL handlers, IRCClient bouncer commands with CAP-END bind sequencing, Zustand store handlers/actions, UI (panel + form), tests, and updated locale bundles. ChangesBouncer Multi-Network Feature
Estimated code review effort: Suggested reviewers:
Poem:
✨ Finishing Touches🧪 Generate unit tests (beta)
|
|
Automated deployment preview for the PR in the Cloudflare Pages. |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@src/lib/bouncerAttrs.ts`:
- Around line 39-42: encodeBouncerAttrs trusts attribute keys and can emit
malformed BOUNCER commands if keys contain forbidden chars; add a validator in
encodeBouncerAttrs that checks each key (in Object.entries) and rejects or
throws when it contains any of ;, =, whitespace, CR or LF (or otherwise invalid
characters), before performing escapeBouncerValue and joining, so only safe keys
are serialized; reference the encodeBouncerAttrs function and validate keys
prior to mapping/joining.
In `@src/store/handlers/bouncer.ts`:
- Around line 129-139: batchTypes is currently keyed only by batchId causing
collisions across servers; update the BATCH_START and BATCH_END handlers to
namespace keys by serverId (e.g. use `${serverId}:${batchId}` or a nested Map
keyed first by serverId then batchId) so lookups/deletes match the same
namespacing. Ensure the BATCH_START handler captures serverId from its event
payload (it currently only destructures { batchId, type }) and stores the
namespaced key when type === "soju.im/bouncer-networks", and update the
BATCH_END handler to check and delete using the same namespaced key before
calling store.setState/ensureBouncer.
In `@src/store/index.ts`:
- Around line 3782-3790: The bouncerAddNetwork and bouncerChangeNetwork handlers
currently pass all attributes (including server-owned read-only fields like
"state" and "error") to ircClient, causing READ_ONLY_ATTRIBUTE errors; update
these functions (bouncerAddNetwork and bouncerChangeNetwork) to filter out
read-only keys before calling encodeBouncerAttrs/ircClient (e.g., remove
"state", "error" and any other server-owned fields from attrs) so only writable
attributes are sent. Ensure the filtering happens immediately before
encodeBouncerAttrs is called so callers can keep full objects but writes send
only allowed fields.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 324f47e2-e9d1-4b10-8b5d-ba4905b77150
📒 Files selected for processing (11)
src/lib/bouncerAttrs.tssrc/lib/irc/IRCClient.tssrc/lib/irc/handlers/bouncer.tssrc/lib/irc/handlers/index.tssrc/store/handlers/bouncer.tssrc/store/handlers/index.tssrc/store/index.tssrc/types/index.tstests/lib/bouncerAttrs.test.tstests/protocol/bouncer.test.tstests/store/bouncer.test.ts
| export function encodeBouncerAttrs(attrs: Record<string, string>): string { | ||
| return Object.entries(attrs) | ||
| .map(([k, v]) => (v === "" ? k : `${k}=${escapeBouncerValue(v)}`)) | ||
| .join(";"); |
There was a problem hiding this comment.
Validate attribute keys before wire serialization.
encodeBouncerAttrs currently trusts keys verbatim. Keys containing ;, =, whitespace, or CR/LF can break token structure and produce malformed outbound BOUNCER commands. Add a strict key validator before join/encode.
Suggested fix
+const BOUNCER_ATTR_KEY_RE = /^[A-Za-z0-9._/-]+$/;
+
export function encodeBouncerAttrs(attrs: Record<string, string>): string {
return Object.entries(attrs)
+ .map(([k, v]) => {
+ if (!BOUNCER_ATTR_KEY_RE.test(k)) {
+ throw new Error(`Invalid bouncer attribute key: ${k}`);
+ }
+ return [k, v] as const;
+ })
.map(([k, v]) => (v === "" ? k : `${k}=${escapeBouncerValue(v)}`))
.join(";");
}🤖 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/bouncerAttrs.ts` around lines 39 - 42, encodeBouncerAttrs trusts
attribute keys and can emit malformed BOUNCER commands if keys contain forbidden
chars; add a validator in encodeBouncerAttrs that checks each key (in
Object.entries) and rejects or throws when it contains any of ;, =, whitespace,
CR or LF (or otherwise invalid characters), before performing escapeBouncerValue
and joining, so only safe keys are serialized; reference the encodeBouncerAttrs
function and validate keys prior to mapping/joining.
| const batchTypes = new Map<string, string>(); // batchId -> type | ||
| ircClient.on("BATCH_START", ({ batchId, type }) => { | ||
| if (type === "soju.im/bouncer-networks") batchTypes.set(batchId, type); | ||
| }); | ||
| ircClient.on("BATCH_END", ({ serverId, batchId }) => { | ||
| if (batchTypes.get(batchId) !== "soju.im/bouncer-networks") return; | ||
| batchTypes.delete(batchId); | ||
| store.setState((state) => ({ | ||
| bouncers: ensureBouncer(state, serverId, { listed: true }), | ||
| })); | ||
| }); |
There was a problem hiding this comment.
Batch ID collision across servers.
The batchTypes Map is keyed only by batchId, but batch IDs are only unique per connection, not globally. If multiple servers (bouncer + upstreams) use the same batch ID concurrently, the Map entries will collide, causing the listed flag to be set incorrectly or not at all.
🔒 Proposed fix to namespace batch IDs by serverId
- const batchTypes = new Map<string, string>(); // batchId -> type
+ const batchTypes = new Map<string, string>(); // "serverId:batchId" -> type
ircClient.on("BATCH_START", ({ serverId, batchId, type }) => {
- if (type === "soju.im/bouncer-networks") batchTypes.set(batchId, type);
+ if (type === "soju.im/bouncer-networks")
+ batchTypes.set(`${serverId}:${batchId}`, type);
});
ircClient.on("BATCH_END", ({ serverId, batchId }) => {
- if (batchTypes.get(batchId) !== "soju.im/bouncer-networks") return;
- batchTypes.delete(batchId);
+ const key = `${serverId}:${batchId}`;
+ if (batchTypes.get(key) !== "soju.im/bouncer-networks") return;
+ batchTypes.delete(key);
store.setState((state) => ({📝 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 batchTypes = new Map<string, string>(); // batchId -> type | |
| ircClient.on("BATCH_START", ({ batchId, type }) => { | |
| if (type === "soju.im/bouncer-networks") batchTypes.set(batchId, type); | |
| }); | |
| ircClient.on("BATCH_END", ({ serverId, batchId }) => { | |
| if (batchTypes.get(batchId) !== "soju.im/bouncer-networks") return; | |
| batchTypes.delete(batchId); | |
| store.setState((state) => ({ | |
| bouncers: ensureBouncer(state, serverId, { listed: true }), | |
| })); | |
| }); | |
| const batchTypes = new Map<string, string>(); // "serverId:batchId" -> type | |
| ircClient.on("BATCH_START", ({ serverId, batchId, type }) => { | |
| if (type === "soju.im/bouncer-networks") | |
| batchTypes.set(`${serverId}:${batchId}`, type); | |
| }); | |
| ircClient.on("BATCH_END", ({ serverId, batchId }) => { | |
| const key = `${serverId}:${batchId}`; | |
| if (batchTypes.get(key) !== "soju.im/bouncer-networks") return; | |
| batchTypes.delete(key); | |
| store.setState((state) => ({ | |
| bouncers: ensureBouncer(state, serverId, { listed: true }), | |
| })); | |
| }); |
🤖 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/store/handlers/bouncer.ts` around lines 129 - 139, batchTypes is
currently keyed only by batchId causing collisions across servers; update the
BATCH_START and BATCH_END handlers to namespace keys by serverId (e.g. use
`${serverId}:${batchId}` or a nested Map keyed first by serverId then batchId)
so lookups/deletes match the same namespacing. Ensure the BATCH_START handler
captures serverId from its event payload (it currently only destructures {
batchId, type }) and stores the namespaced key when type ===
"soju.im/bouncer-networks", and update the BATCH_END handler to check and delete
using the same namespaced key before calling store.setState/ensureBouncer.
| bouncerAddNetwork: (bouncerServerId, attrs) => { | ||
| ircClient.bouncerAddNetwork(bouncerServerId, encodeBouncerAttrs(attrs)); | ||
| }, | ||
| bouncerChangeNetwork: (bouncerServerId, netid, attrs) => { | ||
| ircClient.bouncerChangeNetwork( | ||
| bouncerServerId, | ||
| netid, | ||
| encodeBouncerAttrs(attrs), | ||
| ); |
There was a problem hiding this comment.
Strip read-only bouncer fields before ADD/CHANGE sends.
Line 3783 and Line 3789 encode and forward all attrs, including server-owned fields like state/error. If callers reuse stored network attrs, these writes will fail with READ_ONLY_ATTRIBUTE.
Suggested fix
-import { encodeBouncerAttrs } from "../lib/bouncerAttrs";
+import {
+ BOUNCER_READ_ONLY_ATTRIBUTES,
+ encodeBouncerAttrs,
+} from "../lib/bouncerAttrs";
+
+const toWritableBouncerAttrs = (attrs: Record<string, string>) =>
+ Object.fromEntries(
+ Object.entries(attrs).filter(
+ ([k]) => !BOUNCER_READ_ONLY_ATTRIBUTES.has(k),
+ ),
+ );
bouncerAddNetwork: (bouncerServerId, attrs) => {
- ircClient.bouncerAddNetwork(bouncerServerId, encodeBouncerAttrs(attrs));
+ ircClient.bouncerAddNetwork(
+ bouncerServerId,
+ encodeBouncerAttrs(toWritableBouncerAttrs(attrs)),
+ );
},
bouncerChangeNetwork: (bouncerServerId, netid, attrs) => {
ircClient.bouncerChangeNetwork(
bouncerServerId,
netid,
- encodeBouncerAttrs(attrs),
+ encodeBouncerAttrs(toWritableBouncerAttrs(attrs)),
);
},🤖 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/store/index.ts` around lines 3782 - 3790, The bouncerAddNetwork and
bouncerChangeNetwork handlers currently pass all attributes (including
server-owned read-only fields like "state" and "error") to ircClient, causing
READ_ONLY_ATTRIBUTE errors; update these functions (bouncerAddNetwork and
bouncerChangeNetwork) to filter out read-only keys before calling
encodeBouncerAttrs/ircClient (e.g., remove "state", "error" and any other
server-owned fields from attrs) so only writable attributes are sent. Ensure the
filtering happens immediately before encodeBouncerAttrs is called so callers can
keep full objects but writes send only allowed fields.
* feat(bouncer-networks): BIND lifecycle + child connections (PR B of 3) Stacks on PR #214 (protocol + store). Wires the connection lifecycle for soju.im/bouncer-networks: one bouncer endpoint can now host N child connections, each bound to a different upstream network via BOUNCER BIND before CAP END. Still headless -- UI lands in PR C. What's in: - IRCClient.sendCapEnd(serverId): centralised CAP END sender that emits a queued 'BOUNCER BIND <netid>' first when the serverId was flagged as a bouncer child via setPendingBouncerBind(). All six CAP END call sites (timeout, no-caps-to-request, post-CAP-ACK, CAP NAK, SASL success/fail, LinkSecurityWarning resume, post- account-registration) now flow through this one helper. - IRCClient.pendingConnections dedup keyed by serverId when one is given, falling back to host:port. Without this, multiple child connections sharing the bouncer endpoint would collapse onto a single Promise<Server>. - Server / ServerConfig: bouncerServerId, bouncerNetid, isBouncerControl fields plumbed through the type, persistence, and the initial Server seed in connectToSavedServers. - store.bouncerConnectNetwork(parentServerId, netid): the public API. Resolves the parent's credentials, derives a deterministic childId via uuidv5(parent:netid, CHANNEL_NAMESPACE), saves the child ServerConfig, seeds the child Server row in 'connecting' state, calls setPendingBouncerBind, and dispatches the IRCClient connect with the explicit childId. - connectToSavedServers: detects bouncer children at restore time and bypasses the store-level connect() wrapper (which would resolve them onto the parent's id via host:port lookup), going direct to ircClient.connect() with the explicit child id and a setPendingBouncerBind call beforehand. What's NOT in (deliberate; PR C): - UI for managing bouncer networks (list / add / change / delete). - Server-list rendering that groups child connections under their parent. - Modal for picking which networks to connect to. Coverage: - tests/protocol/bouncerBind.test.ts -- sendCapEnd emits CAP END alone when no BIND queued, BOUNCER BIND <netid> immediately before CAP END when queued, BIND is consumed (single-use), BINDs are scoped per serverId. - tests/store/bouncerConnect.test.ts -- queues BIND on a fresh childId before calling connect, seeds the child Server with parent linkage + 'connecting' state + friendly name from the BOUNCER NETWORK attributes, persists the child ServerConfig with the parent's SASL credentials, repeated calls are idempotent for persistence, returns undefined when the parent has no saved config. Full suite green (821 / 1 skipped), build clean. * feat(bouncer-networks): management UI (PR C of 3) (#216) * feat(bouncer-networks): management UI (PR C of 3) Adds the user-facing UI for the soju.im/bouncer-networks extension: - BouncerNetworksModal: list/add/edit modes with state badges (green/yellow-pulsing/gray dots), connected-first sort, inline-confirm delete, success highlight on ack, empty state - BouncerNetworkForm: TLS-on-default toggle, only-changed-attrs diff for edits, field-level error surfacing tied to FAIL BOUNCER, read-only attribute filtering - ServerList: "Manage Networks" affordance on bouncer-control servers (only shown when supported && not a child binding) - store: bouncerNetworksModalServerId UI slot + toggle action - App: globally mounted modal driven by store state Includes tests for form helpers (attrsToValues/valuesToAttrs diff logic) and modal interactions, plus translations across all 18 supported locales. * fix(bouncer-networks): match CAP_ACKNOWLEDGED per-cap event shape The auth-side CAP ACK splitter re-emits CAP_ACKNOWLEDGED once per cap with the cap name in `key` (and any =value in `capabilities`). My handler was treating the event as a bulk "ACK"/"NEW" tag with a space-separated cap list, so `key !== "ACK"` always returned early and `supported` never flipped to true — the Manage Networks button never appeared even when soju acked the cap. Fix: just check `key` against the two cap names directly. Update test to fire one event per cap to match the real emission pattern. * feat(bouncer-networks): auto-bind each upstream as a child server Previously you could add a network in the management modal and watch it sit there as a row, but there was no way to actually use the connection -- the BIND lifecycle from PR B was wired up but never dispatched. Now: on every BOUNCER_NETWORK upsert from a control session, fire bouncerConnectNetwork. The action is made idempotent (early-return if a child server already exists for the bouncer+netid pair) so this is safe to call on every snapshot, every notify update, and on reconnects. Result: networks added in the modal show up as their own server bubbles in the sidebar, ready to /join channels on the upstream. * fix(bouncer-networks): return undefined from idempotent bouncerConnectNetwork The early-return for the already-bound case was returning childId (a string) but the action's declared signature is Promise<Server | undefined>, breaking tsc. Return undefined -- no caller inspects the value. * feat(bouncer-networks): inline network browser in chat area + connect URL fix Replaces the floating layer-group icon + modal with a proper chat-area panel: select the bouncer in the sidebar and the main pane becomes a list of upstream networks with explicit Connect/Open buttons per row. Auto-connect-everything is gone -- the user picks which networks to bind to. Also fixes the actual binding bug: child connections were inheriting parent.host (the bare hostname after URL parsing) instead of the saved WSS URL with its /socket path, so wss://host:6662/socket children were hitting wss://host:6662/ and getting refused by soju. Use savedParent to preserve the original URL form. Drops: - BouncerNetworksModal + its test (replaced by BouncerNetworksPanel) - bouncerNetworksModalServerId UI slot + toggleBouncerNetworksModal action - FaLayerGroup overlay button on ServerIcon Adds: - BouncerNetworksPanel rendered when selected server is bouncer-control and no channel is selected - "Connect" button per row -> dispatches bouncerConnectNetwork + switches the sidebar selection to the new child server - "Open" button when the child is already present (just selects it) * fix(bouncer-networks): gate child reconnects on parent ready event connectToSavedServers used to fire parent + child reconnects in parallel at app startup. Both opened WSS to the same soju endpoint and raced their SASL/BIND handshakes against each other, surfacing as 'invalid password' against the child sessions even though the credentials were identical and the manual click path worked fine. Split the reconnect into two phases: parents (and standalone servers) go first, each child Server row is seeded into state immediately so the UI doesn't blink, then a one-shot `ready` listener dispatches every child of a parent as soon as that parent finishes registration. If a parent fails outright the listener detaches and its children are marked disconnected so the UI stops spinning. Tests pin: seeded child row, deferred dispatch, ready-fire dispatch, and that unrelated ready events are ignored.
There was a problem hiding this comment.
Actionable comments posted: 3
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (18)
src/components/ui/LinkSecurityWarningModal.tsx-124-126 (1)
124-126:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMark CAP negotiation complete before resuming the connection.
This path now sends
CAP ENDandUSER, but it never flipsircClient.capNegotiationCompletetotrue. Any code that checksisCapNegotiationComplete(serverId)will keep treating this session as mid-negotiation after the user accepts the warning. Set the flag here or route this through one helper that completes the whole CAP flow.🤖 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/components/ui/LinkSecurityWarningModal.tsx` around lines 124 - 126, The CAP flow resumes by calling ircClient.sendCapEnd(serverId) and ircClient.userOnConnect(serverId) but never flips the negotiation flag; update this path to mark the CAP negotiation complete by setting ircClient.capNegotiationComplete[serverId] = true (or call the existing helper that finalizes CAP negotiation) before or immediately after sendCapEnd so that isCapNegotiationComplete(serverId) returns true and subsequent logic treats the session as post-negotiation.src/locales/fr/messages.po-62-64 (1)
62-64:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFrench catalog still contains untranslated new UI strings.
These
msgstrvalues are empty, so the new bouncer UI falls back to English infr.Suggested translations
msgid "{0} network{1} — pick one to join" -msgstr "" +msgstr "{0} réseau{1} — choisissez-en un pour rejoindre" msgid "Back to network list" -msgstr "" +msgstr "Retour à la liste des réseaux" msgid "Open" -msgstr "" +msgstr "Ouvrir"Also applies to: 387-390, 1770-1773
🤖 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/locales/fr/messages.po` around lines 62 - 64, Update the French .po entries where msgstr is empty for the new UI strings (e.g. the msgid "{0} network{1} — pick one to join" and the other occurrences noted around lines 387-390 and 1770-1773) by supplying proper French translations and preserving interpolation placeholders ({0}, {1}) and punctuation/emdash exactly as in the msgid; ensure each corresponding msgstr is filled in the same file (messages.po) so the fr locale no longer falls back to English.src/components/ui/BouncerNetworkForm.tsx-166-171 (1)
166-171:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize the host placeholder through Lingui.
placeholder="irc.libera.chat"is user-visible and won’t be extracted for translations.Suggested fix
- placeholder="irc.libera.chat" + placeholder={t`irc.libera.chat`}As per coding guidelines "Wrap all user-visible text with LinguiJS
<Trans>for JSX children,tfor JSX props and simple strings, andtwith interpolation for variables".🤖 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/components/ui/BouncerNetworkForm.tsx` around lines 166 - 171, The placeholder string for the TextInput in BouncerNetworkForm.tsx is hard-coded and not localizable; import the Lingui helper (t) from '`@lingui/macro`' at the top of the file and replace placeholder="irc.libera.chat" with a Lingui-wrapped prop (use t for JSX props) so the host placeholder becomes localizable for translations for the TextInput component in BouncerNetworkForm.src/components/ui/BouncerNetworksPanel.tsx-27-34 (1)
27-34:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize connection-state labels instead of hardcoding English text.
STATE_COPYembeds user-visible labels at module scope, so they are never extracted to locale catalogs.Suggested direction
-const STATE_COPY: Record< - string, - { label: string; dotClass: string; pulse?: boolean } -> = { - connected: { label: "Connected", dotClass: "bg-green-400" }, - connecting: { label: "Connecting…", dotClass: "bg-yellow-400", pulse: true }, - disconnected: { label: "Disconnected", dotClass: "bg-discord-text-muted" }, -}; +// inside component body +const stateCopy = useMemo( + () => ({ + connected: { label: t`Connected`, dotClass: "bg-green-400" }, + connecting: { + label: t`Connecting…`, + dotClass: "bg-yellow-400", + pulse: true, + }, + disconnected: { label: t`Disconnected`, dotClass: "bg-discord-text-muted" }, + }), + [t], +);As per coding guidelines "Wrap all user-visible text with LinguiJS
<Trans>for JSX children,tfor JSX props and simple strings, andtwith interpolation for variables" and "Do not usetmacro at module scope; move translatable strings inside function bodies to ensure they evaluate afteri18n.activate()runs".🤖 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/components/ui/BouncerNetworksPanel.tsx` around lines 27 - 34, STATE_COPY currently embeds English labels at module scope which prevents extraction and breaks Lingui's runtime activation; refactor by removing user-visible strings from STATE_COPY (keep keys like connected/connecting/disconnected and dotClass/pulse only) and move translation into the component render path (inside the BouncerNetworksPanel function) using Lingui's t for prop strings or <Trans> for JSX children so translations evaluate after i18n.activate(); update any usages of STATE_COPY.label to call the translator (e.g., t`...` or <Trans> ...) at render time.src/locales/it/messages.po-63-63 (1)
63-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFill missing Italian translations for new bouncer UI strings.
These
msgstrvalues are empty, so Italian users will see fallback source text in key navigation/actions for the new bouncer flow.Also applies to: 389-389, 1772-1772
🤖 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/locales/it/messages.po` at line 63, Fill the empty Italian translations in the messages.po entries for the new bouncer UI by replacing the empty msgstr "" values with accurate Italian translations that match the corresponding msgid source strings; update each affected msgstr (the entries around the currently empty msgstr at the reported locations) ensuring proper escaping and plural forms if present, keep the meaning and UI tone consistent with other Italian locale strings, and then run the PO linter/validation (or msgfmt) to verify there are no syntax or encoding errors.src/locales/ja/messages.po-63-63 (1)
63-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd missing Japanese translations for newly introduced bouncer labels/actions.
These entries are currently untranslated (
msgstr ""), which causes fallback-language strings in Japanese UI flows.Also applies to: 389-389, 1772-1772
🤖 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/locales/ja/messages.po` at line 63, Replace the empty msgstr entries for the new bouncer label/action msgid entries in the ja messages.po with proper Japanese translations (fill msgstr for each corresponding msgid), handle any plural forms by populating msgid_plural and msgstr[N] as appropriate, ensure the file remains UTF-8 encoded and escapes/quoting match existing PO format, and run your PO linter/msgfmt to validate the updated src/locales/ja/messages.po before committing.src/locales/ko/messages.po-1770-1772 (1)
1770-1772:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winComplete the missing translation for action button.
The msgstr is empty, so Korean users will see English text instead. This breaks localization consistency for action buttons in the bouncer networks panel.
Suggested translation:
msgid "Open" -msgstr "" +msgstr "열기"Note: "열기" is the standard Korean translation for "Open" as an action verb, consistent with similar UI action verbs throughout the file.
🤖 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/locales/ko/messages.po` around lines 1770 - 1772, The translation entry for msgid "Open" in the Korean PO file is empty; update the msgstr for that msgid to the Korean action verb "열기" so the BouncerNetworksPanel action button is localized consistently (locate the msgid "Open" entry in the messages.po file and set msgstr "열기").src/locales/ko/messages.po-59-63 (1)
59-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winComplete the missing translation for network selection prompt.
The msgstr is empty, so Korean users will see English text instead. This breaks localization completeness for the bouncer networks panel.
Suggested translation:
msgid "{0} network{1} — pick one to join" -msgstr "" +msgstr "{0}개의 네트워크 — 참여할 네트워크를 선택하세요"Note: Korean doesn't require plural forms like English "s", so {1} can be omitted in the translation while keeping {0} for the count.
🤖 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/locales/ko/messages.po` around lines 59 - 63, Add a Korean translation for the msgid "{0} network{1} — pick one to join" in src/locales/ko/messages.po (used by BouncerNetworksPanel.tsx): set msgstr to a Korean string that preserves the {0} placeholder (count) and omits {1} since Korean doesn't use English plural "s" (for example, "네트워크 {0}개 — 참여할 네트워크를 선택하세요" or similar), ensuring the placeholder syntax remains unchanged.src/locales/ko/messages.po-387-389 (1)
387-389:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winComplete the missing translation for navigation action.
The msgstr is empty, so Korean users will see English text instead. This is inconsistent with other translated navigation strings in the file (e.g., line 127: "← 뒤로").
Suggested translation:
msgid "Back to network list" -msgstr "" +msgstr "네트워크 목록으로 돌아가기"🤖 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/locales/ko/messages.po` around lines 387 - 389, Add the missing Korean translation for the msgid "Back to network list" in src/locales/ko/messages.po by filling in the msgstr for that entry (the entry referencing src/components/ui/BouncerNetworksPanel.tsx with msgid "Back to network list"); use the suggested translation (e.g., "← 뒤로 네트워크 목록으로") or another accurate Korean phrase and ensure the msgstr is not empty so the UI shows the translated label.src/locales/cs/messages.po-63-64 (1)
63-64:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winComplete missing Czech translations for new bouncer labels.
These new entries have empty
msgstr, so users in Czech locale will see fallback English in the bouncer flow. Please fill these translations and regenerate locale artifacts.Also applies to: 389-390, 1772-1773
🤖 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/locales/cs/messages.po` around lines 63 - 64, Fill the empty Czech translations in the Czech PO file for the new bouncer labels by replacing each blank msgstr for the bouncer-related msgid entries with correct Czech text (the affected entries are the new bouncer label msgids currently showing empty msgstr). After updating the msgstr values for those entries, run the project's locale regeneration/compile step to rebuild the locale artifacts so the changes take effect. Ensure you update all occurrences (the three blank msgstr groups referenced) and keep msgid text unchanged.src/locales/de/messages.po-63-64 (1)
63-64:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFill missing German translations before merge.
These entries are still untranslated (
msgstr ""), which leaves English text in the German bouncer UI. Please provide Germanmsgstrvalues and regenerate message bundles.Also applies to: 389-390, 1772-1773
🤖 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/locales/de/messages.po` around lines 63 - 64, Replace the empty msgstr entries in the German .po file (messages.po) with proper German translations matching each msgid (including plural forms where applicable) for the untranslated entries flagged in the review (three occurrences), then run the project's i18n message bundle generation step to regenerate the compiled locale bundles so the UI uses the new German strings.src/locales/es/messages.po-59-64 (1)
59-64:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFill missing Spanish translations for newly added bouncer UI strings.
msgstris empty for three user-facing entries, so Spanish users will see English fallback text in the new flow.💬 Suggested translations
msgid "{0} network{1} — pick one to join" -msgstr "" +msgstr "{0} red{1} — elige una para unirte" msgid "Back to network list" -msgstr "" +msgstr "Volver a la lista de redes" msgid "Open" -msgstr "" +msgstr "Abrir"Also applies to: 387-390, 1770-1773
🤖 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/locales/es/messages.po` around lines 59 - 64, Populate the empty msgstr for the new bouncer UI strings in src/locales/es/messages.po by providing a Spanish translation that preserves the placeholders {0} and {1} used for pluralization; for the msgid "{0} network{1} — pick one to join" (referenced from src/components/ui/BouncerNetworksPanel.tsx) set msgstr to a Spanish equivalent such as "{0} red{1} — elige una para unirte", and apply the same approach to the other identical entries mentioned (around the other ranges) so all occurrences use the translated string with placeholders intact.src/locales/fi/messages.po-62-63 (1)
62-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winComplete the missing Finnish translations.
Three user-facing strings in the bouncer networks UI lack Finnish translations and will display in English:
- Line 62-63:
"{0} network{1} — pick one to join"- Line 388-389:
"Back to network list"- Line 771-772:
"Open"Finnish users will see mixed-language UI text. Complete these translations to maintain a consistent localized experience.
Also applies to: 388-389, 771-772
🤖 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/locales/fi/messages.po` around lines 62 - 63, The PO file contains three untranslated msgid entries — "{0} network{1} — pick one to join", "Back to network list", and "Open" — causing mixed English UI for Finnish users; add appropriate Finnish msgstr values for each msgid in the same file (provide correct Finnish translations preserving placeholders like {0} and {1} exactly), verify encoding and plural/formatting is preserved, and run a quick lint/compile of localization assets to ensure the new translations are picked up by functions that render the bouncer networks UI.src/locales/fi/messages.mjs-1-1 (1)
1-1:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMissing Finnish translation for new bouncer network string.
The message ID
STmlpb("Back to network list") remains in English instead of being translated to Finnish. This will cause the back button'saria-labelin the bouncer networks panel to display in English for Finnish-speaking users.As per the PR objectives, this layer adds bouncer network management UI strings to locale catalogs. The translation should be added to the source
.pofile and regenerated.🤖 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/locales/fi/messages.mjs` at line 1, The locale file is missing a Finnish translation for message ID "STmlpb" (currently "Back to network list"); add the Finnish string (e.g. "Takaisin verkkojen luetteloon" or your approved translation) to the Finnish .po source entry for STmlpb and then regenerate the compiled messages (which produce src/locales/fi/messages.mjs) so that messages.STmlpb contains the Finnish text instead of English.src/locales/ja/messages.mjs-1-1 (1)
1-1:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMissing Japanese translation for new bouncer network string.
The message ID
STmlpb("Back to network list") remains in English instead of being translated to Japanese. This will cause the back button'saria-labelin the bouncer networks panel to display in English for Japanese-speaking users.As per the PR objectives, this layer adds bouncer network management UI strings to locale catalogs. The translation should be added to the source
.pofile and regenerated.🤖 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/locales/ja/messages.mjs` at line 1, The locale entry for message ID STmlpb is still English ("Back to network list"); add its Japanese translation (e.g. "ネットワークリストに戻る") to the source .po for Japanese and then regenerate the compiled locale bundle so messages.mjs includes the new translated value for STmlpb; ensure the updated messages.mjs contains STmlpb mapped to the Japanese string used by the bouncer networks panel.src/locales/fr/messages.mjs-1-1 (1)
1-1:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMissing French translation for new bouncer network string.
The message ID
STmlpb("Back to network list") remains in English instead of being translated to French. This will cause the back button'saria-labelin the bouncer networks panel to display in English for French-speaking users.As per the PR objectives, this layer adds bouncer network management UI strings to locale catalogs. The translation should be added to the source
.pofile and regenerated.🤖 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/locales/fr/messages.mjs` at line 1, The messages catalog contains an untranslated entry "STmlpb" ("Back to network list"); update the French translation for message ID STmlpb in the source .po locale (and then regenerate the messages.mjs so messages includes the French string) — e.g. set STmlpb to "Retour à la liste des réseaux" in the FR .po and run the locale build that produces the messages const in messages.mjs.src/locales/es/messages.mjs-1-1 (1)
1-1:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMissing Spanish translation for new bouncer network string.
The message ID
STmlpb("Back to network list") remains in English instead of being translated to Spanish. This will cause the back button'saria-labelin the bouncer networks panel to display in English for Spanish-speaking users.As per the PR objectives, this layer adds bouncer network management UI strings to locale catalogs. The translation should be added to the source
.pofile and regenerated.🤖 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/locales/es/messages.mjs` at line 1, The locale file is missing a Spanish translation for the new bouncer network string id "STmlpb" (currently "Back to network list"); add the Spanish translation (e.g., "Volver a la lista de redes") for message id "STmlpb" in the Spanish source .po file, then regenerate the locale bundle so src/locales/es/messages.mjs (messages JSON) includes the updated entry for "STmlpb".src/locales/ko/messages.mjs-1-1 (1)
1-1:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMissing Korean translation for new bouncer network string.
The message ID
STmlpb("Back to network list") remains in English instead of being translated to Korean. This will cause the back button'saria-labelin the bouncer networks panel to display in English for Korean-speaking users.As per the PR objectives, this layer adds bouncer network management UI strings to locale catalogs. The translation should be added to the source
.pofile and regenerated.🤖 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/locales/ko/messages.mjs` at line 1, The locale bundle still contains the English string for message ID STmlpb ("Back to network list"); add the Korean translation for STmlpb in the Korean .po source (the entry for msgid "Back to network list") and then re-run the localization build/regeneration step so src/locales/ko/messages.mjs is regenerated with the new Korean string used by the bouncer networks panel aria-label.
🤖 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 `@src/components/ui/BouncerNetworksPanel.tsx`:
- Around line 90-103: The optimistic 500ms timeout in the useEffect that
references bouncer, pendingFor, bouncer.lastError, setConfirmedSuccessFor,
setMode, and setPendingFor can hide real failures; replace the wall-clock timer
with a protocol/store-correlated completion signal: stop using setTimeout and
instead wait for an explicit ACK or observable state change tied to the
operation (e.g., a bouncer.ackFor/pendingAck field, a transition on
bouncer.state, or a network-list mutation that includes the new network) and
only call
setConfirmedSuccessFor(pendingFor)/setMode({kind:"list"})/setPendingFor(null)
when that success signal is observed (or immediately handle bouncer.lastError
when a FAIL arrives); ensure you still clear any temporary
listeners/subscriptions in the effect cleanup.
In `@src/lib/irc/handlers/connection.ts`:
- Around line 175-177: The CAP NAK branch currently ends CAP negotiation but
doesn't finish registration; after the existing calls to
ctx.sendCapEnd(serverId) and ctx.capNegotiationComplete.set(serverId, true) in
the subcommand === "NAK" branch, invoke ctx.userOnConnect(serverId) so the
client proceeds with the USER/registration flow (mirror the behavior in the
"ACK", timeout, and SASL completion paths by calling ctx.userOnConnect with the
same serverId).
In `@src/lib/irc/IRCClient.ts`:
- Around line 649-653: The pending-connection key is derived as serverId ??
`${host}:${port}` in connect(), but disconnect()/removeServer() still delete
`${server.host}:${server.port}` causing stale promises; centralize key
derivation into a single helper (e.g., deriveConnectionKey(server, host, port,
serverId)) and replace all places that set/delete pendingConnections (connect(),
disconnect(), removeServer(), and socket.onclose handler) to call that helper so
the same connectionKey is used for both set and delete; ensure the helper is
used where connectionKey is currently declared and where
`${server.host}:${server.port}` is constructed.
---
Minor comments:
In `@src/components/ui/BouncerNetworkForm.tsx`:
- Around line 166-171: The placeholder string for the TextInput in
BouncerNetworkForm.tsx is hard-coded and not localizable; import the Lingui
helper (t) from '`@lingui/macro`' at the top of the file and replace
placeholder="irc.libera.chat" with a Lingui-wrapped prop (use t for JSX props)
so the host placeholder becomes localizable for translations for the TextInput
component in BouncerNetworkForm.
In `@src/components/ui/BouncerNetworksPanel.tsx`:
- Around line 27-34: STATE_COPY currently embeds English labels at module scope
which prevents extraction and breaks Lingui's runtime activation; refactor by
removing user-visible strings from STATE_COPY (keep keys like
connected/connecting/disconnected and dotClass/pulse only) and move translation
into the component render path (inside the BouncerNetworksPanel function) using
Lingui's t for prop strings or <Trans> for JSX children so translations evaluate
after i18n.activate(); update any usages of STATE_COPY.label to call the
translator (e.g., t`...` or <Trans> ...) at render time.
In `@src/components/ui/LinkSecurityWarningModal.tsx`:
- Around line 124-126: The CAP flow resumes by calling
ircClient.sendCapEnd(serverId) and ircClient.userOnConnect(serverId) but never
flips the negotiation flag; update this path to mark the CAP negotiation
complete by setting ircClient.capNegotiationComplete[serverId] = true (or call
the existing helper that finalizes CAP negotiation) before or immediately after
sendCapEnd so that isCapNegotiationComplete(serverId) returns true and
subsequent logic treats the session as post-negotiation.
In `@src/locales/cs/messages.po`:
- Around line 63-64: Fill the empty Czech translations in the Czech PO file for
the new bouncer labels by replacing each blank msgstr for the bouncer-related
msgid entries with correct Czech text (the affected entries are the new bouncer
label msgids currently showing empty msgstr). After updating the msgstr values
for those entries, run the project's locale regeneration/compile step to rebuild
the locale artifacts so the changes take effect. Ensure you update all
occurrences (the three blank msgstr groups referenced) and keep msgid text
unchanged.
In `@src/locales/de/messages.po`:
- Around line 63-64: Replace the empty msgstr entries in the German .po file
(messages.po) with proper German translations matching each msgid (including
plural forms where applicable) for the untranslated entries flagged in the
review (three occurrences), then run the project's i18n message bundle
generation step to regenerate the compiled locale bundles so the UI uses the new
German strings.
In `@src/locales/es/messages.mjs`:
- Line 1: The locale file is missing a Spanish translation for the new bouncer
network string id "STmlpb" (currently "Back to network list"); add the Spanish
translation (e.g., "Volver a la lista de redes") for message id "STmlpb" in the
Spanish source .po file, then regenerate the locale bundle so
src/locales/es/messages.mjs (messages JSON) includes the updated entry for
"STmlpb".
In `@src/locales/es/messages.po`:
- Around line 59-64: Populate the empty msgstr for the new bouncer UI strings in
src/locales/es/messages.po by providing a Spanish translation that preserves the
placeholders {0} and {1} used for pluralization; for the msgid "{0} network{1} —
pick one to join" (referenced from src/components/ui/BouncerNetworksPanel.tsx)
set msgstr to a Spanish equivalent such as "{0} red{1} — elige una para unirte",
and apply the same approach to the other identical entries mentioned (around the
other ranges) so all occurrences use the translated string with placeholders
intact.
In `@src/locales/fi/messages.mjs`:
- Line 1: The locale file is missing a Finnish translation for message ID
"STmlpb" (currently "Back to network list"); add the Finnish string (e.g.
"Takaisin verkkojen luetteloon" or your approved translation) to the Finnish .po
source entry for STmlpb and then regenerate the compiled messages (which produce
src/locales/fi/messages.mjs) so that messages.STmlpb contains the Finnish text
instead of English.
In `@src/locales/fi/messages.po`:
- Around line 62-63: The PO file contains three untranslated msgid entries —
"{0} network{1} — pick one to join", "Back to network list", and "Open" —
causing mixed English UI for Finnish users; add appropriate Finnish msgstr
values for each msgid in the same file (provide correct Finnish translations
preserving placeholders like {0} and {1} exactly), verify encoding and
plural/formatting is preserved, and run a quick lint/compile of localization
assets to ensure the new translations are picked up by functions that render the
bouncer networks UI.
In `@src/locales/fr/messages.mjs`:
- Line 1: The messages catalog contains an untranslated entry "STmlpb" ("Back to
network list"); update the French translation for message ID STmlpb in the
source .po locale (and then regenerate the messages.mjs so messages includes the
French string) — e.g. set STmlpb to "Retour à la liste des réseaux" in the FR
.po and run the locale build that produces the messages const in messages.mjs.
In `@src/locales/fr/messages.po`:
- Around line 62-64: Update the French .po entries where msgstr is empty for the
new UI strings (e.g. the msgid "{0} network{1} — pick one to join" and the other
occurrences noted around lines 387-390 and 1770-1773) by supplying proper French
translations and preserving interpolation placeholders ({0}, {1}) and
punctuation/emdash exactly as in the msgid; ensure each corresponding msgstr is
filled in the same file (messages.po) so the fr locale no longer falls back to
English.
In `@src/locales/it/messages.po`:
- Line 63: Fill the empty Italian translations in the messages.po entries for
the new bouncer UI by replacing the empty msgstr "" values with accurate Italian
translations that match the corresponding msgid source strings; update each
affected msgstr (the entries around the currently empty msgstr at the reported
locations) ensuring proper escaping and plural forms if present, keep the
meaning and UI tone consistent with other Italian locale strings, and then run
the PO linter/validation (or msgfmt) to verify there are no syntax or encoding
errors.
In `@src/locales/ja/messages.mjs`:
- Line 1: The locale entry for message ID STmlpb is still English ("Back to
network list"); add its Japanese translation (e.g. "ネットワークリストに戻る") to the source
.po for Japanese and then regenerate the compiled locale bundle so messages.mjs
includes the new translated value for STmlpb; ensure the updated messages.mjs
contains STmlpb mapped to the Japanese string used by the bouncer networks
panel.
In `@src/locales/ja/messages.po`:
- Line 63: Replace the empty msgstr entries for the new bouncer label/action
msgid entries in the ja messages.po with proper Japanese translations (fill
msgstr for each corresponding msgid), handle any plural forms by populating
msgid_plural and msgstr[N] as appropriate, ensure the file remains UTF-8 encoded
and escapes/quoting match existing PO format, and run your PO linter/msgfmt to
validate the updated src/locales/ja/messages.po before committing.
In `@src/locales/ko/messages.mjs`:
- Line 1: The locale bundle still contains the English string for message ID
STmlpb ("Back to network list"); add the Korean translation for STmlpb in the
Korean .po source (the entry for msgid "Back to network list") and then re-run
the localization build/regeneration step so src/locales/ko/messages.mjs is
regenerated with the new Korean string used by the bouncer networks panel
aria-label.
In `@src/locales/ko/messages.po`:
- Around line 1770-1772: The translation entry for msgid "Open" in the Korean PO
file is empty; update the msgstr for that msgid to the Korean action verb "열기"
so the BouncerNetworksPanel action button is localized consistently (locate the
msgid "Open" entry in the messages.po file and set msgstr "열기").
- Around line 59-63: Add a Korean translation for the msgid "{0} network{1} —
pick one to join" in src/locales/ko/messages.po (used by
BouncerNetworksPanel.tsx): set msgstr to a Korean string that preserves the {0}
placeholder (count) and omits {1} since Korean doesn't use English plural "s"
(for example, "네트워크 {0}개 — 참여할 네트워크를 선택하세요" or similar), ensuring the
placeholder syntax remains unchanged.
- Around line 387-389: Add the missing Korean translation for the msgid "Back to
network list" in src/locales/ko/messages.po by filling in the msgstr for that
entry (the entry referencing src/components/ui/BouncerNetworksPanel.tsx with
msgid "Back to network list"); use the suggested translation (e.g., "← 뒤로 네트워크
목록으로") or another accurate Korean phrase and ensure the msgstr is not empty so
the UI shows the translated label.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: d94689ea-4be0-44be-a491-6d0e6d537e16
📒 Files selected for processing (55)
src/components/layout/ChatArea.tsxsrc/components/ui/BouncerNetworkForm.tsxsrc/components/ui/BouncerNetworksPanel.tsxsrc/components/ui/LinkSecurityWarningModal.tsxsrc/lib/irc/IRCClient.tssrc/lib/irc/IRCClientContext.tssrc/lib/irc/handlers/connection.tssrc/locales/cs/messages.mjssrc/locales/cs/messages.posrc/locales/de/messages.mjssrc/locales/de/messages.posrc/locales/en/messages.mjssrc/locales/en/messages.posrc/locales/es/messages.mjssrc/locales/es/messages.posrc/locales/fi/messages.mjssrc/locales/fi/messages.posrc/locales/fr/messages.mjssrc/locales/fr/messages.posrc/locales/it/messages.mjssrc/locales/it/messages.posrc/locales/ja/messages.mjssrc/locales/ja/messages.posrc/locales/ko/messages.mjssrc/locales/ko/messages.posrc/locales/nl/messages.mjssrc/locales/nl/messages.posrc/locales/pl/messages.mjssrc/locales/pl/messages.posrc/locales/pt/messages.mjssrc/locales/pt/messages.posrc/locales/ro/messages.mjssrc/locales/ro/messages.posrc/locales/ru/messages.mjssrc/locales/ru/messages.posrc/locales/sv/messages.mjssrc/locales/sv/messages.posrc/locales/tr/messages.mjssrc/locales/tr/messages.posrc/locales/uk/messages.mjssrc/locales/uk/messages.posrc/locales/zh-TW/messages.mjssrc/locales/zh-TW/messages.posrc/locales/zh/messages.mjssrc/locales/zh/messages.posrc/store/handlers/auth.tssrc/store/handlers/bouncer.tssrc/store/index.tssrc/types/index.tstests/components/BouncerNetworkForm.test.tsxtests/components/LinkSecurityWarningModal.test.tsxtests/protocol/bouncerBind.test.tstests/store/bouncer.test.tstests/store/bouncerConnect.test.tstests/store/connectToSavedServers.test.ts
✅ Files skipped from review due to trivial changes (2)
- src/locales/it/messages.mjs
- src/locales/cs/messages.mjs
| } else if (subcommand === "NAK") { | ||
| ctx.sendRaw(serverId, "CAP END"); | ||
| ctx.sendCapEnd(serverId); | ||
| ctx.capNegotiationComplete.set(serverId, true); |
There was a problem hiding this comment.
Finish registration on CAP NAK.
This branch ends CAP negotiation but never calls ctx.userOnConnect(serverId). If a requested CAP batch is rejected, the client stays after NICK/PASS and never sends USER, unlike the ACK, timeout, and SASL completion paths.
Suggested fix
} else if (subcommand === "NAK") {
ctx.sendCapEnd(serverId);
ctx.capNegotiationComplete.set(serverId, true);
+ ctx.userOnConnect(serverId);
} else if (subcommand === "NEW") ctx.onCapNew(serverId, caps);📝 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.
| } else if (subcommand === "NAK") { | |
| ctx.sendRaw(serverId, "CAP END"); | |
| ctx.sendCapEnd(serverId); | |
| ctx.capNegotiationComplete.set(serverId, true); | |
| } else if (subcommand === "NAK") { | |
| ctx.sendCapEnd(serverId); | |
| ctx.capNegotiationComplete.set(serverId, true); | |
| ctx.userOnConnect(serverId); | |
| } else if (subcommand === "NEW") ctx.onCapNew(serverId, caps); |
🤖 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/irc/handlers/connection.ts` around lines 175 - 177, The CAP NAK
branch currently ends CAP negotiation but doesn't finish registration; after the
existing calls to ctx.sendCapEnd(serverId) and
ctx.capNegotiationComplete.set(serverId, true) in the subcommand === "NAK"
branch, invoke ctx.userOnConnect(serverId) so the client proceeds with the
USER/registration flow (mirror the behavior in the "ACK", timeout, and SASL
completion paths by calling ctx.userOnConnect with the same serverId).
| // Bouncer child connections share host:port with the control | ||
| // connection (and with each other). Scope the pending-connection | ||
| // dedup by serverId when one is provided so each child gets its | ||
| // own promise rather than landing on a sibling's. | ||
| const connectionKey = serverId ?? `${host}:${port}`; |
There was a problem hiding this comment.
Use the same pending-connection key for cleanup.
connect() now stores child-server promises under serverId, but disconnect() still deletes ${server.host}:${server.port}. In the removeServer() path, socket.onclose exits early once the server entry is gone, so that promise can stay stuck in pendingConnections and block later reconnects for the same child server. Please centralize key derivation and reuse it for every set/delete site.
🤖 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/irc/IRCClient.ts` around lines 649 - 653, The pending-connection key
is derived as serverId ?? `${host}:${port}` in connect(), but
disconnect()/removeServer() still delete `${server.host}:${server.port}` causing
stale promises; centralize key derivation into a single helper (e.g.,
deriveConnectionKey(server, host, port, serverId)) and replace all places that
set/delete pendingConnections (connect(), disconnect(), removeServer(), and
socket.onclose handler) to call that helper so the same connectionKey is used
for both set and delete; ensure the helper is used where connectionKey is
currently declared and where `${server.host}:${server.port}` is constructed.
Resolutions:
- IRCClient.ts CAP REQ list: both branches added a vendor cap entry in
the same neighbourhood (HEAD added "soju.im/bouncer-networks" + its
notify variant; main added "obby.world/invitation"). Kept all three.
- store/index.ts type import block: HEAD added BouncerState, main added
InviteLink. Kept both.
- src/locales/*.po: took ours then re-extracted to merge in main's
invitations / raw-log / drop-files strings. Backfilled 26 already-
translated strings × 18 locales by copying msgstrs verbatim from
obbyworld/main; the remaining 3 (bouncer-UI: "Open", "Back to network
list", "{0} network{1} — pick one to join") were translated inline.
879 tests + build green; nothing on this branch needed code changes
for the merge itself.
|
Automated deployment preview for the PR in the Cloudflare Pages. |
There was a problem hiding this comment.
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 (2)
src/locales/de/messages.po (1)
59-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winEnglish suffix placeholder
{1}is not valid German pluralization.Line 63 keeps
{1}fromnetwork{1}(""/"s"), which yields incorrect German forms. Please localize this without English suffix logic (or switch the source message to ICU plural and regenerate catalogs).🤖 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/locales/de/messages.po` around lines 59 - 63, The translation is using the English suffix placeholder `{1}` from msgid "{0} network{1} — pick one to join" which is invalid for German; update the German entry (the msgstr for that msgid used by BouncerNetworksPanel.tsx) to remove the `{1}`-based English plural logic and provide a correct German phrasing (or convert the source string to an ICU plural form and regenerate catalogs so translations can use proper pluralization). Ensure the msgstr no longer references `{1}` and correctly handles singular/plural in German.src/locales/cs/messages.po (1)
59-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid English
"{1}"suffix pluralization in Czech translation.Line 63 reuses
{1}from Englishnetwork{1}logic (""/"s"), which can produce malformed Czech output. Please use Czech-safe wording (or ICU pluralization) that does not depend on the English suffix token.🤖 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/locales/cs/messages.po` around lines 59 - 63, The Czech translation reuses the English suffix token "{1}" (from msgid "{0} network{1} — pick one to join") which produces incorrect Czech output; update the translation in messages.po for the msgid referenced by BouncerNetworksPanel.tsx to avoid relying on "{1}" — either provide full Czech plural forms using gettext pluralization (convert to msgid_plural and supply msgstr[0], msgstr[1], msgstr[2] with correct Czech forms for singular/plural) or replace the single msgstr with a Czech-safe phrase that does not use "{1}" (e.g. use a form that embeds the count like "{0} síť — vyberte jednu pro připojení" for singular and the appropriate plural variants), ensuring placeholders include only the numeric {0} and that the component BouncerNetworksPanel.tsx will use the pluralized entry or ICU pattern.
🧹 Nitpick comments (1)
src/locales/ja/messages.po (1)
59-63: ⚡ Quick winDon’t carry English plural suffix placeholder into Japanese output.
Line 63 currently renders
{0} ネットワーク{1}..., where{1}is the English"s"suffix. In Japanese this produces incorrect mixed-language output for plurals. Prefer a translation that ignores{1}and uses Japanese-native phrasing.🤖 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/locales/ja/messages.po` around lines 59 - 63, The Japanese translation must not include the English plural placeholder {1}; update the msgstr for the msgid "{0} network{1} — pick one to join" (from src/components/ui/BouncerNetworksPanel.tsx) to remove {1} and use a natural Japanese phrasing that incorporates the count (e.g. use "{0}件のネットワークから参加するものを選択" or "{0} ネットワークから参加するものを選択") so the string only uses the {0} placeholder and no English plural suffix.
🤖 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 `@src/locales/cs/messages.po`:
- Around line 85-99: Restore UTF-8 Czech translations in the PO catalog by
replacing the mojibake msgstr values with properly encoded Czech text for the
invitation age strings (e.g., change the msgstr for msgid "{0}d ago" from "p�ed
{0} dny" to "před {0} dny", for "{0}h ago" to "před {0} h", and for "{0}m ago"
to "před {0} min"); ensure the file is saved as UTF-8 and scan/apply the same
fix to the other corrupted ranges mentioned (132-135, 597-600, 745-784, 858-873,
933-936, 1334-1375, 1620-1623, 1740-1751, 2544-2555, 2710-2713, 2908-2935) so
all msgstr entries are valid UTF-8 Czech strings and not mojibake.
In `@src/locales/de/messages.po`:
- Around line 597-600: The German .po entries contain mojibake (e.g., msgid
"Click again to confirm" with msgstr "Erneut klicken zum Bestätigen") — open
src/locales/de/messages.po and replace corrupted encodings with proper UTF-8
German strings (e.g., "Erneut klicken zum Bestätigen"); search for other
corrupted msgstr values referenced in the review (around the msgids used by
InvitationsPanel.tsx and any msgids for delete/for/selected/supported) and fix
them similarly so all msgstr values use proper umlauts/ß (e.g., löschen, für,
ausgewählt, unterstützt) ensuring the file is saved as UTF-8.
In `@src/locales/es/messages.po`:
- Around line 699-702: The Spanish .po strings contain mojibake (e.g., msgid
"Confirm?" with msgstr "¿Confirmar?")—open the translations for the referenced
keys (e.g., the entry for msgid "Confirm?" used by
src/components/ui/InvitationsPanel.tsx and the other listed msgid ranges),
replace corrupted sequences (¿, ó, ñ, �, etc.) with correct UTF-8 Spanish
characters (¿, ó, ñ, —, etc.), re-save/export the .po file encoded as UTF-8
without BOM, and re-run the localization export to ensure the clean UTF-8
catalog is used in the app.
In `@src/locales/fi/messages.po`:
- Around line 781-784: Replace the mojibake in the Finnish .po translations for
the given msgid entries (e.g., msgid "Create and manage your invite links" in
the block associated with UserSettings.tsx) by updating the corrupted msgstr
values to the correct Finnish phrases (e.g., "Luo ja hallinnoi kutsulinkkejäsi")
and similarly fix the other listed ranges (lines referencing the other msgid
strings). Ensure the tool or script that writes the .po file writes UTF-8 (no
BOM) end-to-end so characters like ä, ö, and å are preserved; verify by opening
the saved .po in a UTF-8-aware editor and running a quick grep for typical
mojibake sequences (ä, ö) to confirm removal.
In `@src/locales/fr/messages.po`:
- Around line 597-599: The French PO strings have mojibake (e.g., msgstr for
msgid "Click again to confirm" and other hunks like occurrences of "Créer",
"réseau", etc. in src/locales/fr/messages.po); restore each affected msgstr to
proper Unicode (e.g., "Cliquez à nouveau pour confirmer" for the shown entry) by
fixing the corrupted characters across the listed hunks (773-783, 870-872,
933-935, 1334-1374, 1620-1622, 1740-1750, 2552-2554, 2710-2712, 2908-2910) and
then correct the import/export process that produced the encoding damage (ensure
UTF-8 read/write, no Latin1/Windows-1252 steps) so future PO updates preserve
accented characters.
In `@src/locales/it/messages.po`:
- Around line 1372-1374: The Italian translation for the InvitationsPanel msgid
contains mojibake (e.g. "è", "�"); open the msgstr for that msgid in
src/locales/it/messages.po (and the other occurrences noted at lines ~2552-2554)
and replace the corrupted byte sequences with the correct Unicode characters
(e.g. "è" for è and "—" for â��) so the string reads: "Lascia il canale vuoto
per un invito generico alla rete. La descrizione è solo per i tuoi appunti —
visibile solo a te in questo elenco."; after fixing, run the same catalog
generation/import step that produced these strings to ensure it preserves UTF‑8
encoding and adjust that pipeline if it reintroduces mojibake.
In `@src/locales/ja/messages.po`:
- Around line 85-99: The Japanese translations in src/locales/ja/messages.po for
msgid "{0}d ago", "{0}h ago", "{0}m ago" (and other listed entries) are
mojibake; restore them by replacing the corrupted msgstr values with the
last-known-good Japanese strings (or retranslate them) and ensure the file is
saved and committed as UTF-8 (no double-encoding, no BOM). Locate the affected
msgid entries (e.g., in InvitationsPanel.tsx references) and update their msgstr
fields to valid UTF-8 Japanese, verify rendering in the UI flows
(invitations/raw-log), and commit the fixed .po file encoded as UTF-8.
---
Outside diff comments:
In `@src/locales/cs/messages.po`:
- Around line 59-63: The Czech translation reuses the English suffix token "{1}"
(from msgid "{0} network{1} — pick one to join") which produces incorrect Czech
output; update the translation in messages.po for the msgid referenced by
BouncerNetworksPanel.tsx to avoid relying on "{1}" — either provide full Czech
plural forms using gettext pluralization (convert to msgid_plural and supply
msgstr[0], msgstr[1], msgstr[2] with correct Czech forms for singular/plural) or
replace the single msgstr with a Czech-safe phrase that does not use "{1}" (e.g.
use a form that embeds the count like "{0} síť — vyberte jednu pro připojení"
for singular and the appropriate plural variants), ensuring placeholders include
only the numeric {0} and that the component BouncerNetworksPanel.tsx will use
the pluralized entry or ICU pattern.
In `@src/locales/de/messages.po`:
- Around line 59-63: The translation is using the English suffix placeholder
`{1}` from msgid "{0} network{1} — pick one to join" which is invalid for
German; update the German entry (the msgstr for that msgid used by
BouncerNetworksPanel.tsx) to remove the `{1}`-based English plural logic and
provide a correct German phrasing (or convert the source string to an ICU plural
form and regenerate catalogs so translations can use proper pluralization).
Ensure the msgstr no longer references `{1}` and correctly handles
singular/plural in German.
---
Nitpick comments:
In `@src/locales/ja/messages.po`:
- Around line 59-63: The Japanese translation must not include the English
plural placeholder {1}; update the msgstr for the msgid "{0} network{1} — pick
one to join" (from src/components/ui/BouncerNetworksPanel.tsx) to remove {1} and
use a natural Japanese phrasing that incorporates the count (e.g. use
"{0}件のネットワークから参加するものを選択" or "{0} ネットワークから参加するものを選択") so the string only uses the
{0} placeholder and no English plural suffix.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 194a6cf5-2b1f-4e72-b4ed-9089cf76fa24
📒 Files selected for processing (45)
src/components/layout/ChatArea.tsxsrc/lib/irc/IRCClient.tssrc/lib/irc/handlers/connection.tssrc/lib/irc/handlers/index.tssrc/locales/cs/messages.mjssrc/locales/cs/messages.posrc/locales/de/messages.mjssrc/locales/de/messages.posrc/locales/en/messages.mjssrc/locales/en/messages.posrc/locales/es/messages.mjssrc/locales/es/messages.posrc/locales/fi/messages.mjssrc/locales/fi/messages.posrc/locales/fr/messages.mjssrc/locales/fr/messages.posrc/locales/it/messages.mjssrc/locales/it/messages.posrc/locales/ja/messages.mjssrc/locales/ja/messages.posrc/locales/ko/messages.mjssrc/locales/ko/messages.posrc/locales/nl/messages.mjssrc/locales/nl/messages.posrc/locales/pl/messages.mjssrc/locales/pl/messages.posrc/locales/pt/messages.mjssrc/locales/pt/messages.posrc/locales/ro/messages.mjssrc/locales/ro/messages.posrc/locales/ru/messages.mjssrc/locales/ru/messages.posrc/locales/sv/messages.mjssrc/locales/sv/messages.posrc/locales/tr/messages.mjssrc/locales/tr/messages.posrc/locales/uk/messages.mjssrc/locales/uk/messages.posrc/locales/zh-TW/messages.mjssrc/locales/zh-TW/messages.posrc/locales/zh/messages.mjssrc/locales/zh/messages.posrc/store/handlers/index.tssrc/store/index.tssrc/types/index.ts
✅ Files skipped from review due to trivial changes (3)
- src/locales/es/messages.mjs
- src/locales/fr/messages.mjs
- src/locales/en/messages.mjs
🚧 Files skipped from review as they are similar to previous changes (10)
- src/locales/de/messages.mjs
- src/locales/it/messages.mjs
- src/lib/irc/handlers/connection.ts
- src/locales/cs/messages.mjs
- src/components/layout/ChatArea.tsx
- src/lib/irc/handlers/index.ts
- src/locales/ja/messages.mjs
- src/locales/fi/messages.mjs
- src/lib/irc/IRCClient.ts
- src/locales/en/messages.po
| #. placeholder {0}: Math.floor(secs / 86400) | ||
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "{0}d ago" | ||
| msgstr "před {0} dny" | ||
| msgstr "pÅed {0} dny" | ||
|
|
||
| #. placeholder {0}: Math.floor(secs / 3600) | ||
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "{0}h ago" | ||
| msgstr "před {0} h" | ||
| msgstr "pÅed {0} h" | ||
|
|
||
| #. placeholder {0}: Math.floor(secs / 60) | ||
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "{0}m ago" | ||
| msgstr "před {0} min" | ||
| msgstr "pÅed {0} min" | ||
|
|
There was a problem hiding this comment.
Fix UTF-8 corruption in Czech invitation-related translations.
Several updated strings are mojibake and will render unreadable in production (examples: Line 88, Line 599, Line 747, Line 1742, Line 2910). This appears to be an encoding conversion issue during catalog update; please restore these entries as proper UTF-8 before merge.
Representative fixes
-msgstr "p�ed {0} dny"
+msgstr "před {0} dny"
-msgstr "KliknÄe znovu pro potvrzenÃ"
+msgstr "Klikněte znovu pro potvrzení"
-msgstr "KopÃrovat"
+msgstr "Kopírovat"
-msgstr "ZatÃm nebyl zachycen žádný surový IRC provoz. Zkuste se pÅ�ipojit nebo odeslat zprávu."
+msgstr "Zatím nebyl zachycen žádný surový IRC provoz. Zkuste se připojit nebo odeslat zprávu."Also applies to: 132-135, 597-600, 745-784, 858-873, 933-936, 1334-1375, 1620-1623, 1740-1751, 2544-2555, 2710-2713, 2908-2935
🤖 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/locales/cs/messages.po` around lines 85 - 99, Restore UTF-8 Czech
translations in the PO catalog by replacing the mojibake msgstr values with
properly encoded Czech text for the invitation age strings (e.g., change the
msgstr for msgid "{0}d ago" from "pÅ�ed {0} dny" to "před {0} dny", for "{0}h
ago" to "před {0} h", and for "{0}m ago" to "před {0} min"); ensure the file is
saved as UTF-8 and scan/apply the same fix to the other corrupted ranges
mentioned (132-135, 597-600, 745-784, 858-873, 933-936, 1334-1375, 1620-1623,
1740-1751, 2544-2555, 2710-2713, 2908-2935) so all msgstr entries are valid
UTF-8 Czech strings and not mojibake.
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "Click again to confirm" | ||
| msgstr "Erneut klicken zum Bestätigen" | ||
| msgstr "Erneut klicken zum Bestätigen" | ||
|
|
There was a problem hiding this comment.
Fix mojibake in German invitation strings.
Several changed entries are encoding-corrupted and will display broken text (examples: Line 599 Bestätigen, Line 860 löschen, Line 1374 für, Line 1750 ausgewählt, Line 2554 unterstützt). Please restore valid UTF-8 German strings before merge.
Representative fixes
-msgstr "Erneut klicken zum Bestätigen"
+msgstr "Erneut klicken zum Bestätigen"
-msgstr "Bestätigen?"
+msgstr "Bestätigen?"
-msgstr "Diese Einladung löschen"
+msgstr "Diese Einladung löschen"
-msgstr "Kein Server ausgewählt. Wähle zuerst einen Server in der Seitenleiste; Einladungslinks werden pro Server verwaltet."
+msgstr "Kein Server ausgewählt. Wähle zuerst einen Server in der Seitenleiste; Einladungslinks werden pro Server verwaltet."Also applies to: 699-702, 858-861, 1372-1375, 1748-1751, 2544-2555
🤖 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/locales/de/messages.po` around lines 597 - 600, The German .po entries
contain mojibake (e.g., msgid "Click again to confirm" with msgstr "Erneut
klicken zum Bestätigen") — open src/locales/de/messages.po and replace
corrupted encodings with proper UTF-8 German strings (e.g., "Erneut klicken zum
Bestätigen"); search for other corrupted msgstr values referenced in the review
(around the msgids used by InvitationsPanel.tsx and any msgids for
delete/for/selected/supported) and fix them similarly so all msgstr values use
proper umlauts/ß (e.g., löschen, für, ausgewählt, unterstützt) ensuring the file
is saved as UTF-8.
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "Confirm?" | ||
| msgstr "¿Confirmar?" | ||
| msgstr "¿Confirmar?" | ||
|
|
There was a problem hiding this comment.
Fix mojibake-corrupted Spanish translations before merge.
Several msgstr values are UTF-8 corrupted (e.g., ¿, ó, ñ, �), so users will see broken text in invite flows and related UI. Please restore these entries from valid Spanish strings and re-export the catalog in UTF-8 to prevent recurrence.
Also applies to: 777-784, 858-873, 1372-1375, 1740-1751, 2552-2555, 2908-2911, 2932-2935
🤖 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/locales/es/messages.po` around lines 699 - 702, The Spanish .po strings
contain mojibake (e.g., msgid "Confirm?" with msgstr "¿Confirmar?")—open the
translations for the referenced keys (e.g., the entry for msgid "Confirm?" used
by src/components/ui/InvitationsPanel.tsx and the other listed msgid ranges),
replace corrupted sequences (¿, ó, ñ, �, etc.) with correct UTF-8 Spanish
characters (¿, ó, ñ, —, etc.), re-save/export the .po file encoded as UTF-8
without BOM, and re-run the localization export to ensure the clean UTF-8
catalog is used in the app.
| #: src/components/ui/UserSettings.tsx | ||
| msgid "Create and manage your invite links" | ||
| msgstr "Luo ja hallinnoi kutsulinkkejäsi" | ||
| msgstr "Luo ja hallinnoi kutsulinkkejäsi" | ||
|
|
There was a problem hiding this comment.
Repair UTF-8 corruption in Finnish locale strings.
This file has mojibake in multiple msgstr values (ä, ö, etc.), which will render unreadable UI text in invitations and adjacent flows. Please replace with correct Finnish translations and ensure the .po write path preserves UTF-8 encoding end-to-end.
Also applies to: 858-861, 933-936, 1372-1375, 1740-1751, 2112-2116, 2544-2555, 2710-2713, 2908-2911
🤖 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/locales/fi/messages.po` around lines 781 - 784, Replace the mojibake in
the Finnish .po translations for the given msgid entries (e.g., msgid "Create
and manage your invite links" in the block associated with UserSettings.tsx) by
updating the corrupted msgstr values to the correct Finnish phrases (e.g., "Luo
ja hallinnoi kutsulinkkejäsi") and similarly fix the other listed ranges (lines
referencing the other msgid strings). Ensure the tool or script that writes the
.po file writes UTF-8 (no BOM) end-to-end so characters like ä, ö, and å are
preserved; verify by opening the saved .po in a UTF-8-aware editor and running a
quick grep for typical mojibake sequences (ä, ö) to confirm removal.
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "Click again to confirm" | ||
| msgstr "Cliquez à nouveau pour confirmer" | ||
| msgstr "Cliquez à nouveau pour confirmer" |
There was a problem hiding this comment.
Fix the mojibake before shipping the French catalog.
Several updated msgstr values in these hunks are already corrupted (Créer, réseau, capturé, capacité, etc.). Because this catalog declares UTF-8, the app will render that garbage verbatim across the invite-link and raw-log UI. Please restore the affected strings with the correct Unicode characters and fix the import/export step that introduced the encoding damage.
Also applies to: 773-783, 870-872, 933-935, 1334-1374, 1620-1622, 1740-1750, 2552-2554, 2710-2712, 2908-2910
🤖 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/locales/fr/messages.po` around lines 597 - 599, The French PO strings
have mojibake (e.g., msgstr for msgid "Click again to confirm" and other hunks
like occurrences of "Créer", "réseau", etc. in src/locales/fr/messages.po);
restore each affected msgstr to proper Unicode (e.g., "Cliquez à nouveau pour
confirmer" for the shown entry) by fixing the corrupted characters across the
listed hunks (773-783, 870-872, 933-935, 1334-1374, 1620-1622, 1740-1750,
2552-2554, 2710-2712, 2908-2910) and then correct the import/export process that
produced the encoding damage (ensure UTF-8 read/write, no Latin1/Windows-1252
steps) so future PO updates preserve accented characters.
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "Leave channel blank for a generic network invite. Description is just for your records — visible only to you in this list." | ||
| msgstr "Lascia il canale vuoto per un invito generico alla rete. La descrizione è solo per i tuoi appunti — visibile solo a te in questo elenco." | ||
| msgstr "Lascia il canale vuoto per un invito generico alla rete. La descrizione è solo per i tuoi appunti â visibile solo a te in questo elenco." |
There was a problem hiding this comment.
Fix the mojibake in the Italian invite-link copy.
These msgstr values contain encoding corruption (è, �), so the invite panel will show broken Italian text instead of accented characters. Please restore the intended Unicode text and check the catalog generation/import step that produced these strings.
Also applies to: 2552-2554
🤖 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/locales/it/messages.po` around lines 1372 - 1374, The Italian translation
for the InvitationsPanel msgid contains mojibake (e.g. "è", "�"); open the
msgstr for that msgid in src/locales/it/messages.po (and the other occurrences
noted at lines ~2552-2554) and replace the corrupted byte sequences with the
correct Unicode characters (e.g. "è" for è and "—" for â��) so the string
reads: "Lascia il canale vuoto per un invito generico alla rete. La descrizione
è solo per i tuoi appunti — visibile solo a te in questo elenco."; after fixing,
run the same catalog generation/import step that produced these strings to
ensure it preserves UTF‑8 encoding and adjust that pipeline if it reintroduces
mojibake.
| #. placeholder {0}: Math.floor(secs / 86400) | ||
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "{0}d ago" | ||
| msgstr "{0}日前" | ||
| msgstr "{0}æ¥å" | ||
|
|
||
| #. placeholder {0}: Math.floor(secs / 3600) | ||
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "{0}h ago" | ||
| msgstr "{0}時間前" | ||
| msgstr "{0}æéå" | ||
|
|
||
| #. placeholder {0}: Math.floor(secs / 60) | ||
| #: src/components/ui/InvitationsPanel.tsx | ||
| msgid "{0}m ago" | ||
| msgstr "{0}分前" | ||
| msgstr "{0}åå" | ||
|
|
There was a problem hiding this comment.
Restore UTF-8 translations; these msgstr values are mojibake and unreadable.
Line 88, Line 134, Line 599, and many other changed entries are corrupted byte sequences rather than valid Japanese text. This will surface broken UI copy in invitations/raw-log flows and must be reverted/fixed before merge.
Use the last known-good Japanese strings (or retranslate) and ensure the PO file is saved and committed as UTF-8 without double-encoding.
Also applies to: 132-135, 597-600, 699-702, 745-752, 765-776, 858-873, 933-936, 1334-1374, 1620-1623, 1740-1751, 2112-2116, 2544-2555, 2710-2713, 2908-2935
🤖 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/locales/ja/messages.po` around lines 85 - 99, The Japanese translations
in src/locales/ja/messages.po for msgid "{0}d ago", "{0}h ago", "{0}m ago" (and
other listed entries) are mojibake; restore them by replacing the corrupted
msgstr values with the last-known-good Japanese strings (or retranslate them)
and ensure the file is saved and committed as UTF-8 (no double-encoding, no
BOM). Locate the affected msgid entries (e.g., in InvitationsPanel.tsx
references) and update their msgstr fields to valid UTF-8 Japanese, verify
rendering in the UI flows (invitations/raw-log), and commit the fixed .po file
encoded as UTF-8.
Six fixes for items mupuf raised against the bouncer-networks test build (issue #120, 2026-05-21). Items 5, 6, 7, 10, 11, 12, 13 from his list are general-client concerns that affect every connection, not bouncer-specific -- tracked separately. #1 deleteServer wipes every server sharing host:port --------------------------------------------------- The filter keyed on (host, port) to drop the saved row. Bouncer children share their parent's soju endpoint (only bouncerNetid distinguishes them), so deleting one bouncer-bound network silently removed the parent and every sibling from localStorage. On next reload nothing was there and the reconnect path surfaced as "Authentication required" spam. Fix: key the filter on the unique server id. New regression test pins it (deleteServerBouncer.test.ts, 3 cases). #2 ADDNETWORK shows a spinner forever; user reloads to see the network ------------------------------------------------------------------ The form's optimistic 500ms timer closed itself on no-FAIL, but the networks list never refreshed -- soju only pushes a BOUNCER NETWORK notification when the `-notify` cap is ACK'd, and some deployments don't advertise it. Fire BOUNCER LISTNETWORKS on the success path so the new row appears regardless of notify-cap state. #3 Connect button text was illegible --------------------------------- The button used `bg-primary` -- a CSS-var-driven class that resolves to almost-white in the dark theme, against `text-white`. Switched every primary-action button in the bouncer panels to `bg-discord-primary` (#5865F2) + `text-white`, matching the Invitations panel's convention. Also fixed the never-actually- defined `bg-primary-hover` class. #4 Networks not auto-joined ------------------------ Already addressed by PR #249's server-initiated-JOIN fix, which is on this branch via the main-merge. PR #249's handler is now also gated on isBouncerControl (see #9 below) so the meta connection doesn't try to CHATHISTORY the empty channels soju auto-joins there. #8 No way to set the WebSocket path (mupuf had to nginx-rewrite) ------------------------------------------------------------ ircClient.connect already preserves a custom path when the host field contains a URL with one, but it was undiscoverable. Added helper text under the Server Host input explaining that a full URL like `wss://host:port/socket` is accepted, with a soju reverse-proxy callout. Translated across all 18 locales. #9 "Cannot fetch chat history on bouncer connection" spam ------------------------------------------------------ Soju FAILs every CHATHISTORY against the bouncer-networks meta session -- it has no real channels of its own. On resume-from- suspend, the reconnect-time loop in connection.ts fired one CHATHISTORY per cached channel, producing one error toast each. Gate every CHATHISTORY send-site on !isBouncerControl: - joinChannel (IRCClient.ts) - requestChathistoryBefore (IRCClient.ts) - reconnect-loop setTimeout (connection.ts) - server-initiated own-JOIN handler (users.ts) The bouncer control session is now treated like a cap-less server: WHO fires immediately, no CHATHISTORY round-trip. Build + 882 tests green.
dda2562 to
456d998
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/locales/it/messages.po (1)
1378-1378:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftFix remaining mojibake in Italian
msgstrvalues.Line 1378 and Line 2558 still contain corrupted byte sequences (
è,�) and will display broken UI copy. Restore proper UTF-8 Italian text and regenerate locale artifacts from the corrected source to avoid reintroducing this.Also applies to: 2558-2558
🤖 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/locales/it/messages.po` at line 1378, The Italian msgstr entries in messages.po contain mojibake (e.g., "è" and "â��"); open the msgstr at the problematic entries (the string currently reading 'Lascia il canale vuoto per un invito generico alla rete. La descrizione è solo per i tuoi appunti â�� visibile solo a te in questo elenco.') and replace the corrupted sequences with proper UTF-8 characters ("è" and "—" or "—" if you prefer an em dash), verify other occurrence(s) such as the entry referenced around line 2558 for the same replacements, save the file in UTF-8 encoding, then regenerate the locale artifacts (po->compiled files) so the corrected strings are propagated to the build.src/locales/ja/messages.po (1)
88-98:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftRestore UTF-8 Japanese translations; many
msgstrvalues are still mojibake.These entries are corrupted and will render unreadable Japanese in production UI. Please replace them with valid UTF-8 strings and regenerate catalogs (
.po→.mjs) using an encoding-safe pipeline.Also applies to: 132-134, 597-599, 699-701, 745-751, 765-779, 858-872, 933-935, 1338-1340, 1376-1378, 1624-1626, 1744-1754, 2116-2119, 2548-2558, 2714-2716, 2912-2914, 2936-2938
🤖 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/locales/ja/messages.po` around lines 88 - 98, Replace the mojibake msgstr values in src/locales/ja/messages.po for the time-ago entries (identify by msgid "{0}d ago", "{0}h ago", "{0}m ago" and the shown msgstrs like "{0}æ�¥å��", "{0}æ��é��å��", "{0}å��å��") with correct UTF-8 Japanese translations (e.g. "{0}日前", "{0}時間前", "{0}分前"), then regenerate the compiled locale catalog (.po → .mjs) using an encoding-safe pipeline (ensure file read/write uses UTF-8) and apply the same fixes to the other listed corrupted ranges so the runtime UI renders proper Japanese.
🤖 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 `@src/locales/fr/messages.mjs`:
- Line 1: The exported const messages uses a serialized JSON string with
mojibake (e.g. corrupted sequences like à and â�) — regenerate the French
translation bundle from the original UTF-8 .po source and replace the
JSON.parse(...) payload so all strings are valid UTF-8; ensure the export step
and any tooling (the code that produces the JSON string in messages, plus the
build/save step) write the file with UTF-8 encoding (no CP1252/Latin-1
conversion or BOM) and verify by opening the generated messages string (the
argument to JSON.parse in the exported messages constant) to confirm accented
characters (é, ç, à, ô, etc.) are intact.
---
Duplicate comments:
In `@src/locales/it/messages.po`:
- Line 1378: The Italian msgstr entries in messages.po contain mojibake (e.g.,
"è" and "�"); open the msgstr at the problematic entries (the string
currently reading 'Lascia il canale vuoto per un invito generico alla rete. La
descrizione è solo per i tuoi appunti � visibile solo a te in questo
elenco.') and replace the corrupted sequences with proper UTF-8 characters ("è"
and "—" or "—" if you prefer an em dash), verify other occurrence(s) such as the
entry referenced around line 2558 for the same replacements, save the file in
UTF-8 encoding, then regenerate the locale artifacts (po->compiled files) so the
corrected strings are propagated to the build.
In `@src/locales/ja/messages.po`:
- Around line 88-98: Replace the mojibake msgstr values in
src/locales/ja/messages.po for the time-ago entries (identify by msgid "{0}d
ago", "{0}h ago", "{0}m ago" and the shown msgstrs like "{0}��",
"{0}���", "{0}��") with correct UTF-8 Japanese translations (e.g.
"{0}日前", "{0}時間前", "{0}分前"), then regenerate the compiled locale catalog (.po →
.mjs) using an encoding-safe pipeline (ensure file read/write uses UTF-8) and
apply the same fixes to the other listed corrupted ranges so the runtime UI
renders proper Japanese.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7257e050-5866-4963-83ec-b6c5ed3216bc
📒 Files selected for processing (46)
src/components/ui/AddServerModal.tsxsrc/components/ui/BouncerNetworkForm.tsxsrc/components/ui/BouncerNetworksPanel.tsxsrc/lib/irc/IRCClient.tssrc/locales/cs/messages.mjssrc/locales/cs/messages.posrc/locales/de/messages.mjssrc/locales/de/messages.posrc/locales/en/messages.mjssrc/locales/en/messages.posrc/locales/es/messages.mjssrc/locales/es/messages.posrc/locales/fi/messages.mjssrc/locales/fi/messages.posrc/locales/fr/messages.mjssrc/locales/fr/messages.posrc/locales/it/messages.mjssrc/locales/it/messages.posrc/locales/ja/messages.mjssrc/locales/ja/messages.posrc/locales/ko/messages.mjssrc/locales/ko/messages.posrc/locales/nl/messages.mjssrc/locales/nl/messages.posrc/locales/pl/messages.mjssrc/locales/pl/messages.posrc/locales/pt/messages.mjssrc/locales/pt/messages.posrc/locales/ro/messages.mjssrc/locales/ro/messages.posrc/locales/ru/messages.mjssrc/locales/ru/messages.posrc/locales/sv/messages.mjssrc/locales/sv/messages.posrc/locales/tr/messages.mjssrc/locales/tr/messages.posrc/locales/uk/messages.mjssrc/locales/uk/messages.posrc/locales/zh-TW/messages.mjssrc/locales/zh-TW/messages.posrc/locales/zh/messages.mjssrc/locales/zh/messages.posrc/store/handlers/connection.tssrc/store/handlers/users.tssrc/store/index.tstests/store/deleteServerBouncer.test.ts
✅ Files skipped from review due to trivial changes (5)
- src/locales/cs/messages.mjs
- src/locales/en/messages.mjs
- src/locales/fi/messages.mjs
- src/locales/it/messages.mjs
- src/locales/en/messages.po
🚧 Files skipped from review as they are similar to previous changes (7)
- src/locales/es/messages.mjs
- src/locales/de/messages.mjs
- src/components/ui/BouncerNetworkForm.tsx
- src/components/ui/BouncerNetworksPanel.tsx
- src/locales/fi/messages.po
- src/locales/de/messages.po
- src/locales/cs/messages.po
| @@ -1 +1 @@ | |||
| /*eslint-disable*/export const messages=JSON.parse("{\"+5VMrz\":[\"Format de modèle invalide. Utilisez le format nick!user@host (jokers * autorisés)\"],\"+6NQQA\":[\"Canal d'assistance générale\"],\"+6NyRG\":[\"Client\"],\"+K0AvT\":[\"Déconnecter\"],\"+cyFdH\":[\"Message par défaut pour le statut absent\"],\"+mVPqU\":[\"Afficher le formatage Markdown dans les messages\"],\"+vqCJH\":[\"Votre nom d'utilisateur de compte pour l'authentification\"],\"+yPBXI\":[\"Choisir un fichier\"],\"+zy2Nq\":[\"Type\"],\"/09cao\":[\"Faible sécurité du lien (niveau \",[\"securityLevel\"],\")\"],\"/3BQ4J\":[\"Les utilisateurs extérieurs ne peuvent pas envoyer de messages\"],\"/4C8U0\":[\"Tout copier\"],\"/6BzZF\":[\"Afficher/masquer la liste des membres\"],\"/AkXyp\":[\"Confirmer ?\"],\"/TNOPk\":[\"L'utilisateur est absent\"],\"/XQgft\":[\"Découvrir\"],\"/cF7Rs\":[\"Volume\"],\"/dqduX\":[\"Page suivante\"],\"/fc3q4\":[\"Tout le contenu\"],\"/kISDh\":[\"Activer les sons de notification\"],\"/n04sB\":[\"Kill\"],\"/rTz0M\":[\"Audio\"],\"/rfkZe\":[\"Jouer des sons pour les mentions et messages\"],\"0/0ZGA\":[\"Masque du nom de salon\"],\"0D6j7U\":[\"En savoir plus sur les règles personnalisées →\"],\"0XsHcR\":[\"Expulser l'utilisateur\"],\"0ZpE//\":[\"Trier par utilisateurs\"],\"0bEPwz\":[\"Se mettre absent\"],\"0dGkPt\":[\"Développer la liste des canaux\"],\"0gS7M5\":[\"Nom d'affichage\"],\"0kS+M8\":[\"ExempleRÉSEAU\"],\"0rgoY7\":[\"Se connecter uniquement aux serveurs choisis\"],\"0wdd7X\":[\"Rejoindre\"],\"0wkVYx\":[\"Messages privés\"],\"111uHX\":[\"Aperçu du lien\"],\"196EG4\":[\"Supprimer la conversation privée\"],\"1DSr1i\":[\"Créer un compte\"],\"1O/24y\":[\"Afficher/masquer la liste des canaux\"],\"1VPJJ2\":[\"Avertissement de lien externe\"],\"1ZC/dv\":[\"Aucune mention ou message non lu\"],\"1pO1zi\":[\"Le nom du serveur est requis\"],\"1uwfzQ\":[\"Voir le sujet du canal\"],\"268g7c\":[\"Saisir le nom d'affichage\"],\"2F9+AZ\":[\"Aucun trafic IRC brut capturé pour le moment. Essayez de vous connecter ou d'envoyer un message.\"],\"2FOFq1\":[\"Les opérateurs du réseau pourraient potentiellement lire vos messages\"],\"2FYpfJ\":[\"Plus\"],\"2HF1Y2\":[[\"inviter\"],\" a invité \",[\"target\"],\" à rejoindre \",[\"channel\"]],\"2I70QL\":[\"Voir les informations du profil utilisateur\"],\"2QYdmE\":[\"Utilisateurs :\"],\"2QpEjG\":[\"a quitté\"],\"2YE223\":[\"Message dans #\",[\"0\"],\" (Entrée pour nouvelle ligne, Maj+Entrée pour envoyer)\"],\"2bimFY\":[\"Utiliser le mot de passe du serveur\"],\"2iTmdZ\":[\"Stockage local :\"],\"2odkwe\":[\"Strict – Protection plus agressive\"],\"2uDhbA\":[\"Saisir le nom d'utilisateur à inviter\"],\"2ygf/L\":[\"← Retour\"],\"2zEgxj\":[\"Rechercher des GIFs...\"],\"3RdPhl\":[\"Renommer le canal\"],\"3THokf\":[\"Utilisateur avec droit de parole\"],\"3TSz9S\":[\"Réduire\"],\"3jBDvM\":[\"Nom d'affichage du salon\"],\"3ryuFU\":[\"Rapports de plantage optionnels pour améliorer l'application\"],\"3uBF/8\":[\"Fermer le visualiseur\"],\"3uwW8F\":[\"https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"3xf8Kz\":[\"Entrez le nom du compte...\"],\"4/Rr0R\":[\"Inviter un utilisateur dans le canal actuel\"],\"4EZrJN\":[\"Règles\"],\"4JJtW9\":[\"#overflow\"],\"4NqeT4\":[\"Profil de flood (+F)\"],\"4RZQRK\":[\"Qu'est-ce que vous faites ?\"],\"4hfTrB\":[\"Pseudo\"],\"4n99LO\":[\"Déjà dans \",[\"0\"]],\"4t6vMV\":[\"Passer automatiquement en mode ligne unique pour les messages courts\"],\"4vsHmf\":[\"Temps (min)\"],\"5+INAX\":[\"Surligner les messages qui vous mentionnent\"],\"5R5Pv/\":[\"Nom Oper\"],\"678PKt\":[\"Nom du réseau\"],\"6Aih4U\":[\"Hors ligne\"],\"6CO3WE\":[\"Mot de passe requis pour rejoindre le salon. Laissez vide pour supprimer la clé.\"],\"6HhMs3\":[\"Message de déconnexion\"],\"6V3Ea3\":[\"Copié\"],\"6lGV3K\":[\"Afficher moins\"],\"6yFOEi\":[\"Entrez le mot de passe oper...\"],\"7+IHTZ\":[\"Aucun fichier choisi\"],\"73hrRi\":[\"nick!user@host (ex. : spam*!*@*, *!*@badhost.com)\"],\"7QkKyN\":[\"Envoyer un message privé\"],\"7U1W7c\":[\"Très détendu\"],\"7Y1YQj\":[\"Nom réel :\"],\"7YHArF\":[\"— ouvrir dans le visualiseur\"],\"7fjnVl\":[\"Rechercher des utilisateurs...\"],\"7jL88x\":[\"Supprimer ce message ? Cette action est irréversible.\"],\"7nGhhM\":[\"À quoi pensez-vous ?\"],\"7sEpu1\":[\"Membres — \",[\"0\"]],\"7sNhEz\":[\"Nom d'utilisateur\"],\"8H0Q+x\":[\"En savoir plus sur les profils →\"],\"8Phu0A\":[\"Afficher quand des utilisateurs changent de pseudo\"],\"8XTG9e\":[\"Saisir le mot de passe oper\"],\"8XsV2J\":[\"Réessayer l'envoi\"],\"8ZsakT\":[\"Mot de passe\"],\"8kR84m\":[\"Vous êtes sur le point d'ouvrir un lien externe :\"],\"8lCgih\":[\"Supprimer la règle\"],\"8o3dPc\":[\"Déposez les fichiers pour les téléverser\"],\"8p/xVT\":[[\"0\",\"plural\",{\"one\":[[\"1\"]],\"other\":[[\"2\"]]}]],\"8wRzac\":[[\"joinCount\",\"plural\",{\"one\":[\"a rejoint\"],\"other\":[\"a rejoint \",[\"joinCount\"],\" fois\"]}]],\"9BMLnJ\":[\"Se reconnecter au serveur\"],\"9OEgyT\":[\"Ajouter une réaction\"],\"9PQ8m2\":[\"G-Line (bannissement global)\"],\"9Qs99X\":[\"E-mail :\"],\"9QupBP\":[\"Supprimer le motif\"],\"9bG48P\":[\"Envoi en cours\"],\"9f5f0u\":[\"Questions sur la confidentialité ? Contactez-nous :\"],\"9unqs3\":[\"Absent :\"],\"9v3hwv\":[\"Aucun serveur trouvé.\"],\"9zb2WA\":[\"Connexion en cours\"],\"A1taO8\":[\"Rechercher\"],\"A2adVi\":[\"Envoyer des notifications de frappe\"],\"A9Rhec\":[\"Nom du salon\"],\"AWOSPo\":[\"Zoomer\"],\"AXSpEQ\":[\"Oper à la connexion\"],\"AeXO77\":[\"Compte\"],\"AhNP40\":[\"Avancer\"],\"Ai2U7L\":[\"Hôte\"],\"AjBQnf\":[\"Pseudo modifié\"],\"AmXVh6\":[\"https://example.com/avatar.png\"],\"AnRu/j\":[\"Annuler la réponse\"],\"ApSx0O\":[[\"0\"],\" messages trouvés correspondant à \\\"\",[\"searchQuery\"],\"\\\"\"],\"AxPAXW\":[\"Aucun résultat trouvé\"],\"AyNqAB\":[\"Afficher tous les événements serveur dans le chat\"],\"B/QqGw\":[\"Absent du clavier\"],\"B8AaMI\":[\"Ce champ est obligatoire\"],\"BA2c49\":[\"Le serveur ne supporte pas le filtrage LIST avancé\"],\"BDKt3I\":[[\"0\"],\", \",[\"1\"],\", \",[\"2\"],\" et \",[\"3\"],\" autres sont en train d'écrire...\"],\"BGul2A\":[\"Vous avez des modifications non enregistrées. Voulez-vous vraiment fermer sans enregistrer ?\"],\"BIf9fi\":[\"Votre message de statut\"],\"BPm98R\":[\"Aucun serveur sélectionné. Choisissez d'abord un serveur dans la barre latérale ; les liens d'invitation sont gérés par serveur.\"],\"BZz3md\":[\"Votre site web personnel\"],\"Bgm/H7\":[\"Permettre la saisie sur plusieurs lignes\"],\"BiQIl1\":[\"Épingler cette conversation privée\"],\"BlNZZ2\":[\"Cliquez pour aller au message\"],\"Bowq3c\":[\"Seuls les opérateurs peuvent modifier le sujet\"],\"Btozzp\":[\"Cette image a expiré\"],\"Bycfjm\":[\"Total : \",[\"0\"]],\"C6IBQc\":[\"Copier le JSON complet\"],\"C9L9wL\":[\"Collecte de données\"],\"CDq4wC\":[\"Modérer l'utilisateur\"],\"CHVRxG\":[\"Message à @\",[\"0\"],\" (Maj+Entrée pour nouvelle ligne)\"],\"CN9zdR\":[\"Le nom oper et le mot de passe sont requis\"],\"CW3sYa\":[\"Ajouter la réaction \",[\"emoji\"]],\"CaAkqd\":[\"Afficher les déconnexions\"],\"CbvaYj\":[\"Bannir par pseudo\"],\"CcK+Ft\":[\"PDF\"],\"Ce8q3L\":[\"Sélectionner un canal\"],\"CsekCi\":[\"Normal\"],\"D+NlUC\":[\"Système\"],\"D28t6+\":[\"a rejoint et quitté\"],\"DB8zMK\":[\"Appliquer\"],\"DBcWHr\":[\"Fichier son de notification personnalisé\"],\"DTy9Xw\":[\"Aperçus des médias\"],\"Dj4pSr\":[\"Choisissez un mot de passe sécurisé\"],\"Du+zn+\":[\"Recherche...\"],\"Du2T2f\":[\"Paramètre introuvable\"],\"DwsSVQ\":[\"Appliquer les filtres & Actualiser\"],\"E3W/zd\":[\"Pseudo par défaut\"],\"E6nRW7\":[\"Copier l'URL\"],\"E703RG\":[\"Modes :\"],\"EAeu1Z\":[\"Envoyer l'invitation\"],\"EFKJQT\":[\"Paramètre\"],\"EGPQBv\":[\"Règles de flood personnalisées (+f)\"],\"ELik0r\":[\"Voir la politique de confidentialité complète\"],\"EPbeC2\":[\"Voir ou modifier le sujet du canal\"],\"EQCDNT\":[\"Entrez le nom d'utilisateur oper...\"],\"EUvulZ\":[\"1 message trouvé correspondant à \\\"\",[\"searchQuery\"],\"\\\"\"],\"EatZYJ\":[\"Image suivante\"],\"EdQY6l\":[\"Aucun\"],\"EnqLYU\":[\"Rechercher des serveurs...\"],\"F0OKMc\":[\"Modifier le serveur\"],\"F6Int2\":[\"Activer les surlignages\"],\"FDoLyE\":[\"Utilisateurs max.\"],\"FUU/hZ\":[\"Contrôle la quantité de médias externes chargés dans le chat.\"],\"Fdp03t\":[\"activé\"],\"FfPWR0\":[\"Modal\"],\"FjkaiT\":[\"Dézoomer\"],\"FlqOE9\":[\"Ce que cela signifie :\"],\"FolHNl\":[\"Gérez votre compte et l'authentification\"],\"Fp2Dif\":[\"A quitté le serveur\"],\"G5KmCc\":[\"GZ-Line (Z-Line globale)\"],\"GDs0lz\":[\"<0>Risque :</0> Des informations sensibles (messages, conversations privées, identifiants de connexion) pourraient être exposées aux administrateurs réseau ou à des attaquants positionnés entre les serveurs IRC.\"],\"GR+2I3\":[\"Ajouter un masque d'invitation (ex. nick!*@*, *!*@host.com)\"],\"GRLyMU\":[\"Fermer les notifications serveur détachées\"],\"GdhD7H\":[\"Cliquez à nouveau pour confirmer\"],\"GlHnXw\":[\"Échec du changement de pseudo: \",[\"error\"],\" \",[\"0\"]],\"GswZF3\":[\"Aperçu :\"],\"GtmO8/\":[\"de\"],\"GtuHUQ\":[\"Renommer ce salon sur le serveur. Tous les utilisateurs verront le nouveau nom.\"],\"GuGfFX\":[\"Activer/désactiver la recherche\"],\"GxkJXS\":[\"Téléversement...\"],\"GzbwnK\":[\"A rejoint le canal\"],\"GzsUDB\":[\"Profil étendu\"],\"H/PnT8\":[\"Insérer un emoji\"],\"H6Izzl\":[\"Votre code couleur préféré\"],\"H9jIv+\":[\"Afficher les entrées/sorties\"],\"HAKBY9\":[\"Télécharger des fichiers\"],\"HdE1If\":[\"Canal\"],\"Hk4AW9\":[\"Votre nom d'affichage préféré\"],\"HmHDk7\":[\"Sélectionner un membre\"],\"HrQzPU\":[\"Canaux sur \",[\"networkName\"]],\"I2tXQ5\":[\"Message à @\",[\"0\"],\" (Entrée pour nouvelle ligne, Maj+Entrée pour envoyer)\"],\"I6bw/h\":[\"Bannir l'utilisateur\"],\"I92Z+b\":[\"Activer les notifications\"],\"I9D72S\":[\"Êtes-vous sûr de vouloir supprimer ce message ? Cette action est irréversible.\"],\"IA+1wo\":[\"Afficher quand des utilisateurs sont expulsés des salons\"],\"IDwkJx\":[\"IRC Operator\"],\"ILlU+s\":[\"Info :\"],\"IUwGEM\":[\"Enregistrer les modifications\"],\"IVeGK6\":[[\"0\"],\", \",[\"1\"],\" et \",[\"2\"],\" sont en train d'écrire...\"],\"IgrLD/\":[\"Pause\"],\"Im6JED\":[\"WHISPER\"],\"ImOQa9\":[\"Répondre\"],\"IoHMnl\":[\"La valeur maximale est \",[\"0\"]],\"IvMj+0\":[\"Op\"],\"J28zul\":[\"Connexion en cours...\"],\"J5T9NW\":[\"Informations utilisateur\"],\"J8Y5+z\":[\"Oups ! La réseau s'est divisé ! ⚠️\"],\"JBHkBA\":[\"A quitté le canal\"],\"JCwL0Q\":[\"Saisir une raison (facultatif)\"],\"JFciKP\":[\"Basculer\"],\"JXGkhG\":[\"Changer le nom du canal (opérateurs uniquement)\"],\"JcD7qf\":[\"Plus d'actions\"],\"JdkA+c\":[\"Secret (+s)\"],\"Jmu12l\":[\"Canaux du serveur\"],\"JvQ++s\":[\"Activer le Markdown\"],\"K2jwh/\":[\"Aucune donnée WHOIS disponible\"],\"KAXSwC\":[\"Voice\"],\"KDfTdX\":[\"Supprimer le message\"],\"KKBlUU\":[\"Intégrer\"],\"KM0pLb\":[\"Bienvenue dans le canal !\"],\"KR6W2h\":[\"Ne plus ignorer l'utilisateur\"],\"KV+Bi1\":[\"Sur invitation uniquement (+i)\"],\"KdCtwE\":[\"Nombre de secondes de surveillance de l'activité de flood avant la réinitialisation des compteurs\"],\"Kkezga\":[\"Mot de passe du serveur\"],\"KsiQ/8\":[\"Les utilisateurs doivent être invités pour rejoindre le salon\"],\"L+gB/D\":[\"Informations sur le salon\"],\"LC1a7n\":[\"Le serveur IRC a signalé que ses liens entre serveurs ont un faible niveau de sécurité. Cela signifie que lorsque vos messages sont relayés entre les serveurs IRC du réseau, ils peuvent ne pas être correctement chiffrés ou les certificats SSL/TLS peuvent ne pas être validés correctement.\"],\"LNfLR5\":[\"Afficher les expulsions\"],\"LQb0W/\":[\"Afficher tous les événements\"],\"LU7/yA\":[\"Nom alternatif pour l'affichage. Peut contenir des espaces, emojis et caractères spéciaux. Le vrai nom (\",[\"channelName\"],\") sera toujours utilisé pour les commandes IRC.\"],\"LUb9O7\":[\"Un port de serveur valide est requis\"],\"LV4fT6\":[\"Description (optionnelle, ex. « Bêta-testeurs T3 »)\"],\"Lb+BUl\":[\"https://example.com/avatar.jpg\"],\"LcET2C\":[\"Politique de confidentialité\"],\"LcuSDR\":[\"Gérez les informations de votre profil et vos métadonnées\"],\"LqLS9B\":[\"Afficher les changements de pseudo\"],\"LsDQt2\":[\"Paramètres du canal\"],\"LtI9AS\":[\"Propriétaire\"],\"LuNhhL\":[\"a réagi à ce message\"],\"M/AZNG\":[\"URL de votre image d'avatar\"],\"M/WIer\":[\"Envoyer un message\"],\"M8er/5\":[\"Nom :\"],\"MHk+7g\":[\"Image précédente\"],\"MRorGe\":[\"MP à l'utilisateur\"],\"MVbSGP\":[\"Fenêtre temporelle (secondes)\"],\"MkpcsT\":[\"Vos messages et paramètres sont stockés localement sur votre appareil\"],\"N/hDSy\":[\"Marquer comme bot, généralement 'on' ou vide\"],\"N7TQbE\":[\"Inviter un utilisateur dans \",[\"channelName\"]],\"NCca/o\":[\"Entrez le pseudo par défaut...\"],\"Nqs6B9\":[\"Affiche tous les médias externes. Toute URL peut déclencher une requête vers un serveur inconnu.\"],\"Nt+9O7\":[\"Utiliser WebSocket au lieu de TCP brut\"],\"NxIHzc\":[\"Expulser l'utilisateur\"],\"O+v/cL\":[\"Parcourir tous les canaux du serveur\"],\"ODwSCk\":[\"Envoyer un GIF\"],\"OGQ5kK\":[\"Configurer les sons de notification et les mises en évidence\"],\"OIPt1Z\":[\"Afficher ou masquer la barre latérale de la liste des membres\"],\"OKSNq/\":[\"Très strict\"],\"ONWvwQ\":[\"Téléverser\"],\"OVKoQO\":[\"Votre mot de passe de compte pour l'authentification\"],\"ObsidianIRC - Bringing IRC to the future\":[\"ObsidianIRC - Amener IRC vers le futur\"],\"OhCpra\":[\"Définir un sujet…\"],\"OkltoQ\":[\"Bannir \",[\"username\"],\" par pseudo (l'empêche de rejoindre avec le même pseudo)\"],\"P+t/Te\":[\"Aucune donnée supplémentaire\"],\"P42Wcc\":[\"Sécurisé\"],\"PD38l0\":[\"Aperçu de l'avatar du canal\"],\"PD9mEt\":[\"Saisir un message...\"],\"PPqfdA\":[\"Ouvrir les paramètres de configuration du canal\"],\"PSCjfZ\":[\"Le sujet affiché pour ce salon. Tous les utilisateurs peuvent le voir.\"],\"PZCecv\":[\"Aperçu PDF\"],\"PeLgsC\":[[\"c\",\"plural\",{\"one\":[\"1 fois\"],\"other\":[[\"c\"],\" fois\"]}]],\"PguS2C\":[\"Ajouter un masque d'exception (ex. nick!*@*, *!*@host.com)\"],\"Pil5Ty\":[\"Affichage de \",[\"displayedChannelsCount\"],\" sur \",[\"0\"],\" canaux\"],\"PqhVlJ\":[\"Bannir l'utilisateur (par hostmask)\"],\"Q+chwU\":[\"Nom d'utilisateur :\"],\"Q2QY4/\":[\"Supprimer cette invitation\"],\"Q6hhn8\":[\"Préférences\"],\"QF4a34\":[\"Veuillez saisir un nom d'utilisateur\"],\"QGqSZ2\":[\"Couleur et mise en forme\"],\"QJQd1J\":[\"Modifier le profil\"],\"QSzGDE\":[\"Inactif\"],\"QUlny5\":[\"Bienvenue sur \",[\"0\"],\" !\"],\"Qoq+GP\":[\"Lire la suite\"],\"QuSkCF\":[\"Filtrer les canaux...\"],\"QwUrDZ\":[\"a changé le sujet en : \",[\"topic\"]],\"R0UH07\":[\"Image \",[\"0\"],\" sur \",[\"1\"]],\"R7SsBE\":[\"Couper le son\"],\"R8rf1X\":[\"Cliquez pour définir le sujet\"],\"RArB3D\":[\"a été expulsé de \",[\"channelName\"],\" par \",[\"username\"]],\"RI3cWd\":[\"Découvrez le monde de l'IRC avec ObsidianIRC\"],\"RIfHS5\":[\"Créer un nouveau lien d'invitation\"],\"RMMaN5\":[\"Modéré (+m)\"],\"RWw9Lg\":[\"Fermer la fenêtre\"],\"RZ2BuZ\":[\"L'enregistrement du compte \",[\"account\"],\" nécessite une vérification : \",[\"message\"]],\"RySp6q\":[\"Masquer les commentaires\"],\"SPKQTd\":[\"Le pseudo est requis\"],\"SPVjfj\":[\"Par défaut « aucune raison » si laissé vide\"],\"SQKPvQ\":[\"Inviter un utilisateur\"],\"SkZcl+\":[\"Choisissez un profil de protection contre le flood prédéfini. Ces profils offrent des paramètres de protection équilibrés pour différents cas d'usage.\"],\"Slr+3C\":[\"Utilisateurs min.\"],\"Spnlre\":[\"Vous avez invité \",[\"target\"],\" à rejoindre \",[\"channel\"]],\"T/ckN5\":[\"Ouvrir dans le visualiseur\"],\"T91vKp\":[\"Lire\"],\"TV2Wdu\":[\"Découvrez comment nous gérons vos données et protégeons votre vie privée.\"],\"TgFpwD\":[\"Application en cours...\"],\"TkzSFB\":[\"Aucune modification\"],\"TtserG\":[\"Saisir le vrai nom\"],\"Ttz9J1\":[\"Entrez le mot de passe...\"],\"Tz0i8g\":[\"Paramètres\"],\"U3pytU\":[\"Admin\"],\"UDb2YD\":[\"Réagir\"],\"UE4KO5\":[\"*channel*\"],\"UETAwW\":[\"Vous n'avez encore créé aucun lien d'invitation. Utilisez le formulaire ci-dessus pour créer le premier.\"],\"UGT5vp\":[\"Enregistrer les paramètres\"],\"UV5hLB\":[\"Aucun bannissement trouvé\"],\"Uaj3Nd\":[\"Messages de statut\"],\"Ue3uny\":[\"Par défaut (aucun profil)\"],\"UkARhe\":[\"Normal – Protection standard\"],\"Umn7Cj\":[\"Pas encore de commentaires. Soyez le premier !\"],\"UtUIRh\":[[\"0\"],\" anciens messages\"],\"UwzP+U\":[\"Connexion sécurisée\"],\"V0/A4O\":[\"Propriétaire du canal\"],\"V4qgxE\":[\"Créé avant (min)\"],\"V8yTm6\":[\"Effacer la recherche\"],\"VJMMyz\":[\"ObsidianIRC - L'IRC vers le futur\"],\"VJScHU\":[\"Raison\"],\"VLsmVV\":[\"Couper les notifications\"],\"VbyRUy\":[\"Commentaires\"],\"Vmx0mQ\":[\"Défini par :\"],\"VqnIZz\":[\"Consulter notre politique de confidentialité et nos pratiques en matière de données\"],\"VrMygG\":[\"La longueur minimale est \",[\"0\"]],\"VrnTui\":[\"Vos pronoms, affichés dans votre profil\"],\"W8E3qn\":[\"Compte authentifié\"],\"WAakm9\":[\"Supprimer le canal\"],\"WFxTHC\":[\"Ajouter un masque de bannissement (ex. nick!*@*, *!*@host.com)\"],\"WN1g9F\":[\"L'hôte du serveur est requis\"],\"WRYdXW\":[\"Position audio\"],\"WUOH5B\":[\"Ignorer l'utilisateur\"],\"WWEXnZ\":[[\"0\",\"plural\",{\"one\":[\"Afficher 1 élément de plus\"],\"other\":[\"Afficher \",[\"1\"],\" éléments de plus\"]}]],\"WYxRzo\":[\"Créer et gérer vos liens d'invitation\"],\"Wd38W1\":[\"Laissez le canal vide pour une invitation générique au réseau. La description sert uniquement à vos notes — visible uniquement par vous dans cette liste.\"],\"Weq9zb\":[\"Général\"],\"Wfj7Sk\":[\"Activer ou désactiver les sons de notification\"],\"Wm7gbG\":[\"GitHub :\"],\"WyeHWY\":[\"*spam*\"],\"WzMCru\":[\"Profil de l'utilisateur\"],\"X6S3lt\":[\"Rechercher des paramètres, canaux, serveurs...\"],\"XEHan5\":[\"Continuer quand même\"],\"XI1+wb\":[\"Format invalide\"],\"XIXeuC\":[\"Message à @\",[\"0\"]],\"XMS+k4\":[\"Démarrer un message privé\"],\"XWgxXq\":[\"Album\"],\"Xd7+IT\":[\"Désépingler la conversation privée\"],\"Xm/s+u\":[\"Affichage\"],\"Xp2n93\":[\"Affiche les médias provenant de l'hébergeur de fichiers de confiance de votre serveur. Aucune requête n'est envoyée à des services externes.\"],\"XvjC4F\":[\"Enregistrement...\"],\"Y/qryO\":[\"Aucun utilisateur ne correspond à votre recherche\"],\"YAqRpI\":[\"Enregistrement du compte réussi pour \",[\"account\"],\" : \",[\"message\"]],\"YEfzvP\":[\"Sujet protégé (+t)\"],\"YQOn6a\":[\"Réduire la liste des membres\"],\"YRCoE9\":[\"Opérateur du canal\"],\"YURQaF\":[\"Voir le profil\"],\"YdBSvr\":[\"Contrôler l'affichage des médias et du contenu externe\"],\"Yj6U3V\":[\"Pas de serveur central :\"],\"YjvpGx\":[\"Pronoms\"],\"YqH4l4\":[\"Aucune clé\"],\"YyUPpV\":[\"Compte :\"],\"ZJSWfw\":[\"Message affiché lors de la déconnexion du serveur\"],\"ZR1dJ4\":[\"Invitations\"],\"ZdWg0V\":[\"Ouvrir dans le navigateur\"],\"ZhRBbl\":[\"Rechercher des messages…\"],\"Zmcu3y\":[\"Filtres avancés\"],\"a2/8e5\":[\"Sujet défini après (min)\"],\"aHKcKc\":[\"Page précédente\"],\"aJTbXX\":[\"Mot de passe Oper\"],\"aQryQv\":[\"Le modèle existe déjà\"],\"aW9pLN\":[\"Nombre maximum d'utilisateurs autorisés. Laissez vide pour aucune limite.\"],\"ah4fmZ\":[\"Affiche également des aperçus de YouTube, Vimeo, SoundCloud et autres services connus.\"],\"aifXak\":[\"Aucun média dans ce salon\"],\"ap2zBz\":[\"Détendu\"],\"az8lvo\":[\"Désactivé\"],\"azXSNo\":[\"Développer la liste des membres\"],\"azdliB\":[\"Se connecter à un compte\"],\"b26wlF\":[\"elle/la\"],\"bD/+Ei\":[\"Strict\"],\"bQ6BJn\":[\"Configurez des règles détaillées de protection contre le flood. Chaque règle précise le type d'activité à surveiller et l'action à prendre lorsque les seuils sont dépassés.\"],\"beV7+y\":[\"L'utilisateur recevra une invitation à rejoindre \",[\"channelName\"],\".\"],\"bk84cH\":[\"Message d'absence\"],\"bkHdLj\":[\"Ajouter un serveur IRC\"],\"bmQLn5\":[\"Ajouter une règle\"],\"bwRvnp\":[\"Action\"],\"c8+EVZ\":[\"Compte vérifié\"],\"cGYUlD\":[\"Aucun aperçu de média n'est chargé.\"],\"cLF98o\":[\"Afficher les commentaires (\",[\"commentCount\"],\")\"],\"cLKIDO\":[\"Aucun utilisateur disponible\"],\"cSgpoS\":[\"Épingler la conversation privée\"],\"cde3ce\":[\"Message <0>\",[\"0\"],\"</0>\"],\"chQsxg\":[\"Copier la sortie formatée\"],\"cl/A5J\":[\"Bienvenue sur \",[\"__DEFAULT_IRC_SERVER_NAME__\"],\" !\"],\"cnGeoo\":[\"Supprimer\"],\"coPLXT\":[\"Nous ne stockons pas vos communications IRC sur nos serveurs\"],\"crYH/6\":[\"Lecteur SoundCloud\"],\"d3sis4\":[\"Ajouter un serveur\"],\"d9aN5k\":[\"Retirer \",[\"username\"],\" du canal\"],\"dEgA5A\":[\"Annuler\"],\"dGi1We\":[\"Désépingler cette conversation privée\"],\"dJVuyC\":[\"a quitté \",[\"channelName\"],\" (\",[\"reason\"],\")\"],\"dMtLDE\":[\"à\"],\"dXqxlh\":[\"<0>⚠️ Risque de sécurité !</0> Cette connexion peut être vulnérable à l'interception ou aux attaques de type man-in-the-middle.\"],\"da9Q/R\":[\"Modes du canal modifiés\"],\"dhJN3N\":[\"Afficher les commentaires\"],\"dj2xTE\":[\"Ignorer la notification\"],\"dpCzmC\":[\"Paramètres de protection contre le flood\"],\"e9dQpT\":[\"Voulez-vous ouvrir ce lien dans un nouvel onglet ?\"],\"ePK91l\":[\"Modifier\"],\"eYBDuB\":[\"Téléverser une image ou fournir une URL avec substitution optionnelle \",[\"size\"]],\"edBbee\":[\"Bannir \",[\"username\"],\" par hostmask (l'empêche de rejoindre depuis la même adresse IP/hôte)\"],\"ekfzWq\":[\"Paramètres utilisateur\"],\"elPDWs\":[\"Personnalisez votre expérience du client IRC\"],\"eu2osY\":[\"<0>💡 Recommandation :</0> Ne continuez que si vous faites confiance à ce serveur et que vous comprenez les risques. Évitez de partager des informations sensibles ou des mots de passe via cette connexion.\"],\"euEhbr\":[\"Cliquez pour rejoindre \",[\"channel\"]],\"ez3vLd\":[\"Activer la saisie multiligne\"],\"f0J5Ki\":[\"Les communications entre serveurs peuvent utiliser des connexions non chiffrées\"],\"f9BHJk\":[\"Avertir l'utilisateur\"],\"fDOLLd\":[\"Aucun canal trouvé.\"],\"ffzDkB\":[\"Analyses anonymes :\"],\"fq1GF9\":[\"Afficher quand des utilisateurs se déconnectent du serveur\"],\"gEF57C\":[\"Ce serveur ne prend en charge qu'un seul type de connexion\"],\"gJuLUI\":[\"Liste d'ignorés\"],\"gNzMrk\":[\"Avatar actuel\"],\"gjPWyO\":[\"Entrez votre pseudo...\"],\"gz6UQ3\":[\"Agrandir\"],\"h6razj\":[\"Exclure le masque de nom de salon\"],\"hG6jnw\":[\"Aucun sujet défini\"],\"hG89Ed\":[\"Image\"],\"hYgDIe\":[\"Créer\"],\"hZ6znB\":[\"Port\"],\"ha+Bz5\":[\"ex. : 100:1440\"],\"he3ygx\":[\"Copier\"],\"hehnjM\":[\"Quantité\"],\"hzdLuQ\":[\"Seuls les utilisateurs avec voice ou plus peuvent parler\"],\"i0qMbr\":[\"Accueil\"],\"iDNBZe\":[\"Notifications\"],\"iH8pgl\":[\"Retour\"],\"iL9SZg\":[\"Bannir l'utilisateur (par pseudo)\"],\"iNt+3c\":[\"Retour à l'image\"],\"iQvi+a\":[\"Ne plus m'avertir de la faible sécurité des liens pour ce serveur\"],\"iSLIjg\":[\"Connecter\"],\"iWXkHH\":[\"Halfop\"],\"iZeTtp\":[\"Hôte du serveur\"],\"idD8Ev\":[\"Enregistré\"],\"iivqkW\":[\"Connecté depuis\"],\"ij+Elv\":[\"Aperçu de l'image\"],\"ilIWp7\":[\"Activer/désactiver les notifications\"],\"iuaqvB\":[\"Utilisez * comme joker. Exemples : baduser!*@*, *!*@spammer.com, troll*!*@*\"],\"ixkTse\":[\"Bot\"],\"j2DGR0\":[\"Bannir par masque d'hôte\"],\"jA4uoI\":[\"Sujet :\"],\"jLXxGK\":[\"https://example.com\"],\"jPSk57\":[\"Raison (facultatif)\"],\"jUV7CU\":[\"Téléverser un avatar\"],\"jW5Uwh\":[\"Contrôle la quantité de médias externes chargés. Désactivé / Sûr / Sources fiables / Tout le contenu.\"],\"jXzms5\":[\"Options de pièce jointe\"],\"jZlrte\":[\"Couleur\"],\"jfC/xh\":[\"Contact\"],\"jywMpv\":[\"#new-channel-name\"],\"k112DD\":[\"Charger les anciens messages\"],\"k3ID0F\":[\"Filtrer les membres…\"],\"k65gsE\":[\"Analyse approfondie\"],\"k7Zgob\":[\"Annuler la connexion\"],\"kAVx5h\":[\"Aucune invitation trouvée\"],\"kCLEPU\":[\"Connecté à\"],\"kF5LKb\":[\"Modèles ignorés :\"],\"kGeOx/\":[\"Rejoindre \",[\"0\"]],\"kITKr8\":[\"Chargement des modes du salon...\"],\"kPpPsw\":[\"Vous êtes un IRC Operator\"],\"kWJmRL\":[\"Vous\"],\"kfcRb0\":[\"Avatar\"],\"kjMqSj\":[\"Copier JSON\"],\"krViRy\":[\"Cliquer pour copier en JSON\"],\"ks71ra\":[\"Exceptions\"],\"kw4lRv\":[\"Semi-opérateur du canal\"],\"kxgIRq\":[\"Sélectionnez ou ajoutez un canal pour commencer.\"],\"ky6dWe\":[\"Aperçu de l'avatar\"],\"l+GxCv\":[\"Chargement des canaux...\"],\"l+IUVW\":[\"Vérification du compte réussie pour \",[\"account\"],\" : \",[\"message\"]],\"l/siQz\":[[\"reconnectCount\",\"plural\",{\"one\":[\"s'est reconnecté\"],\"other\":[\"s'est reconnecté \",[\"reconnectCount\"],\" fois\"]}]],\"l1l8sj\":[\"il y a \",[\"0\"],\" j\"],\"l5NhnV\":[\"#canal (optionnel)\"],\"l5jmzx\":[[\"0\"],\" et \",[\"1\"],\" sont en train d'écrire...\"],\"lCF0wC\":[\"Actualiser\"],\"lHy8N5\":[\"Chargement de canaux supplémentaires...\"],\"lasgrr\":[\"utilisé\"],\"lbpf14\":[\"Rejoindre \",[\"value\"]],\"lfFsZ4\":[\"Canaux\"],\"lkNdiH\":[\"Nom de compte\"],\"ln500L\":[\"ObsidianIRC\"],\"lnCMdg\":[\"Téléverser une image\"],\"loQxaJ\":[\"Je suis de retour\"],\"lvfaxv\":[\"ACCUEIL\"],\"m16xKo\":[\"Ajouter\"],\"m8flAk\":[\"Aperçu (pas encore envoyé)\"],\"mEPxTp\":[\"<0>⚠️ Attention !</0> N'ouvrez que des liens provenant de sources fiables. Des liens malveillants peuvent compromettre votre sécurité ou votre vie privée.\"],\"mHGdhG\":[\"Informations sur le serveur\"],\"mHS8lb\":[\"Message dans #\",[\"0\"]],\"mMYBD9\":[\"Large – Portée de protection étendue\"],\"mTGsPd\":[\"Sujet du salon\"],\"mU8j6O\":[\"Pas de messages externes (+n)\"],\"mZp8FL\":[\"Retour automatique à une seule ligne\"],\"mdQu8G\":[\"VotrePseudo\"],\"miSSBQ\":[\"Commentaires (\",[\"commentCount\"],\")\"],\"mvyLSy\":[\"L'utilisateur est authentifié\"],\"mwtcGl\":[\"Fermer les commentaires\"],\"mzI/c+\":[\"Télécharger\"],\"n3fGRk\":[\"défini par \",[\"0\"]],\"nE9jsU\":[\"Détendu – Protection moins agressive\"],\"nNflMD\":[\"Quitter le canal\"],\"nPXkBi\":[\"Chargement des données WHOIS...\"],\"nQnxxF\":[\"Message dans #\",[\"0\"],\" (Maj+Entrée pour nouvelle ligne)\"],\"nWMRxa\":[\"Désépingler\"],\"nkC032\":[\"Aucun profil anti-flood\"],\"o69z4d\":[\"Envoyer un message d'avertissement à \",[\"username\"]],\"o9ylQi\":[\"Recherchez des GIFs pour commencer\"],\"oFGkER\":[\"Avis du serveur\"],\"oOi11l\":[\"Défiler vers le bas\"],\"oPYIL5\":[\"réseau\"],\"oQEzQR\":[\"Nouveau message privé\"],\"oXOSPE\":[\"En ligne\"],\"oal760\":[\"Des attaques man-in-the-middle sur les liens serveur sont possibles\"],\"oeqmmJ\":[\"Sources de confiance\"],\"optX0N\":[\"il y a \",[\"0\"],\" h\"],\"ovBPCi\":[\"Par défaut\"],\"p0Z69r\":[\"Le modèle ne peut pas être vide\"],\"p1KgtK\":[\"Échec du chargement audio\"],\"p59pEv\":[\"Détails supplémentaires\"],\"p7sRI6\":[\"Informer les autres que vous écrivez\"],\"pBm1od\":[\"Canal secret\"],\"pNmiXx\":[\"Votre pseudo par défaut pour tous les serveurs\"],\"pUUo9G\":[\"Nom d'hôte :\"],\"pVGPmz\":[\"Mot de passe du compte\"],\"peNE68\":[\"Permanent\"],\"plhHQt\":[\"Aucune donnée\"],\"pm6+q5\":[\"Avertissement de sécurité\"],\"pn5qSs\":[\"Informations supplémentaires\"],\"q0cR4S\":[\"est maintenant connu sous le nom de **\",[\"newNick\"],\"**\"],\"qFcunY\":[\"Le salon n'apparaîtra pas dans les commandes LIST ou NAMES\"],\"qLpTm/\":[\"Supprimer la réaction \",[\"emoji\"]],\"qVkGWK\":[\"Épingler\"],\"qY8wNa\":[\"Page d'accueil\"],\"qb0xJ7\":[\"Jokers : * correspond à toute séquence, ? à un seul caractère. Exemples : nick!*@*, *!*@host.com, *!*user@*\"],\"qhzpRq\":[\"Clé du salon (+k)\"],\"qtoOYG\":[\"Aucune limite\"],\"r1W2AS\":[\"Image hébergée\"],\"rIPR2O\":[\"Sujet défini avant (min)\"],\"rMMSYo\":[\"La longueur maximale est \",[\"0\"]],\"rWtzQe\":[\"Le réseau s'est divisé et reconnecté. ✅\"],\"rYG2u6\":[\"Veuillez patienter...\"],\"rdUucN\":[\"Aperçu\"],\"rjGI/Q\":[\"Confidentialité\"],\"rk8iDX\":[\"Chargement des GIFs...\"],\"rn6SBY\":[\"Rétablir le son\"],\"s/UKqq\":[\"A été expulsé du canal\"],\"s8cATI\":[\"a rejoint \",[\"channelName\"]],\"sCO9ue\":[\"La connexion à <0>\",[\"serverName\"],\"</0> présente les problèmes de sécurité suivants :\"],\"sGH11W\":[\"Serveur\"],\"sHI1H+\":[\"est maintenant connu sous le nom de **\",[\"newNick\"],\"**\"],\"sJyV04\":[[\"inviter\"],\" vous a invité à rejoindre \",[\"channel\"]],\"sby+1/\":[\"Cliquer pour copier\"],\"sfN25C\":[\"Votre nom réel ou complet\"],\"sliuzR\":[\"Ouvrir le lien\"],\"sqrO9R\":[\"Mentions personnalisées\"],\"sr6RdJ\":[\"Multiligne avec Shift+Entrée\"],\"swrCpB\":[\"Le canal a été renommé de \",[\"oldName\"],\" en \",[\"newName\"],\" par \",[\"user\"],[\"0\"]],\"sxkWRg\":[\"Avancé\"],\"t/YqKh\":[\"Supprimer\"],\"t47eHD\":[\"Votre identifiant unique sur ce serveur\"],\"tAkAh0\":[\"URL avec substitution optionnelle \",[\"size\"],\". Exemple : https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"tXLJS3\":[\"Afficher ou masquer la barre latérale de la liste des canaux\"],\"tfDRzk\":[\"Enregistrer\"],\"tiBsJk\":[\"a quitté \",[\"channelName\"]],\"tt4/UD\":[\"a quitté (\",[\"reason\"],\")\"],\"u0TcnO\":[\"Le pseudo {nick} est déjà utilisé, nouvel essai avec {newNick}\"],\"u0a8B4\":[\"S'authentifier en tant qu'opérateur IRC pour l'accès administratif\"],\"u0rWFU\":[\"Créé après (min)\"],\"u72w3t\":[\"Utilisateurs et modèles à ignorer\"],\"u7jc2L\":[\"a quitté\"],\"uAQUqI\":[\"Statut\"],\"uB85T3\":[\"Échec de l'enregistrement : \",[\"msg\"]],\"uV3DOL\":[\"G-Line\"],\"uW3lLI\":[\"Serveurs IRC :\"],\"ukyW4o\":[\"Vos liens d'invitation\"],\"usSSr/\":[\"Niveau de zoom\"],\"v7uvcf\":[\"Logiciel :\"],\"vE8kb+\":[\"Shift+Entrée pour les nouvelles lignes (Entrée envoie)\"],\"vERlcd\":[\"Profil\"],\"vK0RL8\":[\"Pas de sujet\"],\"vSJd18\":[\"Vidéo\"],\"vXIe7J\":[\"Langue\"],\"vaHYxN\":[\"Vrai nom\"],\"vhjbKr\":[\"Absent\"],\"w4NYox\":[\"client \",[\"title\"]],\"w8xQRx\":[\"Valeur invalide\"],\"wFjjxZ\":[\"a été expulsé de \",[\"channelName\"],\" par \",[\"username\"],\" (\",[\"reason\"],\")\"],\"wGjaGl\":[\"Aucune exception de bannissement trouvée\"],\"wPrGnM\":[\"Administrateur du canal\"],\"wRkP2d\":[\"GIF\"],\"wbm86v\":[\"Afficher quand des utilisateurs rejoignent ou quittent des salons\"],\"whqZ9r\":[\"Mots ou phrases supplémentaires à surligner\"],\"wm7RV4\":[\"Son de notification\"],\"wz/Yoq\":[\"Vos messages pourraient être interceptés lors du relais entre serveurs\"],\"x3+y8b\":[\"Nombre de personnes inscrites via ce lien\"],\"xCJdfg\":[\"Effacer\"],\"xOTzt5\":[\"à l'instant\"],\"xUHRTR\":[\"S'authentifier automatiquement comme opérateur à la connexion\"],\"xWHwwQ\":[\"Bannissements\"],\"xYilR2\":[\"Médias\"],\"xbi8D6\":[\"Ce serveur ne prend pas en charge les liens d'invitation (la capacité<0>obby.world/invitation</0>n'est pas annoncée). Vous pouvez toujours discuter normalement ; ce panneau est destiné aux réseaux propulsés par obbyircd.\"],\"xceQrO\":[\"Seuls les websockets sécurisés sont pris en charge\"],\"xdtXa+\":[\"nom-du-salon\"],\"xfXC7q\":[\"Salons textuels\"],\"xlCYOE\":[\"Chargement des messages...\"],\"xlhswE\":[\"La valeur minimale est \",[\"0\"]],\"xq97Ci\":[\"Ajouter un mot ou une expression...\"],\"xuRqRq\":[\"Limite de clients (+l)\"],\"xwF+7J\":[[\"0\"],\" est en train d'écrire...\"],\"y1eoq1\":[\"Copier le lien\"],\"yNeucF\":[\"Ce serveur ne supporte pas les métadonnées de profil étendues (extension IRCv3 METADATA). Les champs comme l'avatar, le nom d'affichage et le statut ne sont pas disponibles.\"],\"yPlrca\":[\"Avatar du salon\"],\"yQE2r9\":[\"Chargement\"],\"ySU+JY\":[\"votre@email.com\"],\"yTX1Rt\":[\"Nom d'utilisateur opérateur\"],\"yYOzWD\":[\"journaux\"],\"yfx9Re\":[\"Mot de passe opérateur IRC\"],\"ygCKqB\":[\"Arrêter\"],\"ymDxJx\":[\"Nom d'utilisateur opérateur IRC\"],\"yrpRsQ\":[\"Trier par nom\"],\"yz7wBu\":[\"Fermer\"],\"zJw+jA\":[\"définit le mode : \",[\"0\"]],\"zbymaY\":[\"il y a \",[\"0\"],\" min\"],\"zebeLu\":[\"Saisir le nom d'utilisateur oper\"],\"zpr0Bw\":[\"GZ-Line\"]}"); No newline at end of file | |||
| /*eslint-disable*/export const messages=JSON.parse("{\"+5VMrz\":[\"Format de modèle invalide. Utilisez le format nick!user@host (jokers * autorisés)\"],\"+6NQQA\":[\"Canal d'assistance générale\"],\"+6NyRG\":[\"Client\"],\"+K0AvT\":[\"Déconnecter\"],\"+cyFdH\":[\"Message par défaut pour le statut absent\"],\"+mVPqU\":[\"Afficher le formatage Markdown dans les messages\"],\"+vqCJH\":[\"Votre nom d'utilisateur de compte pour l'authentification\"],\"+yPBXI\":[\"Choisir un fichier\"],\"+zy2Nq\":[\"Type\"],\"/09cao\":[\"Faible sécurité du lien (niveau \",[\"securityLevel\"],\")\"],\"/3BQ4J\":[\"Les utilisateurs extérieurs ne peuvent pas envoyer de messages\"],\"/4C8U0\":[\"Tout copier\"],\"/6BzZF\":[\"Afficher/masquer la liste des membres\"],\"/AkXyp\":[\"Confirmer ?\"],\"/TNOPk\":[\"L'utilisateur est absent\"],\"/XQgft\":[\"Découvrir\"],\"/cF7Rs\":[\"Volume\"],\"/dqduX\":[\"Page suivante\"],\"/fc3q4\":[\"Tout le contenu\"],\"/kISDh\":[\"Activer les sons de notification\"],\"/n04sB\":[\"Kill\"],\"/rTz0M\":[\"Audio\"],\"/rfkZe\":[\"Jouer des sons pour les mentions et messages\"],\"0/0ZGA\":[\"Masque du nom de salon\"],\"0D6j7U\":[\"En savoir plus sur les règles personnalisées →\"],\"0XsHcR\":[\"Expulser l'utilisateur\"],\"0ZpE//\":[\"Trier par utilisateurs\"],\"0bEPwz\":[\"Se mettre absent\"],\"0dGkPt\":[\"Développer la liste des canaux\"],\"0gS7M5\":[\"Nom d'affichage\"],\"0kS+M8\":[\"ExempleRÉSEAU\"],\"0rgoY7\":[\"Se connecter uniquement aux serveurs choisis\"],\"0wdd7X\":[\"Rejoindre\"],\"0wkVYx\":[\"Messages privés\"],\"111uHX\":[\"Aperçu du lien\"],\"196EG4\":[\"Supprimer la conversation privée\"],\"1DSr1i\":[\"Créer un compte\"],\"1O/24y\":[\"Afficher/masquer la liste des canaux\"],\"1TNIig\":[\"Ouvrir\"],\"1VPJJ2\":[\"Avertissement de lien externe\"],\"1ZC/dv\":[\"Aucune mention ou message non lu\"],\"1pO1zi\":[\"Le nom du serveur est requis\"],\"1uwfzQ\":[\"Voir le sujet du canal\"],\"268g7c\":[\"Saisir le nom d'affichage\"],\"2F9+AZ\":[\"Aucun trafic IRC brut capturé pour le moment. Essayez de vous connecter ou d'envoyer un message.\"],\"2FOFq1\":[\"Les opérateurs du réseau pourraient potentiellement lire vos messages\"],\"2FYpfJ\":[\"Plus\"],\"2HF1Y2\":[[\"inviter\"],\" a invité \",[\"target\"],\" à rejoindre \",[\"channel\"]],\"2I70QL\":[\"Voir les informations du profil utilisateur\"],\"2QYdmE\":[\"Utilisateurs :\"],\"2QpEjG\":[\"a quitté\"],\"2YE223\":[\"Message dans #\",[\"0\"],\" (Entrée pour nouvelle ligne, Maj+Entrée pour envoyer)\"],\"2bimFY\":[\"Utiliser le mot de passe du serveur\"],\"2iTmdZ\":[\"Stockage local :\"],\"2odkwe\":[\"Strict – Protection plus agressive\"],\"2uDhbA\":[\"Saisir le nom d'utilisateur à inviter\"],\"2ygf/L\":[\"← Retour\"],\"2zEgxj\":[\"Rechercher des GIFs...\"],\"3RdPhl\":[\"Renommer le canal\"],\"3THokf\":[\"Utilisateur avec droit de parole\"],\"3TSz9S\":[\"Réduire\"],\"3jBDvM\":[\"Nom d'affichage du salon\"],\"3ryuFU\":[\"Rapports de plantage optionnels pour améliorer l'application\"],\"3uBF/8\":[\"Fermer le visualiseur\"],\"3uwW8F\":[\"https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"3xf8Kz\":[\"Entrez le nom du compte...\"],\"4/Rr0R\":[\"Inviter un utilisateur dans le canal actuel\"],\"4EZrJN\":[\"Règles\"],\"4JJtW9\":[\"#overflow\"],\"4NqeT4\":[\"Profil de flood (+F)\"],\"4RZQRK\":[\"Qu'est-ce que vous faites ?\"],\"4hfTrB\":[\"Pseudo\"],\"4n99LO\":[\"Déjà dans \",[\"0\"]],\"4t6vMV\":[\"Passer automatiquement en mode ligne unique pour les messages courts\"],\"4vsHmf\":[\"Temps (min)\"],\"4x/Axu\":[\"Votre bouncer n'a aucun réseau pour le moment. Ajoutez-en un pour commencer.\"],\"5+INAX\":[\"Surligner les messages qui vous mentionnent\"],\"5R5Pv/\":[\"Nom Oper\"],\"678PKt\":[\"Nom du réseau\"],\"6Aih4U\":[\"Hors ligne\"],\"6CO3WE\":[\"Mot de passe requis pour rejoindre le salon. Laissez vide pour supprimer la clé.\"],\"6HhMs3\":[\"Message de déconnexion\"],\"6V3Ea3\":[\"Copié\"],\"6lGV3K\":[\"Afficher moins\"],\"6yFOEi\":[\"Entrez le mot de passe oper...\"],\"7+IHTZ\":[\"Aucun fichier choisi\"],\"73fnil\":[\"TLS\"],\"73hrRi\":[\"nick!user@host (ex. : spam*!*@*, *!*@badhost.com)\"],\"7QkKyN\":[\"Envoyer un message privé\"],\"7U1W7c\":[\"Très détendu\"],\"7Y1YQj\":[\"Nom réel :\"],\"7YHArF\":[\"— ouvrir dans le visualiseur\"],\"7fjnVl\":[\"Rechercher des utilisateurs...\"],\"7jL88x\":[\"Supprimer ce message ? Cette action est irréversible.\"],\"7nGhhM\":[\"À quoi pensez-vous ?\"],\"7sEpu1\":[\"Membres — \",[\"0\"]],\"7sNhEz\":[\"Nom d'utilisateur\"],\"8H0Q+x\":[\"En savoir plus sur les profils →\"],\"8Phu0A\":[\"Afficher quand des utilisateurs changent de pseudo\"],\"8XTG9e\":[\"Saisir le mot de passe oper\"],\"8XsV2J\":[\"Réessayer l'envoi\"],\"8ZsakT\":[\"Mot de passe\"],\"8kR84m\":[\"Vous êtes sur le point d'ouvrir un lien externe :\"],\"8lCgih\":[\"Supprimer la règle\"],\"8o3dPc\":[\"Déposez les fichiers pour les téléverser\"],\"8p/xVT\":[[\"0\",\"plural\",{\"one\":[[\"1\"]],\"other\":[[\"2\"]]}]],\"8wRzac\":[[\"joinCount\",\"plural\",{\"one\":[\"a rejoint\"],\"other\":[\"a rejoint \",[\"joinCount\"],\" fois\"]}]],\"9BMLnJ\":[\"Se reconnecter au serveur\"],\"9OEgyT\":[\"Ajouter une réaction\"],\"9PQ8m2\":[\"G-Line (bannissement global)\"],\"9Qs99X\":[\"E-mail :\"],\"9QupBP\":[\"Supprimer le motif\"],\"9W7tl5\":[\"(inchangé)\"],\"9bG48P\":[\"Envoi en cours\"],\"9f5f0u\":[\"Questions sur la confidentialité ? Contactez-nous :\"],\"9iweoP\":[\"Réseaux sur \",[\"0\"]],\"9unqs3\":[\"Absent :\"],\"9v3hwv\":[\"Aucun serveur trouvé.\"],\"9zb2WA\":[\"Connexion en cours\"],\"A1taO8\":[\"Rechercher\"],\"A2adVi\":[\"Envoyer des notifications de frappe\"],\"A9Rhec\":[\"Nom du salon\"],\"AWOSPo\":[\"Zoomer\"],\"AXSpEQ\":[\"Oper à la connexion\"],\"AeXO77\":[\"Compte\"],\"AhNP40\":[\"Avancer\"],\"Ai2U7L\":[\"Hôte\"],\"AjBQnf\":[\"Pseudo modifié\"],\"AmXVh6\":[\"https://example.com/avatar.png\"],\"AnRu/j\":[\"Annuler la réponse\"],\"ApSx0O\":[[\"0\"],\" messages trouvés correspondant à \\\"\",[\"searchQuery\"],\"\\\"\"],\"AxPAXW\":[\"Aucun résultat trouvé\"],\"AyNqAB\":[\"Afficher tous les événements serveur dans le chat\"],\"B/QqGw\":[\"Absent du clavier\"],\"B0sB2k\":[\"Texte en clair\"],\"B8AaMI\":[\"Ce champ est obligatoire\"],\"BA2c49\":[\"Le serveur ne supporte pas le filtrage LIST avancé\"],\"BDKt3I\":[[\"0\"],\", \",[\"1\"],\", \",[\"2\"],\" et \",[\"3\"],\" autres sont en train d'écrire...\"],\"BGul2A\":[\"Vous avez des modifications non enregistrées. Voulez-vous vraiment fermer sans enregistrer ?\"],\"BIf9fi\":[\"Votre message de statut\"],\"BPm98R\":[\"Aucun serveur sélectionné. Choisissez d'abord un serveur dans la barre latérale ; les liens d'invitation sont gérés par serveur.\"],\"BZz3md\":[\"Votre site web personnel\"],\"Bgm/H7\":[\"Permettre la saisie sur plusieurs lignes\"],\"BiQIl1\":[\"Épingler cette conversation privée\"],\"BlNZZ2\":[\"Cliquez pour aller au message\"],\"Bowq3c\":[\"Seuls les opérateurs peuvent modifier le sujet\"],\"Btozzp\":[\"Cette image a expiré\"],\"Bycfjm\":[\"Total : \",[\"0\"]],\"C6IBQc\":[\"Copier le JSON complet\"],\"C9L9wL\":[\"Collecte de données\"],\"CDq4wC\":[\"Modérer l'utilisateur\"],\"CHVRxG\":[\"Message à @\",[\"0\"],\" (Maj+Entrée pour nouvelle ligne)\"],\"CN9zdR\":[\"Le nom oper et le mot de passe sont requis\"],\"CW3sYa\":[\"Ajouter la réaction \",[\"emoji\"]],\"CaAkqd\":[\"Afficher les déconnexions\"],\"CbvaYj\":[\"Bannir par pseudo\"],\"CcK+Ft\":[\"PDF\"],\"Ce8q3L\":[\"Sélectionner un canal\"],\"CsekCi\":[\"Normal\"],\"D+NlUC\":[\"Système\"],\"D28t6+\":[\"a rejoint et quitté\"],\"DB8zMK\":[\"Appliquer\"],\"DBcWHr\":[\"Fichier son de notification personnalisé\"],\"DTy9Xw\":[\"Aperçus des médias\"],\"Dj4pSr\":[\"Choisissez un mot de passe sécurisé\"],\"Du+zn+\":[\"Recherche...\"],\"Du2T2f\":[\"Paramètre introuvable\"],\"DwsSVQ\":[\"Appliquer les filtres & Actualiser\"],\"E3W/zd\":[\"Pseudo par défaut\"],\"E6nRW7\":[\"Copier l'URL\"],\"E703RG\":[\"Modes :\"],\"EAeu1Z\":[\"Envoyer l'invitation\"],\"EFKJQT\":[\"Paramètre\"],\"EGPQBv\":[\"Règles de flood personnalisées (+f)\"],\"ELik0r\":[\"Voir la politique de confidentialité complète\"],\"EPbeC2\":[\"Voir ou modifier le sujet du canal\"],\"EQCDNT\":[\"Entrez le nom d'utilisateur oper...\"],\"EUvulZ\":[\"1 message trouvé correspondant à \\\"\",[\"searchQuery\"],\"\\\"\"],\"EatZYJ\":[\"Image suivante\"],\"EdQY6l\":[\"Aucun\"],\"EnqLYU\":[\"Rechercher des serveurs...\"],\"F0OKMc\":[\"Modifier le serveur\"],\"F6Int2\":[\"Activer les surlignages\"],\"FDoLyE\":[\"Utilisateurs max.\"],\"FUU/hZ\":[\"Contrôle la quantité de médias externes chargés dans le chat.\"],\"Fdp03t\":[\"activé\"],\"FfPWR0\":[\"Modal\"],\"FjkaiT\":[\"Dézoomer\"],\"FlqOE9\":[\"Ce que cela signifie :\"],\"FolHNl\":[\"Gérez votre compte et l'authentification\"],\"Fp2Dif\":[\"A quitté le serveur\"],\"G5KmCc\":[\"GZ-Line (Z-Line globale)\"],\"GDs0lz\":[\"<0>Risque :</0> Des informations sensibles (messages, conversations privées, identifiants de connexion) pourraient être exposées aux administrateurs réseau ou à des attaquants positionnés entre les serveurs IRC.\"],\"GR+2I3\":[\"Ajouter un masque d'invitation (ex. nick!*@*, *!*@host.com)\"],\"GRLyMU\":[\"Fermer les notifications serveur détachées\"],\"GdhD7H\":[\"Cliquez Ã\xA0 nouveau pour confirmer\"],\"GlHnXw\":[\"Échec du changement de pseudo: \",[\"error\"],\" \",[\"0\"]],\"GswZF3\":[\"Aperçu :\"],\"GtmO8/\":[\"de\"],\"GtuHUQ\":[\"Renommer ce salon sur le serveur. Tous les utilisateurs verront le nouveau nom.\"],\"GuGfFX\":[\"Activer/désactiver la recherche\"],\"GxkJXS\":[\"Téléversement...\"],\"GzbwnK\":[\"A rejoint le canal\"],\"GzsUDB\":[\"Profil étendu\"],\"H/PnT8\":[\"Insérer un emoji\"],\"H6Izzl\":[\"Votre code couleur préféré\"],\"H9jIv+\":[\"Afficher les entrées/sorties\"],\"HAKBY9\":[\"Télécharger des fichiers\"],\"HdE1If\":[\"Canal\"],\"Hk4AW9\":[\"Votre nom d'affichage préféré\"],\"HmHDk7\":[\"Sélectionner un membre\"],\"HrQzPU\":[\"Canaux sur \",[\"networkName\"]],\"I2tXQ5\":[\"Message à @\",[\"0\"],\" (Entrée pour nouvelle ligne, Maj+Entrée pour envoyer)\"],\"I6bw/h\":[\"Bannir l'utilisateur\"],\"I92Z+b\":[\"Activer les notifications\"],\"I9D72S\":[\"Êtes-vous sûr de vouloir supprimer ce message ? Cette action est irréversible.\"],\"IA+1wo\":[\"Afficher quand des utilisateurs sont expulsés des salons\"],\"IDwkJx\":[\"IRC Operator\"],\"ILlU+s\":[\"Info :\"],\"IUwGEM\":[\"Enregistrer les modifications\"],\"IVeGK6\":[[\"0\"],\", \",[\"1\"],\" et \",[\"2\"],\" sont en train d'écrire...\"],\"IgrLD/\":[\"Pause\"],\"Im6JED\":[\"WHISPER\"],\"ImOQa9\":[\"Répondre\"],\"IoHMnl\":[\"La valeur maximale est \",[\"0\"]],\"IvMj+0\":[\"Op\"],\"J28zul\":[\"Connexion en cours...\"],\"J5T9NW\":[\"Informations utilisateur\"],\"J8Y5+z\":[\"Oups ! La réseau s'est divisé ! ⚠️\"],\"JBHkBA\":[\"A quitté le canal\"],\"JCwL0Q\":[\"Saisir une raison (facultatif)\"],\"JFciKP\":[\"Basculer\"],\"JXGkhG\":[\"Changer le nom du canal (opérateurs uniquement)\"],\"JcD7qf\":[\"Plus d'actions\"],\"JdkA+c\":[\"Secret (+s)\"],\"Jmu12l\":[\"Canaux du serveur\"],\"JvQ++s\":[\"Activer le Markdown\"],\"K2jwh/\":[\"Aucune donnée WHOIS disponible\"],\"KAXSwC\":[\"Voice\"],\"KDfTdX\":[\"Supprimer le message\"],\"KKBlUU\":[\"Intégrer\"],\"KM0pLb\":[\"Bienvenue dans le canal !\"],\"KR6W2h\":[\"Ne plus ignorer l'utilisateur\"],\"KV+Bi1\":[\"Sur invitation uniquement (+i)\"],\"KdCtwE\":[\"Nombre de secondes de surveillance de l'activité de flood avant la réinitialisation des compteurs\"],\"Kkezga\":[\"Mot de passe du serveur\"],\"KsiQ/8\":[\"Les utilisateurs doivent être invités pour rejoindre le salon\"],\"L+gB/D\":[\"Informations sur le salon\"],\"LC1a7n\":[\"Le serveur IRC a signalé que ses liens entre serveurs ont un faible niveau de sécurité. Cela signifie que lorsque vos messages sont relayés entre les serveurs IRC du réseau, ils peuvent ne pas être correctement chiffrés ou les certificats SSL/TLS peuvent ne pas être validés correctement.\"],\"LNfLR5\":[\"Afficher les expulsions\"],\"LP+1Z7\":[\"Ajouter un réseau\"],\"LQb0W/\":[\"Afficher tous les événements\"],\"LU7/yA\":[\"Nom alternatif pour l'affichage. Peut contenir des espaces, emojis et caractères spéciaux. Le vrai nom (\",[\"channelName\"],\") sera toujours utilisé pour les commandes IRC.\"],\"LUb9O7\":[\"Un port de serveur valide est requis\"],\"LV4fT6\":[\"Description (optionnelle, ex. « Bêta-testeurs T3 »)\"],\"Lb+BUl\":[\"https://example.com/avatar.jpg\"],\"LcET2C\":[\"Politique de confidentialité\"],\"LcuSDR\":[\"Gérez les informations de votre profil et vos métadonnées\"],\"LqLS9B\":[\"Afficher les changements de pseudo\"],\"LsDQt2\":[\"Paramètres du canal\"],\"LtI9AS\":[\"Propriétaire\"],\"LuNhhL\":[\"a réagi à ce message\"],\"M/AZNG\":[\"URL de votre image d'avatar\"],\"M/WIer\":[\"Envoyer un message\"],\"M8er/5\":[\"Nom :\"],\"MHk+7g\":[\"Image précédente\"],\"MRorGe\":[\"MP à l'utilisateur\"],\"MVbSGP\":[\"Fenêtre temporelle (secondes)\"],\"MkpcsT\":[\"Vos messages et paramètres sont stockés localement sur votre appareil\"],\"MzPdC2\":[\"Mot de passe du serveur (PASS)\"],\"N/hDSy\":[\"Marquer comme bot, généralement 'on' ou vide\"],\"N6j2JH\":[\"Modifier \",[\"0\"]],\"N7TQbE\":[\"Inviter un utilisateur dans \",[\"channelName\"]],\"NCca/o\":[\"Entrez le pseudo par défaut...\"],\"Nqs6B9\":[\"Affiche tous les médias externes. Toute URL peut déclencher une requête vers un serveur inconnu.\"],\"Nt+9O7\":[\"Utiliser WebSocket au lieu de TCP brut\"],\"NxIHzc\":[\"Expulser l'utilisateur\"],\"O+v/cL\":[\"Parcourir tous les canaux du serveur\"],\"OCGpR4\":[\"(hériter)\"],\"ODwSCk\":[\"Envoyer un GIF\"],\"OGQ5kK\":[\"Configurer les sons de notification et les mises en évidence\"],\"OIPt1Z\":[\"Afficher ou masquer la barre latérale de la liste des membres\"],\"OKSNq/\":[\"Très strict\"],\"ONWvwQ\":[\"Téléverser\"],\"OVKoQO\":[\"Votre mot de passe de compte pour l'authentification\"],\"ObsidianIRC - Bringing IRC to the future\":[\"ObsidianIRC - Amener IRC vers le futur\"],\"OhCpra\":[\"Définir un sujet…\"],\"OkltoQ\":[\"Bannir \",[\"username\"],\" par pseudo (l'empêche de rejoindre avec le même pseudo)\"],\"P+t/Te\":[\"Aucune donnée supplémentaire\"],\"P42Wcc\":[\"Sécurisé\"],\"PD38l0\":[\"Aperçu de l'avatar du canal\"],\"PD9mEt\":[\"Saisir un message...\"],\"PPqfdA\":[\"Ouvrir les paramètres de configuration du canal\"],\"PSCjfZ\":[\"Le sujet affiché pour ce salon. Tous les utilisateurs peuvent le voir.\"],\"PZCecv\":[\"Aperçu PDF\"],\"PeLgsC\":[[\"c\",\"plural\",{\"one\":[\"1 fois\"],\"other\":[[\"c\"],\" fois\"]}]],\"PguS2C\":[\"Ajouter un masque d'exception (ex. nick!*@*, *!*@host.com)\"],\"Pil5Ty\":[\"Affichage de \",[\"displayedChannelsCount\"],\" sur \",[\"0\"],\" canaux\"],\"PqhVlJ\":[\"Bannir l'utilisateur (par hostmask)\"],\"Q+chwU\":[\"Nom d'utilisateur :\"],\"Q2QY4/\":[\"Supprimer cette invitation\"],\"Q3v9Wc\":[\"Oui, supprimer\"],\"Q6hhn8\":[\"Préférences\"],\"QF4a34\":[\"Veuillez saisir un nom d'utilisateur\"],\"QGqSZ2\":[\"Couleur et mise en forme\"],\"QJQd1J\":[\"Modifier le profil\"],\"QSzGDE\":[\"Inactif\"],\"QUlny5\":[\"Bienvenue sur \",[\"0\"],\" !\"],\"Qoq+GP\":[\"Lire la suite\"],\"QuSkCF\":[\"Filtrer les canaux...\"],\"QwUrDZ\":[\"a changé le sujet en : \",[\"topic\"]],\"R0UH07\":[\"Image \",[\"0\"],\" sur \",[\"1\"]],\"R7SsBE\":[\"Couper le son\"],\"R8rf1X\":[\"Cliquez pour définir le sujet\"],\"RArB3D\":[\"a été expulsé de \",[\"channelName\"],\" par \",[\"username\"]],\"RI3cWd\":[\"Découvrez le monde de l'IRC avec ObsidianIRC\"],\"RIfHS5\":[\"Créer un nouveau lien d'invitation\"],\"RMMaN5\":[\"Modéré (+m)\"],\"RWw9Lg\":[\"Fermer la fenêtre\"],\"RZ2BuZ\":[\"L'enregistrement du compte \",[\"account\"],\" nécessite une vérification : \",[\"message\"]],\"RySp6q\":[\"Masquer les commentaires\"],\"S5Togi\":[\"Chargement des réseaux de votre bouncer…\"],\"SPKQTd\":[\"Le pseudo est requis\"],\"SPVjfj\":[\"Par défaut « aucune raison » si laissé vide\"],\"SQKPvQ\":[\"Inviter un utilisateur\"],\"STmlpb\":[\"Retour à la liste des réseaux\"],\"SkZcl+\":[\"Choisissez un profil de protection contre le flood prédéfini. Ces profils offrent des paramètres de protection équilibrés pour différents cas d'usage.\"],\"Slr+3C\":[\"Utilisateurs min.\"],\"Spnlre\":[\"Vous avez invité \",[\"target\"],\" à rejoindre \",[\"channel\"]],\"T/ckN5\":[\"Ouvrir dans le visualiseur\"],\"T91vKp\":[\"Lire\"],\"TV2Wdu\":[\"Découvrez comment nous gérons vos données et protégeons votre vie privée.\"],\"TgFpwD\":[\"Application en cours...\"],\"TkzSFB\":[\"Aucune modification\"],\"TtserG\":[\"Saisir le vrai nom\"],\"Ttz9J1\":[\"Entrez le mot de passe...\"],\"Tz0i8g\":[\"Paramètres\"],\"U3pytU\":[\"Admin\"],\"UDb2YD\":[\"Réagir\"],\"UE4KO5\":[\"*channel*\"],\"UETAwW\":[\"Vous n'avez encore créé aucun lien d'invitation. Utilisez le formulaire ci-dessus pour créer le premier.\"],\"UGT5vp\":[\"Enregistrer les paramètres\"],\"UV5hLB\":[\"Aucun bannissement trouvé\"],\"Uaj3Nd\":[\"Messages de statut\"],\"Ue3uny\":[\"Par défaut (aucun profil)\"],\"UkARhe\":[\"Normal – Protection standard\"],\"Umn7Cj\":[\"Pas encore de commentaires. Soyez le premier !\"],\"UtUIRh\":[[\"0\"],\" anciens messages\"],\"UwzP+U\":[\"Connexion sécurisée\"],\"V0/A4O\":[\"Propriétaire du canal\"],\"V4qgxE\":[\"Créé avant (min)\"],\"V8yTm6\":[\"Effacer la recherche\"],\"VJMMyz\":[\"ObsidianIRC - L'IRC vers le futur\"],\"VJScHU\":[\"Raison\"],\"VLsmVV\":[\"Couper les notifications\"],\"VbyRUy\":[\"Commentaires\"],\"Vmx0mQ\":[\"Défini par :\"],\"VqnIZz\":[\"Consulter notre politique de confidentialité et nos pratiques en matière de données\"],\"VrMygG\":[\"La longueur minimale est \",[\"0\"]],\"VrnTui\":[\"Vos pronoms, affichés dans votre profil\"],\"W8E3qn\":[\"Compte authentifié\"],\"WAakm9\":[\"Supprimer le canal\"],\"WFxTHC\":[\"Ajouter un masque de bannissement (ex. nick!*@*, *!*@host.com)\"],\"WN1g9F\":[\"L'hôte du serveur est requis\"],\"WRYdXW\":[\"Position audio\"],\"WUOH5B\":[\"Ignorer l'utilisateur\"],\"WWEXnZ\":[[\"0\",\"plural\",{\"one\":[\"Afficher 1 élément de plus\"],\"other\":[\"Afficher \",[\"1\"],\" éléments de plus\"]}]],\"WYxRzo\":[\"Créer et gérer vos liens d'invitation\"],\"Wd38W1\":[\"Laissez le canal vide pour une invitation générique au réseau. La description sert uniquement Ã\xA0 vos notes â visible uniquement par vous dans cette liste.\"],\"Weq9zb\":[\"Général\"],\"Wfj7Sk\":[\"Activer ou désactiver les sons de notification\"],\"Wm7gbG\":[\"GitHub :\"],\"WyeHWY\":[\"*spam*\"],\"WzMCru\":[\"Profil de l'utilisateur\"],\"X6S3lt\":[\"Rechercher des paramètres, canaux, serveurs...\"],\"XEHan5\":[\"Continuer quand même\"],\"XI1+wb\":[\"Format invalide\"],\"XIXeuC\":[\"Message à @\",[\"0\"]],\"XMS+k4\":[\"Démarrer un message privé\"],\"XWgxXq\":[\"Album\"],\"Xd7+IT\":[\"Désépingler la conversation privée\"],\"Xm/s+u\":[\"Affichage\"],\"Xp2n93\":[\"Affiche les médias provenant de l'hébergeur de fichiers de confiance de votre serveur. Aucune requête n'est envoyée à des services externes.\"],\"XvjC4F\":[\"Enregistrement...\"],\"Y/qryO\":[\"Aucun utilisateur ne correspond à votre recherche\"],\"YAqRpI\":[\"Enregistrement du compte réussi pour \",[\"account\"],\" : \",[\"message\"]],\"YEfzvP\":[\"Sujet protégé (+t)\"],\"YQOn6a\":[\"Réduire la liste des membres\"],\"YRCoE9\":[\"Opérateur du canal\"],\"YURQaF\":[\"Voir le profil\"],\"YdBSvr\":[\"Contrôler l'affichage des médias et du contenu externe\"],\"Yj6U3V\":[\"Pas de serveur central :\"],\"YjvpGx\":[\"Pronoms\"],\"YqH4l4\":[\"Aucune clé\"],\"YyUPpV\":[\"Compte :\"],\"ZJSWfw\":[\"Message affiché lors de la déconnexion du serveur\"],\"ZR1dJ4\":[\"Invitations\"],\"ZdWg0V\":[\"Ouvrir dans le navigateur\"],\"ZhRBbl\":[\"Rechercher des messages…\"],\"Zmcu3y\":[\"Filtres avancés\"],\"a2/8e5\":[\"Sujet défini après (min)\"],\"aHKcKc\":[\"Page précédente\"],\"aJTbXX\":[\"Mot de passe Oper\"],\"aQryQv\":[\"Le modèle existe déjà\"],\"aW9pLN\":[\"Nombre maximum d'utilisateurs autorisés. Laissez vide pour aucune limite.\"],\"ah4fmZ\":[\"Affiche également des aperçus de YouTube, Vimeo, SoundCloud et autres services connus.\"],\"aifXak\":[\"Aucun média dans ce salon\"],\"ap2zBz\":[\"Détendu\"],\"az8lvo\":[\"Désactivé\"],\"azXSNo\":[\"Développer la liste des membres\"],\"azdliB\":[\"Se connecter à un compte\"],\"b26wlF\":[\"elle/la\"],\"bD/+Ei\":[\"Strict\"],\"bQ6BJn\":[\"Configurez des règles détaillées de protection contre le flood. Chaque règle précise le type d'activité à surveiller et l'action à prendre lorsque les seuils sont dépassés.\"],\"beV7+y\":[\"L'utilisateur recevra une invitation à rejoindre \",[\"channelName\"],\".\"],\"bk84cH\":[\"Message d'absence\"],\"bkHdLj\":[\"Ajouter un serveur IRC\"],\"bmQLn5\":[\"Ajouter une règle\"],\"bv4cFj\":[\"Transport\"],\"bwRvnp\":[\"Action\"],\"c8+EVZ\":[\"Compte vérifié\"],\"cGYUlD\":[\"Aucun aperçu de média n'est chargé.\"],\"cLF98o\":[\"Afficher les commentaires (\",[\"commentCount\"],\")\"],\"cLKIDO\":[\"Aucun utilisateur disponible\"],\"cSgpoS\":[\"Épingler la conversation privée\"],\"cde3ce\":[\"Message <0>\",[\"0\"],\"</0>\"],\"chQsxg\":[\"Copier la sortie formatée\"],\"cl/A5J\":[\"Bienvenue sur \",[\"__DEFAULT_IRC_SERVER_NAME__\"],\" !\"],\"cnGeoo\":[\"Supprimer\"],\"coPLXT\":[\"Nous ne stockons pas vos communications IRC sur nos serveurs\"],\"crYH/6\":[\"Lecteur SoundCloud\"],\"cv5DQb\":[\"aucun hôte défini\"],\"d3sis4\":[\"Ajouter un serveur\"],\"d9aN5k\":[\"Retirer \",[\"username\"],\" du canal\"],\"dEgA5A\":[\"Annuler\"],\"dGi1We\":[\"Désépingler cette conversation privée\"],\"dJVuyC\":[\"a quitté \",[\"channelName\"],\" (\",[\"reason\"],\")\"],\"dMtLDE\":[\"à\"],\"dXqxlh\":[\"<0>⚠️ Risque de sécurité !</0> Cette connexion peut être vulnérable à l'interception ou aux attaques de type man-in-the-middle.\"],\"da9Q/R\":[\"Modes du canal modifiés\"],\"dhJN3N\":[\"Afficher les commentaires\"],\"dj2xTE\":[\"Ignorer la notification\"],\"dpCzmC\":[\"Paramètres de protection contre le flood\"],\"e9dQpT\":[\"Voulez-vous ouvrir ce lien dans un nouvel onglet ?\"],\"ePK91l\":[\"Modifier\"],\"eYBDuB\":[\"Téléverser une image ou fournir une URL avec substitution optionnelle \",[\"size\"]],\"edBbee\":[\"Bannir \",[\"username\"],\" par hostmask (l'empêche de rejoindre depuis la même adresse IP/hôte)\"],\"ekfzWq\":[\"Paramètres utilisateur\"],\"elPDWs\":[\"Personnalisez votre expérience du client IRC\"],\"eu2osY\":[\"<0>💡 Recommandation :</0> Ne continuez que si vous faites confiance à ce serveur et que vous comprenez les risques. Évitez de partager des informations sensibles ou des mots de passe via cette connexion.\"],\"euEhbr\":[\"Cliquez pour rejoindre \",[\"channel\"]],\"ez3vLd\":[\"Activer la saisie multiligne\"],\"f0J5Ki\":[\"Les communications entre serveurs peuvent utiliser des connexions non chiffrées\"],\"f9BHJk\":[\"Avertir l'utilisateur\"],\"fDOLLd\":[\"Aucun canal trouvé.\"],\"ffzDkB\":[\"Analyses anonymes :\"],\"fq1GF9\":[\"Afficher quand des utilisateurs se déconnectent du serveur\"],\"gEF57C\":[\"Ce serveur ne prend en charge qu'un seul type de connexion\"],\"gJuLUI\":[\"Liste d'ignorés\"],\"gNzMrk\":[\"Avatar actuel\"],\"gjPWyO\":[\"Entrez votre pseudo...\"],\"gz6UQ3\":[\"Agrandir\"],\"h6/IMX\":[\"Ajoutez votre premier réseau\"],\"h6razj\":[\"Exclure le masque de nom de salon\"],\"hG6jnw\":[\"Aucun sujet défini\"],\"hG89Ed\":[\"Image\"],\"hYgDIe\":[\"Créer\"],\"hZ6znB\":[\"Port\"],\"ha+Bz5\":[\"ex. : 100:1440\"],\"he3ygx\":[\"Copier\"],\"hehnjM\":[\"Quantité\"],\"hzdLuQ\":[\"Seuls les utilisateurs avec voice ou plus peuvent parler\"],\"i0qMbr\":[\"Accueil\"],\"iDNBZe\":[\"Notifications\"],\"iH8pgl\":[\"Retour\"],\"iL9SZg\":[\"Bannir l'utilisateur (par pseudo)\"],\"iNt+3c\":[\"Retour à l'image\"],\"iQvi+a\":[\"Ne plus m'avertir de la faible sécurité des liens pour ce serveur\"],\"iSLIjg\":[\"Connecter\"],\"iWXkHH\":[\"Halfop\"],\"iZeTtp\":[\"Hôte du serveur\"],\"idD8Ev\":[\"Enregistré\"],\"iivqkW\":[\"Connecté depuis\"],\"ij+Elv\":[\"Aperçu de l'image\"],\"ilIWp7\":[\"Activer/désactiver les notifications\"],\"iuaqvB\":[\"Utilisez * comme joker. Exemples : baduser!*@*, *!*@spammer.com, troll*!*@*\"],\"ixkTse\":[\"Bot\"],\"j2DGR0\":[\"Bannir par masque d'hôte\"],\"jA4uoI\":[\"Sujet :\"],\"jLXxGK\":[\"https://example.com\"],\"jPSk57\":[\"Raison (facultatif)\"],\"jUV7CU\":[\"Téléverser un avatar\"],\"jW5Uwh\":[\"Contrôle la quantité de médias externes chargés. Désactivé / Sûr / Sources fiables / Tout le contenu.\"],\"jXzms5\":[\"Options de pièce jointe\"],\"jZlrte\":[\"Couleur\"],\"jfC/xh\":[\"Contact\"],\"jywMpv\":[\"#new-channel-name\"],\"k112DD\":[\"Charger les anciens messages\"],\"k3ID0F\":[\"Filtrer les membres…\"],\"k65gsE\":[\"Analyse approfondie\"],\"k7Zgob\":[\"Annuler la connexion\"],\"kAVx5h\":[\"Aucune invitation trouvée\"],\"kCLEPU\":[\"Connecté à\"],\"kF5LKb\":[\"Modèles ignorés :\"],\"kGeOx/\":[\"Rejoindre \",[\"0\"]],\"kITKr8\":[\"Chargement des modes du salon...\"],\"kPpPsw\":[\"Vous êtes un IRC Operator\"],\"kWJmRL\":[\"Vous\"],\"kfcRb0\":[\"Avatar\"],\"kjMqSj\":[\"Copier JSON\"],\"krViRy\":[\"Cliquer pour copier en JSON\"],\"ks71ra\":[\"Exceptions\"],\"kw4lRv\":[\"Semi-opérateur du canal\"],\"kxgIRq\":[\"Sélectionnez ou ajoutez un canal pour commencer.\"],\"ky6dWe\":[\"Aperçu de l'avatar\"],\"l+GxCv\":[\"Chargement des canaux...\"],\"l+IUVW\":[\"Vérification du compte réussie pour \",[\"account\"],\" : \",[\"message\"]],\"l/siQz\":[[\"reconnectCount\",\"plural\",{\"one\":[\"s'est reconnecté\"],\"other\":[\"s'est reconnecté \",[\"reconnectCount\"],\" fois\"]}]],\"l1l8sj\":[\"il y a \",[\"0\"],\" j\"],\"l5NhnV\":[\"#canal (optionnel)\"],\"l5jmzx\":[[\"0\"],\" et \",[\"1\"],\" sont en train d'écrire...\"],\"lCF0wC\":[\"Actualiser\"],\"lHy8N5\":[\"Chargement de canaux supplémentaires...\"],\"lasgrr\":[\"utilisé\"],\"lbpf14\":[\"Rejoindre \",[\"value\"]],\"lfFsZ4\":[\"Canaux\"],\"lkNdiH\":[\"Nom de compte\"],\"ln500L\":[\"ObsidianIRC\"],\"lnCMdg\":[\"Téléverser une image\"],\"loQxaJ\":[\"Je suis de retour\"],\"lvfaxv\":[\"ACCUEIL\"],\"m0oxpP\":[\"Libera Chat\"],\"m16xKo\":[\"Ajouter\"],\"m8flAk\":[\"Aperçu (pas encore envoyé)\"],\"mEPxTp\":[\"<0>⚠️ Attention !</0> N'ouvrez que des liens provenant de sources fiables. Des liens malveillants peuvent compromettre votre sécurité ou votre vie privée.\"],\"mHGdhG\":[\"Informations sur le serveur\"],\"mHS8lb\":[\"Message dans #\",[\"0\"]],\"mIibu3\":[\"Pour les endpoints WebSocket non standards (par exemple soju derrière un reverse proxy), entrez l'URL complète incluant le chemin : <0>wss://host:port/socket</0>.\"],\"mMYBD9\":[\"Large – Portée de protection étendue\"],\"mTGsPd\":[\"Sujet du salon\"],\"mU8j6O\":[\"Pas de messages externes (+n)\"],\"mZp8FL\":[\"Retour automatique à une seule ligne\"],\"mdQu8G\":[\"VotrePseudo\"],\"miSSBQ\":[\"Commentaires (\",[\"commentCount\"],\")\"],\"mvyLSy\":[\"L'utilisateur est authentifié\"],\"mwtcGl\":[\"Fermer les commentaires\"],\"myL0MR\":[\"Supprimer ce réseau ?\"],\"mzI/c+\":[\"Télécharger\"],\"n3fGRk\":[\"défini par \",[\"0\"]],\"nE9jsU\":[\"Détendu – Protection moins agressive\"],\"nNflMD\":[\"Quitter le canal\"],\"nPXkBi\":[\"Chargement des données WHOIS...\"],\"nQnxxF\":[\"Message dans #\",[\"0\"],\" (Maj+Entrée pour nouvelle ligne)\"],\"nWMRxa\":[\"Désépingler\"],\"nkC032\":[\"Aucun profil anti-flood\"],\"o69z4d\":[\"Envoyer un message d'avertissement à \",[\"username\"]],\"o9ylQi\":[\"Recherchez des GIFs pour commencer\"],\"oFGkER\":[\"Avis du serveur\"],\"oOi11l\":[\"Défiler vers le bas\"],\"oPYIL5\":[\"réseau\"],\"oQEzQR\":[\"Nouveau message privé\"],\"oXOSPE\":[\"En ligne\"],\"oal760\":[\"Des attaques man-in-the-middle sur les liens serveur sont possibles\"],\"oeqmmJ\":[\"Sources de confiance\"],\"optX0N\":[\"il y a \",[\"0\"],\" h\"],\"ovBPCi\":[\"Par défaut\"],\"p0Z69r\":[\"Le modèle ne peut pas être vide\"],\"p1KgtK\":[\"Échec du chargement audio\"],\"p59pEv\":[\"Détails supplémentaires\"],\"p7sRI6\":[\"Informer les autres que vous écrivez\"],\"pBm1od\":[\"Canal secret\"],\"pNmiXx\":[\"Votre pseudo par défaut pour tous les serveurs\"],\"pUUo9G\":[\"Nom d'hôte :\"],\"pVGPmz\":[\"Mot de passe du compte\"],\"peNE68\":[\"Permanent\"],\"plhHQt\":[\"Aucune donnée\"],\"pm6+q5\":[\"Avertissement de sécurité\"],\"pn5qSs\":[\"Informations supplémentaires\"],\"q0cR4S\":[\"est maintenant connu sous le nom de **\",[\"newNick\"],\"**\"],\"qFcunY\":[\"Le salon n'apparaîtra pas dans les commandes LIST ou NAMES\"],\"qLpTm/\":[\"Supprimer la réaction \",[\"emoji\"]],\"qVkGWK\":[\"Épingler\"],\"qY8wNa\":[\"Page d'accueil\"],\"qb0xJ7\":[\"Jokers : * correspond à toute séquence, ? à un seul caractère. Exemples : nick!*@*, *!*@host.com, *!*user@*\"],\"qhzpRq\":[\"Clé du salon (+k)\"],\"qtoOYG\":[\"Aucune limite\"],\"r1W2AS\":[\"Image hébergée\"],\"rIPR2O\":[\"Sujet défini avant (min)\"],\"rMMSYo\":[\"La longueur maximale est \",[\"0\"]],\"rWtzQe\":[\"Le réseau s'est divisé et reconnecté. ✅\"],\"rYG2u6\":[\"Veuillez patienter...\"],\"rdUucN\":[\"Aperçu\"],\"rjGI/Q\":[\"Confidentialité\"],\"rk8iDX\":[\"Chargement des GIFs...\"],\"rn6SBY\":[\"Rétablir le son\"],\"s/UKqq\":[\"A été expulsé du canal\"],\"s8cATI\":[\"a rejoint \",[\"channelName\"]],\"sCO9ue\":[\"La connexion à <0>\",[\"serverName\"],\"</0> présente les problèmes de sécurité suivants :\"],\"sGH11W\":[\"Serveur\"],\"sHI1H+\":[\"est maintenant connu sous le nom de **\",[\"newNick\"],\"**\"],\"sJyV04\":[[\"inviter\"],\" vous a invité à rejoindre \",[\"channel\"]],\"sUBSbK\":[\"Aucun réseau en amont pour le moment.\"],\"sby+1/\":[\"Cliquer pour copier\"],\"sfN25C\":[\"Votre nom réel ou complet\"],\"sliuzR\":[\"Ouvrir le lien\"],\"sqrO9R\":[\"Mentions personnalisées\"],\"sr6RdJ\":[\"Multiligne avec Shift+Entrée\"],\"swrCpB\":[\"Le canal a été renommé de \",[\"oldName\"],\" en \",[\"newName\"],\" par \",[\"user\"],[\"0\"]],\"sxkWRg\":[\"Avancé\"],\"t/YqKh\":[\"Supprimer\"],\"t47eHD\":[\"Votre identifiant unique sur ce serveur\"],\"tAkAh0\":[\"URL avec substitution optionnelle \",[\"size\"],\". Exemple : https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"tXLJS3\":[\"Afficher ou masquer la barre latérale de la liste des canaux\"],\"tfDRzk\":[\"Enregistrer\"],\"tiBsJk\":[\"a quitté \",[\"channelName\"]],\"tt4/UD\":[\"a quitté (\",[\"reason\"],\")\"],\"u0TcnO\":[\"Le pseudo {nick} est déjà utilisé, nouvel essai avec {newNick}\"],\"u0a8B4\":[\"S'authentifier en tant qu'opérateur IRC pour l'accès administratif\"],\"u0rWFU\":[\"Créé après (min)\"],\"u72w3t\":[\"Utilisateurs et modèles à ignorer\"],\"u7jc2L\":[\"a quitté\"],\"uAQUqI\":[\"Statut\"],\"uB85T3\":[\"Échec de l'enregistrement : \",[\"msg\"]],\"uV3DOL\":[\"G-Line\"],\"uW3lLI\":[\"Serveurs IRC :\"],\"ukyW4o\":[\"Vos liens d'invitation\"],\"usSSr/\":[\"Niveau de zoom\"],\"v7uvcf\":[\"Logiciel :\"],\"vE8kb+\":[\"Shift+Entrée pour les nouvelles lignes (Entrée envoie)\"],\"vERlcd\":[\"Profil\"],\"vK0RL8\":[\"Pas de sujet\"],\"vSJd18\":[\"Vidéo\"],\"vXIe7J\":[\"Langue\"],\"vaHYxN\":[\"Vrai nom\"],\"vhjbKr\":[\"Absent\"],\"w/nogd\":[[\"0\"],\" réseau\",[\"1\"],\" — choisissez-en un à rejoindre\"],\"w4NYox\":[\"client \",[\"title\"]],\"w8xQRx\":[\"Valeur invalide\"],\"wFjjxZ\":[\"a été expulsé de \",[\"channelName\"],\" par \",[\"username\"],\" (\",[\"reason\"],\")\"],\"wGjaGl\":[\"Aucune exception de bannissement trouvée\"],\"wPrGnM\":[\"Administrateur du canal\"],\"wRkP2d\":[\"GIF\"],\"wbm86v\":[\"Afficher quand des utilisateurs rejoignent ou quittent des salons\"],\"whqZ9r\":[\"Mots ou phrases supplémentaires à surligner\"],\"wm7RV4\":[\"Son de notification\"],\"wz/Yoq\":[\"Vos messages pourraient être interceptés lors du relais entre serveurs\"],\"x3+y8b\":[\"Nombre de personnes inscrites via ce lien\"],\"xCJdfg\":[\"Effacer\"],\"xOTzt5\":[\"Ã\xA0 l'instant\"],\"xUHRTR\":[\"S'authentifier automatiquement comme opérateur à la connexion\"],\"xWHwwQ\":[\"Bannissements\"],\"xYilR2\":[\"Médias\"],\"xbi8D6\":[\"Ce serveur ne prend pas en charge les liens d'invitation (la capacité<0>obby.world/invitation</0>n'est pas annoncée). Vous pouvez toujours discuter normalement ; ce panneau est destiné aux réseaux propulsés par obbyircd.\"],\"xceQrO\":[\"Seuls les websockets sécurisés sont pris en charge\"],\"xdtXa+\":[\"nom-du-salon\"],\"xfXC7q\":[\"Salons textuels\"],\"xlCYOE\":[\"Chargement des messages...\"],\"xlhswE\":[\"La valeur minimale est \",[\"0\"]],\"xq97Ci\":[\"Ajouter un mot ou une expression...\"],\"xuRqRq\":[\"Limite de clients (+l)\"],\"xwF+7J\":[[\"0\"],\" est en train d'écrire...\"],\"y1eoq1\":[\"Copier le lien\"],\"yJztBY\":[\"Supprimer le réseau\"],\"yNeucF\":[\"Ce serveur ne supporte pas les métadonnées de profil étendues (extension IRCv3 METADATA). Les champs comme l'avatar, le nom d'affichage et le statut ne sont pas disponibles.\"],\"yPlrca\":[\"Avatar du salon\"],\"yQE2r9\":[\"Chargement\"],\"ySU+JY\":[\"votre@email.com\"],\"yTX1Rt\":[\"Nom d'utilisateur opérateur\"],\"yYOzWD\":[\"journaux\"],\"yfx9Re\":[\"Mot de passe opérateur IRC\"],\"ygCKqB\":[\"Arrêter\"],\"ymDxJx\":[\"Nom d'utilisateur opérateur IRC\"],\"yrpRsQ\":[\"Trier par nom\"],\"yz7wBu\":[\"Fermer\"],\"zJw+jA\":[\"définit le mode : \",[\"0\"]],\"zbymaY\":[\"il y a \",[\"0\"],\" min\"],\"zebeLu\":[\"Saisir le nom d'utilisateur oper\"],\"zpr0Bw\":[\"GZ-Line\"]}"); No newline at end of file | |||
There was a problem hiding this comment.
Regenerated French bundle still ships mojibake text.
Line 1 contains multiple corrupted strings (Ã, â�) in the serialized catalog; these will render as broken French in production UI. Please regenerate this bundle from a clean UTF-8 .po source and verify the export step preserves UTF-8 end-to-end.
🤖 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/locales/fr/messages.mjs` at line 1, The exported const messages uses a
serialized JSON string with mojibake (e.g. corrupted sequences like à and â�) —
regenerate the French translation bundle from the original UTF-8 .po source and
replace the JSON.parse(...) payload so all strings are valid UTF-8; ensure the
export step and any tooling (the code that produces the JSON string in messages,
plus the build/save step) write the file with UTF-8 encoding (no CP1252/Latin-1
conversion or BOM) and verify by opening the generated messages string (the
argument to JSON.parse in the exported messages constant) to confirm accented
characters (é, ç, à, ô, etc.) are intact.
…cer's say-so Mupuf's #120 complaint + your follow-up: when a bouncer network shows state=connected on the bouncer side, the client should bind to it automatically -- both incrementally (admin enables a network mid- session) and on cold start (page reload restores the previously active set). New architecture in store/handlers/bouncer.ts ----------------------------------------- autoBindConnectedNetworks(parentId) iterates the bouncer's current networks, filters for attributes.state === "connected", computes the deterministic uuid-v5 childId, and skips any binding whose Server row already exists. The remainder go through bouncerConnectNetwork (idempotent). Triggers: - BOUNCER_NETWORK with `state` present (incremental notify mode) - BATCH_END for a soju.im/bouncer-networks batch (initial dump sweep, also catches notify-mode flushes that buffered multiple updates) connectToSavedServers no longer pre-dispatches saved children ------------------------------------------------------------ The old design seeded child Server rows from localStorage and raced their SASL/BIND against the parent's not-yet-authenticated socket -- exactly the "Authentication required" loop you and mupuf hit on reload. The new flow boots only parents; children come back via the bouncer reducer's auto-bind once the parent has responded to LISTNETWORKS. Saved-child entries in localStorage become a no-op (left in place for any future restore-on-disconnect path, but not acted on here). Tests rewritten to match the new contract: - connectToSavedServers.test.ts: pins parent-only dispatch and explicitly asserts NO setPendingBouncerBind / child connect after a parent's `ready` event. - bouncerAutoBind.test.ts: covers the per-event auto-bind -- state=connected triggers a wire bind, state=disconnected doesn't. A third case for the BATCH_END sweep was attempted but vi.spyOn() interacts poorly with zustand's state-replacing setState across multiple triggers in one test (chain executes correctly per debug logs but the spy stops recording after the first call). The implementation is the same code path -- left to manual verification against a real soju. 882 tests + build green.
…plicate-server explosion) The previous dedup checked state.servers.some(s => s.id === childId) on every call, but the per-event trigger fires once per BOUNCER_NETWORK, and during the initial LISTNETWORKS dump those events arrive back-to- back in the same tick. Each invocation's state snapshot in some flows saw the pre-set version even though bouncerConnectNetwork's set() is synchronous on paper -- result was the same network being seeded multiple times (you reported 12+ duplicate rows for a 3-network bouncer). Switch to a module-scope Set of attempted childIds. Cheap, correct, unaffected by store-snapshot timing. The Set resets on every page load (fresh module), which is the only time we genuinely want auto-bind to re-fire from scratch. Tests still green (the existing cases cover single-event triggers; the multi-event burst was the failing case I had to delete earlier because of the same vi.spyOn / zustand interaction).
…plicate-server bug
…nnections (fix exponential server explosion) Soju advertises soju.im/bouncer-networks on bound child connections too (not just the meta control session) and sends them LISTNETWORKS. Without a guard, every child reduces those replies through the same auto-bind handler -- using the CHILD's serverId as the bouncerServerId, computing fresh uuid-v5 childIds against that, and seeding more "grandchild" Server rows. Each grandchild then becomes a bouncer in state and the cycle repeats. Result was an exponential explosion: 3 networks bound on the parent produced 3 children, then 9 grandchildren, then 27, etc. -- the 50+ duplicate rows ValwareIRC pasted in console logs. Guard: when autoBindConnectedNetworks is called for a serverId, look up the Server row -- if it has `bouncerNetid` set, it's itself a child; return early. Only true bouncer parents drive the auto-bind. Also drops the debug console.warn lines now that the diagnosis is in.
…k' header Three UX polish items on top of the bouncer stack: * Server-list tab badge -- top-right of every soju-bouncer server tile. Shotglass icon (react-icons/gi GiGlassShot) signals "this is on the soju bouncer"; paired with a crown for the control session and a plug for each bound upstream network. Tooltip surfaces the same. Mention dot was moved to the bottom-right corner so it doesn't overlap the new badge. * Edit pencil for a bouncer-bound child no longer opens the generic AddServerModal -- most ServerConfig fields are irrelevant for those rows (host/port/sasl etc. are inherited from the parent). It now selects the parent and switches the BouncerNetworksPanel straight into its inline edit form for the right netid. Plumbed via a ui.pendingBouncerEdit hand-off slot + matching editBouncerNetwork / consumePendingBouncerEdit store actions. * ChatHeader's blank-page CTA now reads "Select a Network" when the selected server is the soju control session (no real channels of its own, the panel is for picking an upstream network). Channels on bound children still get "Select a channel". 3 new strings translated across all 18 non-English locales; 883 tests + build green.
…r-networks CAP ack The shotglass+crown sidebar badge keyed on Server.isBouncerControl, which was previously only set on entries restored from saved storage (seed of the children Server rows in connectToSavedServers). Fresh AddServerModal connections never picked up the flag, so the parent tab stayed unbadged even though everything else worked. When the CAP ack handler sets bouncers[serverId].supported = true, also flip the matching Server row to isBouncerControl = true -- but only when the Server isn't itself a bouncer-bound child (which would mean the same cap is being negotiated on a child connection, and the crown belongs to the parent, not the child).
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/locales/cs/messages.po (1)
59-63:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse ICU pluralization instead of the English
{1}suffix.At Line 63,
{0} sítí{1}can render impossible Czech like2 sítís. This needs a real plural message for Czech counts instead of carrying the English plural suffix through the translation.Suggested translation shape
-msgstr "{0} sítí{1} — vyberte jednu pro připojení" +msgstr "{0, plural, one {{0} síť — vyberte ji pro připojení} few {{0} sítě — vyberte jednu pro připojení} other {{0} sítí — vyberte jednu pro připojení}}"🤖 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/locales/cs/messages.po` around lines 59 - 63, The translated string for msgid "{0} network{1} — pick one to join" in src/locales/cs/messages.po must not reuse the English "{1}" suffix; replace it with ICU pluralization: use the {0} count and an ICU plural expression (e.g. {0, plural, one {...} few {...} other {...}}) in msgstr so Czech singular/few/other forms are correct and remove the {1} placeholder; update the translation for the Czech forms accordingly and ensure the code that formats this message (referenced from BouncerNetworksPanel.tsx) passes the numeric count as placeholder {0}.
♻️ Duplicate comments (1)
src/locales/ja/messages.po (1)
85-99:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftMojibake corruption persists across multiple Japanese translations.
The previously flagged UTF-8 encoding corruption remains unfixed. Many
msgstrentries display garbled byte sequences instead of valid Japanese characters (e.g., line 88:"{0}æ¥å"should be"{0}日前"; line 599:"ããä¸åº¦ã¯ãªãã¯ãã¦ç¢ºèª"should be"もう一度クリックして確認"). This will render broken UI copy across invitations, time-ago labels, and other user-facing strings.Restore valid UTF-8 Japanese translations by replacing corrupted
msgstrvalues with correctly encoded text, and ensure the file is saved and committed as UTF-8 without double-encoding.Also applies to: 132-135, 597-600, 699-702, 745-752, 765-776, 858-873, 933-936, 1334-1374, 1620-1623, 1740-1751, 2112-2116, 2544-2555, 2710-2713, 2908-2935
🤖 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/locales/ja/messages.po` around lines 85 - 99, Multiple Japanese msgstr entries are mojibake-corrupted (e.g., msgid "{0}d ago", "{0}h ago", "{0}m ago" and others); open src/locales/ja/messages.po, locate the affected msgid keys (like "{0}d ago", "{0}h ago", "{0}m ago", "Click again to confirm" equivalents shown in the review) and replace their garbled msgstr values with properly encoded Japanese text (e.g., "{0}日前", "{0}時間前", "{0}分前", "もう一度クリックして確認" etc.), then save and commit the .po file encoded as UTF-8 (ensure no double-encoding or BOM) so the translations render correctly.
🤖 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 `@src/locales/en/messages.po`:
- Around line 1627-1629: The locale catalog src/locales/en/messages.po is out of
sync (see msgid "Network bound through soju bouncer" used in
src/components/layout/ServerList.tsx); regenerate the i18n catalogs by running
the project's extraction/compile commands (e.g., i18n extract/compile or the
project's npm/yarn scripts that update src/locales/), verify messages.po and
other generated files are updated, and commit the regenerated files so the CI
i18n catalog check passes.
In `@src/locales/ja/messages.mjs`:
- Line 1: The messages export contains widespread mojibake due to an
encoding/serialization issue; regenerate the Japanese catalog from a clean UTF-8
source and replace the current JSON payload in the messages constant so all
strings are valid Japanese (no sequences like "ã..." or broken escapes). Rebuild
the localization generation/compile step that produces the messages variable and
ensure the pipeline writes the output file using UTF-8 (verify JSON.stringify
output or your i18n tool emits proper Unicode and the writeFile/writeStream uses
'utf8'), then run the same JSON.parse(...) usage as before to confirm the file
loads correctly at runtime.
---
Outside diff comments:
In `@src/locales/cs/messages.po`:
- Around line 59-63: The translated string for msgid "{0} network{1} — pick one
to join" in src/locales/cs/messages.po must not reuse the English "{1}" suffix;
replace it with ICU pluralization: use the {0} count and an ICU plural
expression (e.g. {0, plural, one {...} few {...} other {...}}) in msgstr so
Czech singular/few/other forms are correct and remove the {1} placeholder;
update the translation for the Czech forms accordingly and ensure the code that
formats this message (referenced from BouncerNetworksPanel.tsx) passes the
numeric count as placeholder {0}.
---
Duplicate comments:
In `@src/locales/ja/messages.po`:
- Around line 85-99: Multiple Japanese msgstr entries are mojibake-corrupted
(e.g., msgid "{0}d ago", "{0}h ago", "{0}m ago" and others); open
src/locales/ja/messages.po, locate the affected msgid keys (like "{0}d ago",
"{0}h ago", "{0}m ago", "Click again to confirm" equivalents shown in the
review) and replace their garbled msgstr values with properly encoded Japanese
text (e.g., "{0}日前", "{0}時間前", "{0}分前", "もう一度クリックして確認" etc.), then save and
commit the .po file encoded as UTF-8 (ensure no double-encoding or BOM) so the
translations render correctly.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: c81f2dd5-e780-4124-8961-1c90a2f1583e
📒 Files selected for processing (45)
src/components/layout/ChatHeader.tsxsrc/components/layout/ServerList.tsxsrc/components/ui/BouncerNetworksPanel.tsxsrc/locales/cs/messages.mjssrc/locales/cs/messages.posrc/locales/de/messages.mjssrc/locales/de/messages.posrc/locales/en/messages.mjssrc/locales/en/messages.posrc/locales/es/messages.mjssrc/locales/es/messages.posrc/locales/fi/messages.mjssrc/locales/fi/messages.posrc/locales/fr/messages.mjssrc/locales/fr/messages.posrc/locales/it/messages.mjssrc/locales/it/messages.posrc/locales/ja/messages.mjssrc/locales/ja/messages.posrc/locales/ko/messages.mjssrc/locales/ko/messages.posrc/locales/nl/messages.mjssrc/locales/nl/messages.posrc/locales/pl/messages.mjssrc/locales/pl/messages.posrc/locales/pt/messages.mjssrc/locales/pt/messages.posrc/locales/ro/messages.mjssrc/locales/ro/messages.posrc/locales/ru/messages.mjssrc/locales/ru/messages.posrc/locales/sv/messages.mjssrc/locales/sv/messages.posrc/locales/tr/messages.mjssrc/locales/tr/messages.posrc/locales/uk/messages.mjssrc/locales/uk/messages.posrc/locales/zh-TW/messages.mjssrc/locales/zh-TW/messages.posrc/locales/zh/messages.mjssrc/locales/zh/messages.posrc/store/handlers/bouncer.tssrc/store/index.tstests/store/bouncerAutoBind.test.tstests/store/connectToSavedServers.test.ts
✅ Files skipped from review due to trivial changes (3)
- src/locales/en/messages.mjs
- src/locales/cs/messages.mjs
- src/locales/it/messages.mjs
🚧 Files skipped from review as they are similar to previous changes (6)
- src/locales/fr/messages.mjs
- src/locales/es/messages.mjs
- src/locales/de/messages.mjs
- src/locales/fi/messages.mjs
- src/components/ui/BouncerNetworksPanel.tsx
- src/locales/fr/messages.po
| @@ -1 +1 @@ | |||
| /*eslint-disable*/export const messages=JSON.parse("{\"+5VMrz\":[\"無効なパターン形式です。nick!user@host 形式を使用してください(ワイルドカード * 使用可)\"],\"+6NQQA\":[\"一般サポートチャンネル\"],\"+6NyRG\":[\"クライアント\"],\"+K0AvT\":[\"切断\"],\"+cyFdH\":[\"離席時に表示するデフォルトメッセージ\"],\"+mVPqU\":[\"メッセージ内のMarkdown書式を表示する\"],\"+vqCJH\":[\"認証用のアカウントユーザー名\"],\"+yPBXI\":[\"ファイルを選択\"],\"+zy2Nq\":[\"種類\"],\"/09cao\":[\"リンクセキュリティが低い(レベル \",[\"securityLevel\"],\")\"],\"/3BQ4J\":[\"チャンネル外のユーザーはメッセージを送信できません\"],\"/4C8U0\":[\"すべてコピー\"],\"/6BzZF\":[\"メンバーリストを切り替え\"],\"/AkXyp\":[\"確認しますか?\"],\"/TNOPk\":[\"ユーザーは離席中です\"],\"/XQgft\":[\"探す\"],\"/cF7Rs\":[\"音量\"],\"/dqduX\":[\"次のページ\"],\"/fc3q4\":[\"すべてのコンテンツ\"],\"/kISDh\":[\"通知音を有効にする\"],\"/n04sB\":[\"Kill\"],\"/rTz0M\":[\"音声\"],\"/rfkZe\":[\"メンションやメッセージに対してサウンドを再生する\"],\"0/0ZGA\":[\"チャンネル名マスク\"],\"0D6j7U\":[\"カスタムルールについて詳しく →\"],\"0XsHcR\":[\"ユーザーをキック\"],\"0ZpE//\":[\"ユーザー数順で並び替え\"],\"0bEPwz\":[\"離席中に設定\"],\"0dGkPt\":[\"チャンネルリストを展開\"],\"0gS7M5\":[\"表示名\"],\"0kS+M8\":[\"サンプルNET\"],\"0rgoY7\":[\"自分で選んだサーバーにのみ接続します\"],\"0wdd7X\":[\"参加\"],\"0wkVYx\":[\"プライベートメッセージ\"],\"111uHX\":[\"リンクプレビュー\"],\"196EG4\":[\"プライベートチャットを削除\"],\"1DSr1i\":[\"アカウントを登録する\"],\"1O/24y\":[\"チャンネルリストを切り替え\"],\"1VPJJ2\":[\"外部リンクの警告\"],\"1ZC/dv\":[\"未読のメンションやメッセージはありません\"],\"1pO1zi\":[\"サーバー名は必須です\"],\"1uwfzQ\":[\"チャンネルトピックを表示\"],\"268g7c\":[\"表示名を入力\"],\"2F9+AZ\":[\"まだ生のIRCトラフィックを取得していません。接続するかメッセージを送信してください。\"],\"2FOFq1\":[\"ネットワーク上のサーバーオペレーターがメッセージを閲覧できる可能性があります\"],\"2FYpfJ\":[\"詳細\"],\"2HF1Y2\":[[\"inviter\"],\" が \",[\"target\"],\" を \",[\"channel\"],\" に招待しました\"],\"2I70QL\":[\"ユーザープロフィール情報を表示する\"],\"2QYdmE\":[\"ユーザー:\"],\"2QpEjG\":[\"退出しました\"],\"2YE223\":[\"#\",[\"0\"],\" へメッセージ(Enterで改行、Shift+Enterで送信)\"],\"2bimFY\":[\"サーバーパスワードを使用する\"],\"2iTmdZ\":[\"ローカルストレージ:\"],\"2odkwe\":[\"厳格 — より積極的な保護\"],\"2uDhbA\":[\"招待するユーザー名を入力\"],\"2ygf/L\":[\"← 戻る\"],\"2zEgxj\":[\"GIFを検索...\"],\"3RdPhl\":[\"チャンネル名を変更\"],\"3THokf\":[\"Voiceユーザー\"],\"3TSz9S\":[\"最小化\"],\"3jBDvM\":[\"チャンネル表示名\"],\"3ryuFU\":[\"アプリ改善のための任意のクラッシュレポート\"],\"3uBF/8\":[\"ビューアを閉じる\"],\"3uwW8F\":[\"https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"3xf8Kz\":[\"アカウント名を入力...\"],\"4/Rr0R\":[\"現在のチャンネルにユーザーを招待する\"],\"4EZrJN\":[\"ルール\"],\"4JJtW9\":[\"#overflow\"],\"4NqeT4\":[\"フラッドプロファイル (+F)\"],\"4RZQRK\":[\"今何してるの?\"],\"4hfTrB\":[\"ニックネーム\"],\"4n99LO\":[\"すでに \",[\"0\"],\" にいます\"],\"4t6vMV\":[\"短いメッセージの場合は自動的に1行入力に切り替える\"],\"4vsHmf\":[\"時間(分)\"],\"5+INAX\":[\"自分へのメンションを含むメッセージをハイライトする\"],\"5R5Pv/\":[\"Oper名\"],\"678PKt\":[\"ネットワーク名\"],\"6Aih4U\":[\"オフライン\"],\"6CO3WE\":[\"チャンネルに参加するために必要なパスワード。キーを削除するには空欄にしてください。\"],\"6HhMs3\":[\"退出メッセージ\"],\"6V3Ea3\":[\"コピーしました\"],\"6lGV3K\":[\"折りたたむ\"],\"6yFOEi\":[\"oper パスワードを入力...\"],\"7+IHTZ\":[\"ファイルが選択されていません\"],\"73hrRi\":[\"nick!user@host(例:spam*!*@*、*!*@badhost.com)\"],\"7QkKyN\":[\"プライベートメッセージを送信\"],\"7U1W7c\":[\"とても緩め\"],\"7Y1YQj\":[\"本名:\"],\"7YHArF\":[\"— ビューアで開く\"],\"7fjnVl\":[\"ユーザーを検索...\"],\"7jL88x\":[\"このメッセージを削除しますか?この操作は元に戻せません。\"],\"7nGhhM\":[\"今どんな気分ですか?\"],\"7sEpu1\":[\"メンバー — \",[\"0\"]],\"7sNhEz\":[\"ユーザー名\"],\"8H0Q+x\":[\"プロファイルについて詳しく →\"],\"8Phu0A\":[\"ユーザーがニックネームを変更したときに表示する\"],\"8XTG9e\":[\"Operパスワードを入力\"],\"8XsV2J\":[\"再送信\"],\"8ZsakT\":[\"パスワード\"],\"8kR84m\":[\"外部リンクを開こうとしています:\"],\"8lCgih\":[\"ルールを削除\"],\"8o3dPc\":[\"ファイルをドロップしてアップロード\"],\"8p/xVT\":[[\"0\",\"plural\",{\"one\":[[\"1\"]],\"other\":[[\"2\"]]}]],\"8wRzac\":[[\"joinCount\",\"plural\",{\"other\":[[\"joinCount\"],\"回 参加した\"]}]],\"9BMLnJ\":[\"サーバーに再接続\"],\"9OEgyT\":[\"リアクションを追加\"],\"9PQ8m2\":[\"G-Line(グローバルBAN)\"],\"9Qs99X\":[\"メール:\"],\"9QupBP\":[\"パターンを削除\"],\"9bG48P\":[\"送信中\"],\"9f5f0u\":[\"プライバシーに関するご質問はこちら:\"],\"9unqs3\":[\"退席中:\"],\"9v3hwv\":[\"サーバーが見つかりません。\"],\"9zb2WA\":[\"接続中\"],\"A1taO8\":[\"検索\"],\"A2adVi\":[\"入力中通知を送信する\"],\"A9Rhec\":[\"チャンネル名\"],\"AWOSPo\":[\"ズームイン\"],\"AXSpEQ\":[\"接続時にOperになる\"],\"AeXO77\":[\"アカウント\"],\"AhNP40\":[\"シーク\"],\"Ai2U7L\":[\"ホスト\"],\"AjBQnf\":[\"ニックネームを変更しました\"],\"AmXVh6\":[\"https://example.com/avatar.png\"],\"AnRu/j\":[\"返信をキャンセル\"],\"ApSx0O\":[\"\\\"\",[\"searchQuery\"],\"\\\"に一致するメッセージが\",[\"0\"],\"件見つかりました\"],\"AxPAXW\":[\"結果が見つかりません\"],\"AyNqAB\":[\"すべてのサーバーイベントをチャットに表示する\"],\"B/QqGw\":[\"席を外しています\"],\"B8AaMI\":[\"この項目は必須です\"],\"BA2c49\":[\"このサーバーは高度なLISTフィルタリングをサポートしていません\"],\"BDKt3I\":[[\"0\"],\"、\",[\"1\"],\"、\",[\"2\"],\" と他 \",[\"3\"],\" 人が入力中...\"],\"BGul2A\":[\"保存されていない変更があります。保存せずに閉じてもよいですか?\"],\"BIf9fi\":[\"ステータスメッセージ\"],\"BPm98R\":[\"サーバーが選択されていません。まずサイドバーからサーバーを選択してください。招待リンクはサーバーごとに管理されます。\"],\"BZz3md\":[\"個人ウェブサイト\"],\"Bgm/H7\":[\"複数行のテキスト入力を許可する\"],\"BiQIl1\":[\"このプライベートメッセージの会話をピン留めする\"],\"BlNZZ2\":[\"クリックしてメッセージに移動\"],\"Bowq3c\":[\"オペレーターのみがチャンネルトピックを変更できます\"],\"Btozzp\":[\"この画像の有効期限が切れています\"],\"Bycfjm\":[\"合計:\",[\"0\"]],\"C6IBQc\":[\"JSON全体をコピー\"],\"C9L9wL\":[\"データ収集\"],\"CDq4wC\":[\"ユーザーをモデレート\"],\"CHVRxG\":[\"@\",[\"0\"],\" へメッセージ(Shift+Enterで改行)\"],\"CN9zdR\":[\"Oper名とパスワードは必須です\"],\"CW3sYa\":[\"リアクション \",[\"emoji\"],\" を追加\"],\"CaAkqd\":[\"退出を表示\"],\"CbvaYj\":[\"ニックネームでBAN\"],\"CcK+Ft\":[\"PDF\"],\"Ce8q3L\":[\"チャンネルを選択\"],\"CsekCi\":[\"通常\"],\"D+NlUC\":[\"システム\"],\"D28t6+\":[\"参加して退出しました\"],\"DB8zMK\":[\"適用\"],\"DBcWHr\":[\"カスタム通知音ファイル\"],\"DTy9Xw\":[\"メディアプレビュー\"],\"Dj4pSr\":[\"安全なパスワードを選択してください\"],\"Du+zn+\":[\"検索中...\"],\"Du2T2f\":[\"設定が見つかりません\"],\"DwsSVQ\":[\"フィルターを適用して更新\"],\"E3W/zd\":[\"デフォルトニックネーム\"],\"E6nRW7\":[\"URLをコピー\"],\"E703RG\":[\"モード:\"],\"EAeu1Z\":[\"招待を送信\"],\"EFKJQT\":[\"設定\"],\"EGPQBv\":[\"カスタムフラッドルール (+f)\"],\"ELik0r\":[\"プライバシーポリシー全文を見る\"],\"EPbeC2\":[\"チャンネルトピックを表示または編集する\"],\"EQCDNT\":[\"oper ユーザー名を入力...\"],\"EUvulZ\":[\"\\\"\",[\"searchQuery\"],\"\\\"に一致するメッセージが1件見つかりました\"],\"EatZYJ\":[\"次の画像\"],\"EdQY6l\":[\"なし\"],\"EnqLYU\":[\"サーバーを検索...\"],\"F0OKMc\":[\"サーバーを編集\"],\"F6Int2\":[\"ハイライトを有効にする\"],\"FDoLyE\":[\"最大ユーザー数\"],\"FUU/hZ\":[\"チャットで読み込む外部メディアの量を制御します。\"],\"Fdp03t\":[\"オン\"],\"FfPWR0\":[\"モーダル\"],\"FjkaiT\":[\"ズームアウト\"],\"FlqOE9\":[\"これが意味すること:\"],\"FolHNl\":[\"アカウントと認証を管理する\"],\"Fp2Dif\":[\"サーバーを退出しました\"],\"G5KmCc\":[\"GZ-Line(グローバルZ-Line)\"],\"GDs0lz\":[\"<0>リスク:</0> 機密情報(メッセージ、プライベート会話、認証情報)が、IRCサーバー間に位置するネットワーク管理者や攻撃者に露出する可能性があります。\"],\"GR+2I3\":[\"招待マスクを追加(例:nick!*@*、*!*@host.com)\"],\"GRLyMU\":[\"ポップアウトしたサーバー通知を閉じる\"],\"GdhD7H\":[\"もう一度クリックして確認\"],\"GlHnXw\":[\"ニックネームの変更に失敗しました: \",[\"error\"],\" \",[\"0\"]],\"GswZF3\":[\"プレビュー:\"],\"GtmO8/\":[\"から\"],\"GtuHUQ\":[\"サーバー上でこのチャンネルの名前を変更します。すべてのユーザーに新しい名前が表示されます。\"],\"GuGfFX\":[\"検索を切り替え\"],\"GxkJXS\":[\"アップロード中...\"],\"GzbwnK\":[\"チャンネルに参加しました\"],\"GzsUDB\":[\"拡張プロフィール\"],\"H/PnT8\":[\"絵文字を挿入\"],\"H6Izzl\":[\"お好みのカラーコード\"],\"H9jIv+\":[\"参加/退出を表示\"],\"HAKBY9\":[\"ファイルをアップロード\"],\"HdE1If\":[\"チャンネル\"],\"Hk4AW9\":[\"お好みの表示名\"],\"HmHDk7\":[\"メンバーを選択\"],\"HrQzPU\":[[\"networkName\"],\" のチャンネル\"],\"I2tXQ5\":[\"@\",[\"0\"],\" へメッセージ(Enterで改行、Shift+Enterで送信)\"],\"I6bw/h\":[\"ユーザーをBAN\"],\"I92Z+b\":[\"通知を有効にする\"],\"I9D72S\":[\"このメッセージを削除してもよいですか?この操作は元に戻せません。\"],\"IA+1wo\":[\"ユーザーがチャンネルからキックされたときに表示する\"],\"IDwkJx\":[\"IRC Operator\"],\"ILlU+s\":[\"情報:\"],\"IUwGEM\":[\"変更を保存\"],\"IVeGK6\":[[\"0\"],\"、\",[\"1\"],\"、\",[\"2\"],\" が入力中...\"],\"IgrLD/\":[\"一時停止\"],\"Im6JED\":[\"WHISPER\"],\"ImOQa9\":[\"返信\"],\"IoHMnl\":[\"最大値は \",[\"0\"],\" です\"],\"IvMj+0\":[\"Op\"],\"J28zul\":[\"接続中...\"],\"J5T9NW\":[\"ユーザー情報\"],\"J8Y5+z\":[\"おっと!ネットワーク分割が発生しました!⚠️\"],\"JBHkBA\":[\"チャンネルを退出しました\"],\"JCwL0Q\":[\"理由を入力(任意)\"],\"JFciKP\":[\"切り替え\"],\"JXGkhG\":[\"チャンネル名を変更する(オペレーターのみ)\"],\"JcD7qf\":[\"その他のアクション\"],\"JdkA+c\":[\"シークレット (+s)\"],\"Jmu12l\":[\"サーバーチャンネル\"],\"JvQ++s\":[\"Markdownを有効にする\"],\"K2jwh/\":[\"WHOISデータがありません\"],\"KAXSwC\":[\"Voice\"],\"KDfTdX\":[\"メッセージを削除\"],\"KKBlUU\":[\"埋め込み\"],\"KM0pLb\":[\"チャンネルへようこそ!\"],\"KR6W2h\":[\"無視を解除\"],\"KV+Bi1\":[\"招待制 (+i)\"],\"KdCtwE\":[\"カウンターをリセットするまでフラッドアクティビティを監視する秒数\"],\"Kkezga\":[\"サーバーパスワード\"],\"KsiQ/8\":[\"ユーザーはチャンネルに参加するために招待が必要です\"],\"L+gB/D\":[\"チャンネル情報\"],\"LC1a7n\":[\"IRCサーバーは、サーバー間リンクのセキュリティレベルが低いと報告しています。これは、ネットワーク内のIRCサーバー間でメッセージが中継される際に、適切に暗号化されていないか、SSL/TLS証明書が正しく検証されない可能性があることを意味します。\"],\"LNfLR5\":[\"キックを表示\"],\"LQb0W/\":[\"すべてのイベントを表示\"],\"LU7/yA\":[\"UI上で表示するための別名です。スペース、絵文字、特殊文字を含めることができます。実際のチャンネル名(\",[\"channelName\"],\")は引き続きIRCコマンドで使用されます。\"],\"LUb9O7\":[\"有効なサーバーポートが必要です\"],\"LV4fT6\":[\"説明(任意、例:「Q3ベータテスター」)\"],\"Lb+BUl\":[\"https://example.com/avatar.jpg\"],\"LcET2C\":[\"プライバシーポリシー\"],\"LcuSDR\":[\"プロフィール情報とメタデータを管理する\"],\"LqLS9B\":[\"ニックネーム変更を表示\"],\"LsDQt2\":[\"チャンネル設定\"],\"LtI9AS\":[\"オーナー\"],\"LuNhhL\":[\"このメッセージにリアクションしました\"],\"M/AZNG\":[\"アバター画像のURL\"],\"M/WIer\":[\"メッセージを送信\"],\"M8er/5\":[\"名前:\"],\"MHk+7g\":[\"前の画像\"],\"MRorGe\":[\"DMを送る\"],\"MVbSGP\":[\"時間ウィンドウ(秒)\"],\"MkpcsT\":[\"メッセージと設定はデバイス上にローカルで保存されます\"],\"N/hDSy\":[\"ボットとしてマーク — 通常は「on」または空欄\"],\"N7TQbE\":[[\"channelName\"],\" にユーザーを招待\"],\"NCca/o\":[\"デフォルトニックネームを入力...\"],\"Nqs6B9\":[\"すべての外部メディアを表示します。URLが不明なサーバーへのリクエストを引き起こす場合があります。\"],\"Nt+9O7\":[\"生のTCPの代わりにWebSocketを使用する\"],\"NxIHzc\":[\"ユーザーを切断\"],\"O+v/cL\":[\"サーバー上のすべてのチャンネルを一覧表示\"],\"ODwSCk\":[\"GIFを送信\"],\"OGQ5kK\":[\"通知音とハイライトを設定する\"],\"OIPt1Z\":[\"メンバーリストのサイドバーを表示/非表示にする\"],\"OKSNq/\":[\"とても厳格\"],\"ONWvwQ\":[\"アップロード\"],\"OVKoQO\":[\"認証用のアカウントパスワード\"],\"ObsidianIRC - Bringing IRC to the future\":[\"ObsidianIRC - IRCを未来へ\"],\"OhCpra\":[\"トピックを設定…\"],\"OkltoQ\":[[\"username\"],\" をニックネームでBANする(同じニックネームでの再参加を防止)\"],\"P+t/Te\":[\"追加データなし\"],\"P42Wcc\":[\"安全\"],\"PD38l0\":[\"チャンネルアバタープレビュー\"],\"PD9mEt\":[\"メッセージを入力...\"],\"PPqfdA\":[\"チャンネル設定を開く\"],\"PSCjfZ\":[\"このチャンネルに表示されるトピックです。すべてのユーザーがトピックを閲覧できます。\"],\"PZCecv\":[\"PDFプレビュー\"],\"PeLgsC\":[[\"c\",\"plural\",{\"other\":[[\"c\"],\"回\"]}]],\"PguS2C\":[\"例外マスクを追加(例:nick!*@*、*!*@host.com)\"],\"Pil5Ty\":[[\"0\"],\" 件中 \",[\"displayedChannelsCount\"],\" 件を表示中\"],\"PqhVlJ\":[\"ユーザーをBAN(ホストマスク)\"],\"Q+chwU\":[\"ユーザー名:\"],\"Q2QY4/\":[\"この招待を削除\"],\"Q6hhn8\":[\"設定\"],\"QF4a34\":[\"ユーザー名を入力してください\"],\"QGqSZ2\":[\"カラーと書式設定\"],\"QJQd1J\":[\"プロフィールを編集\"],\"QSzGDE\":[\"アイドル\"],\"QUlny5\":[[\"0\"],\" へようこそ!\"],\"Qoq+GP\":[\"もっと読む\"],\"QuSkCF\":[\"チャンネルをフィルター...\"],\"QwUrDZ\":[\"トピックを変更しました: \",[\"topic\"]],\"R0UH07\":[[\"1\"],\"枚中\",[\"0\"],\"枚目の画像\"],\"R7SsBE\":[\"ミュート\"],\"R8rf1X\":[\"クリックしてトピックを設定\"],\"RArB3D\":[[\"username\"],\" によって \",[\"channelName\"],\" からキックされました\"],\"RI3cWd\":[\"ObsidianIRCでIRCの世界を探索しよう\"],\"RIfHS5\":[\"新しい招待リンクを作成\"],\"RMMaN5\":[\"モデレート制 (+m)\"],\"RWw9Lg\":[\"モーダルを閉じる\"],\"RZ2BuZ\":[[\"account\"],\" のアカウント登録には確認が必要です: \",[\"message\"]],\"RySp6q\":[\"コメントを非表示\"],\"SPKQTd\":[\"ニックネームは必須です\"],\"SPVjfj\":[\"空欄の場合は「理由なし」がデフォルトになります\"],\"SQKPvQ\":[\"ユーザーを招待\"],\"SkZcl+\":[\"定義済みのフラッド保護プロファイルを選択してください。これらのプロファイルは、さまざまなユースケースに対してバランスの取れた保護設定を提供します。\"],\"Slr+3C\":[\"最小ユーザー数\"],\"Spnlre\":[[\"target\"],\" を \",[\"channel\"],\" に招待しました\"],\"T/ckN5\":[\"ビューアで開く\"],\"T91vKp\":[\"再生\"],\"TV2Wdu\":[\"データの取り扱いとプライバシー保護について詳しく見る。\"],\"TgFpwD\":[\"適用中...\"],\"TkzSFB\":[\"変更なし\"],\"TtserG\":[\"本名を入力\"],\"Ttz9J1\":[\"パスワードを入力...\"],\"Tz0i8g\":[\"設定\"],\"U3pytU\":[\"管理者\"],\"UDb2YD\":[\"リアクション\"],\"UE4KO5\":[\"*channel*\"],\"UETAwW\":[\"まだ招待リンクを作成していません。上のフォームから最初の招待リンクを作成してください。\"],\"UGT5vp\":[\"設定を保存\"],\"UV5hLB\":[\"BANが見つかりません\"],\"Uaj3Nd\":[\"ステータスメッセージ\"],\"Ue3uny\":[\"デフォルト(プロファイルなし)\"],\"UkARhe\":[\"通常 — 標準的な保護\"],\"Umn7Cj\":[\"まだコメントはありません。最初のコメントを投稿しましょう!\"],\"UtUIRh\":[[\"0\"],\" 件の古いメッセージ\"],\"UwzP+U\":[\"セキュア接続\"],\"V0/A4O\":[\"チャンネルオーナー\"],\"V4qgxE\":[\"作成日時(以前、分前)\"],\"V8yTm6\":[\"検索をクリア\"],\"VJMMyz\":[\"ObsidianIRC — IRCを未来へ\"],\"VJScHU\":[\"理由\"],\"VLsmVV\":[\"通知をミュート\"],\"VbyRUy\":[\"コメント\"],\"Vmx0mQ\":[\"設定者:\"],\"VqnIZz\":[\"プライバシーポリシーとデータ取り扱い方針を見る\"],\"VrMygG\":[\"最小文字数は \",[\"0\"],\" 文字です\"],\"VrnTui\":[\"プロフィールに表示される代名詞\"],\"W8E3qn\":[\"認証済みアカウント\"],\"WAakm9\":[\"チャンネルを削除\"],\"WFxTHC\":[\"BANマスクを追加(例:nick!*@*、*!*@host.com)\"],\"WN1g9F\":[\"サーバーホストは必須です\"],\"WRYdXW\":[\"音声の再生位置\"],\"WUOH5B\":[\"ユーザーを無視\"],\"WWEXnZ\":[[\"0\",\"plural\",{\"other\":[\"さらに \",[\"1\"],\" 件表示\"]}]],\"WYxRzo\":[\"招待リンクを作成・管理する\"],\"Wd38W1\":[\"チャンネルを空欄にすると、汎用のネットワーク招待になります。説明はあなた専用の記録で、このリストで自分にのみ表示されます。\"],\"Weq9zb\":[\"一般\"],\"Wfj7Sk\":[\"通知音をミュートまたはミュート解除する\"],\"Wm7gbG\":[\"GitHub:\"],\"WyeHWY\":[\"*spam*\"],\"WzMCru\":[\"ユーザープロフィール\"],\"X6S3lt\":[\"設定、チャンネル、サーバーを検索...\"],\"XEHan5\":[\"このまま続行\"],\"XI1+wb\":[\"無効な形式\"],\"XIXeuC\":[\"@\",[\"0\"],\" へメッセージ\"],\"XMS+k4\":[\"プライベートメッセージを開始\"],\"XWgxXq\":[\"アルバム\"],\"Xd7+IT\":[\"プライベートチャットのピン留めを解除\"],\"Xm/s+u\":[\"表示\"],\"Xp2n93\":[\"サーバーの信頼済みファイルホストからのメディアを表示します。外部サービスへのリクエストは発生しません。\"],\"XvjC4F\":[\"保存中...\"],\"Y/qryO\":[\"検索に一致するユーザーが見つかりません\"],\"YAqRpI\":[[\"account\"],\" のアカウント登録に成功しました: \",[\"message\"]],\"YEfzvP\":[\"トピック保護 (+t)\"],\"YQOn6a\":[\"メンバーリストを折りたたむ\"],\"YRCoE9\":[\"チャンネルオペレーター\"],\"YURQaF\":[\"プロフィールを表示\"],\"YdBSvr\":[\"メディア表示と外部コンテンツを制御する\"],\"Yj6U3V\":[\"中央サーバーなし:\"],\"YjvpGx\":[\"代名詞\"],\"YqH4l4\":[\"キーなし\"],\"YyUPpV\":[\"アカウント:\"],\"ZJSWfw\":[\"サーバーから切断したときに表示されるメッセージ\"],\"ZR1dJ4\":[\"招待\"],\"ZdWg0V\":[\"ブラウザで開く\"],\"ZhRBbl\":[\"メッセージを検索…\"],\"Zmcu3y\":[\"詳細フィルター\"],\"a2/8e5\":[\"トピック設定日時(以降、分前)\"],\"aHKcKc\":[\"前のページ\"],\"aJTbXX\":[\"Operパスワード\"],\"aQryQv\":[\"パターンはすでに存在します\"],\"aW9pLN\":[\"チャンネルに参加できる最大ユーザー数。制限なしの場合は空欄にしてください。\"],\"ah4fmZ\":[\"YouTube、Vimeo、SoundCloudなどの既知のサービスのプレビューも表示します。\"],\"aifXak\":[\"このチャンネルにはメディアがありません\"],\"ap2zBz\":[\"緩め\"],\"az8lvo\":[\"オフ\"],\"azXSNo\":[\"メンバーリストを展開\"],\"azdliB\":[\"アカウントにログイン\"],\"b26wlF\":[\"彼女/彼女の\"],\"bD/+Ei\":[\"厳格\"],\"bQ6BJn\":[\"詳細なフラッド保護ルールを設定します。各ルールは、監視するアクティビティの種類と、しきい値を超えた場合に実行するアクションを指定します。\"],\"beV7+y\":[\"ユーザーは \",[\"channelName\"],\" への参加招待を受け取ります。\"],\"bk84cH\":[\"離席メッセージ\"],\"bkHdLj\":[\"IRCサーバーを追加\"],\"bmQLn5\":[\"ルールを追加\"],\"bwRvnp\":[\"アクション\"],\"c8+EVZ\":[\"認証済みアカウント\"],\"cGYUlD\":[\"メディアプレビューは読み込まれません。\"],\"cLF98o\":[\"コメントを表示 (\",[\"commentCount\"],\")\"],\"cLKIDO\":[\"利用可能なユーザーがいません\"],\"cSgpoS\":[\"プライベートチャットをピン留め\"],\"cde3ce\":[\"<0>\",[\"0\"],\"</0> へメッセージ\"],\"chQsxg\":[\"フォーマット済み出力をコピー\"],\"cl/A5J\":[[\"__DEFAULT_IRC_SERVER_NAME__\"],\" へようこそ!\"],\"cnGeoo\":[\"削除\"],\"coPLXT\":[\"IRC通信はサーバーに保存されません\"],\"crYH/6\":[\"SoundCloudプレーヤー\"],\"d3sis4\":[\"サーバーを追加\"],\"d9aN5k\":[[\"username\"],\" をチャンネルから削除する\"],\"dEgA5A\":[\"キャンセル\"],\"dGi1We\":[\"このプライベートメッセージの会話のピン留めを解除する\"],\"dJVuyC\":[[\"channelName\"],\" を退出しました (\",[\"reason\"],\")\"],\"dMtLDE\":[\"宛て\"],\"dXqxlh\":[\"<0>⚠️ セキュリティリスク!</0> この接続は傍受や中間者攻撃に対して脆弱な可能性があります。\"],\"da9Q/R\":[\"チャンネルモードを変更しました\"],\"dhJN3N\":[\"コメントを表示\"],\"dj2xTE\":[\"通知を閉じる\"],\"dpCzmC\":[\"フラッド保護設定\"],\"e9dQpT\":[\"このリンクを新しいタブで開きますか?\"],\"ePK91l\":[\"編集\"],\"eYBDuB\":[\"画像をアップロードするか、動的サイズ変換のための \",[\"size\"],\" 置換を含むURLを入力してください\"],\"edBbee\":[[\"username\"],\" をホストマスクでBANする(同じIP/ホストからの再参加を防止)\"],\"ekfzWq\":[\"ユーザー設定\"],\"elPDWs\":[\"IRCクライアントの使い心地をカスタマイズする\"],\"eu2osY\":[\"<0>💡 推奨事項:</0> このサーバーを信頼し、リスクを理解している場合のみ続行してください。この接続で機密情報やパスワードを共有しないようにしてください。\"],\"euEhbr\":[\"クリックして \",[\"channel\"],\" に参加\"],\"ez3vLd\":[\"複数行入力を有効にする\"],\"f0J5Ki\":[\"サーバー間通信に暗号化されていない接続が使用される可能性があります\"],\"f9BHJk\":[\"ユーザーに警告\"],\"fDOLLd\":[\"チャンネルが見つかりません。\"],\"ffzDkB\":[\"匿名分析:\"],\"fq1GF9\":[\"ユーザーがサーバーから切断したときに表示\"],\"gEF57C\":[\"このサーバーは1種類の接続タイプのみサポートしています\"],\"gJuLUI\":[\"無視リスト\"],\"gNzMrk\":[\"現在のアバター\"],\"gjPWyO\":[\"ニックネームを入力...\"],\"gz6UQ3\":[\"最大化\"],\"h6razj\":[\"チャンネル名マスクを除外\"],\"hG6jnw\":[\"トピックが設定されていません\"],\"hG89Ed\":[\"画像\"],\"hYgDIe\":[\"作成\"],\"hZ6znB\":[\"ポート\"],\"ha+Bz5\":[\"例:100:1440\"],\"he3ygx\":[\"コピー\"],\"hehnjM\":[\"数量\"],\"hzdLuQ\":[\"Voice以上の権限を持つユーザーのみ発言できます\"],\"i0qMbr\":[\"ホーム\"],\"iDNBZe\":[\"通知\"],\"iH8pgl\":[\"戻る\"],\"iL9SZg\":[\"ユーザーをBAN(ニックネーム)\"],\"iNt+3c\":[\"画像に戻る\"],\"iQvi+a\":[\"このサーバーのリンクセキュリティが低い場合に警告しない\"],\"iSLIjg\":[\"接続\"],\"iWXkHH\":[\"Halfop\"],\"iZeTtp\":[\"サーバーホスト\"],\"idD8Ev\":[\"保存済み\"],\"iivqkW\":[\"サインイン日時\"],\"ij+Elv\":[\"画像プレビュー\"],\"ilIWp7\":[\"通知を切り替え\"],\"iuaqvB\":[\"ワイルドカードには * を使用してください。例:baduser!*@*、*!*@spammer.com、troll*!*@*\"],\"ixkTse\":[\"ボット\"],\"j2DGR0\":[\"ホストマスクでBAN\"],\"jA4uoI\":[\"トピック:\"],\"jLXxGK\":[\"https://example.com\"],\"jPSk57\":[\"理由(任意)\"],\"jUV7CU\":[\"アバターをアップロード\"],\"jW5Uwh\":[\"外部メディアの読み込み範囲を制御します。オフ/安全/信頼できるソース/すべてのコンテンツ。\"],\"jXzms5\":[\"添付オプション\"],\"jZlrte\":[\"カラー\"],\"jfC/xh\":[\"連絡先\"],\"jywMpv\":[\"#new-channel-name\"],\"k112DD\":[\"古いメッセージを読み込む\"],\"k3ID0F\":[\"メンバーをフィルター…\"],\"k65gsE\":[\"詳細を見る\"],\"k7Zgob\":[\"接続をキャンセル\"],\"kAVx5h\":[\"招待が見つかりません\"],\"kCLEPU\":[\"接続先\"],\"kF5LKb\":[\"無視パターン:\"],\"kGeOx/\":[[\"0\"],\" に参加\"],\"kITKr8\":[\"チャンネルモードを読み込み中...\"],\"kPpPsw\":[\"あなたはIRC Operatorです\"],\"kWJmRL\":[\"あなた\"],\"kfcRb0\":[\"アバター\"],\"kjMqSj\":[\"JSONをコピー\"],\"krViRy\":[\"JSONとしてコピーするにはクリック\"],\"ks71ra\":[\"例外\"],\"kw4lRv\":[\"チャンネルハーフオペレーター\"],\"kxgIRq\":[\"チャンネルを選択または追加して始めましょう。\"],\"ky6dWe\":[\"アバタープレビュー\"],\"l+GxCv\":[\"チャンネルを読み込み中...\"],\"l+IUVW\":[[\"account\"],\" のアカウント確認に成功しました: \",[\"message\"]],\"l/siQz\":[[\"reconnectCount\",\"plural\",{\"other\":[[\"reconnectCount\"],\"回 再接続した\"]}]],\"l1l8sj\":[[\"0\"],\"日前\"],\"l5NhnV\":[\"#チャンネル(任意)\"],\"l5jmzx\":[[\"0\"],\" と \",[\"1\"],\" が入力中...\"],\"lCF0wC\":[\"更新\"],\"lHy8N5\":[\"さらにチャンネルを読み込み中...\"],\"lasgrr\":[\"使用済み\"],\"lbpf14\":[[\"value\"],\"に参加\"],\"lfFsZ4\":[\"チャンネル\"],\"lkNdiH\":[\"アカウント名\"],\"ln500L\":[\"ObsidianIRC\"],\"lnCMdg\":[\"画像をアップロード\"],\"loQxaJ\":[\"戻りました\"],\"lvfaxv\":[\"ホーム\"],\"m16xKo\":[\"追加\"],\"m8flAk\":[\"プレビュー(未アップロード)\"],\"mEPxTp\":[\"<0>⚠️ 注意!</0> 信頼できる送信元のリンクのみ開いてください。悪意のあるリンクはセキュリティやプライバシーを侵害する恐れがあります。\"],\"mHGdhG\":[\"サーバー情報\"],\"mHS8lb\":[\"#\",[\"0\"],\" へメッセージ\"],\"mMYBD9\":[\"広め — より広範な保護範囲\"],\"mTGsPd\":[\"チャンネルトピック\"],\"mU8j6O\":[\"外部メッセージ禁止 (+n)\"],\"mZp8FL\":[\"自動的に1行入力に切り替え\"],\"mdQu8G\":[\"YourNickname\"],\"miSSBQ\":[\"コメント (\",[\"commentCount\"],\")\"],\"mvyLSy\":[\"ユーザーは認証済みです\"],\"mwtcGl\":[\"コメントを閉じる\"],\"mzI/c+\":[\"ダウンロード\"],\"n3fGRk\":[[\"0\"],\" が設定\"],\"nE9jsU\":[\"緩め — 控えめな保護\"],\"nNflMD\":[\"チャンネルを退出\"],\"nPXkBi\":[\"WHOISデータを読み込み中...\"],\"nQnxxF\":[\"#\",[\"0\"],\" へメッセージ(Shift+Enterで改行)\"],\"nWMRxa\":[\"ピン留めを解除\"],\"nkC032\":[\"フラッドプロファイルなし\"],\"o69z4d\":[[\"username\"],\" に警告メッセージを送る\"],\"o9ylQi\":[\"GIFを検索して始めましょう\"],\"oFGkER\":[\"サーバー通知\"],\"oOi11l\":[\"最下部にスクロール\"],\"oPYIL5\":[\"ネットワーク\"],\"oQEzQR\":[\"新しいDM\"],\"oXOSPE\":[\"オンライン\"],\"oal760\":[\"サーバーリンクへの中間者攻撃が可能な状態です\"],\"oeqmmJ\":[\"信頼できるソース\"],\"optX0N\":[[\"0\"],\"時間前\"],\"ovBPCi\":[\"デフォルト\"],\"p0Z69r\":[\"パターンを空にすることはできません\"],\"p1KgtK\":[\"音声の読み込みに失敗しました\"],\"p59pEv\":[\"詳細情報\"],\"p7sRI6\":[\"入力中であることを他のユーザーに知らせる\"],\"pBm1od\":[\"シークレットチャンネル\"],\"pNmiXx\":[\"すべてのサーバーで使用するデフォルトのニックネーム\"],\"pUUo9G\":[\"ホスト名:\"],\"pVGPmz\":[\"アカウントパスワード\"],\"peNE68\":[\"永続的\"],\"plhHQt\":[\"データなし\"],\"pm6+q5\":[\"セキュリティ警告\"],\"pn5qSs\":[\"追加情報\"],\"q0cR4S\":[\"は現在 **\",[\"newNick\"],\"** として知られています\"],\"qFcunY\":[\"LIST または NAMES コマンドにチャンネルが表示されません\"],\"qLpTm/\":[\"リアクション \",[\"emoji\"],\" を削除\"],\"qVkGWK\":[\"ピン留め\"],\"qY8wNa\":[\"ホームページ\"],\"qb0xJ7\":[\"ワイルドカードを使用してください:* は任意の文字列、? は任意の1文字に一致します。例:nick!*@*、*!*@host.com、*!*user@*\"],\"qhzpRq\":[\"チャンネルキー (+k)\"],\"qtoOYG\":[\"制限なし\"],\"r1W2AS\":[\"ファイルホスト画像\"],\"rIPR2O\":[\"トピック設定日時(以前、分前)\"],\"rMMSYo\":[\"最大文字数は \",[\"0\"],\" 文字です\"],\"rWtzQe\":[\"ネットワークが分割され、再接続されました。✅\"],\"rYG2u6\":[\"しばらくお待ちください...\"],\"rdUucN\":[\"プレビュー\"],\"rjGI/Q\":[\"プライバシー\"],\"rk8iDX\":[\"GIFを読み込み中...\"],\"rn6SBY\":[\"ミュート解除\"],\"s/UKqq\":[\"チャンネルからキックされました\"],\"s8cATI\":[[\"channelName\"],\" に参加しました\"],\"sCO9ue\":[\"<0>\",[\"serverName\"],\"</0> への接続には次のセキュリティ上の懸念事項があります:\"],\"sGH11W\":[\"サーバー\"],\"sHI1H+\":[\"は現在 **\",[\"newNick\"],\"** として知られています\"],\"sJyV04\":[[\"inviter\"],\" があなたを \",[\"channel\"],\" に招待しました\"],\"sby+1/\":[\"クリックしてコピー\"],\"sfN25C\":[\"本名またはフルネーム\"],\"sliuzR\":[\"リンクを開く\"],\"sqrO9R\":[\"カスタムメンション\"],\"sr6RdJ\":[\"Shift+Enterで複数行入力\"],\"swrCpB\":[[\"user\"],\" がチャンネルを \",[\"oldName\"],\" から \",[\"newName\"],\" に変更しました\",[\"0\"]],\"sxkWRg\":[\"詳細設定\"],\"t/YqKh\":[\"削除\"],\"t47eHD\":[\"このサーバーでの固有識別子\"],\"tAkAh0\":[\"動的サイズ変換のための \",[\"size\"],\" 置換を含むURL。例:https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"tXLJS3\":[\"チャンネルリストのサイドバーを表示/非表示にする\"],\"tfDRzk\":[\"保存\"],\"tiBsJk\":[[\"channelName\"],\" を退出しました\"],\"tt4/UD\":[\"退出しました (\",[\"reason\"],\")\"],\"u0TcnO\":[\"ニックネーム {nick} は既に使用中です。{newNick} で再試行します\"],\"u0a8B4\":[\"管理者アクセスのためにIRC Operatorとして認証する\"],\"u0rWFU\":[\"作成日時(以降、分前)\"],\"u72w3t\":[\"無視するユーザーとパターン\"],\"u7jc2L\":[\"退出しました\"],\"uAQUqI\":[\"ステータス\"],\"uB85T3\":[\"保存失敗:\",[\"msg\"]],\"uV3DOL\":[\"G-Line\"],\"uW3lLI\":[\"IRCサーバー:\"],\"ukyW4o\":[\"あなたの招待リンク\"],\"usSSr/\":[\"ズームレベル\"],\"v7uvcf\":[\"ソフトウェア:\"],\"vE8kb+\":[\"Shift+Enterで改行(Enterで送信)\"],\"vERlcd\":[\"プロフィール\"],\"vK0RL8\":[\"トピックなし\"],\"vSJd18\":[\"動画\"],\"vXIe7J\":[\"言語\"],\"vaHYxN\":[\"本名\"],\"vhjbKr\":[\"離席中\"],\"w4NYox\":[[\"title\"],\" クライアント\"],\"w8xQRx\":[\"無効な値\"],\"wFjjxZ\":[[\"username\"],\" によって \",[\"channelName\"],\" からキックされました (\",[\"reason\"],\")\"],\"wGjaGl\":[\"BAN例外が見つかりません\"],\"wPrGnM\":[\"チャンネル管理者\"],\"wRkP2d\":[\"GIF\"],\"wbm86v\":[\"ユーザーがチャンネルに参加・退出したときに表示する\"],\"whqZ9r\":[\"ハイライトする追加の単語またはフレーズ\"],\"wm7RV4\":[\"通知音\"],\"wz/Yoq\":[\"サーバー間で中継される際にメッセージが傍受される可能性があります\"],\"x3+y8b\":[\"このリンクから登録した人数\"],\"xCJdfg\":[\"クリア\"],\"xOTzt5\":[\"たった今\"],\"xUHRTR\":[\"接続時に自動的にOperatorとして認証する\"],\"xWHwwQ\":[\"BAN一覧\"],\"xYilR2\":[\"メディア\"],\"xbi8D6\":[\"このサーバーは招待リンクに対応していません(<0>obby.world/invitation</0>ケイパビリティが告知されていません)。通常通りチャットは利用できます。このパネルはobbyircdベースのネットワーク用です。\"],\"xceQrO\":[\"安全なWebSocketのみサポートされています\"],\"xdtXa+\":[\"チャンネル名\"],\"xfXC7q\":[\"テキストチャンネル\"],\"xlCYOE\":[\"メッセージを取得中...\"],\"xlhswE\":[\"最小値は \",[\"0\"],\" です\"],\"xq97Ci\":[\"単語またはフレーズを追加...\"],\"xuRqRq\":[\"クライアント制限 (+l)\"],\"xwF+7J\":[[\"0\"],\" が入力中...\"],\"y1eoq1\":[\"リンクをコピー\"],\"yNeucF\":[\"このサーバーは拡張プロフィールメタデータ(IRCv3 METADATA拡張)をサポートしていません。アバター、表示名、ステータスなどの追加フィールドは利用できません。\"],\"yPlrca\":[\"チャンネルアバター\"],\"yQE2r9\":[\"読み込み中\"],\"ySU+JY\":[\"your@email.com\"],\"yTX1Rt\":[\"Operユーザー名\"],\"yYOzWD\":[\"ログ\"],\"yfx9Re\":[\"IRC Operatorパスワード\"],\"ygCKqB\":[\"停止\"],\"ymDxJx\":[\"IRC Operatorユーザー名\"],\"yrpRsQ\":[\"名前順で並び替え\"],\"yz7wBu\":[\"閉じる\"],\"zJw+jA\":[\"モードを設定: \",[\"0\"]],\"zbymaY\":[[\"0\"],\"分前\"],\"zebeLu\":[\"Operユーザー名を入力\"],\"zpr0Bw\":[\"GZ-Line\"]}"); No newline at end of file | |||
| /*eslint-disable*/export const messages=JSON.parse("{\"+5VMrz\":[\"無効なパターン形式です。nick!user@host 形式を使用してください(ワイルドカード * 使用可)\"],\"+6NQQA\":[\"一般サポートチャンネル\"],\"+6NyRG\":[\"クライアント\"],\"+K0AvT\":[\"切断\"],\"+cyFdH\":[\"離席時に表示するデフォルトメッセージ\"],\"+mVPqU\":[\"メッセージ内のMarkdown書式を表示する\"],\"+vqCJH\":[\"認証用のアカウントユーザー名\"],\"+yPBXI\":[\"ファイルを選択\"],\"+zy2Nq\":[\"種類\"],\"/09cao\":[\"リンクセキュリティが低い(レベル \",[\"securityLevel\"],\")\"],\"/3BQ4J\":[\"チャンネル外のユーザーはメッセージを送信できません\"],\"/4C8U0\":[\"ãã¹ã¦ã³ãã¼\"],\"/6BzZF\":[\"メンバーリストを切り替え\"],\"/AkXyp\":[\"確èªãã¾ããï¼\"],\"/TNOPk\":[\"ユーザーは離席中です\"],\"/XQgft\":[\"探す\"],\"/cF7Rs\":[\"音量\"],\"/dqduX\":[\"次のページ\"],\"/fc3q4\":[\"すべてのコンテンツ\"],\"/kISDh\":[\"通知音を有効にする\"],\"/n04sB\":[\"Kill\"],\"/rTz0M\":[\"音声\"],\"/rfkZe\":[\"メンションやメッセージに対してサウンドを再生する\"],\"0/0ZGA\":[\"チャンネル名マスク\"],\"0D6j7U\":[\"カスタムルールについて詳しく →\"],\"0XsHcR\":[\"ユーザーをキック\"],\"0ZpE//\":[\"ユーザー数順で並び替え\"],\"0bEPwz\":[\"離席中に設定\"],\"0dGkPt\":[\"チャンネルリストを展開\"],\"0gS7M5\":[\"表示名\"],\"0kS+M8\":[\"サンプルNET\"],\"0rgoY7\":[\"自分で選んだサーバーにのみ接続します\"],\"0wdd7X\":[\"参加\"],\"0wkVYx\":[\"プライベートメッセージ\"],\"111uHX\":[\"リンクプレビュー\"],\"196EG4\":[\"プライベートチャットを削除\"],\"1DSr1i\":[\"アカウントを登録する\"],\"1O/24y\":[\"チャンネルリストを切り替え\"],\"1TNIig\":[\"開く\"],\"1VPJJ2\":[\"外部リンクの警告\"],\"1ZC/dv\":[\"未読のメンションやメッセージはありません\"],\"1pO1zi\":[\"サーバー名は必須です\"],\"1uwfzQ\":[\"チャンネルトピックを表示\"],\"268g7c\":[\"表示名を入力\"],\"2CEOW6\":[\"soju バウンサー経由でバインドされたネットワーク\"],\"2F9+AZ\":[\"ã¾ã\xA0çã®IRCãã©ãã£ãã¯ãåå¾ãã¦ãã¾ãããæ¥ç¶ãããã¡ãã»ã¼ã¸ãéä¿¡ãã¦ãã\xA0ããã\"],\"2FOFq1\":[\"ネットワーク上のサーバーオペレーターがメッセージを閲覧できる可能性があります\"],\"2FYpfJ\":[\"詳細\"],\"2HF1Y2\":[[\"inviter\"],\" が \",[\"target\"],\" を \",[\"channel\"],\" に招待しました\"],\"2I70QL\":[\"ユーザープロフィール情報を表示する\"],\"2QYdmE\":[\"ユーザー:\"],\"2QpEjG\":[\"退出しました\"],\"2YE223\":[\"#\",[\"0\"],\" へメッセージ(Enterで改行、Shift+Enterで送信)\"],\"2bimFY\":[\"サーバーパスワードを使用する\"],\"2iTmdZ\":[\"ローカルストレージ:\"],\"2odkwe\":[\"厳格 — より積極的な保護\"],\"2uDhbA\":[\"招待するユーザー名を入力\"],\"2ygf/L\":[\"← 戻る\"],\"2zEgxj\":[\"GIFを検索...\"],\"3RdPhl\":[\"チャンネル名を変更\"],\"3THokf\":[\"Voiceユーザー\"],\"3TSz9S\":[\"最小化\"],\"3jBDvM\":[\"チャンネル表示名\"],\"3ryuFU\":[\"アプリ改善のための任意のクラッシュレポート\"],\"3uBF/8\":[\"ビューアを閉じる\"],\"3uwW8F\":[\"https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"3xf8Kz\":[\"アカウント名を入力...\"],\"4/Rr0R\":[\"現在のチャンネルにユーザーを招待する\"],\"4EZrJN\":[\"ルール\"],\"4JJtW9\":[\"#overflow\"],\"4NqeT4\":[\"フラッドプロファイル (+F)\"],\"4RZQRK\":[\"今何してるの?\"],\"4hfTrB\":[\"ニックネーム\"],\"4n99LO\":[\"すでに \",[\"0\"],\" にいます\"],\"4t6vMV\":[\"短いメッセージの場合は自動的に1行入力に切り替える\"],\"4vsHmf\":[\"時間(分)\"],\"4x/Axu\":[\"バウンサーにはまだネットワークがありません。最初のネットワークを追加して始めましょう。\"],\"5+INAX\":[\"自分へのメンションを含むメッセージをハイライトする\"],\"5R5Pv/\":[\"Oper名\"],\"678PKt\":[\"ネットワーク名\"],\"6Aih4U\":[\"オフライン\"],\"6CO3WE\":[\"チャンネルに参加するために必要なパスワード。キーを削除するには空欄にしてください。\"],\"6HhMs3\":[\"退出メッセージ\"],\"6V3Ea3\":[\"コピーしました\"],\"6lGV3K\":[\"折りたたむ\"],\"6yFOEi\":[\"oper パスワードを入力...\"],\"7+IHTZ\":[\"ファイルが選択されていません\"],\"73fnil\":[\"TLS\"],\"73hrRi\":[\"nick!user@host(例:spam*!*@*、*!*@badhost.com)\"],\"7QkKyN\":[\"プライベートメッセージを送信\"],\"7U1W7c\":[\"とても緩め\"],\"7Y1YQj\":[\"本名:\"],\"7YHArF\":[\"— ビューアで開く\"],\"7fjnVl\":[\"ユーザーを検索...\"],\"7jL88x\":[\"このメッセージを削除しますか?この操作は元に戻せません。\"],\"7nGhhM\":[\"今どんな気分ですか?\"],\"7sEpu1\":[\"メンバー — \",[\"0\"]],\"7sNhEz\":[\"ユーザー名\"],\"8H0Q+x\":[\"プロファイルについて詳しく →\"],\"8Phu0A\":[\"ユーザーがニックネームを変更したときに表示する\"],\"8XTG9e\":[\"Operパスワードを入力\"],\"8XsV2J\":[\"再送信\"],\"8ZsakT\":[\"パスワード\"],\"8kR84m\":[\"外部リンクを開こうとしています:\"],\"8lCgih\":[\"ルールを削除\"],\"8o3dPc\":[\"ãã¡ã¤ã«ããããããã¦ã¢ãããã¼ã\"],\"8p/xVT\":[[\"0\",\"plural\",{\"one\":[[\"1\"]],\"other\":[[\"2\"]]}]],\"8wRzac\":[[\"joinCount\",\"plural\",{\"other\":[[\"joinCount\"],\"回 参加した\"]}]],\"9BMLnJ\":[\"サーバーに再接続\"],\"9OEgyT\":[\"リアクションを追加\"],\"9PQ8m2\":[\"G-Line(グローバルBAN)\"],\"9Qs99X\":[\"メール:\"],\"9QupBP\":[\"パターンを削除\"],\"9W7tl5\":[\"(変更なし)\"],\"9bG48P\":[\"送信中\"],\"9f5f0u\":[\"プライバシーに関するご質問はこちら:\"],\"9iweoP\":[[\"0\"],\" のネットワーク\"],\"9unqs3\":[\"退席中:\"],\"9v3hwv\":[\"サーバーが見つかりません。\"],\"9zb2WA\":[\"接続中\"],\"A1taO8\":[\"検索\"],\"A2adVi\":[\"入力中通知を送信する\"],\"A9Rhec\":[\"チャンネル名\"],\"AWOSPo\":[\"ズームイン\"],\"AXSpEQ\":[\"接続時にOperになる\"],\"AeXO77\":[\"アカウント\"],\"AhNP40\":[\"シーク\"],\"Ai2U7L\":[\"ホスト\"],\"AjBQnf\":[\"ニックネームを変更しました\"],\"AmXVh6\":[\"https://example.com/avatar.png\"],\"AnRu/j\":[\"返信をキャンセル\"],\"ApSx0O\":[\"\\\"\",[\"searchQuery\"],\"\\\"に一致するメッセージが\",[\"0\"],\"件見つかりました\"],\"AxPAXW\":[\"結果が見つかりません\"],\"AyNqAB\":[\"すべてのサーバーイベントをチャットに表示する\"],\"B/QqGw\":[\"席を外しています\"],\"B0sB2k\":[\"平文\"],\"B8AaMI\":[\"この項目は必須です\"],\"BA2c49\":[\"このサーバーは高度なLISTフィルタリングをサポートしていません\"],\"BDKt3I\":[[\"0\"],\"、\",[\"1\"],\"、\",[\"2\"],\" と他 \",[\"3\"],\" 人が入力中...\"],\"BGul2A\":[\"保存されていない変更があります。保存せずに閉じてもよいですか?\"],\"BIf9fi\":[\"ステータスメッセージ\"],\"BPm98R\":[\"ãµã¼ãã¼ã鏿ããã¦ãã¾ãããã¾ããµã¤ããã¼ãããµã¼ãã¼ã鏿ãã¦ãã\xA0ãããæå¾ ãªã³ã¯ã¯ãµã¼ãã¼ãã¨ã«ç®¡çããã¾ãã\"],\"BZz3md\":[\"個人ウェブサイト\"],\"Bgm/H7\":[\"複数行のテキスト入力を許可する\"],\"BiQIl1\":[\"このプライベートメッセージの会話をピン留めする\"],\"BlNZZ2\":[\"クリックしてメッセージに移動\"],\"Bowq3c\":[\"オペレーターのみがチャンネルトピックを変更できます\"],\"Btozzp\":[\"この画像の有効期限が切れています\"],\"Bycfjm\":[\"合計:\",[\"0\"]],\"C6IBQc\":[\"JSON全体をコピー\"],\"C9L9wL\":[\"データ収集\"],\"CDq4wC\":[\"ユーザーをモデレート\"],\"CHVRxG\":[\"@\",[\"0\"],\" へメッセージ(Shift+Enterで改行)\"],\"CN9zdR\":[\"Oper名とパスワードは必須です\"],\"CW3sYa\":[\"リアクション \",[\"emoji\"],\" を追加\"],\"CaAkqd\":[\"退出を表示\"],\"CbvaYj\":[\"ニックネームでBAN\"],\"CcK+Ft\":[\"PDF\"],\"Ce8q3L\":[\"チャンネルを選択\"],\"CsekCi\":[\"通常\"],\"D+NlUC\":[\"システム\"],\"D28t6+\":[\"参加して退出しました\"],\"DB8zMK\":[\"適用\"],\"DBcWHr\":[\"カスタム通知音ファイル\"],\"DTy9Xw\":[\"メディアプレビュー\"],\"Dj4pSr\":[\"安全なパスワードを選択してください\"],\"Du+zn+\":[\"検索中...\"],\"Du2T2f\":[\"設定が見つかりません\"],\"DwsSVQ\":[\"フィルターを適用して更新\"],\"E3W/zd\":[\"デフォルトニックネーム\"],\"E6nRW7\":[\"URLをコピー\"],\"E703RG\":[\"モード:\"],\"EAeu1Z\":[\"招待を送信\"],\"EFKJQT\":[\"設定\"],\"EGPQBv\":[\"カスタムフラッドルール (+f)\"],\"ELik0r\":[\"プライバシーポリシー全文を見る\"],\"EPbeC2\":[\"チャンネルトピックを表示または編集する\"],\"EQCDNT\":[\"oper ユーザー名を入力...\"],\"EUvulZ\":[\"\\\"\",[\"searchQuery\"],\"\\\"に一致するメッセージが1件見つかりました\"],\"EatZYJ\":[\"次の画像\"],\"EdQY6l\":[\"なし\"],\"EnqLYU\":[\"サーバーを検索...\"],\"F0OKMc\":[\"サーバーを編集\"],\"F6Int2\":[\"ハイライトを有効にする\"],\"FDoLyE\":[\"最大ユーザー数\"],\"FUU/hZ\":[\"チャットで読み込む外部メディアの量を制御します。\"],\"Fdp03t\":[\"オン\"],\"FfPWR0\":[\"モーダル\"],\"FjkaiT\":[\"ズームアウト\"],\"FlqOE9\":[\"これが意味すること:\"],\"FolHNl\":[\"アカウントと認証を管理する\"],\"Fp2Dif\":[\"サーバーを退出しました\"],\"G5KmCc\":[\"GZ-Line(グローバルZ-Line)\"],\"GDs0lz\":[\"<0>リスク:</0> 機密情報(メッセージ、プライベート会話、認証情報)が、IRCサーバー間に位置するネットワーク管理者や攻撃者に露出する可能性があります。\"],\"GR+2I3\":[\"招待マスクを追加(例:nick!*@*、*!*@host.com)\"],\"GRLyMU\":[\"ポップアウトしたサーバー通知を閉じる\"],\"GdhD7H\":[\"ããä¸åº¦ã¯ãªãã¯ãã¦ç¢ºèª\"],\"GlHnXw\":[\"ニックネームの変更に失敗しました: \",[\"error\"],\" \",[\"0\"]],\"GswZF3\":[\"プレビュー:\"],\"GtmO8/\":[\"から\"],\"GtuHUQ\":[\"サーバー上でこのチャンネルの名前を変更します。すべてのユーザーに新しい名前が表示されます。\"],\"GuGfFX\":[\"検索を切り替え\"],\"GxkJXS\":[\"アップロード中...\"],\"GzbwnK\":[\"チャンネルに参加しました\"],\"GzsUDB\":[\"拡張プロフィール\"],\"H/PnT8\":[\"絵文字を挿入\"],\"H6Izzl\":[\"お好みのカラーコード\"],\"H9jIv+\":[\"参加/退出を表示\"],\"HAKBY9\":[\"ファイルをアップロード\"],\"HdE1If\":[\"チャンネル\"],\"Hk4AW9\":[\"お好みの表示名\"],\"HmHDk7\":[\"メンバーを選択\"],\"HrQzPU\":[[\"networkName\"],\" のチャンネル\"],\"I2tXQ5\":[\"@\",[\"0\"],\" へメッセージ(Enterで改行、Shift+Enterで送信)\"],\"I6bw/h\":[\"ユーザーをBAN\"],\"I92Z+b\":[\"通知を有効にする\"],\"I9D72S\":[\"このメッセージを削除してもよいですか?この操作は元に戻せません。\"],\"IA+1wo\":[\"ユーザーがチャンネルからキックされたときに表示する\"],\"IDwkJx\":[\"IRC Operator\"],\"ILlU+s\":[\"情報:\"],\"IUwGEM\":[\"変更を保存\"],\"IVeGK6\":[[\"0\"],\"、\",[\"1\"],\"、\",[\"2\"],\" が入力中...\"],\"IgrLD/\":[\"一時停止\"],\"Im6JED\":[\"WHISPER\"],\"ImOQa9\":[\"返信\"],\"IoHMnl\":[\"最大値は \",[\"0\"],\" です\"],\"IvMj+0\":[\"Op\"],\"J28zul\":[\"接続中...\"],\"J5T9NW\":[\"ユーザー情報\"],\"J8Y5+z\":[\"おっと!ネットワーク分割が発生しました!⚠️\"],\"JBHkBA\":[\"チャンネルを退出しました\"],\"JCwL0Q\":[\"理由を入力(任意)\"],\"JFciKP\":[\"切り替え\"],\"JXGkhG\":[\"チャンネル名を変更する(オペレーターのみ)\"],\"JcD7qf\":[\"その他のアクション\"],\"JdkA+c\":[\"シークレット (+s)\"],\"Jmu12l\":[\"サーバーチャンネル\"],\"JvQ++s\":[\"Markdownを有効にする\"],\"K2jwh/\":[\"WHOISデータがありません\"],\"KAXSwC\":[\"Voice\"],\"KDfTdX\":[\"メッセージを削除\"],\"KKBlUU\":[\"埋め込み\"],\"KM0pLb\":[\"チャンネルへようこそ!\"],\"KR6W2h\":[\"無視を解除\"],\"KV+Bi1\":[\"招待制 (+i)\"],\"KdCtwE\":[\"カウンターをリセットするまでフラッドアクティビティを監視する秒数\"],\"Kkezga\":[\"サーバーパスワード\"],\"KsiQ/8\":[\"ユーザーはチャンネルに参加するために招待が必要です\"],\"L+gB/D\":[\"チャンネル情報\"],\"LC1a7n\":[\"IRCサーバーは、サーバー間リンクのセキュリティレベルが低いと報告しています。これは、ネットワーク内のIRCサーバー間でメッセージが中継される際に、適切に暗号化されていないか、SSL/TLS証明書が正しく検証されない可能性があることを意味します。\"],\"LEwpeL\":[\"soju バウンサー(制御)\"],\"LNfLR5\":[\"キックを表示\"],\"LP+1Z7\":[\"ネットワークを追加\"],\"LQb0W/\":[\"すべてのイベントを表示\"],\"LU7/yA\":[\"UI上で表示するための別名です。スペース、絵文字、特殊文字を含めることができます。実際のチャンネル名(\",[\"channelName\"],\")は引き続きIRCコマンドで使用されます。\"],\"LUb9O7\":[\"有効なサーバーポートが必要です\"],\"LV4fT6\":[\"説æï¼ä»»æãä¾ï¼ãQ3ãã¼ã¿ãã¹ã¿ã¼ãï¼\"],\"Lb+BUl\":[\"https://example.com/avatar.jpg\"],\"LcET2C\":[\"プライバシーポリシー\"],\"LcuSDR\":[\"プロフィール情報とメタデータを管理する\"],\"LqLS9B\":[\"ニックネーム変更を表示\"],\"LsDQt2\":[\"チャンネル設定\"],\"LtI9AS\":[\"オーナー\"],\"LuNhhL\":[\"このメッセージにリアクションしました\"],\"M/AZNG\":[\"アバター画像のURL\"],\"M/WIer\":[\"メッセージを送信\"],\"M8er/5\":[\"名前:\"],\"MHk+7g\":[\"前の画像\"],\"MRorGe\":[\"DMを送る\"],\"MVbSGP\":[\"時間ウィンドウ(秒)\"],\"MkpcsT\":[\"メッセージと設定はデバイス上にローカルで保存されます\"],\"MzPdC2\":[\"サーバーパスワード (PASS)\"],\"N/hDSy\":[\"ボットとしてマーク — 通常は「on」または空欄\"],\"N6j2JH\":[[\"0\"],\" を編集\"],\"N7TQbE\":[[\"channelName\"],\" にユーザーを招待\"],\"NCca/o\":[\"デフォルトニックネームを入力...\"],\"Nqs6B9\":[\"すべての外部メディアを表示します。URLが不明なサーバーへのリクエストを引き起こす場合があります。\"],\"Nt+9O7\":[\"生のTCPの代わりにWebSocketを使用する\"],\"NxIHzc\":[\"ユーザーを切断\"],\"O+v/cL\":[\"サーバー上のすべてのチャンネルを一覧表示\"],\"OCGpR4\":[\"(継承)\"],\"ODwSCk\":[\"GIFを送信\"],\"OGQ5kK\":[\"通知音とハイライトを設定する\"],\"OIPt1Z\":[\"メンバーリストのサイドバーを表示/非表示にする\"],\"OKSNq/\":[\"とても厳格\"],\"ONWvwQ\":[\"アップロード\"],\"OVKoQO\":[\"認証用のアカウントパスワード\"],\"ObsidianIRC - Bringing IRC to the future\":[\"ObsidianIRC - IRCを未来へ\"],\"OhCpra\":[\"トピックを設定…\"],\"OkltoQ\":[[\"username\"],\" をニックネームでBANする(同じニックネームでの再参加を防止)\"],\"P+t/Te\":[\"追加データなし\"],\"P42Wcc\":[\"安全\"],\"PD38l0\":[\"チャンネルアバタープレビュー\"],\"PD9mEt\":[\"メッセージを入力...\"],\"PPqfdA\":[\"チャンネル設定を開く\"],\"PSCjfZ\":[\"このチャンネルに表示されるトピックです。すべてのユーザーがトピックを閲覧できます。\"],\"PZCecv\":[\"PDFプレビュー\"],\"PeLgsC\":[[\"c\",\"plural\",{\"other\":[[\"c\"],\"回\"]}]],\"PguS2C\":[\"例外マスクを追加(例:nick!*@*、*!*@host.com)\"],\"Pil5Ty\":[[\"0\"],\" 件中 \",[\"displayedChannelsCount\"],\" 件を表示中\"],\"PqhVlJ\":[\"ユーザーをBAN(ホストマスク)\"],\"Q+chwU\":[\"ユーザー名:\"],\"Q2QY4/\":[\"ãã®æå¾ ãåé¤\"],\"Q3v9Wc\":[\"はい、削除します\"],\"Q6hhn8\":[\"設定\"],\"QF4a34\":[\"ユーザー名を入力してください\"],\"QGqSZ2\":[\"カラーと書式設定\"],\"QJQd1J\":[\"プロフィールを編集\"],\"QSzGDE\":[\"アイドル\"],\"QUlny5\":[[\"0\"],\" へようこそ!\"],\"Qoq+GP\":[\"もっと読む\"],\"QuSkCF\":[\"チャンネルをフィルター...\"],\"QwUrDZ\":[\"トピックを変更しました: \",[\"topic\"]],\"R0UH07\":[[\"1\"],\"枚中\",[\"0\"],\"枚目の画像\"],\"R7SsBE\":[\"ミュート\"],\"R8rf1X\":[\"クリックしてトピックを設定\"],\"RArB3D\":[[\"username\"],\" によって \",[\"channelName\"],\" からキックされました\"],\"RI3cWd\":[\"ObsidianIRCでIRCの世界を探索しよう\"],\"RIfHS5\":[\"æ°ããæå¾ ãªã³ã¯ã使\"],\"RMMaN5\":[\"モデレート制 (+m)\"],\"RWw9Lg\":[\"モーダルを閉じる\"],\"RZ2BuZ\":[[\"account\"],\" のアカウント登録には確認が必要です: \",[\"message\"]],\"RySp6q\":[\"コメントを非表示\"],\"S5Togi\":[\"バウンサーからネットワークを読み込み中…\"],\"SPKQTd\":[\"ニックネームは必須です\"],\"SPVjfj\":[\"空欄の場合は「理由なし」がデフォルトになります\"],\"SQKPvQ\":[\"ユーザーを招待\"],\"STmlpb\":[\"ネットワーク一覧に戻る\"],\"SkZcl+\":[\"定義済みのフラッド保護プロファイルを選択してください。これらのプロファイルは、さまざまなユースケースに対してバランスの取れた保護設定を提供します。\"],\"Slr+3C\":[\"最小ユーザー数\"],\"Spnlre\":[[\"target\"],\" を \",[\"channel\"],\" に招待しました\"],\"T/ckN5\":[\"ビューアで開く\"],\"T91vKp\":[\"再生\"],\"TV2Wdu\":[\"データの取り扱いとプライバシー保護について詳しく見る。\"],\"TgFpwD\":[\"適用中...\"],\"TkzSFB\":[\"変更なし\"],\"TtserG\":[\"本名を入力\"],\"Ttz9J1\":[\"パスワードを入力...\"],\"Tz0i8g\":[\"設定\"],\"U3pytU\":[\"管理者\"],\"UDb2YD\":[\"リアクション\"],\"UE4KO5\":[\"*channel*\"],\"UETAwW\":[\"ã¾ã\xA0æå¾ ãªã³ã¯ã使ãã¦ãã¾ãããä¸ã®ãã©ã¼ã\xA0ããæåã®æå¾ ãªã³ã¯ã使ãã¦ãã\xA0ããã\"],\"UGT5vp\":[\"設定を保存\"],\"UV5hLB\":[\"BANが見つかりません\"],\"Uaj3Nd\":[\"ステータスメッセージ\"],\"Ue3uny\":[\"デフォルト(プロファイルなし)\"],\"UkARhe\":[\"通常 — 標準的な保護\"],\"Umn7Cj\":[\"まだコメントはありません。最初のコメントを投稿しましょう!\"],\"UtUIRh\":[[\"0\"],\" 件の古いメッセージ\"],\"UwzP+U\":[\"セキュア接続\"],\"V0/A4O\":[\"チャンネルオーナー\"],\"V4qgxE\":[\"作成日時(以前、分前)\"],\"V8yTm6\":[\"検索をクリア\"],\"VJMMyz\":[\"ObsidianIRC — IRCを未来へ\"],\"VJScHU\":[\"理由\"],\"VLsmVV\":[\"通知をミュート\"],\"VbyRUy\":[\"コメント\"],\"Vmx0mQ\":[\"設定者:\"],\"VqnIZz\":[\"プライバシーポリシーとデータ取り扱い方針を見る\"],\"VrMygG\":[\"最小文字数は \",[\"0\"],\" 文字です\"],\"VrnTui\":[\"プロフィールに表示される代名詞\"],\"W8E3qn\":[\"認証済みアカウント\"],\"WAakm9\":[\"チャンネルを削除\"],\"WFxTHC\":[\"BANマスクを追加(例:nick!*@*、*!*@host.com)\"],\"WN1g9F\":[\"サーバーホストは必須です\"],\"WRYdXW\":[\"音声の再生位置\"],\"WUOH5B\":[\"ユーザーを無視\"],\"WWEXnZ\":[[\"0\",\"plural\",{\"other\":[\"さらに \",[\"1\"],\" 件表示\"]}]],\"WYxRzo\":[\"æå¾ ãªã³ã¯ã使ã»ç®¡çãã\"],\"Wd38W1\":[\"ãã£ã³ãã«ã空æ¬ã«ããã¨ãæ±ç¨ã®ãããã¯ã¼ã¯æå¾ ã«ãªãã¾ãã説æã¯ããªãå°ç¨ã®è¨é²ã§ããã®ãªã¹ãã§èªåã«ã®ã¿è¡¨ç¤ºããã¾ãã\"],\"Weq9zb\":[\"一般\"],\"Wfj7Sk\":[\"通知音をミュートまたはミュート解除する\"],\"Wm7gbG\":[\"GitHub:\"],\"WyeHWY\":[\"*spam*\"],\"WzMCru\":[\"ユーザープロフィール\"],\"X6S3lt\":[\"設定、チャンネル、サーバーを検索...\"],\"XEHan5\":[\"このまま続行\"],\"XI1+wb\":[\"無効な形式\"],\"XIXeuC\":[\"@\",[\"0\"],\" へメッセージ\"],\"XMS+k4\":[\"プライベートメッセージを開始\"],\"XWgxXq\":[\"アルバム\"],\"Xd7+IT\":[\"プライベートチャットのピン留めを解除\"],\"Xm/s+u\":[\"表示\"],\"Xp2n93\":[\"サーバーの信頼済みファイルホストからのメディアを表示します。外部サービスへのリクエストは発生しません。\"],\"XvjC4F\":[\"保存中...\"],\"Y/qryO\":[\"検索に一致するユーザーが見つかりません\"],\"YAqRpI\":[[\"account\"],\" のアカウント登録に成功しました: \",[\"message\"]],\"YEfzvP\":[\"トピック保護 (+t)\"],\"YQOn6a\":[\"メンバーリストを折りたたむ\"],\"YRCoE9\":[\"チャンネルオペレーター\"],\"YURQaF\":[\"プロフィールを表示\"],\"YdBSvr\":[\"メディア表示と外部コンテンツを制御する\"],\"Yj6U3V\":[\"中央サーバーなし:\"],\"YjvpGx\":[\"代名詞\"],\"YqH4l4\":[\"キーなし\"],\"YyUPpV\":[\"アカウント:\"],\"ZJSWfw\":[\"サーバーから切断したときに表示されるメッセージ\"],\"ZR1dJ4\":[\"招待\"],\"ZdWg0V\":[\"ブラウザで開く\"],\"ZhRBbl\":[\"メッセージを検索…\"],\"Zmcu3y\":[\"詳細フィルター\"],\"a2/8e5\":[\"トピック設定日時(以降、分前)\"],\"aHKcKc\":[\"前のページ\"],\"aJTbXX\":[\"Operパスワード\"],\"aQryQv\":[\"パターンはすでに存在します\"],\"aW9pLN\":[\"チャンネルに参加できる最大ユーザー数。制限なしの場合は空欄にしてください。\"],\"ah4fmZ\":[\"YouTube、Vimeo、SoundCloudなどの既知のサービスのプレビューも表示します。\"],\"aifXak\":[\"このチャンネルにはメディアがありません\"],\"ap2zBz\":[\"緩め\"],\"az8lvo\":[\"オフ\"],\"azXSNo\":[\"メンバーリストを展開\"],\"azdliB\":[\"アカウントにログイン\"],\"b26wlF\":[\"彼女/彼女の\"],\"bD/+Ei\":[\"厳格\"],\"bQ6BJn\":[\"詳細なフラッド保護ルールを設定します。各ルールは、監視するアクティビティの種類と、しきい値を超えた場合に実行するアクションを指定します。\"],\"beV7+y\":[\"ユーザーは \",[\"channelName\"],\" への参加招待を受け取ります。\"],\"bk84cH\":[\"離席メッセージ\"],\"bkHdLj\":[\"IRCサーバーを追加\"],\"bmQLn5\":[\"ルールを追加\"],\"bv4cFj\":[\"トランスポート\"],\"bwRvnp\":[\"アクション\"],\"c8+EVZ\":[\"認証済みアカウント\"],\"cGYUlD\":[\"メディアプレビューは読み込まれません。\"],\"cLF98o\":[\"コメントを表示 (\",[\"commentCount\"],\")\"],\"cLKIDO\":[\"利用可能なユーザーがいません\"],\"cSgpoS\":[\"プライベートチャットをピン留め\"],\"cde3ce\":[\"<0>\",[\"0\"],\"</0> へメッセージ\"],\"chQsxg\":[\"フォーマット済み出力をコピー\"],\"cl/A5J\":[[\"__DEFAULT_IRC_SERVER_NAME__\"],\" へようこそ!\"],\"cnGeoo\":[\"削除\"],\"coPLXT\":[\"IRC通信はサーバーに保存されません\"],\"crYH/6\":[\"SoundCloudプレーヤー\"],\"cv5DQb\":[\"ホスト未設定\"],\"d3sis4\":[\"サーバーを追加\"],\"d9aN5k\":[[\"username\"],\" をチャンネルから削除する\"],\"dEgA5A\":[\"キャンセル\"],\"dGi1We\":[\"このプライベートメッセージの会話のピン留めを解除する\"],\"dJVuyC\":[[\"channelName\"],\" を退出しました (\",[\"reason\"],\")\"],\"dMtLDE\":[\"宛て\"],\"dXqxlh\":[\"<0>⚠️ セキュリティリスク!</0> この接続は傍受や中間者攻撃に対して脆弱な可能性があります。\"],\"da9Q/R\":[\"チャンネルモードを変更しました\"],\"dhJN3N\":[\"コメントを表示\"],\"dj2xTE\":[\"通知を閉じる\"],\"dpCzmC\":[\"フラッド保護設定\"],\"e9dQpT\":[\"このリンクを新しいタブで開きますか?\"],\"ePK91l\":[\"編集\"],\"eYBDuB\":[\"画像をアップロードするか、動的サイズ変換のための \",[\"size\"],\" 置換を含むURLを入力してください\"],\"edBbee\":[[\"username\"],\" をホストマスクでBANする(同じIP/ホストからの再参加を防止)\"],\"ekfzWq\":[\"ユーザー設定\"],\"elPDWs\":[\"IRCクライアントの使い心地をカスタマイズする\"],\"eu2osY\":[\"<0>💡 推奨事項:</0> このサーバーを信頼し、リスクを理解している場合のみ続行してください。この接続で機密情報やパスワードを共有しないようにしてください。\"],\"euEhbr\":[\"クリックして \",[\"channel\"],\" に参加\"],\"ez3vLd\":[\"複数行入力を有効にする\"],\"f0J5Ki\":[\"サーバー間通信に暗号化されていない接続が使用される可能性があります\"],\"f9BHJk\":[\"ユーザーに警告\"],\"fDOLLd\":[\"チャンネルが見つかりません。\"],\"ffzDkB\":[\"匿名分析:\"],\"fq1GF9\":[\"ユーザーがサーバーから切断したときに表示\"],\"gEF57C\":[\"このサーバーは1種類の接続タイプのみサポートしています\"],\"gJuLUI\":[\"無視リスト\"],\"gNzMrk\":[\"現在のアバター\"],\"gjPWyO\":[\"ニックネームを入力...\"],\"gz6UQ3\":[\"最大化\"],\"h6/IMX\":[\"最初のネットワークを追加\"],\"h6razj\":[\"チャンネル名マスクを除外\"],\"hG6jnw\":[\"トピックが設定されていません\"],\"hG89Ed\":[\"画像\"],\"hYgDIe\":[\"使\"],\"hZ6znB\":[\"ポート\"],\"ha+Bz5\":[\"例:100:1440\"],\"he3ygx\":[\"ã³ãã¼\"],\"hehnjM\":[\"数量\"],\"hzdLuQ\":[\"Voice以上の権限を持つユーザーのみ発言できます\"],\"i0qMbr\":[\"ホーム\"],\"iDNBZe\":[\"通知\"],\"iH8pgl\":[\"戻る\"],\"iL9SZg\":[\"ユーザーをBAN(ニックネーム)\"],\"iNt+3c\":[\"画像に戻る\"],\"iQvi+a\":[\"このサーバーのリンクセキュリティが低い場合に警告しない\"],\"iSLIjg\":[\"接続\"],\"iWXkHH\":[\"Halfop\"],\"iZeTtp\":[\"サーバーホスト\"],\"idD8Ev\":[\"保存済み\"],\"iivqkW\":[\"サインイン日時\"],\"ij+Elv\":[\"画像プレビュー\"],\"ilIWp7\":[\"通知を切り替え\"],\"iuaqvB\":[\"ワイルドカードには * を使用してください。例:baduser!*@*、*!*@spammer.com、troll*!*@*\"],\"ixkTse\":[\"ボット\"],\"j2DGR0\":[\"ホストマスクでBAN\"],\"jA4uoI\":[\"トピック:\"],\"jLXxGK\":[\"https://example.com\"],\"jPSk57\":[\"理由(任意)\"],\"jUV7CU\":[\"アバターをアップロード\"],\"jW5Uwh\":[\"外部メディアの読み込み範囲を制御します。オフ/安全/信頼できるソース/すべてのコンテンツ。\"],\"jXzms5\":[\"添付オプション\"],\"jZlrte\":[\"カラー\"],\"jfC/xh\":[\"連絡先\"],\"jywMpv\":[\"#new-channel-name\"],\"k112DD\":[\"古いメッセージを読み込む\"],\"k3ID0F\":[\"メンバーをフィルター…\"],\"k65gsE\":[\"詳細を見る\"],\"k7Zgob\":[\"接続をキャンセル\"],\"kAVx5h\":[\"招待が見つかりません\"],\"kCLEPU\":[\"接続先\"],\"kF5LKb\":[\"無視パターン:\"],\"kGeOx/\":[[\"0\"],\" に参加\"],\"kITKr8\":[\"チャンネルモードを読み込み中...\"],\"kPpPsw\":[\"あなたはIRC Operatorです\"],\"kWJmRL\":[\"あなた\"],\"kfcRb0\":[\"アバター\"],\"kjMqSj\":[\"JSONをコピー\"],\"krViRy\":[\"JSONとしてコピーするにはクリック\"],\"ks71ra\":[\"例外\"],\"kw4lRv\":[\"チャンネルハーフオペレーター\"],\"kxgIRq\":[\"チャンネルを選択または追加して始めましょう。\"],\"ky6dWe\":[\"アバタープレビュー\"],\"l+GxCv\":[\"チャンネルを読み込み中...\"],\"l+IUVW\":[[\"account\"],\" のアカウント確認に成功しました: \",[\"message\"]],\"l/siQz\":[[\"reconnectCount\",\"plural\",{\"other\":[[\"reconnectCount\"],\"回 再接続した\"]}]],\"l1l8sj\":[[\"0\"],\"æ¥å\"],\"l5NhnV\":[\"#ãã£ã³ãã«ï¼ä»»æï¼\"],\"l5jmzx\":[[\"0\"],\" と \",[\"1\"],\" が入力中...\"],\"lCF0wC\":[\"æ´æ°\"],\"lHy8N5\":[\"さらにチャンネルを読み込み中...\"],\"lasgrr\":[\"ä½¿ç¨æ¸ã¿\"],\"lbpf14\":[[\"value\"],\"に参加\"],\"lfFsZ4\":[\"チャンネル\"],\"lkNdiH\":[\"アカウント名\"],\"ln500L\":[\"ObsidianIRC\"],\"lnCMdg\":[\"画像をアップロード\"],\"loQxaJ\":[\"戻りました\"],\"lvfaxv\":[\"ホーム\"],\"m0oxpP\":[\"Libera Chat\"],\"m16xKo\":[\"追加\"],\"m8flAk\":[\"プレビュー(未アップロード)\"],\"mEPxTp\":[\"<0>⚠️ 注意!</0> 信頼できる送信元のリンクのみ開いてください。悪意のあるリンクはセキュリティやプライバシーを侵害する恐れがあります。\"],\"mHGdhG\":[\"サーバー情報\"],\"mHS8lb\":[\"#\",[\"0\"],\" へメッセージ\"],\"mIibu3\":[\"標準ではない WebSocket エンドポイント (例: リバースプロキシ越しの soju) の場合は、パスを含む完全な URL を入力してください: <0>wss://host:port/socket</0>。\"],\"mMYBD9\":[\"広め — より広範な保護範囲\"],\"mTGsPd\":[\"チャンネルトピック\"],\"mU8j6O\":[\"外部メッセージ禁止 (+n)\"],\"mZp8FL\":[\"自動的に1行入力に切り替え\"],\"mdQu8G\":[\"YourNickname\"],\"miSSBQ\":[\"コメント (\",[\"commentCount\"],\")\"],\"mvyLSy\":[\"ユーザーは認証済みです\"],\"mwtcGl\":[\"コメントを閉じる\"],\"myL0MR\":[\"このネットワークを削除しますか?\"],\"mzI/c+\":[\"ダウンロード\"],\"n3fGRk\":[[\"0\"],\" が設定\"],\"nE9jsU\":[\"緩め — 控えめな保護\"],\"nNflMD\":[\"チャンネルを退出\"],\"nPXkBi\":[\"WHOISデータを読み込み中...\"],\"nQnxxF\":[\"#\",[\"0\"],\" へメッセージ(Shift+Enterで改行)\"],\"nWMRxa\":[\"ピン留めを解除\"],\"nkC032\":[\"フラッドプロファイルなし\"],\"o69z4d\":[[\"username\"],\" に警告メッセージを送る\"],\"o9ylQi\":[\"GIFを検索して始めましょう\"],\"oFGkER\":[\"サーバー通知\"],\"oOi11l\":[\"最下部にスクロール\"],\"oPYIL5\":[\"ãããã¯ã¼ã¯\"],\"oQEzQR\":[\"新しいDM\"],\"oXOSPE\":[\"オンライン\"],\"oal760\":[\"サーバーリンクへの中間者攻撃が可能な状態です\"],\"oeqmmJ\":[\"信頼できるソース\"],\"optX0N\":[[\"0\"],\"æéå\"],\"ovBPCi\":[\"デフォルト\"],\"p0Z69r\":[\"パターンを空にすることはできません\"],\"p1KgtK\":[\"音声の読み込みに失敗しました\"],\"p59pEv\":[\"詳細情報\"],\"p7sRI6\":[\"入力中であることを他のユーザーに知らせる\"],\"pBm1od\":[\"シークレットチャンネル\"],\"pNmiXx\":[\"すべてのサーバーで使用するデフォルトのニックネーム\"],\"pUUo9G\":[\"ホスト名:\"],\"pVGPmz\":[\"アカウントパスワード\"],\"peNE68\":[\"永続的\"],\"plhHQt\":[\"データなし\"],\"pm6+q5\":[\"セキュリティ警告\"],\"pn5qSs\":[\"追加情報\"],\"q0cR4S\":[\"は現在 **\",[\"newNick\"],\"** として知られています\"],\"qFcunY\":[\"LIST または NAMES コマンドにチャンネルが表示されません\"],\"qLpTm/\":[\"リアクション \",[\"emoji\"],\" を削除\"],\"qVkGWK\":[\"ピン留め\"],\"qY8wNa\":[\"ホームページ\"],\"qb0xJ7\":[\"ワイルドカードを使用してください:* は任意の文字列、? は任意の1文字に一致します。例:nick!*@*、*!*@host.com、*!*user@*\"],\"qhzpRq\":[\"チャンネルキー (+k)\"],\"qtoOYG\":[\"制限なし\"],\"r1W2AS\":[\"ファイルホスト画像\"],\"rIPR2O\":[\"トピック設定日時(以前、分前)\"],\"rMMSYo\":[\"最大文字数は \",[\"0\"],\" 文字です\"],\"rWtzQe\":[\"ネットワークが分割され、再接続されました。✅\"],\"rYG2u6\":[\"しばらくお待ちください...\"],\"rdUucN\":[\"プレビュー\"],\"rjGI/Q\":[\"プライバシー\"],\"rk8iDX\":[\"GIFを読み込み中...\"],\"rn6SBY\":[\"ミュート解除\"],\"s/UKqq\":[\"チャンネルからキックされました\"],\"s7oqXR\":[\"ネットワークを選択\"],\"s8cATI\":[[\"channelName\"],\" に参加しました\"],\"sCO9ue\":[\"<0>\",[\"serverName\"],\"</0> への接続には次のセキュリティ上の懸念事項があります:\"],\"sGH11W\":[\"サーバー\"],\"sHI1H+\":[\"は現在 **\",[\"newNick\"],\"** として知られています\"],\"sJyV04\":[[\"inviter\"],\" があなたを \",[\"channel\"],\" に招待しました\"],\"sUBSbK\":[\"アップストリームネットワークがまだありません。\"],\"sby+1/\":[\"クリックしてコピー\"],\"sfN25C\":[\"本名またはフルネーム\"],\"sliuzR\":[\"リンクを開く\"],\"sqrO9R\":[\"カスタムメンション\"],\"sr6RdJ\":[\"Shift+Enterで複数行入力\"],\"swrCpB\":[[\"user\"],\" がチャンネルを \",[\"oldName\"],\" から \",[\"newName\"],\" に変更しました\",[\"0\"]],\"sxkWRg\":[\"詳細設定\"],\"t/YqKh\":[\"削除\"],\"t47eHD\":[\"このサーバーでの固有識別子\"],\"tAkAh0\":[\"動的サイズ変換のための \",[\"size\"],\" 置換を含むURL。例:https://example.com/avatar/\",[\"size\"],\"/channel.jpg\"],\"tXLJS3\":[\"チャンネルリストのサイドバーを表示/非表示にする\"],\"tfDRzk\":[\"保存\"],\"tiBsJk\":[[\"channelName\"],\" を退出しました\"],\"tt4/UD\":[\"退出しました (\",[\"reason\"],\")\"],\"u0TcnO\":[\"ニックネーム {nick} は既に使用中です。{newNick} で再試行します\"],\"u0a8B4\":[\"管理者アクセスのためにIRC Operatorとして認証する\"],\"u0rWFU\":[\"作成日時(以降、分前)\"],\"u72w3t\":[\"無視するユーザーとパターン\"],\"u7jc2L\":[\"退出しました\"],\"uAQUqI\":[\"ステータス\"],\"uB85T3\":[\"保存失敗:\",[\"msg\"]],\"uV3DOL\":[\"G-Line\"],\"uW3lLI\":[\"IRCサーバー:\"],\"ukyW4o\":[\"ããªãã®æå¾ ãªã³ã¯\"],\"usSSr/\":[\"ズームレベル\"],\"v7uvcf\":[\"ソフトウェア:\"],\"vE8kb+\":[\"Shift+Enterで改行(Enterで送信)\"],\"vERlcd\":[\"プロフィール\"],\"vK0RL8\":[\"トピックなし\"],\"vSJd18\":[\"動画\"],\"vXIe7J\":[\"言語\"],\"vaHYxN\":[\"本名\"],\"vhjbKr\":[\"離席中\"],\"w/nogd\":[[\"0\"],\" ネットワーク\",[\"1\"],\" — 参加するものを選択\"],\"w4NYox\":[[\"title\"],\" クライアント\"],\"w8xQRx\":[\"無効な値\"],\"wFjjxZ\":[[\"username\"],\" によって \",[\"channelName\"],\" からキックされました (\",[\"reason\"],\")\"],\"wGjaGl\":[\"BAN例外が見つかりません\"],\"wPrGnM\":[\"チャンネル管理者\"],\"wRkP2d\":[\"GIF\"],\"wbm86v\":[\"ユーザーがチャンネルに参加・退出したときに表示する\"],\"whqZ9r\":[\"ハイライトする追加の単語またはフレーズ\"],\"wm7RV4\":[\"通知音\"],\"wz/Yoq\":[\"サーバー間で中継される際にメッセージが傍受される可能性があります\"],\"x3+y8b\":[\"ãã®ãªã³ã¯ããç»é²ãã人æ°\"],\"xCJdfg\":[\"クリア\"],\"xOTzt5\":[\"ãã£ãä»\"],\"xUHRTR\":[\"接続時に自動的にOperatorとして認証する\"],\"xWHwwQ\":[\"BAN一覧\"],\"xYilR2\":[\"メディア\"],\"xbi8D6\":[\"ãã®ãµã¼ãã¼ã¯æå¾ ãªã³ã¯ã«å¯¾å¿ãã¦ãã¾ããï¼<0>obby.world/invitation</0>ã±ã¤ãããªãã£ãåç¥ããã¦ãã¾ããï¼ãé常éããã£ããã¯å©ç¨ã§ãã¾ãããã®ããã«ã¯obbyircdãã¼ã¹ã®ãããã¯ã¼ã¯ç¨ã§ãã\"],\"xceQrO\":[\"安全なWebSocketのみサポートされています\"],\"xdtXa+\":[\"チャンネル名\"],\"xfXC7q\":[\"テキストチャンネル\"],\"xlCYOE\":[\"メッセージを取得中...\"],\"xlhswE\":[\"最小値は \",[\"0\"],\" です\"],\"xq97Ci\":[\"単語またはフレーズを追加...\"],\"xuRqRq\":[\"クライアント制限 (+l)\"],\"xwF+7J\":[[\"0\"],\" が入力中...\"],\"y1eoq1\":[\"ãªã³ã¯ãã³ãã¼\"],\"yJztBY\":[\"ネットワークを削除\"],\"yNeucF\":[\"このサーバーは拡張プロフィールメタデータ(IRCv3 METADATA拡張)をサポートしていません。アバター、表示名、ステータスなどの追加フィールドは利用できません。\"],\"yPlrca\":[\"チャンネルアバター\"],\"yQE2r9\":[\"読み込み中\"],\"ySU+JY\":[\"your@email.com\"],\"yTX1Rt\":[\"Operユーザー名\"],\"yYOzWD\":[\"ログ\"],\"yfx9Re\":[\"IRC Operatorパスワード\"],\"ygCKqB\":[\"停止\"],\"ymDxJx\":[\"IRC Operatorユーザー名\"],\"yrpRsQ\":[\"名前順で並び替え\"],\"yz7wBu\":[\"閉じる\"],\"zJw+jA\":[\"モードを設定: \",[\"0\"]],\"zbymaY\":[[\"0\"],\"åå\"],\"zebeLu\":[\"Operユーザー名を入力\"],\"zpr0Bw\":[\"GZ-Line\"]}"); No newline at end of file | |||
There was a problem hiding this comment.
Regenerate this Japanese catalog: payload contains widespread mojibake.
Many entries are corrupted (ã..., ç..., broken escapes), so Japanese UI text will render unreadable at runtime. Rebuild src/locales/ja/messages.mjs from a valid UTF-8 source catalog and ensure the compile/write step preserves UTF-8 end-to-end.
🤖 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/locales/ja/messages.mjs` at line 1, The messages export contains
widespread mojibake due to an encoding/serialization issue; regenerate the
Japanese catalog from a clean UTF-8 source and replace the current JSON payload
in the messages constant so all strings are valid Japanese (no sequences like
"ã..." or broken escapes). Rebuild the localization generation/compile step that
produces the messages variable and ensure the pipeline writes the output file
using UTF-8 (verify JSON.stringify output or your i18n tool emits proper Unicode
and the writeFile/writeStream uses 'utf8'), then run the same JSON.parse(...)
usage as before to confirm the file loads correctly at runtime.
…essions You joined #linux on Libera (a soju-bouncer-bound child) and that channel got persisted onto the soju control row -- because the savedServer lookup in joinChannel keys on host:port, and the control session, every bound child, and any sibling network all share the same soju endpoint. On reconnect every one of them tried to JOIN #linux, including the control connection which doesn't have channels of its own. soju already remembers per-network channel state and replays the JOINs after BIND, so client-side persistence is at best redundant and at worst destructive. Gate every channel-state write on `!(isBouncerControl || bouncerNetid)`: * joinChannel: skip the savedServer.channels.push for bouncer sessions * leaveChannel: skip the savedServer.channels = … reassignment * reorderChannels: keep the per-server channelOrder write (keyed by serverId, no ambiguity) but skip the savedServer.channels write * connection.ts ready handler: skip the for-each saved-channel joinChannel() replay for bouncer sessions Standalone (non-bouncer) servers continue to persist + replay as before.
Rationale moves to commit messages; source keeps only what helps a reader who walks in cold (invariants, edge cases). Per repo convention I kept ignoring in earlier commits. No behaviour change.
…trol
Disconnecting the soju control session now opens an in-app modal
(not window.confirm) listing the bound networks that will close
alongside it, then drops the parent and every child in one action.
* New UI slot ui.disconnectConfirmTarget; requestDeleteServer() routes
through it for bouncer parents, calls deleteServer() directly
otherwise.
* deleteServer() collects {parentId, ...children with bouncerServerId=parent}
up-front, then removes them from ircClient, savedServers, metadata,
and in-memory state in one set().
* BouncerDisconnectConfirmModal renders shotglass+crown header, lists
each child as a network card (shotglass+plug chip, friendly name,
host), and confirm/cancel buttons.
* ServerList's onDelete now calls requestDeleteServer.
* deleteServerBouncer test updated: parent delete is now expected to
cascade (was previously expected to leave children behind, when
there was no UI path to that anyway).
* 4 strings × 18 locales translated.
…ss badge
* Propagate ISUPPORT NETWORK from ircClient into the Zustand Server
row (set Server.networkName) so the channel-list header can
resolve the upstream identity ("Libera.Chat") instead of falling
back to the seeded child name ("soju/1") when the bouncer's
NETWORK attribute is missing.
* ChannelList header gets the shotglass+crown chip on the control
session and shotglass+plug on every bound child, matching the
sidebar tile badge. Title remains networkName || name.
Part 1 of 3 for soju.im/bouncer-networks support. Headless — no UI yet.
What this PR is
Implements the protocol parser and store reducer for soju.im/bouncer-networks. Lays the foundation for a single bouncer endpoint to expose multiple upstream networks to one ObsidianIRC client.
Scope
key=value;…with\\s \\: \\\\ \\r \\nescapes)BOUNCERcommand dispatch +FAIL BOUNCERper-code decodingBouncerNetwork/BouncerStatetypesBOUNCER_NETID, BATCH-end listed flag, network merge / delete / FAIL stashing)bouncersslice +bouncerListNetworks / Add / Change / DelactionsbouncerListNetworks / bouncerBind / bouncerAddNetwork / bouncerChangeNetwork / bouncerDelNetworksoju.im/bouncer-networks+-notifyWhat's deliberately NOT in this PR
BOUNCER BINDlifecycle — comes in PR 2. We don't yet open one child connection per upstream network.Test plan
npm run format,npm run check,npm run test(812 pass / 1 skipped),npm run buildcleanattr=with empty value), repeated semicolons, unknown-escape pass-throughBOUNCER NETWORK / ADD / CHANGE / DEL, case-insensitive subcommand,*deletion marker, batch-tag carry-throughFAIL BOUNCERper-code decoding (INVALID_NETID,INVALID_ATTRIBUTE,NEED_ATTRIBUTE, unknown codes)ISUPPORT BOUNCER_NETID, batch-endlistedflip,lastErrorcaptureSummary by CodeRabbit
New Features
Bug Fixes
Localization
Tests