From b5fd5e2dc53ef20da3edf65a6b8ceb9d9196930b Mon Sep 17 00:00:00 2001 From: Zallom Date: Fri, 22 May 2026 15:23:26 +0200 Subject: [PATCH 1/2] feat(translations): EN/DE/ES/PT pour landing, thank-you, frame/cta + Hero/Servers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Composants partagés Hero, Servers, frame/cta refactorés avec (traductions dans i18n/{locale}/code.json, 13 nouvelles clés × 5 locales). Overlays par locale pour les pages longues : - i18n/{en,de,es,pt}/docusaurus-plugin-content-pages/index.tsx (landing) - i18n/{en,de,es,pt}/docusaurus-plugin-content-pages/thank-you.tsx (avec PERMISSION_MESSAGES traduites via les libellés Discord officiels de chaque locale) Sources Webflow utilisées pour EN/DE/ES quand disponibles ; PT et la section Pricing (absente côté EN/DE/ES Webflow) traduits depuis FR. Le frame/cta-pt.html Webflow contenait du contenu espagnol — PT ré-traduit proprement en portugais européen. Fix incident : le dropdown langue de thank-you utilisait qui auto-préfixait la locale (/fr/en/thank-you), passé en . Build confirmé FR + EN. --- i18n/de/code.json | 52 +++ .../docusaurus-plugin-content-pages/index.tsx | 435 ++++++++++++++++++ .../thank-you.tsx | 400 ++++++++++++++++ i18n/en/code.json | 52 +++ .../docusaurus-plugin-content-pages/index.tsx | 433 +++++++++++++++++ .../thank-you.tsx | 398 ++++++++++++++++ i18n/es/code.json | 52 +++ .../docusaurus-plugin-content-pages/index.tsx | 433 +++++++++++++++++ .../thank-you.tsx | 398 ++++++++++++++++ i18n/fr/code.json | 52 +++ i18n/pt/code.json | 52 +++ .../docusaurus-plugin-content-pages/index.tsx | 435 ++++++++++++++++++ .../thank-you.tsx | 398 ++++++++++++++++ src/components/landing/Hero/index.tsx | 61 ++- src/components/landing/Servers/index.tsx | 80 +++- src/pages/frame/cta.tsx | 39 +- src/pages/thank-you.tsx | 22 +- 17 files changed, 3746 insertions(+), 46 deletions(-) create mode 100644 i18n/de/docusaurus-plugin-content-pages/index.tsx create mode 100644 i18n/de/docusaurus-plugin-content-pages/thank-you.tsx create mode 100644 i18n/en/docusaurus-plugin-content-pages/index.tsx create mode 100644 i18n/en/docusaurus-plugin-content-pages/thank-you.tsx create mode 100644 i18n/es/docusaurus-plugin-content-pages/index.tsx create mode 100644 i18n/es/docusaurus-plugin-content-pages/thank-you.tsx create mode 100644 i18n/pt/docusaurus-plugin-content-pages/index.tsx create mode 100644 i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx diff --git a/i18n/de/code.json b/i18n/de/code.json index 1d1a999..8b243cf 100644 --- a/i18n/de/code.json +++ b/i18n/de/code.json @@ -415,5 +415,57 @@ }, "editor.preview.title": { "message": "Vorschau" + }, + "hero.preTitle": { + "message": "Von über {count} Servern verwendet", + "description": "Hero pre-title displayed above the main title; {count} is a locale-formatted number of servers" + }, + "hero.title": { + "message": "Der beste {highlight} Bot für Discord", + "description": "Hero main title; {highlight} renders the gradient-highlighted word" + }, + "hero.title.highlight": { + "message": "Sicherheits", + "description": "The highlighted word inside the hero title (security)" + }, + "hero.description": { + "message": "Schützen Sie Ihren Discord-Server vor böswilligen Nutzern und verhindern Sie Schaden, bevor er entsteht.", + "description": "Hero description below the title" + }, + "hero.cta.primary": { + "message": "Zu Discord hinzufügen", + "description": "Primary CTA: invite the bot to Discord" + }, + "hero.cta.secondary": { + "message": "Funktionen anzeigen", + "description": "Secondary CTA: scroll to the features section" + }, + "servers.title": { + "message": "Wir schützen die Größten", + "description": "Servers marquee title: 'We protect the biggest'" + }, + "servers.memberCount": { + "message": "{count} Mitglieder", + "description": "Server card: number of members; {count} is locale-formatted" + }, + "frame.cta.title": { + "message": "Einen Schritt {highlight} bleiben", + "description": "Frame CTA title; {highlight} renders the gradient-highlighted word" + }, + "frame.cta.title.highlight": { + "message": "voraus", + "description": "Highlighted word inside the frame CTA title" + }, + "frame.cta.description": { + "message": "Fügen Sie RaidProtect hinzu und beginnen Sie noch heute damit, Ihren Server zu schützen.", + "description": "Frame CTA description" + }, + "frame.cta.primary": { + "message": "Bot hinzufügen", + "description": "Frame CTA primary button: add the bot to Discord" + }, + "frame.cta.secondary": { + "message": "Server beitreten", + "description": "Frame CTA secondary button: join the Discord server" } } diff --git a/i18n/de/docusaurus-plugin-content-pages/index.tsx b/i18n/de/docusaurus-plugin-content-pages/index.tsx new file mode 100644 index 0000000..3663819 --- /dev/null +++ b/i18n/de/docusaurus-plugin-content-pages/index.tsx @@ -0,0 +1,435 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import clsx from 'clsx'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import Hero from '@site/src/components/landing/Hero'; +import Servers from '@site/src/components/landing/Servers'; +import shared from '@site/src/components/landing/styles/shared.module.css'; +import styles from '@site/src/pages/index.module.css'; + +type Counts = { + servers: number; + users: number; + captcha: number; + antispam: number; +}; + +type FormattedValue = { + value: string; + unit: string; +}; + +function formatValue(value: number): FormattedValue { + if (value >= 1_000_000) { + return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; + } + return {value: (value / 1_000).toFixed(1), unit: 'k'}; +} + +function StatCounter({ + rawValue, + label, + fallback, +}: { + rawValue?: number; + label: string; + fallback: string; +}) { + if (rawValue == null) { + return ( +
+
+ {fallback} +
+
{label}
+
+ ); + } + const formatted = formatValue(rawValue); + return ( +
+
+ {formatted.value} + {formatted.unit} +
+
{label}
+
+ ); +} + +const CHECK_ICON = '/img/landing/icon-02.svg'; + +function FeatureItem({children}: {children: ReactNode}) { + return ( +
+ +
{children}
+
+ ); +} + +type Feature = { + to: string; + icon: string; + iconAlt: string; + title: string; + description: string; +}; + +const FEATURES: Feature[] = [ + { + to: '/docs/features/anti-spam', + icon: '/img/landing/iconAntispamWhite.svg', + iconAlt: 'RaidProtect icon Antispam', + title: 'Anti-Spam-Schutz', + description: + 'Sanktionieren Sie Spam-Versuche sofort, ganz ohne Ihr Zutun.', + }, + { + to: '/docs/features/raid-mode', + icon: '/img/landing/iconAntiraidWhite.svg', + iconAlt: 'RaidProtect icon Antiraid', + title: 'Raid-Schutz', + description: + 'Fürchten Sie einen Raid? Unser Bot erkennt und blockiert ihn, noch bevor er Ihren Server beeinträchtigt.', + }, + { + to: '/docs/features/captcha', + icon: '/img/landing/iconCaptchaWhite.svg', + iconAlt: 'RaidProtect icon Captcha', + title: 'Bot-Schutz durch Captcha', + description: + 'Dank Captcha müssen neue Mitglieder beweisen, dass sie menschlich sind. Verabschieden Sie sich von automatisierten Accounts.', + }, + { + to: '/docs/features/utilities', + icon: '/img/landing/iconReportWhite.svg', + iconAlt: 'RaidProtect icon Report', + title: 'Moderation & Verwaltung', + description: + 'Verwalten Sie Ihren Server wie ein Profi mit unseren umfassenden Moderations- und Verwaltungsfunktionen.', + }, + { + to: '/docs/features/tag-role', + icon: '/img/landing/iconTagWhite.svg', + iconAlt: 'RaidProtect icon Tag', + title: 'Tag-Rolle', + description: + 'Die Tag-Rolle weist Mitgliedern automatisch eine Rolle zu, wenn sie den Tag Ihres Servers hinzufügen.', + }, + { + to: '/docs/features/dm-lock', + icon: '/img/landing/iconDmlockWhite.svg', + iconAlt: 'RaidProtect icon DM Lock', + title: 'Privatnachrichten sperren', + description: + 'Ein einzigartiger Schutzschild gegen Spam, Betrug und Scam in Direktnachrichten.', + }, +]; + +export default function Home(): ReactNode { + const [counts, setCounts] = useState(null); + + useEffect(() => { + let cancelled = false; + fetch('https://docs.raidprotect.bot/counts.json') + .then((res) => { + if (!res.ok) throw new Error('Failed to fetch counts'); + return res.json(); + }) + .then((data: Counts) => { + if (!cancelled) setCounts(data); + }) + .catch((err) => { + // Stats are best-effort; failure is non-blocking + // eslint-disable-next-line no-console + console.error('Stats update error:', err); + }); + return () => { + cancelled = true; + }; + }, []); + + return ( + +
+ + + + {/* About */} +
+
+
+

+ Unsere{' '} + Ergebnisse sprechen + für sich +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+
+
+ + {/* Features */} +
+
+
+

+ Unsere Funktionen +

+

+ Erfahren Sie, warum wir zu den besten Bots gehören, um Ihren + Discord-Server vor böswilligen Nutzern zu schützen. +

+
+
+ +
+
+ + {/* Pricing */} +
+
+
+
+

+ Einen Schritt{' '} + voraus bleiben +

+

+ Fügen Sie RaidProtect hinzu und beginnen Sie noch heute damit, + Ihren Server zu schützen. +

+
+ +
+ {/* Basic */} + + + {/* Founder */} +
+
+ Founder +
+
+

Abonnement

+

+ 2,99 $ +

+
+

+ Einführungsangebot für die ersten Abonnenten +

+
+ Anpassbares Bot-Profil + Eigene Sanktionsnamen + Erweiterter Zugriff auf Auth Manager + Erweiterter Zugriff auf Display Public + Zugang zur öffentlichen Beta + Exklusive Rolle auf unserem Server +
+ +
+ + {/* Business */} +
+
+ Business +
+
+

Auf Anfrage

+
+

+ Für Projekte mit hohen Sicherheitsanforderungen +

+
+ Alle Founder-Funktionen + Dedizierte, isolierte Instanz + Initiales Audit Ihres Servers + Integration mit Ihren Tools + Maßgeschneiderte Funktionen + Regelmäßige Termine mit einem Experten + Priorisierter Support +
+ +
+
+
+
+
+ + ); +} diff --git a/i18n/de/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/de/docusaurus-plugin-content-pages/thank-you.tsx new file mode 100644 index 0000000..660ae82 --- /dev/null +++ b/i18n/de/docusaurus-plugin-content-pages/thank-you.tsx @@ -0,0 +1,400 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import Head from '@docusaurus/Head'; +import Link from '@docusaurus/Link'; +import styles from '@site/src/pages/thank-you.module.css'; + +type ServerBadge = 'partner' | 'verified' | null; + +type ServerInfo = { + name: string; + members: string; + inviteUrl: string; + iconUrl: string; + badge: ServerBadge; +}; + +type PermissionWarning = { + tone: 'admin' | 'missing'; + message: string; + missing: string[]; +}; + +const DEFAULT_ICON_URL = + 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; + +const BADGE_SRC: Record, string> = { + partner: '/img/landing/serverBadgePartner.svg', + verified: '/img/landing/serverBadgeVerified.svg', +}; + +const REQUIRED_PERMISSIONS = 1117660769534n; +const ADMIN_PERMISSION = 8n; + +// Discord-style permission names (German). +const PERMISSION_MESSAGES: Array<[string, bigint]> = [ + ['Administrator', ADMIN_PERMISSION], + ['Server verwalten', 32n], + ['Rollen verwalten', 268435456n], + ['Kanäle verwalten', 16n], + ['Mitglieder kicken', 2n], + ['Mitglieder bannen', 4n], + ['Spitznamen verwalten', 134217728n], + ['Webhooks verwalten', 536870912n], + ['Audit-Log anzeigen', 524288n], + ['Kanäle anzeigen', 1024n], + ['Mitglieder moderieren', 1099511627776n], + ['Nachrichten senden', 2048n], + ['Nachrichten verwalten', 8192n], + ['Threads verwalten', 17179869184n], + ['Links einbetten', 16384n], + ['Dateien anhängen', 32768n], + ['Nachrichtenverlauf lesen', 65536n], + ['Reaktionen hinzufügen', 64n], + ['Externe Emojis verwenden', 262144n], + ['Mitglieder stummschalten', 4194304n], + ['Mitglieder taub schalten', 8388608n], + ['Mitglieder verschieben', 16777216n], +]; + +async function fetchServerInfo(guildId: string): Promise { + try { + const widgetResponse = await fetch( + `https://discord.com/api/guilds/${guildId}/widget.json`, + ); + if (!widgetResponse.ok) { + throw new Error('Widget deaktiviert.'); + } + const widgetData = await widgetResponse.json(); + + const name: string = widgetData.name || 'Unbekannter Server'; + const members: string = + typeof widgetData.presence_count === 'number' + ? `${widgetData.presence_count} Mitglieder online` + : 'Mitgliederzahl unbekannt'; + const inviteUrl: string = widgetData.instant_invite || '#'; + + let iconUrl = DEFAULT_ICON_URL; + let badge: ServerBadge = null; + + const inviteCode: string | null = widgetData.instant_invite + ? widgetData.instant_invite.split('/').pop() || null + : null; + + if (inviteCode) { + const inviteResponse = await fetch( + `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, + ); + if (inviteResponse.ok) { + const inviteData = await inviteResponse.json(); + const server = inviteData.guild; + if (server?.icon) { + iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; + } + if (Array.isArray(server?.features)) { + if (server.features.includes('PARTNERED')) { + badge = 'partner'; + } else if (server.features.includes('VERIFIED')) { + badge = 'verified'; + } + } + } + } + + return {name, members, inviteUrl, iconUrl, badge}; + } catch (error) { + // Discord widget may be disabled or network may be unreachable. + // eslint-disable-next-line no-console + console.error(error); + return null; + } +} + +function computePermissionWarning( + permissionsParam: string, +): PermissionWarning | null { + let currentPermissions: bigint; + try { + currentPermissions = BigInt(permissionsParam); + } catch { + return null; + } + + const hasAdminPermission = + (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; + const missingPermissions = PERMISSION_MESSAGES.filter( + ([, value]) => + (REQUIRED_PERMISSIONS & value) === value && + (currentPermissions & value) !== value, + ).map(([name]) => name); + + if (hasAdminPermission || missingPermissions.length === 0) { + return null; + } + + if ( + missingPermissions.length === 1 && + missingPermissions[0] === 'Administrator' + ) { + return { + tone: 'admin', + message: + '⚠️ Alle spezifischen Berechtigungen sind erteilt, aber ohne die Administrator-Berechtigung kann der Bot möglicherweise nicht auf alle Kanäle zugreifen.', + missing: [], + }; + } + + return { + tone: 'missing', + message: + '⚠️ Damit der Bot ordnungsgemäß funktioniert, empfehlen wir, folgende Berechtigungen hinzuzufügen:', + missing: missingPermissions, + }; +} + +export default function ThankYou(): ReactNode { + const [serverInfo, setServerInfo] = useState(null); + const [permissionWarning, setPermissionWarning] = + useState(null); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const urlParams = new URLSearchParams(window.location.search); + + const guildId = urlParams.get('guild_id'); + let cancelled = false; + if (guildId) { + fetchServerInfo(guildId).then((info) => { + if (!cancelled && info) { + setServerInfo(info); + } + }); + } + + const permissionsParam = urlParams.get('permissions'); + if (permissionsParam) { + setPermissionWarning(computePermissionWarning(permissionsParam)); + } + + // Auto-redirect to Discord invite after 60s, matching the Webflow page. + const redirectTimer = window.setTimeout(() => { + window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; + }, 60000); + + return () => { + cancelled = true; + window.clearTimeout(redirectTimer); + }; + }, []); + + return ( + <> + + Vielen Dank | RaidProtect + + +
+
+ + RaidProtect title logo + + +

+ Vielen Dank, dass Sie RaidProtect eingeladen haben! +

+ + {serverInfo && ( + + )} + + {permissionWarning && ( +
+ {permissionWarning.message} + {permissionWarning.missing.length > 0 && ( +
    + {permissionWarning.missing.map((perm) => ( +
  • + {perm} +
  • + ))} +
+ )} +
+ )} + +

+ Zum Einstieg empfehlen wir, unsere Dokumentation zu konsultieren und + unserem Server beizutreten. +

+ +
+ + Unserem Discord-Server beitreten + + + Dokumentation anzeigen + +
+ + +
+ +
+ + ); +} diff --git a/i18n/en/code.json b/i18n/en/code.json index a8ea12d..3455dcd 100644 --- a/i18n/en/code.json +++ b/i18n/en/code.json @@ -415,5 +415,57 @@ }, "editor.preview.title": { "message": "Preview" + }, + "hero.preTitle": { + "message": "Used by over {count} servers", + "description": "Hero pre-title displayed above the main title; {count} is a locale-formatted number of servers" + }, + "hero.title": { + "message": "The best {highlight} Discord bot", + "description": "Hero main title; {highlight} renders the gradient-highlighted word" + }, + "hero.title.highlight": { + "message": "security", + "description": "The highlighted word inside the hero title (security)" + }, + "hero.description": { + "message": "Prevent malicious users from damaging your Discord server.", + "description": "Hero description below the title" + }, + "hero.cta.primary": { + "message": "Add to Discord", + "description": "Primary CTA: invite the bot to Discord" + }, + "hero.cta.secondary": { + "message": "View features", + "description": "Secondary CTA: scroll to the features section" + }, + "servers.title": { + "message": "We protect the greatest", + "description": "Servers marquee title: 'We protect the biggest'" + }, + "servers.memberCount": { + "message": "{count} members", + "description": "Server card: number of members (e.g. '40,000 members'); {count} is locale-formatted" + }, + "frame.cta.title": { + "message": "Keeping one step {highlight}", + "description": "Frame CTA title; {highlight} renders the gradient-highlighted word" + }, + "frame.cta.title.highlight": { + "message": "ahead", + "description": "Highlighted word inside the frame CTA title" + }, + "frame.cta.description": { + "message": "Add RaidProtect and start protecting your server today.", + "description": "Frame CTA description" + }, + "frame.cta.primary": { + "message": "Add the bot", + "description": "Frame CTA primary button: add the bot to Discord" + }, + "frame.cta.secondary": { + "message": "Join the server", + "description": "Frame CTA secondary button: join the Discord server" } } diff --git a/i18n/en/docusaurus-plugin-content-pages/index.tsx b/i18n/en/docusaurus-plugin-content-pages/index.tsx new file mode 100644 index 0000000..c92b354 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-pages/index.tsx @@ -0,0 +1,433 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import clsx from 'clsx'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import Hero from '@site/src/components/landing/Hero'; +import Servers from '@site/src/components/landing/Servers'; +import shared from '@site/src/components/landing/styles/shared.module.css'; +import styles from '@site/src/pages/index.module.css'; + +type Counts = { + servers: number; + users: number; + captcha: number; + antispam: number; +}; + +type FormattedValue = { + value: string; + unit: string; +}; + +function formatValue(value: number): FormattedValue { + if (value >= 1_000_000) { + return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; + } + return {value: (value / 1_000).toFixed(1), unit: 'k'}; +} + +function StatCounter({ + rawValue, + label, + fallback, +}: { + rawValue?: number; + label: string; + fallback: string; +}) { + if (rawValue == null) { + return ( +
+
+ {fallback} +
+
{label}
+
+ ); + } + const formatted = formatValue(rawValue); + return ( +
+
+ {formatted.value} + {formatted.unit} +
+
{label}
+
+ ); +} + +const CHECK_ICON = '/img/landing/icon-02.svg'; + +function FeatureItem({children}: {children: ReactNode}) { + return ( +
+ +
{children}
+
+ ); +} + +type Feature = { + to: string; + icon: string; + iconAlt: string; + title: string; + description: string; +}; + +const FEATURES: Feature[] = [ + { + to: '/docs/features/anti-spam', + icon: '/img/landing/iconAntispamWhite.svg', + iconAlt: 'RaidProtect icon Antispam', + title: 'Anti-spam protection', + description: + 'Sanction spam attempts instantly, without any intervention on your part.', + }, + { + to: '/docs/features/raid-mode', + icon: '/img/landing/iconAntiraidWhite.svg', + iconAlt: 'RaidProtect icon Antiraid', + title: 'Blocking raids', + description: + 'Worried about a raid? Our bot is capable of detecting and blocking it before it even impacts your server.', + }, + { + to: '/docs/features/captcha', + icon: '/img/landing/iconCaptchaWhite.svg', + iconAlt: 'RaidProtect icon Captcha', + title: 'Protection against bots', + description: + "With captcha, your members have to prove they're human. Say goodbye to automated accounts.", + }, + { + to: '/docs/features/utilities', + icon: '/img/landing/iconReportWhite.svg', + iconAlt: 'RaidProtect icon Report', + title: 'Moderation & administration', + description: + 'Manage your server like a pro with our various moderation and administration features.', + }, + { + to: '/docs/features/tag-role', + icon: '/img/landing/iconTagWhite.svg', + iconAlt: 'RaidProtect icon Tag', + title: 'Tag Role', + description: + "Tag Role automatically assigns a role to members who add your server's tag.", + }, + { + to: '/docs/features/dm-lock', + icon: '/img/landing/iconDmlockWhite.svg', + iconAlt: 'RaidProtect icon DM Lock', + title: 'DM Lock', + description: + 'An unprecedented shield against spam, scam and private message scams.', + }, +]; + +export default function Home(): ReactNode { + const [counts, setCounts] = useState(null); + + useEffect(() => { + let cancelled = false; + fetch('https://docs.raidprotect.bot/counts.json') + .then((res) => { + if (!res.ok) throw new Error('Failed to fetch counts'); + return res.json(); + }) + .then((data: Counts) => { + if (!cancelled) setCounts(data); + }) + .catch((err) => { + // Stats are best-effort; failure is non-blocking + // eslint-disable-next-line no-console + console.error('Stats update error:', err); + }); + return () => { + cancelled = true; + }; + }, []); + + return ( + +
+ + + + {/* About */} +
+
+
+

+ Our results have an{' '} + impact +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+
+
+ + {/* Features */} +
+
+
+

+ Our features +

+

+ Find out what makes us one of the best bots for protecting your + Discord server from malicious users. +

+
+
+ +
+
+ + {/* Pricing */} +
+
+
+
+

+ Keeping one step{' '} + ahead +

+

+ Add RaidProtect and start protecting your server today. +

+
+ +
+ {/* Basic */} +
+
Basic
+
+

Free

+
+

+ Essential security guaranteed forever +

+
+ Anti-spam protections + Automatic raid blocking + Malicious bot filtering + Moderation & administration + + And much more... + +
+ +
+ + {/* Founder */} +
+
+ Founder +
+
+

Subscription

+

+ $2.99 +

+
+

+ Launch offer reserved for early subscribers +

+
+ Customisable bot profile + Custom sanction names + Advanced access to Auth Manager + Extended access to Display Public + Access to the public Beta + Exclusive role on our server +
+ +
+ + {/* Business */} +
+
+ Business +
+
+

On request

+
+

+ For projects with high security requirements +

+
+ All Founder features + Dedicated, isolated instance + Initial audit of your server + Integration with your tools + Custom features + Regular check-ins with an expert + Priority support +
+ +
+
+
+
+
+
+ ); +} diff --git a/i18n/en/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/en/docusaurus-plugin-content-pages/thank-you.tsx new file mode 100644 index 0000000..4f87bcf --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-pages/thank-you.tsx @@ -0,0 +1,398 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import Head from '@docusaurus/Head'; +import Link from '@docusaurus/Link'; +import styles from '@site/src/pages/thank-you.module.css'; + +type ServerBadge = 'partner' | 'verified' | null; + +type ServerInfo = { + name: string; + members: string; + inviteUrl: string; + iconUrl: string; + badge: ServerBadge; +}; + +type PermissionWarning = { + tone: 'admin' | 'missing'; + message: string; + missing: string[]; +}; + +const DEFAULT_ICON_URL = + 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; + +const BADGE_SRC: Record, string> = { + partner: '/img/landing/serverBadgePartner.svg', + verified: '/img/landing/serverBadgeVerified.svg', +}; + +const REQUIRED_PERMISSIONS = 1117660769534n; +const ADMIN_PERMISSION = 8n; + +// Discord-style permission names (English). +const PERMISSION_MESSAGES: Array<[string, bigint]> = [ + ['Administrator', ADMIN_PERMISSION], + ['Manage Server', 32n], + ['Manage Roles', 268435456n], + ['Manage Channels', 16n], + ['Kick Members', 2n], + ['Ban Members', 4n], + ['Manage Nicknames', 134217728n], + ['Manage Webhooks', 536870912n], + ['View Audit Log', 524288n], + ['View Channels', 1024n], + ['Moderate Members', 1099511627776n], + ['Send Messages', 2048n], + ['Manage Messages', 8192n], + ['Manage Threads', 17179869184n], + ['Embed Links', 16384n], + ['Attach Files', 32768n], + ['Read Message History', 65536n], + ['Add Reactions', 64n], + ['Use External Emojis', 262144n], + ['Mute Members', 4194304n], + ['Deafen Members', 8388608n], + ['Move Members', 16777216n], +]; + +async function fetchServerInfo(guildId: string): Promise { + try { + const widgetResponse = await fetch( + `https://discord.com/api/guilds/${guildId}/widget.json`, + ); + if (!widgetResponse.ok) { + throw new Error('Widget disabled.'); + } + const widgetData = await widgetResponse.json(); + + const name: string = widgetData.name || 'Unknown server'; + const members: string = + typeof widgetData.presence_count === 'number' + ? `${widgetData.presence_count} members online` + : 'Member count unknown'; + const inviteUrl: string = widgetData.instant_invite || '#'; + + let iconUrl = DEFAULT_ICON_URL; + let badge: ServerBadge = null; + + const inviteCode: string | null = widgetData.instant_invite + ? widgetData.instant_invite.split('/').pop() || null + : null; + + if (inviteCode) { + const inviteResponse = await fetch( + `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, + ); + if (inviteResponse.ok) { + const inviteData = await inviteResponse.json(); + const server = inviteData.guild; + if (server?.icon) { + iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; + } + if (Array.isArray(server?.features)) { + if (server.features.includes('PARTNERED')) { + badge = 'partner'; + } else if (server.features.includes('VERIFIED')) { + badge = 'verified'; + } + } + } + } + + return {name, members, inviteUrl, iconUrl, badge}; + } catch (error) { + // Discord widget may be disabled or network may be unreachable. + // eslint-disable-next-line no-console + console.error(error); + return null; + } +} + +function computePermissionWarning( + permissionsParam: string, +): PermissionWarning | null { + let currentPermissions: bigint; + try { + currentPermissions = BigInt(permissionsParam); + } catch { + return null; + } + + const hasAdminPermission = + (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; + const missingPermissions = PERMISSION_MESSAGES.filter( + ([, value]) => + (REQUIRED_PERMISSIONS & value) === value && + (currentPermissions & value) !== value, + ).map(([name]) => name); + + if (hasAdminPermission || missingPermissions.length === 0) { + return null; + } + + if ( + missingPermissions.length === 1 && + missingPermissions[0] === 'Administrator' + ) { + return { + tone: 'admin', + message: + '⚠️ All specific permissions are granted, but without the Administrator permission, the bot may not be able to access every channel.', + missing: [], + }; + } + + return { + tone: 'missing', + message: + '⚠️ To ensure the bot runs properly, we recommend adding the following permissions:', + missing: missingPermissions, + }; +} + +export default function ThankYou(): ReactNode { + const [serverInfo, setServerInfo] = useState(null); + const [permissionWarning, setPermissionWarning] = + useState(null); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const urlParams = new URLSearchParams(window.location.search); + + const guildId = urlParams.get('guild_id'); + let cancelled = false; + if (guildId) { + fetchServerInfo(guildId).then((info) => { + if (!cancelled && info) { + setServerInfo(info); + } + }); + } + + const permissionsParam = urlParams.get('permissions'); + if (permissionsParam) { + setPermissionWarning(computePermissionWarning(permissionsParam)); + } + + // Auto-redirect to Discord invite after 60s, matching the Webflow page. + const redirectTimer = window.setTimeout(() => { + window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; + }, 60000); + + return () => { + cancelled = true; + window.clearTimeout(redirectTimer); + }; + }, []); + + return ( + <> + + Thank you | RaidProtect + + +
+
+ + RaidProtect title logo + + +

Thank you for inviting RaidProtect!

+ + {serverInfo && ( + + )} + + {permissionWarning && ( +
+ {permissionWarning.message} + {permissionWarning.missing.length > 0 && ( +
    + {permissionWarning.missing.map((perm) => ( +
  • + {perm} +
  • + ))} +
+ )} +
+ )} + +

+ To get started, we recommend checking our documentation and joining + our server. +

+ +
+ + Join our Discord server + + + View documentation + +
+ + +
+ +
+ + ); +} diff --git a/i18n/es/code.json b/i18n/es/code.json index 889a66e..b990b1c 100644 --- a/i18n/es/code.json +++ b/i18n/es/code.json @@ -415,5 +415,57 @@ }, "editor.preview.title": { "message": "Vista previa" + }, + "hero.preTitle": { + "message": "Utilizado por más de {count} servidores", + "description": "Hero pre-title displayed above the main title; {count} is a locale-formatted number of servers" + }, + "hero.title": { + "message": "El mejor bot de {highlight} para Discord", + "description": "Hero main title; {highlight} renders the gradient-highlighted word" + }, + "hero.title.highlight": { + "message": "seguridad", + "description": "The highlighted word inside the hero title (security)" + }, + "hero.description": { + "message": "Evita que los usuarios malintencionados dañen tu servidor de Discord.", + "description": "Hero description below the title" + }, + "hero.cta.primary": { + "message": "Añadir a Discord", + "description": "Primary CTA: invite the bot to Discord" + }, + "hero.cta.secondary": { + "message": "Ver funciones", + "description": "Secondary CTA: scroll to the features section" + }, + "servers.title": { + "message": "Protegemos a los más grandes", + "description": "Servers marquee title: 'We protect the biggest'" + }, + "servers.memberCount": { + "message": "{count} miembros", + "description": "Server card: number of members; {count} is locale-formatted" + }, + "frame.cta.title": { + "message": "Mantente a la {highlight}", + "description": "Frame CTA title; {highlight} renders the gradient-highlighted word" + }, + "frame.cta.title.highlight": { + "message": "vanguardia", + "description": "Highlighted word inside the frame CTA title" + }, + "frame.cta.description": { + "message": "Añade RaidProtect y empieza a proteger tu servidor hoy mismo.", + "description": "Frame CTA description" + }, + "frame.cta.primary": { + "message": "Añadir el bot", + "description": "Frame CTA primary button: add the bot to Discord" + }, + "frame.cta.secondary": { + "message": "Unirse al servidor", + "description": "Frame CTA secondary button: join the Discord server" } } diff --git a/i18n/es/docusaurus-plugin-content-pages/index.tsx b/i18n/es/docusaurus-plugin-content-pages/index.tsx new file mode 100644 index 0000000..1610b9c --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-pages/index.tsx @@ -0,0 +1,433 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import clsx from 'clsx'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import Hero from '@site/src/components/landing/Hero'; +import Servers from '@site/src/components/landing/Servers'; +import shared from '@site/src/components/landing/styles/shared.module.css'; +import styles from '@site/src/pages/index.module.css'; + +type Counts = { + servers: number; + users: number; + captcha: number; + antispam: number; +}; + +type FormattedValue = { + value: string; + unit: string; +}; + +function formatValue(value: number): FormattedValue { + if (value >= 1_000_000) { + return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; + } + return {value: (value / 1_000).toFixed(1), unit: 'k'}; +} + +function StatCounter({ + rawValue, + label, + fallback, +}: { + rawValue?: number; + label: string; + fallback: string; +}) { + if (rawValue == null) { + return ( +
+
+ {fallback} +
+
{label}
+
+ ); + } + const formatted = formatValue(rawValue); + return ( +
+
+ {formatted.value} + {formatted.unit} +
+
{label}
+
+ ); +} + +const CHECK_ICON = '/img/landing/icon-02.svg'; + +function FeatureItem({children}: {children: ReactNode}) { + return ( +
+ +
{children}
+
+ ); +} + +type Feature = { + to: string; + icon: string; + iconAlt: string; + title: string; + description: string; +}; + +const FEATURES: Feature[] = [ + { + to: '/docs/features/anti-spam', + icon: '/img/landing/iconAntispamWhite.svg', + iconAlt: 'RaidProtect icon Antispam', + title: 'Protección antispam', + description: + 'Sanciona al instante los intentos de spam, sin necesidad de intervención por tu parte.', + }, + { + to: '/docs/features/raid-mode', + icon: '/img/landing/iconAntiraidWhite.svg', + iconAlt: 'RaidProtect icon Antiraid', + title: 'Bloqueo de raids', + description: + '¿Temes un raid? Nuestro bot puede detectarlo y bloquearlo antes de que afecte a tu servidor.', + }, + { + to: '/docs/features/captcha', + icon: '/img/landing/iconCaptchaWhite.svg', + iconAlt: 'RaidProtect icon Captcha', + title: 'Protección contra bots', + description: + 'Gracias al captcha, tus miembros deben demostrar que son humanos. Di adiós a las cuentas automatizadas.', + }, + { + to: '/docs/features/utilities', + icon: '/img/landing/iconReportWhite.svg', + iconAlt: 'RaidProtect icon Report', + title: 'Moderación y administración', + description: + 'Administra tu servidor como un profesional con nuestras diversas funciones de moderación y administración.', + }, + { + to: '/docs/features/tag-role', + icon: '/img/landing/iconTagWhite.svg', + iconAlt: 'RaidProtect icon Tag', + title: 'Rol por etiqueta', + description: + 'El Rol por etiqueta asigna automáticamente un rol a los miembros que añaden la etiqueta de tu servidor.', + }, + { + to: '/docs/features/dm-lock', + icon: '/img/landing/iconDmlockWhite.svg', + iconAlt: 'RaidProtect icon DM Lock', + title: 'Bloqueo de MP', + description: + 'Un escudo único contra el spam, el scam y las estafas por mensaje privado.', + }, +]; + +export default function Home(): ReactNode { + const [counts, setCounts] = useState(null); + + useEffect(() => { + let cancelled = false; + fetch('https://docs.raidprotect.bot/counts.json') + .then((res) => { + if (!res.ok) throw new Error('Failed to fetch counts'); + return res.json(); + }) + .then((data: Counts) => { + if (!cancelled) setCounts(data); + }) + .catch((err) => { + // Stats are best-effort; failure is non-blocking + // eslint-disable-next-line no-console + console.error('Stats update error:', err); + }); + return () => { + cancelled = true; + }; + }, []); + + return ( + +
+ + + + {/* About */} +
+
+
+

+ Nuestros resultados tienen un{' '} + impacto +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+
+
+ + {/* Features */} +
+
+
+

+ Nuestras funciones +

+

+ Descubre por qué somos uno de los mejores bots para proteger tu + servidor de Discord contra usuarios malintencionados. +

+
+
+ +
+
+ + {/* Pricing */} +
+
+
+
+

+ Mantente a la{' '} + vanguardia +

+

+ Añade RaidProtect y empieza a proteger tu servidor hoy mismo. +

+
+ +
+ {/* Basic */} +
+
Basic
+
+

Gratis

+
+

+ La seguridad esencial garantizada para siempre +

+
+ Protecciones antispam + Bloqueo automático de raids + Filtrado de bots maliciosos + Moderación y administración + + Y mucho más... + +
+ +
+ + {/* Founder */} +
+
+ Founder +
+
+

Suscripción

+

+ 2,99 $ +

+
+

+ Oferta de lanzamiento reservada a los primeros suscriptores +

+
+ Perfil del bot personalizable + Nombres de sanciones personalizados + Acceso avanzado al Auth Manager + Acceso ampliado al Display Public + Acceso a la Beta pública + Rol exclusivo en nuestro servidor +
+ +
+ + {/* Business */} +
+
+ Business +
+
+

Bajo solicitud

+
+

+ Para proyectos con altos requisitos de seguridad +

+
+ Todas las funciones Founder + Instancia dedicada y aislada + Auditoría inicial de tu servidor + Integración con tus herramientas + Funciones a medida + Seguimiento regular con un experto + Soporte prioritario +
+ +
+
+
+
+
+
+ ); +} diff --git a/i18n/es/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/es/docusaurus-plugin-content-pages/thank-you.tsx new file mode 100644 index 0000000..f502bc4 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-pages/thank-you.tsx @@ -0,0 +1,398 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import Head from '@docusaurus/Head'; +import Link from '@docusaurus/Link'; +import styles from '@site/src/pages/thank-you.module.css'; + +type ServerBadge = 'partner' | 'verified' | null; + +type ServerInfo = { + name: string; + members: string; + inviteUrl: string; + iconUrl: string; + badge: ServerBadge; +}; + +type PermissionWarning = { + tone: 'admin' | 'missing'; + message: string; + missing: string[]; +}; + +const DEFAULT_ICON_URL = + 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; + +const BADGE_SRC: Record, string> = { + partner: '/img/landing/serverBadgePartner.svg', + verified: '/img/landing/serverBadgeVerified.svg', +}; + +const REQUIRED_PERMISSIONS = 1117660769534n; +const ADMIN_PERMISSION = 8n; + +// Discord-style permission names (Spanish). +const PERMISSION_MESSAGES: Array<[string, bigint]> = [ + ['Administrador', ADMIN_PERMISSION], + ['Gestionar servidor', 32n], + ['Gestionar roles', 268435456n], + ['Gestionar canales', 16n], + ['Expulsar miembros', 2n], + ['Banear miembros', 4n], + ['Gestionar apodos', 134217728n], + ['Gestionar webhooks', 536870912n], + ['Ver registro de auditoría', 524288n], + ['Ver canales', 1024n], + ['Moderar miembros', 1099511627776n], + ['Enviar mensajes', 2048n], + ['Gestionar mensajes', 8192n], + ['Gestionar hilos', 17179869184n], + ['Insertar enlaces', 16384n], + ['Adjuntar archivos', 32768n], + ['Leer historial de mensajes', 65536n], + ['Añadir reacciones', 64n], + ['Usar emojis externos', 262144n], + ['Silenciar miembros', 4194304n], + ['Ensordecer miembros', 8388608n], + ['Mover miembros', 16777216n], +]; + +async function fetchServerInfo(guildId: string): Promise { + try { + const widgetResponse = await fetch( + `https://discord.com/api/guilds/${guildId}/widget.json`, + ); + if (!widgetResponse.ok) { + throw new Error('Widget desactivado.'); + } + const widgetData = await widgetResponse.json(); + + const name: string = widgetData.name || 'Servidor desconocido'; + const members: string = + typeof widgetData.presence_count === 'number' + ? `${widgetData.presence_count} miembros en línea` + : 'Número de miembros desconocido'; + const inviteUrl: string = widgetData.instant_invite || '#'; + + let iconUrl = DEFAULT_ICON_URL; + let badge: ServerBadge = null; + + const inviteCode: string | null = widgetData.instant_invite + ? widgetData.instant_invite.split('/').pop() || null + : null; + + if (inviteCode) { + const inviteResponse = await fetch( + `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, + ); + if (inviteResponse.ok) { + const inviteData = await inviteResponse.json(); + const server = inviteData.guild; + if (server?.icon) { + iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; + } + if (Array.isArray(server?.features)) { + if (server.features.includes('PARTNERED')) { + badge = 'partner'; + } else if (server.features.includes('VERIFIED')) { + badge = 'verified'; + } + } + } + } + + return {name, members, inviteUrl, iconUrl, badge}; + } catch (error) { + // Discord widget may be disabled or network may be unreachable. + // eslint-disable-next-line no-console + console.error(error); + return null; + } +} + +function computePermissionWarning( + permissionsParam: string, +): PermissionWarning | null { + let currentPermissions: bigint; + try { + currentPermissions = BigInt(permissionsParam); + } catch { + return null; + } + + const hasAdminPermission = + (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; + const missingPermissions = PERMISSION_MESSAGES.filter( + ([, value]) => + (REQUIRED_PERMISSIONS & value) === value && + (currentPermissions & value) !== value, + ).map(([name]) => name); + + if (hasAdminPermission || missingPermissions.length === 0) { + return null; + } + + if ( + missingPermissions.length === 1 && + missingPermissions[0] === 'Administrador' + ) { + return { + tone: 'admin', + message: + '⚠️ Todos los permisos específicos están otorgados, pero sin el permiso de Administrador el bot podría no acceder a todos los canales.', + missing: [], + }; + } + + return { + tone: 'missing', + message: + '⚠️ Para que el bot funcione correctamente, recomendamos agregar los siguientes permisos:', + missing: missingPermissions, + }; +} + +export default function ThankYou(): ReactNode { + const [serverInfo, setServerInfo] = useState(null); + const [permissionWarning, setPermissionWarning] = + useState(null); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const urlParams = new URLSearchParams(window.location.search); + + const guildId = urlParams.get('guild_id'); + let cancelled = false; + if (guildId) { + fetchServerInfo(guildId).then((info) => { + if (!cancelled && info) { + setServerInfo(info); + } + }); + } + + const permissionsParam = urlParams.get('permissions'); + if (permissionsParam) { + setPermissionWarning(computePermissionWarning(permissionsParam)); + } + + // Auto-redirect to Discord invite after 60s, matching the Webflow page. + const redirectTimer = window.setTimeout(() => { + window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; + }, 60000); + + return () => { + cancelled = true; + window.clearTimeout(redirectTimer); + }; + }, []); + + return ( + <> + + Gracias | RaidProtect + + +
+
+ + RaidProtect title logo + + +

¡Gracias por invitar a RaidProtect!

+ + {serverInfo && ( + + )} + + {permissionWarning && ( +
+ {permissionWarning.message} + {permissionWarning.missing.length > 0 && ( +
    + {permissionWarning.missing.map((perm) => ( +
  • + {perm} +
  • + ))} +
+ )} +
+ )} + +

+ Para empezar con buen pie, te recomendamos consultar nuestra + documentación y unirte a nuestro servidor. +

+ +
+ + Unirse a nuestro servidor de Discord + + + Consultar la documentación + +
+ + +
+ +
+ + ); +} diff --git a/i18n/fr/code.json b/i18n/fr/code.json index d78af15..997cd1b 100644 --- a/i18n/fr/code.json +++ b/i18n/fr/code.json @@ -434,5 +434,57 @@ }, "editor.contribute.featured": { "message": "Pages suggérées" + }, + "hero.preTitle": { + "message": "Utilisé par plus de {count} serveurs", + "description": "Hero pre-title displayed above the main title; {count} is a locale-formatted number of servers" + }, + "hero.title": { + "message": "Le meilleur bot Discord de {highlight}", + "description": "Hero main title; {highlight} renders the gradient-highlighted word" + }, + "hero.title.highlight": { + "message": "sécurité", + "description": "The highlighted word inside the hero title (security)" + }, + "hero.description": { + "message": "Empêchez les utilisateurs malintentionnés de nuire à votre serveur Discord.", + "description": "Hero description below the title" + }, + "hero.cta.primary": { + "message": "Ajouter à Discord", + "description": "Primary CTA: invite the bot to Discord" + }, + "hero.cta.secondary": { + "message": "Voir les fonctionnalités", + "description": "Secondary CTA: scroll to the features section" + }, + "servers.title": { + "message": "Nous protégeons les plus grands", + "description": "Servers marquee title: 'We protect the biggest'" + }, + "servers.memberCount": { + "message": "{count} membres", + "description": "Server card: number of members (e.g. '40 000 members'); {count} is locale-formatted" + }, + "frame.cta.title": { + "message": "Garder une longueur {highlight}", + "description": "Frame CTA title; {highlight} renders the gradient-highlighted word" + }, + "frame.cta.title.highlight": { + "message": "d'avance", + "description": "Highlighted word inside the frame CTA title" + }, + "frame.cta.description": { + "message": "Ajoutez RaidProtect et commencez à protéger votre serveur dès aujourd'hui.", + "description": "Frame CTA description" + }, + "frame.cta.primary": { + "message": "Ajouter le bot", + "description": "Frame CTA primary button: add the bot to Discord" + }, + "frame.cta.secondary": { + "message": "Rejoindre le serveur", + "description": "Frame CTA secondary button: join the Discord server" } } diff --git a/i18n/pt/code.json b/i18n/pt/code.json index 50a083a..87d4fce 100644 --- a/i18n/pt/code.json +++ b/i18n/pt/code.json @@ -415,5 +415,57 @@ }, "editor.preview.title": { "message": "Pré-visualização" + }, + "hero.preTitle": { + "message": "Utilizado por mais de {count} servidores", + "description": "Hero pre-title displayed above the main title; {count} is a locale-formatted number of servers" + }, + "hero.title": { + "message": "O melhor bot de {highlight} para Discord", + "description": "Hero main title; {highlight} renders the gradient-highlighted word" + }, + "hero.title.highlight": { + "message": "segurança", + "description": "The highlighted word inside the hero title (security)" + }, + "hero.description": { + "message": "Impeça que utilizadores maliciosos prejudiquem o seu servidor Discord.", + "description": "Hero description below the title" + }, + "hero.cta.primary": { + "message": "Adicionar ao Discord", + "description": "Primary CTA: invite the bot to Discord" + }, + "hero.cta.secondary": { + "message": "Ver funcionalidades", + "description": "Secondary CTA: scroll to the features section" + }, + "servers.title": { + "message": "Protegemos os maiores", + "description": "Servers marquee title: 'We protect the biggest'" + }, + "servers.memberCount": { + "message": "{count} membros", + "description": "Server card: number of members; {count} is locale-formatted" + }, + "frame.cta.title": { + "message": "Manter-se um passo {highlight}", + "description": "Frame CTA title; {highlight} renders the gradient-highlighted word" + }, + "frame.cta.title.highlight": { + "message": "à frente", + "description": "Highlighted word inside the frame CTA title" + }, + "frame.cta.description": { + "message": "Adicione o RaidProtect e comece a proteger o seu servidor hoje mesmo.", + "description": "Frame CTA description" + }, + "frame.cta.primary": { + "message": "Adicionar o bot", + "description": "Frame CTA primary button: add the bot to Discord" + }, + "frame.cta.secondary": { + "message": "Entrar no servidor", + "description": "Frame CTA secondary button: join the Discord server" } } diff --git a/i18n/pt/docusaurus-plugin-content-pages/index.tsx b/i18n/pt/docusaurus-plugin-content-pages/index.tsx new file mode 100644 index 0000000..0658580 --- /dev/null +++ b/i18n/pt/docusaurus-plugin-content-pages/index.tsx @@ -0,0 +1,435 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import clsx from 'clsx'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import Hero from '@site/src/components/landing/Hero'; +import Servers from '@site/src/components/landing/Servers'; +import shared from '@site/src/components/landing/styles/shared.module.css'; +import styles from '@site/src/pages/index.module.css'; + +type Counts = { + servers: number; + users: number; + captcha: number; + antispam: number; +}; + +type FormattedValue = { + value: string; + unit: string; +}; + +function formatValue(value: number): FormattedValue { + if (value >= 1_000_000) { + return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; + } + return {value: (value / 1_000).toFixed(1), unit: 'k'}; +} + +function StatCounter({ + rawValue, + label, + fallback, +}: { + rawValue?: number; + label: string; + fallback: string; +}) { + if (rawValue == null) { + return ( +
+
+ {fallback} +
+
{label}
+
+ ); + } + const formatted = formatValue(rawValue); + return ( +
+
+ {formatted.value} + {formatted.unit} +
+
{label}
+
+ ); +} + +const CHECK_ICON = '/img/landing/icon-02.svg'; + +function FeatureItem({children}: {children: ReactNode}) { + return ( +
+ +
{children}
+
+ ); +} + +type Feature = { + to: string; + icon: string; + iconAlt: string; + title: string; + description: string; +}; + +const FEATURES: Feature[] = [ + { + to: '/docs/features/anti-spam', + icon: '/img/landing/iconAntispamWhite.svg', + iconAlt: 'RaidProtect icon Antispam', + title: 'Proteção antispam', + description: + 'Sancione instantaneamente as tentativas de spam, sem qualquer intervenção da sua parte.', + }, + { + to: '/docs/features/raid-mode', + icon: '/img/landing/iconAntiraidWhite.svg', + iconAlt: 'RaidProtect icon Antiraid', + title: 'Bloqueio de raids', + description: + 'Receia um raid? O nosso bot é capaz de o detetar e bloquear antes que afete o seu servidor.', + }, + { + to: '/docs/features/captcha', + icon: '/img/landing/iconCaptchaWhite.svg', + iconAlt: 'RaidProtect icon Captcha', + title: 'Proteção contra bots', + description: + 'Graças ao captcha, os seus membros têm de provar que são humanos. Diga adeus às contas automatizadas.', + }, + { + to: '/docs/features/utilities', + icon: '/img/landing/iconReportWhite.svg', + iconAlt: 'RaidProtect icon Report', + title: 'Moderação e administração', + description: + 'Gira o seu servidor como um profissional graças às nossas várias funcionalidades de moderação e administração.', + }, + { + to: '/docs/features/tag-role', + icon: '/img/landing/iconTagWhite.svg', + iconAlt: 'RaidProtect icon Tag', + title: 'Cargo de Etiqueta', + description: + 'O Cargo de Etiqueta atribui automaticamente um cargo aos membros que adicionam a etiqueta do seu servidor.', + }, + { + to: '/docs/features/dm-lock', + icon: '/img/landing/iconDmlockWhite.svg', + iconAlt: 'RaidProtect icon DM Lock', + title: 'Bloqueio de MP', + description: + 'Um escudo único contra spam, scam e burlas por mensagem privada.', + }, +]; + +export default function Home(): ReactNode { + const [counts, setCounts] = useState(null); + + useEffect(() => { + let cancelled = false; + fetch('https://docs.raidprotect.bot/counts.json') + .then((res) => { + if (!res.ok) throw new Error('Failed to fetch counts'); + return res.json(); + }) + .then((data: Counts) => { + if (!cancelled) setCounts(data); + }) + .catch((err) => { + // Stats are best-effort; failure is non-blocking + // eslint-disable-next-line no-console + console.error('Stats update error:', err); + }); + return () => { + cancelled = true; + }; + }, []); + + return ( + +
+ + + + {/* About */} +
+
+
+

+ Os nossos resultados têm{' '} + impacto +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+
+
+
+ + {/* Features */} +
+
+
+

+ As nossas{' '} + funcionalidades +

+

+ Descubra o que faz de nós um dos melhores bots para proteger o + seu servidor Discord de utilizadores maliciosos. +

+
+
+ +
+
+ + {/* Pricing */} +
+
+
+
+

+ Manter-se um passo{' '} + à frente +

+

+ Adicione o RaidProtect e comece a proteger o seu servidor hoje + mesmo. +

+
+ +
+ {/* Basic */} +
+
Basic
+
+

Gratuito

+
+

+ A segurança essencial garantida para sempre +

+
+ Proteções antispam + Bloqueio automático de raids + Filtragem de bots maliciosos + Moderação e administração + + E muito mais... + +
+ +
+ + {/* Founder */} +
+
+ Founder +
+
+

Subscrição

+

+ 2,99 $ +

+
+

+ Oferta de lançamento reservada aos primeiros subscritores +

+
+ Perfil do bot personalizável + Nomes de sanções personalizados + Acesso avançado ao Auth Manager + Acesso alargado ao Display Public + Acesso à Beta pública + Cargo exclusivo no nosso servidor +
+ +
+ + {/* Business */} +
+
+ Business +
+
+

Sob consulta

+
+

+ Para projetos com exigências de segurança elevadas +

+
+ Todas as funcionalidades Founder + Instância dedicada e isolada + Auditoria inicial do seu servidor + Integração com as suas ferramentas + Funcionalidades à medida + Acompanhamento regular com um especialista + Suporte prioritário +
+ +
+
+
+
+
+
+ ); +} diff --git a/i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx new file mode 100644 index 0000000..8aa29dc --- /dev/null +++ b/i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx @@ -0,0 +1,398 @@ +import React, {type ReactNode, useEffect, useState} from 'react'; +import Head from '@docusaurus/Head'; +import Link from '@docusaurus/Link'; +import styles from '@site/src/pages/thank-you.module.css'; + +type ServerBadge = 'partner' | 'verified' | null; + +type ServerInfo = { + name: string; + members: string; + inviteUrl: string; + iconUrl: string; + badge: ServerBadge; +}; + +type PermissionWarning = { + tone: 'admin' | 'missing'; + message: string; + missing: string[]; +}; + +const DEFAULT_ICON_URL = + 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; + +const BADGE_SRC: Record, string> = { + partner: '/img/landing/serverBadgePartner.svg', + verified: '/img/landing/serverBadgeVerified.svg', +}; + +const REQUIRED_PERMISSIONS = 1117660769534n; +const ADMIN_PERMISSION = 8n; + +// Discord-style permission names (Portuguese). +const PERMISSION_MESSAGES: Array<[string, bigint]> = [ + ['Administrador', ADMIN_PERMISSION], + ['Gerir servidor', 32n], + ['Gerir cargos', 268435456n], + ['Gerir canais', 16n], + ['Expulsar membros', 2n], + ['Banir membros', 4n], + ['Gerir alcunhas', 134217728n], + ['Gerir webhooks', 536870912n], + ['Ver registo de auditoria', 524288n], + ['Ver canais', 1024n], + ['Moderar membros', 1099511627776n], + ['Enviar mensagens', 2048n], + ['Gerir mensagens', 8192n], + ['Gerir tópicos', 17179869184n], + ['Incorporar ligações', 16384n], + ['Anexar ficheiros', 32768n], + ['Ler histórico de mensagens', 65536n], + ['Adicionar reações', 64n], + ['Usar emojis externos', 262144n], + ['Silenciar membros', 4194304n], + ['Ensurdecer membros', 8388608n], + ['Mover membros', 16777216n], +]; + +async function fetchServerInfo(guildId: string): Promise { + try { + const widgetResponse = await fetch( + `https://discord.com/api/guilds/${guildId}/widget.json`, + ); + if (!widgetResponse.ok) { + throw new Error('Widget desativado.'); + } + const widgetData = await widgetResponse.json(); + + const name: string = widgetData.name || 'Servidor desconhecido'; + const members: string = + typeof widgetData.presence_count === 'number' + ? `${widgetData.presence_count} membros online` + : 'Número de membros desconhecido'; + const inviteUrl: string = widgetData.instant_invite || '#'; + + let iconUrl = DEFAULT_ICON_URL; + let badge: ServerBadge = null; + + const inviteCode: string | null = widgetData.instant_invite + ? widgetData.instant_invite.split('/').pop() || null + : null; + + if (inviteCode) { + const inviteResponse = await fetch( + `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, + ); + if (inviteResponse.ok) { + const inviteData = await inviteResponse.json(); + const server = inviteData.guild; + if (server?.icon) { + iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; + } + if (Array.isArray(server?.features)) { + if (server.features.includes('PARTNERED')) { + badge = 'partner'; + } else if (server.features.includes('VERIFIED')) { + badge = 'verified'; + } + } + } + } + + return {name, members, inviteUrl, iconUrl, badge}; + } catch (error) { + // Discord widget may be disabled or network may be unreachable. + // eslint-disable-next-line no-console + console.error(error); + return null; + } +} + +function computePermissionWarning( + permissionsParam: string, +): PermissionWarning | null { + let currentPermissions: bigint; + try { + currentPermissions = BigInt(permissionsParam); + } catch { + return null; + } + + const hasAdminPermission = + (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; + const missingPermissions = PERMISSION_MESSAGES.filter( + ([, value]) => + (REQUIRED_PERMISSIONS & value) === value && + (currentPermissions & value) !== value, + ).map(([name]) => name); + + if (hasAdminPermission || missingPermissions.length === 0) { + return null; + } + + if ( + missingPermissions.length === 1 && + missingPermissions[0] === 'Administrador' + ) { + return { + tone: 'admin', + message: + '⚠️ Todas as permissões específicas estão concedidas, mas sem a permissão de Administrador o bot pode não conseguir aceder a todos os canais.', + missing: [], + }; + } + + return { + tone: 'missing', + message: + '⚠️ Para garantir o bom funcionamento do bot, recomendamos adicionar as seguintes permissões:', + missing: missingPermissions, + }; +} + +export default function ThankYou(): ReactNode { + const [serverInfo, setServerInfo] = useState(null); + const [permissionWarning, setPermissionWarning] = + useState(null); + + useEffect(() => { + if (typeof window === 'undefined') return; + + const urlParams = new URLSearchParams(window.location.search); + + const guildId = urlParams.get('guild_id'); + let cancelled = false; + if (guildId) { + fetchServerInfo(guildId).then((info) => { + if (!cancelled && info) { + setServerInfo(info); + } + }); + } + + const permissionsParam = urlParams.get('permissions'); + if (permissionsParam) { + setPermissionWarning(computePermissionWarning(permissionsParam)); + } + + // Auto-redirect to Discord invite after 60s, matching the Webflow page. + const redirectTimer = window.setTimeout(() => { + window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; + }, 60000); + + return () => { + cancelled = true; + window.clearTimeout(redirectTimer); + }; + }, []); + + return ( + <> + + Obrigado | RaidProtect + + +
+
+ + RaidProtect title logo + + +

Obrigado por convidar o RaidProtect!

+ + {serverInfo && ( + + )} + + {permissionWarning && ( +
+ {permissionWarning.message} + {permissionWarning.missing.length > 0 && ( +
    + {permissionWarning.missing.map((perm) => ( +
  • + {perm} +
  • + ))} +
+ )} +
+ )} + +

+ Para começar com o pé direito, recomendamos consultar a nossa + documentação e entrar no nosso servidor. +

+ +
+ + Entrar no nosso servidor Discord + + + Consultar a documentação + +
+ + +
+ +
+ + ); +} diff --git a/src/components/landing/Hero/index.tsx b/src/components/landing/Hero/index.tsx index 912654b..228af7e 100644 --- a/src/components/landing/Hero/index.tsx +++ b/src/components/landing/Hero/index.tsx @@ -1,5 +1,7 @@ import React, {type ReactNode} from 'react'; import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import shared from '../styles/shared.module.css'; import styles from './styles.module.css'; @@ -17,24 +19,59 @@ function roundedServerCount(raw: number | undefined): number { return Math.floor(raw / PRE_TITLE_STEP) * PRE_TITLE_STEP; } +const LOCALE_TO_BCP47: Record = { + fr: 'fr-FR', + en: 'en-US', + de: 'de-DE', + es: 'es-ES', + pt: 'pt-PT', +}; + export default function Hero({serverCount}: HeroProps): ReactNode { - const formatted = roundedServerCount(serverCount).toLocaleString('fr-FR'); + const { + i18n: {currentLocale}, + } = useDocusaurusContext(); + const bcp47 = LOCALE_TO_BCP47[currentLocale] ?? currentLocale; + const formatted = roundedServerCount(serverCount).toLocaleString(bcp47); return (
- Utilisé par plus de {formatted} serveurs + + {'Utilisé par plus de {count} serveurs'} +

- Le meilleur bot Discord de{' '} - sécurité + + + sécurité + + + ), + }}> + {'Le meilleur bot Discord de {highlight}'} +

- Empêchez les utilisateurs malintentionnés de nuire à votre serveur - Discord. + + Empêchez les utilisateurs malintentionnés de nuire à votre serveur + Discord. +

diff --git a/src/components/landing/Servers/index.tsx b/src/components/landing/Servers/index.tsx index 4d8090a..9fd5deb 100644 --- a/src/components/landing/Servers/index.tsx +++ b/src/components/landing/Servers/index.tsx @@ -1,5 +1,7 @@ import React, {type ReactNode} from 'react'; import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import shared from '../styles/shared.module.css'; import styles from './styles.module.css'; @@ -10,7 +12,8 @@ type Server = { icon: string; alt: string; href: string; - members: string; + /** Raw member count (rounded). Localised at render time. */ + members: number; badge: Badge; }; @@ -20,7 +23,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconWankilStudio.webp', alt: 'Wankil Studio Discord server icon', href: 'https://discord.com/invite/wankilstudio', - members: '40 000 membres', + members: 40000, badge: 'verified', }, { @@ -28,7 +31,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconRocketLeagueFrance.webp', alt: 'Rocket League France Discord server icon', href: 'https://discord.com/invite/rlfr', - members: '196 500 membres', + members: 196500, badge: 'partner', }, { @@ -36,7 +39,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconSlashFR.webp', alt: 'Slash FR Discord server icon', href: 'https://discord.com/invite/fr', - members: '48 500 membres', + members: 48500, badge: null, }, { @@ -44,7 +47,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconZetFar.webp', alt: 'ZetFar Discord server icon', href: 'https://discord.com/invite/zetfar', - members: '135 000 membres', + members: 135000, badge: 'verified', }, { @@ -52,7 +55,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconLigue1.webp', alt: 'Ligue 1 Discord server icon', href: 'https://discord.com/invite/ligue1', - members: '15 000 membres', + members: 15000, badge: 'verified', }, { @@ -60,7 +63,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconJobless.webp', alt: 'Jobless Discord server icon', href: 'https://discord.com/invite/jobless', - members: '56 500 membres', + members: 56500, badge: 'partner', }, { @@ -68,7 +71,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconBloxFruitsFR.webp', alt: 'Blox Fruits FR Discord server icon', href: 'https://discord.com/invite/bloxfruitsfr', - members: '124 000 membres', + members: 124000, badge: null, }, { @@ -76,7 +79,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconCyrilmp4.webp', alt: 'CYRILmp4 Discord server icon', href: 'https://discord.com/invite/cyrilmp4', - members: '22 500 membres', + members: 22500, badge: 'partner', }, { @@ -84,7 +87,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconFortniteHouse.webp', alt: 'Fortnite House Discord server icon', href: 'https://discord.com/invite/officiel', - members: '66 500 membres', + members: 66500, badge: 'partner', }, { @@ -92,7 +95,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconPUBGMobileFrance.webp', alt: 'PUBG MOBILE FRANCE Discord server icon', href: 'https://discord.com/invite/pubgmfr', - members: '18 000 membres', + members: 18000, badge: 'verified', }, { @@ -100,7 +103,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconNationsGlory.webp', alt: 'NationsGlory server icon', href: 'https://discord.com/invite/nationsglory', - members: '51 000 membres', + members: 51000, badge: 'partner', }, { @@ -108,7 +111,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconMastu.webp', alt: 'MASTU Discord server icon', href: 'https://discord.com/invite/mastu', - members: '17 000 membres', + members: 17000, badge: 'partner', }, { @@ -116,7 +119,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconClashRoyaleFR.webp', alt: 'Clash Royale FR Discord server icon', href: 'https://discord.com/invite/clashfr', - members: '34 000 membres', + members: 34000, badge: 'partner', }, { @@ -124,7 +127,7 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconTeamVitality.webp', alt: 'TEAM VITALITY Discord server icon', href: 'https://discord.com/invite/teamvitality', - members: '19 500 membres', + members: 19500, badge: null, }, { @@ -132,11 +135,19 @@ const SERVERS: Server[] = [ icon: '/img/landing/iconGenshinImpactFR.webp', alt: 'Genshin Impact FR Discord server icon', href: 'https://discord.com/invite/genshinimpactfr', - members: '55 000 membres', + members: 55000, badge: 'partner', }, ]; +const LOCALE_TO_BCP47: Record = { + fr: 'fr-FR', + en: 'en-US', + de: 'de-DE', + es: 'es-ES', + pt: 'pt-PT', +}; + function BadgeImg({badge}: {badge: Badge}) { if (!badge) return null; const src = @@ -159,7 +170,10 @@ function BadgeImg({badge}: {badge: Badge}) { ); } -function ServerCard({server}: {server: Server}) { +function ServerCard({server, locale}: {server: Server; locale: string}) { + const formatted = server.members.toLocaleString( + LOCALE_TO_BCP47[locale] ?? locale, + ); return ( {server.name}
-
{server.members}
+
+ + {'{count} membres'} + +
@@ -186,20 +207,37 @@ function ServerCard({server}: {server: Server}) { } export default function Servers(): ReactNode { + const { + i18n: {currentLocale}, + } = useDocusaurusContext(); return (
-

Nous protégeons les plus grands

+

+ + Nous protégeons les plus grands + +

{SERVERS.map((server) => ( - + ))}
diff --git a/src/pages/frame/cta.tsx b/src/pages/frame/cta.tsx index f80d9cd..407e80e 100644 --- a/src/pages/frame/cta.tsx +++ b/src/pages/frame/cta.tsx @@ -1,5 +1,6 @@ import React, {type ReactNode} from 'react'; import Head from '@docusaurus/Head'; +import Translate from '@docusaurus/Translate'; import clsx from 'clsx'; import shared from '@site/src/components/landing/styles/shared.module.css'; import frame from './frame.module.css'; @@ -19,12 +20,30 @@ export default function FrameCta(): ReactNode {

- Garder une longueur{' '} - d'avance + + + d'avance + + + ), + }}> + {'Garder une longueur {highlight}'} +

- Ajoutez RaidProtect et commencez à protéger votre serveur dès - aujourd'hui. + + Ajoutez RaidProtect et commencez à protéger votre serveur + dès aujourd'hui. +

diff --git a/src/pages/thank-you.tsx b/src/pages/thank-you.tsx index 6aa12f0..8dd630c 100644 --- a/src/pages/thank-you.tsx +++ b/src/pages/thank-you.tsx @@ -306,23 +306,23 @@ export default function ThankYou(): ReactNode { Français
From be7131b110bcbadd25c7146546ff4d12c1ec7701 Mon Sep 17 00:00:00 2001 From: Zallom Date: Fri, 22 May 2026 15:52:12 +0200 Subject: [PATCH 2/2] refactor(translations): tout en sur landing + thank-you, plus d'overlays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Une seule source TSX par page, toutes les chaînes dans i18n/{locale}/code.json (~80 nouvelles clés × 5 locales). Une modif de structure = 1 fichier à toucher. - landing.{about,features,pricing}.* couvrent toutes les sections inline - thankYou.* + permission.* (22 noms Discord) pour la page invitée OAuth - FEATURES driven par un array slugé : anti-spam/raid/captcha/mod/tag/dm - PERMISSION_MESSAGES en tuples [id, defaultLabel, BigInt], discriminateur admin basé sur l'id et non la chaîne traduite (locale-safe) - Highlights via placeholders {highlight} (compatibles syntaxe FR/DE où le mot mis en évidence n'est pas toujours en fin de phrase) Supprime 8 overlays : i18n/{en,de,es,pt}/docusaurus-plugin-content-pages/{index,thank-you}.tsx. Conserve les overlays legal/terms/privacy/cookies (scope différent). Build OK FR + EN + DE + ES + PT. --- i18n/de/code.json | 380 +++++++++++++++ .../docusaurus-plugin-content-pages/index.tsx | 435 ----------------- .../thank-you.tsx | 400 ---------------- i18n/en/code.json | 380 +++++++++++++++ .../docusaurus-plugin-content-pages/index.tsx | 433 ----------------- .../thank-you.tsx | 398 ---------------- i18n/es/code.json | 380 +++++++++++++++ .../docusaurus-plugin-content-pages/index.tsx | 433 ----------------- .../thank-you.tsx | 398 ---------------- i18n/fr/code.json | 380 +++++++++++++++ i18n/pt/code.json | 380 +++++++++++++++ .../docusaurus-plugin-content-pages/index.tsx | 435 ----------------- .../thank-you.tsx | 398 ---------------- src/pages/index.tsx | 438 ++++++++++++++---- src/pages/thank-you.tsx | 230 ++++++--- 15 files changed, 2417 insertions(+), 3481 deletions(-) delete mode 100644 i18n/de/docusaurus-plugin-content-pages/index.tsx delete mode 100644 i18n/de/docusaurus-plugin-content-pages/thank-you.tsx delete mode 100644 i18n/en/docusaurus-plugin-content-pages/index.tsx delete mode 100644 i18n/en/docusaurus-plugin-content-pages/thank-you.tsx delete mode 100644 i18n/es/docusaurus-plugin-content-pages/index.tsx delete mode 100644 i18n/es/docusaurus-plugin-content-pages/thank-you.tsx delete mode 100644 i18n/pt/docusaurus-plugin-content-pages/index.tsx delete mode 100644 i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx diff --git a/i18n/de/code.json b/i18n/de/code.json index 8b243cf..64dc367 100644 --- a/i18n/de/code.json +++ b/i18n/de/code.json @@ -467,5 +467,385 @@ "frame.cta.secondary": { "message": "Server beitreten", "description": "Frame CTA secondary button: join the Discord server" + }, + "landing.layout.title": { + "message": "RaidProtect • Sichern Sie Ihren Discord-Server", + "description": "Browser tab title for the landing page" + }, + "landing.layout.description": { + "message": "RaidProtect ist ein französischer Discord-Bot mit der Mission, Ihren Server einfach vor böswilligen Nutzern zu schützen.", + "description": "Meta description for the landing page" + }, + "landing.about.title": { + "message": "Unsere {highlight} sprechen für sich", + "description": "About section title; {highlight} renders the gradient-highlighted word" + }, + "landing.about.title.highlight": { + "message": "Ergebnisse", + "description": "Highlighted word inside the about section title" + }, + "landing.about.curve.ariaLabel": { + "message": "Zunahme der Serveranzahl", + "description": "ARIA label for the decorative SVG curve in the about section" + }, + "landing.about.counter.servers": { + "message": "Gesicherte Server", + "description": "About stat label: number of secured servers" + }, + "landing.about.counter.captcha": { + "message": "Gelöste Captchas", + "description": "About stat label: number of captchas solved" + }, + "landing.about.counter.antispam": { + "message": "Blockierte Spams", + "description": "About stat label: number of spam messages blocked" + }, + "landing.about.counter.users": { + "message": "Geschützte Nutzer", + "description": "About stat label: number of protected users" + }, + "landing.features.title": { + "message": "Unsere {highlight}", + "description": "Features section title; {highlight} renders the gradient-highlighted word" + }, + "landing.features.title.highlight": { + "message": "Funktionen", + "description": "Highlighted word inside the features section title" + }, + "landing.features.subtitle": { + "message": "Erfahren Sie, warum wir zu den besten Bots gehören, um Ihren Discord-Server vor böswilligen Nutzern zu schützen.", + "description": "Features section subtitle/description" + }, + "landing.features.anti-spam.title": { + "message": "Anti-Spam-Schutz", + "description": "Feature card title: anti-spam" + }, + "landing.features.anti-spam.description": { + "message": "Sanktionieren Sie Spam-Versuche sofort, ganz ohne Ihr Zutun.", + "description": "Feature card description: anti-spam" + }, + "landing.features.raid.title": { + "message": "Raid-Schutz", + "description": "Feature card title: raid" + }, + "landing.features.raid.description": { + "message": "Fürchten Sie einen Raid? Unser Bot erkennt und blockiert ihn, noch bevor er Ihren Server beeinträchtigt.", + "description": "Feature card description: raid" + }, + "landing.features.captcha.title": { + "message": "Bot-Schutz durch Captcha", + "description": "Feature card title: captcha" + }, + "landing.features.captcha.description": { + "message": "Dank Captcha müssen neue Mitglieder beweisen, dass sie menschlich sind. Verabschieden Sie sich von automatisierten Accounts.", + "description": "Feature card description: captcha" + }, + "landing.features.mod.title": { + "message": "Moderation & Verwaltung", + "description": "Feature card title: mod" + }, + "landing.features.mod.description": { + "message": "Verwalten Sie Ihren Server wie ein Profi mit unseren umfassenden Moderations- und Verwaltungsfunktionen.", + "description": "Feature card description: mod" + }, + "landing.features.tag.title": { + "message": "Tag-Rolle", + "description": "Feature card title: tag" + }, + "landing.features.tag.description": { + "message": "Die Tag-Rolle weist Mitgliedern automatisch eine Rolle zu, wenn sie den Tag Ihres Servers hinzufügen.", + "description": "Feature card description: tag" + }, + "landing.features.dm.title": { + "message": "Privatnachrichten sperren", + "description": "Feature card title: dm" + }, + "landing.features.dm.description": { + "message": "Ein einzigartiger Schutzschild gegen Spam, Betrug und Scam in Direktnachrichten.", + "description": "Feature card description: dm" + }, + "landing.pricing.title": { + "message": "Einen Schritt {highlight} bleiben", + "description": "Pricing section title; {highlight} renders the gradient-highlighted word" + }, + "landing.pricing.title.highlight": { + "message": "voraus", + "description": "Highlighted word inside the pricing section title" + }, + "landing.pricing.description": { + "message": "Fügen Sie RaidProtect hinzu und beginnen Sie noch heute damit, Ihren Server zu schützen.", + "description": "Pricing section description below the title" + }, + "landing.pricing.basic.preTitle": { + "message": "Basic", + "description": "Pricing card pre-title for the Basic tier" + }, + "landing.pricing.basic.price": { + "message": "Kostenlos", + "description": "Pricing card price label for the Basic tier (free)" + }, + "landing.pricing.basic.tagline": { + "message": "Wesentliche Sicherheit dauerhaft garantiert", + "description": "Pricing card tagline for the Basic tier" + }, + "landing.pricing.basic.feature.1": { + "message": "Anti-Spam-Schutz", + "description": "Basic tier feature 1" + }, + "landing.pricing.basic.feature.2": { + "message": "Automatische Raid-Blockierung", + "description": "Basic tier feature 2" + }, + "landing.pricing.basic.feature.3": { + "message": "Filterung böswilliger Bots", + "description": "Basic tier feature 3" + }, + "landing.pricing.basic.feature.4": { + "message": "Moderation & Verwaltung", + "description": "Basic tier feature 4" + }, + "landing.pricing.basic.feature.5": { + "message": "Und vieles mehr...", + "description": "Basic tier feature 5 (emphasis)" + }, + "landing.pricing.basic.button": { + "message": "Zu Discord hinzufügen", + "description": "Basic tier CTA button: invite the bot" + }, + "landing.pricing.founder.preTitle": { + "message": "Founder", + "description": "Pricing card pre-title for the Founder tier" + }, + "landing.pricing.founder.price.label": { + "message": "Abonnement", + "description": "Founder tier price label (subscription)" + }, + "landing.pricing.founder.price.amount": { + "message": "2,99 $", + "description": "Founder tier price amount; stays the same across locales" + }, + "landing.pricing.founder.tagline": { + "message": "Einführungsangebot für die ersten Abonnenten", + "description": "Pricing card tagline for the Founder tier" + }, + "landing.pricing.founder.feature.1": { + "message": "Anpassbares Bot-Profil", + "description": "Founder tier feature 1" + }, + "landing.pricing.founder.feature.2": { + "message": "Eigene Sanktionsnamen", + "description": "Founder tier feature 2" + }, + "landing.pricing.founder.feature.3": { + "message": "Erweiterter Zugriff auf Auth Manager", + "description": "Founder tier feature 3" + }, + "landing.pricing.founder.feature.4": { + "message": "Erweiterter Zugriff auf Display Public", + "description": "Founder tier feature 4" + }, + "landing.pricing.founder.feature.5": { + "message": "Zugang zur öffentlichen Beta", + "description": "Founder tier feature 5" + }, + "landing.pricing.founder.feature.6": { + "message": "Exklusive Rolle auf unserem Server", + "description": "Founder tier feature 6" + }, + "landing.pricing.founder.button": { + "message": "Über Discord abonnieren", + "description": "Founder tier CTA button: subscribe" + }, + "landing.pricing.business.preTitle": { + "message": "Business", + "description": "Pricing card pre-title for the Business tier" + }, + "landing.pricing.business.price": { + "message": "Auf Anfrage", + "description": "Pricing card price label for the Business tier (on request)" + }, + "landing.pricing.business.tagline": { + "message": "Für Projekte mit hohen Sicherheitsanforderungen", + "description": "Pricing card tagline for the Business tier" + }, + "landing.pricing.business.feature.1": { + "message": "Alle Founder-Funktionen", + "description": "Business tier feature 1" + }, + "landing.pricing.business.feature.2": { + "message": "Dedizierte, isolierte Instanz", + "description": "Business tier feature 2" + }, + "landing.pricing.business.feature.3": { + "message": "Initiales Audit Ihres Servers", + "description": "Business tier feature 3" + }, + "landing.pricing.business.feature.4": { + "message": "Integration mit Ihren Tools", + "description": "Business tier feature 4" + }, + "landing.pricing.business.feature.5": { + "message": "Maßgeschneiderte Funktionen", + "description": "Business tier feature 5" + }, + "landing.pricing.business.feature.6": { + "message": "Regelmäßige Termine mit einem Experten", + "description": "Business tier feature 6" + }, + "landing.pricing.business.feature.7": { + "message": "Priorisierter Support", + "description": "Business tier feature 7" + }, + "landing.pricing.business.button": { + "message": "Termin vereinbaren", + "description": "Business tier CTA button: book a meeting" + }, + "thankYou.head.title": { + "message": "Vielen Dank | RaidProtect", + "description": "Browser tab title for the thank-you page" + }, + "thankYou.head.description": { + "message": "Vielen Dank, dass Sie RaidProtect eingeladen haben! Zum Einstieg empfehlen wir, unsere Dokumentation zu konsultieren und unserem Discord-Server beizutreten.", + "description": "Meta description for the thank-you page" + }, + "thankYou.title": { + "message": "Vielen Dank, dass Sie RaidProtect eingeladen haben!", + "description": "Main heading of the thank-you page" + }, + "thankYou.description": { + "message": "Zum Einstieg empfehlen wir, unsere Dokumentation zu konsultieren und unserem Server beizutreten.", + "description": "Body description of the thank-you page" + }, + "thankYou.cta.joinDiscord": { + "message": "Unserem Discord-Server beitreten", + "description": "Primary CTA on the thank-you page: join the Discord server" + }, + "thankYou.cta.viewDocs": { + "message": "Dokumentation anzeigen", + "description": "Secondary CTA on the thank-you page: open the documentation" + }, + "thankYou.server.unknown": { + "message": "Unbekannter Server", + "description": "Fallback name shown when the Discord widget returns no server name" + }, + "thankYou.server.memberCountUnknown": { + "message": "Mitgliederzahl unbekannt", + "description": "Fallback shown when the Discord widget returns no member count" + }, + "thankYou.server.membersOnline": { + "message": "{count} Mitglieder online", + "description": "Number of members currently online on the invited server" + }, + "thankYou.permissions.adminOnlyWarning": { + "message": "⚠️ Alle spezifischen Berechtigungen sind erteilt, aber ohne die Administrator-Berechtigung kann der Bot möglicherweise nicht auf alle Kanäle zugreifen.", + "description": "Warning shown when the only missing permission is Administrator" + }, + "thankYou.permissions.missingWarning": { + "message": "⚠️ Damit der Bot ordnungsgemäß funktioniert, empfehlen wir, folgende Berechtigungen hinzuzufügen:", + "description": "Warning shown when some required permissions are missing" + }, + "thankYou.social.discord.ariaLabel": { + "message": "Discord", + "description": "ARIA label for the Discord social link" + }, + "thankYou.social.x.ariaLabel": { + "message": "X", + "description": "ARIA label for the X (Twitter) social link" + }, + "thankYou.social.youtube.ariaLabel": { + "message": "YouTube", + "description": "ARIA label for the YouTube social link" + }, + "thankYou.social.github.ariaLabel": { + "message": "GitHub", + "description": "ARIA label for the GitHub social link" + }, + "permission.administrator": { + "message": "Administrator", + "description": "Discord permission name" + }, + "permission.manageServer": { + "message": "Server verwalten", + "description": "Discord permission name" + }, + "permission.manageRoles": { + "message": "Rollen verwalten", + "description": "Discord permission name" + }, + "permission.manageChannels": { + "message": "Kanäle verwalten", + "description": "Discord permission name" + }, + "permission.kickMembers": { + "message": "Mitglieder kicken", + "description": "Discord permission name" + }, + "permission.banMembers": { + "message": "Mitglieder bannen", + "description": "Discord permission name" + }, + "permission.manageNicknames": { + "message": "Spitznamen verwalten", + "description": "Discord permission name" + }, + "permission.manageWebhooks": { + "message": "Webhooks verwalten", + "description": "Discord permission name" + }, + "permission.viewAuditLog": { + "message": "Audit-Log anzeigen", + "description": "Discord permission name" + }, + "permission.viewChannels": { + "message": "Kanäle anzeigen", + "description": "Discord permission name" + }, + "permission.moderateMembers": { + "message": "Mitglieder moderieren", + "description": "Discord permission name" + }, + "permission.sendMessages": { + "message": "Nachrichten senden", + "description": "Discord permission name" + }, + "permission.manageMessages": { + "message": "Nachrichten verwalten", + "description": "Discord permission name" + }, + "permission.manageThreads": { + "message": "Threads verwalten", + "description": "Discord permission name" + }, + "permission.embedLinks": { + "message": "Links einbetten", + "description": "Discord permission name" + }, + "permission.attachFiles": { + "message": "Dateien anhängen", + "description": "Discord permission name" + }, + "permission.readMessageHistory": { + "message": "Nachrichtenverlauf lesen", + "description": "Discord permission name" + }, + "permission.addReactions": { + "message": "Reaktionen hinzufügen", + "description": "Discord permission name" + }, + "permission.useExternalEmojis": { + "message": "Externe Emojis verwenden", + "description": "Discord permission name" + }, + "permission.muteMembers": { + "message": "Mitglieder stummschalten", + "description": "Discord permission name" + }, + "permission.deafenMembers": { + "message": "Mitglieder taub schalten", + "description": "Discord permission name" + }, + "permission.moveMembers": { + "message": "Mitglieder verschieben", + "description": "Discord permission name" } } diff --git a/i18n/de/docusaurus-plugin-content-pages/index.tsx b/i18n/de/docusaurus-plugin-content-pages/index.tsx deleted file mode 100644 index 3663819..0000000 --- a/i18n/de/docusaurus-plugin-content-pages/index.tsx +++ /dev/null @@ -1,435 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import clsx from 'clsx'; -import Layout from '@theme/Layout'; -import Link from '@docusaurus/Link'; -import Hero from '@site/src/components/landing/Hero'; -import Servers from '@site/src/components/landing/Servers'; -import shared from '@site/src/components/landing/styles/shared.module.css'; -import styles from '@site/src/pages/index.module.css'; - -type Counts = { - servers: number; - users: number; - captcha: number; - antispam: number; -}; - -type FormattedValue = { - value: string; - unit: string; -}; - -function formatValue(value: number): FormattedValue { - if (value >= 1_000_000) { - return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; - } - return {value: (value / 1_000).toFixed(1), unit: 'k'}; -} - -function StatCounter({ - rawValue, - label, - fallback, -}: { - rawValue?: number; - label: string; - fallback: string; -}) { - if (rawValue == null) { - return ( -
-
- {fallback} -
-
{label}
-
- ); - } - const formatted = formatValue(rawValue); - return ( -
-
- {formatted.value} - {formatted.unit} -
-
{label}
-
- ); -} - -const CHECK_ICON = '/img/landing/icon-02.svg'; - -function FeatureItem({children}: {children: ReactNode}) { - return ( -
- -
{children}
-
- ); -} - -type Feature = { - to: string; - icon: string; - iconAlt: string; - title: string; - description: string; -}; - -const FEATURES: Feature[] = [ - { - to: '/docs/features/anti-spam', - icon: '/img/landing/iconAntispamWhite.svg', - iconAlt: 'RaidProtect icon Antispam', - title: 'Anti-Spam-Schutz', - description: - 'Sanktionieren Sie Spam-Versuche sofort, ganz ohne Ihr Zutun.', - }, - { - to: '/docs/features/raid-mode', - icon: '/img/landing/iconAntiraidWhite.svg', - iconAlt: 'RaidProtect icon Antiraid', - title: 'Raid-Schutz', - description: - 'Fürchten Sie einen Raid? Unser Bot erkennt und blockiert ihn, noch bevor er Ihren Server beeinträchtigt.', - }, - { - to: '/docs/features/captcha', - icon: '/img/landing/iconCaptchaWhite.svg', - iconAlt: 'RaidProtect icon Captcha', - title: 'Bot-Schutz durch Captcha', - description: - 'Dank Captcha müssen neue Mitglieder beweisen, dass sie menschlich sind. Verabschieden Sie sich von automatisierten Accounts.', - }, - { - to: '/docs/features/utilities', - icon: '/img/landing/iconReportWhite.svg', - iconAlt: 'RaidProtect icon Report', - title: 'Moderation & Verwaltung', - description: - 'Verwalten Sie Ihren Server wie ein Profi mit unseren umfassenden Moderations- und Verwaltungsfunktionen.', - }, - { - to: '/docs/features/tag-role', - icon: '/img/landing/iconTagWhite.svg', - iconAlt: 'RaidProtect icon Tag', - title: 'Tag-Rolle', - description: - 'Die Tag-Rolle weist Mitgliedern automatisch eine Rolle zu, wenn sie den Tag Ihres Servers hinzufügen.', - }, - { - to: '/docs/features/dm-lock', - icon: '/img/landing/iconDmlockWhite.svg', - iconAlt: 'RaidProtect icon DM Lock', - title: 'Privatnachrichten sperren', - description: - 'Ein einzigartiger Schutzschild gegen Spam, Betrug und Scam in Direktnachrichten.', - }, -]; - -export default function Home(): ReactNode { - const [counts, setCounts] = useState(null); - - useEffect(() => { - let cancelled = false; - fetch('https://docs.raidprotect.bot/counts.json') - .then((res) => { - if (!res.ok) throw new Error('Failed to fetch counts'); - return res.json(); - }) - .then((data: Counts) => { - if (!cancelled) setCounts(data); - }) - .catch((err) => { - // Stats are best-effort; failure is non-blocking - // eslint-disable-next-line no-console - console.error('Stats update error:', err); - }); - return () => { - cancelled = true; - }; - }, []); - - return ( - -
- - - - {/* About */} -
-
-
-

- Unsere{' '} - Ergebnisse sprechen - für sich -

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-
- - {/* Features */} -
-
-
-

- Unsere Funktionen -

-

- Erfahren Sie, warum wir zu den besten Bots gehören, um Ihren - Discord-Server vor böswilligen Nutzern zu schützen. -

-
-
- -
-
- - {/* Pricing */} -
-
-
-
-

- Einen Schritt{' '} - voraus bleiben -

-

- Fügen Sie RaidProtect hinzu und beginnen Sie noch heute damit, - Ihren Server zu schützen. -

-
- -
- {/* Basic */} -
-
Basic
-
-

Kostenlos

-
-

- Wesentliche Sicherheit dauerhaft garantiert -

-
- Anti-Spam-Schutz - Automatische Raid-Blockierung - Filterung böswilliger Bots - Moderation & Verwaltung - - Und vieles mehr... - -
- -
- - {/* Founder */} -
-
- Founder -
-
-

Abonnement

-

- 2,99 $ -

-
-

- Einführungsangebot für die ersten Abonnenten -

-
- Anpassbares Bot-Profil - Eigene Sanktionsnamen - Erweiterter Zugriff auf Auth Manager - Erweiterter Zugriff auf Display Public - Zugang zur öffentlichen Beta - Exklusive Rolle auf unserem Server -
- -
- - {/* Business */} -
-
- Business -
-
-

Auf Anfrage

-
-

- Für Projekte mit hohen Sicherheitsanforderungen -

-
- Alle Founder-Funktionen - Dedizierte, isolierte Instanz - Initiales Audit Ihres Servers - Integration mit Ihren Tools - Maßgeschneiderte Funktionen - Regelmäßige Termine mit einem Experten - Priorisierter Support -
- -
-
-
-
-
-
- ); -} diff --git a/i18n/de/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/de/docusaurus-plugin-content-pages/thank-you.tsx deleted file mode 100644 index 660ae82..0000000 --- a/i18n/de/docusaurus-plugin-content-pages/thank-you.tsx +++ /dev/null @@ -1,400 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import Head from '@docusaurus/Head'; -import Link from '@docusaurus/Link'; -import styles from '@site/src/pages/thank-you.module.css'; - -type ServerBadge = 'partner' | 'verified' | null; - -type ServerInfo = { - name: string; - members: string; - inviteUrl: string; - iconUrl: string; - badge: ServerBadge; -}; - -type PermissionWarning = { - tone: 'admin' | 'missing'; - message: string; - missing: string[]; -}; - -const DEFAULT_ICON_URL = - 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; - -const BADGE_SRC: Record, string> = { - partner: '/img/landing/serverBadgePartner.svg', - verified: '/img/landing/serverBadgeVerified.svg', -}; - -const REQUIRED_PERMISSIONS = 1117660769534n; -const ADMIN_PERMISSION = 8n; - -// Discord-style permission names (German). -const PERMISSION_MESSAGES: Array<[string, bigint]> = [ - ['Administrator', ADMIN_PERMISSION], - ['Server verwalten', 32n], - ['Rollen verwalten', 268435456n], - ['Kanäle verwalten', 16n], - ['Mitglieder kicken', 2n], - ['Mitglieder bannen', 4n], - ['Spitznamen verwalten', 134217728n], - ['Webhooks verwalten', 536870912n], - ['Audit-Log anzeigen', 524288n], - ['Kanäle anzeigen', 1024n], - ['Mitglieder moderieren', 1099511627776n], - ['Nachrichten senden', 2048n], - ['Nachrichten verwalten', 8192n], - ['Threads verwalten', 17179869184n], - ['Links einbetten', 16384n], - ['Dateien anhängen', 32768n], - ['Nachrichtenverlauf lesen', 65536n], - ['Reaktionen hinzufügen', 64n], - ['Externe Emojis verwenden', 262144n], - ['Mitglieder stummschalten', 4194304n], - ['Mitglieder taub schalten', 8388608n], - ['Mitglieder verschieben', 16777216n], -]; - -async function fetchServerInfo(guildId: string): Promise { - try { - const widgetResponse = await fetch( - `https://discord.com/api/guilds/${guildId}/widget.json`, - ); - if (!widgetResponse.ok) { - throw new Error('Widget deaktiviert.'); - } - const widgetData = await widgetResponse.json(); - - const name: string = widgetData.name || 'Unbekannter Server'; - const members: string = - typeof widgetData.presence_count === 'number' - ? `${widgetData.presence_count} Mitglieder online` - : 'Mitgliederzahl unbekannt'; - const inviteUrl: string = widgetData.instant_invite || '#'; - - let iconUrl = DEFAULT_ICON_URL; - let badge: ServerBadge = null; - - const inviteCode: string | null = widgetData.instant_invite - ? widgetData.instant_invite.split('/').pop() || null - : null; - - if (inviteCode) { - const inviteResponse = await fetch( - `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, - ); - if (inviteResponse.ok) { - const inviteData = await inviteResponse.json(); - const server = inviteData.guild; - if (server?.icon) { - iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; - } - if (Array.isArray(server?.features)) { - if (server.features.includes('PARTNERED')) { - badge = 'partner'; - } else if (server.features.includes('VERIFIED')) { - badge = 'verified'; - } - } - } - } - - return {name, members, inviteUrl, iconUrl, badge}; - } catch (error) { - // Discord widget may be disabled or network may be unreachable. - // eslint-disable-next-line no-console - console.error(error); - return null; - } -} - -function computePermissionWarning( - permissionsParam: string, -): PermissionWarning | null { - let currentPermissions: bigint; - try { - currentPermissions = BigInt(permissionsParam); - } catch { - return null; - } - - const hasAdminPermission = - (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; - const missingPermissions = PERMISSION_MESSAGES.filter( - ([, value]) => - (REQUIRED_PERMISSIONS & value) === value && - (currentPermissions & value) !== value, - ).map(([name]) => name); - - if (hasAdminPermission || missingPermissions.length === 0) { - return null; - } - - if ( - missingPermissions.length === 1 && - missingPermissions[0] === 'Administrator' - ) { - return { - tone: 'admin', - message: - '⚠️ Alle spezifischen Berechtigungen sind erteilt, aber ohne die Administrator-Berechtigung kann der Bot möglicherweise nicht auf alle Kanäle zugreifen.', - missing: [], - }; - } - - return { - tone: 'missing', - message: - '⚠️ Damit der Bot ordnungsgemäß funktioniert, empfehlen wir, folgende Berechtigungen hinzuzufügen:', - missing: missingPermissions, - }; -} - -export default function ThankYou(): ReactNode { - const [serverInfo, setServerInfo] = useState(null); - const [permissionWarning, setPermissionWarning] = - useState(null); - - useEffect(() => { - if (typeof window === 'undefined') return; - - const urlParams = new URLSearchParams(window.location.search); - - const guildId = urlParams.get('guild_id'); - let cancelled = false; - if (guildId) { - fetchServerInfo(guildId).then((info) => { - if (!cancelled && info) { - setServerInfo(info); - } - }); - } - - const permissionsParam = urlParams.get('permissions'); - if (permissionsParam) { - setPermissionWarning(computePermissionWarning(permissionsParam)); - } - - // Auto-redirect to Discord invite after 60s, matching the Webflow page. - const redirectTimer = window.setTimeout(() => { - window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; - }, 60000); - - return () => { - cancelled = true; - window.clearTimeout(redirectTimer); - }; - }, []); - - return ( - <> - - Vielen Dank | RaidProtect - - -
-
- - RaidProtect title logo - - -

- Vielen Dank, dass Sie RaidProtect eingeladen haben! -

- - {serverInfo && ( - - )} - - {permissionWarning && ( -
- {permissionWarning.message} - {permissionWarning.missing.length > 0 && ( -
    - {permissionWarning.missing.map((perm) => ( -
  • - {perm} -
  • - ))} -
- )} -
- )} - -

- Zum Einstieg empfehlen wir, unsere Dokumentation zu konsultieren und - unserem Server beizutreten. -

- -
- - Unserem Discord-Server beitreten - - - Dokumentation anzeigen - -
- - -
- -
- - ); -} diff --git a/i18n/en/code.json b/i18n/en/code.json index 3455dcd..0020eca 100644 --- a/i18n/en/code.json +++ b/i18n/en/code.json @@ -467,5 +467,385 @@ "frame.cta.secondary": { "message": "Join the server", "description": "Frame CTA secondary button: join the Discord server" + }, + "landing.layout.title": { + "message": "RaidProtect • Secure your Discord server", + "description": "Browser tab title for the landing page" + }, + "landing.layout.description": { + "message": "RaidProtect is a French Discord bot whose mission is to easily protect your server from malicious users.", + "description": "Meta description for the landing page" + }, + "landing.about.title": { + "message": "Our results have an {highlight}", + "description": "About section title; {highlight} renders the gradient-highlighted word" + }, + "landing.about.title.highlight": { + "message": "impact", + "description": "Highlighted word inside the about section title" + }, + "landing.about.curve.ariaLabel": { + "message": "Growth in the number of servers", + "description": "ARIA label for the decorative SVG curve in the about section" + }, + "landing.about.counter.servers": { + "message": "Secure servers", + "description": "About stat label: number of secured servers" + }, + "landing.about.counter.captcha": { + "message": "Captchas solved", + "description": "About stat label: number of captchas solved" + }, + "landing.about.counter.antispam": { + "message": "Spam blocked", + "description": "About stat label: number of spam messages blocked" + }, + "landing.about.counter.users": { + "message": "Protected users", + "description": "About stat label: number of protected users" + }, + "landing.features.title": { + "message": "Our {highlight}", + "description": "Features section title; {highlight} renders the gradient-highlighted word" + }, + "landing.features.title.highlight": { + "message": "features", + "description": "Highlighted word inside the features section title" + }, + "landing.features.subtitle": { + "message": "Find out what makes us one of the best bots for protecting your Discord server from malicious users.", + "description": "Features section subtitle/description" + }, + "landing.features.anti-spam.title": { + "message": "Anti-spam protection", + "description": "Feature card title: anti-spam" + }, + "landing.features.anti-spam.description": { + "message": "Sanction spam attempts instantly, without any intervention on your part.", + "description": "Feature card description: anti-spam" + }, + "landing.features.raid.title": { + "message": "Blocking raids", + "description": "Feature card title: raid" + }, + "landing.features.raid.description": { + "message": "Worried about a raid? Our bot is capable of detecting and blocking it before it even impacts your server.", + "description": "Feature card description: raid" + }, + "landing.features.captcha.title": { + "message": "Protection against bots", + "description": "Feature card title: captcha" + }, + "landing.features.captcha.description": { + "message": "With captcha, your members have to prove they're human. Say goodbye to automated accounts.", + "description": "Feature card description: captcha" + }, + "landing.features.mod.title": { + "message": "Moderation & administration", + "description": "Feature card title: mod" + }, + "landing.features.mod.description": { + "message": "Manage your server like a pro with our various moderation and administration features.", + "description": "Feature card description: mod" + }, + "landing.features.tag.title": { + "message": "Tag Role", + "description": "Feature card title: tag" + }, + "landing.features.tag.description": { + "message": "Tag Role automatically assigns a role to members who add your server's tag.", + "description": "Feature card description: tag" + }, + "landing.features.dm.title": { + "message": "DM Lock", + "description": "Feature card title: dm" + }, + "landing.features.dm.description": { + "message": "An unprecedented shield against spam, scam and private message scams.", + "description": "Feature card description: dm" + }, + "landing.pricing.title": { + "message": "Keeping one step {highlight}", + "description": "Pricing section title; {highlight} renders the gradient-highlighted word" + }, + "landing.pricing.title.highlight": { + "message": "ahead", + "description": "Highlighted word inside the pricing section title" + }, + "landing.pricing.description": { + "message": "Add RaidProtect and start protecting your server today.", + "description": "Pricing section description below the title" + }, + "landing.pricing.basic.preTitle": { + "message": "Basic", + "description": "Pricing card pre-title for the Basic tier" + }, + "landing.pricing.basic.price": { + "message": "Free", + "description": "Pricing card price label for the Basic tier (free)" + }, + "landing.pricing.basic.tagline": { + "message": "Essential security guaranteed forever", + "description": "Pricing card tagline for the Basic tier" + }, + "landing.pricing.basic.feature.1": { + "message": "Anti-spam protections", + "description": "Basic tier feature 1" + }, + "landing.pricing.basic.feature.2": { + "message": "Automatic raid blocking", + "description": "Basic tier feature 2" + }, + "landing.pricing.basic.feature.3": { + "message": "Malicious bot filtering", + "description": "Basic tier feature 3" + }, + "landing.pricing.basic.feature.4": { + "message": "Moderation & administration", + "description": "Basic tier feature 4" + }, + "landing.pricing.basic.feature.5": { + "message": "And much more...", + "description": "Basic tier feature 5 (emphasis)" + }, + "landing.pricing.basic.button": { + "message": "Add to Discord", + "description": "Basic tier CTA button: invite the bot" + }, + "landing.pricing.founder.preTitle": { + "message": "Founder", + "description": "Pricing card pre-title for the Founder tier" + }, + "landing.pricing.founder.price.label": { + "message": "Subscription", + "description": "Founder tier price label (subscription)" + }, + "landing.pricing.founder.price.amount": { + "message": "$2.99", + "description": "Founder tier price amount; stays the same across locales" + }, + "landing.pricing.founder.tagline": { + "message": "Launch offer reserved for early subscribers", + "description": "Pricing card tagline for the Founder tier" + }, + "landing.pricing.founder.feature.1": { + "message": "Customisable bot profile", + "description": "Founder tier feature 1" + }, + "landing.pricing.founder.feature.2": { + "message": "Custom sanction names", + "description": "Founder tier feature 2" + }, + "landing.pricing.founder.feature.3": { + "message": "Advanced access to Auth Manager", + "description": "Founder tier feature 3" + }, + "landing.pricing.founder.feature.4": { + "message": "Extended access to Display Public", + "description": "Founder tier feature 4" + }, + "landing.pricing.founder.feature.5": { + "message": "Access to the public Beta", + "description": "Founder tier feature 5" + }, + "landing.pricing.founder.feature.6": { + "message": "Exclusive role on our server", + "description": "Founder tier feature 6" + }, + "landing.pricing.founder.button": { + "message": "Subscribe via Discord", + "description": "Founder tier CTA button: subscribe" + }, + "landing.pricing.business.preTitle": { + "message": "Business", + "description": "Pricing card pre-title for the Business tier" + }, + "landing.pricing.business.price": { + "message": "On request", + "description": "Pricing card price label for the Business tier (on request)" + }, + "landing.pricing.business.tagline": { + "message": "For projects with high security requirements", + "description": "Pricing card tagline for the Business tier" + }, + "landing.pricing.business.feature.1": { + "message": "All Founder features", + "description": "Business tier feature 1" + }, + "landing.pricing.business.feature.2": { + "message": "Dedicated, isolated instance", + "description": "Business tier feature 2" + }, + "landing.pricing.business.feature.3": { + "message": "Initial audit of your server", + "description": "Business tier feature 3" + }, + "landing.pricing.business.feature.4": { + "message": "Integration with your tools", + "description": "Business tier feature 4" + }, + "landing.pricing.business.feature.5": { + "message": "Custom features", + "description": "Business tier feature 5" + }, + "landing.pricing.business.feature.6": { + "message": "Regular check-ins with an expert", + "description": "Business tier feature 6" + }, + "landing.pricing.business.feature.7": { + "message": "Priority support", + "description": "Business tier feature 7" + }, + "landing.pricing.business.button": { + "message": "Book a meeting", + "description": "Business tier CTA button: book a meeting" + }, + "thankYou.head.title": { + "message": "Thank you | RaidProtect", + "description": "Browser tab title for the thank-you page" + }, + "thankYou.head.description": { + "message": "Thank you for inviting RaidProtect! To get started, we recommend checking out our documentation and joining our Discord server.", + "description": "Meta description for the thank-you page" + }, + "thankYou.title": { + "message": "Thank you for inviting RaidProtect!", + "description": "Main heading of the thank-you page" + }, + "thankYou.description": { + "message": "To get started, we recommend checking our documentation and joining our server.", + "description": "Body description of the thank-you page" + }, + "thankYou.cta.joinDiscord": { + "message": "Join our Discord server", + "description": "Primary CTA on the thank-you page: join the Discord server" + }, + "thankYou.cta.viewDocs": { + "message": "View documentation", + "description": "Secondary CTA on the thank-you page: open the documentation" + }, + "thankYou.server.unknown": { + "message": "Unknown server", + "description": "Fallback name shown when the Discord widget returns no server name" + }, + "thankYou.server.memberCountUnknown": { + "message": "Member count unknown", + "description": "Fallback shown when the Discord widget returns no member count" + }, + "thankYou.server.membersOnline": { + "message": "{count} members online", + "description": "Number of members currently online on the invited server" + }, + "thankYou.permissions.adminOnlyWarning": { + "message": "⚠️ All specific permissions are granted, but without the Administrator permission, the bot may not be able to access every channel.", + "description": "Warning shown when the only missing permission is Administrator" + }, + "thankYou.permissions.missingWarning": { + "message": "⚠️ To ensure the bot runs properly, we recommend adding the following permissions:", + "description": "Warning shown when some required permissions are missing" + }, + "thankYou.social.discord.ariaLabel": { + "message": "Discord", + "description": "ARIA label for the Discord social link" + }, + "thankYou.social.x.ariaLabel": { + "message": "X", + "description": "ARIA label for the X (Twitter) social link" + }, + "thankYou.social.youtube.ariaLabel": { + "message": "YouTube", + "description": "ARIA label for the YouTube social link" + }, + "thankYou.social.github.ariaLabel": { + "message": "GitHub", + "description": "ARIA label for the GitHub social link" + }, + "permission.administrator": { + "message": "Administrator", + "description": "Discord permission name" + }, + "permission.manageServer": { + "message": "Manage Server", + "description": "Discord permission name" + }, + "permission.manageRoles": { + "message": "Manage Roles", + "description": "Discord permission name" + }, + "permission.manageChannels": { + "message": "Manage Channels", + "description": "Discord permission name" + }, + "permission.kickMembers": { + "message": "Kick Members", + "description": "Discord permission name" + }, + "permission.banMembers": { + "message": "Ban Members", + "description": "Discord permission name" + }, + "permission.manageNicknames": { + "message": "Manage Nicknames", + "description": "Discord permission name" + }, + "permission.manageWebhooks": { + "message": "Manage Webhooks", + "description": "Discord permission name" + }, + "permission.viewAuditLog": { + "message": "View Audit Log", + "description": "Discord permission name" + }, + "permission.viewChannels": { + "message": "View Channels", + "description": "Discord permission name" + }, + "permission.moderateMembers": { + "message": "Moderate Members", + "description": "Discord permission name" + }, + "permission.sendMessages": { + "message": "Send Messages", + "description": "Discord permission name" + }, + "permission.manageMessages": { + "message": "Manage Messages", + "description": "Discord permission name" + }, + "permission.manageThreads": { + "message": "Manage Threads", + "description": "Discord permission name" + }, + "permission.embedLinks": { + "message": "Embed Links", + "description": "Discord permission name" + }, + "permission.attachFiles": { + "message": "Attach Files", + "description": "Discord permission name" + }, + "permission.readMessageHistory": { + "message": "Read Message History", + "description": "Discord permission name" + }, + "permission.addReactions": { + "message": "Add Reactions", + "description": "Discord permission name" + }, + "permission.useExternalEmojis": { + "message": "Use External Emojis", + "description": "Discord permission name" + }, + "permission.muteMembers": { + "message": "Mute Members", + "description": "Discord permission name" + }, + "permission.deafenMembers": { + "message": "Deafen Members", + "description": "Discord permission name" + }, + "permission.moveMembers": { + "message": "Move Members", + "description": "Discord permission name" } } diff --git a/i18n/en/docusaurus-plugin-content-pages/index.tsx b/i18n/en/docusaurus-plugin-content-pages/index.tsx deleted file mode 100644 index c92b354..0000000 --- a/i18n/en/docusaurus-plugin-content-pages/index.tsx +++ /dev/null @@ -1,433 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import clsx from 'clsx'; -import Layout from '@theme/Layout'; -import Link from '@docusaurus/Link'; -import Hero from '@site/src/components/landing/Hero'; -import Servers from '@site/src/components/landing/Servers'; -import shared from '@site/src/components/landing/styles/shared.module.css'; -import styles from '@site/src/pages/index.module.css'; - -type Counts = { - servers: number; - users: number; - captcha: number; - antispam: number; -}; - -type FormattedValue = { - value: string; - unit: string; -}; - -function formatValue(value: number): FormattedValue { - if (value >= 1_000_000) { - return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; - } - return {value: (value / 1_000).toFixed(1), unit: 'k'}; -} - -function StatCounter({ - rawValue, - label, - fallback, -}: { - rawValue?: number; - label: string; - fallback: string; -}) { - if (rawValue == null) { - return ( -
-
- {fallback} -
-
{label}
-
- ); - } - const formatted = formatValue(rawValue); - return ( -
-
- {formatted.value} - {formatted.unit} -
-
{label}
-
- ); -} - -const CHECK_ICON = '/img/landing/icon-02.svg'; - -function FeatureItem({children}: {children: ReactNode}) { - return ( -
- -
{children}
-
- ); -} - -type Feature = { - to: string; - icon: string; - iconAlt: string; - title: string; - description: string; -}; - -const FEATURES: Feature[] = [ - { - to: '/docs/features/anti-spam', - icon: '/img/landing/iconAntispamWhite.svg', - iconAlt: 'RaidProtect icon Antispam', - title: 'Anti-spam protection', - description: - 'Sanction spam attempts instantly, without any intervention on your part.', - }, - { - to: '/docs/features/raid-mode', - icon: '/img/landing/iconAntiraidWhite.svg', - iconAlt: 'RaidProtect icon Antiraid', - title: 'Blocking raids', - description: - 'Worried about a raid? Our bot is capable of detecting and blocking it before it even impacts your server.', - }, - { - to: '/docs/features/captcha', - icon: '/img/landing/iconCaptchaWhite.svg', - iconAlt: 'RaidProtect icon Captcha', - title: 'Protection against bots', - description: - "With captcha, your members have to prove they're human. Say goodbye to automated accounts.", - }, - { - to: '/docs/features/utilities', - icon: '/img/landing/iconReportWhite.svg', - iconAlt: 'RaidProtect icon Report', - title: 'Moderation & administration', - description: - 'Manage your server like a pro with our various moderation and administration features.', - }, - { - to: '/docs/features/tag-role', - icon: '/img/landing/iconTagWhite.svg', - iconAlt: 'RaidProtect icon Tag', - title: 'Tag Role', - description: - "Tag Role automatically assigns a role to members who add your server's tag.", - }, - { - to: '/docs/features/dm-lock', - icon: '/img/landing/iconDmlockWhite.svg', - iconAlt: 'RaidProtect icon DM Lock', - title: 'DM Lock', - description: - 'An unprecedented shield against spam, scam and private message scams.', - }, -]; - -export default function Home(): ReactNode { - const [counts, setCounts] = useState(null); - - useEffect(() => { - let cancelled = false; - fetch('https://docs.raidprotect.bot/counts.json') - .then((res) => { - if (!res.ok) throw new Error('Failed to fetch counts'); - return res.json(); - }) - .then((data: Counts) => { - if (!cancelled) setCounts(data); - }) - .catch((err) => { - // Stats are best-effort; failure is non-blocking - // eslint-disable-next-line no-console - console.error('Stats update error:', err); - }); - return () => { - cancelled = true; - }; - }, []); - - return ( - -
- - - - {/* About */} -
-
-
-

- Our results have an{' '} - impact -

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-
- - {/* Features */} -
-
-
-

- Our features -

-

- Find out what makes us one of the best bots for protecting your - Discord server from malicious users. -

-
-
- -
-
- - {/* Pricing */} -
-
-
-
-

- Keeping one step{' '} - ahead -

-

- Add RaidProtect and start protecting your server today. -

-
- -
- {/* Basic */} -
-
Basic
-
-

Free

-
-

- Essential security guaranteed forever -

-
- Anti-spam protections - Automatic raid blocking - Malicious bot filtering - Moderation & administration - - And much more... - -
- -
- - {/* Founder */} -
-
- Founder -
-
-

Subscription

-

- $2.99 -

-
-

- Launch offer reserved for early subscribers -

-
- Customisable bot profile - Custom sanction names - Advanced access to Auth Manager - Extended access to Display Public - Access to the public Beta - Exclusive role on our server -
- -
- - {/* Business */} -
-
- Business -
-
-

On request

-
-

- For projects with high security requirements -

-
- All Founder features - Dedicated, isolated instance - Initial audit of your server - Integration with your tools - Custom features - Regular check-ins with an expert - Priority support -
- -
-
-
-
-
-
- ); -} diff --git a/i18n/en/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/en/docusaurus-plugin-content-pages/thank-you.tsx deleted file mode 100644 index 4f87bcf..0000000 --- a/i18n/en/docusaurus-plugin-content-pages/thank-you.tsx +++ /dev/null @@ -1,398 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import Head from '@docusaurus/Head'; -import Link from '@docusaurus/Link'; -import styles from '@site/src/pages/thank-you.module.css'; - -type ServerBadge = 'partner' | 'verified' | null; - -type ServerInfo = { - name: string; - members: string; - inviteUrl: string; - iconUrl: string; - badge: ServerBadge; -}; - -type PermissionWarning = { - tone: 'admin' | 'missing'; - message: string; - missing: string[]; -}; - -const DEFAULT_ICON_URL = - 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; - -const BADGE_SRC: Record, string> = { - partner: '/img/landing/serverBadgePartner.svg', - verified: '/img/landing/serverBadgeVerified.svg', -}; - -const REQUIRED_PERMISSIONS = 1117660769534n; -const ADMIN_PERMISSION = 8n; - -// Discord-style permission names (English). -const PERMISSION_MESSAGES: Array<[string, bigint]> = [ - ['Administrator', ADMIN_PERMISSION], - ['Manage Server', 32n], - ['Manage Roles', 268435456n], - ['Manage Channels', 16n], - ['Kick Members', 2n], - ['Ban Members', 4n], - ['Manage Nicknames', 134217728n], - ['Manage Webhooks', 536870912n], - ['View Audit Log', 524288n], - ['View Channels', 1024n], - ['Moderate Members', 1099511627776n], - ['Send Messages', 2048n], - ['Manage Messages', 8192n], - ['Manage Threads', 17179869184n], - ['Embed Links', 16384n], - ['Attach Files', 32768n], - ['Read Message History', 65536n], - ['Add Reactions', 64n], - ['Use External Emojis', 262144n], - ['Mute Members', 4194304n], - ['Deafen Members', 8388608n], - ['Move Members', 16777216n], -]; - -async function fetchServerInfo(guildId: string): Promise { - try { - const widgetResponse = await fetch( - `https://discord.com/api/guilds/${guildId}/widget.json`, - ); - if (!widgetResponse.ok) { - throw new Error('Widget disabled.'); - } - const widgetData = await widgetResponse.json(); - - const name: string = widgetData.name || 'Unknown server'; - const members: string = - typeof widgetData.presence_count === 'number' - ? `${widgetData.presence_count} members online` - : 'Member count unknown'; - const inviteUrl: string = widgetData.instant_invite || '#'; - - let iconUrl = DEFAULT_ICON_URL; - let badge: ServerBadge = null; - - const inviteCode: string | null = widgetData.instant_invite - ? widgetData.instant_invite.split('/').pop() || null - : null; - - if (inviteCode) { - const inviteResponse = await fetch( - `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, - ); - if (inviteResponse.ok) { - const inviteData = await inviteResponse.json(); - const server = inviteData.guild; - if (server?.icon) { - iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; - } - if (Array.isArray(server?.features)) { - if (server.features.includes('PARTNERED')) { - badge = 'partner'; - } else if (server.features.includes('VERIFIED')) { - badge = 'verified'; - } - } - } - } - - return {name, members, inviteUrl, iconUrl, badge}; - } catch (error) { - // Discord widget may be disabled or network may be unreachable. - // eslint-disable-next-line no-console - console.error(error); - return null; - } -} - -function computePermissionWarning( - permissionsParam: string, -): PermissionWarning | null { - let currentPermissions: bigint; - try { - currentPermissions = BigInt(permissionsParam); - } catch { - return null; - } - - const hasAdminPermission = - (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; - const missingPermissions = PERMISSION_MESSAGES.filter( - ([, value]) => - (REQUIRED_PERMISSIONS & value) === value && - (currentPermissions & value) !== value, - ).map(([name]) => name); - - if (hasAdminPermission || missingPermissions.length === 0) { - return null; - } - - if ( - missingPermissions.length === 1 && - missingPermissions[0] === 'Administrator' - ) { - return { - tone: 'admin', - message: - '⚠️ All specific permissions are granted, but without the Administrator permission, the bot may not be able to access every channel.', - missing: [], - }; - } - - return { - tone: 'missing', - message: - '⚠️ To ensure the bot runs properly, we recommend adding the following permissions:', - missing: missingPermissions, - }; -} - -export default function ThankYou(): ReactNode { - const [serverInfo, setServerInfo] = useState(null); - const [permissionWarning, setPermissionWarning] = - useState(null); - - useEffect(() => { - if (typeof window === 'undefined') return; - - const urlParams = new URLSearchParams(window.location.search); - - const guildId = urlParams.get('guild_id'); - let cancelled = false; - if (guildId) { - fetchServerInfo(guildId).then((info) => { - if (!cancelled && info) { - setServerInfo(info); - } - }); - } - - const permissionsParam = urlParams.get('permissions'); - if (permissionsParam) { - setPermissionWarning(computePermissionWarning(permissionsParam)); - } - - // Auto-redirect to Discord invite after 60s, matching the Webflow page. - const redirectTimer = window.setTimeout(() => { - window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; - }, 60000); - - return () => { - cancelled = true; - window.clearTimeout(redirectTimer); - }; - }, []); - - return ( - <> - - Thank you | RaidProtect - - -
-
- - RaidProtect title logo - - -

Thank you for inviting RaidProtect!

- - {serverInfo && ( - - )} - - {permissionWarning && ( -
- {permissionWarning.message} - {permissionWarning.missing.length > 0 && ( -
    - {permissionWarning.missing.map((perm) => ( -
  • - {perm} -
  • - ))} -
- )} -
- )} - -

- To get started, we recommend checking our documentation and joining - our server. -

- -
- - Join our Discord server - - - View documentation - -
- - -
- -
- - ); -} diff --git a/i18n/es/code.json b/i18n/es/code.json index b990b1c..fa4b09d 100644 --- a/i18n/es/code.json +++ b/i18n/es/code.json @@ -467,5 +467,385 @@ "frame.cta.secondary": { "message": "Unirse al servidor", "description": "Frame CTA secondary button: join the Discord server" + }, + "landing.layout.title": { + "message": "RaidProtect • Protege tu servidor de Discord", + "description": "Browser tab title for the landing page" + }, + "landing.layout.description": { + "message": "RaidProtect es un bot de Discord francés cuya misión es proteger fácilmente tu servidor de usuarios malintencionados.", + "description": "Meta description for the landing page" + }, + "landing.about.title": { + "message": "Nuestros resultados tienen un {highlight}", + "description": "About section title; {highlight} renders the gradient-highlighted word" + }, + "landing.about.title.highlight": { + "message": "impacto", + "description": "Highlighted word inside the about section title" + }, + "landing.about.curve.ariaLabel": { + "message": "Crecimiento en el número de servidores", + "description": "ARIA label for the decorative SVG curve in the about section" + }, + "landing.about.counter.servers": { + "message": "Servidores protegidos", + "description": "About stat label: number of secured servers" + }, + "landing.about.counter.captcha": { + "message": "Captchas resueltos", + "description": "About stat label: number of captchas solved" + }, + "landing.about.counter.antispam": { + "message": "Spams bloqueados", + "description": "About stat label: number of spam messages blocked" + }, + "landing.about.counter.users": { + "message": "Usuarios protegidos", + "description": "About stat label: number of protected users" + }, + "landing.features.title": { + "message": "Nuestras {highlight}", + "description": "Features section title; {highlight} renders the gradient-highlighted word" + }, + "landing.features.title.highlight": { + "message": "funciones", + "description": "Highlighted word inside the features section title" + }, + "landing.features.subtitle": { + "message": "Descubre por qué somos uno de los mejores bots para proteger tu servidor de Discord contra usuarios malintencionados.", + "description": "Features section subtitle/description" + }, + "landing.features.anti-spam.title": { + "message": "Protección antispam", + "description": "Feature card title: anti-spam" + }, + "landing.features.anti-spam.description": { + "message": "Sanciona al instante los intentos de spam, sin necesidad de intervención por tu parte.", + "description": "Feature card description: anti-spam" + }, + "landing.features.raid.title": { + "message": "Bloqueo de raids", + "description": "Feature card title: raid" + }, + "landing.features.raid.description": { + "message": "¿Temes un raid? Nuestro bot puede detectarlo y bloquearlo antes de que afecte a tu servidor.", + "description": "Feature card description: raid" + }, + "landing.features.captcha.title": { + "message": "Protección contra bots", + "description": "Feature card title: captcha" + }, + "landing.features.captcha.description": { + "message": "Gracias al captcha, tus miembros deben demostrar que son humanos. Di adiós a las cuentas automatizadas.", + "description": "Feature card description: captcha" + }, + "landing.features.mod.title": { + "message": "Moderación y administración", + "description": "Feature card title: mod" + }, + "landing.features.mod.description": { + "message": "Administra tu servidor como un profesional con nuestras diversas funciones de moderación y administración.", + "description": "Feature card description: mod" + }, + "landing.features.tag.title": { + "message": "Rol por etiqueta", + "description": "Feature card title: tag" + }, + "landing.features.tag.description": { + "message": "El Rol por etiqueta asigna automáticamente un rol a los miembros que añaden la etiqueta de tu servidor.", + "description": "Feature card description: tag" + }, + "landing.features.dm.title": { + "message": "Bloqueo de MP", + "description": "Feature card title: dm" + }, + "landing.features.dm.description": { + "message": "Un escudo único contra el spam, el scam y las estafas por mensaje privado.", + "description": "Feature card description: dm" + }, + "landing.pricing.title": { + "message": "Mantente a la {highlight}", + "description": "Pricing section title; {highlight} renders the gradient-highlighted word" + }, + "landing.pricing.title.highlight": { + "message": "vanguardia", + "description": "Highlighted word inside the pricing section title" + }, + "landing.pricing.description": { + "message": "Añade RaidProtect y empieza a proteger tu servidor hoy mismo.", + "description": "Pricing section description below the title" + }, + "landing.pricing.basic.preTitle": { + "message": "Basic", + "description": "Pricing card pre-title for the Basic tier" + }, + "landing.pricing.basic.price": { + "message": "Gratis", + "description": "Pricing card price label for the Basic tier (free)" + }, + "landing.pricing.basic.tagline": { + "message": "La seguridad esencial garantizada para siempre", + "description": "Pricing card tagline for the Basic tier" + }, + "landing.pricing.basic.feature.1": { + "message": "Protecciones antispam", + "description": "Basic tier feature 1" + }, + "landing.pricing.basic.feature.2": { + "message": "Bloqueo automático de raids", + "description": "Basic tier feature 2" + }, + "landing.pricing.basic.feature.3": { + "message": "Filtrado de bots maliciosos", + "description": "Basic tier feature 3" + }, + "landing.pricing.basic.feature.4": { + "message": "Moderación y administración", + "description": "Basic tier feature 4" + }, + "landing.pricing.basic.feature.5": { + "message": "Y mucho más...", + "description": "Basic tier feature 5 (emphasis)" + }, + "landing.pricing.basic.button": { + "message": "Añadir a Discord", + "description": "Basic tier CTA button: invite the bot" + }, + "landing.pricing.founder.preTitle": { + "message": "Founder", + "description": "Pricing card pre-title for the Founder tier" + }, + "landing.pricing.founder.price.label": { + "message": "Suscripción", + "description": "Founder tier price label (subscription)" + }, + "landing.pricing.founder.price.amount": { + "message": "2,99 $", + "description": "Founder tier price amount; stays the same across locales" + }, + "landing.pricing.founder.tagline": { + "message": "Oferta de lanzamiento reservada a los primeros suscriptores", + "description": "Pricing card tagline for the Founder tier" + }, + "landing.pricing.founder.feature.1": { + "message": "Perfil del bot personalizable", + "description": "Founder tier feature 1" + }, + "landing.pricing.founder.feature.2": { + "message": "Nombres de sanciones personalizados", + "description": "Founder tier feature 2" + }, + "landing.pricing.founder.feature.3": { + "message": "Acceso avanzado al Auth Manager", + "description": "Founder tier feature 3" + }, + "landing.pricing.founder.feature.4": { + "message": "Acceso ampliado al Display Public", + "description": "Founder tier feature 4" + }, + "landing.pricing.founder.feature.5": { + "message": "Acceso a la Beta pública", + "description": "Founder tier feature 5" + }, + "landing.pricing.founder.feature.6": { + "message": "Rol exclusivo en nuestro servidor", + "description": "Founder tier feature 6" + }, + "landing.pricing.founder.button": { + "message": "Suscribirse vía Discord", + "description": "Founder tier CTA button: subscribe" + }, + "landing.pricing.business.preTitle": { + "message": "Business", + "description": "Pricing card pre-title for the Business tier" + }, + "landing.pricing.business.price": { + "message": "Bajo solicitud", + "description": "Pricing card price label for the Business tier (on request)" + }, + "landing.pricing.business.tagline": { + "message": "Para proyectos con altos requisitos de seguridad", + "description": "Pricing card tagline for the Business tier" + }, + "landing.pricing.business.feature.1": { + "message": "Todas las funciones Founder", + "description": "Business tier feature 1" + }, + "landing.pricing.business.feature.2": { + "message": "Instancia dedicada y aislada", + "description": "Business tier feature 2" + }, + "landing.pricing.business.feature.3": { + "message": "Auditoría inicial de tu servidor", + "description": "Business tier feature 3" + }, + "landing.pricing.business.feature.4": { + "message": "Integración con tus herramientas", + "description": "Business tier feature 4" + }, + "landing.pricing.business.feature.5": { + "message": "Funciones a medida", + "description": "Business tier feature 5" + }, + "landing.pricing.business.feature.6": { + "message": "Seguimiento regular con un experto", + "description": "Business tier feature 6" + }, + "landing.pricing.business.feature.7": { + "message": "Soporte prioritario", + "description": "Business tier feature 7" + }, + "landing.pricing.business.button": { + "message": "Pedir una cita", + "description": "Business tier CTA button: book a meeting" + }, + "thankYou.head.title": { + "message": "Gracias | RaidProtect", + "description": "Browser tab title for the thank-you page" + }, + "thankYou.head.description": { + "message": "¡Gracias por invitar a RaidProtect! Para empezar, te recomendamos consultar nuestra documentación y unirte a nuestro servidor de Discord.", + "description": "Meta description for the thank-you page" + }, + "thankYou.title": { + "message": "¡Gracias por invitar a RaidProtect!", + "description": "Main heading of the thank-you page" + }, + "thankYou.description": { + "message": "Para empezar con buen pie, te recomendamos consultar nuestra documentación y unirte a nuestro servidor.", + "description": "Body description of the thank-you page" + }, + "thankYou.cta.joinDiscord": { + "message": "Unirse a nuestro servidor de Discord", + "description": "Primary CTA on the thank-you page: join the Discord server" + }, + "thankYou.cta.viewDocs": { + "message": "Consultar la documentación", + "description": "Secondary CTA on the thank-you page: open the documentation" + }, + "thankYou.server.unknown": { + "message": "Servidor desconocido", + "description": "Fallback name shown when the Discord widget returns no server name" + }, + "thankYou.server.memberCountUnknown": { + "message": "Número de miembros desconocido", + "description": "Fallback shown when the Discord widget returns no member count" + }, + "thankYou.server.membersOnline": { + "message": "{count} miembros en línea", + "description": "Number of members currently online on the invited server" + }, + "thankYou.permissions.adminOnlyWarning": { + "message": "⚠️ Todos los permisos específicos están otorgados, pero sin el permiso de Administrador el bot podría no acceder a todos los canales.", + "description": "Warning shown when the only missing permission is Administrator" + }, + "thankYou.permissions.missingWarning": { + "message": "⚠️ Para que el bot funcione correctamente, recomendamos agregar los siguientes permisos:", + "description": "Warning shown when some required permissions are missing" + }, + "thankYou.social.discord.ariaLabel": { + "message": "Discord", + "description": "ARIA label for the Discord social link" + }, + "thankYou.social.x.ariaLabel": { + "message": "X", + "description": "ARIA label for the X (Twitter) social link" + }, + "thankYou.social.youtube.ariaLabel": { + "message": "YouTube", + "description": "ARIA label for the YouTube social link" + }, + "thankYou.social.github.ariaLabel": { + "message": "GitHub", + "description": "ARIA label for the GitHub social link" + }, + "permission.administrator": { + "message": "Administrador", + "description": "Discord permission name" + }, + "permission.manageServer": { + "message": "Gestionar servidor", + "description": "Discord permission name" + }, + "permission.manageRoles": { + "message": "Gestionar roles", + "description": "Discord permission name" + }, + "permission.manageChannels": { + "message": "Gestionar canales", + "description": "Discord permission name" + }, + "permission.kickMembers": { + "message": "Expulsar miembros", + "description": "Discord permission name" + }, + "permission.banMembers": { + "message": "Banear miembros", + "description": "Discord permission name" + }, + "permission.manageNicknames": { + "message": "Gestionar apodos", + "description": "Discord permission name" + }, + "permission.manageWebhooks": { + "message": "Gestionar webhooks", + "description": "Discord permission name" + }, + "permission.viewAuditLog": { + "message": "Ver registro de auditoría", + "description": "Discord permission name" + }, + "permission.viewChannels": { + "message": "Ver canales", + "description": "Discord permission name" + }, + "permission.moderateMembers": { + "message": "Moderar miembros", + "description": "Discord permission name" + }, + "permission.sendMessages": { + "message": "Enviar mensajes", + "description": "Discord permission name" + }, + "permission.manageMessages": { + "message": "Gestionar mensajes", + "description": "Discord permission name" + }, + "permission.manageThreads": { + "message": "Gestionar hilos", + "description": "Discord permission name" + }, + "permission.embedLinks": { + "message": "Insertar enlaces", + "description": "Discord permission name" + }, + "permission.attachFiles": { + "message": "Adjuntar archivos", + "description": "Discord permission name" + }, + "permission.readMessageHistory": { + "message": "Leer historial de mensajes", + "description": "Discord permission name" + }, + "permission.addReactions": { + "message": "Añadir reacciones", + "description": "Discord permission name" + }, + "permission.useExternalEmojis": { + "message": "Usar emojis externos", + "description": "Discord permission name" + }, + "permission.muteMembers": { + "message": "Silenciar miembros", + "description": "Discord permission name" + }, + "permission.deafenMembers": { + "message": "Ensordecer miembros", + "description": "Discord permission name" + }, + "permission.moveMembers": { + "message": "Mover miembros", + "description": "Discord permission name" } } diff --git a/i18n/es/docusaurus-plugin-content-pages/index.tsx b/i18n/es/docusaurus-plugin-content-pages/index.tsx deleted file mode 100644 index 1610b9c..0000000 --- a/i18n/es/docusaurus-plugin-content-pages/index.tsx +++ /dev/null @@ -1,433 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import clsx from 'clsx'; -import Layout from '@theme/Layout'; -import Link from '@docusaurus/Link'; -import Hero from '@site/src/components/landing/Hero'; -import Servers from '@site/src/components/landing/Servers'; -import shared from '@site/src/components/landing/styles/shared.module.css'; -import styles from '@site/src/pages/index.module.css'; - -type Counts = { - servers: number; - users: number; - captcha: number; - antispam: number; -}; - -type FormattedValue = { - value: string; - unit: string; -}; - -function formatValue(value: number): FormattedValue { - if (value >= 1_000_000) { - return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; - } - return {value: (value / 1_000).toFixed(1), unit: 'k'}; -} - -function StatCounter({ - rawValue, - label, - fallback, -}: { - rawValue?: number; - label: string; - fallback: string; -}) { - if (rawValue == null) { - return ( -
-
- {fallback} -
-
{label}
-
- ); - } - const formatted = formatValue(rawValue); - return ( -
-
- {formatted.value} - {formatted.unit} -
-
{label}
-
- ); -} - -const CHECK_ICON = '/img/landing/icon-02.svg'; - -function FeatureItem({children}: {children: ReactNode}) { - return ( -
- -
{children}
-
- ); -} - -type Feature = { - to: string; - icon: string; - iconAlt: string; - title: string; - description: string; -}; - -const FEATURES: Feature[] = [ - { - to: '/docs/features/anti-spam', - icon: '/img/landing/iconAntispamWhite.svg', - iconAlt: 'RaidProtect icon Antispam', - title: 'Protección antispam', - description: - 'Sanciona al instante los intentos de spam, sin necesidad de intervención por tu parte.', - }, - { - to: '/docs/features/raid-mode', - icon: '/img/landing/iconAntiraidWhite.svg', - iconAlt: 'RaidProtect icon Antiraid', - title: 'Bloqueo de raids', - description: - '¿Temes un raid? Nuestro bot puede detectarlo y bloquearlo antes de que afecte a tu servidor.', - }, - { - to: '/docs/features/captcha', - icon: '/img/landing/iconCaptchaWhite.svg', - iconAlt: 'RaidProtect icon Captcha', - title: 'Protección contra bots', - description: - 'Gracias al captcha, tus miembros deben demostrar que son humanos. Di adiós a las cuentas automatizadas.', - }, - { - to: '/docs/features/utilities', - icon: '/img/landing/iconReportWhite.svg', - iconAlt: 'RaidProtect icon Report', - title: 'Moderación y administración', - description: - 'Administra tu servidor como un profesional con nuestras diversas funciones de moderación y administración.', - }, - { - to: '/docs/features/tag-role', - icon: '/img/landing/iconTagWhite.svg', - iconAlt: 'RaidProtect icon Tag', - title: 'Rol por etiqueta', - description: - 'El Rol por etiqueta asigna automáticamente un rol a los miembros que añaden la etiqueta de tu servidor.', - }, - { - to: '/docs/features/dm-lock', - icon: '/img/landing/iconDmlockWhite.svg', - iconAlt: 'RaidProtect icon DM Lock', - title: 'Bloqueo de MP', - description: - 'Un escudo único contra el spam, el scam y las estafas por mensaje privado.', - }, -]; - -export default function Home(): ReactNode { - const [counts, setCounts] = useState(null); - - useEffect(() => { - let cancelled = false; - fetch('https://docs.raidprotect.bot/counts.json') - .then((res) => { - if (!res.ok) throw new Error('Failed to fetch counts'); - return res.json(); - }) - .then((data: Counts) => { - if (!cancelled) setCounts(data); - }) - .catch((err) => { - // Stats are best-effort; failure is non-blocking - // eslint-disable-next-line no-console - console.error('Stats update error:', err); - }); - return () => { - cancelled = true; - }; - }, []); - - return ( - -
- - - - {/* About */} -
-
-
-

- Nuestros resultados tienen un{' '} - impacto -

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-
- - {/* Features */} -
-
-
-

- Nuestras funciones -

-

- Descubre por qué somos uno de los mejores bots para proteger tu - servidor de Discord contra usuarios malintencionados. -

-
-
- -
-
- - {/* Pricing */} -
-
-
-
-

- Mantente a la{' '} - vanguardia -

-

- Añade RaidProtect y empieza a proteger tu servidor hoy mismo. -

-
- -
- {/* Basic */} -
-
Basic
-
-

Gratis

-
-

- La seguridad esencial garantizada para siempre -

-
- Protecciones antispam - Bloqueo automático de raids - Filtrado de bots maliciosos - Moderación y administración - - Y mucho más... - -
- -
- - {/* Founder */} -
-
- Founder -
-
-

Suscripción

-

- 2,99 $ -

-
-

- Oferta de lanzamiento reservada a los primeros suscriptores -

-
- Perfil del bot personalizable - Nombres de sanciones personalizados - Acceso avanzado al Auth Manager - Acceso ampliado al Display Public - Acceso a la Beta pública - Rol exclusivo en nuestro servidor -
- -
- - {/* Business */} -
-
- Business -
-
-

Bajo solicitud

-
-

- Para proyectos con altos requisitos de seguridad -

-
- Todas las funciones Founder - Instancia dedicada y aislada - Auditoría inicial de tu servidor - Integración con tus herramientas - Funciones a medida - Seguimiento regular con un experto - Soporte prioritario -
- -
-
-
-
-
-
- ); -} diff --git a/i18n/es/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/es/docusaurus-plugin-content-pages/thank-you.tsx deleted file mode 100644 index f502bc4..0000000 --- a/i18n/es/docusaurus-plugin-content-pages/thank-you.tsx +++ /dev/null @@ -1,398 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import Head from '@docusaurus/Head'; -import Link from '@docusaurus/Link'; -import styles from '@site/src/pages/thank-you.module.css'; - -type ServerBadge = 'partner' | 'verified' | null; - -type ServerInfo = { - name: string; - members: string; - inviteUrl: string; - iconUrl: string; - badge: ServerBadge; -}; - -type PermissionWarning = { - tone: 'admin' | 'missing'; - message: string; - missing: string[]; -}; - -const DEFAULT_ICON_URL = - 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; - -const BADGE_SRC: Record, string> = { - partner: '/img/landing/serverBadgePartner.svg', - verified: '/img/landing/serverBadgeVerified.svg', -}; - -const REQUIRED_PERMISSIONS = 1117660769534n; -const ADMIN_PERMISSION = 8n; - -// Discord-style permission names (Spanish). -const PERMISSION_MESSAGES: Array<[string, bigint]> = [ - ['Administrador', ADMIN_PERMISSION], - ['Gestionar servidor', 32n], - ['Gestionar roles', 268435456n], - ['Gestionar canales', 16n], - ['Expulsar miembros', 2n], - ['Banear miembros', 4n], - ['Gestionar apodos', 134217728n], - ['Gestionar webhooks', 536870912n], - ['Ver registro de auditoría', 524288n], - ['Ver canales', 1024n], - ['Moderar miembros', 1099511627776n], - ['Enviar mensajes', 2048n], - ['Gestionar mensajes', 8192n], - ['Gestionar hilos', 17179869184n], - ['Insertar enlaces', 16384n], - ['Adjuntar archivos', 32768n], - ['Leer historial de mensajes', 65536n], - ['Añadir reacciones', 64n], - ['Usar emojis externos', 262144n], - ['Silenciar miembros', 4194304n], - ['Ensordecer miembros', 8388608n], - ['Mover miembros', 16777216n], -]; - -async function fetchServerInfo(guildId: string): Promise { - try { - const widgetResponse = await fetch( - `https://discord.com/api/guilds/${guildId}/widget.json`, - ); - if (!widgetResponse.ok) { - throw new Error('Widget desactivado.'); - } - const widgetData = await widgetResponse.json(); - - const name: string = widgetData.name || 'Servidor desconocido'; - const members: string = - typeof widgetData.presence_count === 'number' - ? `${widgetData.presence_count} miembros en línea` - : 'Número de miembros desconocido'; - const inviteUrl: string = widgetData.instant_invite || '#'; - - let iconUrl = DEFAULT_ICON_URL; - let badge: ServerBadge = null; - - const inviteCode: string | null = widgetData.instant_invite - ? widgetData.instant_invite.split('/').pop() || null - : null; - - if (inviteCode) { - const inviteResponse = await fetch( - `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, - ); - if (inviteResponse.ok) { - const inviteData = await inviteResponse.json(); - const server = inviteData.guild; - if (server?.icon) { - iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; - } - if (Array.isArray(server?.features)) { - if (server.features.includes('PARTNERED')) { - badge = 'partner'; - } else if (server.features.includes('VERIFIED')) { - badge = 'verified'; - } - } - } - } - - return {name, members, inviteUrl, iconUrl, badge}; - } catch (error) { - // Discord widget may be disabled or network may be unreachable. - // eslint-disable-next-line no-console - console.error(error); - return null; - } -} - -function computePermissionWarning( - permissionsParam: string, -): PermissionWarning | null { - let currentPermissions: bigint; - try { - currentPermissions = BigInt(permissionsParam); - } catch { - return null; - } - - const hasAdminPermission = - (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; - const missingPermissions = PERMISSION_MESSAGES.filter( - ([, value]) => - (REQUIRED_PERMISSIONS & value) === value && - (currentPermissions & value) !== value, - ).map(([name]) => name); - - if (hasAdminPermission || missingPermissions.length === 0) { - return null; - } - - if ( - missingPermissions.length === 1 && - missingPermissions[0] === 'Administrador' - ) { - return { - tone: 'admin', - message: - '⚠️ Todos los permisos específicos están otorgados, pero sin el permiso de Administrador el bot podría no acceder a todos los canales.', - missing: [], - }; - } - - return { - tone: 'missing', - message: - '⚠️ Para que el bot funcione correctamente, recomendamos agregar los siguientes permisos:', - missing: missingPermissions, - }; -} - -export default function ThankYou(): ReactNode { - const [serverInfo, setServerInfo] = useState(null); - const [permissionWarning, setPermissionWarning] = - useState(null); - - useEffect(() => { - if (typeof window === 'undefined') return; - - const urlParams = new URLSearchParams(window.location.search); - - const guildId = urlParams.get('guild_id'); - let cancelled = false; - if (guildId) { - fetchServerInfo(guildId).then((info) => { - if (!cancelled && info) { - setServerInfo(info); - } - }); - } - - const permissionsParam = urlParams.get('permissions'); - if (permissionsParam) { - setPermissionWarning(computePermissionWarning(permissionsParam)); - } - - // Auto-redirect to Discord invite after 60s, matching the Webflow page. - const redirectTimer = window.setTimeout(() => { - window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; - }, 60000); - - return () => { - cancelled = true; - window.clearTimeout(redirectTimer); - }; - }, []); - - return ( - <> - - Gracias | RaidProtect - - -
-
- - RaidProtect title logo - - -

¡Gracias por invitar a RaidProtect!

- - {serverInfo && ( - - )} - - {permissionWarning && ( -
- {permissionWarning.message} - {permissionWarning.missing.length > 0 && ( -
    - {permissionWarning.missing.map((perm) => ( -
  • - {perm} -
  • - ))} -
- )} -
- )} - -

- Para empezar con buen pie, te recomendamos consultar nuestra - documentación y unirte a nuestro servidor. -

- -
- - Unirse a nuestro servidor de Discord - - - Consultar la documentación - -
- - -
- -
- - ); -} diff --git a/i18n/fr/code.json b/i18n/fr/code.json index 997cd1b..27cd03d 100644 --- a/i18n/fr/code.json +++ b/i18n/fr/code.json @@ -486,5 +486,385 @@ "frame.cta.secondary": { "message": "Rejoindre le serveur", "description": "Frame CTA secondary button: join the Discord server" + }, + "landing.layout.title": { + "message": "RaidProtect • Sécurisez votre serveur Discord", + "description": "Browser tab title for the landing page" + }, + "landing.layout.description": { + "message": "RaidProtect est un bot Discord français ayant pour mission de protéger simplement votre serveur des utilisateurs malintentionnés.", + "description": "Meta description for the landing page" + }, + "landing.about.title": { + "message": "Nos résultats ont un {highlight}", + "description": "About section title; {highlight} renders the gradient-highlighted word" + }, + "landing.about.title.highlight": { + "message": "impact", + "description": "Highlighted word inside the about section title" + }, + "landing.about.curve.ariaLabel": { + "message": "Croissance du nombre de serveurs", + "description": "ARIA label for the decorative SVG curve in the about section" + }, + "landing.about.counter.servers": { + "message": "Serveurs sécurisés", + "description": "About stat label: number of secured servers" + }, + "landing.about.counter.captcha": { + "message": "Captchas résolus", + "description": "About stat label: number of captchas solved" + }, + "landing.about.counter.antispam": { + "message": "Spams bloqués", + "description": "About stat label: number of spam messages blocked" + }, + "landing.about.counter.users": { + "message": "Utilisateurs protégés", + "description": "About stat label: number of protected users" + }, + "landing.features.title": { + "message": "Nos {highlight}", + "description": "Features section title; {highlight} renders the gradient-highlighted word" + }, + "landing.features.title.highlight": { + "message": "fonctionnalités", + "description": "Highlighted word inside the features section title" + }, + "landing.features.subtitle": { + "message": "Découvrez ce qui fait de nous l'un des meilleurs bots pour protéger votre serveur Discord des utilisateurs malintentionnés.", + "description": "Features section subtitle/description" + }, + "landing.features.anti-spam.title": { + "message": "Protection anti-spam", + "description": "Feature card title: anti-spam" + }, + "landing.features.anti-spam.description": { + "message": "Sanctionnez instantanément les tentatives de spam, sans aucune intervention de votre part.", + "description": "Feature card description: anti-spam" + }, + "landing.features.raid.title": { + "message": "Blocage des raids", + "description": "Feature card title: raid" + }, + "landing.features.raid.description": { + "message": "Vous craignez un raid ? Notre bot est capable de le détecter et de le bloquer avant même qu'il impacte votre serveur.", + "description": "Feature card description: raid" + }, + "landing.features.captcha.title": { + "message": "Protection contre les robots", + "description": "Feature card title: captcha" + }, + "landing.features.captcha.description": { + "message": "Grâce au captcha, vos membres doivent prouver qu'ils sont humains. Dites adieu aux comptes automatisés.", + "description": "Feature card description: captcha" + }, + "landing.features.mod.title": { + "message": "Modération & administration", + "description": "Feature card title: mod" + }, + "landing.features.mod.description": { + "message": "Gérez votre serveur comme un pro avec nos diverses fonctionalités de modération et d'administration.", + "description": "Feature card description: mod" + }, + "landing.features.tag.title": { + "message": "Rôle de Tag", + "description": "Feature card title: tag" + }, + "landing.features.tag.description": { + "message": "Le Rôle de Tag permet d’attribuer automatiquement un rôle aux membres qui ajoutent le tag de votre serveur.", + "description": "Feature card description: tag" + }, + "landing.features.dm.title": { + "message": "Fermeture des MP", + "description": "Feature card title: dm" + }, + "landing.features.dm.description": { + "message": "Un bouclier inédit contre le spam, le scam et les arnaques par message privé.", + "description": "Feature card description: dm" + }, + "landing.pricing.title": { + "message": "Garder une longueur {highlight}", + "description": "Pricing section title; {highlight} renders the gradient-highlighted word" + }, + "landing.pricing.title.highlight": { + "message": "d'avance", + "description": "Highlighted word inside the pricing section title" + }, + "landing.pricing.description": { + "message": "Ajoutez RaidProtect et commencez à protéger votre serveur dès aujourd'hui.", + "description": "Pricing section description below the title" + }, + "landing.pricing.basic.preTitle": { + "message": "Basic", + "description": "Pricing card pre-title for the Basic tier" + }, + "landing.pricing.basic.price": { + "message": "Gratuit", + "description": "Pricing card price label for the Basic tier (free)" + }, + "landing.pricing.basic.tagline": { + "message": "La sécurité essentielle assurée pour toujours", + "description": "Pricing card tagline for the Basic tier" + }, + "landing.pricing.basic.feature.1": { + "message": "Protections anti-spam", + "description": "Basic tier feature 1" + }, + "landing.pricing.basic.feature.2": { + "message": "Blocage automatique des raids", + "description": "Basic tier feature 2" + }, + "landing.pricing.basic.feature.3": { + "message": "Filtrage des bots malveillants", + "description": "Basic tier feature 3" + }, + "landing.pricing.basic.feature.4": { + "message": "Modération & administration", + "description": "Basic tier feature 4" + }, + "landing.pricing.basic.feature.5": { + "message": "Et bien plus encore...", + "description": "Basic tier feature 5 (emphasis)" + }, + "landing.pricing.basic.button": { + "message": "Ajouter à Discord", + "description": "Basic tier CTA button: invite the bot" + }, + "landing.pricing.founder.preTitle": { + "message": "Founder", + "description": "Pricing card pre-title for the Founder tier" + }, + "landing.pricing.founder.price.label": { + "message": "Abonnement", + "description": "Founder tier price label (subscription)" + }, + "landing.pricing.founder.price.amount": { + "message": "2,99 $", + "description": "Founder tier price amount; stays the same across locales" + }, + "landing.pricing.founder.tagline": { + "message": "Offre de lancement réservée aux premiers abonnés", + "description": "Pricing card tagline for the Founder tier" + }, + "landing.pricing.founder.feature.1": { + "message": "Profil du bot personnalisable", + "description": "Founder tier feature 1" + }, + "landing.pricing.founder.feature.2": { + "message": "Noms de sanctions custom", + "description": "Founder tier feature 2" + }, + "landing.pricing.founder.feature.3": { + "message": "Accès avancé à l'Auth Manager", + "description": "Founder tier feature 3" + }, + "landing.pricing.founder.feature.4": { + "message": "Accès étendu au Display Public", + "description": "Founder tier feature 4" + }, + "landing.pricing.founder.feature.5": { + "message": "Accès à la Bêta publique", + "description": "Founder tier feature 5" + }, + "landing.pricing.founder.feature.6": { + "message": "Rôle exclusif sur notre serveur", + "description": "Founder tier feature 6" + }, + "landing.pricing.founder.button": { + "message": "S'abonner via Discord", + "description": "Founder tier CTA button: subscribe" + }, + "landing.pricing.business.preTitle": { + "message": "Business", + "description": "Pricing card pre-title for the Business tier" + }, + "landing.pricing.business.price": { + "message": "Sur demande", + "description": "Pricing card price label for the Business tier (on request)" + }, + "landing.pricing.business.tagline": { + "message": "Pour les projets aux exigences de sécurité élevées", + "description": "Pricing card tagline for the Business tier" + }, + "landing.pricing.business.feature.1": { + "message": "Les fonctionnalités Founder", + "description": "Business tier feature 1" + }, + "landing.pricing.business.feature.2": { + "message": "Instance dédiée et isolée", + "description": "Business tier feature 2" + }, + "landing.pricing.business.feature.3": { + "message": "Audit initial de votre serveur", + "description": "Business tier feature 3" + }, + "landing.pricing.business.feature.4": { + "message": "Intégration avec vos outils", + "description": "Business tier feature 4" + }, + "landing.pricing.business.feature.5": { + "message": "Fonctionnalités sur mesure", + "description": "Business tier feature 5" + }, + "landing.pricing.business.feature.6": { + "message": "Suivi régulier avec un expert", + "description": "Business tier feature 6" + }, + "landing.pricing.business.feature.7": { + "message": "Support prioritaire", + "description": "Business tier feature 7" + }, + "landing.pricing.business.button": { + "message": "Prendre rendez-vous", + "description": "Business tier CTA button: book a meeting" + }, + "thankYou.head.title": { + "message": "Merci | RaidProtect", + "description": "Browser tab title for the thank-you page" + }, + "thankYou.head.description": { + "message": "Merci d'avoir invité RaidProtect ! Pour bien débuter, nous vous recommandons de consulter notre documentation et de rejoindre notre serveur Discord.", + "description": "Meta description for the thank-you page" + }, + "thankYou.title": { + "message": "Merci d'avoir invité RaidProtect !", + "description": "Main heading of the thank-you page" + }, + "thankYou.description": { + "message": "Pour bien débuter, nous vous recommandons de consulter notre documentation et de rejoindre notre serveur.", + "description": "Body description of the thank-you page" + }, + "thankYou.cta.joinDiscord": { + "message": "Rejoindre notre serveur Discord", + "description": "Primary CTA on the thank-you page: join the Discord server" + }, + "thankYou.cta.viewDocs": { + "message": "Consulter la documentation", + "description": "Secondary CTA on the thank-you page: open the documentation" + }, + "thankYou.server.unknown": { + "message": "Serveur inconnu", + "description": "Fallback name shown when the Discord widget returns no server name" + }, + "thankYou.server.memberCountUnknown": { + "message": "Nombre de membres inconnu", + "description": "Fallback shown when the Discord widget returns no member count" + }, + "thankYou.server.membersOnline": { + "message": "{count} membres en ligne", + "description": "Number of members currently online on the invited server" + }, + "thankYou.permissions.adminOnlyWarning": { + "message": "⚠️ Toutes les permissions spécifiques sont accordées, mais sans la permission Administrateur, le bot pourrait ne pas accéder à tous les salons.", + "description": "Warning shown when the only missing permission is Administrator" + }, + "thankYou.permissions.missingWarning": { + "message": "⚠️ Afin d'assurer le bon fonctionnement du bot, nous vous recommandons d'ajouter les permissions suivantes :", + "description": "Warning shown when some required permissions are missing" + }, + "thankYou.social.discord.ariaLabel": { + "message": "Discord", + "description": "ARIA label for the Discord social link" + }, + "thankYou.social.x.ariaLabel": { + "message": "X", + "description": "ARIA label for the X (Twitter) social link" + }, + "thankYou.social.youtube.ariaLabel": { + "message": "YouTube", + "description": "ARIA label for the YouTube social link" + }, + "thankYou.social.github.ariaLabel": { + "message": "GitHub", + "description": "ARIA label for the GitHub social link" + }, + "permission.administrator": { + "message": "Administrateur", + "description": "Discord permission name" + }, + "permission.manageServer": { + "message": "Gérer le serveur", + "description": "Discord permission name" + }, + "permission.manageRoles": { + "message": "Gérer les rôles", + "description": "Discord permission name" + }, + "permission.manageChannels": { + "message": "Gérer les salons", + "description": "Discord permission name" + }, + "permission.kickMembers": { + "message": "Expulser des membres", + "description": "Discord permission name" + }, + "permission.banMembers": { + "message": "Bannir des membres", + "description": "Discord permission name" + }, + "permission.manageNicknames": { + "message": "Gérer les pseudos", + "description": "Discord permission name" + }, + "permission.manageWebhooks": { + "message": "Gérer les webhooks", + "description": "Discord permission name" + }, + "permission.viewAuditLog": { + "message": "Voir les logs du serveur", + "description": "Discord permission name" + }, + "permission.viewChannels": { + "message": "Voir les salons", + "description": "Discord permission name" + }, + "permission.moderateMembers": { + "message": "Modérer les membres", + "description": "Discord permission name" + }, + "permission.sendMessages": { + "message": "Envoyer des messages", + "description": "Discord permission name" + }, + "permission.manageMessages": { + "message": "Gérer les messages", + "description": "Discord permission name" + }, + "permission.manageThreads": { + "message": "Gérer les fils", + "description": "Discord permission name" + }, + "permission.embedLinks": { + "message": "Intégrer des liens", + "description": "Discord permission name" + }, + "permission.attachFiles": { + "message": "Joindre des fichiers", + "description": "Discord permission name" + }, + "permission.readMessageHistory": { + "message": "Voir les anciens messages", + "description": "Discord permission name" + }, + "permission.addReactions": { + "message": "Ajouter des réactions", + "description": "Discord permission name" + }, + "permission.useExternalEmojis": { + "message": "Utiliser des émojis externes", + "description": "Discord permission name" + }, + "permission.muteMembers": { + "message": "Rendre les membres muets", + "description": "Discord permission name" + }, + "permission.deafenMembers": { + "message": "Mettre en sourdine des membres", + "description": "Discord permission name" + }, + "permission.moveMembers": { + "message": "Déplacer des membres", + "description": "Discord permission name" } } diff --git a/i18n/pt/code.json b/i18n/pt/code.json index 87d4fce..c263618 100644 --- a/i18n/pt/code.json +++ b/i18n/pt/code.json @@ -467,5 +467,385 @@ "frame.cta.secondary": { "message": "Entrar no servidor", "description": "Frame CTA secondary button: join the Discord server" + }, + "landing.layout.title": { + "message": "RaidProtect • Proteja o seu servidor Discord", + "description": "Browser tab title for the landing page" + }, + "landing.layout.description": { + "message": "O RaidProtect é um bot Discord francês que tem por missão proteger facilmente o seu servidor de utilizadores maliciosos.", + "description": "Meta description for the landing page" + }, + "landing.about.title": { + "message": "Os nossos resultados têm {highlight}", + "description": "About section title; {highlight} renders the gradient-highlighted word" + }, + "landing.about.title.highlight": { + "message": "impacto", + "description": "Highlighted word inside the about section title" + }, + "landing.about.curve.ariaLabel": { + "message": "Crescimento do número de servidores", + "description": "ARIA label for the decorative SVG curve in the about section" + }, + "landing.about.counter.servers": { + "message": "Servidores protegidos", + "description": "About stat label: number of secured servers" + }, + "landing.about.counter.captcha": { + "message": "Captchas resolvidos", + "description": "About stat label: number of captchas solved" + }, + "landing.about.counter.antispam": { + "message": "Spams bloqueados", + "description": "About stat label: number of spam messages blocked" + }, + "landing.about.counter.users": { + "message": "Utilizadores protegidos", + "description": "About stat label: number of protected users" + }, + "landing.features.title": { + "message": "As nossas {highlight}", + "description": "Features section title; {highlight} renders the gradient-highlighted word" + }, + "landing.features.title.highlight": { + "message": "funcionalidades", + "description": "Highlighted word inside the features section title" + }, + "landing.features.subtitle": { + "message": "Descubra o que faz de nós um dos melhores bots para proteger o seu servidor Discord de utilizadores maliciosos.", + "description": "Features section subtitle/description" + }, + "landing.features.anti-spam.title": { + "message": "Proteção antispam", + "description": "Feature card title: anti-spam" + }, + "landing.features.anti-spam.description": { + "message": "Sancione instantaneamente as tentativas de spam, sem qualquer intervenção da sua parte.", + "description": "Feature card description: anti-spam" + }, + "landing.features.raid.title": { + "message": "Bloqueio de raids", + "description": "Feature card title: raid" + }, + "landing.features.raid.description": { + "message": "Receia um raid? O nosso bot é capaz de o detetar e bloquear antes que afete o seu servidor.", + "description": "Feature card description: raid" + }, + "landing.features.captcha.title": { + "message": "Proteção contra bots", + "description": "Feature card title: captcha" + }, + "landing.features.captcha.description": { + "message": "Graças ao captcha, os seus membros têm de provar que são humanos. Diga adeus às contas automatizadas.", + "description": "Feature card description: captcha" + }, + "landing.features.mod.title": { + "message": "Moderação e administração", + "description": "Feature card title: mod" + }, + "landing.features.mod.description": { + "message": "Gira o seu servidor como um profissional graças às nossas várias funcionalidades de moderação e administração.", + "description": "Feature card description: mod" + }, + "landing.features.tag.title": { + "message": "Cargo de Etiqueta", + "description": "Feature card title: tag" + }, + "landing.features.tag.description": { + "message": "O Cargo de Etiqueta atribui automaticamente um cargo aos membros que adicionam a etiqueta do seu servidor.", + "description": "Feature card description: tag" + }, + "landing.features.dm.title": { + "message": "Bloqueio de MP", + "description": "Feature card title: dm" + }, + "landing.features.dm.description": { + "message": "Um escudo único contra spam, scam e burlas por mensagem privada.", + "description": "Feature card description: dm" + }, + "landing.pricing.title": { + "message": "Manter-se um passo {highlight}", + "description": "Pricing section title; {highlight} renders the gradient-highlighted word" + }, + "landing.pricing.title.highlight": { + "message": "à frente", + "description": "Highlighted word inside the pricing section title" + }, + "landing.pricing.description": { + "message": "Adicione o RaidProtect e comece a proteger o seu servidor hoje mesmo.", + "description": "Pricing section description below the title" + }, + "landing.pricing.basic.preTitle": { + "message": "Basic", + "description": "Pricing card pre-title for the Basic tier" + }, + "landing.pricing.basic.price": { + "message": "Gratuito", + "description": "Pricing card price label for the Basic tier (free)" + }, + "landing.pricing.basic.tagline": { + "message": "A segurança essencial garantida para sempre", + "description": "Pricing card tagline for the Basic tier" + }, + "landing.pricing.basic.feature.1": { + "message": "Proteções antispam", + "description": "Basic tier feature 1" + }, + "landing.pricing.basic.feature.2": { + "message": "Bloqueio automático de raids", + "description": "Basic tier feature 2" + }, + "landing.pricing.basic.feature.3": { + "message": "Filtragem de bots maliciosos", + "description": "Basic tier feature 3" + }, + "landing.pricing.basic.feature.4": { + "message": "Moderação e administração", + "description": "Basic tier feature 4" + }, + "landing.pricing.basic.feature.5": { + "message": "E muito mais...", + "description": "Basic tier feature 5 (emphasis)" + }, + "landing.pricing.basic.button": { + "message": "Adicionar ao Discord", + "description": "Basic tier CTA button: invite the bot" + }, + "landing.pricing.founder.preTitle": { + "message": "Founder", + "description": "Pricing card pre-title for the Founder tier" + }, + "landing.pricing.founder.price.label": { + "message": "Subscrição", + "description": "Founder tier price label (subscription)" + }, + "landing.pricing.founder.price.amount": { + "message": "2,99 $", + "description": "Founder tier price amount; stays the same across locales" + }, + "landing.pricing.founder.tagline": { + "message": "Oferta de lançamento reservada aos primeiros subscritores", + "description": "Pricing card tagline for the Founder tier" + }, + "landing.pricing.founder.feature.1": { + "message": "Perfil do bot personalizável", + "description": "Founder tier feature 1" + }, + "landing.pricing.founder.feature.2": { + "message": "Nomes de sanções personalizados", + "description": "Founder tier feature 2" + }, + "landing.pricing.founder.feature.3": { + "message": "Acesso avançado ao Auth Manager", + "description": "Founder tier feature 3" + }, + "landing.pricing.founder.feature.4": { + "message": "Acesso alargado ao Display Public", + "description": "Founder tier feature 4" + }, + "landing.pricing.founder.feature.5": { + "message": "Acesso à Beta pública", + "description": "Founder tier feature 5" + }, + "landing.pricing.founder.feature.6": { + "message": "Cargo exclusivo no nosso servidor", + "description": "Founder tier feature 6" + }, + "landing.pricing.founder.button": { + "message": "Subscrever via Discord", + "description": "Founder tier CTA button: subscribe" + }, + "landing.pricing.business.preTitle": { + "message": "Business", + "description": "Pricing card pre-title for the Business tier" + }, + "landing.pricing.business.price": { + "message": "Sob consulta", + "description": "Pricing card price label for the Business tier (on request)" + }, + "landing.pricing.business.tagline": { + "message": "Para projetos com exigências de segurança elevadas", + "description": "Pricing card tagline for the Business tier" + }, + "landing.pricing.business.feature.1": { + "message": "Todas as funcionalidades Founder", + "description": "Business tier feature 1" + }, + "landing.pricing.business.feature.2": { + "message": "Instância dedicada e isolada", + "description": "Business tier feature 2" + }, + "landing.pricing.business.feature.3": { + "message": "Auditoria inicial do seu servidor", + "description": "Business tier feature 3" + }, + "landing.pricing.business.feature.4": { + "message": "Integração com as suas ferramentas", + "description": "Business tier feature 4" + }, + "landing.pricing.business.feature.5": { + "message": "Funcionalidades à medida", + "description": "Business tier feature 5" + }, + "landing.pricing.business.feature.6": { + "message": "Acompanhamento regular com um especialista", + "description": "Business tier feature 6" + }, + "landing.pricing.business.feature.7": { + "message": "Suporte prioritário", + "description": "Business tier feature 7" + }, + "landing.pricing.business.button": { + "message": "Marcar reunião", + "description": "Business tier CTA button: book a meeting" + }, + "thankYou.head.title": { + "message": "Obrigado | RaidProtect", + "description": "Browser tab title for the thank-you page" + }, + "thankYou.head.description": { + "message": "Obrigado por convidar o RaidProtect! Para começar, recomendamos consultar a nossa documentação e entrar no nosso servidor Discord.", + "description": "Meta description for the thank-you page" + }, + "thankYou.title": { + "message": "Obrigado por convidar o RaidProtect!", + "description": "Main heading of the thank-you page" + }, + "thankYou.description": { + "message": "Para começar com o pé direito, recomendamos consultar a nossa documentação e entrar no nosso servidor.", + "description": "Body description of the thank-you page" + }, + "thankYou.cta.joinDiscord": { + "message": "Entrar no nosso servidor Discord", + "description": "Primary CTA on the thank-you page: join the Discord server" + }, + "thankYou.cta.viewDocs": { + "message": "Consultar a documentação", + "description": "Secondary CTA on the thank-you page: open the documentation" + }, + "thankYou.server.unknown": { + "message": "Servidor desconhecido", + "description": "Fallback name shown when the Discord widget returns no server name" + }, + "thankYou.server.memberCountUnknown": { + "message": "Número de membros desconhecido", + "description": "Fallback shown when the Discord widget returns no member count" + }, + "thankYou.server.membersOnline": { + "message": "{count} membros online", + "description": "Number of members currently online on the invited server" + }, + "thankYou.permissions.adminOnlyWarning": { + "message": "⚠️ Todas as permissões específicas estão concedidas, mas sem a permissão de Administrador o bot pode não conseguir aceder a todos os canais.", + "description": "Warning shown when the only missing permission is Administrator" + }, + "thankYou.permissions.missingWarning": { + "message": "⚠️ Para garantir o bom funcionamento do bot, recomendamos adicionar as seguintes permissões:", + "description": "Warning shown when some required permissions are missing" + }, + "thankYou.social.discord.ariaLabel": { + "message": "Discord", + "description": "ARIA label for the Discord social link" + }, + "thankYou.social.x.ariaLabel": { + "message": "X", + "description": "ARIA label for the X (Twitter) social link" + }, + "thankYou.social.youtube.ariaLabel": { + "message": "YouTube", + "description": "ARIA label for the YouTube social link" + }, + "thankYou.social.github.ariaLabel": { + "message": "GitHub", + "description": "ARIA label for the GitHub social link" + }, + "permission.administrator": { + "message": "Administrador", + "description": "Discord permission name" + }, + "permission.manageServer": { + "message": "Gerir servidor", + "description": "Discord permission name" + }, + "permission.manageRoles": { + "message": "Gerir cargos", + "description": "Discord permission name" + }, + "permission.manageChannels": { + "message": "Gerir canais", + "description": "Discord permission name" + }, + "permission.kickMembers": { + "message": "Expulsar membros", + "description": "Discord permission name" + }, + "permission.banMembers": { + "message": "Banir membros", + "description": "Discord permission name" + }, + "permission.manageNicknames": { + "message": "Gerir alcunhas", + "description": "Discord permission name" + }, + "permission.manageWebhooks": { + "message": "Gerir webhooks", + "description": "Discord permission name" + }, + "permission.viewAuditLog": { + "message": "Ver registo de auditoria", + "description": "Discord permission name" + }, + "permission.viewChannels": { + "message": "Ver canais", + "description": "Discord permission name" + }, + "permission.moderateMembers": { + "message": "Moderar membros", + "description": "Discord permission name" + }, + "permission.sendMessages": { + "message": "Enviar mensagens", + "description": "Discord permission name" + }, + "permission.manageMessages": { + "message": "Gerir mensagens", + "description": "Discord permission name" + }, + "permission.manageThreads": { + "message": "Gerir tópicos", + "description": "Discord permission name" + }, + "permission.embedLinks": { + "message": "Incorporar ligações", + "description": "Discord permission name" + }, + "permission.attachFiles": { + "message": "Anexar ficheiros", + "description": "Discord permission name" + }, + "permission.readMessageHistory": { + "message": "Ler histórico de mensagens", + "description": "Discord permission name" + }, + "permission.addReactions": { + "message": "Adicionar reações", + "description": "Discord permission name" + }, + "permission.useExternalEmojis": { + "message": "Usar emojis externos", + "description": "Discord permission name" + }, + "permission.muteMembers": { + "message": "Silenciar membros", + "description": "Discord permission name" + }, + "permission.deafenMembers": { + "message": "Ensurdecer membros", + "description": "Discord permission name" + }, + "permission.moveMembers": { + "message": "Mover membros", + "description": "Discord permission name" } } diff --git a/i18n/pt/docusaurus-plugin-content-pages/index.tsx b/i18n/pt/docusaurus-plugin-content-pages/index.tsx deleted file mode 100644 index 0658580..0000000 --- a/i18n/pt/docusaurus-plugin-content-pages/index.tsx +++ /dev/null @@ -1,435 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import clsx from 'clsx'; -import Layout from '@theme/Layout'; -import Link from '@docusaurus/Link'; -import Hero from '@site/src/components/landing/Hero'; -import Servers from '@site/src/components/landing/Servers'; -import shared from '@site/src/components/landing/styles/shared.module.css'; -import styles from '@site/src/pages/index.module.css'; - -type Counts = { - servers: number; - users: number; - captcha: number; - antispam: number; -}; - -type FormattedValue = { - value: string; - unit: string; -}; - -function formatValue(value: number): FormattedValue { - if (value >= 1_000_000) { - return {value: (value / 1_000_000).toFixed(1), unit: 'M'}; - } - return {value: (value / 1_000).toFixed(1), unit: 'k'}; -} - -function StatCounter({ - rawValue, - label, - fallback, -}: { - rawValue?: number; - label: string; - fallback: string; -}) { - if (rawValue == null) { - return ( -
-
- {fallback} -
-
{label}
-
- ); - } - const formatted = formatValue(rawValue); - return ( -
-
- {formatted.value} - {formatted.unit} -
-
{label}
-
- ); -} - -const CHECK_ICON = '/img/landing/icon-02.svg'; - -function FeatureItem({children}: {children: ReactNode}) { - return ( -
- -
{children}
-
- ); -} - -type Feature = { - to: string; - icon: string; - iconAlt: string; - title: string; - description: string; -}; - -const FEATURES: Feature[] = [ - { - to: '/docs/features/anti-spam', - icon: '/img/landing/iconAntispamWhite.svg', - iconAlt: 'RaidProtect icon Antispam', - title: 'Proteção antispam', - description: - 'Sancione instantaneamente as tentativas de spam, sem qualquer intervenção da sua parte.', - }, - { - to: '/docs/features/raid-mode', - icon: '/img/landing/iconAntiraidWhite.svg', - iconAlt: 'RaidProtect icon Antiraid', - title: 'Bloqueio de raids', - description: - 'Receia um raid? O nosso bot é capaz de o detetar e bloquear antes que afete o seu servidor.', - }, - { - to: '/docs/features/captcha', - icon: '/img/landing/iconCaptchaWhite.svg', - iconAlt: 'RaidProtect icon Captcha', - title: 'Proteção contra bots', - description: - 'Graças ao captcha, os seus membros têm de provar que são humanos. Diga adeus às contas automatizadas.', - }, - { - to: '/docs/features/utilities', - icon: '/img/landing/iconReportWhite.svg', - iconAlt: 'RaidProtect icon Report', - title: 'Moderação e administração', - description: - 'Gira o seu servidor como um profissional graças às nossas várias funcionalidades de moderação e administração.', - }, - { - to: '/docs/features/tag-role', - icon: '/img/landing/iconTagWhite.svg', - iconAlt: 'RaidProtect icon Tag', - title: 'Cargo de Etiqueta', - description: - 'O Cargo de Etiqueta atribui automaticamente um cargo aos membros que adicionam a etiqueta do seu servidor.', - }, - { - to: '/docs/features/dm-lock', - icon: '/img/landing/iconDmlockWhite.svg', - iconAlt: 'RaidProtect icon DM Lock', - title: 'Bloqueio de MP', - description: - 'Um escudo único contra spam, scam e burlas por mensagem privada.', - }, -]; - -export default function Home(): ReactNode { - const [counts, setCounts] = useState(null); - - useEffect(() => { - let cancelled = false; - fetch('https://docs.raidprotect.bot/counts.json') - .then((res) => { - if (!res.ok) throw new Error('Failed to fetch counts'); - return res.json(); - }) - .then((data: Counts) => { - if (!cancelled) setCounts(data); - }) - .catch((err) => { - // Stats are best-effort; failure is non-blocking - // eslint-disable-next-line no-console - console.error('Stats update error:', err); - }); - return () => { - cancelled = true; - }; - }, []); - - return ( - -
- - - - {/* About */} -
-
-
-

- Os nossos resultados têm{' '} - impacto -

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - -
-
-
-
- - {/* Features */} -
-
-
-

- As nossas{' '} - funcionalidades -

-

- Descubra o que faz de nós um dos melhores bots para proteger o - seu servidor Discord de utilizadores maliciosos. -

-
-
- -
-
- - {/* Pricing */} -
-
-
-
-

- Manter-se um passo{' '} - à frente -

-

- Adicione o RaidProtect e comece a proteger o seu servidor hoje - mesmo. -

-
- -
- {/* Basic */} -
-
Basic
-
-

Gratuito

-
-

- A segurança essencial garantida para sempre -

-
- Proteções antispam - Bloqueio automático de raids - Filtragem de bots maliciosos - Moderação e administração - - E muito mais... - -
- -
- - {/* Founder */} -
-
- Founder -
-
-

Subscrição

-

- 2,99 $ -

-
-

- Oferta de lançamento reservada aos primeiros subscritores -

-
- Perfil do bot personalizável - Nomes de sanções personalizados - Acesso avançado ao Auth Manager - Acesso alargado ao Display Public - Acesso à Beta pública - Cargo exclusivo no nosso servidor -
- -
- - {/* Business */} -
-
- Business -
-
-

Sob consulta

-
-

- Para projetos com exigências de segurança elevadas -

-
- Todas as funcionalidades Founder - Instância dedicada e isolada - Auditoria inicial do seu servidor - Integração com as suas ferramentas - Funcionalidades à medida - Acompanhamento regular com um especialista - Suporte prioritário -
- -
-
-
-
-
-
- ); -} diff --git a/i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx b/i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx deleted file mode 100644 index 8aa29dc..0000000 --- a/i18n/pt/docusaurus-plugin-content-pages/thank-you.tsx +++ /dev/null @@ -1,398 +0,0 @@ -import React, {type ReactNode, useEffect, useState} from 'react'; -import Head from '@docusaurus/Head'; -import Link from '@docusaurus/Link'; -import styles from '@site/src/pages/thank-you.module.css'; - -type ServerBadge = 'partner' | 'verified' | null; - -type ServerInfo = { - name: string; - members: string; - inviteUrl: string; - iconUrl: string; - badge: ServerBadge; -}; - -type PermissionWarning = { - tone: 'admin' | 'missing'; - message: string; - missing: string[]; -}; - -const DEFAULT_ICON_URL = - 'https://cdn.prod.website-files.com/677fbd67c3c9318f7fb56659/67c33922eb3265808c183c50_411d8a698dd15ddf.webp'; - -const BADGE_SRC: Record, string> = { - partner: '/img/landing/serverBadgePartner.svg', - verified: '/img/landing/serverBadgeVerified.svg', -}; - -const REQUIRED_PERMISSIONS = 1117660769534n; -const ADMIN_PERMISSION = 8n; - -// Discord-style permission names (Portuguese). -const PERMISSION_MESSAGES: Array<[string, bigint]> = [ - ['Administrador', ADMIN_PERMISSION], - ['Gerir servidor', 32n], - ['Gerir cargos', 268435456n], - ['Gerir canais', 16n], - ['Expulsar membros', 2n], - ['Banir membros', 4n], - ['Gerir alcunhas', 134217728n], - ['Gerir webhooks', 536870912n], - ['Ver registo de auditoria', 524288n], - ['Ver canais', 1024n], - ['Moderar membros', 1099511627776n], - ['Enviar mensagens', 2048n], - ['Gerir mensagens', 8192n], - ['Gerir tópicos', 17179869184n], - ['Incorporar ligações', 16384n], - ['Anexar ficheiros', 32768n], - ['Ler histórico de mensagens', 65536n], - ['Adicionar reações', 64n], - ['Usar emojis externos', 262144n], - ['Silenciar membros', 4194304n], - ['Ensurdecer membros', 8388608n], - ['Mover membros', 16777216n], -]; - -async function fetchServerInfo(guildId: string): Promise { - try { - const widgetResponse = await fetch( - `https://discord.com/api/guilds/${guildId}/widget.json`, - ); - if (!widgetResponse.ok) { - throw new Error('Widget desativado.'); - } - const widgetData = await widgetResponse.json(); - - const name: string = widgetData.name || 'Servidor desconhecido'; - const members: string = - typeof widgetData.presence_count === 'number' - ? `${widgetData.presence_count} membros online` - : 'Número de membros desconhecido'; - const inviteUrl: string = widgetData.instant_invite || '#'; - - let iconUrl = DEFAULT_ICON_URL; - let badge: ServerBadge = null; - - const inviteCode: string | null = widgetData.instant_invite - ? widgetData.instant_invite.split('/').pop() || null - : null; - - if (inviteCode) { - const inviteResponse = await fetch( - `https://discord.com/api/invites/${inviteCode}?with_counts=true&with_expiration=true`, - ); - if (inviteResponse.ok) { - const inviteData = await inviteResponse.json(); - const server = inviteData.guild; - if (server?.icon) { - iconUrl = `https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`; - } - if (Array.isArray(server?.features)) { - if (server.features.includes('PARTNERED')) { - badge = 'partner'; - } else if (server.features.includes('VERIFIED')) { - badge = 'verified'; - } - } - } - } - - return {name, members, inviteUrl, iconUrl, badge}; - } catch (error) { - // Discord widget may be disabled or network may be unreachable. - // eslint-disable-next-line no-console - console.error(error); - return null; - } -} - -function computePermissionWarning( - permissionsParam: string, -): PermissionWarning | null { - let currentPermissions: bigint; - try { - currentPermissions = BigInt(permissionsParam); - } catch { - return null; - } - - const hasAdminPermission = - (currentPermissions & ADMIN_PERMISSION) === ADMIN_PERMISSION; - const missingPermissions = PERMISSION_MESSAGES.filter( - ([, value]) => - (REQUIRED_PERMISSIONS & value) === value && - (currentPermissions & value) !== value, - ).map(([name]) => name); - - if (hasAdminPermission || missingPermissions.length === 0) { - return null; - } - - if ( - missingPermissions.length === 1 && - missingPermissions[0] === 'Administrador' - ) { - return { - tone: 'admin', - message: - '⚠️ Todas as permissões específicas estão concedidas, mas sem a permissão de Administrador o bot pode não conseguir aceder a todos os canais.', - missing: [], - }; - } - - return { - tone: 'missing', - message: - '⚠️ Para garantir o bom funcionamento do bot, recomendamos adicionar as seguintes permissões:', - missing: missingPermissions, - }; -} - -export default function ThankYou(): ReactNode { - const [serverInfo, setServerInfo] = useState(null); - const [permissionWarning, setPermissionWarning] = - useState(null); - - useEffect(() => { - if (typeof window === 'undefined') return; - - const urlParams = new URLSearchParams(window.location.search); - - const guildId = urlParams.get('guild_id'); - let cancelled = false; - if (guildId) { - fetchServerInfo(guildId).then((info) => { - if (!cancelled && info) { - setServerInfo(info); - } - }); - } - - const permissionsParam = urlParams.get('permissions'); - if (permissionsParam) { - setPermissionWarning(computePermissionWarning(permissionsParam)); - } - - // Auto-redirect to Discord invite after 60s, matching the Webflow page. - const redirectTimer = window.setTimeout(() => { - window.location.href = 'https://discord.com/invite/HfMYDHbgqc'; - }, 60000); - - return () => { - cancelled = true; - window.clearTimeout(redirectTimer); - }; - }, []); - - return ( - <> - - Obrigado | RaidProtect - - -
-
- - RaidProtect title logo - - -

Obrigado por convidar o RaidProtect!

- - {serverInfo && ( - - )} - - {permissionWarning && ( -
- {permissionWarning.message} - {permissionWarning.missing.length > 0 && ( -
    - {permissionWarning.missing.map((perm) => ( -
  • - {perm} -
  • - ))} -
- )} -
- )} - -

- Para começar com o pé direito, recomendamos consultar a nossa - documentação e entrar no nosso servidor. -

- -
- - Entrar no nosso servidor Discord - - - Consultar a documentação - -
- - -
- -
- - ); -} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 69d2115..1bab475 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,6 +2,7 @@ import React, {type ReactNode, useEffect, useState} from 'react'; import clsx from 'clsx'; import Layout from '@theme/Layout'; import Link from '@docusaurus/Link'; +import Translate, {translate} from '@docusaurus/Translate'; import Hero from '@site/src/components/landing/Hero'; import Servers from '@site/src/components/landing/Servers'; import shared from '@site/src/components/landing/styles/shared.module.css'; @@ -74,60 +75,67 @@ function FeatureItem({children}: {children: ReactNode}) { } type Feature = { + slug: string; to: string; icon: string; iconAlt: string; - title: string; - description: string; + defaultTitle: string; + defaultDescription: string; }; const FEATURES: Feature[] = [ { + slug: 'anti-spam', to: '/docs/features/anti-spam', icon: '/img/landing/iconAntispamWhite.svg', iconAlt: 'RaidProtect icon Antispam', - title: 'Protection anti-spam', - description: + defaultTitle: 'Protection anti-spam', + defaultDescription: 'Sanctionnez instantanément les tentatives de spam, sans aucune intervention de votre part.', }, { + slug: 'raid', to: '/docs/features/raid-mode', icon: '/img/landing/iconAntiraidWhite.svg', iconAlt: 'RaidProtect icon Antiraid', - title: 'Blocage des raids', - description: + defaultTitle: 'Blocage des raids', + defaultDescription: "Vous craignez un raid ? Notre bot est capable de le détecter et de le bloquer avant même qu'il impacte votre serveur.", }, { + slug: 'captcha', to: '/docs/features/captcha', icon: '/img/landing/iconCaptchaWhite.svg', iconAlt: 'RaidProtect icon Captcha', - title: 'Protection contre les robots', - description: + defaultTitle: 'Protection contre les robots', + defaultDescription: "Grâce au captcha, vos membres doivent prouver qu'ils sont humains. Dites adieu aux comptes automatisés.", }, { + slug: 'mod', to: '/docs/features/utilities', icon: '/img/landing/iconReportWhite.svg', iconAlt: 'RaidProtect icon Report', - title: 'Modération & administration', - description: + defaultTitle: 'Modération & administration', + defaultDescription: "Gérez votre serveur comme un pro avec nos diverses fonctionalités de modération et d'administration.", }, { + slug: 'tag', to: '/docs/features/tag-role', icon: '/img/landing/iconTagWhite.svg', iconAlt: 'RaidProtect icon Tag', - title: 'Rôle de Tag', - description: + defaultTitle: 'Rôle de Tag', + defaultDescription: 'Le Rôle de Tag permet d’attribuer automatiquement un rôle aux membres qui ajoutent le tag de votre serveur.', }, { + slug: 'dm', to: '/docs/features/dm-lock', icon: '/img/landing/iconDmlockWhite.svg', iconAlt: 'RaidProtect icon DM Lock', - title: 'Fermeture des MP', - description: + defaultTitle: 'Fermeture des MP', + defaultDescription: 'Un bouclier inédit contre le spam, le scam et les arnaques par message privé.', }, ]; @@ -155,10 +163,25 @@ export default function Home(): ReactNode { }; }, []); + const layoutTitle = translate({ + id: 'landing.layout.title', + message: 'RaidProtect • Sécurisez votre serveur Discord', + description: 'Browser tab title for the landing page', + }); + const layoutDescription = translate({ + id: 'landing.layout.description', + message: + 'RaidProtect est un bot Discord français ayant pour mission de protéger simplement votre serveur des utilisateurs malintentionnés.', + description: 'Meta description for the landing page', + }); + const aboutCurveAriaLabel = translate({ + id: 'landing.about.curve.ariaLabel', + message: 'Croissance du nombre de serveurs', + description: 'ARIA label for the decorative SVG curve in the about section', + }); + return ( - +
@@ -173,8 +196,22 @@ export default function Home(): ReactNode {

- Nos résultats ont un{' '} - impact + + + impact + + + ), + }}> + {'Nos résultats ont un {highlight}'} +

@@ -183,7 +220,7 @@ export default function Home(): ReactNode { className={styles.curve} viewBox="0 0 900 280" role="img" - aria-label="Croissance du nombre de serveurs"> + aria-label={aboutCurveAriaLabel}> @@ -241,22 +278,38 @@ export default function Home(): ReactNode {
@@ -275,38 +328,69 @@ export default function Home(): ReactNode {

- Nos fonctionnalités + + + fonctionnalités + + + ), + }}> + {'Nos {highlight}'} +

- Découvrez ce qui fait de nous l'un des meilleurs bots pour protéger - votre serveur Discord des utilisateurs malintentionnés. + + Découvrez ce qui fait de nous l'un des meilleurs bots pour + protéger votre serveur Discord des utilisateurs malintentionnés. +

@@ -319,12 +403,30 @@ export default function Home(): ReactNode {

- Garder une longueur{' '} - d'avance + + + d'avance + + + ), + }}> + {'Garder une longueur {highlight}'} +

- Ajoutez RaidProtect et commencez à protéger votre serveur dès - aujourd'hui. + + Ajoutez RaidProtect et commencez à protéger votre serveur dès + aujourd'hui. +