полная локализация проекта
This commit is contained in:
parent
2bf8cb05b2
commit
334d77a2ef
@ -6,6 +6,7 @@ import { initializeUserId, getCurrentUserId } from './constants/user';
|
|||||||
import { trackScreenView } from './services/analyticsService';
|
import { trackScreenView } from './services/analyticsService';
|
||||||
import customAnalyticsService from './services/customAnalyticsService';
|
import customAnalyticsService from './services/customAnalyticsService';
|
||||||
import { BalanceProvider } from './contexts/BalanceContext';
|
import { BalanceProvider } from './contexts/BalanceContext';
|
||||||
|
import { getTranslation } from './constants/translations';
|
||||||
|
|
||||||
// Ленивая загрузка компонентов
|
// Ленивая загрузка компонентов
|
||||||
const OnboardingWelcome = lazy(() => import('./screens/onboarding/OnboardingWelcome'));
|
const OnboardingWelcome = lazy(() => import('./screens/onboarding/OnboardingWelcome'));
|
||||||
@ -29,7 +30,7 @@ const LoadingScreen = () => (
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: '100vh'
|
height: '100vh'
|
||||||
}}>
|
}}>
|
||||||
Загрузка...
|
{getTranslation('loading')}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styles from './GenerateButton.module.css';
|
import styles from './GenerateButton.module.css';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface GenerateButtonProps {
|
interface GenerateButtonProps {
|
||||||
onGenerate: () => void;
|
onGenerate: () => void;
|
||||||
@ -13,8 +14,8 @@ const GenerateButton: React.FC<GenerateButtonProps> = ({ onGenerate, tokenCount
|
|||||||
onClick={onGenerate}
|
onClick={onGenerate}
|
||||||
>
|
>
|
||||||
<span className={styles.generateButtonText}>
|
<span className={styles.generateButtonText}>
|
||||||
Начать генерацию
|
{getTranslation('start_generation')}
|
||||||
<span className={styles.tokenCount}>{tokenCount} токенов</span>
|
<span className={styles.tokenCount}>{getTranslation('tokens_count', tokenCount)}</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import { ButtonBlock } from '../../types/blocks';
|
import { ButtonBlock } from '../../types/blocks';
|
||||||
import SquareButton from './SquareButton';
|
import SquareButton from './SquareButton';
|
||||||
import styles from './GridButtonsBlock.module.css';
|
import styles from './GridButtonsBlock.module.css';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface GridButtonsBlockProps {
|
interface GridButtonsBlockProps {
|
||||||
block: ButtonBlock;
|
block: ButtonBlock;
|
||||||
@ -61,7 +62,7 @@ const GridButtonsBlock: React.FC<GridButtonsBlockProps> = ({ block, onAction, is
|
|||||||
className={styles.showMoreButton}
|
className={styles.showMoreButton}
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
>
|
>
|
||||||
{expanded ? 'Свернуть' : 'Показать больше'}
|
{expanded ? getTranslation('collapse') : getTranslation('show_more')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import styles from './TextInputBlock.module.css';
|
import styles from './TextInputBlock.module.css';
|
||||||
import { TextInputBlock as TextInputBlockType } from '../../types/blocks';
|
import { TextInputBlock as TextInputBlockType } from '../../types/blocks';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface TextInputBlockProps {
|
interface TextInputBlockProps {
|
||||||
block: TextInputBlockType;
|
block: TextInputBlockType;
|
||||||
@ -40,7 +41,7 @@ const TextInputBlock: React.FC<TextInputBlockProps> = ({ block, visible, onTextC
|
|||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
className={styles.input}
|
className={styles.input}
|
||||||
placeholder="Опишите, какой стикер вы хотите получить..."
|
placeholder={getTranslation('sticker_description_placeholder')}
|
||||||
rows={3}
|
rows={3}
|
||||||
value={text}
|
value={text}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import styles from './UploadPhotoBlock.module.css';
|
import styles from './UploadPhotoBlock.module.css';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface UploadPhotoBlockProps {
|
interface UploadPhotoBlockProps {
|
||||||
onPhotoSelect?: (file: File) => void;
|
onPhotoSelect?: (file: File) => void;
|
||||||
@ -64,17 +65,17 @@ const UploadPhotoBlock: React.FC<UploadPhotoBlockProps> = ({ onPhotoSelect, prev
|
|||||||
>
|
>
|
||||||
{previewUrl ? (
|
{previewUrl ? (
|
||||||
<div className={styles.preview}>
|
<div className={styles.preview}>
|
||||||
<img src={previewUrl} alt="Preview" className={styles.previewImage} />
|
<img src={previewUrl} alt={getTranslation('preview_alt')} className={styles.previewImage} />
|
||||||
<div className={styles.changeOverlay}>
|
<div className={styles.changeOverlay}>
|
||||||
<span className={styles.changeText}>Изменить фото</span>
|
<span className={styles.changeText}>{getTranslation('change_photo')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className={styles.icon}>📷</div>
|
<div className={styles.icon}>📷</div>
|
||||||
<div className={styles.text}>
|
<div className={styles.text}>
|
||||||
<span className={styles.title}>Загрузите фото</span>
|
<span className={styles.title}>{getTranslation('upload_photo_block_title')}</span>
|
||||||
<span className={styles.subtitle}>Перетащите или нажмите для выбора</span>
|
<span className={styles.subtitle}>{getTranslation('upload_photo_block_subtitle')}</span>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import BlockRenderer from '../blocks/BlockRenderer';
|
import BlockRenderer from '../blocks/BlockRenderer';
|
||||||
import { homeScreenConfig } from '../../config/homeScreen';
|
import { homeScreenConfig } from '../../config/homeScreen';
|
||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
@ -16,6 +17,7 @@ const MainActions: React.FC<MainActionsProps> = ({
|
|||||||
onSendFeedback,
|
onSendFeedback,
|
||||||
onOpenTelegramBot
|
onOpenTelegramBot
|
||||||
}) => {
|
}) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
// Находим блок верхних кнопок и разделитель в конфигурации
|
// Находим блок верхних кнопок и разделитель в конфигурации
|
||||||
const actionsBlock = homeScreenConfig.homeScreen.blocks.find(block => block.id === 'mainActions');
|
const actionsBlock = homeScreenConfig.homeScreen.blocks.find(block => block.id === 'mainActions');
|
||||||
const dividerBlock = homeScreenConfig.homeScreen.blocks.find(block => block.id === 'mainDivider');
|
const dividerBlock = homeScreenConfig.homeScreen.blocks.find(block => block.id === 'mainDivider');
|
||||||
@ -45,6 +47,15 @@ const MainActions: React.FC<MainActionsProps> = ({
|
|||||||
});
|
});
|
||||||
onOpenTelegramBot();
|
onOpenTelegramBot();
|
||||||
}
|
}
|
||||||
|
} else if (actionType === 'route') {
|
||||||
|
// Отслеживаем событие нажатия на кнопку навигации
|
||||||
|
customAnalyticsService.trackEvent({
|
||||||
|
telegram_id: getCurrentUserId(),
|
||||||
|
event_category: 'ui_interaction',
|
||||||
|
event_name: 'instruction_button_click',
|
||||||
|
unit: actionValue
|
||||||
|
});
|
||||||
|
navigate(actionValue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { tokenPacks } from '../../constants/tokenPacks';
|
|||||||
import NotificationModal from '../shared/NotificationModal';
|
import NotificationModal from '../shared/NotificationModal';
|
||||||
import { useBalance } from '../../contexts/BalanceContext';
|
import { useBalance } from '../../contexts/BalanceContext';
|
||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
import styles from './Header.module.css';
|
import styles from './Header.module.css';
|
||||||
|
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
@ -55,7 +56,7 @@ const Header: React.FC = () => {
|
|||||||
<div className={styles.avatar}>
|
<div className={styles.avatar}>
|
||||||
<img
|
<img
|
||||||
src={user.avatarUrl}
|
src={user.avatarUrl}
|
||||||
alt="Avatar"
|
alt={getTranslation('avatar_alt')}
|
||||||
className={styles.avatarImage}
|
className={styles.avatarImage}
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
e.currentTarget.onerror = null;
|
e.currentTarget.onerror = null;
|
||||||
@ -80,10 +81,10 @@ const Header: React.FC = () => {
|
|||||||
});
|
});
|
||||||
setShowTokensModal(true);
|
setShowTokensModal(true);
|
||||||
}}
|
}}
|
||||||
title="Нажмите чтобы пополнить баланс"
|
title={getTranslation('add_balance_title')}
|
||||||
>
|
>
|
||||||
<span className={styles.balanceIcon}>
|
<span className={styles.balanceIcon}>
|
||||||
<img src={images.tokenIcon} alt="Токены" className={styles.tokenImage} />
|
<img src={images.tokenIcon} alt={getTranslation('tokens_alt')} className={styles.tokenImage} />
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.balanceValue}>
|
<span className={styles.balanceValue}>
|
||||||
{balance}
|
{balance}
|
||||||
@ -111,8 +112,8 @@ const Header: React.FC = () => {
|
|||||||
updateBalance();
|
updateBalance();
|
||||||
|
|
||||||
// Показываем модальное окно с информацией об успешной оплате
|
// Показываем модальное окно с информацией об успешной оплате
|
||||||
setNotificationTitle('Оплата успешна!');
|
setNotificationTitle(getTranslation('payment_success'));
|
||||||
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов.`);
|
setNotificationMessage(getTranslation('tokens_purchased', pack.tokens + pack.bonusTokens));
|
||||||
setShowNotificationModal(true);
|
setShowNotificationModal(true);
|
||||||
} else {
|
} else {
|
||||||
// Если данные не получены, делаем запрос на получение данных пользователя
|
// Если данные не получены, делаем запрос на получение данных пользователя
|
||||||
@ -124,8 +125,8 @@ const Header: React.FC = () => {
|
|||||||
updateBalance();
|
updateBalance();
|
||||||
|
|
||||||
// Показываем модальное окно с информацией об успешной оплате
|
// Показываем модальное окно с информацией об успешной оплате
|
||||||
setNotificationTitle('Оплата успешна!');
|
setNotificationTitle(getTranslation('payment_success'));
|
||||||
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов. Ваш текущий баланс: ${balance} токенов.`);
|
setNotificationMessage(getTranslation('profile_tokens_purchased', pack.tokens + pack.bonusTokens, balance));
|
||||||
setShowNotificationModal(true);
|
setShowNotificationModal(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка при обновлении данных пользователя:', error);
|
console.error('Ошибка при обновлении данных пользователя:', error);
|
||||||
@ -143,7 +144,7 @@ const Header: React.FC = () => {
|
|||||||
onGalleryClick={() => setShowNotificationModal(false)}
|
onGalleryClick={() => setShowNotificationModal(false)}
|
||||||
onContinueClick={() => setShowNotificationModal(false)}
|
onContinueClick={() => setShowNotificationModal(false)}
|
||||||
showGalleryButton={false}
|
showGalleryButton={false}
|
||||||
continueButtonText="Закрыть"
|
continueButtonText={getTranslation('close')}
|
||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useNavigate, useLocation } from 'react-router-dom';
|
|||||||
import styles from './Navigation.module.css';
|
import styles from './Navigation.module.css';
|
||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../../constants/user';
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
const NavigationComponent: React.FC = () => {
|
const NavigationComponent: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -32,7 +33,7 @@ const NavigationComponent: React.FC = () => {
|
|||||||
<path d="M9 22V12h6v10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
<path d="M9 22V12h6v10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.label}>Главная</span>
|
<span className={styles.label}>{getTranslation('nav_home')}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -54,7 +55,7 @@ const NavigationComponent: React.FC = () => {
|
|||||||
<path d="M20 16l-4-4L6 20" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
<path d="M20 16l-4-4L6 20" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.label}>Галерея</span>
|
<span className={styles.label}>{getTranslation('nav_gallery')}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -75,7 +76,7 @@ const NavigationComponent: React.FC = () => {
|
|||||||
<path d="M4 8l8 4 8-4M12 12v8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
<path d="M4 8l8 4 8-4M12 12v8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.label}>Стикерпаки</span>
|
<span className={styles.label}>{getTranslation('nav_sticker_packs')}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -96,7 +97,7 @@ const NavigationComponent: React.FC = () => {
|
|||||||
<circle cx="12" cy="7" r="4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
<circle cx="12" cy="7" r="4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.label}>Профиль</span>
|
<span className={styles.label}>{getTranslation('nav_profile')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import styles from './NotificationModal.module.css';
|
import styles from './NotificationModal.module.css';
|
||||||
import emojiStyles from './EmojiPickerModal.module.css';
|
import emojiStyles from './EmojiPickerModal.module.css';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface EmojiPickerModalProps {
|
interface EmojiPickerModalProps {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
@ -63,7 +64,7 @@ const EmojiPickerModal: React.FC<EmojiPickerModalProps> = ({
|
|||||||
<div className={styles.overlay}>
|
<div className={styles.overlay}>
|
||||||
<div className={styles.modal}>
|
<div className={styles.modal}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className={styles.title}>Выберите эмодзи</div>
|
<div className={styles.title}>{getTranslation('emoji_picker_title')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={emojiStyles.emojiContainer}>
|
<div className={emojiStyles.emojiContainer}>
|
||||||
@ -85,13 +86,13 @@ const EmojiPickerModal: React.FC<EmojiPickerModalProps> = ({
|
|||||||
className={`${styles.button} ${styles.secondaryButton}`}
|
className={`${styles.button} ${styles.secondaryButton}`}
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
>
|
>
|
||||||
Отмена
|
{getTranslation('emoji_picker_cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`${styles.button} ${styles.primaryButton}`}
|
className={`${styles.button} ${styles.primaryButton}`}
|
||||||
onClick={handleApply}
|
onClick={handleApply}
|
||||||
>
|
>
|
||||||
Применить
|
{getTranslation('emoji_picker_apply')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useRef, useEffect, memo } from 'react';
|
import React, { useState, useRef, useEffect, memo } from 'react';
|
||||||
import styles from './NotificationModal.module.css'; // Используем существующие стили
|
import styles from './NotificationModal.module.css'; // Используем существующие стили
|
||||||
import feedbackStyles from './FeedbackModal.module.css'; // Дополнительные стили для формы
|
import feedbackStyles from './FeedbackModal.module.css'; // Дополнительные стили для формы
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
export interface FeedbackData {
|
export interface FeedbackData {
|
||||||
text: string;
|
text: string;
|
||||||
@ -41,9 +42,9 @@ const ImagePreview = memo(({
|
|||||||
return (
|
return (
|
||||||
<div className={feedbackStyles.imagePreview}>
|
<div className={feedbackStyles.imagePreview}>
|
||||||
{url ? (
|
{url ? (
|
||||||
<img src={url} alt={`Preview ${index}`} />
|
<img src={url} alt={getTranslation('preview_image_alt', index)} />
|
||||||
) : (
|
) : (
|
||||||
<div className={feedbackStyles.imageLoading}>Загрузка...</div>
|
<div className={feedbackStyles.imageLoading}>{getTranslation('loading_image')}</div>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
className={feedbackStyles.removeImageButton}
|
className={feedbackStyles.removeImageButton}
|
||||||
@ -101,7 +102,7 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
setImages(prevImages => [...prevImages, file]);
|
setImages(prevImages => [...prevImages, file]);
|
||||||
} else {
|
} else {
|
||||||
console.error('Выбранный файл не является изображением');
|
console.error('Выбранный файл не является изображением');
|
||||||
setError('Пожалуйста, выберите файл изображения (JPEG, PNG и т.д.)');
|
setError(getTranslation('select_image_file'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при обработке выбранного файла:', err);
|
console.error('Ошибка при обработке выбранного файла:', err);
|
||||||
setError('Произошла ошибка при выборе файла. Пожалуйста, попробуйте еще раз.');
|
setError(getTranslation('file_selection_error'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
// Проверяем, что есть текст или изображения
|
// Проверяем, что есть текст или изображения
|
||||||
if (!text.trim() && images.length === 0) {
|
if (!text.trim() && images.length === 0) {
|
||||||
setError('Пожалуйста, введите текст или добавьте изображение');
|
setError(getTranslation('enter_text_or_add_image'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,11 +151,11 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
if (result) {
|
if (result) {
|
||||||
handleClose();
|
handleClose();
|
||||||
} else {
|
} else {
|
||||||
setError('Не удалось отправить обратную связь. Пожалуйста, попробуйте позже.');
|
setError(getTranslation('failed_to_send_feedback'));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при отправке обратной связи:', err);
|
console.error('Ошибка при отправке обратной связи:', err);
|
||||||
setError('Произошла ошибка при отправке. Пожалуйста, попробуйте позже.');
|
setError(getTranslation('error_sending_feedback'));
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@ -168,16 +169,16 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
{isLoading && <span className={styles.spinner}></span>}
|
{isLoading && <span className={styles.spinner}></span>}
|
||||||
Обратная связь
|
{getTranslation('feedback_title')}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.message}>Расскажите нам о проблеме или предложении</div>
|
<div className={styles.message}>{getTranslation('feedback_subtitle')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Поле для ввода текста */}
|
{/* Поле для ввода текста */}
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
className={feedbackStyles.textarea}
|
className={feedbackStyles.textarea}
|
||||||
placeholder="Опишите вашу проблему или предложение..."
|
placeholder={getTranslation('feedback_placeholder')}
|
||||||
value={text}
|
value={text}
|
||||||
onChange={(e) => setText(e.target.value)}
|
onChange={(e) => setText(e.target.value)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
@ -193,7 +194,7 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
Добавить изображение
|
{getTranslation('add_image')}
|
||||||
</button>
|
</button>
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
@ -235,7 +236,7 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
Отмена
|
{getTranslation('cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`${styles.button} ${styles.primaryButton}`}
|
className={`${styles.button} ${styles.primaryButton}`}
|
||||||
@ -243,7 +244,7 @@ const FeedbackModal: React.FC<FeedbackModalProps> = ({ isVisible, onClose, onSub
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{isLoading ? 'Отправка...' : 'Отправить'}
|
{isLoading ? getTranslation('generation_sending') : getTranslation('accept')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
import styles from './ImageViewer.module.css';
|
import styles from './ImageViewer.module.css';
|
||||||
|
|
||||||
interface ImageViewerProps {
|
interface ImageViewerProps {
|
||||||
@ -96,7 +97,7 @@ const ImageViewer: React.FC<ImageViewerProps> = ({ imageUrl, onClose }) => {
|
|||||||
</button>
|
</button>
|
||||||
<img
|
<img
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
alt="Полноэкранный просмотр"
|
alt={getTranslation('fullscreen_view_alt')}
|
||||||
className={styles.fullImage}
|
className={styles.fullImage}
|
||||||
style={{ transform: `translateY(-${translateY}px)` }}
|
style={{ transform: `translateY(-${translateY}px)` }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styles from './NotificationModal.module.css';
|
import styles from './NotificationModal.module.css';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface NotificationModalProps {
|
interface NotificationModalProps {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
@ -28,8 +29,8 @@ const NotificationModal: React.FC<NotificationModalProps> = ({
|
|||||||
onContinueClick,
|
onContinueClick,
|
||||||
showGalleryButton = true, // По умолчанию кнопка "В галерею" видима
|
showGalleryButton = true, // По умолчанию кнопка "В галерею" видима
|
||||||
showButtons = true, // По умолчанию все кнопки видимы
|
showButtons = true, // По умолчанию все кнопки видимы
|
||||||
continueButtonText = 'Продолжить', // По умолчанию текст кнопки "Продолжить"
|
continueButtonText = getTranslation('continue'), // По умолчанию текст кнопки "Продолжить"
|
||||||
galleryButtonText = 'В галерею', // По умолчанию текст кнопки "В галерею"
|
galleryButtonText = getTranslation('gallery'), // По умолчанию текст кнопки "В галерею"
|
||||||
isPrimaryGalleryButton = true, // По умолчанию кнопка "В галерею" синяя
|
isPrimaryGalleryButton = true, // По умолчанию кнопка "В галерею" синяя
|
||||||
taskId,
|
taskId,
|
||||||
generatedImageUrl,
|
generatedImageUrl,
|
||||||
@ -52,13 +53,13 @@ const NotificationModal: React.FC<NotificationModalProps> = ({
|
|||||||
{isGenerating && queuePosition !== undefined && queuePosition <= 2 && (
|
{isGenerating && queuePosition !== undefined && queuePosition <= 2 && (
|
||||||
<div className={styles.generationPreview}>
|
<div className={styles.generationPreview}>
|
||||||
{generatedImageUrl ? (
|
{generatedImageUrl ? (
|
||||||
<img src={generatedImageUrl} alt="Сгенерированный стикер" className={styles.generatedImage} />
|
<img src={generatedImageUrl} alt={getTranslation('generated_sticker')} className={styles.generatedImage} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className={styles.magicRipple}>
|
<div className={styles.magicRipple}>
|
||||||
{/* Анимация магической ряби */}
|
{/* Анимация магической ряби */}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.generationText}>Генерация...</div>
|
<div className={styles.generationText}>{getTranslation('generating')}</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { TokenPack } from '../../constants/tokenPacks';
|
|||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../../constants/user';
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
import styles from './TokenPackCard.module.css';
|
import styles from './TokenPackCard.module.css';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface TokenPackCardProps extends TokenPack {
|
interface TokenPackCardProps extends TokenPack {
|
||||||
onBuy: () => void;
|
onBuy: () => void;
|
||||||
@ -13,6 +14,7 @@ interface TokenPackCardProps extends TokenPack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
||||||
|
id,
|
||||||
title,
|
title,
|
||||||
tokens,
|
tokens,
|
||||||
bonusTokens,
|
bonusTokens,
|
||||||
@ -43,38 +45,38 @@ const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
|||||||
>
|
>
|
||||||
{isPopular && (
|
{isPopular && (
|
||||||
<div className={`${styles.badge} ${styles.popularBadge}`}>
|
<div className={`${styles.badge} ${styles.popularBadge}`}>
|
||||||
Популярный выбор
|
{getTranslation('token_pack_popular')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isBestValue && (
|
{isBestValue && (
|
||||||
<div className={`${styles.badge} ${styles.bestValueBadge}`}>
|
<div className={`${styles.badge} ${styles.bestValueBadge}`}>
|
||||||
Максимальная выгода
|
{getTranslation('token_pack_best_value')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<h3 className={styles.title}>{title}</h3>
|
<h3 className={styles.title}>{getTranslation(`token_pack_${id}_title`)}</h3>
|
||||||
|
|
||||||
<div className={styles.tokenInfo}>
|
<div className={styles.tokenInfo}>
|
||||||
<img src={images.tokenIcon} alt="Токены" className={styles.tokenIcon} />
|
<img src={images.tokenIcon} alt={getTranslation('token_pack_tokens')} className={styles.tokenIcon} />
|
||||||
<div className={styles.tokenCount}>
|
<div className={styles.tokenCount}>
|
||||||
<span className={styles.baseTokens}>{tokens}</span>
|
<span className={styles.baseTokens}>{tokens}</span>
|
||||||
{bonusTokens > 0 && (
|
{bonusTokens > 0 && (
|
||||||
<span className={styles.bonusTokens}>+{bonusTokens} БОНУС</span>
|
<span className={styles.bonusTokens}>{getTranslation('token_pack_bonus', bonusTokens)}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.stickersCount}>
|
<div className={styles.stickersCount}>
|
||||||
{stickersCount} стикеров
|
{getTranslation('token_pack_stickers_count', stickersCount)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{description && (
|
{description && (
|
||||||
<p className={styles.description}>{description}</p>
|
<p className={styles.description}>{getTranslation(`token_pack_${id}_description`)}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.priceSection}>
|
<div className={styles.priceSection}>
|
||||||
<div className={styles.price}>
|
<div className={styles.price}>
|
||||||
{price} Stars ⭐
|
{price} {getTranslation('token_pack_stars')}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className={styles.buyButton}
|
className={styles.buyButton}
|
||||||
@ -90,7 +92,7 @@ const TokenPackCard: React.FC<TokenPackCardProps> = ({
|
|||||||
onBuy();
|
onBuy();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
КУПИТЬ
|
{getTranslation('token_pack_buy')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useRef, useEffect } from 'react';
|
|||||||
import { tokenPacks } from '../../constants/tokenPacks';
|
import { tokenPacks } from '../../constants/tokenPacks';
|
||||||
import TokenPackCard from './TokenPackCard';
|
import TokenPackCard from './TokenPackCard';
|
||||||
import styles from './TokenPacksList.module.css';
|
import styles from './TokenPacksList.module.css';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface TokenPacksListProps {
|
interface TokenPacksListProps {
|
||||||
onBuyPack: (packId: string) => void;
|
onBuyPack: (packId: string) => void;
|
||||||
@ -32,7 +33,7 @@ const TokenPacksList: React.FC<TokenPacksListProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.container} ${className}`}>
|
<div className={`${styles.container} ${className}`}>
|
||||||
<h2 className={styles.title}>Пакеты токенов</h2>
|
<h2 className={styles.title}>{getTranslation('token_packs_title')}</h2>
|
||||||
<div
|
<div
|
||||||
className={`${styles.list} ${compact ? styles.horizontalScroll : ''}`}
|
className={`${styles.list} ${compact ? styles.horizontalScroll : ''}`}
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { tokenPacks, TokenPack } from '../../constants/tokenPacks';
|
import { tokenPacks, TokenPack } from '../../constants/tokenPacks';
|
||||||
import styles from './TokenPacksModal.module.css';
|
import styles from './TokenPacksModal.module.css';
|
||||||
import TokenPacksList from './TokenPacksList';
|
import TokenPacksList from './TokenPacksList';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
interface TokenPacksModalProps {
|
interface TokenPacksModalProps {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
@ -35,7 +36,7 @@ const TokenPacksModal: React.FC<TokenPacksModalProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.overlay} onClick={onClose}>
|
<div className={styles.overlay} onClick={onClose}>
|
||||||
<div className={styles.modal} onClick={e => e.stopPropagation()}>
|
<div className={styles.modal} onClick={e => e.stopPropagation()}>
|
||||||
<h2 className={styles.title}>Недостаточно токенов для генерации</h2>
|
<h2 className={styles.title}>{getTranslation('token_modal_title')}</h2>
|
||||||
|
|
||||||
<TokenPacksList
|
<TokenPacksList
|
||||||
onBuyPack={onBuyPack}
|
onBuyPack={onBuyPack}
|
||||||
@ -50,7 +51,7 @@ const TokenPacksModal: React.FC<TokenPacksModalProps> = ({
|
|||||||
onShowAllPacks();
|
onShowAllPacks();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Показать все пакеты
|
{getTranslation('token_modal_show_all')}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { AppConfig } from '../types/blocks';
|
import { AppConfig } from '../types/blocks';
|
||||||
import { images } from '../assets';
|
import { images } from '../assets';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
export const homeScreenConfig: AppConfig = {
|
export const homeScreenConfig: AppConfig = {
|
||||||
homeScreen: {
|
homeScreen: {
|
||||||
@ -15,7 +16,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Обратная связь',
|
title: getTranslation('feedback'),
|
||||||
imageUrl: images.feedback,
|
imageUrl: images.feedback,
|
||||||
action: {
|
action: {
|
||||||
type: 'function',
|
type: 'function',
|
||||||
@ -29,7 +30,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#2B9CFF', '#1E88E5']
|
colors: ['#2B9CFF', '#1E88E5']
|
||||||
},
|
},
|
||||||
title: 'Инструкция',
|
title: getTranslation('instruction'),
|
||||||
imageUrl: images.faq,
|
imageUrl: images.faq,
|
||||||
action: {
|
action: {
|
||||||
type: 'route',
|
type: 'route',
|
||||||
@ -43,7 +44,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#8A2BE2', '#9400D3']
|
colors: ['#8A2BE2', '#9400D3']
|
||||||
},
|
},
|
||||||
title: 'ShortsLoader',
|
title: getTranslation('shortsLoader'),
|
||||||
imageUrl: images.shorts,
|
imageUrl: images.shorts,
|
||||||
action: {
|
action: {
|
||||||
type: 'function',
|
type: 'function',
|
||||||
@ -68,7 +69,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'stepTitle',
|
type: 'stepTitle',
|
||||||
id: 'step1',
|
id: 'step1',
|
||||||
number: 1,
|
number: 1,
|
||||||
text: 'Загрузи фото с лицом'
|
text: getTranslation('uploadPhoto')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'uploadPhoto',
|
type: 'uploadPhoto',
|
||||||
@ -88,7 +89,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'color',
|
type: 'color',
|
||||||
colors: ['#E0E0E0']
|
colors: ['#E0E0E0']
|
||||||
},
|
},
|
||||||
title: 'Авто',
|
title: getTranslation('auto'),
|
||||||
action: {
|
action: {
|
||||||
type: 'selectGenderDetection',
|
type: 'selectGenderDetection',
|
||||||
value: 'auto'
|
value: 'auto'
|
||||||
@ -101,7 +102,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'color',
|
type: 'color',
|
||||||
colors: ['#E0E0E0']
|
colors: ['#E0E0E0']
|
||||||
},
|
},
|
||||||
title: 'Мужчина',
|
title: getTranslation('man'),
|
||||||
action: {
|
action: {
|
||||||
type: 'selectGender',
|
type: 'selectGender',
|
||||||
value: 'man'
|
value: 'man'
|
||||||
@ -114,7 +115,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'color',
|
type: 'color',
|
||||||
colors: ['#E0E0E0']
|
colors: ['#E0E0E0']
|
||||||
},
|
},
|
||||||
title: 'Женщина',
|
title: getTranslation('woman'),
|
||||||
action: {
|
action: {
|
||||||
type: 'selectGender',
|
type: 'selectGender',
|
||||||
value: 'woman'
|
value: 'woman'
|
||||||
@ -132,7 +133,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'stepTitle',
|
type: 'stepTitle',
|
||||||
id: 'step2',
|
id: 'step2',
|
||||||
number: 2,
|
number: 2,
|
||||||
text: 'Выбери стиль'
|
text: getTranslation('chooseStyle')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'scrollableButtons',
|
type: 'scrollableButtons',
|
||||||
@ -145,7 +146,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#2B9CFF', '#1E88E5']
|
colors: ['#2B9CFF', '#1E88E5']
|
||||||
},
|
},
|
||||||
title: 'Эмоции',
|
title: getTranslation('emotions'),
|
||||||
imageUrl: images.emotions,
|
imageUrl: images.emotions,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectStyle',
|
type: 'selectStyle',
|
||||||
@ -159,7 +160,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Чиби',
|
title: getTranslation('chibi'),
|
||||||
imageUrl: images.balerina,
|
imageUrl: images.balerina,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectStyle',
|
type: 'selectStyle',
|
||||||
@ -173,7 +174,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#45A049']
|
colors: ['#4CAF50', '#45A049']
|
||||||
},
|
},
|
||||||
title: 'Реализм',
|
title: getTranslation('realism'),
|
||||||
imageUrl: images.realism,
|
imageUrl: images.realism,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectStyle',
|
type: 'selectStyle',
|
||||||
@ -192,7 +193,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'stepTitle',
|
type: 'stepTitle',
|
||||||
id: 'step3',
|
id: 'step3',
|
||||||
number: 3,
|
number: 3,
|
||||||
text: 'Выбери образ'
|
text: getTranslation('chooseImage')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'gridButtons',
|
type: 'gridButtons',
|
||||||
@ -205,7 +206,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#2B9CFF', '#1E88E5']
|
colors: ['#2B9CFF', '#1E88E5']
|
||||||
},
|
},
|
||||||
title: 'Коллекция',
|
title: getTranslation('collection'),
|
||||||
action: {
|
action: {
|
||||||
type: 'selectEmotionType',
|
type: 'selectEmotionType',
|
||||||
value: 'prompts'
|
value: 'prompts'
|
||||||
@ -218,7 +219,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Мемы',
|
title: getTranslation('memes'),
|
||||||
action: {
|
action: {
|
||||||
type: 'selectEmotionType',
|
type: 'selectEmotionType',
|
||||||
value: 'memes'
|
value: 'memes'
|
||||||
@ -243,7 +244,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Мем 1',
|
title: getTranslation('meme1'),
|
||||||
imageUrl: images.meme1,
|
imageUrl: images.meme1,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -257,7 +258,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#2B9CFF', '#1E88E5']
|
colors: ['#2B9CFF', '#1E88E5']
|
||||||
},
|
},
|
||||||
title: 'Мем 2',
|
title: getTranslation('meme2'),
|
||||||
imageUrl: images.meme2,
|
imageUrl: images.meme2,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -271,7 +272,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#45A049']
|
colors: ['#4CAF50', '#45A049']
|
||||||
},
|
},
|
||||||
title: 'Мем 3',
|
title: getTranslation('meme3'),
|
||||||
imageUrl: images.meme3,
|
imageUrl: images.meme3,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -285,7 +286,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9C27B0', '#7B1FA2']
|
colors: ['#9C27B0', '#7B1FA2']
|
||||||
},
|
},
|
||||||
title: 'Мем 4',
|
title: getTranslation('meme4'),
|
||||||
imageUrl: images.meme4,
|
imageUrl: images.meme4,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -299,7 +300,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Мем 5',
|
title: getTranslation('meme5'),
|
||||||
imageUrl: images.meme5,
|
imageUrl: images.meme5,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -313,7 +314,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF9800', '#FB8C00']
|
colors: ['#FF9800', '#FB8C00']
|
||||||
},
|
},
|
||||||
title: 'Мем 6',
|
title: getTranslation('meme6'),
|
||||||
imageUrl: images.meme6,
|
imageUrl: images.meme6,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -327,7 +328,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#607D8B', '#455A64']
|
colors: ['#607D8B', '#455A64']
|
||||||
},
|
},
|
||||||
title: 'Мем 7',
|
title: getTranslation('meme7'),
|
||||||
imageUrl: images.meme7,
|
imageUrl: images.meme7,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -341,7 +342,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Мем 8',
|
title: getTranslation('meme8'),
|
||||||
imageUrl: images.meme8,
|
imageUrl: images.meme8,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -355,7 +356,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#009688', '#00796B']
|
colors: ['#009688', '#00796B']
|
||||||
},
|
},
|
||||||
title: 'Мем 9',
|
title: getTranslation('meme9'),
|
||||||
imageUrl: images.meme9,
|
imageUrl: images.meme9,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
@ -369,7 +370,7 @@ export const homeScreenConfig: AppConfig = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#673AB7', '#512DA8']
|
colors: ['#673AB7', '#512DA8']
|
||||||
},
|
},
|
||||||
title: 'Мем 10',
|
title: getTranslation('meme10'),
|
||||||
imageUrl: images.meme10,
|
imageUrl: images.meme10,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectMeme',
|
type: 'selectMeme',
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { BlockButton } from '../types/blocks';
|
import { BlockButton } from '../types/blocks';
|
||||||
import { images } from '../assets';
|
import { images } from '../assets';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
interface StylePresets {
|
interface StylePresets {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
@ -17,7 +18,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Спорткар',
|
title: getTranslation('preset_sportscar'),
|
||||||
imageUrl: images.sportcar,
|
imageUrl: images.sportcar,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -31,7 +32,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#45A049']
|
colors: ['#4CAF50', '#45A049']
|
||||||
},
|
},
|
||||||
title: 'Скейтборд',
|
title: getTranslation('preset_skateboard'),
|
||||||
imageUrl: images.skateboard,
|
imageUrl: images.skateboard,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -45,7 +46,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Кофе',
|
title: getTranslation('preset_coffee'),
|
||||||
imageUrl: images.coffee,
|
imageUrl: images.coffee,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -59,7 +60,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Цветы',
|
title: getTranslation('preset_flowers'),
|
||||||
imageUrl: images.flowers,
|
imageUrl: images.flowers,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -73,7 +74,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#2196F3', '#1976D2']
|
colors: ['#2196F3', '#1976D2']
|
||||||
},
|
},
|
||||||
title: 'Шарик',
|
title: getTranslation('preset_balloon'),
|
||||||
imageUrl: images.balloon,
|
imageUrl: images.balloon,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -87,7 +88,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9C27B0', '#7B1FA2']
|
colors: ['#9C27B0', '#7B1FA2']
|
||||||
},
|
},
|
||||||
title: 'Книга',
|
title: getTranslation('preset_book'),
|
||||||
imageUrl: images.book,
|
imageUrl: images.book,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -101,7 +102,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Мороженое',
|
title: getTranslation('preset_icecream'),
|
||||||
imageUrl: images.icecream,
|
imageUrl: images.icecream,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -115,7 +116,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#3F51B5', '#303F9F']
|
colors: ['#3F51B5', '#303F9F']
|
||||||
},
|
},
|
||||||
title: 'Зонт',
|
title: getTranslation('preset_umbrella'),
|
||||||
imageUrl: images.umbrella,
|
imageUrl: images.umbrella,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -129,7 +130,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#E91E63', '#C2185B']
|
colors: ['#E91E63', '#C2185B']
|
||||||
},
|
},
|
||||||
title: 'Коктейль',
|
title: getTranslation('preset_cocktail'),
|
||||||
imageUrl: images.cocktail,
|
imageUrl: images.cocktail,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -143,7 +144,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Подарок',
|
title: getTranslation('preset_gift'),
|
||||||
imageUrl: images.gift,
|
imageUrl: images.gift,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -157,7 +158,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Собака',
|
title: getTranslation('preset_dog'),
|
||||||
imageUrl: images.dog,
|
imageUrl: images.dog,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -171,7 +172,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#607D8B', '#455A64']
|
colors: ['#607D8B', '#455A64']
|
||||||
},
|
},
|
||||||
title: 'Газета',
|
title: getTranslation('preset_newspaper'),
|
||||||
imageUrl: images.newspaper,
|
imageUrl: images.newspaper,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -185,7 +186,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#45A049']
|
colors: ['#4CAF50', '#45A049']
|
||||||
},
|
},
|
||||||
title: 'Велосипед',
|
title: getTranslation('preset_bicycle'),
|
||||||
imageUrl: images.bicycle,
|
imageUrl: images.bicycle,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -199,7 +200,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#00BCD4', '#0097A7']
|
colors: ['#00BCD4', '#0097A7']
|
||||||
},
|
},
|
||||||
title: 'Серфер',
|
title: getTranslation('preset_surfer'),
|
||||||
imageUrl: images.surfing,
|
imageUrl: images.surfing,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -213,7 +214,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9E9E9E', '#757575']
|
colors: ['#9E9E9E', '#757575']
|
||||||
},
|
},
|
||||||
title: 'Детектив',
|
title: getTranslation('preset_detective'),
|
||||||
imageUrl: images.detective,
|
imageUrl: images.detective,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -227,7 +228,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#212121', '#000000']
|
colors: ['#212121', '#000000']
|
||||||
},
|
},
|
||||||
title: 'Байкер',
|
title: getTranslation('preset_biker'),
|
||||||
imageUrl: images.moto,
|
imageUrl: images.moto,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -241,7 +242,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Фея',
|
title: getTranslation('preset_fairy'),
|
||||||
imageUrl: images.fairy,
|
imageUrl: images.fairy,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -255,7 +256,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#3F51B5', '#303F9F']
|
colors: ['#3F51B5', '#303F9F']
|
||||||
},
|
},
|
||||||
title: 'Ученый',
|
title: getTranslation('preset_scientist'),
|
||||||
imageUrl: images.science,
|
imageUrl: images.science,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -269,7 +270,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Ковбой',
|
title: getTranslation('preset_cowboy'),
|
||||||
imageUrl: images.cowboy,
|
imageUrl: images.cowboy,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -283,7 +284,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#607D8B', '#455A64']
|
colors: ['#607D8B', '#455A64']
|
||||||
},
|
},
|
||||||
title: 'Рыцарь',
|
title: getTranslation('preset_knight'),
|
||||||
imageUrl: images.knight,
|
imageUrl: images.knight,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -297,7 +298,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Балерина',
|
title: getTranslation('preset_ballerina'),
|
||||||
imageUrl: images.balerina,
|
imageUrl: images.balerina,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -311,7 +312,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Пожарный',
|
title: getTranslation('preset_firefighter'),
|
||||||
imageUrl: images.fire,
|
imageUrl: images.fire,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -325,7 +326,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Шеф-повар',
|
title: getTranslation('preset_chef'),
|
||||||
imageUrl: images.cook,
|
imageUrl: images.cook,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -343,7 +344,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#2196F3', '#1976D2']
|
colors: ['#2196F3', '#1976D2']
|
||||||
},
|
},
|
||||||
title: 'Деловой',
|
title: getTranslation('preset_business'),
|
||||||
icon: '💼',
|
icon: '💼',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -357,7 +358,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Повседневный',
|
title: getTranslation('preset_casual'),
|
||||||
icon: '👕',
|
icon: '👕',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -371,7 +372,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#45A049']
|
colors: ['#4CAF50', '#45A049']
|
||||||
},
|
},
|
||||||
title: 'Спортивный',
|
title: getTranslation('preset_sport'),
|
||||||
icon: '⚽',
|
icon: '⚽',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -385,7 +386,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9C27B0', '#7B1FA2']
|
colors: ['#9C27B0', '#7B1FA2']
|
||||||
},
|
},
|
||||||
title: 'Вечерний',
|
title: getTranslation('preset_evening'),
|
||||||
icon: '🌙',
|
icon: '🌙',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -399,7 +400,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#00BCD4', '#0097A7']
|
colors: ['#00BCD4', '#0097A7']
|
||||||
},
|
},
|
||||||
title: 'Пляжный',
|
title: getTranslation('preset_beach'),
|
||||||
icon: '🏖️',
|
icon: '🏖️',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -413,7 +414,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#90CAF9', '#64B5F6']
|
colors: ['#90CAF9', '#64B5F6']
|
||||||
},
|
},
|
||||||
title: 'Зимний',
|
title: getTranslation('preset_winter'),
|
||||||
icon: '❄️',
|
icon: '❄️',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -427,7 +428,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Ретро',
|
title: getTranslation('preset_retro'),
|
||||||
icon: '🎸',
|
icon: '🎸',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -441,7 +442,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#E91E63', '#9C27B0']
|
colors: ['#E91E63', '#9C27B0']
|
||||||
},
|
},
|
||||||
title: 'Киберпанк',
|
title: getTranslation('preset_cyberpunk'),
|
||||||
icon: '🤖',
|
icon: '🤖',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -455,7 +456,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#2E7D32']
|
colors: ['#4CAF50', '#2E7D32']
|
||||||
},
|
},
|
||||||
title: 'Военный',
|
title: getTranslation('preset_military'),
|
||||||
icon: '🪖',
|
icon: '🪖',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -469,7 +470,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#3E2723']
|
colors: ['#795548', '#3E2723']
|
||||||
},
|
},
|
||||||
title: 'Стимпанк',
|
title: getTranslation('preset_steampunk'),
|
||||||
icon: '⚙️',
|
icon: '⚙️',
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -488,7 +489,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#45A049']
|
colors: ['#4CAF50', '#45A049']
|
||||||
},
|
},
|
||||||
title: 'Привет',
|
title: getTranslation('preset_greeting'),
|
||||||
imageUrl: images.emoGreeting,
|
imageUrl: images.emoGreeting,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -502,7 +503,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#2196F3', '#1976D2']
|
colors: ['#2196F3', '#1976D2']
|
||||||
},
|
},
|
||||||
title: 'Класс',
|
title: getTranslation('preset_thumbsup'),
|
||||||
imageUrl: images.emoThumbsUp,
|
imageUrl: images.emoThumbsUp,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -516,7 +517,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Сердце',
|
title: getTranslation('preset_heart'),
|
||||||
imageUrl: images.emoHeart,
|
imageUrl: images.emoHeart,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -530,7 +531,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FFD700', '#FFA500']
|
colors: ['#FFD700', '#FFA500']
|
||||||
},
|
},
|
||||||
title: 'Смех',
|
title: getTranslation('preset_laugh'),
|
||||||
imageUrl: images.emoLaugh,
|
imageUrl: images.emoLaugh,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -544,7 +545,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9C27B0', '#7B1FA2']
|
colors: ['#9C27B0', '#7B1FA2']
|
||||||
},
|
},
|
||||||
title: 'Шок',
|
title: getTranslation('preset_shock'),
|
||||||
imageUrl: images.emoShock,
|
imageUrl: images.emoShock,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -558,7 +559,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Угроза',
|
title: getTranslation('preset_threat'),
|
||||||
imageUrl: images.emoThreat,
|
imageUrl: images.emoThreat,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -572,7 +573,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#2E7D32']
|
colors: ['#4CAF50', '#2E7D32']
|
||||||
},
|
},
|
||||||
title: 'Халк',
|
title: getTranslation('preset_hulk'),
|
||||||
imageUrl: images.emoHulk,
|
imageUrl: images.emoHulk,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -586,7 +587,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Огонь',
|
title: getTranslation('preset_fire'),
|
||||||
imageUrl: images.emoFire,
|
imageUrl: images.emoFire,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -600,7 +601,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Чай',
|
title: getTranslation('preset_tea'),
|
||||||
imageUrl: images.emoTea,
|
imageUrl: images.emoTea,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -614,7 +615,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9575CD', '#7E57C2']
|
colors: ['#9575CD', '#7E57C2']
|
||||||
},
|
},
|
||||||
title: 'Танец',
|
title: getTranslation('preset_dance'),
|
||||||
imageUrl: images.emoDance,
|
imageUrl: images.emoDance,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -629,7 +630,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FFD700', '#FFA500']
|
colors: ['#FFD700', '#FFA500']
|
||||||
},
|
},
|
||||||
title: 'Трофей',
|
title: getTranslation('preset_trophy'),
|
||||||
imageUrl: images.emoTrophy,
|
imageUrl: images.emoTrophy,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -643,7 +644,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Мишка',
|
title: getTranslation('preset_teddybear'),
|
||||||
imageUrl: images.emoTeddyBear,
|
imageUrl: images.emoTeddyBear,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -657,7 +658,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Роза',
|
title: getTranslation('preset_rose'),
|
||||||
imageUrl: images.emoRose,
|
imageUrl: images.emoRose,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -671,7 +672,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Подарок',
|
title: getTranslation('preset_gift'),
|
||||||
imageUrl: images.emoGift,
|
imageUrl: images.emoGift,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -685,7 +686,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#45A049']
|
colors: ['#4CAF50', '#45A049']
|
||||||
},
|
},
|
||||||
title: 'Букет',
|
title: getTranslation('preset_flowers_bouquet'),
|
||||||
imageUrl: images.emoFlowers,
|
imageUrl: images.emoFlowers,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -699,7 +700,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9C27B0', '#7B1FA2']
|
colors: ['#9C27B0', '#7B1FA2']
|
||||||
},
|
},
|
||||||
title: 'Караоке',
|
title: getTranslation('preset_karaoke'),
|
||||||
imageUrl: images.emoKaraoke,
|
imageUrl: images.emoKaraoke,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -713,7 +714,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#2E7D32']
|
colors: ['#4CAF50', '#2E7D32']
|
||||||
},
|
},
|
||||||
title: 'Зомби',
|
title: getTranslation('preset_zombie'),
|
||||||
imageUrl: images.emoZombie,
|
imageUrl: images.emoZombie,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -727,7 +728,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#607D8B', '#455A64']
|
colors: ['#607D8B', '#455A64']
|
||||||
},
|
},
|
||||||
title: 'Бездомный',
|
title: getTranslation('preset_homeless'),
|
||||||
imageUrl: images.emoHomeless,
|
imageUrl: images.emoHomeless,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -741,7 +742,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF9800', '#F57C00']
|
colors: ['#FF9800', '#F57C00']
|
||||||
},
|
},
|
||||||
title: 'Пиво',
|
title: getTranslation('preset_beer'),
|
||||||
imageUrl: images.emoBeer,
|
imageUrl: images.emoBeer,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -755,7 +756,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Бокс',
|
title: getTranslation('preset_boxing'),
|
||||||
imageUrl: images.emoBoxing,
|
imageUrl: images.emoBoxing,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -769,7 +770,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF69B4', '#FF1493']
|
colors: ['#FF69B4', '#FF1493']
|
||||||
},
|
},
|
||||||
title: 'Котёнок',
|
title: getTranslation('preset_kitten'),
|
||||||
imageUrl: images.emoKitten,
|
imageUrl: images.emoKitten,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -783,7 +784,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Щенок',
|
title: getTranslation('preset_puppy'),
|
||||||
imageUrl: images.emoPuppy,
|
imageUrl: images.emoPuppy,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -797,7 +798,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#3F51B5', '#303F9F']
|
colors: ['#3F51B5', '#303F9F']
|
||||||
},
|
},
|
||||||
title: 'Супергерой',
|
title: getTranslation('preset_superhero'),
|
||||||
imageUrl: images.emoSuperhero,
|
imageUrl: images.emoSuperhero,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -811,7 +812,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#00BCD4', '#0097A7']
|
colors: ['#00BCD4', '#0097A7']
|
||||||
},
|
},
|
||||||
title: 'Мороженое',
|
title: getTranslation('preset_icecream'),
|
||||||
imageUrl: images.emoIceCream,
|
imageUrl: images.emoIceCream,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -825,7 +826,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9C27B0', '#7B1FA2']
|
colors: ['#9C27B0', '#7B1FA2']
|
||||||
},
|
},
|
||||||
title: 'Волшебник',
|
title: getTranslation('preset_wizard'),
|
||||||
imageUrl: images.emoWizard,
|
imageUrl: images.emoWizard,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -839,7 +840,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#FF5722', '#F4511E']
|
colors: ['#FF5722', '#F4511E']
|
||||||
},
|
},
|
||||||
title: 'Дракон',
|
title: getTranslation('preset_dragon'),
|
||||||
imageUrl: images.emoDragon,
|
imageUrl: images.emoDragon,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -853,7 +854,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#212121', '#000000']
|
colors: ['#212121', '#000000']
|
||||||
},
|
},
|
||||||
title: 'Киберпанк',
|
title: getTranslation('preset_cyberpunk'),
|
||||||
imageUrl: images.emoCyberpunk,
|
imageUrl: images.emoCyberpunk,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -867,7 +868,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#3F51B5', '#303F9F']
|
colors: ['#3F51B5', '#303F9F']
|
||||||
},
|
},
|
||||||
title: 'Маг',
|
title: getTranslation('preset_mage'),
|
||||||
imageUrl: images.emoMage,
|
imageUrl: images.emoMage,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -881,7 +882,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Пират',
|
title: getTranslation('preset_pirate'),
|
||||||
imageUrl: images.emoPirate,
|
imageUrl: images.emoPirate,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -895,7 +896,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#212121', '#000000']
|
colors: ['#212121', '#000000']
|
||||||
},
|
},
|
||||||
title: 'Самурай',
|
title: getTranslation('preset_samurai'),
|
||||||
imageUrl: images.emoSamurai,
|
imageUrl: images.emoSamurai,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -909,7 +910,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#607D8B', '#455A64']
|
colors: ['#607D8B', '#455A64']
|
||||||
},
|
},
|
||||||
title: 'Учёный',
|
title: getTranslation('preset_scientist'),
|
||||||
imageUrl: images.emoScientist,
|
imageUrl: images.emoScientist,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -923,7 +924,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#9C27B0', '#7B1FA2']
|
colors: ['#9C27B0', '#7B1FA2']
|
||||||
},
|
},
|
||||||
title: 'Вино',
|
title: getTranslation('preset_wine'),
|
||||||
imageUrl: images.emoWine,
|
imageUrl: images.emoWine,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -937,7 +938,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#4CAF50', '#2E7D32']
|
colors: ['#4CAF50', '#2E7D32']
|
||||||
},
|
},
|
||||||
title: 'Слизь',
|
title: getTranslation('preset_slime'),
|
||||||
imageUrl: images.emoSlime,
|
imageUrl: images.emoSlime,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -951,7 +952,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#795548', '#5D4037']
|
colors: ['#795548', '#5D4037']
|
||||||
},
|
},
|
||||||
title: 'Всадник',
|
title: getTranslation('preset_rider'),
|
||||||
imageUrl: images.emoRider,
|
imageUrl: images.emoRider,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -965,7 +966,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#3F51B5', '#303F9F']
|
colors: ['#3F51B5', '#303F9F']
|
||||||
},
|
},
|
||||||
title: 'Доктор Стрэндж',
|
title: getTranslation('preset_drstrange'),
|
||||||
imageUrl: images.emoDrStrange,
|
imageUrl: images.emoDrStrange,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
@ -979,7 +980,7 @@ export const stylePresets: StylePresets = {
|
|||||||
type: 'gradient',
|
type: 'gradient',
|
||||||
colors: ['#3F51B5', '#303F9F']
|
colors: ['#3F51B5', '#303F9F']
|
||||||
},
|
},
|
||||||
title: 'Капитан Америка',
|
title: getTranslation('preset_captainamerica'),
|
||||||
imageUrl: images.emoCaptainAmerica,
|
imageUrl: images.emoCaptainAmerica,
|
||||||
action: {
|
action: {
|
||||||
type: 'selectPreset',
|
type: 'selectPreset',
|
||||||
|
|||||||
797
src/constants/translations.ts
Normal file
797
src/constants/translations.ts
Normal file
@ -0,0 +1,797 @@
|
|||||||
|
/**
|
||||||
|
* Файл с переводами и функциями для локализации
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Импортируем функцию для получения информации о пользователе
|
||||||
|
import { getUserInfo } from './user';
|
||||||
|
|
||||||
|
// Типы для объектов переводов
|
||||||
|
type TranslationDictionary = {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Translations = {
|
||||||
|
[language: string]: TranslationDictionary;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объект с переводами
|
||||||
|
export const translations: Translations = {
|
||||||
|
ru: {
|
||||||
|
// Тексты из homeScreen.ts
|
||||||
|
'feedback': 'Обратная связь',
|
||||||
|
'instruction': 'Инструкция',
|
||||||
|
'shortsLoader': 'ShortsLoader',
|
||||||
|
'uploadPhoto': 'Загрузи фото с лицом',
|
||||||
|
'auto': 'Авто',
|
||||||
|
'man': 'Мужчина',
|
||||||
|
'woman': 'Женщина',
|
||||||
|
'chooseStyle': 'Выбери стиль',
|
||||||
|
'emotions': 'Эмоции',
|
||||||
|
'chibi': 'Чиби',
|
||||||
|
'realism': 'Реализм',
|
||||||
|
'chooseImage': 'Выбери образ',
|
||||||
|
'collection': 'Коллекция',
|
||||||
|
'memes': 'Мемы',
|
||||||
|
'loading': 'Загрузка...',
|
||||||
|
'generate': 'Сгенерировать',
|
||||||
|
'meme1': 'Мем 1',
|
||||||
|
'meme2': 'Мем 2',
|
||||||
|
'meme3': 'Мем 3',
|
||||||
|
'meme4': 'Мем 4',
|
||||||
|
'meme5': 'Мем 5',
|
||||||
|
'meme6': 'Мем 6',
|
||||||
|
'meme7': 'Мем 7',
|
||||||
|
'meme8': 'Мем 8',
|
||||||
|
'meme9': 'Мем 9',
|
||||||
|
'meme10': 'Мем 10',
|
||||||
|
|
||||||
|
// Тексты из OnboardingWelcome.tsx
|
||||||
|
'welcome_title': 'Добро пожаловать в Sticker Generator',
|
||||||
|
'welcome_description': 'Создавайте уникальные стикеры из ваших фотографий с помощью искусственного интеллекта',
|
||||||
|
'next': 'Далее',
|
||||||
|
'skip': 'Пропустить',
|
||||||
|
|
||||||
|
// Тексты из OnboardingHowTo.tsx
|
||||||
|
'how_to_title': 'Как создать стикер',
|
||||||
|
'upload_photo_title': 'Загрузите фото',
|
||||||
|
'upload_photo_description': 'Выберите фотографию и обрежьте её в квадрат',
|
||||||
|
'choose_style_title': 'Выберите стиль',
|
||||||
|
'choose_style_description': 'Подберите подходящий стиль для вашего стикера',
|
||||||
|
'create_sticker_title': 'Создайте стикер',
|
||||||
|
'create_sticker_description': 'Дождитесь генерации и сохраните результат',
|
||||||
|
|
||||||
|
// Тексты из OnboardingStickerPacks.tsx
|
||||||
|
'sticker_packs_title': 'Создавайте стикерпаки',
|
||||||
|
'start': 'Начать',
|
||||||
|
'select_stickers_title': 'Выберите стикеры',
|
||||||
|
'select_stickers_description': 'Отберите лучшие стикеры из вашей галереи',
|
||||||
|
'organize_pack_title': 'Организуйте набор',
|
||||||
|
'organize_pack_description': 'Расположите стикеры в нужном порядке',
|
||||||
|
'publish_title': 'Опубликуйте',
|
||||||
|
'publish_description': 'Создайте стикерпак в Telegram одним нажатием',
|
||||||
|
|
||||||
|
// Тексты из TermsAndConditions.tsx
|
||||||
|
'terms_title': 'Условия и Конфиденциальность',
|
||||||
|
'terms_description': 'Продолжая использовать это приложение, вы соглашаетесь с нашими Условиями использования и Политикой конфиденциальности.',
|
||||||
|
'terms_of_use': 'Условия использования',
|
||||||
|
'privacy_policy': 'Политика конфиденциальности',
|
||||||
|
'accept': 'Принять',
|
||||||
|
'decline': 'Отклонить',
|
||||||
|
|
||||||
|
// Тексты для NotificationModal
|
||||||
|
'continue': 'Продолжить',
|
||||||
|
'gallery': 'В галерею',
|
||||||
|
'generating': 'Генерация...',
|
||||||
|
'generated_sticker': 'Сгенерированный стикер',
|
||||||
|
|
||||||
|
// Тексты для useNotifications
|
||||||
|
'cancel': 'Отменить',
|
||||||
|
'close': 'Закрыть',
|
||||||
|
'connection_error': 'Возникли проблемы с подключением. Пожалуйста, проверьте интернет-соединение.\n\nЗапрос все еще обрабатывается, но это может занять больше времени, чем обычно.',
|
||||||
|
'feedback_thanks': 'Спасибо за обратную связь',
|
||||||
|
'feedback_sent': 'Ваше сообщение успешно отправлено',
|
||||||
|
'payment_success': 'Оплата успешна!',
|
||||||
|
'tokens_purchased': 'Вы успешно приобрели {0} токенов.',
|
||||||
|
|
||||||
|
// Тексты для UploadPhotoBlock
|
||||||
|
'change_photo': 'Изменить фото',
|
||||||
|
'upload_photo_block_title': 'Загрузите фото',
|
||||||
|
'upload_photo_block_subtitle': 'Перетащите или нажмите для выбора',
|
||||||
|
|
||||||
|
// Тексты для GridButtonsBlock
|
||||||
|
'show_more': 'Показать больше',
|
||||||
|
'collapse': 'Свернуть',
|
||||||
|
|
||||||
|
// Тексты для stylePresets (Чиби)
|
||||||
|
'preset_sportscar': 'Спорткар',
|
||||||
|
'preset_skateboard': 'Скейтборд',
|
||||||
|
'preset_coffee': 'Кофе',
|
||||||
|
'preset_flowers': 'Цветы',
|
||||||
|
'preset_balloon': 'Шарик',
|
||||||
|
'preset_book': 'Книга',
|
||||||
|
'preset_icecream': 'Мороженое',
|
||||||
|
'preset_umbrella': 'Зонт',
|
||||||
|
'preset_cocktail': 'Коктейль',
|
||||||
|
'preset_gift': 'Подарок',
|
||||||
|
'preset_dog': 'Собака',
|
||||||
|
'preset_newspaper': 'Газета',
|
||||||
|
'preset_bicycle': 'Велосипед',
|
||||||
|
'preset_surfer': 'Серфер',
|
||||||
|
'preset_detective': 'Детектив',
|
||||||
|
'preset_biker': 'Байкер',
|
||||||
|
'preset_fairy': 'Фея',
|
||||||
|
'preset_scientist': 'Ученый',
|
||||||
|
'preset_cowboy': 'Ковбой',
|
||||||
|
'preset_knight': 'Рыцарь',
|
||||||
|
'preset_ballerina': 'Балерина',
|
||||||
|
'preset_firefighter': 'Пожарный',
|
||||||
|
'preset_chef': 'Шеф-повар',
|
||||||
|
|
||||||
|
// Тексты для stylePresets (Реализм)
|
||||||
|
'preset_business': 'Деловой',
|
||||||
|
'preset_casual': 'Повседневный',
|
||||||
|
'preset_sport': 'Спортивный',
|
||||||
|
'preset_evening': 'Вечерний',
|
||||||
|
'preset_beach': 'Пляжный',
|
||||||
|
'preset_winter': 'Зимний',
|
||||||
|
'preset_retro': 'Ретро',
|
||||||
|
'preset_cyberpunk': 'Киберпанк',
|
||||||
|
'preset_military': 'Военный',
|
||||||
|
'preset_steampunk': 'Стимпанк',
|
||||||
|
|
||||||
|
// Тексты для stylePresets (Эмоции)
|
||||||
|
'preset_greeting': 'Привет',
|
||||||
|
'preset_thumbsup': 'Класс',
|
||||||
|
'preset_heart': 'Сердце',
|
||||||
|
'preset_laugh': 'Смех',
|
||||||
|
'preset_shock': 'Шок',
|
||||||
|
'preset_threat': 'Угроза',
|
||||||
|
'preset_hulk': 'Халк',
|
||||||
|
'preset_fire': 'Огонь',
|
||||||
|
'preset_tea': 'Чай',
|
||||||
|
'preset_dance': 'Танец',
|
||||||
|
'preset_trophy': 'Трофей',
|
||||||
|
'preset_teddybear': 'Мишка',
|
||||||
|
'preset_rose': 'Роза',
|
||||||
|
'preset_flowers_bouquet': 'Букет',
|
||||||
|
'preset_karaoke': 'Караоке',
|
||||||
|
'preset_zombie': 'Зомби',
|
||||||
|
'preset_homeless': 'Бездомный',
|
||||||
|
'preset_beer': 'Пиво',
|
||||||
|
'preset_boxing': 'Бокс',
|
||||||
|
'preset_kitten': 'Котёнок',
|
||||||
|
'preset_puppy': 'Щенок',
|
||||||
|
'preset_superhero': 'Супергерой',
|
||||||
|
'preset_wizard': 'Волшебник',
|
||||||
|
'preset_dragon': 'Дракон',
|
||||||
|
'preset_mage': 'Маг',
|
||||||
|
'preset_pirate': 'Пират',
|
||||||
|
'preset_samurai': 'Самурай',
|
||||||
|
'preset_wine': 'Вино',
|
||||||
|
'preset_slime': 'Слизь',
|
||||||
|
'preset_rider': 'Всадник',
|
||||||
|
'preset_drstrange': 'Доктор Стрэндж',
|
||||||
|
'preset_captainamerica': 'Капитан Америка',
|
||||||
|
|
||||||
|
// Тексты для GenerateButton
|
||||||
|
'start_generation': 'Начать генерацию',
|
||||||
|
'tokens_count': '{0} токенов',
|
||||||
|
|
||||||
|
// Тексты для Gallery
|
||||||
|
'gallery_title': 'Галерея стикеров',
|
||||||
|
'gallery_generating_section': 'В процессе генерации',
|
||||||
|
'gallery_loading': 'Загрузка изображений...',
|
||||||
|
'gallery_empty': 'У вас пока нет сгенерированных стикеров',
|
||||||
|
'gallery_refreshing': 'Обновление...',
|
||||||
|
'gallery_queue_position': 'В очереди: {0}',
|
||||||
|
'gallery_generating': 'Генерация...',
|
||||||
|
'gallery_time_left': 'Осталось: {0}',
|
||||||
|
'gallery_sticker_alt': 'Стикер {0}',
|
||||||
|
'gallery_create_pack': 'Создать стикерпак',
|
||||||
|
|
||||||
|
// Тексты для модального окна удаления
|
||||||
|
'gallery_delete_title': 'Удаление стикера',
|
||||||
|
'gallery_delete_message': 'Вы уверены, что хотите удалить этот стикер?',
|
||||||
|
'gallery_delete_cancel': 'Отмена',
|
||||||
|
'gallery_delete_confirm': 'Удалить',
|
||||||
|
|
||||||
|
// Тексты для расчета времени ожидания
|
||||||
|
'gallery_generation_started': 'Генерация началась',
|
||||||
|
'gallery_seconds': '{0} сек',
|
||||||
|
'gallery_minutes_seconds': '{0} мин {1} сек',
|
||||||
|
|
||||||
|
// Тексты для StickerPacks
|
||||||
|
'stickerpacks_title': 'Мои стикерпаки',
|
||||||
|
'stickerpacks_subtitle': 'Создавайте и публикуйте наборы стикеров в Telegram',
|
||||||
|
'stickerpacks_pack_title_prefix': 'Стикерпак',
|
||||||
|
'stickerpacks_pack_stats': '{0} / 49 стикеров',
|
||||||
|
'stickerpacks_loading': 'Загрузка стикерпаков...',
|
||||||
|
'stickerpacks_empty': 'У вас пока нет стикерпаков',
|
||||||
|
'stickerpacks_error': 'Не удалось загрузить список стикерпаков',
|
||||||
|
'stickerpacks_sticker_id_error': 'Не удалось определить ID стикера',
|
||||||
|
'stickerpacks_delete_sticker_error': 'Не удалось удалить стикер',
|
||||||
|
'stickerpacks_delete_pack_error': 'Не удалось удалить стикерпак',
|
||||||
|
'stickerpacks_create_button': 'Создать стикерпак',
|
||||||
|
'stickerpacks_retry_button': 'Повторить',
|
||||||
|
'stickerpacks_delete_button': 'Удалить',
|
||||||
|
'stickerpacks_back_button': '← Назад',
|
||||||
|
'stickerpacks_add_sticker_button': 'Добавить стикер',
|
||||||
|
'stickerpacks_open_telegram_button': 'Открыть в Telegram',
|
||||||
|
'stickerpacks_sticker_alt': 'Стикер {0}',
|
||||||
|
|
||||||
|
// Тексты для модальных окон удаления стикерпаков
|
||||||
|
'stickerpacks_delete_pack_title': 'Удаление стикерпака',
|
||||||
|
'stickerpacks_delete_pack_message': 'Вы уверены, что хотите удалить этот стикерпак?',
|
||||||
|
'stickerpacks_delete_sticker_title': 'Удаление стикера',
|
||||||
|
'stickerpacks_delete_sticker_message': 'Вы уверены, что хотите удалить этот стикер?',
|
||||||
|
'stickerpacks_cancel_button': 'Отмена',
|
||||||
|
'stickerpacks_confirm_delete_button': 'Удалить',
|
||||||
|
|
||||||
|
// Тексты для Profile
|
||||||
|
'profile_title': 'Профиль',
|
||||||
|
'profile_subtitle': 'Ваша статистика и настройки',
|
||||||
|
'profile_stickers_created': 'Стикеров создано',
|
||||||
|
'profile_sticker_packs': 'Стикерпаков',
|
||||||
|
'profile_tokens': 'Токенов',
|
||||||
|
'profile_payment_success': 'Оплата успешна!',
|
||||||
|
'profile_tokens_purchased': 'Вы успешно приобрели {0} токенов. Ваш текущий баланс: {1} токенов.',
|
||||||
|
'profile_close': 'Закрыть',
|
||||||
|
|
||||||
|
// Тексты для TokenPacksList
|
||||||
|
'token_packs_title': 'Пакеты токенов',
|
||||||
|
|
||||||
|
// Тексты для TokenPackCard
|
||||||
|
'token_pack_popular': 'Популярный выбор',
|
||||||
|
'token_pack_best_value': 'Максимальная выгода',
|
||||||
|
'token_pack_tokens': 'Токены',
|
||||||
|
'token_pack_bonus': '+{0} БОНУС',
|
||||||
|
'token_pack_stickers_count': '{0} стикеров',
|
||||||
|
'token_pack_buy': 'КУПИТЬ',
|
||||||
|
'token_pack_stars': 'Stars ⭐',
|
||||||
|
|
||||||
|
// Названия и описания пакетов токенов
|
||||||
|
'token_pack_basic_title': 'Стартовый набор стикеромана',
|
||||||
|
'token_pack_basic_description': 'Идеальный вариант для начала! Сгенерируйте 15 уникальных стикеров для вашего Telegram.',
|
||||||
|
'token_pack_optimal_title': 'Стикерный запас',
|
||||||
|
'token_pack_optimal_description': 'Создавайте стикеры для всех случаев жизни с оптимальным запасом токенов.',
|
||||||
|
'token_pack_advanced_title': 'Стикерный энтузиаст',
|
||||||
|
'token_pack_advanced_description': 'Расширьте свои возможности! Создавайте стикеры без ограничений.',
|
||||||
|
'token_pack_super_title': 'Стикерный магнат',
|
||||||
|
'token_pack_super_description': 'Для самых требовательных. Создавайте целые коллекции стикеров с максимальной выгодой.',
|
||||||
|
'token_pack_unlimited_title': 'Бог стикеров',
|
||||||
|
'token_pack_unlimited_description': 'Для профессионалов и настоящих ценителей. Неограниченные возможности для творчества с максимальной выгодой.',
|
||||||
|
|
||||||
|
// Для CropPhoto.tsx
|
||||||
|
'crop_photo_title': 'Обрезка фото',
|
||||||
|
'crop_photo_done': 'Готово',
|
||||||
|
'crop_photo_hint': 'Отрегулируйте масштаб и положение фото',
|
||||||
|
|
||||||
|
// Для TokenPacksModal.tsx
|
||||||
|
'token_modal_title': 'Недостаточно токенов для генерации',
|
||||||
|
'token_modal_show_all': 'Показать все пакеты',
|
||||||
|
|
||||||
|
// Для Navigation.tsx
|
||||||
|
'nav_home': 'Главная',
|
||||||
|
'nav_gallery': 'Галерея',
|
||||||
|
'nav_sticker_packs': 'Стикерпаки',
|
||||||
|
'nav_profile': 'Профиль',
|
||||||
|
|
||||||
|
// Для useGenerationState.ts
|
||||||
|
'generation_attention': 'Внимание',
|
||||||
|
'generation_duplicate_error': 'Нельзя отправить одну и ту же комбинацию изображения и образа подряд. Пожалуйста, измените изображение или выберите другой образ.',
|
||||||
|
'generation_title': 'Генерация стикера',
|
||||||
|
'generation_sending': 'Отправка запроса...',
|
||||||
|
'generation_connection_error': 'Возникли проблемы с подключением. Пожалуйста, проверьте интернет-соединение.',
|
||||||
|
'generation_invalid_prompt_title': 'Недопустимый промпт',
|
||||||
|
'generation_invalid_prompt_message': 'Промпт содержит недопустимый контент. Пожалуйста, используйте более нейтральные формулировки.',
|
||||||
|
'generation_started': 'Создание стикеров началось!',
|
||||||
|
'generation_queue_position': 'Позиция в очереди: {0}',
|
||||||
|
'generation_wait_time': 'Время ожидания: {0}',
|
||||||
|
'generation_result_available': 'Результат будет доступен в галерее после завершения генерации.',
|
||||||
|
'generation_error_title': 'Ошибка',
|
||||||
|
'generation_error_message': 'Не удалось начать генерацию',
|
||||||
|
|
||||||
|
// Для CreateSticker.tsx
|
||||||
|
'create_sticker_screen_title': 'Создание стикера',
|
||||||
|
'create_sticker_screen_subtitle': 'Загрузите фотографию для создания стикера',
|
||||||
|
'create_sticker_screen_upload_text': 'Нажмите чтобы выбрать фото',
|
||||||
|
'create_sticker_screen_upload_hint': 'или перетащите файл сюда',
|
||||||
|
'create_sticker_screen_cost': 'Стоимость: {0} токенов',
|
||||||
|
'create_sticker_screen_balance_error': 'Произошла ошибка при проверке баланса. Попробуйте позже.',
|
||||||
|
|
||||||
|
// Для форматирования времени
|
||||||
|
'time_minutes_short': 'мин',
|
||||||
|
'time_seconds_short': 'сек',
|
||||||
|
|
||||||
|
// Для компонентов с жестко закодированными строками
|
||||||
|
'create_first': 'Сначала создайте изображения в разделе "Создать стикер".',
|
||||||
|
'add_balance_title': 'Нажмите чтобы пополнить баланс',
|
||||||
|
'tokens_alt': 'Токены',
|
||||||
|
'feedback_placeholder': 'Опишите вашу проблему или предложение...',
|
||||||
|
'sticker_description_placeholder': 'Опишите, какой стикер вы хотите получить...',
|
||||||
|
'sticker_pack_name_label': 'Название стикерпака',
|
||||||
|
'fullscreen_view_alt': 'Полноэкранный просмотр',
|
||||||
|
'preview_alt': 'Предпросмотр',
|
||||||
|
'avatar_alt': 'Аватар',
|
||||||
|
|
||||||
|
// Для сообщений об ошибках в сервисах
|
||||||
|
'invalid_prompt': 'Недопустимый промпт',
|
||||||
|
'failed_to_generate': 'Не удалось сгенерировать изображение',
|
||||||
|
'failed_to_translate': 'Не удалось перевести текст',
|
||||||
|
|
||||||
|
// Для AddStickerToPackScreen.tsx
|
||||||
|
'back_button': '← Назад',
|
||||||
|
'add_stickers_to_pack_title': 'Добавление стикеров в "{0}"',
|
||||||
|
'loading_data': 'Загрузка данных...',
|
||||||
|
'select_images_for_stickers': 'Выберите изображения для стикеров',
|
||||||
|
'image_alt': 'Изображение {0}',
|
||||||
|
'stickers_not_selected': 'Стикеры не выбраны',
|
||||||
|
'select_at_least_one_image': 'Пожалуйста, выберите хотя бы одно изображение для добавления в стикерпак.',
|
||||||
|
'error': 'Ошибка',
|
||||||
|
'pack_name_not_specified': 'Не указано имя стикерпака.',
|
||||||
|
'failed_to_add_stickers': 'Не удалось добавить стикеры в стикерпак. Пожалуйста, попробуйте еще раз.',
|
||||||
|
'adding': 'Добавление...',
|
||||||
|
'add_stickers': 'Добавить стикеры',
|
||||||
|
'failed_to_load_data': 'Не удалось загрузить данные',
|
||||||
|
|
||||||
|
// Для FeedbackModal.tsx
|
||||||
|
'feedback_title': 'Обратная связь',
|
||||||
|
'feedback_subtitle': 'Расскажите нам о проблеме или предложении',
|
||||||
|
'add_image': 'Добавить изображение',
|
||||||
|
'loading_image': 'Загрузка...',
|
||||||
|
'preview_image_alt': 'Превью {0}',
|
||||||
|
'select_image_file': 'Пожалуйста, выберите файл изображения (JPEG, PNG и т.д.)',
|
||||||
|
'file_selection_error': 'Произошла ошибка при выборе файла. Пожалуйста, попробуйте еще раз.',
|
||||||
|
'enter_text_or_add_image': 'Пожалуйста, введите текст или добавьте изображение',
|
||||||
|
'failed_to_send_feedback': 'Не удалось отправить обратную связь. Пожалуйста, попробуйте позже.',
|
||||||
|
'error_sending_feedback': 'Произошла ошибка при отправке. Пожалуйста, попробуйте позже.',
|
||||||
|
|
||||||
|
// Для модальных окон валидации генерации
|
||||||
|
'select_style_warning_title': 'Внимание',
|
||||||
|
'select_style_warning_message': 'Выберите образ для генерации',
|
||||||
|
'upload_image_warning_title': 'Внимание',
|
||||||
|
'upload_image_warning_message': 'Сначала загрузите изображение',
|
||||||
|
'enter_prompt_warning_title': 'Внимание',
|
||||||
|
'enter_prompt_warning_message': 'Введите текст промпта',
|
||||||
|
|
||||||
|
// Для модального окна выбора эмодзи
|
||||||
|
'emoji_picker_title': 'Выберите эмодзи',
|
||||||
|
'emoji_picker_cancel': 'Отмена',
|
||||||
|
'emoji_picker_apply': 'Применить',
|
||||||
|
|
||||||
|
// Для экрана создания стикерпака
|
||||||
|
'create_sticker_pack_back': '← Назад',
|
||||||
|
'create_sticker_pack_title': 'Создание стикерпака',
|
||||||
|
'create_sticker_pack_select_images': 'Выберите изображения для стикеров',
|
||||||
|
'create_sticker_pack_loading': 'Загрузка изображений...',
|
||||||
|
'create_sticker_pack_image_alt': 'Изображение {0}',
|
||||||
|
'create_sticker_pack_creating': 'Создание...',
|
||||||
|
'create_sticker_pack_create': 'Создать стикерпак',
|
||||||
|
'create_sticker_pack_name_required_title': 'Название не указано',
|
||||||
|
'create_sticker_pack_name_required_message': 'Пожалуйста, введите название для вашего стикерпака.',
|
||||||
|
'create_sticker_pack_no_stickers_title': 'Стикеры не выбраны',
|
||||||
|
'create_sticker_pack_no_stickers_message': 'Пожалуйста, выберите хотя бы одно изображение для вашего стикерпака.',
|
||||||
|
'create_sticker_pack_name_occupied_title': 'Имя стикерпака уже занято',
|
||||||
|
'create_sticker_pack_name_occupied_message': 'Стикерпак с таким именем уже существует в Telegram. Пожалуйста, измените название и попробуйте снова.',
|
||||||
|
'create_sticker_pack_error_title': 'Ошибка при создании стикерпака',
|
||||||
|
'create_sticker_pack_error_message': 'Не удалось создать стикерпак. Пожалуйста, попробуйте еще раз с другим названием или изображениями.'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
// Английские переводы
|
||||||
|
'feedback': 'Feedback',
|
||||||
|
'instruction': 'Instructions',
|
||||||
|
'shortsLoader': 'ShortsLoader',
|
||||||
|
'uploadPhoto': 'Upload a photo with a face',
|
||||||
|
'auto': 'Auto',
|
||||||
|
'man': 'Male',
|
||||||
|
'woman': 'Female',
|
||||||
|
'chooseStyle': 'Choose style',
|
||||||
|
'emotions': 'Emotions',
|
||||||
|
'chibi': 'Chibi',
|
||||||
|
'realism': 'Realism',
|
||||||
|
'chooseImage': 'Choose image',
|
||||||
|
'collection': 'Collection',
|
||||||
|
'memes': 'Memes',
|
||||||
|
'loading': 'Loading...',
|
||||||
|
'generate': 'Generate',
|
||||||
|
'meme1': 'Meme 1',
|
||||||
|
'meme2': 'Meme 2',
|
||||||
|
'meme3': 'Meme 3',
|
||||||
|
'meme4': 'Meme 4',
|
||||||
|
'meme5': 'Meme 5',
|
||||||
|
'meme6': 'Meme 6',
|
||||||
|
'meme7': 'Meme 7',
|
||||||
|
'meme8': 'Meme 8',
|
||||||
|
'meme9': 'Meme 9',
|
||||||
|
'meme10': 'Meme 10',
|
||||||
|
|
||||||
|
// Английские переводы для OnboardingWelcome
|
||||||
|
'welcome_title': 'Welcome to Sticker Generator',
|
||||||
|
'welcome_description': 'Create unique stickers from your photos using artificial intelligence',
|
||||||
|
'next': 'Next',
|
||||||
|
'skip': 'Skip',
|
||||||
|
|
||||||
|
// Английские переводы для OnboardingHowTo
|
||||||
|
'how_to_title': 'How to Create a Sticker',
|
||||||
|
'upload_photo_title': 'Upload a Photo',
|
||||||
|
'upload_photo_description': 'Choose a photo and crop it into a square',
|
||||||
|
'choose_style_title': 'Choose a Style',
|
||||||
|
'choose_style_description': 'Select a suitable style for your sticker',
|
||||||
|
'create_sticker_title': 'Create a Sticker',
|
||||||
|
'create_sticker_description': 'Wait for generation and save the result',
|
||||||
|
|
||||||
|
// Английские переводы для OnboardingStickerPacks
|
||||||
|
'sticker_packs_title': 'Create Sticker Packs',
|
||||||
|
'start': 'Start',
|
||||||
|
'select_stickers_title': 'Select Stickers',
|
||||||
|
'select_stickers_description': 'Choose the best stickers from your gallery',
|
||||||
|
'organize_pack_title': 'Organize Pack',
|
||||||
|
'organize_pack_description': 'Arrange stickers in the desired order',
|
||||||
|
'publish_title': 'Publish',
|
||||||
|
'publish_description': 'Create a Telegram sticker pack with one click',
|
||||||
|
|
||||||
|
// Английские переводы для TermsAndConditions
|
||||||
|
'terms_title': 'Terms and Privacy',
|
||||||
|
'terms_description': 'By continuing to use this application, you agree to our Terms of Use and Privacy Policy.',
|
||||||
|
'terms_of_use': 'Terms of Use',
|
||||||
|
'privacy_policy': 'Privacy Policy',
|
||||||
|
'accept': 'Accept',
|
||||||
|
'decline': 'Decline',
|
||||||
|
|
||||||
|
// Английские переводы для NotificationModal
|
||||||
|
'continue': 'Continue',
|
||||||
|
'gallery': 'To Gallery',
|
||||||
|
'generating': 'Generating...',
|
||||||
|
'generated_sticker': 'Generated sticker',
|
||||||
|
|
||||||
|
// Английские переводы для useNotifications
|
||||||
|
'cancel': 'Cancel',
|
||||||
|
'close': 'Close',
|
||||||
|
'connection_error': 'Connection problems detected. Please check your internet connection.\n\nThe request is still being processed, but it may take longer than usual.',
|
||||||
|
'feedback_thanks': 'Thank you for your feedback',
|
||||||
|
'feedback_sent': 'Your message has been successfully sent',
|
||||||
|
'payment_success': 'Payment successful!',
|
||||||
|
'tokens_purchased': 'You have successfully purchased {0} tokens.',
|
||||||
|
|
||||||
|
// Английские переводы для UploadPhotoBlock
|
||||||
|
'change_photo': 'Change Photo',
|
||||||
|
'upload_photo_block_title': 'Upload Photo',
|
||||||
|
'upload_photo_block_subtitle': 'Drag and drop or click to select',
|
||||||
|
|
||||||
|
// Английские переводы для GridButtonsBlock
|
||||||
|
'show_more': 'Show More',
|
||||||
|
'collapse': 'Collapse',
|
||||||
|
|
||||||
|
// Английские переводы для stylePresets (Чиби)
|
||||||
|
'preset_sportscar': 'Sports Car',
|
||||||
|
'preset_skateboard': 'Skateboard',
|
||||||
|
'preset_coffee': 'Coffee',
|
||||||
|
'preset_flowers': 'Flowers',
|
||||||
|
'preset_balloon': 'Balloon',
|
||||||
|
'preset_book': 'Book',
|
||||||
|
'preset_icecream': 'Ice Cream',
|
||||||
|
'preset_umbrella': 'Umbrella',
|
||||||
|
'preset_cocktail': 'Cocktail',
|
||||||
|
'preset_gift': 'Gift',
|
||||||
|
'preset_dog': 'Dog',
|
||||||
|
'preset_newspaper': 'Newspaper',
|
||||||
|
'preset_bicycle': 'Bicycle',
|
||||||
|
'preset_surfer': 'Surfer',
|
||||||
|
'preset_detective': 'Detective',
|
||||||
|
'preset_biker': 'Biker',
|
||||||
|
'preset_fairy': 'Fairy',
|
||||||
|
'preset_scientist': 'Scientist',
|
||||||
|
'preset_cowboy': 'Cowboy',
|
||||||
|
'preset_knight': 'Knight',
|
||||||
|
'preset_ballerina': 'Ballerina',
|
||||||
|
'preset_firefighter': 'Firefighter',
|
||||||
|
'preset_chef': 'Chef',
|
||||||
|
|
||||||
|
// Английские переводы для stylePresets (Реализм)
|
||||||
|
'preset_business': 'Business',
|
||||||
|
'preset_casual': 'Casual',
|
||||||
|
'preset_sport': 'Sport',
|
||||||
|
'preset_evening': 'Evening',
|
||||||
|
'preset_beach': 'Beach',
|
||||||
|
'preset_winter': 'Winter',
|
||||||
|
'preset_retro': 'Retro',
|
||||||
|
'preset_cyberpunk': 'Cyberpunk',
|
||||||
|
'preset_military': 'Military',
|
||||||
|
'preset_steampunk': 'Steampunk',
|
||||||
|
|
||||||
|
// Английские переводы для stylePresets (Эмоции)
|
||||||
|
'preset_greeting': 'Greeting',
|
||||||
|
'preset_thumbsup': 'Thumbs Up',
|
||||||
|
'preset_heart': 'Heart',
|
||||||
|
'preset_laugh': 'Laugh',
|
||||||
|
'preset_shock': 'Shock',
|
||||||
|
'preset_threat': 'Threat',
|
||||||
|
'preset_hulk': 'Hulk',
|
||||||
|
'preset_fire': 'Fire',
|
||||||
|
'preset_tea': 'Tea',
|
||||||
|
'preset_dance': 'Dance',
|
||||||
|
'preset_trophy': 'Trophy',
|
||||||
|
'preset_teddybear': 'Teddy Bear',
|
||||||
|
'preset_rose': 'Rose',
|
||||||
|
'preset_flowers_bouquet': 'Bouquet',
|
||||||
|
'preset_karaoke': 'Karaoke',
|
||||||
|
'preset_zombie': 'Zombie',
|
||||||
|
'preset_homeless': 'Homeless',
|
||||||
|
'preset_beer': 'Beer',
|
||||||
|
'preset_boxing': 'Boxing',
|
||||||
|
'preset_kitten': 'Kitten',
|
||||||
|
'preset_puppy': 'Puppy',
|
||||||
|
'preset_superhero': 'Superhero',
|
||||||
|
'preset_wizard': 'Wizard',
|
||||||
|
'preset_dragon': 'Dragon',
|
||||||
|
'preset_mage': 'Mage',
|
||||||
|
'preset_pirate': 'Pirate',
|
||||||
|
'preset_samurai': 'Samurai',
|
||||||
|
'preset_wine': 'Wine',
|
||||||
|
'preset_slime': 'Slime',
|
||||||
|
'preset_rider': 'Rider',
|
||||||
|
'preset_drstrange': 'Dr. Strange',
|
||||||
|
'preset_captainamerica': 'Captain America',
|
||||||
|
|
||||||
|
// Английские переводы для GenerateButton
|
||||||
|
'start_generation': 'Start Generation',
|
||||||
|
'tokens_count': '{0} tokens',
|
||||||
|
|
||||||
|
// Английские переводы для Gallery
|
||||||
|
'gallery_title': 'Sticker Gallery',
|
||||||
|
'gallery_generating_section': 'In Progress',
|
||||||
|
'gallery_loading': 'Loading images...',
|
||||||
|
'gallery_empty': 'You don\'t have any generated stickers yet',
|
||||||
|
'gallery_refreshing': 'Refreshing...',
|
||||||
|
'gallery_queue_position': 'In queue: {0}',
|
||||||
|
'gallery_generating': 'Generating...',
|
||||||
|
'gallery_time_left': 'Time left: {0}',
|
||||||
|
'gallery_sticker_alt': 'Sticker {0}',
|
||||||
|
'gallery_create_pack': 'Create Sticker Pack',
|
||||||
|
|
||||||
|
// Английские переводы для модального окна удаления
|
||||||
|
'gallery_delete_title': 'Delete Sticker',
|
||||||
|
'gallery_delete_message': 'Are you sure you want to delete this sticker?',
|
||||||
|
'gallery_delete_cancel': 'Cancel',
|
||||||
|
'gallery_delete_confirm': 'Delete',
|
||||||
|
|
||||||
|
// Английские переводы для расчета времени ожидания
|
||||||
|
'gallery_generation_started': 'Generation started',
|
||||||
|
'gallery_seconds': '{0} sec',
|
||||||
|
'gallery_minutes_seconds': '{0} min {1} sec',
|
||||||
|
|
||||||
|
// Английские переводы для StickerPacks
|
||||||
|
'stickerpacks_title': 'My Sticker Packs',
|
||||||
|
'stickerpacks_subtitle': 'Create and publish sticker sets in Telegram',
|
||||||
|
'stickerpacks_pack_title_prefix': 'Sticker Pack',
|
||||||
|
'stickerpacks_pack_stats': '{0} / 49 stickers',
|
||||||
|
'stickerpacks_loading': 'Loading sticker packs...',
|
||||||
|
'stickerpacks_empty': 'You don\'t have any sticker packs yet',
|
||||||
|
'stickerpacks_error': 'Failed to load sticker packs',
|
||||||
|
'stickerpacks_sticker_id_error': 'Failed to determine sticker ID',
|
||||||
|
'stickerpacks_delete_sticker_error': 'Failed to delete sticker',
|
||||||
|
'stickerpacks_delete_pack_error': 'Failed to delete sticker pack',
|
||||||
|
'stickerpacks_create_button': 'Create Sticker Pack',
|
||||||
|
'stickerpacks_retry_button': 'Retry',
|
||||||
|
'stickerpacks_delete_button': 'Delete',
|
||||||
|
'stickerpacks_back_button': '← Back',
|
||||||
|
'stickerpacks_add_sticker_button': 'Add Sticker',
|
||||||
|
'stickerpacks_open_telegram_button': 'Open in Telegram',
|
||||||
|
'stickerpacks_sticker_alt': 'Sticker {0}',
|
||||||
|
|
||||||
|
// Английские переводы для модальных окон удаления стикерпаков
|
||||||
|
'stickerpacks_delete_pack_title': 'Delete Sticker Pack',
|
||||||
|
'stickerpacks_delete_pack_message': 'Are you sure you want to delete this sticker pack?',
|
||||||
|
'stickerpacks_delete_sticker_title': 'Delete Sticker',
|
||||||
|
'stickerpacks_delete_sticker_message': 'Are you sure you want to delete this sticker?',
|
||||||
|
'stickerpacks_cancel_button': 'Cancel',
|
||||||
|
'stickerpacks_confirm_delete_button': 'Delete',
|
||||||
|
|
||||||
|
// Английские переводы для Profile
|
||||||
|
'profile_title': 'Profile',
|
||||||
|
'profile_subtitle': 'Your statistics and settings',
|
||||||
|
'profile_stickers_created': 'Stickers created',
|
||||||
|
'profile_sticker_packs': 'Sticker packs',
|
||||||
|
'profile_tokens': 'Tokens',
|
||||||
|
'profile_payment_success': 'Payment successful!',
|
||||||
|
'profile_tokens_purchased': 'You have successfully purchased {0} tokens. Your current balance: {1} tokens.',
|
||||||
|
'profile_close': 'Close',
|
||||||
|
|
||||||
|
// Английские переводы для TokenPacksList
|
||||||
|
'token_packs_title': 'Token Packages',
|
||||||
|
|
||||||
|
// Английские переводы для TokenPackCard
|
||||||
|
'token_pack_popular': 'Popular choice',
|
||||||
|
'token_pack_best_value': 'Best value',
|
||||||
|
'token_pack_tokens': 'Tokens',
|
||||||
|
'token_pack_bonus': '+{0} BONUS',
|
||||||
|
'token_pack_stickers_count': '{0} stickers',
|
||||||
|
'token_pack_buy': 'BUY',
|
||||||
|
'token_pack_stars': 'Stars ⭐',
|
||||||
|
|
||||||
|
// Названия и описания пакетов токенов (английские)
|
||||||
|
'token_pack_basic_title': 'Sticker Beginner Pack',
|
||||||
|
'token_pack_basic_description': 'Perfect for beginners! Generate 15 unique stickers for your Telegram.',
|
||||||
|
'token_pack_optimal_title': 'Sticker Supply',
|
||||||
|
'token_pack_optimal_description': 'Create stickers for all occasions with an optimal token supply.',
|
||||||
|
'token_pack_advanced_title': 'Sticker Enthusiast',
|
||||||
|
'token_pack_advanced_description': 'Expand your possibilities! Create stickers without limitations.',
|
||||||
|
'token_pack_super_title': 'Sticker Magnate',
|
||||||
|
'token_pack_super_description': 'For the most demanding users. Create entire collections of stickers with maximum value.',
|
||||||
|
'token_pack_unlimited_title': 'Sticker God',
|
||||||
|
'token_pack_unlimited_description': 'For professionals and true connoisseurs. Unlimited creative possibilities with maximum value.',
|
||||||
|
|
||||||
|
// Для CropPhoto.tsx (английские)
|
||||||
|
'crop_photo_title': 'Crop Photo',
|
||||||
|
'crop_photo_done': 'Done',
|
||||||
|
'crop_photo_hint': 'Adjust the scale and position of the photo',
|
||||||
|
|
||||||
|
// Для TokenPacksModal.tsx (английские)
|
||||||
|
'token_modal_title': 'Not enough tokens for generation',
|
||||||
|
'token_modal_show_all': 'Show all packages',
|
||||||
|
|
||||||
|
// Для Navigation.tsx (английские)
|
||||||
|
'nav_home': 'Home',
|
||||||
|
'nav_gallery': 'Gallery',
|
||||||
|
'nav_sticker_packs': 'Sticker Packs',
|
||||||
|
'nav_profile': 'Profile',
|
||||||
|
|
||||||
|
// Для useGenerationState.ts (английские)
|
||||||
|
'generation_attention': 'Attention',
|
||||||
|
'generation_duplicate_error': 'You cannot send the same combination of image and style in a row. Please change the image or select a different style.',
|
||||||
|
'generation_title': 'Sticker Generation',
|
||||||
|
'generation_sending': 'Sending request...',
|
||||||
|
'generation_connection_error': 'Connection problems detected. Please check your internet connection.',
|
||||||
|
'generation_invalid_prompt_title': 'Invalid Prompt',
|
||||||
|
'generation_invalid_prompt_message': 'The prompt contains inappropriate content. Please use more neutral wording.',
|
||||||
|
'generation_started': 'Sticker creation has started!',
|
||||||
|
'generation_queue_position': 'Queue position: {0}',
|
||||||
|
'generation_wait_time': 'Estimated wait time: {0}',
|
||||||
|
'generation_result_available': 'The result will be available in the gallery after generation is complete.',
|
||||||
|
'generation_error_title': 'Error',
|
||||||
|
'generation_error_message': 'Failed to start generation',
|
||||||
|
|
||||||
|
// Для CreateSticker.tsx (английские)
|
||||||
|
'create_sticker_screen_title': 'Create Sticker',
|
||||||
|
'create_sticker_screen_subtitle': 'Upload a photo to create a sticker',
|
||||||
|
'create_sticker_screen_upload_text': 'Click to select a photo',
|
||||||
|
'create_sticker_screen_upload_hint': 'or drag and drop a file here',
|
||||||
|
'create_sticker_screen_cost': 'Cost: {0} tokens',
|
||||||
|
'create_sticker_screen_balance_error': 'An error occurred while checking your balance. Please try again later.',
|
||||||
|
|
||||||
|
// Для форматирования времени (английские)
|
||||||
|
'time_minutes_short': 'min',
|
||||||
|
'time_seconds_short': 'sec',
|
||||||
|
|
||||||
|
// Для компонентов с жестко закодированными строками (английские)
|
||||||
|
'create_first': 'First create images in the "Create Sticker" section.',
|
||||||
|
'add_balance_title': 'Click to add balance',
|
||||||
|
'tokens_alt': 'Tokens',
|
||||||
|
'feedback_placeholder': 'Describe your issue or suggestion...',
|
||||||
|
'sticker_description_placeholder': 'Describe what kind of sticker you want to get...',
|
||||||
|
'sticker_pack_name_label': 'Sticker pack name',
|
||||||
|
'fullscreen_view_alt': 'Fullscreen view',
|
||||||
|
'preview_alt': 'Preview',
|
||||||
|
'avatar_alt': 'Avatar',
|
||||||
|
|
||||||
|
// Для сообщений об ошибках в сервисах (английские)
|
||||||
|
'invalid_prompt': 'Invalid prompt',
|
||||||
|
'failed_to_generate': 'Failed to generate image',
|
||||||
|
'failed_to_translate': 'Failed to translate text',
|
||||||
|
|
||||||
|
// Для AddStickerToPackScreen.tsx (английские)
|
||||||
|
'back_button': '← Back',
|
||||||
|
'add_stickers_to_pack_title': 'Add stickers to "{0}"',
|
||||||
|
'loading_data': 'Loading data...',
|
||||||
|
'select_images_for_stickers': 'Select images for stickers',
|
||||||
|
'image_alt': 'Image {0}',
|
||||||
|
'stickers_not_selected': 'No stickers selected',
|
||||||
|
'select_at_least_one_image': 'Please select at least one image to add to the sticker pack.',
|
||||||
|
'error': 'Error',
|
||||||
|
'pack_name_not_specified': 'Sticker pack name not specified.',
|
||||||
|
'failed_to_add_stickers': 'Failed to add stickers to the sticker pack. Please try again.',
|
||||||
|
'adding': 'Adding...',
|
||||||
|
'add_stickers': 'Add stickers',
|
||||||
|
'failed_to_load_data': 'Failed to load data',
|
||||||
|
|
||||||
|
// Для FeedbackModal.tsx (английские)
|
||||||
|
'feedback_title': 'Feedback',
|
||||||
|
'feedback_subtitle': 'Tell us about your issue or suggestion',
|
||||||
|
'add_image': 'Add image',
|
||||||
|
'loading_image': 'Loading...',
|
||||||
|
'preview_image_alt': 'Preview {0}',
|
||||||
|
'select_image_file': 'Please select an image file (JPEG, PNG, etc.)',
|
||||||
|
'file_selection_error': 'An error occurred while selecting the file. Please try again.',
|
||||||
|
'enter_text_or_add_image': 'Please enter text or add an image',
|
||||||
|
'failed_to_send_feedback': 'Failed to send feedback. Please try again later.',
|
||||||
|
'error_sending_feedback': 'An error occurred while sending. Please try again later.',
|
||||||
|
|
||||||
|
// Для модальных окон валидации генерации (английские)
|
||||||
|
'select_style_warning_title': 'Attention',
|
||||||
|
'select_style_warning_message': 'Please select a style for generation',
|
||||||
|
'upload_image_warning_title': 'Attention',
|
||||||
|
'upload_image_warning_message': 'Please upload an image first',
|
||||||
|
'enter_prompt_warning_title': 'Attention',
|
||||||
|
'enter_prompt_warning_message': 'Please enter prompt text',
|
||||||
|
|
||||||
|
// Для модального окна выбора эмодзи (английские)
|
||||||
|
'emoji_picker_title': 'Choose emoji',
|
||||||
|
'emoji_picker_cancel': 'Cancel',
|
||||||
|
'emoji_picker_apply': 'Apply',
|
||||||
|
|
||||||
|
// Для экрана создания стикерпака (английские)
|
||||||
|
'create_sticker_pack_back': '← Back',
|
||||||
|
'create_sticker_pack_title': 'Create Sticker Pack',
|
||||||
|
'create_sticker_pack_select_images': 'Select images for stickers',
|
||||||
|
'create_sticker_pack_loading': 'Loading images...',
|
||||||
|
'create_sticker_pack_image_alt': 'Image {0}',
|
||||||
|
'create_sticker_pack_creating': 'Creating...',
|
||||||
|
'create_sticker_pack_create': 'Create Sticker Pack',
|
||||||
|
'create_sticker_pack_name_required_title': 'Name Required',
|
||||||
|
'create_sticker_pack_name_required_message': 'Please enter a name for your sticker pack.',
|
||||||
|
'create_sticker_pack_no_stickers_title': 'No Stickers Selected',
|
||||||
|
'create_sticker_pack_no_stickers_message': 'Please select at least one image for your sticker pack.',
|
||||||
|
'create_sticker_pack_name_occupied_title': 'Sticker Pack Name Already Taken',
|
||||||
|
'create_sticker_pack_name_occupied_message': 'A sticker pack with this name already exists in Telegram. Please change the name and try again.',
|
||||||
|
'create_sticker_pack_error_title': 'Error Creating Sticker Pack',
|
||||||
|
'create_sticker_pack_error_message': 'Failed to create sticker pack. Please try again with a different name or images.'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Функция для форматирования строки с параметрами
|
||||||
|
* @param str Строка с плейсхолдерами {0}, {1}, ...
|
||||||
|
* @param args Аргументы для замены плейсхолдеров
|
||||||
|
* @returns Отформатированная строка
|
||||||
|
*/
|
||||||
|
export const formatString = (str: string, ...args: any[]): string => {
|
||||||
|
return str.replace(/{(\d+)}/g, (match, index) => {
|
||||||
|
return typeof args[index] !== 'undefined' ? args[index] : match;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Функция для получения языка пользователя
|
||||||
|
* @returns Код языка ('ru' или 'en')
|
||||||
|
*/
|
||||||
|
export const getUserLanguage = (): 'ru' | 'en' => {
|
||||||
|
try {
|
||||||
|
const userInfo = getUserInfo();
|
||||||
|
// Если язык пользователя русский, возвращаем 'ru', иначе 'en'
|
||||||
|
return userInfo.language_code === 'ru' ? 'ru' : 'en';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при получении языка пользователя:', error);
|
||||||
|
// По умолчанию возвращаем русский язык
|
||||||
|
return 'en';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Функция для получения перевода по ключу
|
||||||
|
* @param key Ключ перевода
|
||||||
|
* @param args Аргументы для форматирования строки (опционально)
|
||||||
|
* @returns Переведенный текст
|
||||||
|
*/
|
||||||
|
export const getTranslation = (key: string, ...args: any[]): string => {
|
||||||
|
const language = getUserLanguage();
|
||||||
|
const translation = translations[language][key] || translations['ru'][key] || key;
|
||||||
|
|
||||||
|
if (args.length > 0) {
|
||||||
|
return formatString(translation, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return translation;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Хук для использования переводов в компонентах (для удобства)
|
||||||
|
* @returns Объект с функцией перевода и текущим языком
|
||||||
|
*/
|
||||||
|
export const useTranslation = () => {
|
||||||
|
const language = getUserLanguage();
|
||||||
|
|
||||||
|
return {
|
||||||
|
t: (key: string, ...args: any[]) => getTranslation(key, ...args),
|
||||||
|
language
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -7,6 +7,7 @@ import { useBalance } from '../contexts/BalanceContext';
|
|||||||
import { WorkflowType } from '../constants/workflows';
|
import { WorkflowType } from '../constants/workflows';
|
||||||
import customAnalyticsService from '../services/customAnalyticsService';
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Хук для управления состоянием генерации
|
* Хук для управления состоянием генерации
|
||||||
@ -192,14 +193,14 @@ export const useGenerationState = (
|
|||||||
const isSame = isSameGeneration(currentData, lastGenerationData);
|
const isSame = isSameGeneration(currentData, lastGenerationData);
|
||||||
console.log('Is same generation:', isSame);
|
console.log('Is same generation:', isSame);
|
||||||
|
|
||||||
if (isSame) {
|
if (isSame) {
|
||||||
showNotification(
|
showNotification(
|
||||||
'Внимание',
|
getTranslation('generation_attention'),
|
||||||
'Нельзя отправить одну и ту же комбинацию изображения и образа подряд. Пожалуйста, измените изображение или выберите другой образ.',
|
getTranslation('generation_duplicate_error'),
|
||||||
{ showGalleryButton: false }
|
{ showGalleryButton: false }
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Объявляем переменную для таймера вне блока try, чтобы она была доступна в блоке catch
|
// Объявляем переменную для таймера вне блока try, чтобы она была доступна в блоке catch
|
||||||
let connectionTimeoutId: ReturnType<typeof setTimeout> | undefined;
|
let connectionTimeoutId: ReturnType<typeof setTimeout> | undefined;
|
||||||
@ -219,8 +220,8 @@ export const useGenerationState = (
|
|||||||
|
|
||||||
// Показываем уведомление о начале генерации
|
// Показываем уведомление о начале генерации
|
||||||
showNotification(
|
showNotification(
|
||||||
'Генерация стикера',
|
getTranslation('generation_title'),
|
||||||
'Отправка запроса...',
|
getTranslation('generation_sending'),
|
||||||
{ isLoading: true, showButtons: false }
|
{ isLoading: true, showButtons: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -244,8 +245,8 @@ export const useGenerationState = (
|
|||||||
|
|
||||||
// Вызываем handleRequestTimeout из useNotifications через showNotification
|
// Вызываем handleRequestTimeout из useNotifications через showNotification
|
||||||
showNotification(
|
showNotification(
|
||||||
'Генерация стикера',
|
getTranslation('generation_title'),
|
||||||
'Возникли проблемы с подключением. Пожалуйста, проверьте интернет-соединение.',
|
getTranslation('generation_connection_error'),
|
||||||
{ isLoading: true, showButtons: false }
|
{ isLoading: true, showButtons: false }
|
||||||
);
|
);
|
||||||
}, 6000);
|
}, 6000);
|
||||||
@ -310,8 +311,8 @@ export const useGenerationState = (
|
|||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
|
|
||||||
showNotification(
|
showNotification(
|
||||||
'Недопустимый промпт',
|
getTranslation('generation_invalid_prompt_title'),
|
||||||
'Промпт содержит недопустимый контент. Пожалуйста, используйте более нейтральные формулировки.',
|
getTranslation('generation_invalid_prompt_message'),
|
||||||
{ isLoading: false, showGalleryButton: false, showButtons: true }
|
{ isLoading: false, showGalleryButton: false, showButtons: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -343,23 +344,23 @@ export const useGenerationState = (
|
|||||||
const minutes = Math.floor(estimatedTime / 60);
|
const minutes = Math.floor(estimatedTime / 60);
|
||||||
const seconds = estimatedTime % 60;
|
const seconds = estimatedTime % 60;
|
||||||
const timeString = minutes > 0
|
const timeString = minutes > 0
|
||||||
? `${minutes} мин ${seconds} сек`
|
? `${minutes} ${getTranslation('time_minutes_short')} ${seconds} ${getTranslation('time_seconds_short')}`
|
||||||
: `${seconds} сек`;
|
: `${seconds} ${getTranslation('time_seconds_short')}`;
|
||||||
|
|
||||||
// Форматируем сообщение
|
// Форматируем сообщение
|
||||||
showNotification(
|
showNotification(
|
||||||
'Генерация стикера',
|
getTranslation('generation_title'),
|
||||||
`Создание стикеров началось!\n` +
|
`${getTranslation('generation_started')}\n` +
|
||||||
`Позиция в очереди: ${result.queue_position}\n` +
|
`${getTranslation('generation_queue_position', result.queue_position)}\n` +
|
||||||
`Время ожидания: ${timeString}\n\n` +
|
`${getTranslation('generation_wait_time', timeString)}\n\n` +
|
||||||
`Результат будет доступен в галерее после завершения генерации.`,
|
`${getTranslation('generation_result_available')}`,
|
||||||
{ isLoading: false, showButtons: true }
|
{ isLoading: false, showButtons: true }
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
showNotification(
|
showNotification(
|
||||||
'Генерация стикера',
|
getTranslation('generation_title'),
|
||||||
`Создание стикеров началось!\n\n` +
|
`${getTranslation('generation_started')}\n\n` +
|
||||||
`Результат будет доступен в галерее после завершения генерации.`,
|
`${getTranslation('generation_result_available')}`,
|
||||||
{ isLoading: false, showButtons: true }
|
{ isLoading: false, showButtons: true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -388,8 +389,8 @@ export const useGenerationState = (
|
|||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
|
|
||||||
showNotification(
|
showNotification(
|
||||||
'Ошибка',
|
getTranslation('generation_error_title'),
|
||||||
'Не удалось начать генерацию',
|
getTranslation('generation_error_message'),
|
||||||
{ isLoading: false, showGalleryButton: false, showButtons: true }
|
{ isLoading: false, showGalleryButton: false, showButtons: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { NotificationState } from '../types/generation';
|
import { NotificationState } from '../types/generation';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Хук для управления уведомлениями
|
* Хук для управления уведомлениями
|
||||||
@ -15,7 +16,7 @@ export const useNotifications = () => {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [showGalleryButton, setShowGalleryButton] = useState(true);
|
const [showGalleryButton, setShowGalleryButton] = useState(true);
|
||||||
const [showButtons, setShowButtons] = useState(true);
|
const [showButtons, setShowButtons] = useState(true);
|
||||||
const [continueButtonText, setContinueButtonText] = useState('Продолжить');
|
const [continueButtonText, setContinueButtonText] = useState(getTranslation('continue'));
|
||||||
|
|
||||||
// Состояния для модального окна с токенами
|
// Состояния для модального окна с токенами
|
||||||
const [showTokensModal, setShowTokensModal] = useState(false);
|
const [showTokensModal, setShowTokensModal] = useState(false);
|
||||||
@ -26,13 +27,10 @@ export const useNotifications = () => {
|
|||||||
* Функция для обработки таймаута запроса
|
* Функция для обработки таймаута запроса
|
||||||
*/
|
*/
|
||||||
const handleRequestTimeout = useCallback(() => {
|
const handleRequestTimeout = useCallback(() => {
|
||||||
setNotificationMessage(
|
setNotificationMessage(getTranslation('connection_error'));
|
||||||
'Возникли проблемы с подключением. Пожалуйста, проверьте интернет-соединение.\n\n' +
|
|
||||||
'Запрос все еще обрабатывается, но это может занять больше времени, чем обычно.'
|
|
||||||
);
|
|
||||||
// Добавляем кнопку отмены, чтобы пользователь мог прервать запрос
|
// Добавляем кнопку отмены, чтобы пользователь мог прервать запрос
|
||||||
setShowButtons(true);
|
setShowButtons(true);
|
||||||
setContinueButtonText('Отменить');
|
setContinueButtonText(getTranslation('cancel'));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,7 +42,7 @@ export const useNotifications = () => {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setShowGalleryButton(false);
|
setShowGalleryButton(false);
|
||||||
setShowButtons(true);
|
setShowButtons(true);
|
||||||
setContinueButtonText('Закрыть');
|
setContinueButtonText(getTranslation('close'));
|
||||||
setIsNotificationVisible(true);
|
setIsNotificationVisible(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -62,7 +60,7 @@ export const useNotifications = () => {
|
|||||||
setIsLoading(options?.isLoading ?? false);
|
setIsLoading(options?.isLoading ?? false);
|
||||||
setShowGalleryButton(options?.showGalleryButton ?? true);
|
setShowGalleryButton(options?.showGalleryButton ?? true);
|
||||||
setShowButtons(options?.showButtons ?? true);
|
setShowButtons(options?.showButtons ?? true);
|
||||||
setContinueButtonText(options?.continueButtonText ?? 'Продолжить');
|
setContinueButtonText(options?.continueButtonText ?? getTranslation('continue'));
|
||||||
setIsNotificationVisible(true);
|
setIsNotificationVisible(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -95,13 +93,13 @@ export const useNotifications = () => {
|
|||||||
*/
|
*/
|
||||||
const showFeedbackSentNotification = useCallback(() => {
|
const showFeedbackSentNotification = useCallback(() => {
|
||||||
showNotification(
|
showNotification(
|
||||||
'Спасибо за обратную связь',
|
getTranslation('feedback_thanks'),
|
||||||
'Ваше сообщение успешно отправлено',
|
getTranslation('feedback_sent'),
|
||||||
{
|
{
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
showGalleryButton: false,
|
showGalleryButton: false,
|
||||||
showButtons: true,
|
showButtons: true,
|
||||||
continueButtonText: 'Закрыть'
|
continueButtonText: getTranslation('close')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [showNotification]);
|
}, [showNotification]);
|
||||||
@ -111,13 +109,13 @@ export const useNotifications = () => {
|
|||||||
*/
|
*/
|
||||||
const showPaymentSuccessNotification = useCallback((tokens: number, bonusTokens: number) => {
|
const showPaymentSuccessNotification = useCallback((tokens: number, bonusTokens: number) => {
|
||||||
showNotification(
|
showNotification(
|
||||||
'Оплата успешна!',
|
getTranslation('payment_success'),
|
||||||
`Вы успешно приобрели ${tokens + bonusTokens} токенов.`,
|
getTranslation('tokens_purchased', tokens + bonusTokens),
|
||||||
{
|
{
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
showGalleryButton: false,
|
showGalleryButton: false,
|
||||||
showButtons: true,
|
showButtons: true,
|
||||||
continueButtonText: 'Закрыть'
|
continueButtonText: getTranslation('close')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [showNotification]);
|
}, [showNotification]);
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { GeneratedImage } from '../types/api';
|
|||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
import EmojiPickerModal from '../components/shared/EmojiPickerModal';
|
import EmojiPickerModal from '../components/shared/EmojiPickerModal';
|
||||||
import ValidationModal from '../components/shared/ValidationModal';
|
import ValidationModal from '../components/shared/ValidationModal';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
const AddStickerToPackScreen: React.FC = () => {
|
const AddStickerToPackScreen: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -47,7 +48,7 @@ const AddStickerToPackScreen: React.FC = () => {
|
|||||||
setError(null);
|
setError(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при загрузке данных:', err);
|
console.error('Ошибка при загрузке данных:', err);
|
||||||
setError('Не удалось загрузить данные');
|
setError(getTranslation('failed_to_load_data'));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@ -114,15 +115,15 @@ const AddStickerToPackScreen: React.FC = () => {
|
|||||||
// Обработчик добавления стикеров
|
// Обработчик добавления стикеров
|
||||||
const handleAddStickers = async () => {
|
const handleAddStickers = async () => {
|
||||||
if (selectedImages.length === 0) {
|
if (selectedImages.length === 0) {
|
||||||
setValidationTitle('Стикеры не выбраны');
|
setValidationTitle(getTranslation('stickers_not_selected'));
|
||||||
setValidationMessage('Пожалуйста, выберите хотя бы одно изображение для добавления в стикерпак.');
|
setValidationMessage(getTranslation('select_at_least_one_image'));
|
||||||
setIsValidationModalVisible(true);
|
setIsValidationModalVisible(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!packName) {
|
if (!packName) {
|
||||||
setValidationTitle('Ошибка');
|
setValidationTitle(getTranslation('error'));
|
||||||
setValidationMessage('Не указано имя стикерпака.');
|
setValidationMessage(getTranslation('pack_name_not_specified'));
|
||||||
setIsValidationModalVisible(true);
|
setIsValidationModalVisible(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -156,8 +157,8 @@ const AddStickerToPackScreen: React.FC = () => {
|
|||||||
navigate('/packs');
|
navigate('/packs');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при добавлении стикеров:', err);
|
console.error('Ошибка при добавлении стикеров:', err);
|
||||||
setValidationTitle('Ошибка');
|
setValidationTitle(getTranslation('error'));
|
||||||
setValidationMessage('Не удалось добавить стикеры в стикерпак. Пожалуйста, попробуйте еще раз.');
|
setValidationMessage(getTranslation('failed_to_add_stickers'));
|
||||||
setIsValidationModalVisible(true);
|
setIsValidationModalVisible(true);
|
||||||
} finally {
|
} finally {
|
||||||
setAdding(false);
|
setAdding(false);
|
||||||
@ -171,17 +172,17 @@ const AddStickerToPackScreen: React.FC = () => {
|
|||||||
className={styles.backButton}
|
className={styles.backButton}
|
||||||
onClick={() => navigate('/packs')}
|
onClick={() => navigate('/packs')}
|
||||||
>
|
>
|
||||||
← Назад
|
{getTranslation('back_button')}
|
||||||
</button>
|
</button>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
Добавление стикеров в "{packTitle || packName}"
|
{getTranslation('add_stickers_to_pack_title', packTitle || packName)}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className={styles.loading}>
|
<div className={styles.loading}>
|
||||||
<p>Загрузка данных...</p>
|
<p>{getTranslation('loading_data')}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -194,12 +195,11 @@ const AddStickerToPackScreen: React.FC = () => {
|
|||||||
{!loading && (
|
{!loading && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<h2 className={styles.sectionTitle}>Выберите изображения для стикеров</h2>
|
<h2 className={styles.sectionTitle}>{getTranslation('select_images_for_stickers')}</h2>
|
||||||
|
|
||||||
{availableImages.length === 0 ? (
|
{availableImages.length === 0 ? (
|
||||||
<p className={styles.noImages}>
|
<p className={styles.noImages}>
|
||||||
У вас пока нет сгенерированных изображений.
|
{getTranslation('create_first')}
|
||||||
Сначала создайте изображения в разделе "Создать стикер".
|
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.imagesGrid}>
|
<div className={styles.imagesGrid}>
|
||||||
@ -215,7 +215,7 @@ const AddStickerToPackScreen: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={image.url || ''}
|
src={image.url || ''}
|
||||||
alt={`Изображение ${index + 1}`}
|
alt={getTranslation('image_alt', index + 1)}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -245,7 +245,7 @@ const AddStickerToPackScreen: React.FC = () => {
|
|||||||
onClick={handleAddStickers}
|
onClick={handleAddStickers}
|
||||||
disabled={adding}
|
disabled={adding}
|
||||||
>
|
>
|
||||||
{adding ? 'Добавление...' : 'Добавить стикеры'}
|
{adding ? getTranslation('adding') : getTranslation('add_stickers')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { paymentService } from '../services/paymentService';
|
|||||||
import apiService from '../services/api';
|
import apiService from '../services/api';
|
||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
import NotificationModal from '../components/shared/NotificationModal';
|
import NotificationModal from '../components/shared/NotificationModal';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
const TOKENS_PER_GENERATION = 10;
|
const TOKENS_PER_GENERATION = 10;
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ const CreateSticker: React.FC = () => {
|
|||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error checking balance:', error);
|
console.error('Error checking balance:', error);
|
||||||
alert('Произошла ошибка при проверке баланса. Попробуйте позже.');
|
alert(getTranslation('create_sticker_screen_balance_error'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -50,10 +51,10 @@ const CreateSticker: React.FC = () => {
|
|||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
Создание стикера
|
{getTranslation('create_sticker_screen_title')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className={styles.subtitle}>
|
<p className={styles.subtitle}>
|
||||||
Загрузите фотографию для создания стикера
|
{getTranslation('create_sticker_screen_subtitle')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -64,13 +65,13 @@ const CreateSticker: React.FC = () => {
|
|||||||
<div className={styles.uploadBox}>
|
<div className={styles.uploadBox}>
|
||||||
<span className={styles.uploadIcon}>📷</span>
|
<span className={styles.uploadIcon}>📷</span>
|
||||||
<span className={styles.uploadText}>
|
<span className={styles.uploadText}>
|
||||||
Нажмите чтобы выбрать фото
|
{getTranslation('create_sticker_screen_upload_text')}
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.uploadHint}>
|
<span className={styles.uploadHint}>
|
||||||
или перетащите файл сюда
|
{getTranslation('create_sticker_screen_upload_hint')}
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.tokenCost}>
|
<span className={styles.tokenCost}>
|
||||||
Стоимость: {TOKENS_PER_GENERATION} токенов
|
{getTranslation('create_sticker_screen_cost', TOKENS_PER_GENERATION)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,8 +92,8 @@ const CreateSticker: React.FC = () => {
|
|||||||
if (userData) {
|
if (userData) {
|
||||||
// Обновляем данные на основе полученной информации
|
// Обновляем данные на основе полученной информации
|
||||||
// Показываем модальное окно с информацией об успешной оплате
|
// Показываем модальное окно с информацией об успешной оплате
|
||||||
setNotificationTitle('Оплата успешна!');
|
setNotificationTitle(getTranslation('payment_success'));
|
||||||
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов.`);
|
setNotificationMessage(getTranslation('tokens_purchased', pack.tokens + pack.bonusTokens));
|
||||||
setShowNotificationModal(true);
|
setShowNotificationModal(true);
|
||||||
} else {
|
} else {
|
||||||
// Если данные не получены, делаем запрос на получение данных пользователя
|
// Если данные не получены, делаем запрос на получение данных пользователя
|
||||||
@ -101,8 +102,8 @@ const CreateSticker: React.FC = () => {
|
|||||||
const balance = await apiService.getBalance(getCurrentUserId());
|
const balance = await apiService.getBalance(getCurrentUserId());
|
||||||
|
|
||||||
// Показываем модальное окно с информацией об успешной оплате
|
// Показываем модальное окно с информацией об успешной оплате
|
||||||
setNotificationTitle('Оплата успешна!');
|
setNotificationTitle(getTranslation('payment_success'));
|
||||||
setNotificationMessage(`Вы успешно приобрели ${pack.tokens + pack.bonusTokens} токенов. Ваш текущий баланс: ${balance} токенов.`);
|
setNotificationMessage(getTranslation('profile_tokens_purchased', pack.tokens + pack.bonusTokens, balance));
|
||||||
setShowNotificationModal(true);
|
setShowNotificationModal(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка при обновлении данных пользователя:', error);
|
console.error('Ошибка при обновлении данных пользователя:', error);
|
||||||
@ -120,7 +121,7 @@ const CreateSticker: React.FC = () => {
|
|||||||
onGalleryClick={() => setShowNotificationModal(false)}
|
onGalleryClick={() => setShowNotificationModal(false)}
|
||||||
onContinueClick={() => setShowNotificationModal(false)}
|
onContinueClick={() => setShowNotificationModal(false)}
|
||||||
showGalleryButton={false}
|
showGalleryButton={false}
|
||||||
continueButtonText="Закрыть"
|
continueButtonText={getTranslation('close')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { getCurrentUserId } from '../constants/user';
|
|||||||
import EmojiPickerModal from '../components/shared/EmojiPickerModal';
|
import EmojiPickerModal from '../components/shared/EmojiPickerModal';
|
||||||
import ValidationModal from '../components/shared/ValidationModal';
|
import ValidationModal from '../components/shared/ValidationModal';
|
||||||
import customAnalyticsService from '../services/customAnalyticsService';
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Транслитерирует кириллический текст в латиницу.
|
* Транслитерирует кириллический текст в латиницу.
|
||||||
@ -160,15 +161,15 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
// Обработчик создания стикерпака
|
// Обработчик создания стикерпака
|
||||||
const handleCreateStickerPack = async () => {
|
const handleCreateStickerPack = async () => {
|
||||||
if (!title.trim()) {
|
if (!title.trim()) {
|
||||||
setValidationTitle('Название не указано');
|
setValidationTitle(getTranslation('create_sticker_pack_name_required_title'));
|
||||||
setValidationMessage('Пожалуйста, введите название для вашего стикерпака.');
|
setValidationMessage(getTranslation('create_sticker_pack_name_required_message'));
|
||||||
setIsValidationModalVisible(true);
|
setIsValidationModalVisible(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedImages.length === 0) {
|
if (selectedImages.length === 0) {
|
||||||
setValidationTitle('Стикеры не выбраны');
|
setValidationTitle(getTranslation('create_sticker_pack_no_stickers_title'));
|
||||||
setValidationMessage('Пожалуйста, выберите хотя бы одно изображение для вашего стикерпака.');
|
setValidationMessage(getTranslation('create_sticker_pack_no_stickers_message'));
|
||||||
setIsValidationModalVisible(true);
|
setIsValidationModalVisible(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -228,11 +229,11 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
|
|
||||||
// Проверяем, содержит ли сообщение об ошибке информацию о занятом имени
|
// Проверяем, содержит ли сообщение об ошибке информацию о занятом имени
|
||||||
if (errorMessage.includes('sticker set name is already occupied')) {
|
if (errorMessage.includes('sticker set name is already occupied')) {
|
||||||
setValidationTitle('Имя стикерпака уже занято');
|
setValidationTitle(getTranslation('create_sticker_pack_name_occupied_title'));
|
||||||
setValidationMessage('Стикерпак с таким именем уже существует в Telegram. Пожалуйста, измените название и попробуйте снова.');
|
setValidationMessage(getTranslation('create_sticker_pack_name_occupied_message'));
|
||||||
} else {
|
} else {
|
||||||
setValidationTitle('Ошибка при создании стикерпака');
|
setValidationTitle(getTranslation('create_sticker_pack_error_title'));
|
||||||
setValidationMessage('Не удалось создать стикерпак. Пожалуйста, попробуйте еще раз с другим названием или изображениями.');
|
setValidationMessage(getTranslation('create_sticker_pack_error_message'));
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsValidationModalVisible(true);
|
setIsValidationModalVisible(true);
|
||||||
@ -248,22 +249,22 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
className={styles.backButton}
|
className={styles.backButton}
|
||||||
onClick={() => navigate('/packs')}
|
onClick={() => navigate('/packs')}
|
||||||
>
|
>
|
||||||
← Назад
|
{getTranslation('create_sticker_pack_back')}
|
||||||
</button>
|
</button>
|
||||||
<h1 className={styles.title}>Создание стикерпака</h1>
|
<h1 className={styles.title}>{getTranslation('create_sticker_pack_title')}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.form}>
|
<div className={styles.form}>
|
||||||
<div className={styles.formGroup}>
|
<div className={styles.formGroup}>
|
||||||
<label htmlFor="title" className={styles.label}>Название стикерпака</label>
|
<label htmlFor="title" className={styles.label}>{getTranslation('sticker_pack_name_label')}</label>
|
||||||
<input
|
<input
|
||||||
id="title"
|
id="title"
|
||||||
type="text"
|
type="text"
|
||||||
className={styles.input}
|
className={styles.input}
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
placeholder="Введите название стикерпака"
|
placeholder={getTranslation('sticker_pack_name_label')}
|
||||||
ref={titleInputRef}
|
ref={titleInputRef}
|
||||||
onKeyDown={handleTitleKeyDown}
|
onKeyDown={handleTitleKeyDown}
|
||||||
enterKeyHint="done"
|
enterKeyHint="done"
|
||||||
@ -271,14 +272,13 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.formGroup}>
|
<div className={styles.formGroup}>
|
||||||
<label className={styles.label}>Выберите изображения для стикеров</label>
|
<label className={styles.label}>{getTranslation('create_sticker_pack_select_images')}</label>
|
||||||
|
|
||||||
{loading && <p className={styles.loading}>Загрузка изображений...</p>}
|
{loading && <p className={styles.loading}>{getTranslation('create_sticker_pack_loading')}</p>}
|
||||||
|
|
||||||
{!loading && availableImages.length === 0 && (
|
{!loading && availableImages.length === 0 && (
|
||||||
<p className={styles.noImages}>
|
<p className={styles.noImages}>
|
||||||
У вас пока нет сгенерированных изображений.
|
{getTranslation('create_first')}
|
||||||
Сначала создайте изображения в разделе "Создать стикер".
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={image.url || ''}
|
src={image.url || ''}
|
||||||
alt={`Изображение ${index + 1}`}
|
alt={getTranslation('create_sticker_pack_image_alt', index + 1)}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ const CreateStickerPack: React.FC = () => {
|
|||||||
onClick={handleCreateStickerPack}
|
onClick={handleCreateStickerPack}
|
||||||
disabled={creating}
|
disabled={creating}
|
||||||
>
|
>
|
||||||
{creating ? 'Создание...' : 'Создать стикерпак'}
|
{creating ? getTranslation('create_sticker_pack_creating') : getTranslation('create_sticker_pack_create')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import styles from './CropPhoto.module.css';
|
import styles from './CropPhoto.module.css';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
const CropPhoto: React.FC = () => {
|
const CropPhoto: React.FC = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -283,9 +284,9 @@ const CropPhoto: React.FC = () => {
|
|||||||
<button className={styles.backButton} onClick={() => navigate('/')}>
|
<button className={styles.backButton} onClick={() => navigate('/')}>
|
||||||
←
|
←
|
||||||
</button>
|
</button>
|
||||||
<h1 className={styles.title}>Обрезка фото</h1>
|
<h1 className={styles.title}>{getTranslation('crop_photo_title')}</h1>
|
||||||
<button className={styles.confirmButton} onClick={handleConfirm}>
|
<button className={styles.confirmButton} onClick={handleConfirm}>
|
||||||
Готово
|
{getTranslation('crop_photo_done')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -337,7 +338,7 @@ const CropPhoto: React.FC = () => {
|
|||||||
className={styles.zoomSlider}
|
className={styles.zoomSlider}
|
||||||
/>
|
/>
|
||||||
<div className={styles.hint}>
|
<div className={styles.hint}>
|
||||||
Отрегулируйте масштаб и положение фото
|
{getTranslation('crop_photo_hint')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import ImageWithFallback from '../components/shared/ImageWithFallback';
|
|||||||
import NotificationModal from '../components/shared/NotificationModal';
|
import NotificationModal from '../components/shared/NotificationModal';
|
||||||
import customAnalyticsService from '../services/customAnalyticsService';
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
const GalleryScreen: React.FC = () => {
|
const GalleryScreen: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -31,11 +32,11 @@ const GalleryScreen: React.FC = () => {
|
|||||||
|
|
||||||
// Функция для расчета времени ожидания
|
// Функция для расчета времени ожидания
|
||||||
const getEstimatedWaitTime = (queuePosition: number | null): string => {
|
const getEstimatedWaitTime = (queuePosition: number | null): string => {
|
||||||
if (queuePosition === null) return 'Генерация началась';
|
if (queuePosition === null) return getTranslation('gallery_generation_started');
|
||||||
|
|
||||||
const seconds = apiService.calculateEstimatedWaitTime(queuePosition);
|
const seconds = apiService.calculateEstimatedWaitTime(queuePosition);
|
||||||
if (seconds < 60) return `${seconds} сек`;
|
if (seconds < 60) return getTranslation('gallery_seconds', seconds);
|
||||||
return `${Math.floor(seconds / 60)} мин ${seconds % 60} сек`;
|
return getTranslation('gallery_minutes_seconds', Math.floor(seconds / 60), seconds % 60);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Обработчики для режима удаления
|
// Обработчики для режима удаления
|
||||||
@ -268,7 +269,7 @@ const GalleryScreen: React.FC = () => {
|
|||||||
style={{ '--pull-distance': `${pullDistance}px` } as React.CSSProperties}
|
style={{ '--pull-distance': `${pullDistance}px` } as React.CSSProperties}
|
||||||
>
|
>
|
||||||
<div className={styles.refreshSpinner}></div>
|
<div className={styles.refreshSpinner}></div>
|
||||||
{refreshing && <span>Обновление...</span>}
|
{refreshing && <span>{getTranslation('gallery_refreshing')}</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -281,13 +282,13 @@ const GalleryScreen: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
Галерея стикеров
|
{getTranslation('gallery_title')}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className={styles.placeholder}>
|
<div className={styles.placeholder}>
|
||||||
Загрузка изображений...
|
{getTranslation('gallery_loading')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -299,13 +300,13 @@ const GalleryScreen: React.FC = () => {
|
|||||||
|
|
||||||
{!loading && !error && images.length === 0 && pendingTasks.length === 0 && (
|
{!loading && !error && images.length === 0 && pendingTasks.length === 0 && (
|
||||||
<div className={styles.placeholder}>
|
<div className={styles.placeholder}>
|
||||||
У вас пока нет сгенерированных стикеров
|
{getTranslation('gallery_empty')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && !loadingPendingTasks && pendingTasks.length > 0 && (
|
{!loading && !loadingPendingTasks && pendingTasks.length > 0 && (
|
||||||
<div className={styles.pendingTasksSection}>
|
<div className={styles.pendingTasksSection}>
|
||||||
<h2 className={styles.sectionTitle}>В процессе генерации</h2>
|
<h2 className={styles.sectionTitle}>{getTranslation('gallery_generating_section')}</h2>
|
||||||
<div className={styles.pendingTasksGrid}>
|
<div className={styles.pendingTasksGrid}>
|
||||||
{pendingTasks.map((task) => (
|
{pendingTasks.map((task) => (
|
||||||
<div key={task.task_id} className={styles.pendingTaskItem}>
|
<div key={task.task_id} className={styles.pendingTaskItem}>
|
||||||
@ -315,11 +316,11 @@ const GalleryScreen: React.FC = () => {
|
|||||||
<div className={styles.pendingTaskInfo}>
|
<div className={styles.pendingTaskInfo}>
|
||||||
<p className={styles.pendingTaskStatus}>
|
<p className={styles.pendingTaskStatus}>
|
||||||
{task.status === 'PENDING'
|
{task.status === 'PENDING'
|
||||||
? `В очереди: ${task.queue_position}`
|
? getTranslation('gallery_queue_position', task.queue_position)
|
||||||
: 'Генерация...'}
|
: getTranslation('gallery_generating')}
|
||||||
</p>
|
</p>
|
||||||
<p className={styles.pendingTaskTime}>
|
<p className={styles.pendingTaskTime}>
|
||||||
Осталось: {getEstimatedWaitTime(task.queue_position)}
|
{getTranslation('gallery_time_left', getEstimatedWaitTime(task.queue_position))}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -343,7 +344,7 @@ const GalleryScreen: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<ImageWithFallback
|
<ImageWithFallback
|
||||||
src={image.url || ''}
|
src={image.url || ''}
|
||||||
alt={`Стикер ${index + 1}`}
|
alt={getTranslation('gallery_sticker_alt', index + 1)}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
onClick={() => !isDeleteMode && image.url && setSelectedImage(image.url)}
|
onClick={() => !isDeleteMode && image.url && setSelectedImage(image.url)}
|
||||||
onContextMenu={(e: React.MouseEvent<HTMLDivElement>) => e.preventDefault()}
|
onContextMenu={(e: React.MouseEvent<HTMLDivElement>) => e.preventDefault()}
|
||||||
@ -376,12 +377,12 @@ const GalleryScreen: React.FC = () => {
|
|||||||
{/* Модальное окно подтверждения удаления */}
|
{/* Модальное окно подтверждения удаления */}
|
||||||
<NotificationModal
|
<NotificationModal
|
||||||
isVisible={!!selectedForDelete}
|
isVisible={!!selectedForDelete}
|
||||||
title="Удаление стикера"
|
title={getTranslation('gallery_delete_title')}
|
||||||
message="Вы уверены, что хотите удалить этот стикер?"
|
message={getTranslation('gallery_delete_message')}
|
||||||
isLoading={isDeleting}
|
isLoading={isDeleting}
|
||||||
showGalleryButton={true}
|
showGalleryButton={true}
|
||||||
galleryButtonText="Отмена"
|
galleryButtonText={getTranslation('gallery_delete_cancel')}
|
||||||
continueButtonText="Удалить"
|
continueButtonText={getTranslation('gallery_delete_confirm')}
|
||||||
onContinueClick={handleConfirmDelete}
|
onContinueClick={handleConfirmDelete}
|
||||||
onGalleryClick={() => setSelectedForDelete(null)}
|
onGalleryClick={() => setSelectedForDelete(null)}
|
||||||
isPrimaryGalleryButton={false}
|
isPrimaryGalleryButton={false}
|
||||||
@ -393,7 +394,7 @@ const GalleryScreen: React.FC = () => {
|
|||||||
className={styles.createButtonFixed}
|
className={styles.createButtonFixed}
|
||||||
onClick={handleCreateStickerPack}
|
onClick={handleCreateStickerPack}
|
||||||
>
|
>
|
||||||
Создать стикерпак
|
{getTranslation('gallery_create_pack')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import TokenPacksList from '../components/tokens/TokenPacksList';
|
|||||||
import { tokenPacks, TokenPack } from '../constants/tokenPacks';
|
import { tokenPacks, TokenPack } from '../constants/tokenPacks';
|
||||||
import { paymentService } from '../services/paymentService';
|
import { paymentService } from '../services/paymentService';
|
||||||
import NotificationModal from '../components/shared/NotificationModal';
|
import NotificationModal from '../components/shared/NotificationModal';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
const Profile: React.FC = () => {
|
const Profile: React.FC = () => {
|
||||||
const [stickersCount, setStickersCount] = useState<number>(0);
|
const [stickersCount, setStickersCount] = useState<number>(0);
|
||||||
@ -83,22 +84,22 @@ const Profile: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<h1 className={styles.title}>Профиль</h1>
|
<h1 className={styles.title}>{getTranslation('profile_title')}</h1>
|
||||||
<p className={styles.subtitle}>Ваша статистика и настройки</p>
|
<p className={styles.subtitle}>{getTranslation('profile_subtitle')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.compactStats}>
|
<div className={styles.compactStats}>
|
||||||
<div className={styles.statItem}>
|
<div className={styles.statItem}>
|
||||||
<span className={styles.statValue}>{loading ? '...' : stickersCount}</span>
|
<span className={styles.statValue}>{loading ? '...' : stickersCount}</span>
|
||||||
<span className={styles.statLabel}>Стикеров создано</span>
|
<span className={styles.statLabel}>{getTranslation('profile_stickers_created')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statItem}>
|
<div className={styles.statItem}>
|
||||||
<span className={styles.statValue}>{loading ? '...' : packsCount}</span>
|
<span className={styles.statValue}>{loading ? '...' : packsCount}</span>
|
||||||
<span className={styles.statLabel}>Стикерпаков</span>
|
<span className={styles.statLabel}>{getTranslation('profile_sticker_packs')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statItem}>
|
<div className={styles.statItem}>
|
||||||
<span className={styles.statValue}>{loading ? '...' : userBalance}</span>
|
<span className={styles.statValue}>{loading ? '...' : userBalance}</span>
|
||||||
<span className={styles.statLabel}>Токенов</span>
|
<span className={styles.statLabel}>{getTranslation('profile_tokens')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -111,12 +112,14 @@ const Profile: React.FC = () => {
|
|||||||
{/* Модальное окно успешной оплаты */}
|
{/* Модальное окно успешной оплаты */}
|
||||||
<NotificationModal
|
<NotificationModal
|
||||||
isVisible={showPaymentSuccessModal}
|
isVisible={showPaymentSuccessModal}
|
||||||
title="Оплата успешна!"
|
title={getTranslation('profile_payment_success')}
|
||||||
message={lastPurchasedPack ? `Вы успешно приобрели ${lastPurchasedPack.tokens + lastPurchasedPack.bonusTokens} токенов. Ваш текущий баланс: ${userBalance} токенов.` : "Оплата прошла успешно!"}
|
message={lastPurchasedPack
|
||||||
|
? getTranslation('profile_tokens_purchased', lastPurchasedPack.tokens + lastPurchasedPack.bonusTokens, userBalance)
|
||||||
|
: getTranslation('profile_payment_success')}
|
||||||
onGalleryClick={handleClosePaymentSuccessModal}
|
onGalleryClick={handleClosePaymentSuccessModal}
|
||||||
onContinueClick={handleClosePaymentSuccessModal}
|
onContinueClick={handleClosePaymentSuccessModal}
|
||||||
showGalleryButton={false}
|
showGalleryButton={false}
|
||||||
continueButtonText="Закрыть"
|
continueButtonText={getTranslation('profile_close')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { stickerService } from '../services/stickerService';
|
|||||||
import { getCurrentUserId } from '../constants/user';
|
import { getCurrentUserId } from '../constants/user';
|
||||||
import NotificationModal from '../components/shared/NotificationModal';
|
import NotificationModal from '../components/shared/NotificationModal';
|
||||||
import customAnalyticsService from '../services/customAnalyticsService';
|
import customAnalyticsService from '../services/customAnalyticsService';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
// Функция для удаления дописанной части из названия стикерпака
|
// Функция для удаления дописанной части из названия стикерпака
|
||||||
const cleanPackTitle = (title: string): string => {
|
const cleanPackTitle = (title: string): string => {
|
||||||
@ -61,7 +62,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
setError(null);
|
setError(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при загрузке стикерпаков:', err);
|
console.error('Ошибка при загрузке стикерпаков:', err);
|
||||||
setError('Не удалось загрузить список стикерпаков');
|
setError(getTranslation('stickerpacks_error'));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@ -114,7 +115,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
setPackToDelete(null);
|
setPackToDelete(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при удалении стикерпака:', err);
|
console.error('Ошибка при удалении стикерпака:', err);
|
||||||
alert('Не удалось удалить стикерпак');
|
alert(getTranslation('stickerpacks_delete_pack_error'));
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,7 +129,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
|
|
||||||
const handleDeleteSticker = (fileId: string) => {
|
const handleDeleteSticker = (fileId: string) => {
|
||||||
if (!fileId) {
|
if (!fileId) {
|
||||||
alert('Не удалось определить ID стикера');
|
alert(getTranslation('stickerpacks_sticker_id_error'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +159,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
setStickerToDelete(null);
|
setStickerToDelete(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при удалении стикера:', err);
|
console.error('Ошибка при удалении стикера:', err);
|
||||||
alert('Не удалось удалить стикер');
|
alert(getTranslation('stickerpacks_delete_sticker_error'));
|
||||||
setIsDeletingSticker(false);
|
setIsDeletingSticker(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,27 +169,27 @@ const StickerPacks: React.FC = () => {
|
|||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<h1 className={styles.title}>
|
<h1 className={styles.title}>
|
||||||
Мои стикерпаки
|
{getTranslation('stickerpacks_title')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className={styles.subtitle}>
|
<p className={styles.subtitle}>
|
||||||
Создавайте и публикуйте наборы стикеров в Telegram
|
{getTranslation('stickerpacks_subtitle')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className={styles.placeholder}>
|
<div className={styles.placeholder}>
|
||||||
<p>Загрузка стикерпаков...</p>
|
<p>{getTranslation('stickerpacks_loading')}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className={styles.error}>
|
<div className={styles.error}>
|
||||||
<p>{error}</p>
|
<p>{getTranslation('stickerpacks_error')}</p>
|
||||||
<button
|
<button
|
||||||
className={styles.retryButton}
|
className={styles.retryButton}
|
||||||
onClick={() => window.location.reload()}
|
onClick={() => window.location.reload()}
|
||||||
>
|
>
|
||||||
Повторить
|
{getTranslation('stickerpacks_retry_button')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -197,13 +198,13 @@ const StickerPacks: React.FC = () => {
|
|||||||
<div className={styles.placeholder}>
|
<div className={styles.placeholder}>
|
||||||
<span className={styles.placeholderIcon}>📦</span>
|
<span className={styles.placeholderIcon}>📦</span>
|
||||||
<p className={styles.placeholderText}>
|
<p className={styles.placeholderText}>
|
||||||
У вас пока нет стикерпаков
|
{getTranslation('stickerpacks_empty')}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
className={styles.createButton}
|
className={styles.createButton}
|
||||||
onClick={handleCreateStickerPack}
|
onClick={handleCreateStickerPack}
|
||||||
>
|
>
|
||||||
Создать стикерпак
|
{getTranslation('stickerpacks_create_button')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -219,10 +220,10 @@ const StickerPacks: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<div className={styles.packHeader}>
|
<div className={styles.packHeader}>
|
||||||
<h3 className={styles.packTitle}>
|
<h3 className={styles.packTitle}>
|
||||||
Стикерпак: <span className={styles.packTitleName}>{cleanPackTitle(pack.title)}</span>
|
{getTranslation('stickerpacks_pack_title_prefix')}: <span className={styles.packTitleName}>{cleanPackTitle(pack.title)}</span>
|
||||||
</h3>
|
</h3>
|
||||||
<p className={styles.packStats}>
|
<p className={styles.packStats}>
|
||||||
{pack.stickers ? pack.stickers.length : 0} / 49 стикеров
|
{getTranslation('stickerpacks_pack_stats', pack.stickers ? pack.stickers.length : 0)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.packStickers}>
|
<div className={styles.packStickers}>
|
||||||
@ -231,7 +232,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
{sticker.file_url ? (
|
{sticker.file_url ? (
|
||||||
<img
|
<img
|
||||||
src={sticker.file_url}
|
src={sticker.file_url}
|
||||||
alt={`Стикер ${idx + 1}`}
|
alt={getTranslation('stickerpacks_sticker_alt', idx + 1)}
|
||||||
className={styles.stickerImage}
|
className={styles.stickerImage}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -249,7 +250,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
handleDeletePack(pack.name);
|
handleDeletePack(pack.name);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Удалить
|
{getTranslation('stickerpacks_delete_button')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -259,7 +260,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
className={styles.createButtonFixed}
|
className={styles.createButtonFixed}
|
||||||
onClick={handleCreateStickerPack}
|
onClick={handleCreateStickerPack}
|
||||||
>
|
>
|
||||||
Создать стикерпак
|
{getTranslation('stickerpacks_create_button')}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -271,7 +272,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
className={styles.backButton}
|
className={styles.backButton}
|
||||||
onClick={handleCloseDetails}
|
onClick={handleCloseDetails}
|
||||||
>
|
>
|
||||||
← Назад
|
{getTranslation('stickerpacks_back_button')}
|
||||||
</button>
|
</button>
|
||||||
<h2 className={styles.detailsTitle}>{cleanPackTitle(selectedPack.title)}</h2>
|
<h2 className={styles.detailsTitle}>{cleanPackTitle(selectedPack.title)}</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -282,14 +283,14 @@ const StickerPacks: React.FC = () => {
|
|||||||
className={styles.addStickerButton}
|
className={styles.addStickerButton}
|
||||||
onClick={() => navigate(`/add-sticker/${selectedPack.name}`)}
|
onClick={() => navigate(`/add-sticker/${selectedPack.name}`)}
|
||||||
>
|
>
|
||||||
Добавить стикер
|
{getTranslation('stickerpacks_add_sticker_button')}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={styles.openInTelegramButton}
|
className={styles.openInTelegramButton}
|
||||||
onClick={handleOpenInTelegram}
|
onClick={handleOpenInTelegram}
|
||||||
>
|
>
|
||||||
Открыть в Telegram
|
{getTranslation('stickerpacks_open_telegram_button')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -299,7 +300,7 @@ const StickerPacks: React.FC = () => {
|
|||||||
{sticker.file_url ? (
|
{sticker.file_url ? (
|
||||||
<img
|
<img
|
||||||
src={sticker.file_url}
|
src={sticker.file_url}
|
||||||
alt={`Стикер ${index + 1}`}
|
alt={getTranslation('stickerpacks_sticker_alt', index + 1)}
|
||||||
className={styles.stickerImage}
|
className={styles.stickerImage}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -326,12 +327,12 @@ const StickerPacks: React.FC = () => {
|
|||||||
{/* Модальное окно подтверждения удаления стикерпака */}
|
{/* Модальное окно подтверждения удаления стикерпака */}
|
||||||
<NotificationModal
|
<NotificationModal
|
||||||
isVisible={!!packToDelete}
|
isVisible={!!packToDelete}
|
||||||
title="Удаление стикерпака"
|
title={getTranslation('stickerpacks_delete_pack_title')}
|
||||||
message="Вы уверены, что хотите удалить этот стикерпак?"
|
message={getTranslation('stickerpacks_delete_pack_message')}
|
||||||
isLoading={isDeleting}
|
isLoading={isDeleting}
|
||||||
showGalleryButton={true}
|
showGalleryButton={true}
|
||||||
galleryButtonText="Отмена"
|
galleryButtonText={getTranslation('stickerpacks_cancel_button')}
|
||||||
continueButtonText="Удалить"
|
continueButtonText={getTranslation('stickerpacks_confirm_delete_button')}
|
||||||
onContinueClick={handleConfirmDelete}
|
onContinueClick={handleConfirmDelete}
|
||||||
onGalleryClick={() => setPackToDelete(null)}
|
onGalleryClick={() => setPackToDelete(null)}
|
||||||
isPrimaryGalleryButton={false}
|
isPrimaryGalleryButton={false}
|
||||||
@ -340,12 +341,12 @@ const StickerPacks: React.FC = () => {
|
|||||||
{/* Модальное окно подтверждения удаления стикера */}
|
{/* Модальное окно подтверждения удаления стикера */}
|
||||||
<NotificationModal
|
<NotificationModal
|
||||||
isVisible={!!stickerToDelete}
|
isVisible={!!stickerToDelete}
|
||||||
title="Удаление стикера"
|
title={getTranslation('stickerpacks_delete_sticker_title')}
|
||||||
message="Вы уверены, что хотите удалить этот стикер?"
|
message={getTranslation('stickerpacks_delete_sticker_message')}
|
||||||
isLoading={isDeletingSticker}
|
isLoading={isDeletingSticker}
|
||||||
showGalleryButton={true}
|
showGalleryButton={true}
|
||||||
galleryButtonText="Отмена"
|
galleryButtonText={getTranslation('stickerpacks_cancel_button')}
|
||||||
continueButtonText="Удалить"
|
continueButtonText={getTranslation('stickerpacks_confirm_delete_button')}
|
||||||
onContinueClick={handleConfirmDeleteSticker}
|
onContinueClick={handleConfirmDeleteSticker}
|
||||||
onGalleryClick={() => setStickerToDelete(null)}
|
onGalleryClick={() => setStickerToDelete(null)}
|
||||||
isPrimaryGalleryButton={false}
|
isPrimaryGalleryButton={false}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
|||||||
import styles from './OnboardingHowTo.module.css';
|
import styles from './OnboardingHowTo.module.css';
|
||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../../constants/user';
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
const OnboardingHowTo: React.FC = () => {
|
const OnboardingHowTo: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -42,11 +43,11 @@ const OnboardingHowTo: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<OnboardingLayout
|
<OnboardingLayout
|
||||||
title="Как создать стикер"
|
title={getTranslation('how_to_title')}
|
||||||
currentStep={2}
|
currentStep={2}
|
||||||
totalSteps={3}
|
totalSteps={3}
|
||||||
primaryButtonText="Далее"
|
primaryButtonText={getTranslation('next')}
|
||||||
secondaryButtonText="Пропустить"
|
secondaryButtonText={getTranslation('skip')}
|
||||||
onPrimaryClick={handleNext}
|
onPrimaryClick={handleNext}
|
||||||
onSecondaryClick={handleSkip}
|
onSecondaryClick={handleSkip}
|
||||||
>
|
>
|
||||||
@ -54,24 +55,24 @@ const OnboardingHowTo: React.FC = () => {
|
|||||||
<div className={styles.step}>
|
<div className={styles.step}>
|
||||||
<div className={styles.stepNumber}>1</div>
|
<div className={styles.stepNumber}>1</div>
|
||||||
<div className={styles.stepContent}>
|
<div className={styles.stepContent}>
|
||||||
<h3 className={styles.stepTitle}>Загрузите фото</h3>
|
<h3 className={styles.stepTitle}>{getTranslation('upload_photo_title')}</h3>
|
||||||
<p className={styles.stepDescription}>Выберите фотографию и обрежьте её в квадрат</p>
|
<p className={styles.stepDescription}>{getTranslation('upload_photo_description')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.step}>
|
<div className={styles.step}>
|
||||||
<div className={styles.stepNumber}>2</div>
|
<div className={styles.stepNumber}>2</div>
|
||||||
<div className={styles.stepContent}>
|
<div className={styles.stepContent}>
|
||||||
<h3 className={styles.stepTitle}>Выберите стиль</h3>
|
<h3 className={styles.stepTitle}>{getTranslation('choose_style_title')}</h3>
|
||||||
<p className={styles.stepDescription}>Подберите подходящий стиль для вашего стикера</p>
|
<p className={styles.stepDescription}>{getTranslation('choose_style_description')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.step}>
|
<div className={styles.step}>
|
||||||
<div className={styles.stepNumber}>3</div>
|
<div className={styles.stepNumber}>3</div>
|
||||||
<div className={styles.stepContent}>
|
<div className={styles.stepContent}>
|
||||||
<h3 className={styles.stepTitle}>Создайте стикер</h3>
|
<h3 className={styles.stepTitle}>{getTranslation('create_sticker_title')}</h3>
|
||||||
<p className={styles.stepDescription}>Дождитесь генерации и сохраните результат</p>
|
<p className={styles.stepDescription}>{getTranslation('create_sticker_description')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
|||||||
import styles from './OnboardingStickerPacks.module.css';
|
import styles from './OnboardingStickerPacks.module.css';
|
||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../../constants/user';
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
const OnboardingStickerPacks: React.FC = () => {
|
const OnboardingStickerPacks: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -42,11 +43,11 @@ const OnboardingStickerPacks: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<OnboardingLayout
|
<OnboardingLayout
|
||||||
title="Создавайте стикерпаки"
|
title={getTranslation('sticker_packs_title')}
|
||||||
currentStep={3}
|
currentStep={3}
|
||||||
totalSteps={3}
|
totalSteps={3}
|
||||||
primaryButtonText="Начать"
|
primaryButtonText={getTranslation('start')}
|
||||||
secondaryButtonText="Пропустить"
|
secondaryButtonText={getTranslation('skip')}
|
||||||
onPrimaryClick={handleStart}
|
onPrimaryClick={handleStart}
|
||||||
onSecondaryClick={handleStart}
|
onSecondaryClick={handleStart}
|
||||||
>
|
>
|
||||||
@ -54,24 +55,24 @@ const OnboardingStickerPacks: React.FC = () => {
|
|||||||
<div className={styles.step}>
|
<div className={styles.step}>
|
||||||
<div className={styles.stepNumber}>1</div>
|
<div className={styles.stepNumber}>1</div>
|
||||||
<div className={styles.stepContent}>
|
<div className={styles.stepContent}>
|
||||||
<h3 className={styles.stepTitle}>Выберите стикеры</h3>
|
<h3 className={styles.stepTitle}>{getTranslation('select_stickers_title')}</h3>
|
||||||
<p className={styles.stepDescription}>Отберите лучшие стикеры из вашей галереи</p>
|
<p className={styles.stepDescription}>{getTranslation('select_stickers_description')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.step}>
|
<div className={styles.step}>
|
||||||
<div className={styles.stepNumber}>2</div>
|
<div className={styles.stepNumber}>2</div>
|
||||||
<div className={styles.stepContent}>
|
<div className={styles.stepContent}>
|
||||||
<h3 className={styles.stepTitle}>Организуйте набор</h3>
|
<h3 className={styles.stepTitle}>{getTranslation('organize_pack_title')}</h3>
|
||||||
<p className={styles.stepDescription}>Расположите стикеры в нужном порядке</p>
|
<p className={styles.stepDescription}>{getTranslation('organize_pack_description')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.step}>
|
<div className={styles.step}>
|
||||||
<div className={styles.stepNumber}>3</div>
|
<div className={styles.stepNumber}>3</div>
|
||||||
<div className={styles.stepContent}>
|
<div className={styles.stepContent}>
|
||||||
<h3 className={styles.stepTitle}>Опубликуйте</h3>
|
<h3 className={styles.stepTitle}>{getTranslation('publish_title')}</h3>
|
||||||
<p className={styles.stepDescription}>Создайте стикерпак в Telegram одним нажатием</p>
|
<p className={styles.stepDescription}>{getTranslation('publish_description')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import OnboardingLayout from '../../components/shared/OnboardingLayout';
|
|||||||
import { images } from '../../assets';
|
import { images } from '../../assets';
|
||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../../constants/user';
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
const OnboardingWelcome: React.FC = () => {
|
const OnboardingWelcome: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -42,13 +43,13 @@ const OnboardingWelcome: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<OnboardingLayout
|
<OnboardingLayout
|
||||||
title="Добро пожаловать в Sticker Generator"
|
title={getTranslation('welcome_title')}
|
||||||
image={images.onboard1}
|
image={images.onboard1}
|
||||||
description="Создавайте уникальные стикеры из ваших фотографий с помощью искусственного интеллекта"
|
description={getTranslation('welcome_description')}
|
||||||
currentStep={1}
|
currentStep={1}
|
||||||
totalSteps={3}
|
totalSteps={3}
|
||||||
primaryButtonText="Далее"
|
primaryButtonText={getTranslation('next')}
|
||||||
secondaryButtonText="Пропустить"
|
secondaryButtonText={getTranslation('skip')}
|
||||||
onPrimaryClick={handleNext}
|
onPrimaryClick={handleNext}
|
||||||
onSecondaryClick={handleSkip}
|
onSecondaryClick={handleSkip}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import styles from './TermsAndConditions.module.css';
|
|||||||
import { images } from '../../assets';
|
import { images } from '../../assets';
|
||||||
import customAnalyticsService from '../../services/customAnalyticsService';
|
import customAnalyticsService from '../../services/customAnalyticsService';
|
||||||
import { getCurrentUserId } from '../../constants/user';
|
import { getCurrentUserId } from '../../constants/user';
|
||||||
|
import { getTranslation } from '../../constants/translations';
|
||||||
|
|
||||||
const TermsAndConditions: React.FC = () => {
|
const TermsAndConditions: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -63,10 +64,10 @@ const TermsAndConditions: React.FC = () => {
|
|||||||
<img src={images.shieldIcon} alt="" className={styles.icon} />
|
<img src={images.shieldIcon} alt="" className={styles.icon} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className={styles.title}>Условия и Конфиденциальность</h1>
|
<h1 className={styles.title}>{getTranslation('terms_title')}</h1>
|
||||||
|
|
||||||
<p className={styles.description}>
|
<p className={styles.description}>
|
||||||
Продолжая использовать это приложение, вы соглашаетесь с нашими Условиями использования и Политикой конфиденциальности.
|
{getTranslation('terms_description')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className={styles.links}>
|
<div className={styles.links}>
|
||||||
@ -77,7 +78,7 @@ const TermsAndConditions: React.FC = () => {
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
onClick={() => handlePolicyLinkClick("https://telegra.ph/Polzovatelskoe-soglashenie-03-19-13")}
|
onClick={() => handlePolicyLinkClick("https://telegra.ph/Polzovatelskoe-soglashenie-03-19-13")}
|
||||||
>
|
>
|
||||||
Условия использования
|
{getTranslation('terms_of_use')}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://telegra.ph/Politika-konfidencialnosti-03-19-10"
|
href="https://telegra.ph/Politika-konfidencialnosti-03-19-10"
|
||||||
@ -86,7 +87,7 @@ const TermsAndConditions: React.FC = () => {
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
onClick={() => handlePolicyLinkClick("https://telegra.ph/Politika-konfidencialnosti-03-19-10")}
|
onClick={() => handlePolicyLinkClick("https://telegra.ph/Politika-konfidencialnosti-03-19-10")}
|
||||||
>
|
>
|
||||||
Политика конфиденциальности
|
{getTranslation('privacy_policy')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -95,13 +96,13 @@ const TermsAndConditions: React.FC = () => {
|
|||||||
className={styles.primaryButton}
|
className={styles.primaryButton}
|
||||||
onClick={handleAccept}
|
onClick={handleAccept}
|
||||||
>
|
>
|
||||||
Принять
|
{getTranslation('accept')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={styles.secondaryButton}
|
className={styles.secondaryButton}
|
||||||
onClick={handleDecline}
|
onClick={handleDecline}
|
||||||
>
|
>
|
||||||
Отклонить
|
{getTranslation('decline')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import translateService from './translateService';
|
|||||||
import { getCurrentUserId, isTelegramWebAppAvailable } from '../constants/user';
|
import { getCurrentUserId, isTelegramWebAppAvailable } from '../constants/user';
|
||||||
import { trackStickerGeneration, trackRejectedPrompt } from './analyticsService';
|
import { trackStickerGeneration, trackRejectedPrompt } from './analyticsService';
|
||||||
import memeService from './memeService';
|
import memeService from './memeService';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
const API_BASE_URL = 'https://stickerserver.gymnasticstuff.uk';
|
const API_BASE_URL = 'https://stickerserver.gymnasticstuff.uk';
|
||||||
|
|
||||||
@ -290,7 +291,7 @@ const apiService = {
|
|||||||
// Не продолжаем генерацию, возвращаем ошибку с сообщением
|
// Не продолжаем генерацию, возвращаем ошибку с сообщением
|
||||||
return {
|
return {
|
||||||
translationFailed: true,
|
translationFailed: true,
|
||||||
usedPrompt: 'Недопустимый промпт', // Сообщение для пользователя
|
usedPrompt: getTranslation('invalid_prompt'), // Сообщение для пользователя
|
||||||
errorDetails: translationResult.text // Детали ошибки для отладки
|
errorDetails: translationResult.text // Детали ошибки для отладки
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -519,7 +520,7 @@ const apiService = {
|
|||||||
// Возвращаем ошибку
|
// Возвращаем ошибку
|
||||||
return {
|
return {
|
||||||
translationFailed: true,
|
translationFailed: true,
|
||||||
usedPrompt: 'Недопустимый промпт',
|
usedPrompt: getTranslation('invalid_prompt'),
|
||||||
errorDetails: translationResult.text
|
errorDetails: translationResult.text
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// URL для LibreTranslate API (сохраняем, но не используем)
|
// URL для LibreTranslate API (сохраняем, но не используем)
|
||||||
const TRANSLATE_API_URL = 'https://translate.maxdev.keenetic.pro/translate';
|
const TRANSLATE_API_URL = 'https://translate.maxdev.keenetic.pro/translate';
|
||||||
|
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
// URL и ключ для OpenRouter API
|
// URL и ключ для OpenRouter API
|
||||||
const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
|
const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
|
||||||
const OPENROUTER_API_KEY = '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';
|
const OPENROUTER_API_KEY = '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';
|
||||||
@ -241,7 +243,7 @@ const translateService = {
|
|||||||
// Если это основная модель, сразу возвращаем ошибку
|
// Если это основная модель, сразу возвращаем ошибку
|
||||||
// и не пытаемся использовать резервную модель
|
// и не пытаемся использовать резервную модель
|
||||||
if (model === TRANSLATION_MODELS.PRIMARY) {
|
if (model === TRANSLATION_MODELS.PRIMARY) {
|
||||||
return { success: false, text: 'Недопустимый промпт' };
|
return { success: false, text: getTranslation('invalid_prompt') };
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Model refused to translate');
|
throw new Error('Model refused to translate');
|
||||||
}
|
}
|
||||||
@ -268,7 +270,7 @@ const translateService = {
|
|||||||
// Если это была последняя модель, прекращаем попытки
|
// Если это была последняя модель, прекращаем попытки
|
||||||
if (i === models.length - 1) {
|
if (i === models.length - 1) {
|
||||||
console.error('Все попытки перевода через LLM не удались');
|
console.error('Все попытки перевода через LLM не удались');
|
||||||
return { success: false, text: 'Недопустимый промпт' };
|
return { success: false, text: getTranslation('invalid_prompt') };
|
||||||
}
|
}
|
||||||
// Иначе продолжаем со следующей моделью (только при технических ошибках)
|
// Иначе продолжаем со следующей моделью (только при технических ошибках)
|
||||||
}
|
}
|
||||||
@ -276,7 +278,7 @@ const translateService = {
|
|||||||
|
|
||||||
// Этот код не должен выполниться, но оставляем для полноты
|
// Этот код не должен выполниться, но оставляем для полноты
|
||||||
console.error('Все попытки перевода через LLM не удались');
|
console.error('Все попытки перевода через LLM не удались');
|
||||||
return { success: false, text: 'Недопустимый промпт' };
|
return { success: false, text: getTranslation('invalid_prompt') };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { WorkflowType } from '../constants/workflows';
|
import { WorkflowType } from '../constants/workflows';
|
||||||
import { GenerationOptions } from '../types/generation';
|
import { GenerationOptions } from '../types/generation';
|
||||||
|
import { getTranslation } from '../constants/translations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Определяет тип воркфлоу в зависимости от выбранного стиля и типа эмоций
|
* Определяет тип воркфлоу в зависимости от выбранного стиля и типа эмоций
|
||||||
@ -86,8 +87,8 @@ export const validateGenerationParams = (
|
|||||||
if (!imageData) {
|
if (!imageData) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
errorTitle: 'Внимание',
|
errorTitle: getTranslation('upload_image_warning_title'),
|
||||||
errorMessage: 'Сначала загрузите изображение'
|
errorMessage: getTranslation('upload_image_warning_message')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,8 +96,8 @@ export const validateGenerationParams = (
|
|||||||
if (!presetId) {
|
if (!presetId) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
errorTitle: 'Внимание',
|
errorTitle: getTranslation('select_style_warning_title'),
|
||||||
errorMessage: 'Выберите образ для генерации'
|
errorMessage: getTranslation('select_style_warning_message')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +105,8 @@ export const validateGenerationParams = (
|
|||||||
if (presetId === 'customPrompt' && (!customPrompt || !customPrompt.trim())) {
|
if (presetId === 'customPrompt' && (!customPrompt || !customPrompt.trim())) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
errorTitle: 'Внимание',
|
errorTitle: getTranslation('enter_prompt_warning_title'),
|
||||||
errorMessage: 'Введите текст промпта'
|
errorMessage: getTranslation('enter_prompt_warning_message')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user