- Votre article de présentation a été créé avec succès. Vous pouvez maintenant publier des articles.
+
)
@@ -38,8 +39,8 @@ function ValidationError({ message }: { message: string | null }) {
return null
}
return (
-
-
{message}
+
)
}
@@ -143,13 +144,12 @@ function PresentationForm({
onSubmit={(e) => {
void handleSubmit(e)
}}
- className="border rounded-lg p-6 bg-white space-y-4"
+ className="border border-neon-cyan/20 rounded-lg p-6 bg-cyber-dark space-y-4"
>
-
Créer votre article de présentation
-
- Cet article est obligatoire pour publier sur zapwall4Science. Il contient votre présentation, la description de
- votre contenu et votre adresse Bitcoin pour le sponsoring.
+
{t('presentation.title')}
+
+ {t('presentation.description')}
diff --git a/components/LanguageSelector.tsx b/components/LanguageSelector.tsx
new file mode 100644
index 0000000..9d5fafe
--- /dev/null
+++ b/components/LanguageSelector.tsx
@@ -0,0 +1,53 @@
+import { useState, useEffect } from 'react'
+import { setLocale, getLocale, type Locale } from '@/lib/i18n'
+
+const LOCALE_STORAGE_KEY = 'zapwall-locale'
+
+export function LanguageSelector() {
+ const [currentLocale, setCurrentLocale] = useState
(getLocale())
+
+ useEffect(() => {
+ // Load saved locale from localStorage
+ const savedLocale = typeof window !== 'undefined' ? (localStorage.getItem(LOCALE_STORAGE_KEY) as Locale | null) : null
+ if (savedLocale && (savedLocale === 'fr' || savedLocale === 'en')) {
+ setLocale(savedLocale)
+ setCurrentLocale(savedLocale)
+ }
+ }, [])
+
+ const handleLocaleChange = (locale: Locale) => {
+ setLocale(locale)
+ setCurrentLocale(locale)
+ if (typeof window !== 'undefined') {
+ localStorage.setItem(LOCALE_STORAGE_KEY, locale)
+ }
+ // Force page reload to update all translations
+ window.location.reload()
+ }
+
+ return (
+
+ handleLocaleChange('fr')}
+ className={`px-2 py-1 text-xs font-medium rounded transition-colors ${
+ currentLocale === 'fr'
+ ? 'bg-neon-cyan/20 text-neon-cyan border border-neon-cyan/50'
+ : 'text-cyber-accent hover:text-neon-cyan border border-transparent hover:border-neon-cyan/30'
+ }`}
+ >
+ FR
+
+ handleLocaleChange('en')}
+ className={`px-2 py-1 text-xs font-medium rounded transition-colors ${
+ currentLocale === 'en'
+ ? 'bg-neon-cyan/20 text-neon-cyan border border-neon-cyan/50'
+ : 'text-cyber-accent hover:text-neon-cyan border border-transparent hover:border-neon-cyan/30'
+ }`}
+ >
+ EN
+
+
+ )
+}
+
diff --git a/components/PageHeader.tsx b/components/PageHeader.tsx
index e5dc7a0..5dc9406 100644
--- a/components/PageHeader.tsx
+++ b/components/PageHeader.tsx
@@ -1,5 +1,6 @@
import Link from 'next/link'
import { ConditionalPublishButton } from './ConditionalPublishButton'
+import { LanguageSelector } from './LanguageSelector'
import { t } from '@/lib/i18n'
export function PageHeader() {
@@ -10,6 +11,7 @@ export function PageHeader() {
{t('home.title')}
+
{
const load = async () => {
try {
+ // Get saved locale from localStorage or use provided locale
+ const savedLocale = typeof window !== 'undefined' ? (localStorage.getItem('zapwall-locale') as Locale | null) : null
+ const initialLocale = savedLocale && (savedLocale === 'fr' || savedLocale === 'en') ? savedLocale : locale
+
// Load translations from files in public directory
const frResponse = await fetch('/locales/fr.txt')
const enResponse = await fetch('/locales/en.txt')
@@ -22,8 +26,8 @@ export function useI18n(locale: Locale = 'fr') {
await loadTranslations('en', enText)
}
- setLocale(locale)
- setCurrentLocale(locale)
+ setLocale(initialLocale)
+ setCurrentLocale(initialLocale)
setLoaded(true)
} catch (e) {
console.error('Error loading translations:', e)
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 3d3ee76..6d53573 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -1,12 +1,28 @@
import '@/styles/globals.css'
import type { AppProps } from 'next/app'
import { useI18n } from '@/hooks/useI18n'
+import { getLocale } from '@/lib/i18n'
function I18nProvider({ children }: { children: React.ReactNode }) {
- const { loaded } = useI18n('fr') // Default to French, can be made dynamic based on user preference or browser locale
+ // Get saved locale from localStorage or default to French
+ const getInitialLocale = (): 'fr' | 'en' => {
+ if (typeof window === 'undefined') {
+ return 'fr'
+ }
+ const savedLocale = localStorage.getItem('zapwall-locale') as 'fr' | 'en' | null
+ if (savedLocale === 'fr' || savedLocale === 'en') {
+ return savedLocale
+ }
+ // Try to detect browser locale
+ const browserLocale = navigator.language.split('-')[0]
+ return browserLocale === 'en' ? 'en' : 'fr'
+ }
+
+ const initialLocale = getInitialLocale()
+ const { loaded } = useI18n(initialLocale)
if (!loaded) {
- return
Loading...
+ return
Loading...
}
return <>{children}>
diff --git a/pages/presentation.tsx b/pages/presentation.tsx
index 0f8ad7b..1bf2b7d 100644
--- a/pages/presentation.tsx
+++ b/pages/presentation.tsx
@@ -1,7 +1,8 @@
import { useEffect, useCallback } from 'react'
import { useRouter } from 'next/router'
import Head from 'next/head'
-import { ConnectButton } from '@/components/ConnectButton'
+import { PageHeader } from '@/components/PageHeader'
+import { Footer } from '@/components/Footer'
import { AuthorPresentationEditor } from '@/components/AuthorPresentationEditor'
import { useNostrConnect } from '@/hooks/useNostrConnect'
import { useAuthorPresentation } from '@/hooks/useAuthorPresentation'
@@ -36,26 +37,22 @@ function PresentationLayout() {
content={t('presentation.description')}
/>
+
-
-
-
+
+
-
{t('presentation.title')}
-
+
{t('presentation.title')}
+
{t('presentation.description')}
+
>
)