fix: escape @ in i18n messages, set useScope:'global' everywhere
Vue-i18n treats a bare '@' as the start of a linked-message reference,
which made every placeholder containing one — you{'@'}example.com,
the Telegram '{'@'}yourname' hint, the bad-Telegram error — crash the
compiler with "Invalid linked format". Escaping each '@' as the
literal {'@'} in en/es/fr fixes the compile and renders as a plain
'@' to the visitor.
Separately, every useI18n() call now passes { useScope: 'global' }.
Without it, components mounted inside <Form> / <Field> contexts
couldn't find a parent i18n scope and vue-i18n logged "Not found
parent scope. use the global scope." on every render. Explicit
global scope silences the warning and matches what the app
actually intends — there are no per-component message bundles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
00e77ecf05
commit
c65ee029dd
14 changed files with 23 additions and 23 deletions
|
|
@ -25,7 +25,7 @@ import {
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
import { submitInquiry, type ContactMethod } from '@/features/nostr/submitInquiry'
|
import { submitInquiry, type ContactMethod } from '@/features/nostr/submitInquiry'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
const METHODS: ContactMethod[] = ['email', 'whatsapp', 'signal', 'telegram', 'nostr']
|
const METHODS: ContactMethod[] = ['email', 'whatsapp', 'signal', 'telegram', 'nostr']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { Lock } from '@lucide/vue'
|
import { Lock } from '@lucide/vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from '@/components/ui/dropdown-menu'
|
} from '@/components/ui/dropdown-menu'
|
||||||
import { SUPPORTED_LOCALES, persistLocale, type LocaleCode } from '@/i18n'
|
import { SUPPORTED_LOCALES, persistLocale, type LocaleCode } from '@/i18n'
|
||||||
|
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
function set(code: LocaleCode) {
|
function set(code: LocaleCode) {
|
||||||
locale.value = code
|
locale.value = code
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
const year = new Date().getFullYear()
|
const year = new Date().getFullYear()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import {
|
||||||
import ThemeToggle from './ThemeToggle.vue'
|
import ThemeToggle from './ThemeToggle.vue'
|
||||||
import LocaleSwitcher from './LocaleSwitcher.vue'
|
import LocaleSwitcher from './LocaleSwitcher.vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
const navItems = computed(() => [
|
const navItems = computed(() => [
|
||||||
{ to: '/portfolio', label: t('nav.portfolio') },
|
{ to: '/portfolio', label: t('nav.portfolio') },
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n'
|
||||||
import { Moon, Sun } from '@lucide/vue'
|
import { Moon, Sun } from '@lucide/vue'
|
||||||
import { useTheme } from '@/composables/useTheme'
|
import { useTheme } from '@/composables/useTheme'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
const { theme, toggle } = useTheme()
|
const { theme, toggle } = useTheme()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import ProjectImage from './ProjectImage.vue'
|
||||||
import type { Project } from '@/data/projects'
|
import type { Project } from '@/data/projects'
|
||||||
|
|
||||||
const props = defineProps<{ project: Project }>()
|
const props = defineProps<{ project: Project }>()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
const hero = computed(() => props.project.images.find((img) => img.feature === 'hero'))
|
const hero = computed(() => props.project.images.find((img) => img.feature === 'hero'))
|
||||||
const rest = computed(() => props.project.images.filter((img) => img.feature !== 'hero'))
|
const rest = computed(() => props.project.images.filter((img) => img.feature !== 'hero'))
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
|
||||||
import type { ProjectImage } from '@/data/projects'
|
import type { ProjectImage } from '@/data/projects'
|
||||||
|
|
||||||
const props = defineProps<{ image: ProjectImage }>()
|
const props = defineProps<{ image: ProjectImage }>()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
"messageTooLong": "Keep it under 2000 characters.",
|
"messageTooLong": "Keep it under 2000 characters.",
|
||||||
"badEmail": "That does not look like an email address.",
|
"badEmail": "That does not look like an email address.",
|
||||||
"badPhoneOrHandle": "Use a phone number or a handle.",
|
"badPhoneOrHandle": "Use a phone number or a handle.",
|
||||||
"badTelegram": "Use a Telegram handle, like @yourname.",
|
"badTelegram": "Use a Telegram handle, like {'@'}yourname.",
|
||||||
"badNpub": "That does not look like an npub."
|
"badNpub": "That does not look like an npub."
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
|
|
@ -125,10 +125,10 @@
|
||||||
"nostr": "Nostr"
|
"nostr": "Nostr"
|
||||||
},
|
},
|
||||||
"methodPlaceholders": {
|
"methodPlaceholders": {
|
||||||
"email": "you@example.com",
|
"email": "you{'@'}example.com",
|
||||||
"whatsapp": "+1 555 123 4567",
|
"whatsapp": "+1 555 123 4567",
|
||||||
"signal": "+1 555 123 4567 or @username",
|
"signal": "+1 555 123 4567 or {'@'}username",
|
||||||
"telegram": "@yourhandle",
|
"telegram": "{'@'}yourhandle",
|
||||||
"nostr": "npub1…"
|
"nostr": "npub1…"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
"messageTooLong": "Manténgalo bajo 2000 caracteres.",
|
"messageTooLong": "Manténgalo bajo 2000 caracteres.",
|
||||||
"badEmail": "Eso no parece una dirección de correo.",
|
"badEmail": "Eso no parece una dirección de correo.",
|
||||||
"badPhoneOrHandle": "Use un número de teléfono o un usuario.",
|
"badPhoneOrHandle": "Use un número de teléfono o un usuario.",
|
||||||
"badTelegram": "Use un usuario de Telegram, como @sunombre.",
|
"badTelegram": "Use un usuario de Telegram, como {'@'}sunombre.",
|
||||||
"badNpub": "Eso no parece un npub."
|
"badNpub": "Eso no parece un npub."
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
|
|
@ -125,10 +125,10 @@
|
||||||
"nostr": "Nostr"
|
"nostr": "Nostr"
|
||||||
},
|
},
|
||||||
"methodPlaceholders": {
|
"methodPlaceholders": {
|
||||||
"email": "tu@ejemplo.com",
|
"email": "tu{'@'}ejemplo.com",
|
||||||
"whatsapp": "+34 600 123 456",
|
"whatsapp": "+34 600 123 456",
|
||||||
"signal": "+34 600 123 456 o @usuario",
|
"signal": "+34 600 123 456 o {'@'}usuario",
|
||||||
"telegram": "@suusuario",
|
"telegram": "{'@'}suusuario",
|
||||||
"nostr": "npub1…"
|
"nostr": "npub1…"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
"messageTooLong": "Moins de 2000 caractères, s'il vous plaît.",
|
"messageTooLong": "Moins de 2000 caractères, s'il vous plaît.",
|
||||||
"badEmail": "Cela ne ressemble pas à une adresse email.",
|
"badEmail": "Cela ne ressemble pas à une adresse email.",
|
||||||
"badPhoneOrHandle": "Utilisez un numéro de téléphone ou un pseudo.",
|
"badPhoneOrHandle": "Utilisez un numéro de téléphone ou un pseudo.",
|
||||||
"badTelegram": "Utilisez un pseudo Telegram, comme @votrenom.",
|
"badTelegram": "Utilisez un pseudo Telegram, comme {'@'}votrenom.",
|
||||||
"badNpub": "Cela ne ressemble pas à un npub."
|
"badNpub": "Cela ne ressemble pas à un npub."
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
|
|
@ -125,10 +125,10 @@
|
||||||
"nostr": "Nostr"
|
"nostr": "Nostr"
|
||||||
},
|
},
|
||||||
"methodPlaceholders": {
|
"methodPlaceholders": {
|
||||||
"email": "vous@exemple.com",
|
"email": "vous{'@'}exemple.com",
|
||||||
"whatsapp": "+33 6 12 34 56 78",
|
"whatsapp": "+33 6 12 34 56 78",
|
||||||
"signal": "+33 6 12 34 56 78 ou @pseudo",
|
"signal": "+33 6 12 34 56 78 ou {'@'}pseudo",
|
||||||
"telegram": "@votrepseudo",
|
"telegram": "{'@'}votrepseudo",
|
||||||
"nostr": "npub1…"
|
"nostr": "npub1…"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n'
|
||||||
import ContactForm from '@/components/contact/ContactForm.vue'
|
import ContactForm from '@/components/contact/ContactForm.vue'
|
||||||
import PrivacyBlurb from '@/components/contact/PrivacyBlurb.vue'
|
import PrivacyBlurb from '@/components/contact/PrivacyBlurb.vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { AspectRatio } from '@/components/ui/aspect-ratio'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { projects } from '@/data/projects'
|
import { projects } from '@/data/projects'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
const projectCards = computed(() =>
|
const projectCards = computed(() =>
|
||||||
projects.map((p) => ({
|
projects.map((p) => ({
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n'
|
||||||
import { AspectRatio } from '@/components/ui/aspect-ratio'
|
import { AspectRatio } from '@/components/ui/aspect-ratio'
|
||||||
import { projects } from '@/data/projects'
|
import { projects } from '@/data/projects'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n({ useScope: 'global' })
|
||||||
|
|
||||||
const projectCards = computed(() =>
|
const projectCards = computed(() =>
|
||||||
projects.map((p) => ({
|
projects.map((p) => ({
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue