feat: simplify localization handling and add resume component with translations

This commit is contained in:
2025-09-05 14:08:13 +02:00
parent 16cf818f0d
commit 7af0464a11
13 changed files with 286 additions and 30 deletions

View File

@@ -15,7 +15,7 @@ const searchTerm = ref('')
const openMessageModal = ref(false) const openMessageModal = ref(false)
const openClearModal = ref(false) const openClearModal = ref(false)
const { t, locale, locales } = useI18n() const { t, locale } = useI18n()
const { messages, submitMessage } = useChat() const { messages, submitMessage } = useChat()
const { clearMessages, messages: storeMessages } = useChatStore() const { clearMessages, messages: storeMessages } = useChatStore()
@@ -32,7 +32,6 @@ function onSelect(item: CommandPaletteItem) {
submitMessage(item.type, item.prompt, item.fetchStates ?? []) submitMessage(item.type, item.prompt, item.fetchStates ?? [])
} }
const currentLocale = computed(() => locales.value.filter(l => l.code === locale.value)[0])
const { changeLocale } = useLanguage() const { changeLocale } = useLanguage()
const { dark, toggleDark } = useTheme() const { dark, toggleDark } = useTheme()
@@ -41,7 +40,7 @@ defineShortcuts({
enter: () => openMessageModal.value = !openMessageModal.value, enter: () => openMessageModal.value = !openMessageModal.value,
meta_k: () => openMessageModal.value = !openMessageModal.value, meta_k: () => openMessageModal.value = !openMessageModal.value,
meta_d: () => openClearModal.value = !openClearModal.value, meta_d: () => openClearModal.value = !openClearModal.value,
l: () => changeLocale(currentLocale.value!.code === 'en' ? 'fr' : currentLocale.value!.code === 'fr' ? 'es' : 'en'), l: () => changeLocale(locale.value === 'en' ? 'fr' : locale.value === 'fr' ? 'es' : 'en'),
t: () => toggleDark({ clientX: window.innerWidth / 2, clientY: window.innerHeight }), t: () => toggleDark({ clientX: window.innerWidth / 2, clientY: window.innerHeight }),
}) })
@@ -279,7 +278,7 @@ function isRoute(name: string): boolean {
aria-label="Change language" aria-label="Change language"
class="cursor-pointer" class="cursor-pointer"
size="xl" size="xl"
@click.prevent="changeLocale(currentLocale!.code === 'en' ? 'fr' : currentLocale!.code === 'fr' ? 'es' : 'en')" @click.prevent="changeLocale(locale === 'en' ? 'fr' : locale === 'fr' ? 'es' : 'en')"
/> />
</UTooltip> </UTooltip>
</UFieldGroup> </UFieldGroup>

View File

@@ -8,9 +8,8 @@ const props = defineProps<{
const isArthur = computed(() => props.message.sender === ChatSender.ARTHUR) const isArthur = computed(() => props.message.sender === ChatSender.ARTHUR)
const { t, locale, locales } = useI18n() const { t, locale } = useI18n()
const currentLocale = computed(() => locales.value.find(l => l.code === locale.value)) const formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: locale.value ?? 'en' }).value)
const formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: currentLocale.value?.code ?? 'en' }).value)
</script> </script>
<template> <template>

View File

@@ -9,6 +9,7 @@ import ToolHobbies from '~/components/tool/Hobbies.vue'
import ToolLanguage from '~/components/tool/Language.vue' import ToolLanguage from '~/components/tool/Language.vue'
import ToolLocation from '~/components/tool/Location.vue' import ToolLocation from '~/components/tool/Location.vue'
import ToolProjects from '~/components/tool/Projects.vue' import ToolProjects from '~/components/tool/Projects.vue'
import ToolResume from '~/components/tool/Resume.vue'
import ToolSkills from '~/components/tool/Skills.vue' import ToolSkills from '~/components/tool/Skills.vue'
import ToolStats from '~/components/tool/Stats.vue' import ToolStats from '~/components/tool/Stats.vue'
import ToolTheme from '~/components/tool/Theme.vue' import ToolTheme from '~/components/tool/Theme.vue'
@@ -20,9 +21,8 @@ const props = defineProps<{
message: ChatMessage message: ChatMessage
}>() }>()
const { locale, locales, t } = useI18n() const { locale, t } = useI18n()
const currentLocale = computed(() => locales.value.find(l => l.code === locale.value)) const formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: locale.value ?? 'en' }).value)
const formatDate = computed(() => useDateFormat(props.message.createdAt, 'D MMMM YYYY, HH:mm', { locales: currentLocale.value?.code ?? 'en' }).value)
const componentMap: Record<ChatType, Component | undefined> = { const componentMap: Record<ChatType, Component | undefined> = {
[ChatType.INIT]: undefined, [ChatType.INIT]: undefined,
@@ -46,7 +46,7 @@ const componentMap: Record<ChatType, Component | undefined> = {
[ChatType.EXPERIENCES]: undefined, [ChatType.EXPERIENCES]: undefined,
[ChatType.STATUS]: undefined, [ChatType.STATUS]: undefined,
[ChatType.CREDITS]: ToolCredits, [ChatType.CREDITS]: ToolCredits,
[ChatType.RESUME]: undefined, [ChatType.RESUME]: ToolResume,
} }
const dynamicComponent = computed(() => componentMap[props.message.type]) const dynamicComponent = computed(() => componentMap[props.message.type])

View File

@@ -3,7 +3,7 @@ import type { UseTimeAgoMessages } from '@vueuse/core'
import type { Activity } from '~~/types' import type { Activity } from '~~/types'
import { activityMessages, IDEs } from '~~/types' import { activityMessages, IDEs } from '~~/types'
const { locale, locales, t } = useI18n() const { locale, t } = useI18n()
const { data: activity, refresh } = await useAsyncData<Activity>('activity', () => $fetch<Activity>('/api/activity')) const { data: activity, refresh } = await useAsyncData<Activity>('activity', () => $fetch<Activity>('/api/activity'))
useIntervalFn(async () => await refresh(), 5000) useIntervalFn(async () => await refresh(), 5000)
@@ -49,8 +49,7 @@ const getActivity = computed(() => {
const ago = useTimeAgo(timestamps.start, { const ago = useTimeAgo(timestamps.start, {
messages: activityMessages[locale.value as keyof typeof activityMessages] as UseTimeAgoMessages, messages: activityMessages[locale.value as keyof typeof activityMessages] as UseTimeAgoMessages,
}).value }).value
const currentLocale = computed(() => locales.value.find(l => l.code === locale.value)) const formatDate = (date: number, format: string) => useDateFormat(date, format, { locales: locale.value ?? 'en' }).value
const formatDate = (date: number, format: string) => useDateFormat(date, format, { locales: currentLocale.value?.code ?? 'en' }).value
return { return {
name, name,

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
const { locale, locales, t } = useI18n() const { locale, t } = useI18n()
const currentLocale = computed(() => locales.value.find(l => l.code === locale.value))
const { data: projects } = await useAsyncData('projects-index', async () => await queryCollection('projects').where('favorite', '=', true).select('title', 'description', 'id', 'publishedAt', 'tags', 'slug').all()) const { data: projects } = await useAsyncData('projects-index', async () => await queryCollection('projects').where('favorite', '=', true).select('title', 'description', 'id', 'publishedAt', 'tags', 'slug').all())
const date = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: currentLocale.value?.code ?? 'en' }) const date = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })
</script> </script>
<template> <template>

View File

@@ -0,0 +1,36 @@
<script lang="ts" setup>
const { t, locale } = useI18n()
const date = (date: string) => useDateFormat(new Date(date), 'D MMM YYYY - hh:mm', { locales: locale.value ?? 'en' })
</script>
<template>
<section class="space-y-4">
<p class="prose dark:prose-invert">
{{ t('tool.resume.main') }}
</p>
<div class="flex gap-4 m-1">
<UCard variant="outline" class="md:max-w-1/2 shadow-sm bg-white dark:bg-neutral-900" :ui="{ body: 'flex justify-between items-center gap-4' }">
<UCard class="rounded-sm" :ui="{ body: 'p-2 sm:p-2 flex items-center justify-center' }">
<UIcon name="i-ph-file-pdf-duotone" size="48" />
</UCard>
<div>
<p>File 1</p>
<p class="text-muted">
{{ t('tool.resume.uploaded') }} {{ date('2025-01-01') }}
</p>
</div>
</UCard>
<UCard variant="outline" class="md:max-w-1/2 shadow-sm bg-white dark:bg-neutral-900" :ui="{ body: 'flex justify-between items-center gap-4' }">
<UCard class="rounded-sm" :ui="{ body: 'p-2 sm:p-2 flex items-center justify-center' }">
<UIcon name="i-ph-file-pdf-duotone" size="48" />
</UCard>
<div>
<p>File 2</p>
<p class="text-muted">
{{ t('tool.resume.uploaded') }} {{ date('2025-01-01') }}
</p>
</div>
</UCard>
</div>
</section>
</template>

View File

@@ -3,11 +3,10 @@ import type { Stats } from '~~/types'
const { data: stats } = await useAsyncData<Stats>('stats', () => $fetch('/api/stats')) const { data: stats } = await useAsyncData<Stats>('stats', () => $fetch('/api/stats'))
const { locale, locales, t } = useI18n() const { locale, t } = useI18n()
const currentLocale = computed(() => locales.value.find(l => l.code === locale.value))
const time = useTimeAgo(new Date(stats.value!.coding.data.range.start) ?? new Date()).value.split(' ')[0] const time = useTimeAgo(new Date(stats.value!.coding.data.range.start) ?? new Date()).value.split(' ')[0]
const date = useDateFormat(new Date(stats.value!.coding.data.range.start ?? new Date()), 'DD MMMM YYYY', { locales: currentLocale.value?.code ?? 'en' }) const date = useDateFormat(new Date(stats.value!.coding.data.range.start ?? new Date()), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })
const hours = usePrecision(stats.value!.coding.data.grand_total.total_seconds_including_other_language / 3600, 0) const hours = usePrecision(stats.value!.coding.data.grand_total.total_seconds_including_other_language / 3600, 0)
</script> </script>

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
const { locale, locales, t } = useI18n() const { locale, t } = useI18n()
const currentLocale = computed(() => locales.value.find(l => l.code === locale.value))
const { data: writings } = await useAsyncData('writings-index', async () => await queryCollection('writings').order('publishedAt', 'DESC').select('title', 'description', 'id', 'publishedAt', 'tags', 'slug').limit(2).all()) const { data: writings } = await useAsyncData('writings-index', async () => await queryCollection('writings').order('publishedAt', 'DESC').select('title', 'description', 'id', 'publishedAt', 'tags', 'slug').limit(2).all())
const date = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: currentLocale.value?.code ?? 'en' }) const date = (date: string) => useDateFormat(new Date(date), 'DD MMMM YYYY', { locales: locale.value ?? 'en' })
</script> </script>
<template> <template>

View File

@@ -19,7 +19,7 @@ const { data: projects } = await useAsyncData('all-projects', () => {
:description="t('projects.description')" :description="t('projects.description')"
:title="t('projects.title')" :title="t('projects.title')"
/> />
<PostAlert /> <PostAlert class="font-bold" />
<ul class="grid grid-cols-1 sm:grid-cols-2 gap-8"> <ul class="grid grid-cols-1 sm:grid-cols-2 gap-8">
<NuxtLink <NuxtLink
v-for="(project, id) in projects" v-for="(project, id) in projects"

View File

@@ -30,7 +30,7 @@ const groupedWritings = computed(() => {
:description="t('writings.description')" :description="t('writings.description')"
:title="t('writings.title')" :title="t('writings.title')"
/> />
<PostAlert /> <PostAlert class="font-bold" />
<div class="space-y-8"> <div class="space-y-8">
<div v-for="year in groupedWritings" :key="year[0]" class="lg:space-y-6 relative"> <div v-for="year in groupedWritings" :key="year[0]" class="lg:space-y-6 relative">
<h2 class="text-4xl lg:absolute top-2 -left-16 font-bold opacity-10 select-none pointer-events-none lg:[writing-mode:vertical-rl] lg:[text-orientation:upright] pl-1 lg:pl-0"> <h2 class="text-4xl lg:absolute top-2 -left-16 font-bold opacity-10 select-none pointer-events-none lg:[writing-mode:vertical-rl] lg:[text-orientation:upright] pl-1 lg:pl-0">

View File

@@ -200,6 +200,10 @@
"thank": "Thank you for visiting my portfolio.", "thank": "Thank you for visiting my portfolio.",
"message": "Do not hesitate to leave a message, a suggestion, a remark or just a nice word - everything is welcome!", "message": "Do not hesitate to leave a message, a suggestion, a remark or just a nice word - everything is welcome!",
"tooltip": "My IA agent " "tooltip": "My IA agent "
},
"resume": {
"main": "Below are two versions of my résumé: one in French and one in English, tailored to suit different academic and professional contexts. {space} Feel free to choose the one that best fits your needs by clicking the corresponding button.",
"uploaded": "Uploaded"
} }
}, },
"error": { "error": {

View File

@@ -1,9 +1,184 @@
{ {
"projects": { "main": {
"description": "Una colección de mis proyectos realizados en R, Python o tecnologías de desarrollo web. Estos proyectos abarcan diversos campos, como análisis de datos, aprendizaje automático y aplicaciones web, mostrando mis habilidades en programación, resolución de problemas y desarrollo de proyectos.", "question": "¡Genial! Háblame un poco más sobre ti.",
"title": "Todos mis proyectos en los que he trabajado, académicos y personales" "about": "Soy estudiante de Matemáticas y Estadísticas en la Universidad Paris-Dauphine en Francia. Con una comprensión profunda de las tecnologías emergentes, estoy en el corazón de un campo en rápida expansión. Mi formación en matemáticas me da una ventaja para comprender los conceptos y teorías detrás de estas tecnologías y para diseñarlas de manera efectiva.",
"powered": "Impulsado por Nuxt"
},
"palette": {
"clear": {
"cancel": "Cancelar",
"submit": "Eliminar",
"title": "¿Estás seguro de que deseas eliminar esta conversación?",
"description": "Esta acción no se puede deshacer."
},
"cmd": {
"placeholder": "Use las flechas para navegar entre los prefacios de inmediato. Presione Entrar para enviar el mensaje.",
"send": "Enviar un nuevo mensaje",
"sending": "Enviando...",
"chat": "Regresar a Artchat"
},
"tooltip": {
"send": "Enviar un nuevo mensaje predefinido",
"clear": "Eliminar todos los mensajes",
"theme": "Cambiar el tema",
"language": "Cambiar la language",
"chat": "Vuelve a ArtChat para reanudar la conversación"
}
},
"command": {
"actions": "Componentes",
"arthur": "Más sobre Arthur",
"interface": "Cambiar la interfaz",
"theme": {
"label": "Cambiar tema",
"prompt": "¿Cómo puedo cambiar el tema?"
},
"stats": {
"label": "Estadística de desarrollo",
"prompt": "¿Cómo puedo ver las estadísticas sobre Arthur?"
},
"weather": {
"label": "Clima",
"prompt": "¿Cómo puedo ver las condiciones climáticas cerca de Arthur?"
},
"location": {
"label": "Ubicación",
"prompt": "¿Cómo puedo ver la ubicación de Arthur?"
},
"language": {
"label": "Cambiar idioma",
"prompt": "¿Cómo puedo cambiar el idioma del chat?"
},
"activity": {
"label": "Actividad",
"prompt": "¿Qué estás haciendo actualmente?"
},
"about": {
"label": "Sobre Arthur",
"prompt": "Quiero que me hables de ti."
},
"projects": {
"label": "Proyectos",
"prompt": "Háblame de tus proyectos."
},
"writings": {
"label": "Escritos",
"prompt": "¿Cuáles son tus últimos artículos?"
},
"experiences": {
"label": "Experiencias",
"prompt": "¿Qué experiencias tienes en tu carrera?"
},
"skills": {
"label": "Habilidades",
"prompt": "¿Cuáles son tus habilidades?"
},
"status": {
"label": "Estado del homelab",
"prompt": "Vi que tienes un homelab, ¿está funcionando actualmente?"
},
"resume": {
"label": "CV",
"prompt": "¿Puedes enviarme tu CV?"
},
"contact": {
"label": "Contacto",
"prompt": "¿Cómo puedo contactarte?"
},
"hobbies": {
"label": "Pasatiempos y pasiones",
"prompt": "¿Cuáles son tus pasatiempos? ¿Tus pasiones? ¿Tus intereses?"
},
"credits": {
"label": "Créditos",
"prompt": "¿Cómo se ha hecho este chat?"
},
"hardware": {
"label": "Hardware",
"prompt": "¿Cómo puedo ver la configuración de hardware de Arthur?"
},
"homelab": {
"label": "Cosas de Homelab",
"prompt": "¿Cómo puedo ver el Homelab de Arthur?"
},
"ide": {
"label": "IDEs y fuente",
"prompt": "¿Cuéntame más sobre el IDE y la fuente que usa Arthur?"
},
"software": {
"label": "Softwares y aplicaciones",
"prompt": "¿Qué software y aplicaciones usa Arthur?"
}
},
"chat": {
"state": {
"thinking": "Pensando...",
"fetching": "Obteniendo los datos...",
"generating": "Generando el componente...",
"done": "¡Hecho!",
"checking": "Comprobación del currículum ..."
},
"welcome": "Bienvenido en Artchat",
"ask": "Pregúntame cualquier cosa sobre Arthur Danjou"
}, },
"tool": { "tool": {
"language": {
"change": "Cambiar idioma",
"response": {
"control": "He añadido el control de cambio de idioma arriba para que puedas cambiar directamente.",
"choose": "Elige Inglés, Francés o Español",
"kbd": "Presiona {kbd} en tu teclado"
}
},
"activity": {
"offline": "Ahora mismo estoy desconectado. Vuelve más tarde para ver en lo que estoy trabajando. {maths}",
"working": "Estoy trabajando en línea. ¡Mira lo que estoy haciendo justo debajo!",
"idling": "Estoy en reposo en mi ordenador con {editor} en segundo plano.",
"maths": "Estoy probablemente haciendo matemáticas o durmiendo.",
"response": "Las estadísticas son propulsadas y registradas por WakaTime.",
"tooltip": {
"online": "Estoy conectado 👋",
"offline": "Estoy desconectado 🫥",
"idling": "Estoy durmiendo 😴"
},
"started": "Comenzó {ago}, el {date} en {hour}",
"secret": "Proyecto Secreto"
},
"location": "Actualmente estoy basado en {location}. Consulta más detalles a continuación.",
"contact": "Existen diferentes formas de contactarme. Aquí hay una lista:",
"duplicated": {
"title": "⚠️ He detectado mensajes duplicados",
"description": "Por lo tanto, eliminé los mensajes duplicados más antiguos para aligerar la aplicación."
},
"stats": {
"main": "Recopilo datos desde hace {time} años, empecé el {date}. He programado durante un total de {hours} horas.",
"editors": "Mis mejores editores son {editors}.",
"os": "Mi mejor OS es {os}.",
"languages": "Mis lenguajes favoritos son {languages}.",
"separator": " y ",
"tooltip": {
"date": "hace tato tiempo…🫣",
"hours": "es mucho 😮"
}
},
"theme": {
"switch": "Cambiar tema",
"light": "Claro",
"dark": "Oscuro",
"response": {
"control": "He añadido el control de alternancia de tema arriba para que puedas cambiar directamente.",
"choose": "Elige Claro (icono de sol) u Oscuro (icono de luna)",
"kbd": "Presiona {kbd} en tu teclado"
}
},
"weather": {
"main": "Tiempo",
"powered_by": "Impulsado por OpenWeatherMap",
"low": "Bajo",
"high": "Alto",
"humidity": "Humedad",
"wind": "Viento"
},
"credits": { "credits": {
"made": "Este sitio fue diseñado con {nuxt} y luego se implementó a través de {vercel}", "made": "Este sitio fue diseñado con {nuxt} y luego se implementó a través de {vercel}",
"heart": "Hecho con ❤️ y mucho reflexión.", "heart": "Hecho con ❤️ y mucho reflexión.",
@@ -12,6 +187,49 @@
"thank": "Gracias por visitar mi cartera.", "thank": "Gracias por visitar mi cartera.",
"message": "No dude en dejar un mensaje, una sugerencia, un comentario o simplemente una buena palabra: ¡todo es bienvenido!", "message": "No dude en dejar un mensaje, una sugerencia, un comentario o simplemente una buena palabra: ¡todo es bienvenido!",
"tooltip": "Mi agente ia" "tooltip": "Mi agente ia"
} },
"resume": {
"main": "A continuación encontrarás dos versiones de mi currículum: una en francés y otra en inglés, pensadas para distintos contextos académicos y profesionales. {space} Elige la que mejor se adapte a tus necesidades haciendo clic en el botón correspondiente.",
"uploaded": "Cargado"
},
"projects": {
"main": "Una selección de mis proyectos realizados en R, Python y desarrollo web, cubriendo áreas variadas como análisis de datos, aprendizaje automático y aplicaciones web. Estos proyectos destacan mis habilidades en codificación, resolución de problemas y diseño de soluciones. {space} Aquí, presento los tres proyectos de los que estoy más orgulloso, pero puedes descubrir todos mis proyectos directamente en la página de {projects}.",
"link": "Proyectos"
},
"writings": {
"main": "Comparto mis reflexiones sobre matemáticas, inteligencia artificial, desarrollo y mis pasiones, organizándolas para seguir la evolución de mis ideas y proyectos. {space}Aquí, encontrarás los dos artículos más recientes, pero todos mis escritos son accesibles en la página de {writings}, para explorar más a fondo mi universo entre código, matemáticas e IA.",
"link": "Artículos"
},
"hobbies": "Además de la programación y mis proyectos técnicos, dedico una gran parte de mi tiempo libre a mis pasiones: deporte, música, viajes y momentos compartidos con amigos. El deporte me trae rigor y perseverancia, la música estimula mi creatividad y el viaje me abre a otras culturas, a otras formas de pensar, lo que también nutre mi curiosidad intelectual. Estas pasiones me ayudan a mantener un buen equilibrio y fortalecer las cualidades que movilizo en mis estudios y en mi carrera: curiosidad, compromiso, autonomía y voluntad constante para progresar. Me hacen alguien motivado, adaptable y siempre listo para asumir nuevos desafíos."
},
"error": {
"main": "Creo que estás perdido, volvamos a la",
"redirect": "página de inicio"
},
"skills": {
"main": "Como ingeniero de software y estudiante de matemáticas, combino el rigor científico con el pragmatismo técnico para diseñar soluciones adaptadas a los desafíos de proyectos de datos y matemáticos. Mi enfoque se centra en una comprensión profunda de las necesidades, desde la preparación de datos hasta su implementación, pasando por la modelización y la optimización del rendimiento.Apasionado por la inteligencia artificial y la ciencia de datos, me esfuerzo por equilibrar la innovación con la robustez estadística. Siempre en busca de aprendizaje, exploro tanto los avances tecnológicos como los retos emprendedores o financieros. Curioso y entusiasta, disfruto compartiendo conocimientos y descubriendo nuevos conceptos, ya sean teoremas o tecnologías emergentes."
},
"post": {
"footer": {
"thanks": "¡Gracias por leer! Me llamo {name} y me encanta escribir sobre inteligencia artificial, ciencia de datos y todo lo que se encuentra en la intersección entre las matemáticas y la programación. {jump} Llevo años programando y explorando las matemáticas, y cada día aprendo algo nuevo — ya sea autoalojando herramientas en mi homelab, experimentando con modelos de aprendizaje automático o profundizando en métodos estadísticos. {jump} Comparto mis conocimientos aquí porque sé lo valiosos que pueden ser los recursos claros, prácticos y accesibles, especialmente cuando uno está empezando o explorando temas técnicos en profundidad. {jump} Si tienes alguna pregunta o simplemente quieres charlar, no dudes en dejar un comentario abajo o contactarme por {linkedin} o {github}. {jump} Espero que este artículo te haya gustado y que hayas aprendido algo útil. Si es así, {comment} — ¡me ayuda mucho y significa mucho para mí!",
"comment": "considera compartirlo"
},
"link": {
"copied": "Link copiado",
"copy": "Copiar link"
},
"top": "Ir arriba"
},
"alert": "Por falta de tiempo, no tuve tiempo para traducir este contenido al francés. Gracias por su comprensión.",
"canva": {
"title": "Cargando el lienzo ..."
},
"writings": {
"description": "Todas mis reflexiones sobre programación, matemáticas, la concepción de la inteligencia artificial, etc., están organizadas en orden cronológico.",
"title": "Escritos sobre matemáticas, inteligencia artificial, desarrollo y mis pasiones."
},
"projects": {
"description": "Una colección de mis proyectos utilizando R, Python o tecnologías de desarrollo web. Estos proyectos abarcan varios dominios, incluyendo análisis de datos, aprendizaje automático y aplicaciones web, mostrando mis habilidades en codificación, resolución de problemas y desarrollo de proyectos.",
"title": "Todos mis proyectos en los que he trabajado, tanto académicos como personales"
} }
} }

View File

@@ -198,8 +198,12 @@
"chat": "Un grand merci à {chat}, mon assistant personnel, toujours prêt à répondre à vos questions avec clarté et rapidité.", "chat": "Un grand merci à {chat}, mon assistant personnel, toujours prêt à répondre à vos questions avec clarté et rapidité.",
"copyrights": "© {year} Arthur DANJOU — Tous droits réservés.", "copyrights": "© {year} Arthur DANJOU — Tous droits réservés.",
"thank": "Merci de visiter mon portfolio.", "thank": "Merci de visiter mon portfolio.",
"message": "Nhésitez pas à laisser un message, une suggestion, une remarque ou simplement un mot sympa — tout est le bienvenu!", "message": "N'hésitez pas à laisser un message, une suggestion, une remarque ou simplement un mot sympa — tout est le bienvenu!",
"tooltip": "Mon agent IA" "tooltip": "Mon agent IA"
},
"resume": {
"main": "Vous trouverez ci-dessous deux versions de mon CV : une en français et une en anglais, afin de répondre aux différents contextes académiques et professionnels.{space} Choisissez celle qui vous convient le mieux en cliquant sur le bouton correspondant.",
"uploaded": "Uploadé"
} }
}, },
"error": { "error": {