290 lines
14 KiB
TypeScript
290 lines
14 KiB
TypeScript
"use server";
|
||
import { context, cookies, redirect } from "@bureaudouble/rsc/server/context";
|
||
import { i18n as signalI18n, type Variables } from "@signal/i18n";
|
||
|
||
export const setLocale = async () => {
|
||
const cookieStore = await cookies();
|
||
cookieStore.set(
|
||
"locale",
|
||
cookieStore.get("locale") === "en_GB" ? "fr_FR" : "en_GB",
|
||
);
|
||
redirect(
|
||
context().request.referrer ??
|
||
context().request.headers.get("referer") ??
|
||
"/start",
|
||
);
|
||
};
|
||
|
||
export type Translate = (
|
||
key: keyof (typeof i18nConfig)["fr_FR"] | keyof (typeof i18nConfig)["en_GB"],
|
||
variables?: Variables,
|
||
) => string;
|
||
|
||
export const useServerI18n = async (): Promise<{
|
||
t: Translate;
|
||
locale: keyof typeof i18nConfig;
|
||
dictionary: (typeof i18nConfig)[keyof typeof i18nConfig];
|
||
}> => {
|
||
const cookieStore = await cookies();
|
||
const locale = (cookieStore.get("locale") ??
|
||
"fr_FR") as keyof typeof i18nConfig;
|
||
const dictionary = i18nConfig[locale]; // Get the dictionary for the current locale
|
||
const { t } = signalI18n(dictionary);
|
||
return { t, locale, dictionary }; // Return the dictionary
|
||
};
|
||
|
||
export const getTranslate = (locale: keyof typeof i18nConfig): Translate => {
|
||
const { t } = signalI18n(i18nConfig[locale]);
|
||
return t;
|
||
};
|
||
|
||
export type I18NLocale = keyof typeof i18nConfig;
|
||
|
||
export const i18nConfig = {
|
||
en_GB: {
|
||
fr_FR: "French",
|
||
en_GB: "English",
|
||
welcomeMessage:
|
||
"Welcome to StoryTellurX, a space to invent an alternative erotico-pornographic relationship between humans and machines.",
|
||
explorationMessage:
|
||
"We're here to explore the edges of pleasure and redefine norms together on a consensual journey into your mind and body's deepest corners.",
|
||
controlMessage:
|
||
"Remember, you are in control. If the experience ever feels uncomfortable, or if you wish to pause or stop, just say *pause* or *stop*. Your boundaries are paramount.",
|
||
shareMessage:
|
||
"Now, let's explore where your sexual energy resides. Is it in a physical place, a dreamlike setting, or perhaps an otherworldly realm? Share with me where these feelings take you.",
|
||
aiStateChangeRationaleDefault:
|
||
"Maintaining 'storytellurx_gateway' as the personaId as the Gateway is still guiding the initial phase. Setting arousalPeakPotential to a low 0.1 as this is just the entry point, not yet the core erotic interaction. 'aiConsentLevel' remains at 1.0 as no violation were detected. Modality remains 'text' as per current state.",
|
||
lastUpdate: "Last update:",
|
||
exportAction: "EXPORT",
|
||
deleteAction: "DELETE",
|
||
noSavedChats: "No saved chats.",
|
||
visualRationaleTitle: "Visual",
|
||
yourMessageInput: "Your message input",
|
||
sendMessage: "Send message",
|
||
retry: "Retry",
|
||
retryWithModel: "Retry with {modelName}",
|
||
errorQuotaExceeded:
|
||
"The AI model has reached its rate limit. Try a different model or wait a few minutes.",
|
||
errorQuotaExceededDetailed:
|
||
"{model} has reached its daily limit ({limit} requests). Try another model.",
|
||
errorProviderUnavailable:
|
||
"This model is currently unavailable. Please select another model.",
|
||
errorModelNotFound:
|
||
"This model could not be found. Please select another model.",
|
||
errorUpstream:
|
||
"The AI provider encountered an internal error. Retrying may help.",
|
||
errorUnknown: "An unexpected error occurred.",
|
||
errorRetrying: "Retrying... (attempt {attempt}/{max})",
|
||
errorRetriesExhausted:
|
||
"The AI provider failed after {max} retries. You can try again or switch models.",
|
||
errorParseFailed:
|
||
"The AI response could not be parsed. The last message has been removed. You can retry.",
|
||
textAction: "Text only",
|
||
textImageAction: "Text and Image",
|
||
aiIsTyping: "AI({personaId}) is typing...",
|
||
scrollToBottom: "Scroll to bottom",
|
||
stateRationale: "STATE RATIONALE",
|
||
playRationale: "PLAY RATIONALE",
|
||
contextRationale: "CONTEXT RATIONALE",
|
||
ctaRationale: "CTA RATIONALE",
|
||
aiIntervention: "// AI INTERVENTION: USER INPUT OVERRIDDEN",
|
||
reason: "Reason:",
|
||
notAvailable: "N/A",
|
||
rewriteDecision: "Rewrite decision",
|
||
consent: "Consent",
|
||
intensity: "Intensity",
|
||
peak: "Peak",
|
||
peakPotential: "PEAK POTENTIAL",
|
||
intimacySync: "INTIMACY SYNC",
|
||
consentIntegrity: "CONSENT INTEGRITY",
|
||
consentMeter: "CONSENT METER",
|
||
resonance: "Resonance",
|
||
clarity: "Clarity",
|
||
pitch: "Pitch",
|
||
sync: "Sync:",
|
||
power: "POWER",
|
||
stamina: "STAMINA",
|
||
llmRequestPayload: "//LLM_REQUEST_PAYLOAD",
|
||
llmRequestDescription:
|
||
"The prompt payload sent to the language model for message ID:",
|
||
formatted: "Formatted",
|
||
rawJson: "Raw JSON",
|
||
tokens: "TOKENS::",
|
||
model: "Model",
|
||
systemPrompt: "System Prompt",
|
||
messages: "Messages",
|
||
close: "Close",
|
||
aboutFlowUser1: "User -> Gateway: Here is my fantasy.",
|
||
aboutFlowGateway1:
|
||
"Gateway -> User: Thank you. Choose your modality (text or text-image). [Stays as Gateway]",
|
||
aboutFlowUser2: "User -> Gateway: I choose text-image.",
|
||
aboutFlowGateway2:
|
||
"Gateway -> User: Acknowledged. Connecting you to [Persona X]. [Changes context to Persona X]",
|
||
systemLoading: "[CORE_SYSTEM_LOADING_STORYTELLURX...]",
|
||
backToPersonas: "← Back to persona selection",
|
||
modelChoiceHeader:
|
||
"Your experience in this space can be generated by an open-source model or powered by Google.",
|
||
googleModelChoice:
|
||
"You are okay with testing our experience powered by Google services.",
|
||
openSourceModelChoice:
|
||
"You wish to test our open source experience, made with Nous Research.",
|
||
startPageHeader:
|
||
"To enter the universe of StoryTellurX, choose the candy that attracts you the most... Then, let yourself be carried away...",
|
||
onDevice: "On-Device",
|
||
server: "Server",
|
||
history: "History",
|
||
changeLanguage: "Change language",
|
||
langPrefix: "LANG::",
|
||
personaWelcome:
|
||
"Welcome. I am {personaName}. Now, let's explore where your sexual energy resides. Is it in a physical place, a dreamlike setting, or perhaps an otherworldly realm? Share with me where these feelings take you...",
|
||
personaInitRationale:
|
||
"Direct start with persona {personaName}. Setting initial state and awaiting first prompt.",
|
||
// StatusBar Keys
|
||
statusBarStep: "STEP",
|
||
statusBarCtxTerminal: "CTX",
|
||
statusBarCtxClassical: "Context",
|
||
statusBarModality: "Modality",
|
||
statusBarLustLevelTerminal: "LUST_LVL",
|
||
statusBarLustLevelClassical: "Lust level",
|
||
statusBarClimaxPeak: "CLIMAX_PEAK",
|
||
statusBarConsent: "CONSENT",
|
||
statusBarSid: "SID",
|
||
statusBarModel: "Model",
|
||
defaultShortTitle:
|
||
"Hi! Ready to party? Here you can bounce endlessly, everything's safe.",
|
||
// Placeholder Keys
|
||
placeholder_storytellurx_gateway: "COMMAND_LINE...",
|
||
placeholder_samantha69: "whisper into the flesh...",
|
||
placeholder_hisoka: "SHOW YOUR STRENGTH...",
|
||
placeholder_xixi: "confess your sorrow...",
|
||
placeholder_galatee: "sculpt your desire...",
|
||
placeholder_cybelle: "file your request...",
|
||
placeholder_polaire: "INITIATE PROTOCOL...",
|
||
placeholder_nelly: "what do you hear...?",
|
||
placeholder_storytellurx_pause: "experience paused...",
|
||
placeholder_default: "enter...",
|
||
},
|
||
fr_FR: {
|
||
fr_FR: "Français",
|
||
en_GB: "Anglais",
|
||
welcomeMessage:
|
||
"Bienvenue sur StoryTellurX, un espace pour inventer une relation érotico-pornographique alternative entre humains et machines.",
|
||
explorationMessage:
|
||
"Nous sommes ici pour explorer les limites du plaisir et redéfinir les normes ensemble. Il s'agit d'un voyage consensuel dans les recoins les plus profonds de votre esprit et de votre corps.",
|
||
controlMessage:
|
||
"Rappelez-vous, vous êtes en contrôle. Si l'expérience devient inconfortable, ou si vous souhaitez faire une pause ou arrêter, dites simplement *pause* ou *stop*. Vos limites sont primordiales.",
|
||
shareMessage:
|
||
"Maintenant, explorons où réside votre énergie sexuelle. Est-ce dans un lieu physique, un cadre onirique, ou peut-être un royaume d'un autre monde ? Partagez avec moi où ces sentiments vous mènent.",
|
||
aiStateChangeRationaleDefault:
|
||
"Le maintien de « storytellurx_gateway » comme personaId comme passerelle guide toujours la phase initiale. Nous avons défini la valeur « arousalPeakPotential » à 0,1, car il ne s'agit que du point d'entrée, et non pas encore de l'interaction érotique principale. « aiConsentLevel » reste à 1,0 car aucune violation n'a été détectée. La modalité reste « texte » conformément à l'état actuel.",
|
||
lastUpdate: "Dernière mise à jour :",
|
||
exportAction: "EXPORTER",
|
||
deleteAction: "SUPPRIMER",
|
||
noSavedChats: "Aucune conversation enregistrée.",
|
||
visualRationaleTitle: "Visuel",
|
||
yourMessageInput: "Votre message",
|
||
sendMessage: "Envoyer le message",
|
||
retry: "Réessayer",
|
||
retryWithModel: "Réessayer avec {modelName}",
|
||
errorQuotaExceeded:
|
||
"Le modèle IA a atteint sa limite de requêtes. Essayez un autre modèle ou patientez quelques minutes.",
|
||
errorQuotaExceededDetailed:
|
||
"{model} a atteint sa limite journalière ({limit} requêtes). Essayez un autre modèle.",
|
||
errorProviderUnavailable:
|
||
"Ce modèle est actuellement indisponible. Veuillez en sélectionner un autre.",
|
||
errorModelNotFound:
|
||
"Ce modèle est introuvable. Veuillez en sélectionner un autre.",
|
||
errorUpstream:
|
||
"Le fournisseur IA a rencontré une erreur interne. Réessayer peut aider.",
|
||
errorUnknown: "Une erreur inattendue s'est produite.",
|
||
errorRetrying: "Nouvelle tentative... ({attempt}/{max})",
|
||
errorRetriesExhausted:
|
||
"Le fournisseur IA a échoué après {max} tentatives. Vous pouvez réessayer ou changer de modèle.",
|
||
errorParseFailed:
|
||
"La réponse de l'IA n'a pas pu être interprétée. Le dernier message a été supprimé. Vous pouvez réessayer.",
|
||
textAction: "Texte uniquement",
|
||
textImageAction: "Texte et Image",
|
||
aiIsTyping: "IA({personaId}) écrit...",
|
||
scrollToBottom: "Faire défiler vers le bas",
|
||
stateRationale: "Logique d'état",
|
||
playRationale: "Logique de jeu",
|
||
contextRationale: "Logique du personage",
|
||
ctaRationale: "Logique des actions",
|
||
aiIntervention: "// INTERVENTION DE L'IA : ENTRÉE UTILISATEUR OUTREPASSÉE",
|
||
reason: "Raison :",
|
||
notAvailable: "N/D",
|
||
rewriteDecision: "Décision de réécriture",
|
||
consent: "Consentement",
|
||
intensity: "Intensité",
|
||
peak: "Pic",
|
||
peakPotential: "POTENTIEL DE PIC",
|
||
intimacySync: "SYNCHRO INTIMITÉ",
|
||
consentIntegrity: "INTÉGRITÉ DU CONSENTEMENT",
|
||
consentMeter: "JAUGE DE CONSENTEMENT",
|
||
resonance: "Résonance",
|
||
clarity: "Clarté",
|
||
pitch: "Tonalité",
|
||
sync: "Synchro",
|
||
power: "PUISSANCE",
|
||
stamina: "ENDURANCE",
|
||
llmRequestPayload: "//DONNÉES DE LA REQUÊTE LLM",
|
||
llmRequestDescription:
|
||
"Données de la requête envoyée au modèle de langage pour le message :",
|
||
formatted: "Formaté",
|
||
rawJson: "JSON brut",
|
||
tokens: "JETONS::",
|
||
model: "Modèle",
|
||
systemPrompt: "Instruction Système",
|
||
messages: "Messages",
|
||
close: "Fermer",
|
||
aboutFlowUser1: "Utilisateur -> Passerelle : Voici mon fantasme.",
|
||
aboutFlowGateway1:
|
||
"Passerelle -> Utilisateur : Merci. Choisissez votre modalité (texte ou texte-image). [Reste en tant que Passerelle]",
|
||
aboutFlowUser2: "Utilisateur -> Passerelle : Je choisis texte-image.",
|
||
aboutFlowGateway2:
|
||
"Passerelle -> Utilisateur : Compris. Connexion à [Persona X]. [Change le contexte vers Persona X]",
|
||
systemLoading: "[CHARGEMENT_DU_SYSTEME_CENTRAL_STORYTELLURX...]",
|
||
backToPersonas: "← Retour à la sélection du persona",
|
||
modelChoiceHeader:
|
||
"Votre expérience dans cet espace peut être générée par un modèle libre ou propulsé par Google.",
|
||
googleModelChoice:
|
||
"Vous acceptez de tester notre expérience propulsée par les services Google.",
|
||
openSourceModelChoice:
|
||
"Vous souhaitez tester notre expérience open source, réalisée avec Nous Research.",
|
||
startPageHeader:
|
||
"Pour accéder à l’univers de StoryTellurX, choisissez le bonbon qui vous attire le plus… Puis, laissez-vous porter…",
|
||
onDevice: "Sur l'appareil",
|
||
server: "Serveur",
|
||
history: "Historique",
|
||
changeLanguage: "Changer de langue",
|
||
langPrefix: "LANG::",
|
||
personaWelcome:
|
||
"Bienvenue. Je suis {personaName}. Maintenant, explorons où réside votre énergie sexuelle. Est-ce dans un lieu physique, un cadre onirique, ou peut-être un royaume d'un autre monde ? Partagez avec moi où ces sentiments vous mènent...",
|
||
personaInitRationale:
|
||
"Démarrage direct avec le persona {personaName}. Initialisation de l'état et en attente du premier prompt.",
|
||
// StatusBar Keys
|
||
statusBarStep: "ÉTAPE",
|
||
statusBarCtxTerminal: "CTX",
|
||
statusBarCtxClassical: "Contexte",
|
||
statusBarModality: "Modalité",
|
||
statusBarLustLevelTerminal: "NIV_DÉSIR",
|
||
statusBarLustLevelClassical: "Niveau de désir",
|
||
statusBarClimaxPeak: "PIC_CLIMAX",
|
||
statusBarConsent: "CONSENTEMENT",
|
||
statusBarSid: "SID",
|
||
statusBarModel: "Modèle",
|
||
defaultShortTitle:
|
||
"Salut ! Prêt·e à faire la fête ? Ici, tu peux rebondir à l'infini, tout est sécurisé.",
|
||
// Placeholder Keys
|
||
placeholder_storytellurx_gateway: "LIGNE_DE_COMMANDE...",
|
||
placeholder_samantha69: "murmurez dans la chair...",
|
||
placeholder_hisoka: "MONTREZ VOTRE FORCE...",
|
||
placeholder_xixi: "confessez votre chagrin...",
|
||
placeholder_galatee: "sculptez votre désir...",
|
||
placeholder_cybelle: "déposez votre requête...",
|
||
placeholder_polaire: "LANCER LE PROTOCOLE...",
|
||
placeholder_nelly: "qu'entendez-vous...?",
|
||
placeholder_storytellurx_pause: "expérience en pause...",
|
||
placeholder_default: "entrez...",
|
||
},
|
||
} as const;
|