feat: integrate MarkdownHtmlWidget for improved markdown rendering in blog and diet components

This commit is contained in:
mso7nueh
2026-01-13 12:36:12 +03:00
parent a778257215
commit bf2ba1fbda
12 changed files with 1877 additions and 1807 deletions

View File

@@ -0,0 +1,67 @@
/// Утилиты для обработки markdown текста
class MarkdownUtils {
/// Обрабатывает markdown текст:
/// - Исправляет неправильный синтаксис markdown ссылок
/// - Автоматически преобразует URL в markdown ссылки
/// - Преобразует **текст** в <b>текст</b> для надежного отображения жирного текста
static String processMarkdown(String text) {
// Сначала исправляем неправильный синтаксис markdown ссылок
// Например: [http://example.com](url) -> [http://example.com](http://example.com)
final wrongLinkRegex = RegExp(r'\[(https?://[^\]]+|www\.[^\]]+)\]\(([^\)]+)\)');
text = text.replaceAllMapped(wrongLinkRegex, (match) {
final urlInText = match.group(1)!;
if (!urlInText.startsWith('http://') && !urlInText.startsWith('https://') && !urlInText.startsWith('www.')) {
return match.group(0)!;
}
final fullUrl = urlInText.startsWith('www.') ? 'https://$urlInText' : urlInText;
return '[$urlInText]($fullUrl)';
});
// Находим все существующие markdown ссылки и их позиции
final linkRegex = RegExp(r'\[([^\]]+)\]\(([^\)]+)\)');
final existingLinks = <_LinkRange>[];
for (final linkMatch in linkRegex.allMatches(text)) {
existingLinks.add(_LinkRange(linkMatch.start, linkMatch.end));
}
// Регулярное выражение для поиска URL (http://, https://, www.)
final urlRegex = RegExp(r'(https?://[^\s\)]+|www\.[^\s\)]+)', caseSensitive: false);
// Заменяем URL на markdown ссылки, если они еще не являются ссылками
String result = text;
int offset = 0;
for (final urlMatch in urlRegex.allMatches(text)) {
// Проверяем, не находится ли URL внутри существующей markdown ссылки
bool isInsideLink = existingLinks.any((link) =>
urlMatch.start >= link.start && urlMatch.end <= link.end);
if (!isInsideLink) {
final url = urlMatch.group(0)!;
final fullUrl = url.startsWith('www.') ? 'https://$url' : url;
final replacement = '[$url]($fullUrl)';
result = result.replaceRange(
urlMatch.start + offset,
urlMatch.end + offset,
replacement,
);
offset += replacement.length - url.length;
}
}
// Гарантируем, что жирный текст будет распознан
// Заменяем **текст** на <b>текст</b> вручную для надежности
final boldRegex = RegExp(r'\*\*([^*]+)\*\*');
result = result.replaceAllMapped(boldRegex, (match) {
return '<b>${match.group(1)}</b>';
});
return result;
}
}
/// Вспомогательный класс для хранения диапазона позиций ссылки
class _LinkRange {
final int start;
final int end;
_LinkRange(this.start, this.end);
}

View File

@@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:url_launcher/url_launcher.dart';
import 'package:eatvio_app/core/utils/markdown_utils.dart';
/// Переиспользуемый виджет для отображения HTML из markdown текста
class MarkdownHtmlWidget extends StatelessWidget {
/// Markdown текст для отображения
final String markdownText;
/// Ширина изображений (по умолчанию ширина экрана минус 32)
final double? imageWidth;
/// Цвет текста для body и параграфов
final Color? textColor;
/// Размер шрифта (по умолчанию 16)
final double? fontSize;
/// Callback для обработки нажатий на ссылки
final OnTap? onLinkTap;
const MarkdownHtmlWidget({
super.key,
required this.markdownText,
this.imageWidth,
this.textColor,
this.fontSize,
this.onLinkTap,
});
@override
Widget build(BuildContext context) {
// Обрабатываем markdown текст
final processedText = MarkdownUtils.processMarkdown(markdownText);
// Конвертируем markdown в HTML
final htmlData = md.markdownToHtml(
processedText,
extensionSet: md.ExtensionSet.gitHubFlavored,
);
final screenWidth = MediaQuery.of(context).size.width;
final defaultImageWidth = imageWidth ?? (screenWidth - 32);
final defaultTextColor = textColor ?? Colors.black87;
final defaultFontSize = fontSize ?? 16.0;
return Html(
data: htmlData,
style: {
"body": Style(
margin: Margins.zero,
padding: HtmlPaddings.zero,
fontSize: FontSize(defaultFontSize),
lineHeight: const LineHeight(1.5),
color: defaultTextColor,
),
"p": Style(
margin: Margins.only(bottom: 12),
color: defaultTextColor,
),
"b, strong": Style(
fontWeight: FontWeight.bold,
color: Colors.black,
),
"h1, h2, h3, h4, h5, h6": Style(
margin: Margins.only(top: 16, bottom: 8),
color: Colors.black,
fontWeight: FontWeight.bold,
),
"h1": Style(fontSize: FontSize(24)),
"h2": Style(fontSize: FontSize(20)),
"h3": Style(fontSize: FontSize(18)),
"ul, ol": Style(
margin: Margins.only(bottom: 12),
padding: HtmlPaddings.only(left: 16),
),
"li": Style(
margin: Margins.only(bottom: 4),
color: defaultTextColor,
),
"strong, b": Style(
fontWeight: FontWeight.bold,
color: Colors.black,
),
"em, i": Style(fontStyle: FontStyle.italic),
"code": Style(
backgroundColor: Colors.grey.shade100,
padding: HtmlPaddings.symmetric(horizontal: 4, vertical: 2),
fontFamily: 'monospace',
),
"pre": Style(
backgroundColor: Colors.grey.shade100,
padding: HtmlPaddings.all(12),
margin: Margins.only(bottom: 12),
),
"blockquote": Style(
padding: HtmlPaddings.only(left: 16),
margin: Margins.only(left: 8, bottom: 12),
fontStyle: FontStyle.italic,
color: Colors.grey.shade700,
),
"a": Style(
color: Colors.blue,
textDecoration: TextDecoration.underline,
),
"img": Style(width: Width(defaultImageWidth)),
},
onLinkTap: onLinkTap ?? ((url, attributes, element) async {
if (url != null) {
try {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
} catch (e) {
debugPrint('Error launching URL: $e');
}
}
}) as OnTap,
);
}
}

View File

@@ -4,6 +4,7 @@ import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/custom_popup.dart';
import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/core/widgets/markdown_html_widget.dart';
import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart'; import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart';
import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_state.dart'; import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_state.dart';
import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
@@ -12,13 +13,11 @@ import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:url_launcher/url_launcher.dart';
class BlogPostScreen extends StatefulWidget { class BlogPostScreen extends StatefulWidget {
final int id; final int id;
@@ -126,7 +125,7 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
// убираем true, чтобы padding учитывался корректно // убираем true, чтобы padding учитывался корректно
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
// вертикальный паддинг увеличен // вертикальный паддинг увеличен
hintText: 'Комментарий', hintText: S.of(context).comment_hint,
hintStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400, height: 1.2, color: Color(0xFFAEAEB2)), hintStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400, height: 1.2, color: Color(0xFFAEAEB2)),
filled: true, filled: true,
border: OutlineInputBorder( border: OutlineInputBorder(
@@ -237,111 +236,11 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
if (blogPostState.blogPostModel?.description.isNotEmpty ?? false) if (blogPostState.blogPostModel?.description.isNotEmpty ?? false)
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Builder( child: MarkdownHtmlWidget(
builder: (context) { markdownText: blogPostState.blogPostModel!.description,
// Автоматически преобразуем URL в markdown ссылки onLinkTap: (url, attributes, element) {
String description = blogPostState.blogPostModel!.description; debugPrint('Link tapped: $url');
return;
// Сначала исправляем неправильный синтаксис markdown ссылок, где URL в тексте, а не в href
// Например: [http://example.com](url) -> [http://example.com](http://example.com)
final wrongLinkRegex = RegExp(r'\[(https?://[^\]]+|www\.[^\]]+)\]\(([^\)]+)\)');
description = description.replaceAllMapped(wrongLinkRegex, (match) {
final urlInText = match.group(1)!;
// Если текст ссылки является URL, используем его как href
if (!urlInText.startsWith('http://') && !urlInText.startsWith('https://') && !urlInText.startsWith('www.')) {
return match.group(0)!; // Оставляем как есть, если это не URL
}
final fullUrl = urlInText.startsWith('www.') ? 'https://$urlInText' : urlInText;
// Используем URL как href, а текст ссылки - это сам URL
return '[$urlInText]($fullUrl)';
});
// Теперь находим все существующие markdown ссылки и их позиции
final linkRegex = RegExp(r'\[([^\]]+)\]\(([^\)]+)\)');
final existingLinks = <_LinkRange>[];
for (final linkMatch in linkRegex.allMatches(description)) {
existingLinks.add(_LinkRange(linkMatch.start, linkMatch.end));
}
// Регулярное выражение для поиска URL (http://, https://, www.)
final urlRegex = RegExp(r'(https?://[^\s\)]+|www\.[^\s\)]+)', caseSensitive: false);
// Заменяем URL на markdown ссылки, если они еще не являются ссылками
String result = description;
int offset = 0;
for (final urlMatch in urlRegex.allMatches(description)) {
// Проверяем, не находится ли URL внутри существующей markdown ссылки
bool isInsideLink = existingLinks.any((link) => urlMatch.start >= link.start && urlMatch.end <= link.end);
if (!isInsideLink) {
final url = urlMatch.group(0)!;
final fullUrl = url.startsWith('www.') ? 'https://$url' : url;
final replacement = '[$url]($fullUrl)';
result = result.replaceRange(urlMatch.start + offset, urlMatch.end + offset, replacement);
offset += replacement.length - url.length;
}
}
description = result;
// Гарантируем, что жирный текст будет распознан
// Заменяем **текст** на <b>текст</b> вручную для надежности
final boldRegex = RegExp(r'\*\*([^*]+)\*\*');
description = description.replaceAllMapped(boldRegex, (match) {
return '<b>${match.group(1)}</b>';
});
final htmlData = md.markdownToHtml(
description,
extensionSet: md.ExtensionSet.gitHubFlavored,
);
return Html(
data: htmlData,
style: {
"body": Style(margin: Margins.zero, padding: HtmlPaddings.zero, fontSize: FontSize(16), lineHeight: const LineHeight(1.5), color: Colors.black87),
"p": Style(margin: Margins.only(bottom: 12), color: Colors.black87),
"b, strong": Style(fontWeight: FontWeight.bold, color: Colors.black),
"h1, h2, h3, h4, h5, h6": Style(margin: Margins.only(top: 16, bottom: 8), color: Colors.black, fontWeight: FontWeight.bold),
"h1": Style(fontSize: FontSize(24)),
"h2": Style(fontSize: FontSize(20)),
"h3": Style(fontSize: FontSize(18)),
"ul, ol": Style(margin: Margins.only(bottom: 12), padding: HtmlPaddings.only(left: 16)),
"li": Style(margin: Margins.only(bottom: 4), color: Colors.black87),
"strong, b": Style(fontWeight: FontWeight.bold, color: Colors.black),
"em, i": Style(fontStyle: FontStyle.italic),
"code": Style(
backgroundColor: Colors.grey.shade100,
padding: HtmlPaddings.symmetric(horizontal: 4, vertical: 2),
fontFamily: 'monospace',
),
"pre": Style(backgroundColor: Colors.grey.shade100, padding: HtmlPaddings.all(12), margin: Margins.only(bottom: 12)),
"blockquote": Style(
padding: HtmlPaddings.only(left: 16),
margin: Margins.only(left: 8, bottom: 12),
fontStyle: FontStyle.italic,
color: Colors.grey.shade700,
),
"a": Style(color: Colors.blue, textDecoration: TextDecoration.underline),
"img": Style(width: Width(MediaQuery.of(context).size.width - 32)),
},
onLinkTap: (url, attributes, element) async {
debugPrint('Link tapped: $url');
if (url != null) {
try {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
debugPrint('Cannot launch URL: $url');
}
} catch (e) {
debugPrint('Error launching URL: $e');
}
}
},
);
}, },
), ),
), ),
@@ -445,10 +344,3 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
); );
} }
} }
class _LinkRange {
final int start;
final int end;
_LinkRange(this.start, this.end);
}

View File

@@ -3,11 +3,10 @@ import 'dart:math' as math;
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/markdown_utils.dart';
import 'package:eatvio_app/core/widgets/markdown_html_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:url_launcher/url_launcher.dart';
class ExpandableText extends StatefulWidget { class ExpandableText extends StatefulWidget {
final String text; final String text;
@@ -57,60 +56,9 @@ class _ExpandableTextState extends State<ExpandableText> {
}); });
} }
String _processMarkdown(String text) {
// Автоматически преобразуем URL в markdown ссылки
// Сначала исправляем неправильный синтаксис markdown ссылок
final wrongLinkRegex = RegExp(r'\[(https?://[^\]]+|www\.[^\]]+)\]\(([^\)]+)\)');
text = text.replaceAllMapped(wrongLinkRegex, (match) {
final urlInText = match.group(1)!;
if (!urlInText.startsWith('http://') && !urlInText.startsWith('https://') && !urlInText.startsWith('www.')) {
return match.group(0)!;
}
final fullUrl = urlInText.startsWith('www.') ? 'https://$urlInText' : urlInText;
return '[$urlInText]($fullUrl)';
});
final linkRegex = RegExp(r'\[([^\]]+)\]\(([^\)]+)\)');
final existingLinks = <_LinkRange>[];
for (final linkMatch in linkRegex.allMatches(text)) {
existingLinks.add(_LinkRange(linkMatch.start, linkMatch.end));
}
final urlRegex = RegExp(r'(https?://[^\s\)]+|www\.[^\s\)]+)', caseSensitive: false);
String result = text;
int offset = 0;
for (final urlMatch in urlRegex.allMatches(text)) {
bool isInsideLink = existingLinks.any((link) =>
urlMatch.start >= link.start && urlMatch.end <= link.end);
if (!isInsideLink) {
final url = urlMatch.group(0)!;
final fullUrl = url.startsWith('www.') ? 'https://$url' : url;
final replacement = '[$url]($fullUrl)';
result = result.replaceRange(
urlMatch.start + offset,
urlMatch.end + offset,
replacement,
);
offset += replacement.length - url.length;
}
}
// Гарантируем, что жирный текст будет распознан
final boldRegex = RegExp(r'\*\*([^*]+)\*\*');
result = result.replaceAllMapped(boldRegex, (match) {
return '<b>${match.group(1)}</b>';
});
return result;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = S.of(context); final s = S.of(context);
final processedText = _processMarkdown(widget.text);
final htmlData = md.markdownToHtml(
processedText,
extensionSet: md.ExtensionSet.gitHubFlavored,
);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -127,49 +75,11 @@ class _ExpandableTextState extends State<ExpandableText> {
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: isExpanded ? double.infinity : (widget.maxLines * (TextStyles.textMMediumAuto.fontSize ?? 16) * 1.5), maxHeight: isExpanded ? double.infinity : (widget.maxLines * (TextStyles.textMMediumAuto.fontSize ?? 16) * 1.5),
), ),
child: Html( child: MarkdownHtmlWidget(
key: _textKey, key: _textKey,
data: htmlData, markdownText: widget.text,
style: { fontSize: TextStyles.textMMediumAuto.fontSize ?? 16,
"body": Style( textColor: appTheme.black,
margin: Margins.zero,
padding: HtmlPaddings.zero,
fontSize: FontSize(TextStyles.textMMediumAuto.fontSize ?? 16),
lineHeight: const LineHeight(1.5),
),
"p": Style(margin: Margins.only(bottom: 12), color: appTheme.black),
"b, strong": Style(fontWeight: FontWeight.bold, color: appTheme.black),
"h1, h2, h3, h4, h5, h6": Style(margin: Margins.only(top: 16, bottom: 8), color: appTheme.black, fontWeight: FontWeight.bold),
"h1": Style(fontSize: FontSize(24)),
"h2": Style(fontSize: FontSize(20)),
"h3": Style(fontSize: FontSize(18)),
"ul, ol": Style(margin: Margins.only(bottom: 12), padding: HtmlPaddings.only(left: 16)),
"li": Style(margin: Margins.only(bottom: 4), color: appTheme.black),
"strong, b": Style(fontWeight: FontWeight.bold, color: appTheme.black),
"em, i": Style(fontStyle: FontStyle.italic),
"code": Style(
backgroundColor: Colors.grey.shade100,
padding: HtmlPaddings.symmetric(horizontal: 4, vertical: 2),
fontFamily: 'monospace',
),
"pre": Style(backgroundColor: Colors.grey.shade100, padding: HtmlPaddings.all(12), margin: Margins.only(bottom: 12)),
"blockquote": Style(
padding: HtmlPaddings.only(left: 16),
margin: Margins.only(left: 8, bottom: 12),
fontStyle: FontStyle.italic,
color: Colors.grey.shade700,
),
"a": Style(color: Colors.blue, textDecoration: TextDecoration.underline),
"img": Style(width: Width(MediaQuery.of(context).size.width - 32)),
},
onLinkTap: (url, attributes, element) async {
if (url != null) {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
},
), ),
), ),
), ),
@@ -222,9 +132,3 @@ class _ExpandableTextState extends State<ExpandableText> {
); );
} }
} }
class _LinkRange {
final int start;
final int end;
_LinkRange(this.start, this.end);
}

View File

@@ -7,17 +7,16 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/custom_popup.dart';
import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
import 'package:eatvio_app/core/widgets/markdown_html_widget.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/subscribe_button_widget.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/subscribe_button_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:markdown/markdown.dart' as md;
class BlogPostWidget extends StatefulWidget { class BlogPostWidget extends StatefulWidget {
final int index; final int index;
@@ -162,47 +161,8 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
description = description.substring(0, cutPoint); description = description.substring(0, cutPoint);
} }
// Гарантируем, что жирный текст будет распознан return MarkdownHtmlWidget(
final boldRegex = RegExp(r'\*\*([^*]+)\*\*'); markdownText: description,
description = description.replaceAllMapped(boldRegex, (match) {
return '<b>${match.group(1)}</b>';
});
// Конвертируем markdown в HTML
String htmlData = md.markdownToHtml(
description,
extensionSet: md.ExtensionSet.gitHubFlavored,
);
return Html(
data: htmlData,
style: {
"body": Style(margin: Margins.zero, padding: HtmlPaddings.zero, fontSize: FontSize(16), lineHeight: const LineHeight(1.5), color: Colors.black87),
"p": Style(margin: Margins.only(bottom: 12), color: Colors.black87),
"b, strong": Style(fontWeight: FontWeight.bold, color: Colors.black),
"h1, h2, h3, h4, h5, h6": Style(margin: Margins.only(top: 16, bottom: 8), color: Colors.black, fontWeight: FontWeight.bold),
"h1": Style(fontSize: FontSize(24)),
"h2": Style(fontSize: FontSize(20)),
"h3": Style(fontSize: FontSize(18)),
"ul, ol": Style(margin: Margins.only(bottom: 12), padding: HtmlPaddings.only(left: 16)),
"li": Style(margin: Margins.only(bottom: 4), color: Colors.black87),
"strong, b": Style(fontWeight: FontWeight.bold, color: Colors.black),
"em, i": Style(fontStyle: FontStyle.italic),
"code": Style(
backgroundColor: Colors.grey.shade100,
padding: HtmlPaddings.symmetric(horizontal: 4, vertical: 2),
fontFamily: 'monospace',
),
"pre": Style(backgroundColor: Colors.grey.shade100, padding: HtmlPaddings.all(12), margin: Margins.only(bottom: 12)),
"blockquote": Style(
padding: HtmlPaddings.only(left: 16),
margin: Margins.only(left: 8, bottom: 12),
fontStyle: FontStyle.italic,
color: Colors.grey.shade700,
),
"a": Style(color: Colors.blue, textDecoration: TextDecoration.underline),
"img": Style(width: Width(MediaQuery.of(context).size.width - 32)),
},
); );
}, },
), ),

View File

@@ -7,6 +7,7 @@ import 'package:eatvio_app/core/widgets/bookmark_button.dart';
import 'package:eatvio_app/core/widgets/custom_popup.dart'; import 'package:eatvio_app/core/widgets/custom_popup.dart';
import 'package:eatvio_app/core/widgets/info_row.dart'; import 'package:eatvio_app/core/widgets/info_row.dart';
import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
import 'package:eatvio_app/core/widgets/markdown_html_widget.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart';
@@ -15,12 +16,9 @@ import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cub
import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:url_launcher/url_launcher.dart';
class PlanWidget extends StatefulWidget { class PlanWidget extends StatefulWidget {
final PlanModel plan; final PlanModel plan;
@@ -117,96 +115,11 @@ class _PlanWidgetState extends State<PlanWidget> {
const SizedBox(height: 12), const SizedBox(height: 12),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Builder( child: MarkdownHtmlWidget(
builder: (context) { markdownText: widget.plan.details.length > 200
String details = widget.plan.details.length > 200 ? '${widget.plan.details.substring(0, 200)}...'
? '${widget.plan.details.substring(0, 200)}...' : widget.plan.details,
: widget.plan.details; textColor: appTheme.black,
// Автоматически преобразуем URL в markdown ссылки
// Сначала исправляем неправильный синтаксис markdown ссылок
final wrongLinkRegex = RegExp(r'\[(https?://[^\]]+|www\.[^\]]+)\]\(([^\)]+)\)');
details = details.replaceAllMapped(wrongLinkRegex, (match) {
final urlInText = match.group(1)!;
if (!urlInText.startsWith('http://') && !urlInText.startsWith('https://') && !urlInText.startsWith('www.')) {
return match.group(0)!;
}
final fullUrl = urlInText.startsWith('www.') ? 'https://$urlInText' : urlInText;
return '[$urlInText]($fullUrl)';
});
final linkRegex = RegExp(r'\[([^\]]+)\]\(([^\)]+)\)');
final existingLinks = <_LinkRange>[];
for (final linkMatch in linkRegex.allMatches(details)) {
existingLinks.add(_LinkRange(linkMatch.start, linkMatch.end));
}
final urlRegex = RegExp(r'(https?://[^\s\)]+|www\.[^\s\)]+)', caseSensitive: false);
String result = details;
int offset = 0;
for (final urlMatch in urlRegex.allMatches(details)) {
bool isInsideLink = existingLinks.any((link) =>
urlMatch.start >= link.start && urlMatch.end <= link.end);
if (!isInsideLink) {
final url = urlMatch.group(0)!;
final fullUrl = url.startsWith('www.') ? 'https://$url' : url;
final replacement = '[$url]($fullUrl)';
result = result.replaceRange(
urlMatch.start + offset,
urlMatch.end + offset,
replacement,
);
offset += replacement.length - url.length;
}
}
details = result;
// Гарантируем, что жирный текст будет распознан
final boldRegex = RegExp(r'\*\*([^*]+)\*\*');
details = details.replaceAllMapped(boldRegex, (match) {
return '<b>${match.group(1)}</b>';
});
return Html(
data: md.markdownToHtml(
details,
extensionSet: md.ExtensionSet.gitHubFlavored,
),
style: {
"body": Style(margin: Margins.zero, padding: HtmlPaddings.zero, fontSize: FontSize(16), lineHeight: const LineHeight(1.5)),
"p": Style(margin: Margins.only(bottom: 12), color: appTheme.black),
"b, strong": Style(fontWeight: FontWeight.bold, color: appTheme.black),
"h1, h2, h3, h4, h5, h6": Style(margin: Margins.only(top: 16, bottom: 8), color: appTheme.black, fontWeight: FontWeight.bold),
"h1": Style(fontSize: FontSize(24)),
"h2": Style(fontSize: FontSize(20)),
"h3": Style(fontSize: FontSize(18)),
"ul, ol": Style(margin: Margins.only(bottom: 12), padding: HtmlPaddings.only(left: 16)),
"li": Style(margin: Margins.only(bottom: 4), color: appTheme.black),
"strong, b": Style(fontWeight: FontWeight.bold, color: appTheme.black),
"em, i": Style(fontStyle: FontStyle.italic),
"code": Style(
backgroundColor: Colors.grey.shade100,
padding: HtmlPaddings.symmetric(horizontal: 4, vertical: 2),
fontFamily: 'monospace',
),
"pre": Style(backgroundColor: Colors.grey.shade100, padding: HtmlPaddings.all(12), margin: Margins.only(bottom: 12)),
"blockquote": Style(
padding: HtmlPaddings.only(left: 16),
margin: Margins.only(left: 8, bottom: 12),
fontStyle: FontStyle.italic,
color: Colors.grey.shade700,
),
"a": Style(color: Colors.blue, textDecoration: TextDecoration.underline),
"img": Style(width: Width(MediaQuery.of(context).size.width - 32)),
},
onLinkTap: (url, attributes, element) async {
if (url != null) {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
},
);
},
), ),
), ),
if (widget.plan.details.length > 200) if (widget.plan.details.length > 200)
@@ -320,9 +233,3 @@ class _PlanWidgetState extends State<PlanWidget> {
); );
} }
} }
class _LinkRange {
final int start;
final int end;
_LinkRange(this.start, this.end);
}

View File

@@ -11,7 +11,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/message_lookup_by_library.dart';
import 'package:intl/src/intl_helpers.dart'; import 'package:intl/src/intl_helpers.dart';
@@ -21,8 +20,8 @@ import 'messages_ru.dart' as messages_ru;
typedef Future<dynamic> LibraryLoader(); typedef Future<dynamic> LibraryLoader();
Map<String, LibraryLoader> _deferredLibraries = { Map<String, LibraryLoader> _deferredLibraries = {
'en': () => new SynchronousFuture(null), 'en': () => new Future.value(null),
'ru': () => new SynchronousFuture(null), 'ru': () => new Future.value(null),
}; };
MessageLookupByLibrary? _findExact(String localeName) { MessageLookupByLibrary? _findExact(String localeName) {
@@ -37,20 +36,19 @@ MessageLookupByLibrary? _findExact(String localeName) {
} }
/// User programs should call this before using [localeName] for messages. /// User programs should call this before using [localeName] for messages.
Future<bool> initializeMessages(String localeName) { Future<bool> initializeMessages(String localeName) async {
var availableLocale = Intl.verifiedLocale( var availableLocale = Intl.verifiedLocale(
localeName, localeName,
(locale) => _deferredLibraries[locale] != null, (locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null, onFailure: (_) => null);
);
if (availableLocale == null) { if (availableLocale == null) {
return new SynchronousFuture(false); return new Future.value(false);
} }
var lib = _deferredLibraries[availableLocale]; var lib = _deferredLibraries[availableLocale];
lib == null ? new SynchronousFuture(false) : lib(); await (lib == null ? new Future.value(false) : lib());
initializeInternalMessageLookup(() => new CompositeMessageLookup()); initializeInternalMessageLookup(() => new CompositeMessageLookup());
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
return new SynchronousFuture(true); return new Future.value(true);
} }
bool _messagesExistFor(String locale) { bool _messagesExistFor(String locale) {
@@ -62,11 +60,8 @@ bool _messagesExistFor(String locale) {
} }
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
var actualLocale = Intl.verifiedLocale( var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
locale, onFailure: (_) => null);
_messagesExistFor,
onFailure: (_) => null,
);
if (actualLocale == null) return null; if (actualLocale == null) return null;
return _findExact(actualLocale); return _findExact(actualLocale);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,7 @@
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes // ignore_for_file:unused_import, file_names
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/message_lookup_by_library.dart';
@@ -21,362 +20,223 @@ class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'ru'; String get localeName => 'ru';
final messages = _notInlinedMessages(_notInlinedMessages); final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{ static _notInlinedMessages(_) => <String, Function> {
"Moscow": MessageLookupByLibrary.simpleMessage("Москва"), "Moscow" : MessageLookupByLibrary.simpleMessage("Москва"),
"Russia": MessageLookupByLibrary.simpleMessage("Россия"), "Russia" : MessageLookupByLibrary.simpleMessage("Россия"),
"about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"), "about_diet" : MessageLookupByLibrary.simpleMessage("Об этой диете"),
"about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"), "about_plan" : MessageLookupByLibrary.simpleMessage("Об этом плане"),
"active": MessageLookupByLibrary.simpleMessage("Активен"), "active" : MessageLookupByLibrary.simpleMessage("Активен"),
"active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"), "active_diet_state" : MessageLookupByLibrary.simpleMessage("Активная"),
"add": MessageLookupByLibrary.simpleMessage("Добавить"), "add" : MessageLookupByLibrary.simpleMessage("Добавить"),
"add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"), "add_cover" : MessageLookupByLibrary.simpleMessage("Добавьте обложку"),
"add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"), "add_measure" : MessageLookupByLibrary.simpleMessage("Добавить замер"),
"adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"), "adding_plan" : MessageLookupByLibrary.simpleMessage("Добавление плана"),
"additional_information": MessageLookupByLibrary.simpleMessage( "additional_information" : MessageLookupByLibrary.simpleMessage("Дополнительная информация"),
"Дополнительная информация", "admin" : MessageLookupByLibrary.simpleMessage("Админ"),
), "all" : MessageLookupByLibrary.simpleMessage("Все"),
"admin": MessageLookupByLibrary.simpleMessage("Админ"), "allergies_presence" : MessageLookupByLibrary.simpleMessage("Наличие аллергии"),
"all": MessageLookupByLibrary.simpleMessage("Все"), "another_form_of_comment" : MessageLookupByLibrary.simpleMessage("комментария"),
"allergies_presence": MessageLookupByLibrary.simpleMessage( "another_form_of_day" : MessageLookupByLibrary.simpleMessage("дня"),
"Наличие аллергии", "answer" : MessageLookupByLibrary.simpleMessage("Ответить"),
), "archived" : MessageLookupByLibrary.simpleMessage("В архиве"),
"another_form_of_comment": MessageLookupByLibrary.simpleMessage( "authentificate" : MessageLookupByLibrary.simpleMessage("Войти"),
"комментария", "average_pressure" : MessageLookupByLibrary.simpleMessage("Средние показатели давления:"),
), "bodymass_chart" : MessageLookupByLibrary.simpleMessage("График ИМТ"),
"another_form_of_day": MessageLookupByLibrary.simpleMessage("дня"), "by_default" : MessageLookupByLibrary.simpleMessage("По умолчанию"),
"answer": MessageLookupByLibrary.simpleMessage("Ответить"), "calories" : MessageLookupByLibrary.simpleMessage("Калории"),
"archived": MessageLookupByLibrary.simpleMessage("В архиве"), "carb_type" : MessageLookupByLibrary.simpleMessage("Мучное"),
"authentificate": MessageLookupByLibrary.simpleMessage("Войти"), "carb_type_desc" : MessageLookupByLibrary.simpleMessage("Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
"average_pressure": MessageLookupByLibrary.simpleMessage( "carbohydrates" : MessageLookupByLibrary.simpleMessage("Углеводы"),
"Средние показатели давления:", "chat" : MessageLookupByLibrary.simpleMessage("Чат"),
), "chest" : MessageLookupByLibrary.simpleMessage("Грудь"),
"bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), "chest_circumference" : MessageLookupByLibrary.simpleMessage("Обхват груди"),
"by_default": MessageLookupByLibrary.simpleMessage("По умолчанию"), "choose_diet_duration" : MessageLookupByLibrary.simpleMessage("Выберите срок диеты"),
"calories": MessageLookupByLibrary.simpleMessage("Калории"), "choose_plan_duration" : MessageLookupByLibrary.simpleMessage("Выберите срок плана"),
"carb_type": MessageLookupByLibrary.simpleMessage("Мучное"), "choose_start_diet_date" : MessageLookupByLibrary.simpleMessage("Выберите дату старты диеты. Она отобразиться у Вас в дневнике"),
"carb_type_desc": MessageLookupByLibrary.simpleMessage( "circumference_empty" : MessageLookupByLibrary.simpleMessage("Все замеры обязательны к заполнению"),
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.", "circumference_is_not_a_number" : MessageLookupByLibrary.simpleMessage("Все замеры должны быть числами"),
), "circumferences_chart" : MessageLookupByLibrary.simpleMessage("График обхватов"),
"carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), "city" : MessageLookupByLibrary.simpleMessage("Город"),
"chat": MessageLookupByLibrary.simpleMessage("Чат"), "city_of_living" : MessageLookupByLibrary.simpleMessage("Город проживания"),
"chest": MessageLookupByLibrary.simpleMessage("Грудь"), "comfort" : MessageLookupByLibrary.simpleMessage("Комфорт"),
"chest_circumference": MessageLookupByLibrary.simpleMessage("Обхват груди"), "comment" : MessageLookupByLibrary.simpleMessage("комментарий"),
"choose_diet_duration": MessageLookupByLibrary.simpleMessage( "comment_hint" : MessageLookupByLibrary.simpleMessage("Комментарий"),
"Выберите срок диеты", "comments" : MessageLookupByLibrary.simpleMessage("Комментарии"),
), "comments_plural" : MessageLookupByLibrary.simpleMessage("комментариев"),
"choose_plan_duration": MessageLookupByLibrary.simpleMessage( "complain" : MessageLookupByLibrary.simpleMessage("Пожаловаться"),
"Выберите срок плана", "continueButton" : MessageLookupByLibrary.simpleMessage("Продолжить"),
), "continue_" : MessageLookupByLibrary.simpleMessage("Продолжить"),
"choose_start_diet_date": MessageLookupByLibrary.simpleMessage( "copied" : MessageLookupByLibrary.simpleMessage("cкопирован"),
"Выберите дату старты диеты. Она отобразиться у Вас в дневнике", "copy_link" : MessageLookupByLibrary.simpleMessage("Скопировать ссылку"),
), "country" : MessageLookupByLibrary.simpleMessage("Страна"),
"circumference_empty": MessageLookupByLibrary.simpleMessage( "create_diet" : MessageLookupByLibrary.simpleMessage("Создание диеты"),
"Все замеры обязательны к заполнению", "customer_price" : MessageLookupByLibrary.simpleMessage("Цена для покупателя"),
), "date_of_birth" : MessageLookupByLibrary.simpleMessage("Дата рождения"),
"circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( "day" : MessageLookupByLibrary.simpleMessage("День"),
"Все замеры должны быть числами", "days" : MessageLookupByLibrary.simpleMessage("дней"),
), "deleted" : MessageLookupByLibrary.simpleMessage("Удалена"),
"circumferences_chart": MessageLookupByLibrary.simpleMessage( "diary" : MessageLookupByLibrary.simpleMessage("Дневник"),
"График обхватов", "diet" : MessageLookupByLibrary.simpleMessage("Диета"),
), "diet_author" : MessageLookupByLibrary.simpleMessage("Автор диеты"),
"city": MessageLookupByLibrary.simpleMessage("Город"), "diet_plan" : MessageLookupByLibrary.simpleMessage("План диеты"),
"city_of_living": MessageLookupByLibrary.simpleMessage("Город проживания"), "diet_state" : MessageLookupByLibrary.simpleMessage("Статус диеты"),
"comfort": MessageLookupByLibrary.simpleMessage("Комфорт"), "diet_type" : MessageLookupByLibrary.simpleMessage("Тип диеты"),
"comment": MessageLookupByLibrary.simpleMessage("комментарий"), "do_you_have_hypertension_tendency" : MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"),
"comments": MessageLookupByLibrary.simpleMessage("Комментарии"), "do_you_have_hypotension_tendency" : MessageLookupByLibrary.simpleMessage("Есть ли у вас гипотония?"),
"comments_plural": MessageLookupByLibrary.simpleMessage("комментариев"), "do_you_take_pills" : MessageLookupByLibrary.simpleMessage("Принимаете ли вы лекарства или бады?"),
"complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"), "down_pressure" : MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"),
"continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"), "draft" : MessageLookupByLibrary.simpleMessage("Черновик"),
"continue_": MessageLookupByLibrary.simpleMessage("Продолжить"), "drafted" : MessageLookupByLibrary.simpleMessage("Черновики"),
"copied": MessageLookupByLibrary.simpleMessage("cкопирован"), "eating_behavior" : MessageLookupByLibrary.simpleMessage("Пищевое поведение"),
"copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"), "eating_behavior_type" : MessageLookupByLibrary.simpleMessage("Формирование пищевого поведения"),
"country": MessageLookupByLibrary.simpleMessage("Страна"), "eating_behavior_type_desc" : MessageLookupByLibrary.simpleMessage("Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;"),
"create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"), "economy" : MessageLookupByLibrary.simpleMessage("Эконом"),
"customer_price": MessageLookupByLibrary.simpleMessage( "edit" : MessageLookupByLibrary.simpleMessage("Редактировать"),
"Цена для покупателя", "email" : MessageLookupByLibrary.simpleMessage("Электронная почта"),
), "email_empty" : MessageLookupByLibrary.simpleMessage("Необходимо ввести адрес электронной почты"),
"date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), "email_invalid" : MessageLookupByLibrary.simpleMessage("Некорректный адрес электронной почты"),
"day": MessageLookupByLibrary.simpleMessage("День"), "enter_additional_information" : MessageLookupByLibrary.simpleMessage("Введите любую дополнительную информацию на своё усмотрение:"),
"days": MessageLookupByLibrary.simpleMessage("дней"), "enter_diet_description" : MessageLookupByLibrary.simpleMessage("Введите описание диеты"),
"deleted": MessageLookupByLibrary.simpleMessage("Удалена"), "enter_diet_name" : MessageLookupByLibrary.simpleMessage("Выведите название диеты"),
"diary": MessageLookupByLibrary.simpleMessage("Дневник"), "enter_information" : MessageLookupByLibrary.simpleMessage("Введите информацию..."),
"diet": MessageLookupByLibrary.simpleMessage("Диета"), "exit" : MessageLookupByLibrary.simpleMessage("Выйти"),
"diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"), "expand" : MessageLookupByLibrary.simpleMessage("Показать полностью"),
"diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"), "fats" : MessageLookupByLibrary.simpleMessage("Жиры"),
"diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"), "favorite_foods" : MessageLookupByLibrary.simpleMessage("Любимые продукты"),
"diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"), "favorite_unfavorite_foods" : MessageLookupByLibrary.simpleMessage("Ваши любимые/нелюбимые продукты питания?"),
"do_you_have_hypertension_tendency": MessageLookupByLibrary.simpleMessage( "female" : MessageLookupByLibrary.simpleMessage("Женский"),
"Есть ли у вас гипертония?", "fill_in_information" : MessageLookupByLibrary.simpleMessage("Заполните информацию:"),
), "finish" : MessageLookupByLibrary.simpleMessage("Завершить"),
"do_you_have_hypotension_tendency": MessageLookupByLibrary.simpleMessage( "first_name" : MessageLookupByLibrary.simpleMessage("Имя"),
"Есть ли у вас гипотония?", "first_name_empty" : MessageLookupByLibrary.simpleMessage("Имя обязательно для заполнения"),
), "first_name_short" : MessageLookupByLibrary.simpleMessage("В имени должен быть минимум 1 символ"),
"do_you_take_pills": MessageLookupByLibrary.simpleMessage( "fit_type" : MessageLookupByLibrary.simpleMessage("Поддержание формы"),
"Принимаете ли вы лекарства или бады?", "fit_type_desc" : MessageLookupByLibrary.simpleMessage("План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;"),
), "fitness" : MessageLookupByLibrary.simpleMessage("Поддержка формы"),
"down_pressure": MessageLookupByLibrary.simpleMessage( "from" : MessageLookupByLibrary.simpleMessage("от"),
"Нижние показатели давления:", "from_a_to_z" : MessageLookupByLibrary.simpleMessage("Название: А-Я"),
), "from_z_to_a" : MessageLookupByLibrary.simpleMessage("Название: Я-А"),
"draft": MessageLookupByLibrary.simpleMessage("Черновик"), "gallery" : MessageLookupByLibrary.simpleMessage("Галерея"),
"drafted": MessageLookupByLibrary.simpleMessage("Черновики"), "gr" : MessageLookupByLibrary.simpleMessage("гр"),
"eating_behavior": MessageLookupByLibrary.simpleMessage( "grams" : MessageLookupByLibrary.simpleMessage("гр"),
"Пищевое поведение", "height_measurements" : MessageLookupByLibrary.simpleMessage("Замер роста"),
), "hide" : MessageLookupByLibrary.simpleMessage("Свернуть"),
"eating_behavior_type": MessageLookupByLibrary.simpleMessage( "hip_circumference" : MessageLookupByLibrary.simpleMessage("Обхват бедер"),
"Формирование пищевого поведения", "hips" : MessageLookupByLibrary.simpleMessage("Бедра"),
), "in_progress" : MessageLookupByLibrary.simpleMessage("В процессе"),
"eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage( "inactive_diet_state" : MessageLookupByLibrary.simpleMessage("Черновик"),
"Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;", "infoAboutOrder" : MessageLookupByLibrary.simpleMessage("Информация о заказе"),
), "initial_measurements" : MessageLookupByLibrary.simpleMessage("Первичные замеры"),
"economy": MessageLookupByLibrary.simpleMessage("Эконом"), "label" : MessageLookupByLibrary.simpleMessage("Название"),
"edit": MessageLookupByLibrary.simpleMessage("Редактировать"), "language" : MessageLookupByLibrary.simpleMessage("Язык"),
"email": MessageLookupByLibrary.simpleMessage("Электронная почта"), "last_name" : MessageLookupByLibrary.simpleMessage("Фамилия"),
"email_empty": MessageLookupByLibrary.simpleMessage( "last_name_empty" : MessageLookupByLibrary.simpleMessage("Фамилия обязательна для заполнения"),
"Необходимо ввести адрес электронной почты", "last_name_short" : MessageLookupByLibrary.simpleMessage("В фамилии должен быть минимум 6 символов"),
), "lbl_english" : MessageLookupByLibrary.simpleMessage("English"),
"email_invalid": MessageLookupByLibrary.simpleMessage( "light_type" : MessageLookupByLibrary.simpleMessage("Легкая"),
"Некорректный адрес электронной почты", "light_type_desc" : MessageLookupByLibrary.simpleMessage("С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."),
), "link_was_copied" : MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"),
"enter_additional_information": MessageLookupByLibrary.simpleMessage( "loading" : MessageLookupByLibrary.simpleMessage("Загрузка..."),
"Введите любую дополнительную информацию на своё усмотрение:", "mail_was_copied" : MessageLookupByLibrary.simpleMessage("Почта успешно скопирована"),
), "male" : MessageLookupByLibrary.simpleMessage("Мужской"),
"enter_diet_description": MessageLookupByLibrary.simpleMessage( "meal" : MessageLookupByLibrary.simpleMessage("Приём пищи"),
"Введите описание диеты", "meat" : MessageLookupByLibrary.simpleMessage("Мясо"),
), "meat_type" : MessageLookupByLibrary.simpleMessage("С мясом"),
"enter_diet_name": MessageLookupByLibrary.simpleMessage( "meat_type_desc" : MessageLookupByLibrary.simpleMessage("Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму."),
"Выведите название диеты", "milliliter" : MessageLookupByLibrary.simpleMessage("мл"),
), "minimal_customer_price" : MessageLookupByLibrary.simpleMessage("Минимальная цена для покупателя: 1200₽"),
"enter_information": MessageLookupByLibrary.simpleMessage( "most_new" : MessageLookupByLibrary.simpleMessage("Самые новые"),
"Введите информацию...", "most_old" : MessageLookupByLibrary.simpleMessage("Самые старые"),
), "msg_network_err" : MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"),
"exit": MessageLookupByLibrary.simpleMessage("Выйти"), "msg_something_went_wrong" : MessageLookupByLibrary.simpleMessage("Что то пошло не так!"),
"expand": MessageLookupByLibrary.simpleMessage("Показать полностью"), "my_blog" : MessageLookupByLibrary.simpleMessage("Мой блог"),
"fats": MessageLookupByLibrary.simpleMessage("Жиры"), "my_diets" : MessageLookupByLibrary.simpleMessage("Мои диеты"),
"favorite_foods": MessageLookupByLibrary.simpleMessage("Любимые продукты"), "my_plans" : MessageLookupByLibrary.simpleMessage("Мои планы"),
"favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( "my_settings" : MessageLookupByLibrary.simpleMessage("Мои настройки"),
"Ваши любимые/нелюбимые продукты питания?", "name_empty" : MessageLookupByLibrary.simpleMessage("Имя пользователя обязательно для заполнения"),
), "name_short" : MessageLookupByLibrary.simpleMessage("В имени пользователя должно быть минимум 6 символов"),
"female": MessageLookupByLibrary.simpleMessage("Женский"), "no" : MessageLookupByLibrary.simpleMessage("Нет"),
"fill_in_information": MessageLookupByLibrary.simpleMessage( "no_account" : MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"),
"Заполните информацию:", "notifications" : MessageLookupByLibrary.simpleMessage("Уведомления"),
), "one_option" : MessageLookupByLibrary.simpleMessage("1 опция"),
"finish": MessageLookupByLibrary.simpleMessage("Завершить"), "option" : MessageLookupByLibrary.simpleMessage("Опция"),
"first_name": MessageLookupByLibrary.simpleMessage("Имя"), "option_content" : MessageLookupByLibrary.simpleMessage("Содержание опции"),
"first_name_empty": MessageLookupByLibrary.simpleMessage( "option_content_hint_text" : MessageLookupByLibrary.simpleMessage("На связи 7 дней в неделю"),
"Имя обязательно для заполнения", "option_desc" : MessageLookupByLibrary.simpleMessage("Описание опции"),
), "option_desc_hint_text" : MessageLookupByLibrary.simpleMessage("Этот план питания для похудения на 7 дней сбалансированный и разнообразный"),
"first_name_short": MessageLookupByLibrary.simpleMessage( "option_duration" : MessageLookupByLibrary.simpleMessage("Продолжительность опции"),
"В имени должен быть минимум 1 символ", "option_personal_diet_desc" : MessageLookupByLibrary.simpleMessage("Опция подразумевает создание индивидуальной диеты"),
), "option_title" : MessageLookupByLibrary.simpleMessage("Заголовок опции"),
"fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"), "order" : MessageLookupByLibrary.simpleMessage("Порядок"),
"fit_type_desc": MessageLookupByLibrary.simpleMessage( "password" : MessageLookupByLibrary.simpleMessage("Пароль"),
"План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;", "password_confirmation" : MessageLookupByLibrary.simpleMessage("Подтвердите пароль"),
), "password_confirmation_mismatch" : MessageLookupByLibrary.simpleMessage("Пароли не совпадают"),
"fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"), "password_empty" : MessageLookupByLibrary.simpleMessage("Пароль обязателен для заполнения"),
"from": MessageLookupByLibrary.simpleMessage("от"), "password_short" : MessageLookupByLibrary.simpleMessage("В пароле должно быть минимум 6 символов"),
"from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"), "pcs" : MessageLookupByLibrary.simpleMessage("шт"),
"from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"), "personal" : MessageLookupByLibrary.simpleMessage("Персональные"),
"gallery": MessageLookupByLibrary.simpleMessage("Галерея"), "personal_diet_state" : MessageLookupByLibrary.simpleMessage("Персональная"),
"gr": MessageLookupByLibrary.simpleMessage("гр"), "photo" : MessageLookupByLibrary.simpleMessage("Фото"),
"grams": MessageLookupByLibrary.simpleMessage("гр"), "place_of_living" : MessageLookupByLibrary.simpleMessage("Место проживания"),
"height_measurements": MessageLookupByLibrary.simpleMessage("Замер роста"), "plan_author" : MessageLookupByLibrary.simpleMessage("Автор плана"),
"hide": MessageLookupByLibrary.simpleMessage("Свернуть"), "plan_desc" : MessageLookupByLibrary.simpleMessage("Описание плана"),
"hip_circumference": MessageLookupByLibrary.simpleMessage("Обхват бедер"), "plan_desc_example" : MessageLookupByLibrary.simpleMessage("Большое подробное, привлекательное описание плана"),
"hips": MessageLookupByLibrary.simpleMessage("Бедра"), "plan_duration" : MessageLookupByLibrary.simpleMessage("Срок плана"),
"in_progress": MessageLookupByLibrary.simpleMessage("В процессе"), "plan_label_desc" : MessageLookupByLibrary.simpleMessage("План для поддержания формы"),
"inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"), "plans_complete" : MessageLookupByLibrary.simpleMessage("Пройденные планы"),
"infoAboutOrder": MessageLookupByLibrary.simpleMessage( "popular" : MessageLookupByLibrary.simpleMessage("Популярные"),
"Информация о заказе", "preferred_language" : MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"),
), "pressure_empty" : MessageLookupByLibrary.simpleMessage("Все показатели давления обязательны к заполнению"),
"initial_measurements": MessageLookupByLibrary.simpleMessage( "pressure_is_not_a_number" : MessageLookupByLibrary.simpleMessage("Все показатели давления должны быть числами"),
"Первичные замеры", "preview" : MessageLookupByLibrary.simpleMessage("Превью"),
), "price" : MessageLookupByLibrary.simpleMessage("Цена"),
"label": MessageLookupByLibrary.simpleMessage("Название"), "price_explanation" : MessageLookupByLibrary.simpleMessage("Разница в ценах, это комиссия Итвио"),
"language": MessageLookupByLibrary.simpleMessage("Язык"), "profile" : MessageLookupByLibrary.simpleMessage("Профиль"),
"last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), "proteins" : MessageLookupByLibrary.simpleMessage("Белки"),
"last_name_empty": MessageLookupByLibrary.simpleMessage( "public_diet_state" : MessageLookupByLibrary.simpleMessage("Публичная"),
"Фамилия обязательна для заполнения", "publish" : MessageLookupByLibrary.simpleMessage("Опубликовать"),
), "published" : MessageLookupByLibrary.simpleMessage("Опубликованные"),
"last_name_short": MessageLookupByLibrary.simpleMessage( "removeFromActive" : MessageLookupByLibrary.simpleMessage("Снять с публикации"),
"В фамилии должен быть минимум 6 символов", "save" : MessageLookupByLibrary.simpleMessage("Сохранить"),
), "services" : MessageLookupByLibrary.simpleMessage("Оказание услуг"),
"lbl_english": MessageLookupByLibrary.simpleMessage("English"), "settings" : MessageLookupByLibrary.simpleMessage("Настройки"),
"light_type": MessageLookupByLibrary.simpleMessage("Легкая"), "share" : MessageLookupByLibrary.simpleMessage("Поделиться"),
"light_type_desc": MessageLookupByLibrary.simpleMessage( "skip" : MessageLookupByLibrary.simpleMessage("Пропустить"),
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.", "slim" : MessageLookupByLibrary.simpleMessage("Похудение"),
), "slim_type" : MessageLookupByLibrary.simpleMessage("Похудение"),
"link_was_copied": MessageLookupByLibrary.simpleMessage( "slim_type_desc" : MessageLookupByLibrary.simpleMessage("План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;"),
"Ссылка успешно скопирована", "standard" : MessageLookupByLibrary.simpleMessage("Стандарт"),
), "startDiet" : MessageLookupByLibrary.simpleMessage("Начать диету"),
"loading": MessageLookupByLibrary.simpleMessage("Загрузка..."), "tell_your_schedule" : MessageLookupByLibrary.simpleMessage("Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"),
"mail_was_copied": MessageLookupByLibrary.simpleMessage( "text_was_copied" : MessageLookupByLibrary.simpleMessage("Текст успешно скопирован"),
"Почта успешно скопирована", "three_options" : MessageLookupByLibrary.simpleMessage("3 опции"),
), "toOffer" : MessageLookupByLibrary.simpleMessage("Предложить"),
"male": MessageLookupByLibrary.simpleMessage("Мужской"), "to_draft" : MessageLookupByLibrary.simpleMessage("В черновик"),
"meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), "unfavorite_foods" : MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"),
"meat": MessageLookupByLibrary.simpleMessage("Мясо"), "up_pressure" : MessageLookupByLibrary.simpleMessage("Верхние показатели давления:"),
"meat_type": MessageLookupByLibrary.simpleMessage("С мясом"), "user_authorization" : MessageLookupByLibrary.simpleMessage("Аутентификация"),
"meat_type_desc": MessageLookupByLibrary.simpleMessage( "user_data" : MessageLookupByLibrary.simpleMessage("Данные пользователя"),
"Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.", "user_registration" : MessageLookupByLibrary.simpleMessage("Регистрация пользователя"),
), "username" : MessageLookupByLibrary.simpleMessage("Имя пользователя"),
"milliliter": MessageLookupByLibrary.simpleMessage("мл"), "vegan" : MessageLookupByLibrary.simpleMessage("Веган."),
"minimal_customer_price": MessageLookupByLibrary.simpleMessage( "vegan_type" : MessageLookupByLibrary.simpleMessage("Вегетарианская"),
"Минимальная цена для покупателя: 1200₽", "vegan_type_desc" : MessageLookupByLibrary.simpleMessage("Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья."),
), "waist" : MessageLookupByLibrary.simpleMessage("Талия"),
"most_new": MessageLookupByLibrary.simpleMessage("Самые новые"), "waist_circumference" : MessageLookupByLibrary.simpleMessage("Обхват талии"),
"most_old": MessageLookupByLibrary.simpleMessage("Самые старые"), "weight" : MessageLookupByLibrary.simpleMessage("Вес"),
"msg_network_err": MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), "weight_chart" : MessageLookupByLibrary.simpleMessage("График веса"),
"msg_something_went_wrong": MessageLookupByLibrary.simpleMessage( "weight_measurements" : MessageLookupByLibrary.simpleMessage("Замер веса"),
"Что то пошло не так!", "wellness" : MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
), "wellness_type" : MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
"my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), "wellness_type_desc" : MessageLookupByLibrary.simpleMessage("Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
"my_diets": MessageLookupByLibrary.simpleMessage("Мои диеты"), "what_is_your_name" : MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
"my_plans": MessageLookupByLibrary.simpleMessage("Мои планы"), "wrong_credentials" : MessageLookupByLibrary.simpleMessage("Неверные данные пользователя"),
"my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), "you_will_get" : MessageLookupByLibrary.simpleMessage("Вы получите"),
"name_empty": MessageLookupByLibrary.simpleMessage( "your_blood_pressure" : MessageLookupByLibrary.simpleMessage("Ваше артериальное давление?"),
"Имя пользователя обязательно для заполнения", "your_favorite_sports" : MessageLookupByLibrary.simpleMessage("Ваши спортивные увлечения?"),
), "your_gender" : MessageLookupByLibrary.simpleMessage("Ваш пол?"),
"name_short": MessageLookupByLibrary.simpleMessage( "your_height" : MessageLookupByLibrary.simpleMessage("Ваш рост:"),
"В имени пользователя должно быть минимум 6 символов", "your_schedule" : MessageLookupByLibrary.simpleMessage("Ваш режим дня?"),
), "your_weight" : MessageLookupByLibrary.simpleMessage("Ваш вес:")
"no": MessageLookupByLibrary.simpleMessage("Нет"),
"no_account": MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"),
"notifications": MessageLookupByLibrary.simpleMessage("Уведомления"),
"one_option": MessageLookupByLibrary.simpleMessage("1 опция"),
"option": MessageLookupByLibrary.simpleMessage("Опция"),
"option_content": MessageLookupByLibrary.simpleMessage("Содержание опции"),
"option_content_hint_text": MessageLookupByLibrary.simpleMessage(
"На связи 7 дней в неделю",
),
"option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"),
"option_desc_hint_text": MessageLookupByLibrary.simpleMessage(
"Этот план питания для похудения на 7 дней сбалансированный и разнообразный",
),
"option_duration": MessageLookupByLibrary.simpleMessage(
"Продолжительность опции",
),
"option_personal_diet_desc": MessageLookupByLibrary.simpleMessage(
"Опция подразумевает создание индивидуальной диеты",
),
"option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"),
"order": MessageLookupByLibrary.simpleMessage("Порядок"),
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
"password_confirmation": MessageLookupByLibrary.simpleMessage(
"Подтвердите пароль",
),
"password_confirmation_mismatch": MessageLookupByLibrary.simpleMessage(
"Пароли не совпадают",
),
"password_empty": MessageLookupByLibrary.simpleMessage(
"Пароль обязателен для заполнения",
),
"password_short": MessageLookupByLibrary.simpleMessage(
"В пароле должно быть минимум 6 символов",
),
"pcs": MessageLookupByLibrary.simpleMessage("шт"),
"personal": MessageLookupByLibrary.simpleMessage("Персональные"),
"personal_diet_state": MessageLookupByLibrary.simpleMessage("Персональная"),
"photo": MessageLookupByLibrary.simpleMessage("Фото"),
"place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"),
"plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"),
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
"Большое подробное, привлекательное описание плана",
),
"plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"),
"plan_label_desc": MessageLookupByLibrary.simpleMessage(
"План для поддержания формы",
),
"plans_complete": MessageLookupByLibrary.simpleMessage("Пройденные планы"),
"popular": MessageLookupByLibrary.simpleMessage("Популярные"),
"preferred_language": MessageLookupByLibrary.simpleMessage(
"Предпочитаемый язык общения",
),
"pressure_empty": MessageLookupByLibrary.simpleMessage(
"Все показатели давления обязательны к заполнению",
),
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
"Все показатели давления должны быть числами",
),
"preview": MessageLookupByLibrary.simpleMessage("Превью"),
"price": MessageLookupByLibrary.simpleMessage("Цена"),
"price_explanation": MessageLookupByLibrary.simpleMessage(
"Разница в ценах, это комиссия Итвио",
),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"proteins": MessageLookupByLibrary.simpleMessage("Белки"),
"public_diet_state": MessageLookupByLibrary.simpleMessage("Публичная"),
"publish": MessageLookupByLibrary.simpleMessage("Опубликовать"),
"published": MessageLookupByLibrary.simpleMessage("Опубликованные"),
"removeFromActive": MessageLookupByLibrary.simpleMessage(
"Снять с публикации",
),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"services": MessageLookupByLibrary.simpleMessage("Оказание услуг"),
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"share": MessageLookupByLibrary.simpleMessage("Поделиться"),
"skip": MessageLookupByLibrary.simpleMessage("Пропустить"),
"slim": MessageLookupByLibrary.simpleMessage("Похудение"),
"slim_type": MessageLookupByLibrary.simpleMessage("Похудение"),
"slim_type_desc": MessageLookupByLibrary.simpleMessage(
"План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;",
),
"standard": MessageLookupByLibrary.simpleMessage("Стандарт"),
"startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"),
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:",
),
"text_was_copied": MessageLookupByLibrary.simpleMessage(
"Текст успешно скопирован",
),
"three_options": MessageLookupByLibrary.simpleMessage("3 опции"),
"toOffer": MessageLookupByLibrary.simpleMessage("Предложить"),
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
"unfavorite_foods": MessageLookupByLibrary.simpleMessage(
"Нелюбимые продукты",
),
"up_pressure": MessageLookupByLibrary.simpleMessage(
"Верхние показатели давления:",
),
"user_authorization": MessageLookupByLibrary.simpleMessage(
"Аутентификация",
),
"user_data": MessageLookupByLibrary.simpleMessage("Данные пользователя"),
"user_registration": MessageLookupByLibrary.simpleMessage(
"Регистрация пользователя",
),
"username": MessageLookupByLibrary.simpleMessage("Имя пользователя"),
"vegan": MessageLookupByLibrary.simpleMessage("Веган."),
"vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"),
"vegan_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.",
),
"waist": MessageLookupByLibrary.simpleMessage("Талия"),
"waist_circumference": MessageLookupByLibrary.simpleMessage("Обхват талии"),
"weight": MessageLookupByLibrary.simpleMessage("Вес"),
"weight_chart": MessageLookupByLibrary.simpleMessage("График веса"),
"weight_measurements": MessageLookupByLibrary.simpleMessage("Замер веса"),
"wellness": MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
"wellness_type": MessageLookupByLibrary.simpleMessage(
"Улучшение самочувствия",
),
"wellness_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
),
"what_is_your_name": MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
"wrong_credentials": MessageLookupByLibrary.simpleMessage(
"Неверные данные пользователя",
),
"you_will_get": MessageLookupByLibrary.simpleMessage("Вы получите"),
"your_blood_pressure": MessageLookupByLibrary.simpleMessage(
"Ваше артериальное давление?",
),
"your_favorite_sports": MessageLookupByLibrary.simpleMessage(
"Ваши спортивные увлечения?",
),
"your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"),
"your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"),
"your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"),
"your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:"),
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@@ -466,6 +466,7 @@
"weAreLookingFor": "Мы собираем самое большое сообщество людей, желающих вести здоровый образ жизни!", "weAreLookingFor": "Мы собираем самое большое сообщество людей, желающих вести здоровый образ жизни!",
"letsInvite": "Давайте пригласим близких в итвио", "letsInvite": "Давайте пригласим близких в итвио",
"eatvioIsAHelthyHabbit": "Итвио — это полезная привычка", "eatvioIsAHelthyHabbit": "Итвио — это полезная привычка",
"invite": "Пригласить" "invite": "Пригласить",
"comment_hint": "Комментарий"
} }

View File

@@ -215,5 +215,6 @@
"from": "от", "from": "от",
"user_data": "Данные пользователя", "user_data": "Данные пользователя",
"infoAboutOrder": "Информация о заказе", "infoAboutOrder": "Информация о заказе",
"notifications": "Уведомления" "notifications": "Уведомления",
"comment_hint": "Комментарий"
} }