feat(i18n): add Italian and Spanish support to configure API fallback briefs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -156,9 +156,12 @@ When site analysis data is provided in the context, you MUST include a dedicated
|
||||
- Explain how the proposed solution addresses each issue found
|
||||
This section demonstrates that LetsBe has already begun analyzing the client's situation before the first call. Never invent data not present in the context — only reference what the analysis actually returned.`;
|
||||
|
||||
const langInstruction = body.locale === 'fr'
|
||||
? '\n\nIMPORTANT: Write the entire brief in French. All headings, body text, and next steps must be in French.'
|
||||
: '';
|
||||
const langInstructions: Record<string, string> = {
|
||||
fr: '\n\nIMPORTANT: Write the entire brief in French. All headings, body text, and next steps must be in French.',
|
||||
it: '\n\nIMPORTANT: Write the entire brief in Italian. All headings, body text, and next steps must be in Italian.',
|
||||
es: '\n\nIMPORTANT: Write the entire brief in Spanish. All headings, body text, and next steps must be in Spanish.',
|
||||
};
|
||||
const langInstruction = langInstructions[body.locale ?? ''] ?? '';
|
||||
|
||||
const userPrompt = `Generate a personalized project brief for the following prospect. The brief should:
|
||||
1. Address the client by first name (${displayName})
|
||||
@@ -221,7 +224,7 @@ ${context}`;
|
||||
|
||||
function generateFallbackBrief(body: ConfigureRequestBody): string {
|
||||
const { services, aiEnabled, aiTypes, industry, scope, timeline, name, company } = body;
|
||||
const isFr = body.locale === 'fr';
|
||||
const locale = body.locale ?? 'en';
|
||||
|
||||
const SERVICE_NAMES_FR: Record<string, string> = {
|
||||
web: 'Design & Développement Web',
|
||||
@@ -246,70 +249,170 @@ function generateFallbackBrief(body: ConfigureRequestBody): string {
|
||||
exploring: 'en phase d\'exploration',
|
||||
};
|
||||
|
||||
const svcNames = isFr ? SERVICE_NAMES_FR : SERVICE_NAMES;
|
||||
const indNames = isFr ? INDUSTRY_NAMES_FR : INDUSTRY_NAMES;
|
||||
const tlNames = isFr ? TIMELINE_NAMES_FR : TIMELINE_NAMES;
|
||||
const SERVICE_NAMES_IT: Record<string, string> = {
|
||||
web: 'Web Design & Sviluppo',
|
||||
systems: 'Software Su Misura',
|
||||
infrastructure: 'Infrastruttura Privata',
|
||||
};
|
||||
|
||||
const INDUSTRY_NAMES_IT: Record<string, string> = {
|
||||
maritime: 'Marittimo & Nautica', hospitality: 'Ospitalità', technology: 'Tecnologia',
|
||||
realestate: 'Immobiliare', finance: 'Finanza', ngo: 'ONG & No-Profit', other: 'Altro',
|
||||
};
|
||||
|
||||
const TIMELINE_NAMES_IT: Record<string, string> = {
|
||||
asap: 'il prima possibile', '1-3months': '1–3 mesi', '3-6months': '3–6 mesi', exploring: 'in fase di esplorazione',
|
||||
};
|
||||
|
||||
const SERVICE_NAMES_ES: Record<string, string> = {
|
||||
web: 'Diseño & Desarrollo Web',
|
||||
systems: 'Software a Medida',
|
||||
infrastructure: 'Infraestructura Privada',
|
||||
};
|
||||
|
||||
const INDUSTRY_NAMES_ES: Record<string, string> = {
|
||||
maritime: 'Marítimo & Náutico', hospitality: 'Hostelería', technology: 'Tecnología',
|
||||
realestate: 'Inmobiliario', finance: 'Finanzas', ngo: 'ONG & Sin Ánimo de Lucro', other: 'Otro',
|
||||
};
|
||||
|
||||
const TIMELINE_NAMES_ES: Record<string, string> = {
|
||||
asap: 'lo antes posible', '1-3months': '1–3 meses', '3-6months': '3–6 meses', exploring: 'en fase de exploración',
|
||||
};
|
||||
|
||||
const svcMap: Record<string, Record<string, string>> = { fr: SERVICE_NAMES_FR, it: SERVICE_NAMES_IT, es: SERVICE_NAMES_ES };
|
||||
const indMap: Record<string, Record<string, string>> = { fr: INDUSTRY_NAMES_FR, it: INDUSTRY_NAMES_IT, es: INDUSTRY_NAMES_ES };
|
||||
const tlMap: Record<string, Record<string, string>> = { fr: TIMELINE_NAMES_FR, it: TIMELINE_NAMES_IT, es: TIMELINE_NAMES_ES };
|
||||
const svcNames = svcMap[locale] ?? SERVICE_NAMES;
|
||||
const indNames = indMap[locale] ?? INDUSTRY_NAMES;
|
||||
const tlNames = tlMap[locale] ?? TIMELINE_NAMES;
|
||||
|
||||
const serviceNames = services.map((s) => svcNames[s] ?? s);
|
||||
const joiner = isFr ? ' et ' : ' and ';
|
||||
const servicesList = serviceNames.length <= 2
|
||||
? serviceNames.join(joiner)
|
||||
: `${serviceNames.slice(0, -1).join(', ')}${isFr ? ' et ' : ', and '}${serviceNames[serviceNames.length - 1]}`;
|
||||
const industryLabel = industry ? indNames[industry] ?? industry : (isFr ? 'votre secteur' : 'your industry');
|
||||
const displayCompany = company.trim() || (isFr ? 'votre organisation' : 'your organization');
|
||||
const displayName = name.split(' ')[0] || (isFr ? 'bonjour' : 'there');
|
||||
|
||||
const joiners: Record<string, { and: string; commaAnd: string }> = {
|
||||
fr: { and: ' et ', commaAnd: ' et ' },
|
||||
it: { and: ' e ', commaAnd: ' e ' },
|
||||
es: { and: ' y ', commaAnd: ' y ' },
|
||||
};
|
||||
const j = joiners[locale] ?? { and: ' and ', commaAnd: ', and ' };
|
||||
|
||||
const servicesList = serviceNames.length <= 2
|
||||
? serviceNames.join(j.and)
|
||||
: `${serviceNames.slice(0, -1).join(', ')}${j.commaAnd}${serviceNames[serviceNames.length - 1]}`;
|
||||
|
||||
const industryFallbacks: Record<string, string> = { fr: 'votre secteur', it: 'il tuo settore', es: 'tu sector' };
|
||||
const companyFallbacks: Record<string, string> = { fr: 'votre organisation', it: 'la tua organizzazione', es: 'tu organización' };
|
||||
const nameFallbacks: Record<string, string> = { fr: 'bonjour', it: 'ciao', es: 'hola' };
|
||||
|
||||
const industryLabel = industry ? indNames[industry] ?? industry : (industryFallbacks[locale] ?? 'your industry');
|
||||
const displayCompany = company.trim() || (companyFallbacks[locale] ?? 'your organization');
|
||||
const displayName = name.split(' ')[0] || (nameFallbacks[locale] ?? 'there');
|
||||
|
||||
const timelineFallbacks: Record<string, string> = {
|
||||
fr: 'un calendrier à convenir', it: 'un calendario da definire', es: 'un calendario a convenir',
|
||||
};
|
||||
const timelineStr = timeline
|
||||
? tlNames[timeline]?.toLowerCase() ?? (isFr ? 'un calendrier à convenir' : 'a timeline to be agreed upon')
|
||||
: (isFr ? 'un calendrier à convenir' : 'a timeline to be agreed upon');
|
||||
? tlNames[timeline]?.toLowerCase() ?? (timelineFallbacks[locale] ?? 'a timeline to be agreed upon')
|
||||
: (timelineFallbacks[locale] ?? 'a timeline to be agreed upon');
|
||||
|
||||
const hasWeb = services.includes('web');
|
||||
const hasSystems = services.includes('systems');
|
||||
const hasInfra = services.includes('infrastructure');
|
||||
|
||||
let sections = '';
|
||||
// Build sections per locale
|
||||
const sectionTemplates: Record<string, () => string> = {
|
||||
fr: () => {
|
||||
let s = '';
|
||||
if (hasWeb) {
|
||||
s += `\n**Design & Développement Web**\nNous concevrons et développerons un site web sur mesure pour ${displayCompany} — sans templates, sans constructeurs de pages. Moderne, responsive, rapide et optimisé pour le référencement dès le premier jour.\n`;
|
||||
}
|
||||
if (hasSystems) {
|
||||
s += `\n**Logiciels Sur Mesure**\nNous développerons un système conçu pour correspondre exactement au fonctionnement de ${displayCompany} — modèle de données personnalisé, accès par rôles et intégrations avec vos outils existants.\n`;
|
||||
}
|
||||
if (hasInfra) {
|
||||
s += `\n**Infrastructure Privée**\nNous mettrons en place un environnement serveur dédié pour ${displayCompany} avec email, stockage cloud et outils métier que vous possédez et contrôlez entièrement.\n`;
|
||||
}
|
||||
if (aiEnabled && aiTypes.length > 0) {
|
||||
const aiLabels = aiTypes.map((t) => AI_TYPE_NAMES[t] ?? t).join(', ');
|
||||
s += `\n**Intégration IA**\nNous intégrerons ${aiLabels.toLowerCase()} dans vos systèmes — en profondeur, pas en surface. L'approche exacte sera définie lors de la phase de découverte.\n`;
|
||||
} else if (aiEnabled) {
|
||||
s += `\n**Intégration IA**\nNous intégrerons l'IA dans vos systèmes — en profondeur, pas en surface. L'approche exacte sera définie lors de la phase de découverte.\n`;
|
||||
}
|
||||
if (scope?.trim()) {
|
||||
s += `\n**Vos Objectifs**\nVous avez partagé : "${scope.trim()}" — nous orienterons nos sessions de découverte autour de ces priorités.\n`;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
it: () => {
|
||||
let s = '';
|
||||
if (hasWeb) {
|
||||
s += `\n**Web Design & Sviluppo**\nProgetteremo e svilupperemo un sito web su misura per ${displayCompany} da zero — nessun template, nessun page builder. Moderno, responsive, veloce e ottimizzato per i motori di ricerca fin dal primo giorno.\n`;
|
||||
}
|
||||
if (hasSystems) {
|
||||
s += `\n**Software Su Misura**\nRealizzaremo un sistema progettato su misura per come opera ${displayCompany} — modello dati personalizzato, accesso basato sui ruoli e integrazioni con i tuoi strumenti esistenti.\n`;
|
||||
}
|
||||
if (hasInfra) {
|
||||
s += `\n**Infrastruttura Privata**\nConfigureremo un ambiente server dedicato per ${displayCompany} con email, cloud storage e strumenti aziendali che possiedi e controlli interamente.\n`;
|
||||
}
|
||||
if (aiEnabled && aiTypes.length > 0) {
|
||||
const aiLabels = aiTypes.map((t) => AI_TYPE_NAMES[t] ?? t).join(', ');
|
||||
s += `\n**Integrazione IA**\nIntegreremo ${aiLabels.toLowerCase()} nei tuoi sistemi — in profondità, non in superficie. L'approccio esatto sarà definito durante la fase di scoperta.\n`;
|
||||
} else if (aiEnabled) {
|
||||
s += `\n**Integrazione IA**\nIntegreremo l'IA nei tuoi sistemi — in profondità, non in superficie. L'approccio esatto sarà definito durante la fase di scoperta.\n`;
|
||||
}
|
||||
if (scope?.trim()) {
|
||||
s += `\n**I Tuoi Obiettivi**\nHai condiviso: "${scope.trim()}" — struttureremo le nostre sessioni di scoperta attorno a queste priorità.\n`;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
es: () => {
|
||||
let s = '';
|
||||
if (hasWeb) {
|
||||
s += `\n**Diseño & Desarrollo Web**\nDiseñaremos y desarrollaremos un sitio web a medida para ${displayCompany} desde cero — sin plantillas, sin constructores de páginas. Moderno, responsive, rápido y optimizado para motores de búsqueda desde el primer día.\n`;
|
||||
}
|
||||
if (hasSystems) {
|
||||
s += `\n**Software a Medida**\nDesarrollaremos un sistema diseñado específicamente para cómo opera ${displayCompany} — modelo de datos personalizado, acceso basado en roles e integraciones con tus herramientas existentes.\n`;
|
||||
}
|
||||
if (hasInfra) {
|
||||
s += `\n**Infraestructura Privada**\nConfiguraremos un entorno de servidor dedicado para ${displayCompany} con correo electrónico, almacenamiento en la nube y herramientas empresariales que posees y controlas completamente.\n`;
|
||||
}
|
||||
if (aiEnabled && aiTypes.length > 0) {
|
||||
const aiLabels = aiTypes.map((t) => AI_TYPE_NAMES[t] ?? t).join(', ');
|
||||
s += `\n**Integración IA**\nIntegraremos ${aiLabels.toLowerCase()} en tus sistemas — de forma profunda, no superficial. El enfoque exacto se definirá durante la fase de descubrimiento.\n`;
|
||||
} else if (aiEnabled) {
|
||||
s += `\n**Integración IA**\nIntegraremos la IA en tus sistemas — de forma profunda, no superficial. El enfoque exacto se definirá durante la fase de descubrimiento.\n`;
|
||||
}
|
||||
if (scope?.trim()) {
|
||||
s += `\n**Tus Objetivos**\nCompartiste: "${scope.trim()}" — orientaremos nuestras sesiones de descubrimiento en torno a estas prioridades.\n`;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
en: () => {
|
||||
let s = '';
|
||||
if (hasWeb) {
|
||||
s += `\n**Web Design & Development**\nWe'll design and build a custom website for ${displayCompany} from scratch — no templates, no page builders. Modern, responsive, fast, and optimized for search engines from day one.\n`;
|
||||
}
|
||||
if (hasSystems) {
|
||||
s += `\n**Custom Software**\nWe'll build a purpose-made system tailored to how ${displayCompany} actually operates — custom data model, role-based access, and integrations with your existing tools.\n`;
|
||||
}
|
||||
if (hasInfra) {
|
||||
s += `\n**Private Infrastructure**\nWe'll set up a dedicated server environment for ${displayCompany} with email, cloud storage, and business tools that you fully own and control.\n`;
|
||||
}
|
||||
if (aiEnabled && aiTypes.length > 0) {
|
||||
const aiLabels = aiTypes.map((t) => AI_TYPE_NAMES[t] ?? t).join(', ');
|
||||
s += `\n**AI Integration**\nWe'll layer ${aiLabels.toLowerCase()} into your systems — deeply integrated, not bolted on. The exact approach will be scoped during discovery.\n`;
|
||||
} else if (aiEnabled) {
|
||||
s += `\n**AI Integration**\nWe'll layer AI integration into your systems — deeply integrated, not bolted on. The exact approach will be scoped during discovery.\n`;
|
||||
}
|
||||
if (scope?.trim()) {
|
||||
s += `\n**Your Goals**\nYou shared: "${scope.trim()}" — we'll frame our discovery sessions around these priorities.\n`;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
};
|
||||
|
||||
if (isFr) {
|
||||
if (hasWeb) {
|
||||
sections += `\n**Design & Développement Web**\nNous concevrons et développerons un site web sur mesure pour ${displayCompany} — sans templates, sans constructeurs de pages. Moderne, responsive, rapide et optimisé pour le référencement dès le premier jour.\n`;
|
||||
}
|
||||
if (hasSystems) {
|
||||
sections += `\n**Logiciels Sur Mesure**\nNous développerons un système conçu pour correspondre exactement au fonctionnement de ${displayCompany} — modèle de données personnalisé, accès par rôles et intégrations avec vos outils existants.\n`;
|
||||
}
|
||||
if (hasInfra) {
|
||||
sections += `\n**Infrastructure Privée**\nNous mettrons en place un environnement serveur dédié pour ${displayCompany} avec email, stockage cloud et outils métier que vous possédez et contrôlez entièrement.\n`;
|
||||
}
|
||||
if (aiEnabled && aiTypes.length > 0) {
|
||||
const aiLabels = aiTypes.map((t) => AI_TYPE_NAMES[t] ?? t).join(', ');
|
||||
sections += `\n**Intégration IA**\nNous intégrerons ${aiLabels.toLowerCase()} dans vos systèmes — en profondeur, pas en surface. L'approche exacte sera définie lors de la phase de découverte.\n`;
|
||||
} else if (aiEnabled) {
|
||||
sections += `\n**Intégration IA**\nNous intégrerons l'IA dans vos systèmes — en profondeur, pas en surface. L'approche exacte sera définie lors de la phase de découverte.\n`;
|
||||
}
|
||||
if (scope?.trim()) {
|
||||
sections += `\n**Vos Objectifs**\nVous avez partagé : "${scope.trim()}" — nous orienterons nos sessions de découverte autour de ces priorités.\n`;
|
||||
}
|
||||
} else {
|
||||
if (hasWeb) {
|
||||
sections += `\n**Web Design & Development**\nWe'll design and build a custom website for ${displayCompany} from scratch — no templates, no page builders. Modern, responsive, fast, and optimized for search engines from day one.\n`;
|
||||
}
|
||||
if (hasSystems) {
|
||||
sections += `\n**Custom Software**\nWe'll build a purpose-made system tailored to how ${displayCompany} actually operates — custom data model, role-based access, and integrations with your existing tools.\n`;
|
||||
}
|
||||
if (hasInfra) {
|
||||
sections += `\n**Private Infrastructure**\nWe'll set up a dedicated server environment for ${displayCompany} with email, cloud storage, and business tools that you fully own and control.\n`;
|
||||
}
|
||||
if (aiEnabled && aiTypes.length > 0) {
|
||||
const aiLabels = aiTypes.map((t) => AI_TYPE_NAMES[t] ?? t).join(', ');
|
||||
sections += `\n**AI Integration**\nWe'll layer ${aiLabels.toLowerCase()} into your systems — deeply integrated, not bolted on. The exact approach will be scoped during discovery.\n`;
|
||||
} else if (aiEnabled) {
|
||||
sections += `\n**AI Integration**\nWe'll layer AI integration into your systems — deeply integrated, not bolted on. The exact approach will be scoped during discovery.\n`;
|
||||
}
|
||||
if (scope?.trim()) {
|
||||
sections += `\n**Your Goals**\nYou shared: "${scope.trim()}" — we'll frame our discovery sessions around these priorities.\n`;
|
||||
}
|
||||
}
|
||||
const sections = (sectionTemplates[locale] ?? sectionTemplates['en'])();
|
||||
|
||||
if (isFr) {
|
||||
if (locale === 'fr') {
|
||||
return `**Brief Projet pour ${displayCompany}**
|
||||
Préparé pour : ${name}
|
||||
Date : ${new Date().toLocaleDateString('fr-FR', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
@@ -341,6 +444,70 @@ Au plaisir de construire quelque chose de formidable ensemble.
|
||||
— L'équipe LetsBe`;
|
||||
}
|
||||
|
||||
if (locale === 'it') {
|
||||
return `**Brief Progetto per ${displayCompany}**
|
||||
Preparato per: ${name}
|
||||
Data: ${new Date().toLocaleDateString('it-IT', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
|
||||
---
|
||||
|
||||
**Panoramica**
|
||||
|
||||
Ciao ${displayName}, in base al tuo interesse per ${servicesList} nel settore ${industryLabel}, ecco un brief preliminare per guidare la nostra prima conversazione.
|
||||
|
||||
Affronteremo questo come un progetto unificato — ogni componente che lavora insieme, interamente di tua proprietà e sotto il tuo controllo.
|
||||
${sections}
|
||||
**Il Nostro Approccio**
|
||||
|
||||
Iniziamo con una fase di Scoperta (2–3 sessioni) per comprendere le tue esigenze prima di scrivere una sola riga di codice.
|
||||
|
||||
**Tempistiche**
|
||||
|
||||
Consegna prevista: ${timelineStr}. Una roadmap dettagliata seguirà la fase di Scoperta.
|
||||
|
||||
**Prossimi Passi**
|
||||
|
||||
1. Prenota una chiamata introduttiva gratuita di 30 minuti
|
||||
2. Ti invieremo un documento di scoping dettagliato entro 48 ore
|
||||
3. La Scoperta inizia — senza impegno
|
||||
|
||||
Non vediamo l'ora di costruire qualcosa di straordinario insieme.
|
||||
|
||||
— Il Team LetsBe`;
|
||||
}
|
||||
|
||||
if (locale === 'es') {
|
||||
return `**Brief de Proyecto para ${displayCompany}**
|
||||
Preparado para: ${name}
|
||||
Fecha: ${new Date().toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
|
||||
---
|
||||
|
||||
**Resumen**
|
||||
|
||||
Hola ${displayName}, basándonos en tu interés en ${servicesList} para el sector ${industryLabel}, aquí tienes un brief preliminar para guiar nuestra primera conversación.
|
||||
|
||||
Abordaremos esto como un proyecto unificado — cada componente trabajando en conjunto, totalmente de tu propiedad y bajo tu control.
|
||||
${sections}
|
||||
**Nuestro Enfoque**
|
||||
|
||||
Comenzamos con una fase de Descubrimiento (2–3 sesiones) para comprender tus requisitos antes de escribir cualquier línea de código.
|
||||
|
||||
**Plazo**
|
||||
|
||||
Entrega objetivo: ${timelineStr}. Una hoja de ruta detallada seguirá a la fase de Descubrimiento.
|
||||
|
||||
**Próximos Pasos**
|
||||
|
||||
1. Reserva una llamada introductoria gratuita de 30 minutos
|
||||
2. Te enviaremos un documento de alcance detallado en 48 horas
|
||||
3. El Descubrimiento comienza — sin compromiso
|
||||
|
||||
Con ganas de construir algo extraordinario juntos.
|
||||
|
||||
— El Equipo LetsBe`;
|
||||
}
|
||||
|
||||
return `**Project Brief for ${displayCompany}**
|
||||
Prepared for: ${name}
|
||||
Date: ${new Date().toLocaleDateString('en-GB', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
|
||||
Reference in New Issue
Block a user