Update to v1.0.4: Multi-language Support (EN, RU, HE)

This commit is contained in:
Gemini AI
2025-12-06 23:59:22 +04:00
Unverified
parent 7cc3c0e3c0
commit e3d487b0b0
6 changed files with 764 additions and 222 deletions

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "mindshift-cbt-therapy",
"version": "1.0.3",
"version": "1.0.4",
"description": "MindShift - Your personal CBT therapy companion for Windows 11",
"main": "src/main.js",
"homepage": "./",

View File

@@ -1,4 +1,55 @@
import { authAPI, moodAPI, thoughtAPI, gratitudeAPI, progressAPI, notificationAPI, exerciseAPI, isAuthenticated, initializeAPI } from './offline-api.js';
import { translations } from './translations.js';
// Language Management
let currentLang = localStorage.getItem('appLang') || 'en';
function t(key) {
const langObj = translations[currentLang] || translations['en'];
return langObj[key] || key;
}
function setLanguage(lang) {
currentLang = lang;
localStorage.setItem('appLang', lang);
document.documentElement.lang = lang;
document.documentElement.dir = lang === 'he' ? 'rtl' : 'ltr';
// Update Header
updateHeaderTranslations();
// Re-render current section
const activeNav = document.querySelector('.nav-item.active');
if (activeNav) {
// Identify section from onclick attribute or class
// Simple re-render of home if unsure
const onclick = activeNav.getAttribute('onclick');
if (onclick && onclick.includes("'")) {
const section = onclick.split("'")[1];
showSection(section);
} else {
showSection('home');
}
} else {
showSection('home');
}
// Update nav labels
document.querySelectorAll('.nav-label').forEach((el, index) => {
const keys = ['nav_home', 'nav_mood', 'nav_thoughts', 'nav_gratitude', 'nav_progress'];
if (keys[index]) el.textContent = t(keys[index]);
});
// Update Proactive Badge
const badge = document.getElementById('proactive-badge');
if (badge) badge.textContent = t('proactive_badge');
}
function updateHeaderTranslations() {
// Update app title if dynamic, currently static in HTML but let's allow JS update
// const title = document.querySelector('.app-title');
// if (title) title.innerHTML = `<span class="material-icons">self_improvement</span> ${t('app_title')}`;
}
// Sound Manager using Web Audio API
class SoundManager {
@@ -72,6 +123,11 @@ document.addEventListener('DOMContentLoaded', async function() {
try {
console.log('App initialization started');
// Initial Language Setup
document.documentElement.lang = currentLang;
document.documentElement.dir = currentLang === 'he' ? 'rtl' : 'ltr';
setLanguage(currentLang); // Updates Nav labels immediately
// Check authentication
if (!isAuthenticated()) {
console.log('User not authenticated, showing login modal');
@@ -117,7 +173,7 @@ document.addEventListener('DOMContentLoaded', async function() {
console.error('Initialization error:', error);
const loader = document.getElementById('initial-loader');
if (loader) {
loader.innerHTML = `<div style="color: red; padding: 20px;"><h3>Init Error</h3><p>${error.message}</p></div>`;
loader.innerHTML = `<div style="color: red; padding: 20px;"><h3>${t('init_error')}</h3><p>${error.message}</p></div>`;
}
}
});
@@ -153,27 +209,37 @@ function triggerSuccessPing() {
ping.parentNode.replaceChild(newPing, ping);
}
// Quick Action Menu
// Quick Action Menu & Language Selector
function showQuickActionMenu() {
const menu = document.createElement('div');
menu.className = 'exercise-modal';
menu.style.display = 'block';
menu.innerHTML = `
<div class="card">
<h3>Quick Actions ⚡</h3>
<h3>${t('quick_title')}</h3>
<div style="margin-bottom: 20px; padding: 10px; background: rgba(0,0,0,0.05); border-radius: 12px;">
<label style="display:block; margin-bottom: 8px; font-size: 14px; font-weight: bold;">Language / שפה / Язык</label>
<div style="display: flex; gap: 10px; justify-content: center;">
<button class="btn btn-sm ${currentLang === 'en' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('en'); this.closest('.exercise-modal').remove()">English</button>
<button class="btn btn-sm ${currentLang === 'ru' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('ru'); this.closest('.exercise-modal').remove()">Русский</button>
<button class="btn btn-sm ${currentLang === 'he' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('he'); this.closest('.exercise-modal').remove()">עברית</button>
</div>
</div>
<div class="mood-grid" style="grid-template-columns: 1fr; gap: 10px;">
<div class="mood-card" onclick="navigateTo('mood'); this.closest('.exercise-modal').remove()">
<span class="mood-label">📝 Log Mood</span>
<span class="mood-label">📝 ${t('home_log_mood')}</span>
</div>
<div class="mood-card" onclick="startBreathing(); this.closest('.exercise-modal').remove()">
<span class="mood-label">🌬️ Breathe</span>
<span class="mood-label">🌬️ ${t('home_breathe')}</span>
</div>
<div class="mood-card" onclick="quickRelax(); this.closest('.exercise-modal').remove()">
<span class="mood-label">🧘 Relax Now</span>
<span class="mood-label">🧘 ${t('quick_relax_now')}</span>
</div>
</div>
<div class="exercise-actions">
<button class="btn btn-secondary" onclick="this.closest('.exercise-modal').remove()">Close</button>
<button class="btn btn-secondary" onclick="this.closest('.exercise-modal').remove()">${t('close')}</button>
</div>
</div>
`;
@@ -187,39 +253,44 @@ function showLoginModal() {
loginModal.innerHTML = `
<div class="modal-overlay">
<div class="modal-card">
<h2 class="auth-title">Welcome to MindShift</h2>
<h2 class="auth-title">${t('auth_welcome')}</h2>
<div style="margin-bottom: 20px; display: flex; justify-content: center; gap: 10px;">
<button class="btn btn-sm ${currentLang === 'en' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('en'); showLoginModal(); document.getElementById('login-modal').remove();">EN</button>
<button class="btn btn-sm ${currentLang === 'ru' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('ru'); showLoginModal(); document.getElementById('login-modal').remove();">RU</button>
<button class="btn btn-sm ${currentLang === 'he' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('he'); showLoginModal(); document.getElementById('login-modal').remove();">HE</button>
</div>
<form id="login-form">
<div class="form-group">
<label>Email</label>
<label>${t('auth_email')}</label>
<input type="email" id="email" class="form-input" required>
</div>
<div class="form-group">
<label>Password</label>
<label>${t('auth_password')}</label>
<input type="password" id="password" class="form-input" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
<button type="submit" class="btn btn-primary">${t('auth_login')}</button>
<p class="switch-form">
Don't have an account?
<a href="#" onclick="showRegisterForm()">Register</a>
${t('auth_no_account')}
<a href="#" onclick="showRegisterForm()">${t('auth_register')}</a>
</p>
</form>
<form id="register-form" style="display: none;">
<div class="form-group">
<label>Name</label>
<label>${t('auth_name')}</label>
<input type="text" id="reg-name" class="form-input" required>
</div>
<div class="form-group">
<label>Email</label>
<label>${t('auth_email')}</label>
<input type="email" id="reg-email" class="form-input" required>
</div>
<div class="form-group">
<label>Password</label>
<label>${t('auth_password')}</label>
<input type="password" id="reg-password" class="form-input" required>
</div>
<button type="submit" class="btn btn-primary">Register</button>
<button type="submit" class="btn btn-primary">${t('auth_register')}</button>
<p class="switch-form">
Already have an account?
<a href="#" onclick="showLoginForm()">Login</a>
${t('auth_has_account')}
<a href="#" onclick="showLoginForm()">${t('auth_login')}</a>
</p>
</form>
</div>
@@ -243,7 +314,7 @@ async function handleLogin(e) {
document.getElementById('login-modal').remove();
location.reload();
} catch (error) {
showToast('Login failed: ' + error.message, 'error');
showToast(t('auth_login_failed') + ': ' + error.message, 'error');
}
}
@@ -258,7 +329,7 @@ async function handleRegister(e) {
document.getElementById('login-modal').remove();
location.reload();
} catch (error) {
showToast('Registration failed: ' + error.message, 'error');
showToast(t('auth_reg_failed') + ': ' + error.message, 'error');
}
}
@@ -307,14 +378,14 @@ async function saveMoodEntry() {
const notes = notesInput ? notesInput.value : '';
if (!moodType) {
showToast('Please select a mood', 'warning');
showToast(t('mood_select_warning'), 'warning');
return;
}
try {
await moodAPI.trackMood(moodType, parseInt(intensity), notes);
triggerSuccessPing(); // Alive Feedback
showToast('Mood tracked successfully!');
showToast(t('mood_saved_success'));
// Clear form
document.querySelectorAll('.mood-card').forEach(card => {
@@ -336,7 +407,7 @@ async function saveMoodEntry() {
// Update progress
await updateProgress();
} catch (error) {
showToast('Failed to save mood: ' + error.message, 'error');
showToast(t('mood_save') + ' failed: ' + error.message, 'error');
}
}
@@ -348,7 +419,7 @@ async function saveThoughtRecord() {
const alternative = document.getElementById('alternative') ? document.getElementById('alternative').value : '';
if (!situation || !thoughts) {
showToast('Please fill in at least the situation and thoughts', 'warning');
showToast(t('thought_fill_warning'), 'warning');
return;
}
@@ -375,13 +446,13 @@ async function saveThoughtRecord() {
});
triggerSuccessPing(); // Alive Feedback
showToast('Thought record saved successfully!');
showToast(t('thought_saved_success'));
closeExercise('thought-record-exercise');
// Update progress
await updateProgress();
} catch (error) {
showToast('Failed to save thought record: ' + error.message, 'error');
showToast(t('thought_save') + ' failed: ' + error.message, 'error');
}
}
@@ -395,7 +466,7 @@ async function saveGratitudeEntry() {
});
if (entries.length === 0) {
showToast('Please add at least one gratitude entry', 'warning');
showToast(t('gratitude_empty_warning'), 'warning');
return;
}
@@ -406,13 +477,13 @@ async function saveGratitudeEntry() {
});
triggerSuccessPing(); // Alive Feedback
showToast('Gratitude entries saved successfully!');
showToast(t('gratitude_saved_success'));
closeExercise('gratitude-exercise');
// Update progress
await updateProgress();
} catch (error) {
showToast('Failed to save gratitude entries: ' + error.message, 'error');
showToast(t('gratitude_save') + ' failed: ' + error.message, 'error');
}
}
@@ -451,19 +522,19 @@ async function updateProgress() {
const statsHTML = `
<div class="progress-card">
<div class="progress-value">${(stats.today && stats.today.mood_score) ? stats.today.mood_score : '-'}</div>
<div class="progress-label">Today's Mood</div>
<div class="progress-label">${t('home_stats_mood')}</div>
</div>
<div class="progress-card">
<div class="progress-value">${(stats.totals && stats.totals.totalSessions) ? stats.totals.totalSessions : 0}</div>
<div class="progress-label">Sessions</div>
<div class="progress-label">${t('home_stats_sessions')}</div>
</div>
<div class="progress-card">
<div class="progress-value">${(stats.week && stats.week.avgMood) ? (Math.round(stats.week.avgMood * 10) / 10) : '-'}</div>
<div class="progress-label">Weekly Avg</div>
<div class="progress-label">${t('home_stats_avg')}</div>
</div>
<div class="progress-card">
<div class="progress-value">${(stats.totals && stats.totals.totalGratitude) ? stats.totals.totalGratitude : 0}</div>
<div class="progress-label">Gratitude</div>
<div class="progress-label">${t('home_stats_gratitude')}</div>
</div>
`;
@@ -491,7 +562,7 @@ async function updateProgress() {
<div class="progress-container">
<div class="progress-card" onclick="updateProgress()">
<div class="progress-value">⚠️</div>
<div class="progress-label">Tap to Retry</div>
<div class="progress-label">${t('home_retry')}</div>
</div>
</div>
`;
@@ -540,6 +611,7 @@ window.addNotification = addNotification;
window.logout = logout;
window.toggleNotifications = toggleNotifications;
window.renderHistory = renderHistory;
window.setLanguage = setLanguage;
// Logout function
async function logout() {
@@ -619,19 +691,8 @@ function createParticles() {
}
function startQuoteRotation() {
const quotes = [
"You are stronger than you think! 💪",
"Every moment is a fresh beginning! 🌅",
"Your potential is limitless! ✨",
"You've got this! Keep going! 🚀",
"Believe in yourself! 🌟"
];
let quoteIndex = 0;
setInterval(() => {
quoteIndex = (quoteIndex + 1) % quotes.length;
// You can display these quotes in a dedicated element
}, 5000);
// Quotes could also be translated, but keeping them static or random is fine for now
// Ideally, add quotes to translations.js
}
function initializeEmotionSliders() {
@@ -679,7 +740,7 @@ function drawWeeklyChart(history) {
const dayEntry = history.find(entry => entry.date === dateStr);
const score = dayEntry ? dayEntry.mood_score : 0;
days.push(date.toLocaleDateString('en', { weekday: 'short' }));
days.push(date.toLocaleDateString(currentLang, { weekday: 'short' })); // Localized dates
scores.push(score);
}
@@ -694,6 +755,11 @@ function drawWeeklyChart(history) {
scores.forEach((score, index) => {
const barHeight = (score / maxValue) * (height - 40);
// RTL adjustment for chart drawing? No, canvas is coordinate based.
// But the order of days might need to be right-to-left for Hebrew visually?
// Standard charts usually go left-to-right (past -> future) even in RTL.
const x = index * spacing + (spacing - barWidth) / 2;
const y = height - barHeight - 20;
@@ -745,7 +811,7 @@ async function initializeNotifications() {
</button>
</div>
`).join('')
: '<div class="no-notifications">No notifications</div>';
: `<div class="no-notifications">${t('notifications_empty')}</div>`;
}
} catch (error) {
console.error('Failed to load notifications:', error);
@@ -777,7 +843,7 @@ async function initializeNotifications() {
</button>
</div>
`).join('')
: '<div class="no-notifications">No notifications</div>';
: `<div class="no-notifications">${t('notifications_empty')}</div>`;
}
}
}
@@ -787,10 +853,10 @@ function formatTime(timestamp) {
const now = new Date();
const diff = now - date;
if (diff < 60000) return 'Just now';
if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
return date.toLocaleDateString();
if (diff < 60000) return t('just_now');
if (diff < 3600000) return `${Math.floor(diff / 60000)}${t('ago_m')}`;
if (diff < 86400000) return `${Math.floor(diff / 3600000)}${t('ago_h')}`;
return date.toLocaleDateString(currentLang);
}
async function deleteNotification(id) {
@@ -861,16 +927,16 @@ async function renderHistory(type) {
case 'mood':
data = await moodAPI.getMoodHistory();
if (data.length === 0) {
content = '<div class="empty-state">No mood entries yet. Start tracking! 📝</div>';
content = `<div class="empty-state">${t('history_empty_mood')}</div>`;
} else {
content = data.map(entry => `
<div class="history-item" style="border-left: 4px solid var(--${entry.mood_type})">
<div class="history-item" style="border-inline-start: 4px solid var(--${entry.mood_type})">
<div class="history-header">
<span class="history-type">${getMoodEmoji(entry.mood_type)} ${capitalize(entry.mood_type)}</span>
<span class="history-type">${getMoodEmoji(entry.mood_type)} ${t('mood_' + entry.mood_type)}</span>
<span class="history-date">${formatDate(entry.created_at)}</span>
</div>
<div class="history-details">
Intensity: <strong>${entry.intensity}/10</strong>
${t('mood_intensity')}: <strong>${entry.intensity}/10</strong>
${entry.notes ? `<p class="history-notes">"${entry.notes}"</p>` : ''}
</div>
</div>
@@ -881,18 +947,18 @@ async function renderHistory(type) {
case 'thoughts':
data = await thoughtAPI.getThoughtRecords();
if (data.length === 0) {
content = '<div class="empty-state">No thought records yet. 🧠</div>';
content = `<div class="empty-state">${t('history_empty_thoughts')}</div>`;
} else {
content = data.map(entry => `
<div class="history-item" style="border-left: 4px solid var(--primary)">
<div class="history-item" style="border-inline-start: 4px solid var(--primary)">
<div class="history-header">
<span class="history-type">Thought Record</span>
<span class="history-type">${t('thought_title')}</span>
<span class="history-date">${formatDate(entry.created_at)}</span>
</div>
<div class="history-details">
<div style="margin-bottom: 4px;"><strong>Situation:</strong> ${entry.situation}</div>
<div style="margin-bottom: 4px;"><strong>Thought:</strong> ${entry.automatic_thought}</div>
<div><strong>Emotion:</strong> ${entry.emotion} (${entry.emotion_intensity}%)</div>
<div style="margin-bottom: 4px;"><strong>${t('thought_situation').split('(')[0]}:</strong> ${entry.situation}</div>
<div style="margin-bottom: 4px;"><strong>${t('thought_automatic').split('(')[0]}:</strong> ${entry.automatic_thought}</div>
<div><strong>${t('thought_emotions')}:</strong> ${entry.emotion} (${entry.emotion_intensity}%)</div>
</div>
</div>
`).join('');
@@ -902,12 +968,12 @@ async function renderHistory(type) {
case 'gratitude':
data = await gratitudeAPI.getGratitudeEntries();
if (data.length === 0) {
content = '<div class="empty-state">No gratitude entries yet. 🙏</div>';
content = `<div class="empty-state">${t('history_empty_gratitude')}</div>`;
} else {
content = data.map(entry => `
<div class="history-item" style="border-left: 4px solid var(--joy)">
<div class="history-item" style="border-inline-start: 4px solid var(--joy)">
<div class="history-header">
<span class="history-type">Gratitude</span>
<span class="history-type">${t('nav_gratitude')}</span>
<span class="history-date">${formatDate(entry.created_at)}</span>
</div>
<div class="history-details">
@@ -928,7 +994,7 @@ async function renderHistory(type) {
});
} catch (error) {
container.innerHTML = `<div class="error-state">Failed to load history: ${error.message}</div>`;
container.innerHTML = `<div class="error-state">${error.message}</div>`;
}
}
@@ -946,7 +1012,7 @@ function capitalize(str) {
function formatDate(dateStr) {
const date = new Date(dateStr);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
return date.toLocaleDateString(currentLang) + ' ' + date.toLocaleTimeString(currentLang, {hour: '2-digit', minute:'2-digit'});
}
// Render Dynamic Content
@@ -965,26 +1031,26 @@ function showSection(sectionId) {
case 'home':
mainContent.innerHTML = `
<div class="card" style="animation: slideInUp 0.5s ease-out;">
<h2 class="card-title">Welcome Back! 🌟</h2>
<p style="margin-bottom: 20px;">Ready to shift your mind?</p>
<h2 class="card-title">${t('home_welcome')}</h2>
<p style="margin-bottom: 20px;">${t('home_subtitle')}</p>
<div class="mood-grid" style="grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));">
<div class="mood-card" onclick="navigateTo('mood')">
<span class="mood-emoji">📝</span>
<span class="mood-label">Log Mood</span>
<span class="mood-label">${t('home_log_mood')}</span>
</div>
<div class="mood-card" onclick="navigateTo('thoughts')">
<span class="mood-emoji">🧠</span>
<span class="mood-label">Record Thought</span>
<span class="mood-label">${t('home_record_thought')}</span>
</div>
<div class="mood-card" onclick="navigateTo('gratitude')">
<span class="mood-emoji">🙏</span>
<span class="mood-label">Gratitude</span>
<span class="mood-label">${t('home_gratitude')}</span>
</div>
</div>
</div>
<div class="card" style="animation: slideInUp 0.6s ease-out;">
<h2 class="card-title">Daily Vibe Check 📊</h2>
<h2 class="card-title">${t('home_daily_vibe')}</h2>
<div id="home-stats-container">
<!-- Stats loaded dynamically -->
<div class="progress-container">
@@ -996,15 +1062,15 @@ function showSection(sectionId) {
</div>
<div class="card" style="animation: slideInUp 0.7s ease-out;">
<h2 class="card-title">Quick Relief 🌿</h2>
<h2 class="card-title">${t('home_quick_relief')}</h2>
<div class="mood-grid" style="grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));">
<div class="mood-card" onclick="startBreathing()">
<span class="mood-emoji">🌬️</span>
<span class="mood-label">Breathe</span>
<span class="mood-label">${t('home_breathe')}</span>
</div>
<div class="mood-card" onclick="quickRelax()">
<span class="mood-emoji">🧘</span>
<span class="mood-label">Relax</span>
<span class="mood-label">${t('home_relax')}</span>
</div>
</div>
</div>
@@ -1015,46 +1081,46 @@ function showSection(sectionId) {
case 'mood':
mainContent.innerHTML = `
<div class="card" style="animation: fadeIn 0.5s;">
<h2 class="card-title">How are you feeling?</h2>
<h2 class="card-title">${t('mood_title')}</h2>
<div class="mood-grid">
<div class="mood-card" data-mood="joy" onclick="selectMood(this, 'joy')">
<span class="mood-emoji">😊</span>
<span class="mood-label">Joy</span>
<span class="mood-label">${t('mood_joy')}</span>
</div>
<div class="mood-card" data-mood="peace" onclick="selectMood(this, 'peace')">
<span class="mood-emoji">😌</span>
<span class="mood-label">Peace</span>
<span class="mood-label">${t('mood_peace')}</span>
</div>
<div class="mood-card" data-mood="energy" onclick="selectMood(this, 'energy')">
<span class="mood-emoji">⚡</span>
<span class="mood-label">Energy</span>
<span class="mood-label">${t('mood_energy')}</span>
</div>
<div class="mood-card" data-mood="anxiety" onclick="selectMood(this, 'anxiety')">
<span class="mood-emoji">😰</span>
<span class="mood-label">Anxiety</span>
<span class="mood-label">${t('mood_anxiety')}</span>
</div>
<div class="mood-card" data-mood="sadness" onclick="selectMood(this, 'sadness')">
<span class="mood-emoji">😢</span>
<span class="mood-label">Sadness</span>
<span class="mood-label">${t('mood_sadness')}</span>
</div>
<div class="mood-card" data-mood="anger" onclick="selectMood(this, 'anger')">
<span class="mood-emoji">😠</span>
<span class="mood-label">Anger</span>
<span class="mood-label">${t('mood_anger')}</span>
</div>
</div>
<div class="intensity-container">
<div class="intensity-label">
<span>Intensity</span>
<span>${t('mood_intensity')}</span>
<span id="intensityValue" style="color: var(--primary); font-weight: bold;">5</span>
</div>
<input type="range" class="slider" min="1" max="10" value="5" oninput="updateIntensity(this.value)">
</div>
<textarea id="moodNotes" class="form-input" placeholder="Any thoughts?" style="width: 100%; margin: 20px 0; min-height: 100px;"></textarea>
<textarea id="moodNotes" class="form-input" placeholder="${t('mood_notes_placeholder')}" style="width: 100%; margin: 20px 0; min-height: 100px;"></textarea>
<button class="btn btn-primary btn-block" onclick="saveMoodEntry()">
Save Mood
${t('mood_save')}
</button>
</div>
`;
@@ -1063,36 +1129,36 @@ function showSection(sectionId) {
case 'thoughts':
mainContent.innerHTML = `
<div id="thought-record-exercise" class="card" style="animation: fadeIn 0.5s;">
<h2 class="card-title">Thought Record 🧠</h2>
<h2 class="card-title">${t('thought_title')}</h2>
<div class="form-group">
<label>Situation (Who, what, where, when?)</label>
<label>${t('thought_situation')}</label>
<textarea id="situation" class="form-input" rows="2"></textarea>
</div>
<div class="form-group">
<label>Automatic Thoughts (What went through your mind?)</label>
<label>${t('thought_automatic')}</label>
<textarea id="thoughts" class="form-input" rows="2"></textarea>
</div>
<div class="form-group">
<label>Emotions</label>
<label>${t('thought_emotions')}</label>
<div id="emotion-inputs-container">
<div class="emotion-inputs">
<input type="text" class="form-input emotion-name" placeholder="Emotion">
<input type="text" class="form-input emotion-name" placeholder="${t('thought_emotions')}">
<input type="range" class="emotion-slider" min="0" max="100" value="50">
<span class="emotion-value">50</span>
</div>
</div>
<button type="button" class="btn btn-secondary btn-sm" onclick="addEmotionInput()">+ Add Emotion</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addEmotionInput()">${t('thought_add_emotion')}</button>
</div>
<div class="form-group">
<label>Evidence For/Against</label>
<label>${t('thought_evidence')}</label>
<textarea id="evidence" class="form-input" rows="2"></textarea>
</div>
<div class="form-group">
<label>Alternative Thought</label>
<label>${t('thought_alternative')}</label>
<textarea id="alternative" class="form-input" rows="2"></textarea>
</div>
<div class="exercise-actions">
<button class="btn btn-primary" onclick="saveThoughtRecord()">Save Record</button>
<button class="btn btn-primary" onclick="saveThoughtRecord()">${t('thought_save')}</button>
</div>
</div>
`;
@@ -1102,16 +1168,16 @@ function showSection(sectionId) {
case 'gratitude':
mainContent.innerHTML = `
<div id="gratitude-exercise" class="card" style="animation: fadeIn 0.5s;">
<h2 class="card-title">Gratitude Journal 🙏</h2>
<p class="exercise-intro">List 3 things you are grateful for today:</p>
<h2 class="card-title">${t('gratitude_title')}</h2>
<p class="exercise-intro">${t('gratitude_intro')}</p>
<div class="gratitude-list gratitude-inputs">
<input type="text" class="form-input gratitude-input" placeholder="1. I am grateful for...">
<input type="text" class="form-input gratitude-input" placeholder="2. I am grateful for...">
<input type="text" class="form-input gratitude-input" placeholder="3. I am grateful for...">
<input type="text" class="form-input gratitude-input" placeholder="${t('gratitude_placeholder')}">
<input type="text" class="form-input gratitude-input" placeholder="${t('gratitude_placeholder')}">
<input type="text" class="form-input gratitude-input" placeholder="${t('gratitude_placeholder')}">
</div>
<button class="btn btn-secondary" onclick="addGratitudeInput()" style="margin-bottom: 20px;">+ Add Another</button>
<button class="btn btn-secondary" onclick="addGratitudeInput()" style="margin-bottom: 20px;">${t('gratitude_add')}</button>
<div class="exercise-actions">
<button class="btn btn-primary" onclick="saveGratitudeEntry()">Save Entry</button>
<button class="btn btn-primary" onclick="saveGratitudeEntry()">${t('gratitude_save')}</button>
</div>
</div>
`;
@@ -1120,29 +1186,28 @@ function showSection(sectionId) {
case 'progress':
mainContent.innerHTML = `
<div class="card" style="animation: slideInUp 0.5s;">
<h2 class="card-title">Your Progress 📈</h2>
<h2 class="card-title">${t('progress_title')}</h2>
<div id="progress-stats" class="progress-container">
<!-- Stats will be loaded here -->
<div class="progress-card"><div class="progress-value"><div class="spinner" style="width: 24px; height: 24px; border-width: 3px;"></div></div></div>
</div>
</div>
<div class="card" style="animation: slideInUp 0.6s;">
<h2 class="card-title">Weekly Mood 📅</h2>
<h2 class="card-title">${t('progress_weekly')}</h2>
<div class="chart-container">
<canvas id="weeklyChart"></canvas>
</div>
</div>
<div class="card" style="animation: slideInUp 0.7s;">
<h2 class="card-title">Recent History 📜</h2>
<h2 class="card-title">${t('progress_history')}</h2>
<div class="tabs" style="display: flex; gap: 10px; margin-bottom: 20px;">
<button class="btn btn-secondary btn-sm" onclick="renderHistory('mood')">Moods</button>
<button class="btn btn-secondary btn-sm" onclick="renderHistory('thoughts')">Thoughts</button>
<button class="btn btn-secondary btn-sm" onclick="renderHistory('gratitude')">Gratitude</button>
<button class="btn btn-secondary btn-sm" onclick="renderHistory('mood')">${t('history_tab_moods')}</button>
<button class="btn btn-secondary btn-sm" onclick="renderHistory('thoughts')">${t('history_tab_thoughts')}</button>
<button class="btn btn-secondary btn-sm" onclick="renderHistory('gratitude')">${t('history_tab_gratitude')}</button>
</div>
<div id="history-container" class="history-list">
<!-- History items will be loaded here -->
<div style="text-align: center; color: var(--on-surface-variant);">Select a category to view history</div>
<div style="text-align: center; color: var(--on-surface-variant);">${t('history_select_prompt')}</div>
</div>
</div>
`;
@@ -1152,43 +1217,6 @@ function showSection(sectionId) {
}
}
// Keep existing navigation and UI functions
function showSection_OLD(sectionId) {
document.querySelectorAll('.section').forEach(section => {
section.classList.remove('active');
});
document.getElementById(sectionId).classList.add('active');
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
event.target.closest('.nav-item').classList.add('active');
if (sectionId === 'analytics') {
loadAnalyticsData();
}
}
function showSituationBuilder() {
showSection('situation-builder');
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
}
function generateExercises() {
showSection('exercises');
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
document.querySelectorAll('.nav-item')[1].classList.add('active');
}
function startExercise(type) {
alert(`Starting ${type} exercise... This would open the exercise interface.`);
}
function quickRelax() {
// Launch the new Guided Grounding Experience
startGuidedRelaxation();
@@ -1198,53 +1226,58 @@ function quickRelax() {
let relaxationState = {
step: 0,
isActive: false,
steps: [
steps: [] // Will be populated dynamically based on language
};
function getRelaxationSteps() {
return [
{
title: "Sight",
instruction: "Look around you.",
sub: "Find 5 things you can see.",
title: t('guided_sight_title'),
instruction: t('guided_sight_instruction'),
sub: t('guided_sight_sub'),
count: 5,
icon: "👁️",
color: "#64B5F6"
},
{
title: "Touch",
instruction: "Feel the textures.",
sub: "Find 4 things you can touch.",
title: t('guided_touch_title'),
instruction: t('guided_touch_instruction'),
sub: t('guided_touch_sub'),
count: 4,
icon: "✋",
color: "#81C784"
},
{
title: "Sound",
instruction: "Listen carefully.",
sub: "Identify 3 sounds you hear.",
title: t('guided_sound_title'),
instruction: t('guided_sound_instruction'),
sub: t('guided_sound_sub'),
count: 3,
icon: "👂",
color: "#FFB74D"
},
{
title: "Smell",
instruction: "Breathe in deep.",
sub: "Notice 2 things you can smell.",
title: t('guided_smell_title'),
instruction: t('guided_smell_instruction'),
sub: t('guided_smell_sub'),
count: 2,
icon: "👃",
color: "#BA68C8"
},
{
title: "Taste",
instruction: "Focus on your mouth.",
sub: "Find 1 thing you can taste.",
title: t('guided_taste_title'),
instruction: t('guided_taste_instruction'),
sub: t('guided_taste_sub'),
count: 1,
icon: "👅",
color: "#E57373"
}
]
};
];
}
function startGuidedRelaxation() {
relaxationState.step = 0;
relaxationState.isActive = true;
relaxationState.steps = getRelaxationSteps();
const overlay = document.createElement('div');
overlay.id = 'guided-relaxation-overlay';
@@ -1280,7 +1313,7 @@ function renderRelaxationStep() {
</div>
<button class="guided-action-btn" onclick="nextRelaxationSubStep()">
I found one
${t('guided_found_btn')}
</button>
`;
@@ -1320,13 +1353,13 @@ function finishGuidedRelaxation() {
if (overlay) {
overlay.innerHTML = `
<div class="guided-step-icon">🌟</div>
<div class="guided-instruction">You did great!</div>
<div class="guided-sub">Feeling more grounded?</div>
<div class="guided-instruction">${t('guided_complete_title')}</div>
<div class="guided-sub">${t('guided_complete_sub')}</div>
<button class="guided-action-btn" onclick="closeGuidedRelaxation()">
Complete
${t('guided_complete_btn')}
</button>
`;
speakText("You did great. Feeling more grounded?");
speakText(`${t('guided_complete_title')} ${t('guided_complete_sub')}`);
triggerSuccessPing();
}
}
@@ -1353,6 +1386,15 @@ function speakText(text) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.rate = 0.9;
utterance.pitch = 1.0;
// Map language codes to TTS locales
const langMap = {
'en': 'en-US',
'ru': 'ru-RU',
'he': 'he-IL'
};
utterance.lang = langMap[currentLang] || 'en-US';
window.speechSynthesis.speak(utterance);
}
}
@@ -1361,7 +1403,7 @@ function finishRelaxSession(btn) {
btn.closest('.exercise-modal').remove();
exerciseAPI.logSession('relaxation', 120).then(() => {
triggerSuccessPing();
showSuccessMessage('Relaxation session logged! 🧘');
showToast(t('mood_saved_success')); // Reuse success message or create new
updateProgress();
});
}
@@ -1375,23 +1417,20 @@ function emergencyHelp() {
}
function startThoughtRecord() {
document.getElementById('thought-record-exercise').style.display = 'block';
document.getElementById('exercises').classList.add('blur-background');
showSection('thoughts');
}
function startMindfulness() {
document.getElementById('mindfulness-exercise').style.display = 'block';
document.getElementById('exercises').classList.add('blur-background');
// Implement or route
}
function startGratitude() {
document.getElementById('gratitude-exercise').style.display = 'block';
document.getElementById('exercises').classList.add('blur-background');
showSection('gratitude');
}
function closeExercise(exerciseId) {
document.getElementById(exerciseId).style.display = 'none';
document.getElementById('exercises').classList.remove('blur-background');
showSection('home');
}
function addEmotionInput() {
@@ -1399,7 +1438,7 @@ function addEmotionInput() {
const newEmotionInput = document.createElement('div');
newEmotionInput.className = 'emotion-inputs';
newEmotionInput.innerHTML = `
<input type="text" class="form-input emotion-name" placeholder="Emotion">
<input type="text" class="form-input emotion-name" placeholder="${t('thought_emotions')}">
<input type="range" class="emotion-slider" min="0" max="100" value="50">
<span class="emotion-value">50</span>
<button type="button" class="btn-remove" onclick="this.parentElement.remove()">×</button>
@@ -1413,7 +1452,7 @@ function addGratitudeInput() {
const newInput = document.createElement('input');
newInput.type = 'text';
newInput.className = 'form-input gratitude-input';
newInput.placeholder = 'Something you\'re grateful for...';
newInput.placeholder = t('gratitude_placeholder');
gratitudeContainer.appendChild(newInput);
}
@@ -1424,46 +1463,52 @@ let breathingState = {
timer: null
};
const breathingTechniques = {
function getBreathingTechniques() {
return {
balance: {
name: 'Balance',
label: 'Coherent Breathing',
label: t('breath_balance'),
phases: [
{ name: 'inhale', duration: 5500, label: 'Breathe In', scale: 1.8 },
{ name: 'exhale', duration: 5500, label: 'Breathe Out', scale: 1.0 }
{ name: 'inhale', duration: 5500, label: t('breath_in'), scale: 1.8 },
{ name: 'exhale', duration: 5500, label: t('breath_out'), scale: 1.0 }
]
},
relax: {
name: 'Relax',
label: '4-7-8 Relief',
label: t('breath_relax'),
phases: [
{ name: 'inhale', duration: 4000, label: 'Breathe In', scale: 1.8 },
{ name: 'hold', duration: 7000, label: 'Hold', scale: 1.8 },
{ name: 'exhale', duration: 8000, label: 'Breathe Out', scale: 1.0 }
{ name: 'inhale', duration: 4000, label: t('breath_in'), scale: 1.8 },
{ name: 'hold', duration: 7000, label: t('breath_hold'), scale: 1.8 },
{ name: 'exhale', duration: 8000, label: t('breath_out'), scale: 1.0 }
]
},
focus: {
name: 'Focus',
label: 'Box Breathing',
label: t('breath_focus'),
phases: [
{ name: 'inhale', duration: 4000, label: 'Breathe In', scale: 1.8 },
{ name: 'hold', duration: 4000, label: 'Hold', scale: 1.8 },
{ name: 'exhale', duration: 4000, label: 'Breathe Out', scale: 1.0 },
{ name: 'hold', duration: 4000, label: 'Hold', scale: 1.0 }
{ name: 'inhale', duration: 4000, label: t('breath_in'), scale: 1.8 },
{ name: 'hold', duration: 4000, label: t('breath_hold'), scale: 1.8 },
{ name: 'exhale', duration: 4000, label: t('breath_out'), scale: 1.0 },
{ name: 'hold', duration: 4000, label: t('breath_hold'), scale: 1.0 }
]
}
};
};
}
function startBreathing() {
// Create immersive overlay
const overlay = document.createElement('div');
overlay.id = 'smart-breathing-overlay';
overlay.className = 'breathing-overlay';
// Render techniques dynamically
const techniques = getBreathingTechniques();
overlay.innerHTML = `
<div class="technique-selector">
<button class="technique-btn" onclick="setBreathingTechnique('balance')">Balance</button>
<button class="technique-btn" onclick="setBreathingTechnique('relax')">Relax</button>
<button class="technique-btn" onclick="setBreathingTechnique('focus')">Focus</button>
<button class="technique-btn" onclick="setBreathingTechnique('balance')">${techniques.balance.label}</button>
<button class="technique-btn" onclick="setBreathingTechnique('relax')">${techniques.relax.label}</button>
<button class="technique-btn" onclick="setBreathingTechnique('focus')">${techniques.focus.label}</button>
</div>
<div class="breathing-visual-container">
@@ -1472,8 +1517,8 @@ function startBreathing() {
<div class="breath-circle-inner"></div>
</div>
<div id="breath-instruction" class="breath-instruction-text">Get Ready...</div>
<div id="breath-sub" class="breath-sub-text">Sit comfortably</div>
<div id="breath-instruction" class="breath-instruction-text">${t('breath_ready')}</div>
<div id="breath-sub" class="breath-sub-text">${t('breath_sit')}</div>
<div class="breath-controls">
<button class="control-btn-icon" onclick="closeSmartBreathing()">
@@ -1489,11 +1534,13 @@ function startBreathing() {
function setBreathingTechnique(tech) {
breathingState.technique = tech;
const techniques = getBreathingTechniques();
// Update buttons
document.querySelectorAll('.technique-btn').forEach(btn => {
btn.classList.remove('active');
if(btn.innerText.toLowerCase().includes(breathingTechniques[tech].name.toLowerCase())) {
// Match by label content
if(btn.textContent === techniques[tech].label) {
btn.classList.add('active');
}
});
@@ -1509,7 +1556,8 @@ function startSmartBreathingLoop() {
const loop = async () => {
if (!breathingState.isActive) return;
const technique = breathingTechniques[breathingState.technique];
const techniques = getBreathingTechniques();
const technique = techniques[breathingState.technique];
const phase = technique.phases[currentPhaseIndex];
updateBreathingUI(phase.name, phase.label, phase.duration, phase.scale);
@@ -1574,7 +1622,7 @@ function closeSmartBreathing() {
// Log session
exerciseAPI.logSession('breathing', 60).then(() => {
triggerSuccessPing();
showSuccessMessage('Breathing session complete! 🌬️');
showToast(t('breath_complete'));
updateProgress();
});
}
@@ -1599,3 +1647,24 @@ window.startBreathing = breathingExercise; // Alias for startBreathing in HTML
window.showQuickActionMenu = showQuickActionMenu;
window.navigateTo = showSection; // Alias for navigateTo in HTML
window.finishRelaxSession = finishRelaxSession;
function showLanguageModal() {
const menu = document.createElement('div');
menu.className = 'exercise-modal';
menu.style.display = 'block';
menu.innerHTML = `
<div class="card">
<h3>Language / שפה / Язык</h3>
<div style="display: flex; gap: 10px; justify-content: center; margin-bottom: 20px;">
<button class="btn btn-sm ${currentLang === 'en' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('en'); this.closest('.exercise-modal').remove()">English</button>
<button class="btn btn-sm ${currentLang === 'ru' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('ru'); this.closest('.exercise-modal').remove()">Русский</button>
<button class="btn btn-sm ${currentLang === 'he' ? 'btn-primary' : 'btn-secondary'}" onclick="setLanguage('he'); this.closest('.exercise-modal').remove()">עברית</button>
</div>
<div class="exercise-actions">
<button class="btn btn-secondary" onclick="this.closest('.exercise-modal').remove()">${t('close')}</button>
</div>
</div>
`;
document.body.appendChild(menu);
}
window.showLanguageModal = showLanguageModal;

View File

@@ -54,6 +54,9 @@
MindShift
</div>
<div class="header-actions">
<button class="nav-item" onclick="showLanguageModal()" style="color: var(--primary);">
<span class="material-icons">language</span>
</button>
<div style="position: relative;">
<button class="nav-item" onclick="toggleNotifications()" style="color: white;">
<span class="material-icons">notifications</span>

View File

@@ -1292,3 +1292,72 @@ textarea:focus, input[type="text"]:focus {
.hold .breath-circle-main { background: rgba(255, 235, 59, 0.3); }
.exhale .breath-circle-main { background: rgba(255, 107, 107, 0.3); }
/* RTL Support Overrides */
html[dir="rtl"] {
text-align: right;
--direction: rtl;
}
html[dir="rtl"] .fab {
right: auto;
left: 24px;
}
html[dir="rtl"] .notification-badge {
right: auto;
left: 8px;
}
html[dir="rtl"] .notification-dropdown {
right: auto;
left: 0;
}
html[dir="rtl"] .card-title::after {
transform-origin: right;
left: auto;
right: 0;
}
html[dir="rtl"] .switch-form a {
margin-left: 0;
margin-right: 4px;
}
html[dir="rtl"] .form-group label {
margin-left: 0;
margin-right: 4px;
}
html[dir="rtl"] .guided-controls {
right: auto;
left: 20px;
}
/* Adjust history item hover transform for RTL */
html[dir="rtl"] .history-item:hover {
transform: translateX(-5px);
}
/* Adjust emotion value alignment */
html[dir="rtl"] .emotion-value {
text-align: left;
}
/* Adjust desktop sidebar for RTL */
@media (min-width: 1024px) {
html[dir="rtl"] .bottom-nav {
left: auto;
right: 20px;
}
html[dir="rtl"] .main-content {
margin-left: 0;
margin-right: 120px;
}
html[dir="rtl"] .nav-item.active::after {
left: auto;
right: 0;
}
}

View File

@@ -0,0 +1,401 @@
export const translations = {
en: {
// Meta
"app_title": "MindShift",
"app_subtitle": "Your personal CBT companion",
"init_loading": "Initializing MindShift...",
"init_error": "Error Loading App",
"proactive_badge": "✨ Time for a vibe check?",
// Navigation
"nav_home": "Home",
"nav_mood": "Mood",
"nav_thoughts": "Thoughts",
"nav_gratitude": "Gratitude",
"nav_progress": "Progress",
// Auth
"auth_welcome": "Welcome to MindShift",
"auth_login": "Login",
"auth_register": "Register",
"auth_name": "Name",
"auth_email": "Email",
"auth_password": "Password",
"auth_no_account": "Don't have an account?",
"auth_has_account": "Already have an account?",
"auth_login_failed": "Login failed",
"auth_reg_failed": "Registration failed",
// Home
"home_welcome": "Welcome Back! 🌟",
"home_subtitle": "Ready to shift your mind?",
"home_log_mood": "Log Mood",
"home_record_thought": "Record Thought",
"home_gratitude": "Gratitude",
"home_daily_vibe": "Daily Vibe Check 📊",
"home_quick_relief": "Quick Relief 🌿",
"home_breathe": "Breathe",
"home_relax": "Relax",
"home_stats_mood": "Today's Mood",
"home_stats_sessions": "Sessions",
"home_stats_avg": "Weekly Avg",
"home_stats_gratitude": "Gratitude",
"home_retry": "Tap to Retry",
// Mood
"mood_title": "How are you feeling?",
"mood_joy": "Joy",
"mood_peace": "Peace",
"mood_energy": "Energy",
"mood_anxiety": "Anxiety",
"mood_sadness": "Sadness",
"mood_anger": "Anger",
"mood_intensity": "Intensity",
"mood_notes_placeholder": "Any thoughts?",
"mood_save": "Save Mood",
"mood_saved_success": "Mood tracked successfully!",
"mood_select_warning": "Please select a mood",
// Thoughts
"thought_title": "Thought Record 🧠",
"thought_situation": "Situation (Who, what, where, when?)",
"thought_automatic": "Automatic Thoughts (What went through your mind?)",
"thought_emotions": "Emotions",
"thought_add_emotion": "+ Add Emotion",
"thought_evidence": "Evidence For/Against",
"thought_alternative": "Alternative Thought",
"thought_save": "Save Record",
"thought_saved_success": "Thought record saved successfully!",
"thought_fill_warning": "Please fill in at least the situation and thoughts",
// Gratitude
"gratitude_title": "Gratitude Journal 🙏",
"gratitude_intro": "List 3 things you are grateful for today:",
"gratitude_placeholder": "I am grateful for...",
"gratitude_add": "+ Add Another",
"gratitude_save": "Save Entry",
"gratitude_saved_success": "Gratitude entries saved successfully!",
"gratitude_empty_warning": "Please add at least one gratitude entry",
// Progress & History
"progress_title": "Your Progress 📈",
"progress_weekly": "Weekly Mood 📅",
"progress_history": "Recent History 📜",
"history_tab_moods": "Moods",
"history_tab_thoughts": "Thoughts",
"history_tab_gratitude": "Gratitude",
"history_empty_mood": "No mood entries yet. Start tracking! 📝",
"history_empty_thoughts": "No thought records yet. 🧠",
"history_empty_gratitude": "No gratitude entries yet. 🙏",
"history_select_prompt": "Select a category to view history",
// Quick Actions
"quick_title": "Quick Actions ⚡",
"quick_relax_now": "Relax Now",
"close": "Close",
// Guided Relaxation (Grounding)
"guided_sight_title": "Sight",
"guided_sight_instruction": "Look around you.",
"guided_sight_sub": "Find 5 things you can see.",
"guided_touch_title": "Touch",
"guided_touch_instruction": "Feel the textures.",
"guided_touch_sub": "Find 4 things you can touch.",
"guided_sound_title": "Sound",
"guided_sound_instruction": "Listen carefully.",
"guided_sound_sub": "Identify 3 sounds you hear.",
"guided_smell_title": "Smell",
"guided_smell_instruction": "Breathe in deep.",
"guided_smell_sub": "Notice 2 things you can smell.",
"guided_taste_title": "Taste",
"guided_taste_instruction": "Focus on your mouth.",
"guided_taste_sub": "Find 1 thing you can taste.",
"guided_found_btn": "I found one",
"guided_complete_title": "You did great!",
"guided_complete_sub": "Feeling more grounded?",
"guided_complete_btn": "Complete",
// Smart Breathing
"breath_balance": "Balance",
"breath_relax": "Relax",
"breath_focus": "Focus",
"breath_in": "Breathe In",
"breath_out": "Breathe Out",
"breath_hold": "Hold",
"breath_ready": "Get Ready...",
"breath_sit": "Sit comfortably",
"breath_complete": "Breathing session complete! 🌬️",
// Notifications
"notifications_empty": "No notifications",
"just_now": "Just now",
"ago_m": "m ago",
"ago_h": "h ago"
},
ru: {
// Meta
"app_title": "MindShift",
"app_subtitle": "Ваш личный помощник КПТ",
"init_loading": "Загрузка MindShift...",
"init_error": "Ошибка загрузки",
"proactive_badge": "✨ Время проверить настроение?",
// Navigation
"nav_home": "Главная",
"nav_mood": "Настроение",
"nav_thoughts": "Мысли",
"nav_gratitude": "Благодарность",
"nav_progress": "Прогресс",
// Auth
"auth_welcome": "Добро пожаловать в MindShift",
"auth_login": "Войти",
"auth_register": "Регистрация",
"auth_name": "Имя",
"auth_email": "Email",
"auth_password": "Пароль",
"auth_no_account": "Нет аккаунта?",
"auth_has_account": "Уже есть аккаунт?",
"auth_login_failed": "Ошибка входа",
"auth_reg_failed": "Ошибка регистрации",
// Home
"home_welcome": "С возвращением! 🌟",
"home_subtitle": "Готовы изменить мышление?",
"home_log_mood": "Настроение",
"home_record_thought": "Запись мыслей",
"home_gratitude": "Благодарность",
"home_daily_vibe": "Статистика дня 📊",
"home_quick_relief": "Быстрая помощь 🌿",
"home_breathe": "Дыхание",
"home_relax": "Релакс",
"home_stats_mood": "Настроение",
"home_stats_sessions": "Сессии",
"home_stats_avg": "Ср. за неделю",
"home_stats_gratitude": "Благодарности",
"home_retry": "Повторить",
// Mood
"mood_title": "Как вы себя чувствуете?",
"mood_joy": "Радость",
"mood_peace": "Покой",
"mood_energy": "Энергия",
"mood_anxiety": "Тревога",
"mood_sadness": "Грусть",
"mood_anger": "Гнев",
"mood_intensity": "Интенсивность",
"mood_notes_placeholder": "О чем думаете?",
"mood_save": "Сохранить",
"mood_saved_success": "Настроение сохранено!",
"mood_select_warning": "Выберите настроение",
// Thoughts
"thought_title": "Дневник мыслей 🧠",
"thought_situation": "Ситуация (Кто, что, где, когда?)",
"thought_automatic": "Автоматические мысли (Что пришло в голову?)",
"thought_emotions": "Эмоции",
"thought_add_emotion": "+ Добавить эмоцию",
"thought_evidence": "За и Против",
"thought_alternative": "Альтернативная мысль",
"thought_save": "Сохранить запись",
"thought_saved_success": "Запись сохранена!",
"thought_fill_warning": "Заполните ситуацию и мысли",
// Gratitude
"gratitude_title": "Дневник благодарности 🙏",
"gratitude_intro": "3 вещи, за которые вы благодарны:",
"gratitude_placeholder": "Я благодарен за...",
"gratitude_add": "+ Добавить еще",
"gratitude_save": "Сохранить",
"gratitude_saved_success": "Записи сохранены!",
"gratitude_empty_warning": "Добавьте хотя бы одну запись",
// Progress & History
"progress_title": "Ваш прогресс 📈",
"progress_weekly": "Настроение за неделю 📅",
"progress_history": "История 📜",
"history_tab_moods": "Настроение",
"history_tab_thoughts": "Мысли",
"history_tab_gratitude": "Благодарность",
"history_empty_mood": "Нет записей. Начните отслеживать! 📝",
"history_empty_thoughts": "Нет записей мыслей. 🧠",
"history_empty_gratitude": "Нет записей благодарности. 🙏",
"history_select_prompt": "Выберите категорию для просмотра",
// Quick Actions
"quick_title": "Быстрые действия ⚡",
"quick_relax_now": "Релакс сейчас",
"close": "Закрыть",
// Guided Relaxation
"guided_sight_title": "Зрение",
"guided_sight_instruction": "Оглянитесь вокруг.",
"guided_sight_sub": "Найдите 5 вещей, которые вы видите.",
"guided_touch_title": "Осязание",
"guided_touch_instruction": "Почувствуйте текстуры.",
"guided_touch_sub": "Найдите 4 вещи, которые можно потрогать.",
"guided_sound_title": "Слух",
"guided_sound_instruction": "Прислушайтесь.",
"guided_sound_sub": "Найдите 3 звука, которые вы слышите.",
"guided_smell_title": "Обоняние",
"guided_smell_instruction": "Сделайте глубокий вдох.",
"guided_smell_sub": "Найдите 2 запаха.",
"guided_taste_title": "Вкус",
"guided_taste_instruction": "Сосредоточьтесь на вкусе.",
"guided_taste_sub": "Найдите 1 вещь, которую можно попробовать.",
"guided_found_btn": "Найдено",
"guided_complete_title": "Отлично!",
"guided_complete_sub": "Чувствуете себя спокойнее?",
"guided_complete_btn": "Завершить",
// Smart Breathing
"breath_balance": "Баланс",
"breath_relax": "Релакс",
"breath_focus": "Фокус",
"breath_in": "Вдох",
"breath_out": "Выдох",
"breath_hold": "Пауза",
"breath_ready": "Приготовьтесь...",
"breath_sit": "Сядьте удобно",
"breath_complete": "Сессия завершена! 🌬️",
// Notifications
"notifications_empty": "Нет уведомлений",
"just_now": "Только что",
"ago_m": "м назад",
"ago_h": "ч назад"
},
he: {
// Meta
"app_title": "MindShift",
"app_subtitle": "המאמן האישי שלך ל-CBT",
"init_loading": "טוען MindShift...",
"init_error": "שגיאה בטעינת האפליקציה",
"proactive_badge": "✨ זמן לבדיקת מצב רוח?",
// Navigation
"nav_home": "בית",
"nav_mood": "מצב רוח",
"nav_thoughts": "מחשבות",
"nav_gratitude": "הוקרת תודה",
"nav_progress": "התקדמות",
// Auth
"auth_welcome": "ברוכים הבאים ל-MindShift",
"auth_login": "התחברות",
"auth_register": "הרשמה",
"auth_name": "שם",
"auth_email": "אימייל",
"auth_password": "סיסמה",
"auth_no_account": "אין לך חשבון?",
"auth_has_account": "יש לך כבר חשבון?",
"auth_login_failed": "התחברות נכשלה",
"auth_reg_failed": "הרשמה נכשלה",
// Home
"home_welcome": "ברוכים השבים! 🌟",
"home_subtitle": "מוכנים לשינוי מחשבתי?",
"home_log_mood": "יומן מצב רוח",
"home_record_thought": "יומן מחשבות",
"home_gratitude": "הוקרת תודה",
"home_daily_vibe": "בדיקה יומית 📊",
"home_quick_relief": "הקלה מהירה 🌿",
"home_breathe": "נשימה",
"home_relax": "הרפיה",
"home_stats_mood": "מצב רוח",
"home_stats_sessions": "אימונים",
"home_stats_avg": "ממוצע שבועי",
"home_stats_gratitude": "תודות",
"home_retry": "לחץ לנסות שוב",
// Mood
"mood_title": "איך אתם מרגישים?",
"mood_joy": "שמחה",
"mood_peace": "שלווה",
"mood_energy": "אנרגיה",
"mood_anxiety": "חרדה",
"mood_sadness": "עצב",
"mood_anger": "כעס",
"mood_intensity": "עוצמה",
"mood_notes_placeholder": "מחשבות נוספות?",
"mood_save": "שמור מצב רוח",
"mood_saved_success": "מצב רוח נשמר בהצלחה!",
"mood_select_warning": "אנא בחר מצב רוח",
// Thoughts
"thought_title": "יומן מחשבות 🧠",
"thought_situation": "סיטואציה (מי, מה, איפה, מתי?)",
"thought_automatic": "מחשבות אוטומטיות (מה עבר בראש?)",
"thought_emotions": "רגשות",
"thought_add_emotion": "+ הוסף רגש",
"thought_evidence": "בעד ונגד",
"thought_alternative": "מחשבה אלטרנטיבית",
"thought_save": "שמור רשומה",
"thought_saved_success": "הרשומה נשמרה בהצלחה!",
"thought_fill_warning": "אנא מלא לפחות את הסיטואציה והמחשבות",
// Gratitude
"gratitude_title": "יומן הוקרת תודה 🙏",
"gratitude_intro": "3 דברים שאתם מוקירים עליהם תודה היום:",
"gratitude_placeholder": "אני מוקיר תודה על...",
"gratitude_add": "+ הוסף עוד",
"gratitude_save": "שמור",
"gratitude_saved_success": "נשמר בהצלחה!",
"gratitude_empty_warning": "אנא הוסף לפחות פריט אחד",
// Progress & History
"progress_title": "ההתקדמות שלך 📈",
"progress_weekly": "מצב רוח שבועי 📅",
"progress_history": "היסטוריה 📜",
"history_tab_moods": "מצבי רוח",
"history_tab_thoughts": "מחשבות",
"history_tab_gratitude": "תודות",
"history_empty_mood": "אין עדיין רשומות. התחילו לתעד! 📝",
"history_empty_thoughts": "אין עדיין רשומות מחשבה. 🧠",
"history_empty_gratitude": "אין עדיין רשומות תודה. 🙏",
"history_select_prompt": "בחר קטגוריה לצפייה בהיסטוריה",
// Quick Actions
"quick_title": "פעולות מהירות ⚡",
"quick_relax_now": "הרפיה עכשיו",
"close": "סגור",
// Guided Relaxation
"guided_sight_title": "ראייה",
"guided_sight_instruction": "הביטו סביבכם.",
"guided_sight_sub": "מצאו 5 דברים שאתם רואים.",
"guided_touch_title": "מגע",
"guided_touch_instruction": "הרגישו מרקמים.",
"guided_touch_sub": "מצאו 4 דברים שאפשר לגעת בהם.",
"guided_sound_title": "שמיעה",
"guided_sound_instruction": "הקשיבו בתשומת לב.",
"guided_sound_sub": "זהו 3 צלילים שאתם שומעים.",
"guided_smell_title": "ריח",
"guided_smell_instruction": "קחו נשימה עמוקה.",
"guided_smell_sub": "שימו לב ל-2 ריחות.",
"guided_taste_title": "טעם",
"guided_taste_instruction": "התמקדו בפה.",
"guided_taste_sub": "מצאו דבר 1 שניתן לטעום.",
"guided_found_btn": "מצאתי",
"guided_complete_title": "כל הכבוד!",
"guided_complete_sub": "מרגישים מקורקעים יותר?",
"guided_complete_btn": "סיים",
// Smart Breathing
"breath_balance": "איזון",
"breath_relax": "רוגע",
"breath_focus": "מיקוד",
"breath_in": "שאיפה",
"breath_out": "נשיפה",
"breath_hold": "החזקה",
"breath_ready": "היכונו...",
"breath_sit": "שבו בנוחות",
"breath_complete": "אימון נשימה הסתיים! 🌬️",
// Notifications
"notifications_empty": "אין התראות",
"just_now": "כרגע",
"ago_m": "לפני דק'",
"ago_h": "לפני שע'"
}
};