diff --git a/components/ArticleField.tsx b/components/ArticleField.tsx index b28bafb..8fa5873 100644 --- a/components/ArticleField.tsx +++ b/components/ArticleField.tsx @@ -86,7 +86,7 @@ export function ArticleField(props: ArticleFieldProps) { const { id, label, value, onChange, required = false, type = 'text', rows, placeholder, helpText, min } = props const inputClass = - 'w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500' + 'w-full px-3 py-2 bg-cyber-darker border border-neon-cyan/20 rounded-lg text-cyber-accent placeholder-cyber-accent/50 focus:ring-2 focus:ring-neon-cyan/50 focus:border-neon-cyan/50 focus:outline-none transition-colors' const input = type === 'textarea' ? ( @@ -114,11 +114,11 @@ export function ArticleField(props: ArticleFieldProps) { return (
-
) } diff --git a/components/ArticleFormButtons.tsx b/components/ArticleFormButtons.tsx index f1932cb..e0ef943 100644 --- a/components/ArticleFormButtons.tsx +++ b/components/ArticleFormButtons.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { t } from '@/lib/i18n' interface ArticleFormButtonsProps { loading: boolean @@ -11,17 +12,17 @@ export function ArticleFormButtons({ loading, onCancel }: ArticleFormButtonsProp {onCancel && ( )} diff --git a/components/AuthorPresentationEditor.tsx b/components/AuthorPresentationEditor.tsx index 954f442..3a0bffd 100644 --- a/components/AuthorPresentationEditor.tsx +++ b/components/AuthorPresentationEditor.tsx @@ -3,6 +3,7 @@ import { useNostrConnect } from '@/hooks/useNostrConnect' import { useAuthorPresentation } from '@/hooks/useAuthorPresentation' import { ArticleField } from './ArticleField' import { ArticleFormButtons } from './ArticleFormButtons' +import { t } from '@/lib/i18n' interface AuthorPresentationDraft { presentation: string @@ -14,9 +15,9 @@ const ADDRESS_PATTERN = /^(1|3|bc1)[a-zA-Z0-9]{25,62}$/ function NotConnected() { return ( -
-

- Connectez-vous avec Nostr pour créer votre article de présentation +

+

+ {t('presentation.notConnected')}

) @@ -24,10 +25,10 @@ function NotConnected() { function SuccessNotice() { return ( -
-

Article de présentation créé !

-

- Votre article de présentation a été créé avec succès. Vous pouvez maintenant publier des articles. +

+

{t('presentation.success')}

+

+ {t('presentation.successMessage')}

) @@ -38,8 +39,8 @@ function ValidationError({ message }: { message: string | null }) { return null } return ( -
-

{message}

+
+

{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 ( +
+ + +
+ ) +} + 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')} /> + -
-
-
-

zapwall.fr

- -
-
- +
+
-

{t('presentation.title')}

-

+

{t('presentation.title')}

+

{t('presentation.description')}

+
)