SignalMod / frontend /src /i18n /locales.ts
Ruperth's picture
feat: add i18n context with es and en dictionaries
21e8095
Raw
History Blame Contribute Delete
10.7 kB
export type Locale = "es" | "en";
export type Dict = {
brand: { tagline: string };
nav: {
home: string;
watch: string;
hub: string;
settings: string;
shorts: string;
subscriptions: string;
library: string;
history: string;
yourVideos: string;
moderation: string;
database: string;
};
topbar: {
searchPlaceholder: string;
signIn: string;
toggleSidebar: string;
toggleTheme: string;
toggleLanguage: string;
languageLabel: string;
themeLightLabel: string;
themeDarkLabel: string;
};
watch: {
defaultTitle: string;
defaultMeta: string;
suggestedVideo: string;
upNext: string;
externalOnly: string;
loadingComments: string;
loadingRecent: string;
commentsCount: (n: number) => string;
toxicDetected: (n: number) => string;
composePlaceholder: string;
composeAriaLabel: string;
analyzing: string;
liveScore: string;
toxicity: string;
cancel: string;
comment: string;
fromYoutube: string;
justNow: string;
posted: string;
posting: string;
you: string;
flagged: string;
demoBanner: string;
placeholderTitleBanner: string;
couldNotLoadVideos: string;
failedToLoadComments: string;
watchOnYoutube: string;
dismiss: string;
channelFallback: string;
};
hub: {
title: string;
threshold: string;
eventsLogged: string;
toxicSession: string;
safeVsToxic: string;
safe: string;
toxic: string;
recentScores: string;
recentActions: string;
colUser: string;
colComment: string;
colScore: string;
colAction: string;
emptyHistory: string;
};
settings: {
title: string;
activeModel: string;
productionNote: (f1: string, gap: string) => string;
baselinesNote: (lrF1: string, bertF1: string, bertGap: string) => string;
productionLabel: string;
lrLabel: string;
bertLabel: string;
installHint: string;
switching: string;
failedSwitch: string;
couldNotLoadStatus: string;
activeModelMsg: (name: string) => string;
thresholdTitle: string;
thresholdNote: string;
quickTest: string;
analyze: string;
analyzing: string;
testResult: (status: string, pct: string) => string;
analysisFailed: string;
defaultTestText: string;
};
badges: { safe: string; toxic: string };
modelBanner: { current: (name: string, f1: string, gap: string) => string };
time: {
justNow: string;
minutesAgo: (n: number) => string;
hoursAgo: (n: number) => string;
daysAgo: (n: number) => string;
};
};
const es: Dict = {
brand: { tagline: "Moderación inteligente para YouTube." },
nav: {
home: "Inicio",
watch: "Ver",
hub: "Panel",
settings: "Ajustes",
shorts: "Shorts",
subscriptions: "Suscripciones",
library: "Biblioteca",
history: "Historial",
yourVideos: "Tus vídeos",
moderation: "Moderación",
database: "Base de datos",
},
topbar: {
searchPlaceholder: "Buscar más vídeos...",
signIn: "Iniciar sesión",
toggleSidebar: "Mostrar menú",
toggleTheme: "Cambiar tema",
toggleLanguage: "Cambiar idioma",
languageLabel: "ES",
themeLightLabel: "Claro",
themeDarkLabel: "Oscuro",
},
watch: {
defaultTitle: "Ver y moderar comentarios",
defaultMeta: "Elige un vídeo de Siguiente para cargar y puntuar sus comentarios",
suggestedVideo: "Vídeo sugerido",
upNext: "Siguiente",
externalOnly: "Solo externo",
loadingComments: "Cargando comentarios…",
loadingRecent: "Cargando comentarios recientes…",
commentsCount: (n) => `${n} comentarios`,
toxicDetected: (n) => ` · ${n} tóxicos detectados`,
composePlaceholder: "Añade un comentario…",
composeAriaLabel: "Escribe un comentario",
analyzing: "Analizando…",
liveScore: "Puntuación en vivo",
toxicity: "Toxicidad",
cancel: "Cancelar",
comment: "Comentar",
fromYoutube: "desde YouTube",
justNow: "ahora mismo",
posted: "Publicado",
posting: "Publicando…",
you: "tú",
flagged: "Marcado para revisión",
demoBanner: "Usando comentarios demo — añade YOUTUBE_API_KEY a .env para hilos reales de YouTube.",
placeholderTitleBanner: "Metadatos demo — añade YOUTUBE_API_KEY a .env para títulos reales.",
couldNotLoadVideos: "No se pudieron cargar los vídeos sugeridos",
failedToLoadComments: "No se pudieron cargar los comentarios",
watchOnYoutube: "Ver en YouTube (embed bloqueado)",
dismiss: "Cerrar",
channelFallback: "YouTube",
},
hub: {
title: "Panel de moderación",
threshold: "Umbral",
eventsLogged: "Eventos registrados",
toxicSession: "Tóxicos (sesión)",
safeVsToxic: "Seguros vs Tóxicos",
safe: "Seguro",
toxic: "Tóxico",
recentScores: "Puntuaciones recientes (%)",
recentActions: "Acciones recientes",
colUser: "Usuario",
colComment: "Comentario",
colScore: "Puntuación",
colAction: "Acción",
emptyHistory: "Publica comentarios en la página Ver para poblar el historial.",
},
settings: {
title: "Ajustes",
activeModel: "Modelo activo",
productionNote: (f1, gap) => `Por defecto: Meta-Feature Stacking (Producción) (F1 ${f1}, gap ${gap}%).`,
baselinesNote: (lrF1, bertF1, bertGap) =>
`Bases: LR + TF-IDF (F1 ${lrF1}) y Frozen Toxic-BERT (F1 ${bertF1}, gap ${bertGap}%).`,
productionLabel: "Meta-Feature Stacking (Producción)",
lrLabel: "LR + TF-IDF",
bertLabel: "Frozen Toxic-BERT",
installHint:
"Producción y BERT congelado requieren uv sync --extra hf (o Docker INSTALL_HF=1). El baseline LR usa solo joblib. La primera carga del transformer puede descargar pesos (~1 min).",
switching: "Cambiando modelo… producción puede tardar hasta un minuto en la primera carga.",
failedSwitch: "No se pudo cambiar de modelo",
couldNotLoadStatus: "No se pudo cargar el estado de los modelos",
activeModelMsg: (name) => `Modelo activo: ${name}`,
thresholdTitle: "Umbral de toxicidad",
thresholdNote: "los comentarios con probabilidad igual o superior se marcan como Tóxicos.",
quickTest: "Prueba rápida",
analyze: "Analizar",
analyzing: "Analizando…",
testResult: (status, pct) => `${status}${pct}% tóxico`,
analysisFailed: "El análisis falló",
defaultTestText: "Eres un idiota",
},
badges: { safe: "Seguro", toxic: "Tóxico" },
modelBanner: {
current: (name, f1, gap) => `En uso: ${name} (F1: ${f1}, Gap: ${gap}%)`,
},
time: {
justNow: "ahora mismo",
minutesAgo: (n) => `hace ${n} min`,
hoursAgo: (n) => `hace ${n} h`,
daysAgo: (n) => `hace ${n} d`,
},
};
const en: Dict = {
brand: { tagline: "Intelligent moderation for YouTube." },
nav: {
home: "Home",
watch: "Watch",
hub: "Hub",
settings: "Settings",
shorts: "Shorts",
subscriptions: "Subscriptions",
library: "Library",
history: "History",
yourVideos: "Your videos",
moderation: "Moderation",
database: "Database",
},
topbar: {
searchPlaceholder: "Search more videos...",
signIn: "Sign in",
toggleSidebar: "Toggle sidebar",
toggleTheme: "Toggle theme",
toggleLanguage: "Toggle language",
languageLabel: "EN",
themeLightLabel: "Light",
themeDarkLabel: "Dark",
},
watch: {
defaultTitle: "Watch and moderate comments",
defaultMeta: "Choose a video from Up next to load and score its comments",
suggestedVideo: "Suggested video",
upNext: "Up next",
externalOnly: "External only",
loadingComments: "Loading comments…",
loadingRecent: "Loading recent comments…",
commentsCount: (n) => `${n} comments`,
toxicDetected: (n) => ` · ${n} toxic detected`,
composePlaceholder: "Add a comment…",
composeAriaLabel: "Write a comment",
analyzing: "Analyzing…",
liveScore: "Live score",
toxicity: "Toxicity",
cancel: "Cancel",
comment: "Comment",
fromYoutube: "from YouTube",
justNow: "just now",
posted: "Posted",
posting: "Posting…",
you: "you",
flagged: "Flagged for review",
demoBanner: "Using demo comments — add YOUTUBE_API_KEY to .env for real YouTube threads.",
placeholderTitleBanner: "Demo metadata — add YOUTUBE_API_KEY to .env for real titles.",
couldNotLoadVideos: "Could not load suggested videos",
failedToLoadComments: "Failed to load comments",
watchOnYoutube: "Watch on YouTube (embedding blocked)",
dismiss: "Dismiss",
channelFallback: "YouTube",
},
hub: {
title: "Moderator Hub",
threshold: "Threshold",
eventsLogged: "Events logged",
toxicSession: "Toxic (session)",
safeVsToxic: "Safe vs Toxic",
safe: "Safe",
toxic: "Toxic",
recentScores: "Recent scores (%)",
recentActions: "Recent actions",
colUser: "User",
colComment: "Comment",
colScore: "Score",
colAction: "Action",
emptyHistory: "Post comments on the Watch page to populate history.",
},
settings: {
title: "Settings",
activeModel: "Active model",
productionNote: (f1, gap) => `Default: Meta-Feature Stacking (Production) (F1 ${f1}, gap ${gap}%).`,
baselinesNote: (lrF1, bertF1, bertGap) =>
`Baselines: LR + TF-IDF (F1 ${lrF1}) and Frozen Toxic-BERT (F1 ${bertF1}, gap ${bertGap}%).`,
productionLabel: "Meta-Feature Stacking (Production)",
lrLabel: "LR + TF-IDF",
bertLabel: "Frozen Toxic-BERT",
installHint:
"Production and frozen BERT need uv sync --extra hf (or Docker INSTALL_HF=1). LR baseline uses joblib only. First transformer load may download weights (~1 min).",
switching: "Switching model… production may take up to a minute on first load.",
failedSwitch: "Failed to switch model",
couldNotLoadStatus: "Could not load model status",
activeModelMsg: (name) => `Active model: ${name}`,
thresholdTitle: "Toxicity threshold",
thresholdNote: "comments at or above this probability are flagged as Toxic.",
quickTest: "Quick test",
analyze: "Analyze",
analyzing: "Analyzing…",
testResult: (status, pct) => `${status}${pct}% toxic`,
analysisFailed: "Analysis failed",
defaultTestText: "You are an idiot",
},
badges: { safe: "Safe", toxic: "Toxic" },
modelBanner: {
current: (name, f1, gap) => `Currently using: ${name} (F1: ${f1}, Gap: ${gap}%)`,
},
time: {
justNow: "just now",
minutesAgo: (n) => `${n}m ago`,
hoursAgo: (n) => `${n}h ago`,
daysAgo: (n) => `${n}d ago`,
},
};
export const DICTIONARIES: Record<Locale, Dict> = { es, en };