generated from ludops/ludops-skeleton
feat: added FR and EN translation
Build and Deploy / build-and-push (push) Failing after 32s
Details
Build and Deploy / build-and-push (push) Failing after 32s
Details
This commit is contained in:
parent
9f03e5bc45
commit
2172dbc483
|
|
@ -11,11 +11,36 @@ main {
|
|||
}
|
||||
}
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.lang-toggle {
|
||||
background: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
color: #555;
|
||||
letter-spacing: 0.5px;
|
||||
transition: border-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.lang-toggle:hover {
|
||||
border-color: #111;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #111;
|
||||
margin: 0 0 8px 0;
|
||||
margin: 0;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
|
|
@ -33,14 +58,14 @@ h1 {
|
|||
margin: 0 0 16px 0;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title--clickable {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.section-title--clickable:hover {
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import TodaysTasks from './components/TodaysTasks'
|
|||
import CreateTaskForm from './components/CreateTaskForm'
|
||||
import type { Task, NewTaskInput } from './types'
|
||||
import { isTaskDueToday } from './utils/taskUtils'
|
||||
|
||||
const APP_NAME = 'TODO'
|
||||
import { useLanguage } from './i18n/LanguageContext'
|
||||
|
||||
type ApiStatus = 'loading' | 'ok' | 'error'
|
||||
|
||||
function App() {
|
||||
const { t, toggleLocale } = useLanguage()
|
||||
const [apiStatus, setApiStatus] = useState<ApiStatus>('loading')
|
||||
const [tasks, setTasks] = useState<Task[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
|
@ -52,14 +52,14 @@ function App() {
|
|||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
setError(errorData.error || 'Failed to create task')
|
||||
setError(errorData.error || t.errorCreateTask)
|
||||
return
|
||||
}
|
||||
|
||||
await fetchTasks()
|
||||
} catch (error) {
|
||||
console.error('Error creating task:', error)
|
||||
setError('Failed to create task')
|
||||
setError(t.errorCreateTask)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,13 +68,13 @@ function App() {
|
|||
try {
|
||||
const response = await fetch(`/api/tasks/${taskId}`, { method: 'DELETE' })
|
||||
if (!response.ok) {
|
||||
setError('Failed to delete task')
|
||||
setError(t.errorDeleteTask)
|
||||
return
|
||||
}
|
||||
await fetchTasks()
|
||||
} catch (error) {
|
||||
console.error('Error deleting task:', error)
|
||||
setError('Failed to delete task')
|
||||
setError(t.errorDeleteTask)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,13 +86,13 @@ function App() {
|
|||
})
|
||||
|
||||
if (!response.ok) {
|
||||
setError('Failed to complete task')
|
||||
setError(t.errorCompleteTask)
|
||||
return
|
||||
}
|
||||
await fetchTasks()
|
||||
} catch (error) {
|
||||
console.error('Error completing task:', error)
|
||||
setError('Failed to complete task')
|
||||
setError(t.errorCompleteTask)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,12 +100,15 @@ function App() {
|
|||
|
||||
return (
|
||||
<main>
|
||||
<h1>{APP_NAME}</h1>
|
||||
<div className="app-header">
|
||||
<h1>{t.appName}</h1>
|
||||
<button className="lang-toggle" onClick={toggleLocale}>{t.switchLang}</button>
|
||||
</div>
|
||||
|
||||
<div className={`status status--${apiStatus}`}>
|
||||
{apiStatus === 'loading' && 'Connecting to API…'}
|
||||
{apiStatus === 'ok' && '✓ API & database connected'}
|
||||
{apiStatus === 'error' && '⚠️ Could not reach API'}
|
||||
{apiStatus === 'loading' && t.statusLoading}
|
||||
{apiStatus === 'ok' && t.statusOk}
|
||||
{apiStatus === 'error' && t.statusError}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
|
|
@ -116,7 +119,7 @@ function App() {
|
|||
)}
|
||||
|
||||
{loading ? (
|
||||
<p>Loading tasks...</p>
|
||||
<p>{t.loadingTasks}</p>
|
||||
) : (
|
||||
<>
|
||||
<TodaysTasks
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
import type { NewTaskInput } from '../types';
|
||||
import { useLanguage } from '../i18n/LanguageContext';
|
||||
import './CreateTaskForm.css';
|
||||
|
||||
interface CreateTaskFormProps {
|
||||
|
|
@ -7,6 +8,7 @@ interface CreateTaskFormProps {
|
|||
}
|
||||
|
||||
export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
||||
const { t } = useLanguage();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [name, setName] = useState('');
|
||||
const [doesRepeat, setDoesRepeat] = useState(true);
|
||||
|
|
@ -44,19 +46,19 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
<div className="create-task-form">
|
||||
{!isOpen ? (
|
||||
<button className="open-form-btn" onClick={() => setIsOpen(true)}>
|
||||
+ Create new task
|
||||
{t.createNewTask}
|
||||
</button>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="task-form">
|
||||
<h3>Create New Task</h3>
|
||||
<h3>{t.createNewTaskTitle}</h3>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Task</label>
|
||||
<label>{t.taskLabel}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value.slice(0, 60))}
|
||||
placeholder="task name"
|
||||
placeholder={t.taskPlaceholder}
|
||||
maxLength={60}
|
||||
autoFocus
|
||||
/>
|
||||
|
|
@ -64,7 +66,7 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Repeats?</label>
|
||||
<label>{t.repeats}</label>
|
||||
<div className="radio-group">
|
||||
<label>
|
||||
<input
|
||||
|
|
@ -72,7 +74,7 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
checked={doesRepeat}
|
||||
onChange={() => setDoesRepeat(true)}
|
||||
/>
|
||||
Yes
|
||||
{t.yes}
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
|
|
@ -80,14 +82,14 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
checked={!doesRepeat}
|
||||
onChange={() => setDoesRepeat(false)}
|
||||
/>
|
||||
No
|
||||
{t.no}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{doesRepeat && (
|
||||
<div className="form-group">
|
||||
<label>Every</label>
|
||||
<label>{t.every}</label>
|
||||
<div className="repeat-input">
|
||||
<input
|
||||
type="number"
|
||||
|
|
@ -102,17 +104,17 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
onChange={(e) => setRepeatUnit(e.target.value as any)}
|
||||
className="repeat-unit"
|
||||
>
|
||||
<option value="DAYS">day{repeatAmount !== '1' ? 's' : ''}</option>
|
||||
<option value="WEEKS">week{repeatAmount !== '1' ? 's' : ''}</option>
|
||||
<option value="MONTHS">month{repeatAmount !== '1' ? 's' : ''}</option>
|
||||
<option value="YEARS">year{repeatAmount !== '1' ? 's' : ''}</option>
|
||||
<option value="DAYS">{repeatAmount !== '1' ? t.days : t.day}</option>
|
||||
<option value="WEEKS">{repeatAmount !== '1' ? t.weeks : t.week}</option>
|
||||
<option value="MONTHS">{repeatAmount !== '1' ? t.months : t.month}</option>
|
||||
<option value="YEARS">{repeatAmount !== '1' ? t.years : t.year}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="form-group">
|
||||
<label>To do right now?</label>
|
||||
<label>{t.toDoRightNow}</label>
|
||||
<div className="radio-group">
|
||||
<label>
|
||||
<input
|
||||
|
|
@ -120,7 +122,7 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
checked={toDoToday}
|
||||
onChange={() => setToDoToday(true)}
|
||||
/>
|
||||
Yes
|
||||
{t.yes}
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
|
|
@ -128,13 +130,13 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
checked={!toDoToday}
|
||||
onChange={() => setToDoToday(false)}
|
||||
/>
|
||||
No
|
||||
{t.no}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Priority</label>
|
||||
<label>{t.priority}</label>
|
||||
<div className="radio-group">
|
||||
<label>
|
||||
<input
|
||||
|
|
@ -142,7 +144,7 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
checked={priority === 'ESSENTIAL'}
|
||||
onChange={() => setPriority('ESSENTIAL')}
|
||||
/>
|
||||
Essential
|
||||
{t.essential}
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
|
|
@ -150,15 +152,15 @@ export default function CreateTaskForm({ onCreateTask }: CreateTaskFormProps) {
|
|||
checked={priority === 'WHEN_I_HAVE_TIME'}
|
||||
onChange={() => setPriority('WHEN_I_HAVE_TIME')}
|
||||
/>
|
||||
When I have time
|
||||
{t.whenIHaveTimePriority}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-actions">
|
||||
<button type="submit" className="submit-btn">Create Task</button>
|
||||
<button type="submit" className="submit-btn">{t.createTask}</button>
|
||||
<button type="button" className="cancel-btn" onClick={() => setIsOpen(false)}>
|
||||
Cancel
|
||||
{t.cancel}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { Task } from '../types';
|
||||
import { calculateNextDueDate } from '../utils/taskUtils';
|
||||
import { useLanguage } from '../i18n/LanguageContext';
|
||||
import './Timeline.css';
|
||||
|
||||
interface TimelineProps {
|
||||
|
|
@ -10,6 +11,7 @@ interface TimelineProps {
|
|||
const MAX_VISIBLE = 5;
|
||||
|
||||
export default function Timeline({ tasks, onDeleteTask }: TimelineProps) {
|
||||
const { t, locale } = useLanguage();
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
|
|
@ -29,16 +31,16 @@ export default function Timeline({ tasks, onDeleteTask }: TimelineProps) {
|
|||
|
||||
const formatDueDate = (date: Date): string => {
|
||||
const diff = Math.ceil((date.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
|
||||
if (diff === 1) return 'Tomorrow';
|
||||
if (diff <= 7) return `In ${diff} days`;
|
||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
if (diff === 1) return t.tomorrow;
|
||||
if (diff <= 7) return t.inDays(diff);
|
||||
return date.toLocaleDateString(locale === 'fr' ? 'fr-FR' : 'en-US', { month: 'short', day: 'numeric' });
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="upcoming-section">
|
||||
<h2 className="section-title">What the future holds</h2>
|
||||
<h2 className="section-title">{t.whatTheFutureHolds}</h2>
|
||||
{upcomingItems.length === 0 ? (
|
||||
<p className="no-upcoming">No upcoming tasks scheduled</p>
|
||||
<p className="no-upcoming">{t.noUpcoming}</p>
|
||||
) : (
|
||||
<div className={`upcoming-list${hasMore ? ' upcoming-list--scrollable' : ''}`}>
|
||||
{upcomingItems.map(({ task, dueDate }) => (
|
||||
|
|
@ -57,16 +59,17 @@ interface UpcomingRowProps {
|
|||
}
|
||||
|
||||
function UpcomingRow({ task, dueLabel, onDelete }: UpcomingRowProps) {
|
||||
const { t } = useLanguage();
|
||||
return (
|
||||
<div className="upcoming-row">
|
||||
<span className="upcoming-name">
|
||||
<span className="upcoming-name-text">{task.name}</span>
|
||||
</span>
|
||||
<span className="upcoming-badge">
|
||||
{task.priority === 'ESSENTIAL' ? 'Essential' : 'When I have time'}
|
||||
{task.priority === 'ESSENTIAL' ? t.essential : t.whenIHaveTime}
|
||||
</span>
|
||||
<span className="upcoming-due">{dueLabel}</span>
|
||||
<button className="upcoming-delete" onClick={onDelete} title="Remove task">✕</button>
|
||||
<button className="upcoming-delete" onClick={onDelete} title={t.removeTask}>✕</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
import type { Task } from '../types';
|
||||
import { useLanguage } from '../i18n/LanguageContext';
|
||||
import './TodaysTasks.css';
|
||||
|
||||
interface TodaysTasksProps {
|
||||
|
|
@ -8,17 +9,19 @@ interface TodaysTasksProps {
|
|||
}
|
||||
|
||||
export default function TodaysTasks({ tasks, onComplete }: TodaysTasksProps) {
|
||||
const essentialTasks = tasks.filter(t => t.priority === 'ESSENTIAL');
|
||||
const whenIHaveTimeTasks = tasks.filter(t => t.priority === 'WHEN_I_HAVE_TIME');
|
||||
const { t } = useLanguage();
|
||||
const essentialTasks = tasks.filter(task => task.priority === 'ESSENTIAL');
|
||||
const whenIHaveTimeTasks = tasks.filter(task => task.priority === 'WHEN_I_HAVE_TIME');
|
||||
const [showWhenIHaveTime, setShowWhenIHaveTime] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="todays-tasks">
|
||||
|
||||
<section className="task-section">
|
||||
<h2 className="section-title">Need to do</h2>
|
||||
<h2 className="section-title">{t.needToDo}
|
||||
<span className="count-badge">{essentialTasks.length}</span></h2>
|
||||
{essentialTasks.length === 0 ? (
|
||||
<p className="no-tasks">Nothing essential for today</p>
|
||||
<p className="no-tasks">{t.nothingEssential}</p>
|
||||
) : (
|
||||
<div className="task-grid">
|
||||
{essentialTasks.map(task => (
|
||||
|
|
@ -37,13 +40,13 @@ export default function TodaysTasks({ tasks, onComplete }: TodaysTasksProps) {
|
|||
className="section-title section-title--clickable"
|
||||
onClick={() => setShowWhenIHaveTime(!showWhenIHaveTime)}
|
||||
>
|
||||
When I have time
|
||||
{t.whenIHaveTime}
|
||||
<span className="count-badge">{whenIHaveTimeTasks.length}</span>
|
||||
<span className="toggle-icon">{showWhenIHaveTime ? '▾' : '▸'}</span>
|
||||
</h2>
|
||||
{showWhenIHaveTime && (
|
||||
whenIHaveTimeTasks.length === 0 ? (
|
||||
<p className="no-tasks">Nothing here</p>
|
||||
<p className="no-tasks">{t.nothingHere}</p>
|
||||
) : (
|
||||
<div className="task-grid">
|
||||
{whenIHaveTimeTasks.map(task => (
|
||||
|
|
@ -67,6 +70,7 @@ interface TaskCardProps {
|
|||
}
|
||||
|
||||
function TaskCard({ task, onComplete }: TaskCardProps) {
|
||||
const { t } = useLanguage();
|
||||
const [tooltipVisible, setTooltipVisible] = useState(false);
|
||||
|
||||
return (
|
||||
|
|
@ -84,7 +88,7 @@ function TaskCard({ task, onComplete }: TaskCardProps) {
|
|||
<button
|
||||
className="action-btn"
|
||||
onClick={() => onComplete(task.id)}
|
||||
title="Mark as completed"
|
||||
title={t.markCompleted}
|
||||
>
|
||||
✓
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import { createContext, useContext, useState } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { translations } from './translations';
|
||||
import type { Locale, Translations } from './translations';
|
||||
|
||||
interface LanguageContextType {
|
||||
locale: Locale;
|
||||
t: Translations;
|
||||
toggleLocale: () => void;
|
||||
}
|
||||
|
||||
const LanguageContext = createContext<LanguageContextType>({
|
||||
locale: 'en',
|
||||
t: translations.en,
|
||||
toggleLocale: () => {},
|
||||
});
|
||||
|
||||
export function LanguageProvider({ children }: { children: ReactNode }) {
|
||||
const [locale, setLocale] = useState<Locale>('en');
|
||||
|
||||
const toggleLocale = () => setLocale(l => (l === 'en' ? 'fr' : 'en'));
|
||||
|
||||
return (
|
||||
<LanguageContext.Provider value={{ locale, t: translations[locale], toggleLocale }}>
|
||||
{children}
|
||||
</LanguageContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useLanguage() {
|
||||
return useContext(LanguageContext);
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
export type Locale = 'en' | 'fr';
|
||||
|
||||
export const translations = {
|
||||
en: {
|
||||
appName: 'TODO',
|
||||
// API status
|
||||
statusLoading: 'Connecting to API…',
|
||||
statusOk: '✓ API & database connected',
|
||||
statusError: '⚠️ Could not reach API',
|
||||
loadingTasks: 'Loading tasks…',
|
||||
// Errors
|
||||
errorCreateTask: 'Failed to create task',
|
||||
errorDeleteTask: 'Failed to delete task',
|
||||
errorCompleteTask: 'Failed to complete task',
|
||||
// TodaysTasks
|
||||
needToDo: 'Need to do',
|
||||
nothingEssential: 'Nothing essential for today',
|
||||
whenIHaveTime: 'When I have time',
|
||||
nothingHere: 'Nothing here',
|
||||
markCompleted: 'Mark as completed',
|
||||
// Timeline
|
||||
whatTheFutureHolds: 'What the future holds',
|
||||
noUpcoming: 'No upcoming tasks scheduled',
|
||||
tomorrow: 'Tomorrow',
|
||||
inDays: (n: number) => `In ${n} days`,
|
||||
removeTask: 'Remove task',
|
||||
// Priority badges
|
||||
essential: 'Essential',
|
||||
// CreateTaskForm
|
||||
createNewTask: '+ Create new task',
|
||||
createNewTaskTitle: 'Create New Task',
|
||||
taskLabel: 'Task',
|
||||
taskPlaceholder: 'task name',
|
||||
repeats: 'Repeats?',
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
every: 'Every',
|
||||
day: 'day',
|
||||
days: 'days',
|
||||
week: 'week',
|
||||
weeks: 'weeks',
|
||||
month: 'month',
|
||||
months: 'months',
|
||||
year: 'year',
|
||||
years: 'years',
|
||||
toDoRightNow: 'To do right now?',
|
||||
priority: 'Priority',
|
||||
whenIHaveTimePriority: 'When I have time',
|
||||
createTask: 'Create Task',
|
||||
cancel: 'Cancel',
|
||||
// Lang toggle
|
||||
switchLang: 'FR',
|
||||
},
|
||||
fr: {
|
||||
appName: 'TODO',
|
||||
// API status
|
||||
statusLoading: 'Connexion à l\'API…',
|
||||
statusOk: '✓ API & base de données connectées',
|
||||
statusError: '⚠️ Impossible de joindre l\'API',
|
||||
loadingTasks: 'Chargement des tâches…',
|
||||
// Errors
|
||||
errorCreateTask: 'Impossible de créer la tâche',
|
||||
errorDeleteTask: 'Impossible de supprimer la tâche',
|
||||
errorCompleteTask: 'Impossible de marquer la tâche comme terminée',
|
||||
// TodaysTasks
|
||||
needToDo: 'À faire',
|
||||
nothingEssential: 'Rien d\'essentiel pour aujourd\'hui',
|
||||
whenIHaveTime: 'Quand j\'ai le temps',
|
||||
nothingHere: 'Rien ici',
|
||||
markCompleted: 'Marquer comme fait',
|
||||
// Timeline
|
||||
whatTheFutureHolds: 'Ce que l\'avenir réserve',
|
||||
noUpcoming: 'Aucune tâche à venir',
|
||||
tomorrow: 'Demain',
|
||||
inDays: (n: number) => `Dans ${n} jours`,
|
||||
removeTask: 'Supprimer la tâche',
|
||||
// Priority badges
|
||||
essential: 'Essentiel',
|
||||
// CreateTaskForm
|
||||
createNewTask: '+ Créer une tâche',
|
||||
createNewTaskTitle: 'Créer une nouvelle tâche',
|
||||
taskLabel: 'Tâche',
|
||||
taskPlaceholder: 'nom de la tâche',
|
||||
repeats: 'Se répète ?',
|
||||
yes: 'Oui',
|
||||
no: 'Non',
|
||||
every: 'Tous les',
|
||||
day: 'jour',
|
||||
days: 'jours',
|
||||
week: 'semaine',
|
||||
weeks: 'semaines',
|
||||
month: 'mois',
|
||||
months: 'mois',
|
||||
year: 'an',
|
||||
years: 'ans',
|
||||
toDoRightNow: 'À faire maintenant ?',
|
||||
priority: 'Priorité',
|
||||
whenIHaveTimePriority: 'Quand j\'ai le temps',
|
||||
createTask: 'Créer la tâche',
|
||||
cancel: 'Annuler',
|
||||
// Lang toggle
|
||||
switchLang: 'EN',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type Translations = typeof translations['en'];
|
||||
|
|
@ -2,9 +2,12 @@ import { StrictMode } from 'react'
|
|||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
import { LanguageProvider } from './i18n/LanguageContext.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<LanguageProvider>
|
||||
<App />
|
||||
</LanguageProvider>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue