Skip to content
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ccfea57
feat(status-page): install next-intl 4.5+ and create i18n config
aggmoulik Mar 7, 2026
90feb8e
feat(status-page): add next-intl plugin to next.config.ts
aggmoulik Mar 7, 2026
4e68768
feat(status-page): add [locale] route segment and move routes
aggmoulik Mar 7, 2026
882ac92
feat(status-page): use useExtracted for status component strings
aggmoulik Mar 12, 2026
b9331ee
feat(status-page): use useExtracted for navigation strings
aggmoulik Mar 7, 2026
7321996
feat(status-page): use useExtracted for events, report, maintenance, …
aggmoulik Mar 7, 2026
5581722
feat(status-page): use useExtracted for auth, forms, and updates strings
aggmoulik Mar 12, 2026
7a5240b
feat(status-page): delete old messages.ts, add language switcher
aggmoulik Mar 7, 2026
b5f131b
feat(status-page): make pathname prefix locale-aware
aggmoulik Mar 7, 2026
2e36a0d
fix(status-page): switch message catalogs from .po to .json format
aggmoulik Mar 7, 2026
5a4e381
Format fix
aggmoulik Mar 7, 2026
af63c63
WIP Status Page static and domain specific
aggmoulik Mar 12, 2026
44594f4
Update the extracted config and proxy fix
aggmoulik Mar 11, 2026
c1199e1
ci: apply automated fixes
aggmoulik Mar 12, 2026
8e0d807
Removed unncessary static component with ui package
aggmoulik Mar 12, 2026
792216b
Updated status-events with blocks component
aggmoulik Mar 12, 2026
6ded716
wip: i18n
mxkaske Mar 20, 2026
b9ef820
fix: date-fns locale
mxkaske Mar 20, 2026
c4b8ccf
fix: locale navigation
mxkaske Mar 20, 2026
7560c1c
chore: locale switcher
mxkaske Mar 20, 2026
3fd58bf
chore: format theme switcher
mxkaske Mar 20, 2026
2135758
Merge branch 'main' into feat/status-page-i18n
mxkaske Mar 20, 2026
b8030ec
fix: missing translations
mxkaske Mar 20, 2026
e99ab77
fix: review
mxkaske Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions apps/status-page/messages/de.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"Y9HHck": "Authentifizieren",
"1QcGkA": "Geben Sie Ihre E-Mail-Adresse ein, um einen Magic Link für den Zugriff auf die Statusseite zu erhalten. Hinweis: Es werden nur E-Mails von genehmigten Domains akzeptiert.",
"txkW56": "Wird gesendet...",
"wSZR47": "Absenden",
"OrFVks": "Überprüfen Sie Ihren Posteingang!",
"n36zhX": "Greifen Sie auf die Statusseite zu, indem Sie auf den Link in der E-Mail klicken.",
"qIAQSi": "Geschützte Seite",
"lbw10C": "Geben Sie das Passwort ein, um auf die Statusseite zuzugreifen.",
"t262xH": "E-Mail und Weiterleitungs-URL sind erforderlich",
"DwevKz": "Bei der Anmeldung ist ein unerwarteter Fehler aufgetreten",
"Ppx673": "Berichte",
"JCMXwP": "Wartungen",
"I7B7SH": "Keine Wartungen gefunden",
"OSI607": "Keine Wartungen für diese Statusseite gefunden.",
"a9S/OH": "Wartung nicht gefunden",
"HSv9BP": "Die gesuchte Wartung existiert nicht.",
"VL1Y/1": "Bericht nicht gefunden",
"Ew1f8q": "Der gesuchte Bericht existiert nicht.",
"awr0AJ": "Monitor nicht gefunden",
"CVsoUM": "Der gesuchte Monitor existiert nicht.",
"BRGcS0": "Globale Latenz",
"9vqdq3": "Regionale Latenz",
"fFOayY": "Regionen",
"u81G9+": "Verfügbarkeit",
"i2FBWn": "Prüfungen",
"G5Lt80": "Die aggregierte Latenz aller aktiven Regionen basierend auf verschiedenen Quantilen.",
"YV7rXP": "Latenz nach Region",
"6zzIEm": "Regionale Latenz pro p75-Quantil, sortiert nach langsamster Region. Vergleichen Sie bis zu 6 Regionen.",
"6pCzRs": "Gesamtverfügbarkeit",
"zL23+z": "Hauptwerte der Verfügbarkeit, transparent dargestellt.",
"79eRW1": "Bestätigung läuft...",
"dX7+Rv": "Bestätigt",
"m0fapd": "Bestätigung fehlgeschlagen",
"sy+pv5": "E-Mail",
"9Utk00": "Abonnement wird aktualisiert...",
"Eq5gCU": "Abonnement aktualisiert",
"qp+wDV": "Aktualisierung des Abonnements fehlgeschlagen",
"d/jCcy": "Bestimmte Komponenten abonnieren",
"FlVuUh": "Keine Komponenten zum Abonnieren",
"8aUjqQ": "Diese Statusseite hat keine Komponenten zum Abonnieren.",
"5sg7KC": "Passwort",
"IGY48m": "Wird abonniert...",
"Pgb3Xj": "Abonniert",
"WOH7Yj": "Abonnement fehlgeschlagen",
"L7z2/k": "Diese Seite hat keine Komponenten zum Abonnieren.",
"NOyDVq": "betrieben von",
"tzMNF3": "Status",
"ZvKSfJ": "Ereignisse",
"xJrRMG": "Monitore",
"tKMlOc": "Menü",
"krEziQ": "Kontakt aufnehmen",
"BQBZU+": "Alle Systeme betriebsbereit",
"b9fOA1": "Eingeschränkte Leistung",
"80EXUh": "Ausfall",
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 20, 2026

Choose a reason for hiding this comment

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

P2: Both "80EXUh" ("Downtime Performance") and "JOZGPR" ("Downtime") are translated to the same German string "Ausfall". Consider differentiating them — e.g., "Ausfallleistung" or "Beeinträchtigter Betrieb (Ausfall)" for the longer variant — so the two labels remain distinguishable in the UI, matching the intent of the English source.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/status-page/messages/de.json, line 55:

<comment>Both `"80EXUh"` ("Downtime Performance") and `"JOZGPR"` ("Downtime") are translated to the same German string `"Ausfall"`. Consider differentiating them — e.g., `"Ausfallleistung"` or `"Beeinträchtigter Betrieb (Ausfall)"` for the longer variant — so the two labels remain distinguishable in the UI, matching the intent of the English source.</comment>

<file context>
@@ -0,0 +1,88 @@
+  "krEziQ": "Kontakt aufnehmen",
+  "BQBZU+": "Alle Systeme betriebsbereit",
+  "b9fOA1": "Eingeschränkte Leistung",
+  "80EXUh": "Ausfall",
+  "dudqv/": "Wartung",
+  "2syGZB": "Bericht gelöst",
</file context>
Fix with Cubic

"dudqv/": "Wartung",
"2syGZB": "Bericht gelöst",
"W6nSYE": "Gelöst",
"1P6GMj": "Überwachung",
"7cv4Uf": "Identifiziert",
"/GKH/w": "Wird untersucht",
"FDReLp": "Keine aktuellen Benachrichtigungen",
"qDj0JR": "In den letzten 7 Tagen gab es keine Berichte.",
"CYs0LF": "Ereignisverlauf anzeigen",
"kkpP2k": "heute",
"Dnob31": "Betriebsbereit",
"VQDmmK": "Eingeschränkt",
"JOZGPR": "Ausfall",
"GbVCQb": "Erneut klicken zum Lösen",
"myq2ZL": "Normal",
"KN7zKn": "Fehler",
"D3rOMr": "Keine Daten",
"uPb/gh": "Updates erhalten",
"sjzDbu": "Slack",
"q0qMyV": "RSS",
"9y9QQh": "JSON",
"waUHa4": "SSH",
"cVqFq/": "Erhalten Sie E-Mail-Benachrichtigungen, wenn ein Bericht erstellt oder gelöst wird",
"gczcC5": "Abonnieren",
"8OoV56": "RSS-Feed abrufen",
"Auj/Ki": "Atom-Feed abrufen",
"SyYroX": "JSON-Updates abrufen",
"PSqtlY": "Status über SSH abrufen",
"rptmhC": "Für Status-Updates in Slack fügen Sie den folgenden Text in einen beliebigen Kanal ein.",
"2yCGR2": "Link in die Zwischenablage kopiert",
"u5aHb4": "Link kopieren",
"45YlLU": "Bestätigen Sie Ihre E-Mail-Adresse, um Updates zu erhalten, und schon sind Sie fertig."
}
88 changes: 88 additions & 0 deletions apps/status-page/messages/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"Y9HHck": "Authenticate",
"1QcGkA": "Enter your email to receive a magic link for accessing the status page. Note: Only emails from approved domains are accepted.",
"txkW56": "Submitting...",
"wSZR47": "Submit",
"OrFVks": "Check your inbox!",
"n36zhX": "Access the status page by clicking the link in the email.",
"qIAQSi": "Protected Page",
"lbw10C": "Enter the password to access the status page.",
"t262xH": "Email and redirectTo are required",
"DwevKz": "An unexpected error occurred during sign in",
"Ppx673": "Reports",
"JCMXwP": "Maintenances",
"I7B7SH": "No maintenances found",
"OSI607": "No maintenances found for this status page.",
"a9S/OH": "Maintenance not found",
"HSv9BP": "The maintenance you are looking for does not exist.",
"VL1Y/1": "Report not found",
"Ew1f8q": "The report you are looking for does not exist.",
"awr0AJ": "Monitor not found",
"CVsoUM": "The monitor you are looking for does not exist.",
"BRGcS0": "Global Latency",
"9vqdq3": "Region Latency",
"fFOayY": "regions",
"u81G9+": "Uptime",
"i2FBWn": "checks",
"G5Lt80": "The aggregated latency from all active regions based on different quantiles.",
"YV7rXP": "Latency by Region",
"6zzIEm": "Region latency per p75 quantile, sorted by slowest region. Compare up to 6 regions.",
"6pCzRs": "Total Uptime",
"zL23+z": "Main values of uptime and availability, transparent.",
"79eRW1": "Confirming...",
"dX7+Rv": "Confirmed",
"m0fapd": "Failed to confirm",
"sy+pv5": "Email",
"9Utk00": "Updating subscription...",
"Eq5gCU": "Subscription updated",
"qp+wDV": "Failed to update subscription",
"d/jCcy": "Subscribe to specific components",
"FlVuUh": "No components to subscribe to",
"8aUjqQ": "This status page has no components to subscribe to.",
"5sg7KC": "Password",
"IGY48m": "Subscribing...",
"Pgb3Xj": "Subscribed",
"WOH7Yj": "Failed to subscribe",
"L7z2/k": "This page has no components to subscribe to.",
"NOyDVq": "powered by",
"tzMNF3": "Status",
"ZvKSfJ": "Events",
"xJrRMG": "Monitors",
"tKMlOc": "Menu",
"krEziQ": "Get in touch",
"BQBZU+": "All Systems Operational",
"b9fOA1": "Degraded Performance",
"80EXUh": "Downtime Performance",
"dudqv/": "Maintenance",
"2syGZB": "Report resolved",
"W6nSYE": "Resolved",
"1P6GMj": "Monitoring",
"7cv4Uf": "Identified",
"/GKH/w": "Investigating",
"FDReLp": "No recent notifications",
"qDj0JR": "There have been no reports within the last 7 days.",
"CYs0LF": "View events history",
"kkpP2k": "today",
"Dnob31": "Operational",
"VQDmmK": "Degraded",
"JOZGPR": "Downtime",
"GbVCQb": "Click again to unpin",
"myq2ZL": "Normal",
"KN7zKn": "Error",
"D3rOMr": "No Data",
"uPb/gh": "Get updates",
"sjzDbu": "Slack",
"q0qMyV": "RSS",
"9y9QQh": "JSON",
"waUHa4": "SSH",
"cVqFq/": "Get email notifications whenever a report has been created or resolved",
"gczcC5": "Subscribe",
"8OoV56": "Get the RSS feed",
"Auj/Ki": "Get the Atom feed",
"SyYroX": "Get the JSON updates",
"PSqtlY": "Get status via SSH",
"rptmhC": "For status updates in Slack, paste the text below into any channel.",
"2yCGR2": "Link copied to clipboard",
"u5aHb4": "Copy Link",
"45YlLU": "Validate your email to receive updates and you are all set."
}
88 changes: 88 additions & 0 deletions apps/status-page/messages/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"Y9HHck": "S'authentifier",
"1QcGkA": "Entrez votre email pour recevoir un lien magique d'accès à la page de statut. Remarque : seuls les emails provenant de domaines approuvés sont acceptés.",
"txkW56": "Envoi en cours...",
"wSZR47": "Envoyer",
"OrFVks": "Vérifiez votre boîte de réception !",
"n36zhX": "Accédez à la page de statut en cliquant sur le lien dans l'email.",
"qIAQSi": "Page protégée",
"lbw10C": "Entrez le mot de passe pour accéder à la page de statut.",
"t262xH": "L'email et l'URL de redirection sont requis",
"DwevKz": "Une erreur inattendue s'est produite lors de la connexion",
"Ppx673": "Rapports",
"JCMXwP": "Maintenances",
"I7B7SH": "Aucune maintenance trouvée",
"OSI607": "Aucune maintenance trouvée pour cette page de statut.",
"a9S/OH": "Maintenance introuvable",
"HSv9BP": "La maintenance que vous recherchez n'existe pas.",
"VL1Y/1": "Rapport introuvable",
"Ew1f8q": "Le rapport que vous recherchez n'existe pas.",
"awr0AJ": "Moniteur introuvable",
"CVsoUM": "Le moniteur que vous recherchez n'existe pas.",
"BRGcS0": "Latence globale",
"9vqdq3": "Latence par région",
"fFOayY": "régions",
"u81G9+": "Disponibilité",
"i2FBWn": "vérifications",
"G5Lt80": "La latence agrégée de toutes les régions actives basée sur différents quantiles.",
"YV7rXP": "Latence par région",
"6zzIEm": "Latence par région au quantile p75, triée par région la plus lente. Comparez jusqu'à 6 régions.",
"6pCzRs": "Disponibilité totale",
"zL23+z": "Valeurs principales de disponibilité, en toute transparence.",
"79eRW1": "Confirmation en cours...",
"dX7+Rv": "Confirmé",
"m0fapd": "Échec de la confirmation",
"sy+pv5": "Email",
"9Utk00": "Mise à jour de l'abonnement...",
"Eq5gCU": "Abonnement mis à jour",
"qp+wDV": "Échec de la mise à jour de l'abonnement",
"d/jCcy": "S'abonner à des composants spécifiques",
"FlVuUh": "Aucun composant auquel s'abonner",
"8aUjqQ": "Cette page de statut n'a aucun composant auquel s'abonner.",
"5sg7KC": "Mot de passe",
"IGY48m": "Abonnement en cours...",
"Pgb3Xj": "Abonné",
"WOH7Yj": "Échec de l'abonnement",
"L7z2/k": "Cette page n'a aucun composant auquel s'abonner.",
"NOyDVq": "propulsé par",
"tzMNF3": "Statut",
"ZvKSfJ": "Événements",
"xJrRMG": "Moniteurs",
"tKMlOc": "Menu",
"krEziQ": "Nous contacter",
"BQBZU+": "Tous les systèmes sont opérationnels",
"b9fOA1": "Performances dégradées",
"80EXUh": "Performances en panne",
"dudqv/": "Maintenance",
"2syGZB": "Rapport résolu",
"W6nSYE": "Résolu",
"1P6GMj": "Surveillance",
"7cv4Uf": "Identifié",
"/GKH/w": "En cours d'investigation",
"FDReLp": "Aucune notification récente",
"qDj0JR": "Aucun rapport au cours des 7 derniers jours.",
"CYs0LF": "Voir l'historique des événements",
"kkpP2k": "aujourd'hui",
"Dnob31": "Opérationnel",
"VQDmmK": "Dégradé",
"JOZGPR": "En panne",
"GbVCQb": "Cliquez à nouveau pour désépingler",
"myq2ZL": "Normal",
"KN7zKn": "Erreur",
"D3rOMr": "Aucune donnée",
"uPb/gh": "Recevoir les mises à jour",
"sjzDbu": "Slack",
"q0qMyV": "RSS",
"9y9QQh": "JSON",
"waUHa4": "SSH",
"cVqFq/": "Recevez des notifications par email à chaque création ou résolution d'un rapport",
"gczcC5": "S'abonner",
"8OoV56": "Obtenir le flux RSS",
"Auj/Ki": "Obtenir le flux Atom",
"SyYroX": "Obtenir les mises à jour JSON",
"PSqtlY": "Obtenir le statut via SSH",
"rptmhC": "Pour recevoir les mises à jour dans Slack, collez le texte ci-dessous dans n'importe quel canal.",
"2yCGR2": "Lien copié dans le presse-papiers",
"u5aHb4": "Copier le lien",
"45YlLU": "Validez votre email pour recevoir les mises à jour et le tour est joué."
}
55 changes: 18 additions & 37 deletions apps/status-page/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { defaultLocale, locales } from "@/i18n/config";
import { withSentryConfig } from "@sentry/nextjs";

import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";

const withNextIntl = createNextIntlPlugin({
requestConfig: "./src/i18n/request.ts",
experimental: {
srcPath: "./src",
extract: {
sourceLocale: defaultLocale,
},
messages: {
path: "./messages",
format: "json",
locales,
},
},
});

const nextConfig: NextConfig = {
output: process.env.SELF_HOST === "true" ? "standalone" : undefined,
Expand All @@ -18,41 +34,6 @@ const nextConfig: NextConfig = {
fullUrl: true,
},
},
async rewrites() {
return {
beforeFiles: [
{
source:
"/:path((?!api|assets|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
has: [
{
type: "host",
value:
process.env.NODE_ENV === "production"
? "(?<subdomain>[^.]+).stpg.dev"
: "(?<subdomain>[^.]+).localhost",
},
],
missing: [
// Skip this rewrite when the request came via proxy from web app
{
type: "header",
key: "x-proxy",
value: "1",
},
{
type: "host",
value:
process.env.NODE_ENV === "production"
? "www.stpg.dev"
: "localhost",
},
],
destination: "/:subdomain/:path*",
},
],
};
},
};

// For detailed options, refer to the official documentation:
Expand All @@ -79,4 +60,4 @@ const sentryConfig = {
},
};

export default withSentryConfig(nextConfig, sentryConfig);
export default withSentryConfig(withNextIntl(nextConfig), sentryConfig);
1 change: 1 addition & 0 deletions apps/status-page/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"lucide-react": "0.525.0",
"next": "16.1.6",
"next-auth": "5.0.0-beta.29",
"next-intl": "^4.8.3",
"next-plausible": "3.12.5",
"next-themes": "0.4.6",
"nuqs": "2.8.5",
Expand Down
6 changes: 3 additions & 3 deletions apps/status-page/src/app/(public)/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import {
SectionTitle,
} from "@/components/content/section";
import { recomputeStyles } from "@/components/status-page/floating-button";
import { StatusMonitorStatic } from "@/components/status-page/static/status-monitor-static";
import {
Status,
StatusContent,
StatusDescription,
StatusHeader,
StatusTitle,
} from "@/components/status-page/status";
import { StatusBanner } from "@/components/status-page/status-banner";
import { StatusMonitor } from "@/components/status-page/status-monitor";
import { ThemePalettePicker } from "@/components/themes/theme-palette-picker";
import { ThemeSelect } from "@/components/themes/theme-select";
import { monitors } from "@/data/monitors";
import { useTRPC } from "@/lib/trpc/client";
import { THEMES, THEME_KEYS } from "@openstatus/theme-store";
import { StatusBanner } from "@openstatus/ui/components/blocks/status-banner";
import { Button } from "@openstatus/ui/components/ui/button";
import { Input } from "@openstatus/ui/components/ui/input";
import { Separator } from "@openstatus/ui/components/ui/separator";
Expand Down Expand Up @@ -298,7 +298,7 @@ function ThemePlaygroundStatus({
<StatusBanner status="success" />
<StatusContent>
{/* TODO: create mock data */}
<StatusMonitor
<StatusMonitorStatic
status="success"
data={uptimeData?.data || []}
monitor={monitors[0]}
Expand Down
Loading
Loading