diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 1b2e5b616..12d15f6a7 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -134,7 +134,7 @@
+ android:value="false" />
io.flutter.embedded_views_preview
FlutterDeepLinkingEnabled
-
+
diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart
index 3617539e3..975f985a6 100644
--- a/lib/config/app_config.dart
+++ b/lib/config/app_config.dart
@@ -145,6 +145,7 @@ abstract class AppConfig {
static bool sendPublicReadReceipts = true;
static bool swipeRightToLeftToReply = true;
static bool? sendOnEnter;
+ static bool useActivityImageAsChatBackground = true;
static bool showPresences = true;
// #Pangea
// static bool displayNavigationRail = false;
diff --git a/lib/config/routes.dart b/lib/config/routes.dart
index a2487a95a..032bf08e5 100644
--- a/lib/config/routes.dart
+++ b/lib/config/routes.dart
@@ -5,10 +5,10 @@ import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:go_router/go_router.dart';
-import 'package:matrix/matrix_api_lite/generated/model.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
+import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/archive/archive.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart';
@@ -21,6 +21,8 @@ import 'package:fluffychat/pages/device_settings/device_settings.dart';
import 'package:fluffychat/pages/login/login.dart';
import 'package:fluffychat/pages/new_group/new_group.dart';
import 'package:fluffychat/pages/new_private_chat/new_private_chat.dart';
+import 'package:fluffychat/pages/onboarding/enable_notifications.dart';
+import 'package:fluffychat/pages/onboarding/space_code_onboarding.dart';
import 'package:fluffychat/pages/settings/settings.dart';
import 'package:fluffychat/pages/settings_3pid/settings_3pid.dart';
import 'package:fluffychat/pages/settings_chat/settings_chat.dart';
@@ -38,6 +40,7 @@ import 'package:fluffychat/pangea/analytics_misc/analytics_navigation_util.dart'
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_page/activity_archive.dart';
import 'package:fluffychat/pangea/analytics_page/empty_analytics_page.dart';
+import 'package:fluffychat/pangea/analytics_practice/analytics_practice_page.dart';
import 'package:fluffychat/pangea/analytics_summary/level_analytics_details_content.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/chat_settings/pages/edit_course.dart';
@@ -45,24 +48,25 @@ import 'package:fluffychat/pangea/chat_settings/pages/pangea_invitation_selectio
import 'package:fluffychat/pangea/common/utils/p_vguard.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/course_creation/course_invite_page.dart';
+import 'package:fluffychat/pangea/course_creation/public_course_preview.dart';
import 'package:fluffychat/pangea/course_creation/selected_course_page.dart';
import 'package:fluffychat/pangea/join_codes/join_with_link_page.dart';
import 'package:fluffychat/pangea/learning_settings/settings_learning.dart';
-import 'package:fluffychat/pangea/login/pages/add_course_page.dart';
import 'package:fluffychat/pangea/login/pages/course_code_page.dart';
import 'package:fluffychat/pangea/login/pages/create_pangea_account_page.dart';
+import 'package:fluffychat/pangea/login/pages/find_course_page.dart';
import 'package:fluffychat/pangea/login/pages/language_selection_page.dart';
import 'package:fluffychat/pangea/login/pages/login_or_signup_view.dart';
import 'package:fluffychat/pangea/login/pages/new_course_page.dart';
-import 'package:fluffychat/pangea/login/pages/public_courses_page.dart';
import 'package:fluffychat/pangea/login/pages/signup.dart';
import 'package:fluffychat/pangea/space_analytics/space_analytics.dart';
import 'package:fluffychat/pangea/spaces/space_constants.dart';
import 'package:fluffychat/pangea/subscription/pages/settings_subscription.dart';
-import 'package:fluffychat/pangea/vocab_practice/vocab_practice_page.dart';
+import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/config_viewer.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:fluffychat/widgets/layouts/two_column_layout.dart';
+import 'package:fluffychat/widgets/local_notifications_extension.dart';
import 'package:fluffychat/widgets/log_view.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/share_scaffold_dialog.dart';
@@ -206,98 +210,29 @@ abstract class AppRoutes {
const CreatePangeaAccountPage(),
),
),
+ GoRoute(
+ path: 'notifications',
+ pageBuilder: (context, state) => defaultPageBuilder(
+ context,
+ state,
+ const EnableNotifications(),
+ ),
+ redirect: (context, state) async {
+ final redirect =
+ await PAuthGaurd.onboardingRedirect(context, state);
+ if (redirect != null) return redirect;
+ final enabled = await Matrix.of(context).notificationsEnabled;
+ if (enabled) return "/registration/course";
+ return null;
+ },
+ ),
GoRoute(
path: 'course',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
- const AddCoursePage(route: 'registration'),
+ const SpaceCodeOnboarding(),
),
- routes: [
- GoRoute(
- path: 'private',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- const CourseCodePage(),
- );
- },
- ),
- GoRoute(
- path: 'public',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- const PublicCoursesPage(
- route: 'registration',
- showFilters: false,
- ),
- );
- },
- routes: [
- GoRoute(
- path: ':courseid',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- SelectedCourse(
- state.pathParameters['courseid']!,
- SelectedCourseMode.join,
- roomChunk: state.extra as PublicRoomsChunk?,
- ),
- );
- },
- ),
- ],
- ),
- GoRoute(
- path: 'own',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- const NewCoursePage(
- route: 'registration',
- showFilters: false,
- ),
- );
- },
- routes: [
- GoRoute(
- path: ':courseid',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- SelectedCourse(
- state.pathParameters['courseid']!,
- SelectedCourseMode.launch,
- ),
- );
- },
- routes: [
- GoRoute(
- path: 'invite',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- CourseInvitePage(
- state.pathParameters['courseid']!,
- courseCreationCompleter:
- state.extra as Completer?,
- ),
- );
- },
- ),
- ],
- ),
- ],
- ),
- ],
),
],
),
@@ -432,7 +367,7 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
- const AddCoursePage(route: 'rooms'),
+ const FindCoursePage(),
),
routes: [
GoRoute(
@@ -445,41 +380,16 @@ abstract class AppRoutes {
);
},
),
- GoRoute(
- path: 'public',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- const PublicCoursesPage(
- route: 'rooms',
- ),
- );
- },
- routes: [
- GoRoute(
- path: ':courseid',
- pageBuilder: (context, state) {
- return defaultPageBuilder(
- context,
- state,
- SelectedCourse(
- state.pathParameters['courseid']!,
- SelectedCourseMode.join,
- roomChunk: state.extra as PublicRoomsChunk?,
- ),
- );
- },
- ),
- ],
- ),
GoRoute(
path: 'own',
pageBuilder: (context, state) {
return defaultPageBuilder(
context,
state,
- const NewCoursePage(route: 'rooms'),
+ NewCoursePage(
+ route: 'rooms',
+ initialLanguageCode: state.uri.queryParameters['lang'],
+ ),
);
},
routes: [
@@ -514,6 +424,18 @@ abstract class AppRoutes {
),
],
),
+ GoRoute(
+ path: ':courseroomid',
+ pageBuilder: (context, state) {
+ return defaultPageBuilder(
+ context,
+ state,
+ PublicCoursePreview(
+ roomID: state.pathParameters['courseroomid']!,
+ ),
+ );
+ },
+ ),
],
),
GoRoute(
@@ -542,6 +464,18 @@ abstract class AppRoutes {
),
redirect: loggedOutRedirect,
routes: [
+ GoRoute(
+ path: 'practice',
+ pageBuilder: (context, state) {
+ return defaultPageBuilder(
+ context,
+ state,
+ const AnalyticsPractice(
+ type: ConstructTypeEnum.morph,
+ ),
+ );
+ },
+ ),
GoRoute(
path: ':construct',
pageBuilder: (context, state) {
@@ -580,9 +514,29 @@ abstract class AppRoutes {
return defaultPageBuilder(
context,
state,
- const VocabPractice(),
+ const AnalyticsPractice(
+ type: ConstructTypeEnum.vocab,
+ ),
);
},
+ onExit: (context, state) async {
+ // Check if bypass flag was set before navigation
+ if (AnalyticsPractice.bypassExitConfirmation) {
+ AnalyticsPractice.bypassExitConfirmation = false;
+ return true;
+ }
+
+ final result = await showOkCancelAlertDialog(
+ useRootNavigator: false,
+ context: context,
+ title: L10n.of(context).areYouSure,
+ okLabel: L10n.of(context).yes,
+ cancelLabel: L10n.of(context).cancel,
+ message: L10n.of(context).exitPractice,
+ );
+
+ return result == OkCancelResult.ok;
+ },
),
GoRoute(
path: ':construct',
diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb
index 37cbcb133..cebe2097f 100644
--- a/lib/l10n/intl_ar.arb
+++ b/lib/l10n/intl_ar.arb
@@ -1,6 +1,6 @@
{
"@@locale": "ar",
- "@@last_modified": "2026-01-07 14:27:10.974178",
+ "@@last_modified": "2026-02-05 10:09:56.397837",
"about": "حول",
"@about": {
"type": "String",
@@ -3670,8 +3670,6 @@
"noPaymentInfo": "لا حاجة لمعلومات الدفع!",
"updatePhoneOS": "قد تحتاج إلى تحديث إصدار نظام تشغيل جهازك.",
"wordsPerMinute": "كلمات في الدقيقة",
- "autoIGCToolName": "تشغيل مساعدة الكتابة Pangea تلقائيًا",
- "autoIGCToolDescription": "تشغيل مساعدة القواعد والترجمة في دردشة Pangea تلقائيًا قبل إرسال رسالتي.",
"tooltipInstructionsTitle": "لست متأكدًا مما يفعله ذلك؟",
"tooltipInstructionsMobileBody": "اضغط مع الاستمرار على العناصر لعرض تلميحات الأدوات.",
"tooltipInstructionsBrowserBody": "مرر فوق العناصر لعرض تلميحات الأدوات.",
@@ -4300,7 +4298,6 @@
"numModules": "{num} وحدات",
"coursePlan": "خطة الدورة",
"editCourseLater": "يمكنك تعديل عنوان النموذج، الأوصاف، وصورة الدورة لاحقًا.",
- "newCourseAccess": "افتراضيًا، الدورات خاصة وتتطلب موافقة المسؤول للانضمام. يمكنك تعديل هذه الإعدادات في أي وقت.",
"createCourse": "إنشاء دورة",
"stats": "إحصائيات",
"createGroupChat": "إنشاء دردشة جماعية",
@@ -6423,14 +6420,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9049,10 +9038,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11042,5 +11027,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 لقد غادرت الدردشة",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "تم بدء التنزيل",
+ "webDownloadPermissionMessage": "إذا كان متصفحك يمنع التنزيلات، يرجى تمكين التنزيلات لهذا الموقع.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "لن يتم حفظ تقدم جلسة التدريب الخاصة بك.",
+ "practiceGrammar": "تدرب على القواعد",
+ "notEnoughToPractice": "أرسل المزيد من الرسائل لفتح التدريب",
+ "constructUseCorGCDesc": "تدريب على فئة القواعد الصحيحة",
+ "constructUseIncGCDesc": "تدريب على فئة القواعد غير الصحيحة",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "ممارسة تصحيح أخطاء القواعد",
+ "constructUseIncGEDesc": "ممارسة أخطاء القواعد غير الصحيحة",
+ "fillInBlank": "املأ الفراغ بالخيار الصحيح",
+ "learn": "تعلم",
+ "languageUpdated": "تم تحديث اللغة المستهدفة!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "صوت بوت بانجيا",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "تم إرسال طلبك إلى إدارة الدورة! سيتم السماح لك بالدخول إذا وافقوا.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "هل لديك رمز دعوة أو رابط لدورة عامة؟",
+ "welcomeUser": "مرحبًا {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "ابحث عن المستخدمين لدعوتهم إلى هذه الدردشة.",
+ "publicInviteDescSpace": "ابحث عن المستخدمين لدعوتهم إلى هذا الفضاء.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "بانجيا شات هو تطبيق رسائل لذا فإن الإشعارات مهمة!",
+ "enableNotificationsDesc": "السماح بالإشعارات",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "استخدم صورة النشاط كخلفية للدردشة",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "الدردشة مع الدعم",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "بشكل افتراضي، الدورات قابلة للبحث علنًا وتتطلب موافقة المسؤول للانضمام. يمكنك تعديل هذه الإعدادات في أي وقت.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "ما اللغة التي تتعلمها؟",
+ "searchLanguagesHint": "ابحث عن اللغات المستهدفة",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "أسئلة؟ نحن هنا للمساعدة!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "حدث خطأ ما، ونحن نعمل بجد على إصلاحه. تحقق مرة أخرى لاحقًا.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "تفعيل مساعدة الكتابة",
+ "autoIGCToolDescription": "تشغيل أدوات دردشة بانجيا تلقائيًا لتصحيح الرسائل المرسلة إلى اللغة المستهدفة.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "فشل التسجيل. يرجى التحقق من أذونات الصوت الخاصة بك والمحاولة مرة أخرى.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "تعبير اصطلاحي",
+ "grammarCopyPOSphrasalv": "فعل مركب",
+ "grammarCopyPOScompn": "مركب",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_be.arb b/lib/l10n/intl_be.arb
index 8f328151c..72fb9a8ca 100644
--- a/lib/l10n/intl_be.arb
+++ b/lib/l10n/intl_be.arb
@@ -1187,8 +1187,6 @@
"noPaymentInfo": "Інфармацыя аб плацяжах не патрабуецца!",
"updatePhoneOS": "Магчыма, вам трэба абнавіць версію аперацыйнай сістэмы вашага прылады.",
"wordsPerMinute": "Словы за хвіліну",
- "autoIGCToolName": "Аўтаматычна запускаць дапамогу для пісьма Pangea",
- "autoIGCToolDescription": "Аўтаматычна запускаць дапамогу для граматыкі і перакладу ў чат-прыкладанні Pangea перад адпраўкай майго паведамлення.",
"tooltipInstructionsTitle": "Не ўпэўнены, што гэта робіць?",
"tooltipInstructionsMobileBody": "Затрымайце і трымайце элементы, каб праглядзець падказкі.",
"tooltipInstructionsBrowserBody": "Навядзіце курсор на элементы, каб праглядзець падказкі.",
@@ -1816,7 +1814,6 @@
"numModules": "{num} модулі",
"coursePlan": "План курса",
"editCourseLater": "Вы можаце рэдагаваць назву шаблона, апісанні і выяву курса пазней.",
- "newCourseAccess": "Па змаўчанні курсы прыватныя і патрабуюць адабрэння адміністратара для далучэння. Вы можаце змяняць гэтыя налады ў любы час.",
"createCourse": "Стварыць курс",
"stats": "Статыстыка",
"createGroupChat": "Стварыць групавы чат",
@@ -1911,7 +1908,7 @@
"playWithAI": "Пакуль гуляйце з ШІ",
"courseStartDesc": "Pangea Bot гатовы да працы ў любы час!\n\n...але навучанне лепш з сябрамі!",
"@@locale": "be",
- "@@last_modified": "2026-01-07 14:26:19.740329",
+ "@@last_modified": "2026-02-05 10:09:46.469770",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7305,14 +7302,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9931,10 +9920,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11924,5 +11909,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Вы пакінулі чат",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Спампоўка ініцыявана",
+ "webDownloadPermissionMessage": "Калі ваш браўзер блакуе спампоўкі, калі ласка, уключыце спампоўкі для гэтага сайта.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Ваш практычны сеанс не будзе захаваны.",
+ "practiceGrammar": "Практыкаваць граматыку",
+ "notEnoughToPractice": "Адпраўце больш паведамленняў, каб разблакаваць практыку",
+ "constructUseCorGCDesc": "Практыка ў катэгорыі правільнай граматыкі",
+ "constructUseIncGCDesc": "Практыка ў катэгорыі няправільнай граматыкі",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Практыка правільнага выкарыстання граматычных памылак",
+ "constructUseIncGEDesc": "Практыка няправільнага выкарыстання граматычных памылак",
+ "fillInBlank": "Запоўніце прабел правільным выбарам",
+ "learn": "Навучыцца",
+ "languageUpdated": "Мэтавая мова абноўлена!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Голас Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Ваш запыт быў адпраўлены адміністрацыі курса! Вы будзеце дапушчаны, калі яны зацвердзяць.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Ці маеце вы код запрашэння або спасылку на публічны курс?",
+ "welcomeUser": "Сардэчна запрашаем, {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Шукаць карыстальнікаў, каб запрасіць іх у гэты чат.",
+ "publicInviteDescSpace": "Шукаць карыстальнікаў, каб запрасіць іх у гэтае прастору.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat - гэта прыкладанне для адпраўкі паведамленняў, таму апавяшчэнні важныя!",
+ "enableNotificationsDesc": "Дазволіць апавяшчэнні",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Выкарыстоўвайце малюнак актыўнасці як фон чата",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Чат з падтрымкай",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Па змаўчанні курсы з'яўляюцца адкрытымі для пошуку і патрабуюць адабрэння адміністратара для далучэння. Вы можаце змяняць гэтыя налады ў любы час.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Якую мову вы вывучаеце?",
+ "searchLanguagesHint": "Пошук мэтавых моў",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Пытанні? Мы тут, каб дапамагчы!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Што-то пайшло не так, і мы актыўна працуем над выпраўленнем. Праверце пазней.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Уключыць дапамогу ў напісанні",
+ "autoIGCToolDescription": "Аўтаматычна запускаць інструменты Pangea Chat для выпраўлення адпраўленых паведамленняў на мэтавую мову.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Запіс не ўдалося. Калі ласка, праверце свае аўдыё дазволы і паспрабуйце яшчэ раз.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Ідыём",
+ "grammarCopyPOSphrasalv": "Фразавы дзеяслоў",
+ "grammarCopyPOScompn": "Складаны",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_bn.arb b/lib/l10n/intl_bn.arb
index 0c4ceee1b..ce0510c60 100644
--- a/lib/l10n/intl_bn.arb
+++ b/lib/l10n/intl_bn.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:28:06.931186",
+ "@@last_modified": "2026-02-05 10:10:08.830801",
"about": "সম্পর্কে",
"@about": {
"type": "String",
@@ -3779,8 +3779,6 @@
"noPaymentInfo": "কোনও পেমেন্ট তথ্য প্রয়োজন নয়!",
"updatePhoneOS": "আপনার ডিভাইসের অপারেটিং সিস্টেমের সংস্করণ আপডেটের প্রয়োজন হতে পারে।",
"wordsPerMinute": "প্রতি মিনিটে শব্দ",
- "autoIGCToolName": "পাঙ্গিয়া লেখনী সহায়তা স্বয়ংক্রিয়ভাবে চালান",
- "autoIGCToolDescription": "আমার বার্তা পাঠানোর আগে স্বয়ংক্রিয়ভাবে পাঙ্গিয়া চ্যাট ব্যাকরণ এবং অনুবাদ লেখনী সহায়তা চালান।",
"tooltipInstructionsTitle": "এটি কি করে তা নিশ্চিত নন?",
"tooltipInstructionsMobileBody": "আইটেমে চাপুন এবং ধরে রাখুন টুলটিপ দেখার জন্য।",
"tooltipInstructionsBrowserBody": "আইটেমের উপর হোভার করে টুলটিপ দেখুন।",
@@ -4407,7 +4405,6 @@
"numModules": "{num} মডিউল",
"coursePlan": "কোর্স পরিকল্পনা",
"editCourseLater": "আপনি পরে টেমপ্লেট শিরোনাম, বিবরণ, এবং কোর্স ছবি সম্পাদনা করতে পারেন।",
- "newCourseAccess": "ডিফল্টভাবে, কোর্সগুলি ব্যক্তিগত এবং যোগদানের জন্য অ্যাডমিন অনুমোদন প্রয়োজন। আপনি এই সেটিংস যেকোন সময় সম্পাদনা করতে পারেন।",
"createCourse": "কোর্স তৈরি করুন",
"stats": "পরিসংখ্যান",
"createGroupChat": "গ্রুপ চ্যাট তৈরি করুন",
@@ -7310,14 +7307,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9936,10 +9925,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11929,5 +11914,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 আপনি চ্যাট ছেড়ে দিয়েছেন",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "ডাউনলোড শুরু হয়েছে",
+ "webDownloadPermissionMessage": "যদি আপনার ব্রাউজার ডাউনলোড ব্লক করে, অনুগ্রহ করে এই সাইটের জন্য ডাউনলোড সক্ষম করুন।",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "আপনার অনুশীলন সেশনের অগ্রগতি সংরক্ষিত হবে না।",
+ "practiceGrammar": "ব্যাকরণ অনুশীলন করুন",
+ "notEnoughToPractice": "অনুশীলন আনলক করতে আরও বার্তা পাঠান",
+ "constructUseCorGCDesc": "সঠিক ব্যাকরণ বিভাগ অনুশীলন",
+ "constructUseIncGCDesc": "ভুল ব্যাকরণ বিভাগ অনুশীলন",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "সঠিক ব্যাকরণ ত্রুটি অনুশীলন",
+ "constructUseIncGEDesc": "ভুল ব্যাকরণ ত্রুটি অনুশীলন",
+ "fillInBlank": "সঠিক পছন্দ দিয়ে ফাঁকা স্থান পূরণ করুন",
+ "learn": "শিখুন",
+ "languageUpdated": "লক্ষ্য ভাষা আপডেট করা হয়েছে!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "প্যাঙ্গিয়া বটের কণ্ঠ",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "আপনার অনুরোধ কোর্স প্রশাসকের কাছে পাঠানো হয়েছে! তারা অনুমোদন করলে আপনাকে প্রবেশ করতে দেওয়া হবে।",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "আপনার কি একটি আমন্ত্রণ কোড বা একটি পাবলিক কোর্সের লিঙ্ক আছে?",
+ "welcomeUser": "স্বাগতম {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "এই চ্যাটে আমন্ত্রণ জানানোর জন্য ব্যবহারকারীদের খুঁজুন।",
+ "publicInviteDescSpace": "এই স্পেসে আমন্ত্রণ জানানোর জন্য ব্যবহারকারীদের খুঁজুন।",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "প্যাঙ্গিয়া চ্যাট একটি টেক্সটিং অ্যাপ, তাই নোটিফিকেশন গুরুত্বপূর্ণ!",
+ "enableNotificationsDesc": "নোটিফিকেশন অনুমোদন করুন",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "চ্যাট ব্যাকগ্রাউন্ড হিসেবে কার্যকলাপের ছবি ব্যবহার করুন",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "সমর্থনের সাথে চ্যাট করুন",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "ডিফল্টভাবে, কোর্সগুলি জনসাধারণের জন্য অনুসন্ধানযোগ্য এবং যোগদানের জন্য প্রশাসক অনুমোদনের প্রয়োজন। আপনি যে কোনও সময় এই সেটিংসগুলি সম্পাদনা করতে পারেন।",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "আপনি কোন ভাষা শিখছেন?",
+ "searchLanguagesHint": "লক্ষ্য ভাষা অনুসন্ধান করুন",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "প্রশ্ন আছে? আমরা সাহায্য করতে এখানে আছি!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "কিছু ভুল হয়েছে, এবং আমরা এটি ঠিক করতে কঠোর পরিশ্রম করছি। পরে আবার চেক করুন।",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "লেখার সহায়তা সক্রিয় করুন",
+ "autoIGCToolDescription": "লক্ষ্য ভাষায় পাঠানো বার্তা সংশোধন করতে স্বয়ংক্রিয়ভাবে প্যাঙ্গিয়া চ্যাট টুলগুলি চালান।",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "রেকর্ডিং ব্যর্থ হয়েছে। দয়া করে আপনার অডিও অনুমতিগুলি পরীক্ষা করুন এবং আবার চেষ্টা করুন।",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "বাগধারা",
+ "grammarCopyPOSphrasalv": "ফ্রেজাল ক্রিয়া",
+ "grammarCopyPOScompn": "যুগ্ম",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_bo.arb b/lib/l10n/intl_bo.arb
index 6bb73e24c..7cf7f1681 100644
--- a/lib/l10n/intl_bo.arb
+++ b/lib/l10n/intl_bo.arb
@@ -3621,8 +3621,6 @@
"translationTooltip": "འབྲེལ་བའི་སྒྲོམ",
"updatePhoneOS": "ཁྱེད་ཀྱི་རྒྱུན་ལས་སྤྱོད་ལམ་ལ་བསྐུར་བྱེད་དགོས་མིན་པ",
"wordsPerMinute": "ཚིག་ལ་སྤྱོད་ལམ་ལ་བརྟེན་",
- "autoIGCToolName": "ཕན་ཚུལ་ལས་འགན་སྤྱོད་ལས་སྤྱོད་ལམ་ལ་བརྟེན་",
- "autoIGCToolDescription": "ཁྱེད་ཀྱི་དུས་སྐབས་སྤྱོད་ལམ་ལ་བརྟེན་པའི་ཕན་ཚུལ་ལས་འགན་སྤྱོད་ལས་སྤྱོད་ལམ་ལ་བརྟེན་",
"tooltipInstructionsTitle": "དེ་ལ་གང་འདྲ་ཡོད་པ?",
"tooltipInstructionsMobileBody": "རྟེན་འབྲེལ་དང་བསྟན་པའི་རྟེན་འབྲེལ་ལ་ལོག་བརྟེན་",
"tooltipInstructionsBrowserBody": "རྟེན་འབྲེལ་ལ་ལོག་བརྟེན་",
@@ -4211,7 +4209,6 @@
"numModules": "{num} ᠪᠣᠯᠣᠰ",
"coursePlan": "ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ",
"editCourseLater": "ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ",
- "newCourseAccess": "ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ",
"createCourse": "ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ",
"stats": "ᠪᠠᠢᠯᠠᠭᠤᠯᠤ",
"createGroupChat": "ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ ᠪᠠᠢᠯᠠᠭᠤᠯᠤ",
@@ -4279,7 +4276,7 @@
"joinPublicTrip": "མི་ཚེས་ལ་ལོག་འབད།",
"startOwnTrip": "ངེད་རང་གི་ལོག་ལ་སྦྱོར་བཅོས།",
"@@locale": "bo",
- "@@last_modified": "2026-01-07 14:27:54.438001",
+ "@@last_modified": "2026-02-05 10:10:06.262776",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -6725,14 +6722,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9183,10 +9172,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10579,5 +10564,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Oi saíste do chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download initiated",
+ "webDownloadPermissionMessage": "If your browser blocks downloads, please enable downloads for this site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Ndae bɔkɔɔ a wopɛ no, wo nsɛm a wopɛ sɛ woyɛ no bɛyɛ a, ɛrenyɛ.",
+ "practiceGrammar": "Bɔ mmara",
+ "notEnoughToPractice": "Sɛ wopɛ sɛ woyɛ bɔ mmara a, fa nsɛm pii to mu",
+ "constructUseCorGCDesc": "Nokware mmara kategorie bɔ mmara",
+ "constructUseIncGCDesc": "Nnokwa mmara kategorie bɔ mmara",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Praktik kesalahan tata bahasa yang benar",
+ "constructUseIncGEDesc": "Praktik kesalahan tata bahasa yang salah",
+ "fillInBlank": "Isi kekosongan dengan pilihan yang benar",
+ "learn": "Belajar",
+ "languageUpdated": "Bahasa target diperbarui!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot voz",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Yor requst has been sent to course admin! Yu'll be let in if dey approve.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Do you have an invite code or link to a public course?",
+ "welcomeUser": "Welcome {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Searc for users to invite them to this chat.",
+ "publicInviteDescSpace": "Searc for users to invite them to this space.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat je aplikacija za slanje poruka, pa su obaveštenja važna!",
+ "enableNotificationsDesc": "Dozvoli obaveštenja",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Bruk aktivitetsbilde som chatbakgrunn",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat with Support",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Biy default, kursi biyo publicly searchable e biyo require admin approval to join. Yu can edit these settings at any time.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Kedua bahasa apa yang Anda pelajari?",
+ "searchLanguagesHint": "Cari bahasa target",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Pytania? Jesteśmy tutaj, aby pomóc!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Nǐng bǐng wǒng, yǐng wǒng bǐng wǒng. Cǐng bǐng yǐng bǐng.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Buka bantuan nulis",
+ "autoIGCToolDescription": "Secara otomatis menjalankan alat Pangea Chat untuk memperbaiki pesan yang dikirim ke bahasa target.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Recording failed. Please check your audio permissions and try again.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Compound",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ca.arb b/lib/l10n/intl_ca.arb
index 0291e8daf..e2777e39a 100644
--- a/lib/l10n/intl_ca.arb
+++ b/lib/l10n/intl_ca.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:26:27.395689",
+ "@@last_modified": "2026-02-05 10:09:47.712187",
"about": "Quant a",
"@about": {
"type": "String",
@@ -3757,8 +3757,6 @@
"noPaymentInfo": "No cal informació de pagament!",
"updatePhoneOS": "Pot ser que necessitis actualitzar la versió del sistema operatiu del teu dispositiu.",
"wordsPerMinute": "Paraules per minut",
- "autoIGCToolName": "Executa automàticament l'assistència d'escriptura Pangea",
- "autoIGCToolDescription": "Executa automàticament l'assistència d'escriptura de gramàtica i traducció de Pangea abans d'enviar el meu missatge.",
"tooltipInstructionsTitle": "No estàs segur de què fa això?",
"tooltipInstructionsMobileBody": "Prem i mantén premut per veure les eines d'informació sobre les opcions.",
"tooltipInstructionsBrowserBody": "Passa el cursor sobre els elements per veure les eines d'informació.",
@@ -4386,7 +4384,6 @@
"numModules": "{num} mòduls",
"coursePlan": "Pla de curs",
"editCourseLater": "Pots editar el títol de la plantilla, les descripcions i la imatge del curs més tard.",
- "newCourseAccess": "Per defecte, els cursos són privats i requereixen l'aprovació de l'administrador per unir-se. Pots editar aquests paràmetres en qualsevol moment.",
"createCourse": "Crear curs",
"stats": "Estadístiques",
"createGroupChat": "Crear xat de grup",
@@ -6230,14 +6227,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8856,10 +8845,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10849,5 +10834,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Has deixat el xat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Descàrrega iniciada",
+ "webDownloadPermissionMessage": "Si el teu navegador bloqueja les descàrregues, si us plau, activa les descàrregues per a aquest lloc.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "El teu progrés de la sessió de pràctica no es desarà.",
+ "practiceGrammar": "Practica gramàtica",
+ "notEnoughToPractice": "Envia més missatges per desbloquejar la pràctica",
+ "constructUseCorGCDesc": "Pràctica de la categoria de gramàtica correcta",
+ "constructUseIncGCDesc": "Pràctica de la categoria de gramàtica incorrecta",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Pràctica d'errors gramaticals correctes",
+ "constructUseIncGEDesc": "Pràctica d'errors gramaticals incorrectes",
+ "fillInBlank": "Omple el buit amb l'elecció correcta",
+ "learn": "Aprendre",
+ "languageUpdated": "Idioma objectiu actualitzat!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Veu del bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "La teva sol·licitud s'ha enviat a l'administrador del curs! Et deixaran entrar si ho aproven.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Tens un codi d'invitació o un enllaç a un curs públic?",
+ "welcomeUser": "Benvingut {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Cerca usuaris per convidar-los a aquest xat.",
+ "publicInviteDescSpace": "Cerca usuaris per convidar-los a aquest espai.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat és una aplicació de missatgeria, així que les notificacions són importants!",
+ "enableNotificationsDesc": "Permetre notificacions",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Utilitza la imatge d'activitat com a fons de xat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Xateja amb el Suport",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Per defecte, els cursos són cercables públicament i requereixen l'aprovació de l'administrador per unir-se. Podeu editar aquestes configuracions en qualsevol moment.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Quina llengua estàs aprenent?",
+ "searchLanguagesHint": "Cerca llengües objectiu",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Preguntes? Som aquí per ajudar!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Alguna cosa ha anat malament, i estem treballant dur per solucionar-ho. Comprova-ho més tard.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Activar l'assistència d'escriptura",
+ "autoIGCToolDescription": "Executar automàticament les eines de Pangea Chat per corregir els missatges enviats a l'idioma de destinació.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "La gravació ha fallat. Si us plau, comproveu els vostres permisos d'àudio i torneu-ho a provar.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verb Phrasal",
+ "grammarCopyPOScompn": "Compost",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb
index ca1035047..9e1758776 100644
--- a/lib/l10n/intl_cs.arb
+++ b/lib/l10n/intl_cs.arb
@@ -1,6 +1,6 @@
{
"@@locale": "cs",
- "@@last_modified": "2026-01-07 14:26:03.848423",
+ "@@last_modified": "2026-02-05 10:09:43.831148",
"about": "O aplikaci",
"@about": {
"type": "String",
@@ -3314,8 +3314,6 @@
"noPaymentInfo": "Není třeba žádné platební informace!",
"updatePhoneOS": "Možná budete muset aktualizovat verzi operačního systému vašeho zařízení",
"wordsPerMinute": "Slov za minutu",
- "autoIGCToolName": "Automaticky spustit pomoc s psaním Pangea",
- "autoIGCToolDescription": "Automaticky spustit gramatickou kontrolu a překlad pomocí Pangea Chat před odesláním mé zprávy",
"tooltipInstructionsTitle": "Nejste si jistí, co to dělá?",
"tooltipInstructionsMobileBody": "Podržte položky pro zobrazení nápověd.",
"tooltipInstructionsBrowserBody": "Na položky najeďte myší pro zobrazení nápověd.",
@@ -3943,7 +3941,6 @@
"numModules": "{num} modulů",
"coursePlan": "Plán kurzu",
"editCourseLater": "Později můžete upravit název šablony, popisy a obrázek kurzu.",
- "newCourseAccess": "Ve výchozím nastavení jsou kurzy soukromé a vyžadují schválení správce pro připojení. Tyto nastavení můžete upravit kdykoli.",
"createCourse": "Vytvořit kurz",
"stats": "Statistiky",
"createGroupChat": "Vytvořit skupinový chat",
@@ -6813,14 +6810,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9439,10 +9428,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11432,5 +11417,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Opustil(a) jsi chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Stahování zahájeno",
+ "webDownloadPermissionMessage": "Pokud váš prohlížeč blokuje stahování, povolte prosím stahování pro tuto stránku.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Pokrok vaší cvičební relace nebude uložen.",
+ "practiceGrammar": "Cvičit gramatiku",
+ "notEnoughToPractice": "Odešlete více zpráv, abyste odemkli cvičení",
+ "constructUseCorGCDesc": "Cvičení správné gramatické kategorie",
+ "constructUseIncGCDesc": "Cvičení nesprávné gramatické kategorie",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Cvičení správné gramatiky",
+ "constructUseIncGEDesc": "Cvičení nesprávné gramatiky",
+ "fillInBlank": "Doplňte prázdné místo správnou volbou",
+ "learn": "Učit se",
+ "languageUpdated": "Cílový jazyk byl aktualizován!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Hlas Pangea Bota",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Vaše žádost byla odeslána administrátorovi kurzu! Budete vpuštěni, pokud ji schválí.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Máte pozvánkový kód nebo odkaz na veřejný kurz?",
+ "welcomeUser": "Vítejte {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Hledejte uživatele, které chcete pozvat do tohoto chatu.",
+ "publicInviteDescSpace": "Hledejte uživatele, které chcete pozvat do tohoto prostoru.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat je aplikace pro zasílání zpráv, takže jsou oznámení důležitá!",
+ "enableNotificationsDesc": "Povolit oznámení",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Použít obrázek aktivity jako pozadí chatu",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat s podporou",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Ve výchozím nastavení jsou kurzy veřejně vyhledatelné a vyžadují schválení administrátora pro připojení. Tyto nastavení můžete kdykoli upravit.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Jaký jazyk se učíte?",
+ "searchLanguagesHint": "Hledejte cílové jazyky",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Otázky? Jsme tu, abychom pomohli!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Něco se pokazilo a my na tom tvrdě pracujeme. Zkontrolujte to prosím později.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Povolit asistenci při psaní",
+ "autoIGCToolDescription": "Automaticky spouštět nástroje Pangea Chat pro opravu odeslaných zpráv do cílového jazyka.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Nahrávání se nezdařilo. Zkontrolujte prosím svá oprávnění k audiosouborům a zkuste to znovu.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Frázové sloveso",
+ "grammarCopyPOScompn": "Složenina",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb
index 512ab67cf..eef287d6f 100644
--- a/lib/l10n/intl_da.arb
+++ b/lib/l10n/intl_da.arb
@@ -1206,8 +1206,6 @@
"noPaymentInfo": "Ingen betalingsoplysninger nødvendige!",
"updatePhoneOS": "Du kan være nødt til at opdatere din enheds OS-version.",
"wordsPerMinute": "Ord pr. minut",
- "autoIGCToolName": "Kør Pangea skriveassistance automatisk",
- "autoIGCToolDescription": "Kør automatisk Pangea Chat grammatik- og oversættelsesassistance, før jeg sender min besked.",
"tooltipInstructionsTitle": "Er du ikke sikker på, hvad det gør?",
"tooltipInstructionsMobileBody": "Tryk og hold på elementer for at se værktøjstip.",
"tooltipInstructionsBrowserBody": "Hold musen over elementer for at se værktøjstip.",
@@ -1835,7 +1833,6 @@
"numModules": "{num} moduler",
"coursePlan": "Kursusplan",
"editCourseLater": "Du kan redigere skabelonens titel, beskrivelser og kursusbillede senere.",
- "newCourseAccess": "Som standard er kurser private og kræver godkendelse fra administrator for at deltage. Du kan redigere disse indstillinger når som helst.",
"createCourse": "Opret kursus",
"stats": "Statistikker",
"createGroupChat": "Opret gruppechat",
@@ -1930,7 +1927,7 @@
"playWithAI": "Leg med AI for nu",
"courseStartDesc": "Pangea Bot er klar til at starte når som helst!\n\n...men læring er bedre med venner!",
"@@locale": "da",
- "@@last_modified": "2026-01-07 14:23:47.042043",
+ "@@last_modified": "2026-02-05 10:09:17.541713",
"@aboutHomeserver": {
"type": "String",
"placeholders": {
@@ -7268,14 +7265,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9894,10 +9883,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11886,5 +11871,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Du forlod chatten",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download påbegyndt",
+ "webDownloadPermissionMessage": "Hvis din browser blokerer downloads, bedes du aktivere downloads for dette site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Din praksis session fremskridt vil ikke blive gemt.",
+ "practiceGrammar": "Øv grammatik",
+ "notEnoughToPractice": "Send flere beskeder for at låse op for praksis",
+ "constructUseCorGCDesc": "Korrekt grammatik kategori praksis",
+ "constructUseIncGCDesc": "Ukorrrekt grammatik kategori praksis",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Korrekt grammatikfejl praksis",
+ "constructUseIncGEDesc": "Ukorrrekt grammatikfejl praksis",
+ "fillInBlank": "Udfyld det tomme felt med det korrekte valg",
+ "learn": "Lær",
+ "languageUpdated": "Mål sprog opdateret!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot stemme",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Din anmodning er sendt til kursusadministratoren! Du vil blive lukket ind, hvis de godkender.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Har du en invitationskode eller et link til et offentligt kursus?",
+ "welcomeUser": "Velkommen {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Søg efter brugere for at invitere dem til denne chat.",
+ "publicInviteDescSpace": "Søg efter brugere for at invitere dem til dette rum.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat er en beskedapp, så notifikationer er vigtige!",
+ "enableNotificationsDesc": "Tillad notifikationer",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Brug aktivitetsbillede som chatbaggrund",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat med support",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Som standard er kurser offentligt søgbare og kræver administratorgodkendelse for at deltage. Du kan redigere disse indstillinger når som helst.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Hvilket sprog lærer du?",
+ "searchLanguagesHint": "Søg efter målsprog",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Spørgsmål? Vi er her for at hjælpe!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Noget gik galt, og vi arbejder hårdt på at løse det. Tjek igen senere.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Aktivér skriveassistance",
+ "autoIGCToolDescription": "Kør automatisk Pangea Chat-værktøjer for at rette sendte beskeder til målsproget.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Optagelse mislykkedes. Tjek venligst dine lydtilladelser og prøv igen.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Sammensat",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb
index 63d7c56b2..9c51df9e8 100644
--- a/lib/l10n/intl_de.arb
+++ b/lib/l10n/intl_de.arb
@@ -1,6 +1,6 @@
{
"@@locale": "de",
- "@@last_modified": "2026-01-07 14:25:24.418870",
+ "@@last_modified": "2026-02-05 10:09:37.665075",
"alwaysUse24HourFormat": "true",
"@alwaysUse24HourFormat": {
"description": "Set to true to always display time of day in 24 hour format."
@@ -3792,8 +3792,6 @@
"noPaymentInfo": "Keine Zahlungsinformationen erforderlich!",
"updatePhoneOS": "Sie müssen möglicherweise die OS-Version Ihres Geräts aktualisieren.",
"wordsPerMinute": "Wörter pro Minute",
- "autoIGCToolName": "Pangea Schreibhilfe automatisch ausführen",
- "autoIGCToolDescription": "Führen Sie die Pangea Chat-Grammatik- und Übersetzungs-Schreibhilfe automatisch aus, bevor Sie meine Nachricht senden.",
"tooltipInstructionsTitle": "Nicht sicher, was das macht?",
"tooltipInstructionsMobileBody": "Tippen und halten Sie Elemente, um Tooltips anzuzeigen.",
"tooltipInstructionsBrowserBody": "Bewegen Sie den Mauszeiger über Elemente, um Tooltips anzuzeigen.",
@@ -4421,7 +4419,6 @@
"numModules": "{num} Module",
"coursePlan": "Kursplan",
"editCourseLater": "Sie können den Titel, die Beschreibungen und das Kursbild später bearbeiten.",
- "newCourseAccess": "Standardmäßig sind Kurse privat und erfordern die Genehmigung eines Administrators, um beizutreten. Sie können diese Einstellungen jederzeit ändern.",
"createCourse": "Kurs erstellen",
"stats": "Statistiken",
"createGroupChat": "Gruppenchats erstellen",
@@ -6213,14 +6210,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8839,10 +8828,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10832,5 +10817,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Du hast den Chat verlassen",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download gestartet",
+ "webDownloadPermissionMessage": "Wenn Ihr Browser Downloads blockiert, aktivieren Sie bitte Downloads für diese Seite.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Ihr Fortschritt in der Übungssitzung wird nicht gespeichert.",
+ "practiceGrammar": "Grammatik üben",
+ "notEnoughToPractice": "Senden Sie mehr Nachrichten, um die Übung freizuschalten",
+ "constructUseCorGCDesc": "Übung der korrekten Grammatikkategorie",
+ "constructUseIncGCDesc": "Übung der inkorrekten Grammatikkategorie",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Korrekte Grammatikfehlerübung",
+ "constructUseIncGEDesc": "Falsche Grammatikfehlerübung",
+ "fillInBlank": "Füllen Sie die Lücke mit der richtigen Wahl aus",
+ "learn": "Lernen",
+ "languageUpdated": "Zielsprache aktualisiert!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot Stimme",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Ihre Anfrage wurde an den Kursadministrator gesendet! Sie werden eingelassen, wenn sie zustimmen.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Haben Sie einen Einladungscode oder einen Link zu einem öffentlichen Kurs?",
+ "welcomeUser": "Willkommen {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Suchen Sie nach Benutzern, um sie zu diesem Chat einzuladen.",
+ "publicInviteDescSpace": "Suchen Sie nach Benutzern, um sie zu diesem Raum einzuladen.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat ist eine Messaging-App, daher sind Benachrichtigungen wichtig!",
+ "enableNotificationsDesc": "Benachrichtigungen erlauben",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Aktivitätsbild als Chat-Hintergrund verwenden",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat mit dem Support",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Standardmäßig sind Kurse öffentlich durchsuchbar und erfordern die Genehmigung eines Administrators, um beizutreten. Sie können diese Einstellungen jederzeit bearbeiten.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Welche Sprache lernst du?",
+ "searchLanguagesHint": "Zielsprachen suchen",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Fragen? Wir sind hier, um zu helfen!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Etwas ist schiefgelaufen, und wir arbeiten hart daran, es zu beheben. Überprüfen Sie es später erneut.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Schreibassistenz aktivieren",
+ "autoIGCToolDescription": "Automatisch Pangea Chat-Tools ausführen, um gesendete Nachrichten in die Zielsprache zu korrigieren.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Die Aufnahme ist fehlgeschlagen. Bitte überprüfen Sie Ihre Audio-Berechtigungen und versuchen Sie es erneut.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Zusammengesetztes Wort",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb
index a8651b345..d2aecfddb 100644
--- a/lib/l10n/intl_el.arb
+++ b/lib/l10n/intl_el.arb
@@ -3732,8 +3732,6 @@
"noPaymentInfo": "Δεν απαιτείται πληροφορία πληρωμής!",
"updatePhoneOS": "Ίσως χρειαστεί να ενημερώσετε την έκδοση του λειτουργικού συστήματος της συσκευής σας.",
"wordsPerMinute": "Λέξεις ανά λεπτό",
- "autoIGCToolName": "Αυτόματη εκτέλεση της βοήθειας γραφής Pangea",
- "autoIGCToolDescription": "Αυτόματα εκτελείτε τη γραμματική και τη βοήθεια μετάφρασης του Pangea Chat πριν στείλετε το μήνυμά μου.",
"tooltipInstructionsTitle": "Δεν είστε σίγουροι τι κάνει αυτό;",
"tooltipInstructionsMobileBody": "Πατήστε και κρατήστε πατημένο αντικείμενα για να δείτε τις συμβουλές εργαλείων.",
"tooltipInstructionsBrowserBody": "Τοποθετήστε το δείκτη πάνω από αντικείμενα για να δείτε τις συμβουλές εργαλείων.",
@@ -4361,7 +4359,6 @@
"numModules": "{num} ενότητες",
"coursePlan": "Πλάνο Μαθήματος",
"editCourseLater": "Μπορείτε να επεξεργαστείτε τον τίτλο, τις περιγραφές και την εικόνα του μαθήματος αργότερα.",
- "newCourseAccess": "Κατά κανόνα, τα μαθήματα είναι ιδιωτικά και απαιτούν έγκριση διαχειριστή για συμμετοχή. Μπορείτε να επεξεργαστείτε αυτές τις ρυθμίσεις οποτεδήποτε.",
"createCourse": "Δημιουργία μαθήματος",
"stats": "Στατιστικά",
"createGroupChat": "Δημιουργία ομαδικής συνομιλίας",
@@ -4456,7 +4453,7 @@
"playWithAI": "Παίξτε με την Τεχνητή Νοημοσύνη προς το παρόν",
"courseStartDesc": "Ο Pangea Bot είναι έτοιμος να ξεκινήσει οποιαδήποτε στιγμή!\n\n...αλλά η μάθηση είναι καλύτερη με φίλους!",
"@@locale": "el",
- "@@last_modified": "2026-01-07 14:28:33.144714",
+ "@@last_modified": "2026-02-05 10:10:14.390437",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7264,14 +7261,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9890,10 +9879,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11883,5 +11868,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Αφήσατε τη συνομιλία",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Η λήψη ξεκίνησε",
+ "webDownloadPermissionMessage": "Εάν ο περιηγητής σας μπλοκάρει τις λήψεις, παρακαλώ ενεργοποιήστε τις λήψεις για αυτόν τον ιστότοπο.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Η πρόοδος της συνεδρίας πρακτικής σας δεν θα αποθηκευτεί.",
+ "practiceGrammar": "Πρακτική γραμματικής",
+ "notEnoughToPractice": "Στείλτε περισσότερα μηνύματα για να ξεκλειδώσετε την πρακτική",
+ "constructUseCorGCDesc": "Πρακτική κατηγορίας σωστής γραμματικής",
+ "constructUseIncGCDesc": "Πρακτική κατηγορίας λανθαστής γραμματικής",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Πρακτική διόρθωσης γραμματικών λαθών",
+ "constructUseIncGEDesc": "Πρακτική λανθασμένων γραμματικών λαθών",
+ "fillInBlank": "Συμπληρώστε το κενό με τη σωστή επιλογή",
+ "learn": "Μάθετε",
+ "languageUpdated": "Η γλώσσα στόχος ενημερώθηκε!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Φωνή Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Το αίτημά σας έχει σταλεί στον διαχειριστή του μαθήματος! Θα σας επιτρέψουν να μπείτε αν το εγκρίνουν.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Έχετε έναν κωδικό πρόσκλησης ή σύνδεσμο για ένα δημόσιο μάθημα;",
+ "welcomeUser": "Καλώς ήρθατε {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτήν την συνομιλία.",
+ "publicInviteDescSpace": "Αναζητήστε χρήστες για να τους προσκαλέσετε σε αυτόν τον χώρο.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Η Pangea Chat είναι μια εφαρμογή μηνυμάτων, οπότε οι ειδοποιήσεις είναι σημαντικές!",
+ "enableNotificationsDesc": "Επιτρέψτε τις ειδοποιήσεις",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Χρησιμοποιήστε την εικόνα δραστηριότητας ως φόντο συνομιλίας",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Συνομιλία με Υποστήριξη",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Από προεπιλογή, τα μαθήματα είναι δημόσια αναζητήσιμα και απαιτούν έγκριση διαχειριστή για να συμμετάσχετε. Μπορείτε να επεξεργαστείτε αυτές τις ρυθμίσεις οποιαδήποτε στιγμή.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Ποια γλώσσα μαθαίνετε;",
+ "searchLanguagesHint": "Αναζητήστε γλώσσες στόχου",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Ερωτήσεις; Είμαστε εδώ για να βοηθήσουμε!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Κάτι πήγε στραβά και εργαζόμαστε σκληρά για να το διορθώσουμε. Έλεγξε ξανά αργότερα.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Ενεργοποίηση βοήθειας γραφής",
+ "autoIGCToolDescription": "Αυτόματα εκτελέστε τα εργαλεία Pangea Chat για να διορθώσετε τα αποσταλμένα μηνύματα στη γλώσσα στόχο.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Η ηχογράφηση απέτυχε. Παρακαλώ ελέγξτε τις άδειες ήχου σας και δοκιμάστε ξανά.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Ιδιωματισμός",
+ "grammarCopyPOSphrasalv": "Φραστικό Ρήμα",
+ "grammarCopyPOScompn": "Σύνθετο",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb
index 7cdbd28e1..a47f1a03e 100644
--- a/lib/l10n/intl_en.arb
+++ b/lib/l10n/intl_en.arb
@@ -3730,8 +3730,6 @@
"noPaymentInfo": "No payment info necessary!",
"updatePhoneOS": "You may need to update your device's OS version.",
"wordsPerMinute": "Words per minute",
- "autoIGCToolName": "Run Pangea writing assistance automatically",
- "autoIGCToolDescription": "Automatically run Pangea Chat grammar and translation writing assistance before sending my message.",
"chatCapacity": "Chat capacity",
"roomFull": "This room is already at capacity.",
"chatCapacityHasBeenChanged": "Chat capacity changed",
@@ -3811,6 +3809,9 @@
"grammarCopyPOSpropn": "Proper Noun",
"grammarCopyPOSnoun": "Noun",
"grammarCopyPOSintj": "Interjection",
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Compound",
"grammarCopyPOSx": "Other",
"grammarCopyGENDERfem": "Feminine",
"grammarCopyPERSON2": "Second Person",
@@ -4563,7 +4564,6 @@
},
"coursePlan": "Course Plan",
"editCourseLater": "You can edit template title, descriptions, and course image later.",
- "newCourseAccess": "By default, courses are private and require admin approval to join. You can edit these settings at any time.",
"createCourse": "Create course",
"stats": "Stats",
"createGroupChat": "Create group chat",
@@ -5029,19 +5029,49 @@
"noActivityRequest": "No current activity request.",
"quit": "Quit",
"congratulationsYouveCompletedPractice": "Congratulations! You've completed the practice session.",
- "noSavedActivitiesYet": "Activities will appear here once they are completed and saved.",
- "practiceActivityCompleted": "Practice activity completed",
- "changeCourse": "Change course",
- "changeCourseDesc": "Here you can change this course's course plan.",
- "introChatTitle": "Create Introductions Chat",
- "introChatDesc": "Anyone in the space can post.",
- "announcementsChatTitle": "Announcements Chat",
- "announcementsChatDesc": "Only space admin can post.",
- "inOngoingActivity": "You have an ongoing activity!",
"activitiesToUnlockTopicTitle": "Activities to Unlock Next Topic",
"activitiesToUnlockTopicDesc": "Set the number of activities to unlock the next course topic",
- "mustHave10Words" : "You must have at least 10 vocab words to practice them. Try talking to a friend or Pangea Bot to discover more!",
+ "mustHave10Words": "You must have at least 10 vocab words to practice them. Try talking to a friend or Pangea Bot to discover more!",
"botSettings": "Bot Settings",
"activitySettingsOverrideWarning": "Language and language level determined by activity plan",
- "voice": "Voice"
+ "voice": "Voice",
+ "youLeftTheChat": "🚪 You left the chat",
+ "downloadInitiated": "Download initiated",
+ "webDownloadPermissionMessage": "If your browser blocks downloads, please enable downloads for this site.",
+ "exitPractice": "Your practice session progress won't be saved.",
+ "practiceGrammar": "Practice grammar",
+ "notEnoughToPractice": "Send more messages to unlock practice",
+ "constructUseCorGCDesc": "Correct grammar category practice",
+ "constructUseIncGCDesc": "Incorrect grammar category practice",
+ "constructUseCorGEDesc": "Correct grammar error practice",
+ "constructUseIncGEDesc": "Incorrect grammar error practice",
+ "fillInBlank": "Fill in the blank with the correct choice",
+ "learn": "Learn",
+ "languageUpdated": "Target language updated!",
+ "voiceDropdownTitle": "Pangea Bot voice",
+ "knockDesc": "Your request has been sent to course admin! You'll be let in if they approve.",
+ "joinSpaceOnboardingDesc": "Do you have an invite code or link to a public course?",
+ "welcomeUser": "Welcome {user}",
+ "@welcomeUser": {
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "findCourse": "Find a course",
+ "publicInviteDescChat": "Search for users to invite them to this chat.",
+ "publicInviteDescSpace": "Search for users to invite them to this space.",
+ "enableNotificationsTitle": "Pangea Chat is a texting app so notifications are important!",
+ "enableNotificationsDesc": "Allow notifications",
+ "useActivityImageAsChatBackground": "Use activity image as chat background",
+ "chatWithSupport": "Chat with Support",
+ "newCourseAccess": "By default, courses are publicly searchable and require admin approval to join. You can edit these settings at any time.",
+ "courseLoadingError": "Something went wrong, and we're hard at work fixing it. Check again later.",
+ "onboardingLanguagesTitle": "What language are you learning?",
+ "searchLanguagesHint": "Search target languages",
+ "supportSubtitle": "Questions? We're here to help!",
+ "autoIGCToolName": "Enable writing assistance",
+ "autoIGCToolDescription": "Automatically run Pangea Chat tools to correct sent messages to target language.",
+ "emptyAudioError": "Recording failed. Please check your audio permissions and try again."
}
diff --git a/lib/l10n/intl_eo.arb b/lib/l10n/intl_eo.arb
index 3776e3eb4..2c6c6ed93 100644
--- a/lib/l10n/intl_eo.arb
+++ b/lib/l10n/intl_eo.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:28:53.013525",
+ "@@last_modified": "2026-02-05 10:10:17.896498",
"about": "Prio",
"@about": {
"type": "String",
@@ -2911,8 +2911,6 @@
"noPaymentInfo": "Ne necesas paginformoj!",
"updatePhoneOS": "Eble vi bezonas ĝisdatigi la version de la operaciumo de via aparato.",
"wordsPerMinute": "Vortoj po minuto",
- "autoIGCToolName": "Kurigu Pangea verkhelpilo aŭtomate",
- "autoIGCToolDescription": "Aŭtomate funkciigu Pangea Chat gramatikon kaj tradukadon por helpi vin verki antaŭ ol sendi vian mesaĝon.",
"tooltipInstructionsTitle": "Ne certas kio tio faras?",
"tooltipInstructionsMobileBody": "Premu kaj teni objektojn por vidi ilustraĵojn.",
"tooltipInstructionsBrowserBody": "Hovru super objektoj por vidi ilustraĵojn.",
@@ -3540,7 +3538,6 @@
"numModules": "{num} moduloj",
"coursePlan": "Kurso Plano",
"editCourseLater": "Vi povas redakti la titolon, priskribojn, kaj bildon de la kurso poste.",
- "newCourseAccess": "Ĝis nun, kursoj estas private kaj postulas administran aprobon por aliĝi. Vi povas ŝanĝi ĉi tiujn agordojn iam ajn.",
"createCourse": "Krei kurson",
"stats": "Statistikoj",
"createGroupChat": "Krei grupan babiladon",
@@ -7295,14 +7292,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9921,10 +9910,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11914,5 +11899,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Vi forlasis la konversacion",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Elŝuto iniciatita",
+ "webDownloadPermissionMessage": "Se via retumilo blokas elŝutojn, bonvolu ebligi elŝutojn por ĉi tiu retejo.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Via praktika sesio progreso ne estos konservita.",
+ "practiceGrammar": "Praktiku gramatikon",
+ "notEnoughToPractice": "Sendu pli da mesaĝoj por malŝlosi praktikon",
+ "constructUseCorGCDesc": "Praktiko de ĝusta gramatika kategorio",
+ "constructUseIncGCDesc": "Praktiko de malĝusta gramatika kategorio",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Praktiko pri ĝusta gramatika eraro",
+ "constructUseIncGEDesc": "Praktiko pri malĝusta gramatika eraro",
+ "fillInBlank": "Plenigu la malplenan lokon per la ĝusta elekto",
+ "learn": "Lerni",
+ "languageUpdated": "Celo lingvo ĝisdatigita!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voĉo de Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Via peto estis sendita al la kursa administranto! Vi estos enirita se ili aprobas.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Ĉu vi havas invitkodon aŭ ligon al publika kurso?",
+ "welcomeUser": "Bonvenon {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Serĉu uzantojn por inviti ilin al ĉi tiu konversacio.",
+ "publicInviteDescSpace": "Serĉu uzantojn por inviti ilin al ĉi tiu spaco.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat estas aplikaĵo por mesaĝado, do notifikoj estas gravaj!",
+ "enableNotificationsDesc": "Permesi notifikojn",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Uzu aktivan bildon kiel ĉatfondon",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Babili kun Subteno",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Laŭ la defaŭlto, kursoj estas publike serĉeblaj kaj postulas administran aprobon por aliĝi. Vi povas redakti ĉi tiujn agordojn iam ajn.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Kian lingvon vi lernas?",
+ "searchLanguagesHint": "Serĉu celajn lingvojn",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Demandoj? Ni ĉi tie por helpi!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Io malĝuste okazis, kaj ni diligente laboras por ripari ĝin. Kontrolu denove poste.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Ebligi skriban asistadon",
+ "autoIGCToolDescription": "Aŭtomate funkciigi Pangea Chat-ilojn por korekti senditajn mesaĝojn al la cellingvo.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Registrado malsukcesis. Bonvolu kontroli viajn aŭdajn permesojn kaj provi denove.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Kunmetita",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb
index b170a1e28..8458be45b 100644
--- a/lib/l10n/intl_es.arb
+++ b/lib/l10n/intl_es.arb
@@ -1,6 +1,6 @@
{
"@@locale": "es",
- "@@last_modified": "2026-01-07 14:23:22.356161",
+ "@@last_modified": "2026-02-05 10:09:12.250951",
"about": "Acerca de",
"@about": {
"type": "String",
@@ -4251,8 +4251,6 @@
"wordsPerMinute": "Palabras por minuto",
"roomFull": "Esta sala ya está al límite de su capacidad.",
"enterNumber": "Introduzca un valor numérico entero.",
- "autoIGCToolName": "Ejecutar automáticamente la asistencia lingüística",
- "autoIGCToolDescription": "Ejecutar automáticamente la asistencia lingüística después de escribir mensajes",
"buildTranslation": "Construye tu traducción a partir de las opciones anteriores",
"languageSettings": "Ajustes de idioma",
"selectSpaceDominantLanguage": "¿Cuál es la lengua más común de los miembros del espacio?",
@@ -5689,7 +5687,6 @@
"numModules": "{num} módulos",
"coursePlan": "Plan de curso",
"editCourseLater": "Puedes editar el título, las descripciones y la imagen del curso más tarde.",
- "newCourseAccess": "Por defecto, los cursos son privados y requieren aprobación del administrador para unirse. Puedes editar estos ajustes en cualquier momento.",
"createCourse": "Crear curso",
"stats": "Estadísticas",
"createGroupChat": "Crear chat grupal",
@@ -6035,10 +6032,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -8059,5 +8052,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Has salido del chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Descarga iniciada",
+ "webDownloadPermissionMessage": "Si tu navegador bloquea las descargas, por favor habilita las descargas para este sitio.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "El progreso de tu sesión de práctica no se guardará.",
+ "practiceGrammar": "Practicar gramática",
+ "notEnoughToPractice": "Envía más mensajes para desbloquear la práctica",
+ "constructUseCorGCDesc": "Práctica de categoría de gramática correcta",
+ "constructUseIncGCDesc": "Práctica de categoría de gramática incorrecta",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Práctica de errores gramaticales correctos",
+ "constructUseIncGEDesc": "Práctica de errores gramaticales incorrectos",
+ "fillInBlank": "Completa el espacio en blanco con la opción correcta",
+ "learn": "Aprender",
+ "languageUpdated": "¡Idioma objetivo actualizado!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voz del bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "¡Tu solicitud ha sido enviada al administrador del curso! Te dejarán entrar si la aprueban.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?",
+ "welcomeUser": "Bienvenido {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Busca usuarios para invitarlos a este chat.",
+ "publicInviteDescSpace": "Busca usuarios para invitarlos a este espacio.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "¡Pangea Chat es una aplicación de mensajería, así que las notificaciones son importantes!",
+ "enableNotificationsDesc": "Permitir notificaciones",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Usar imagen de actividad como fondo de chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chatear con Soporte",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Por defecto, los cursos son buscables públicamente y requieren aprobación del administrador para unirse. Puedes editar estas configuraciones en cualquier momento.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "¿Qué idioma estás aprendiendo?",
+ "searchLanguagesHint": "Buscar idiomas objetivo",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "¿Preguntas? ¡Estamos aquí para ayudar!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Algo salió mal y estamos trabajando arduamente para solucionarlo. Revisa de nuevo más tarde.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Habilitar asistencia de escritura",
+ "autoIGCToolDescription": "Ejecutar automáticamente las herramientas de Pangea Chat para corregir los mensajes enviados al idioma de destino.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "La grabación falló. Por favor, verifica tus permisos de audio y vuelve a intentarlo.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Modismo",
+ "grammarCopyPOSphrasalv": "Verbo Frasal",
+ "grammarCopyPOScompn": "Compuesto",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_et.arb b/lib/l10n/intl_et.arb
index 5eb052ca0..ddb897208 100644
--- a/lib/l10n/intl_et.arb
+++ b/lib/l10n/intl_et.arb
@@ -1,6 +1,6 @@
{
"@@locale": "et",
- "@@last_modified": "2026-01-07 14:25:18.173924",
+ "@@last_modified": "2026-02-05 10:09:36.127342",
"about": "Rakenduse teave",
"@about": {
"type": "String",
@@ -3811,8 +3811,6 @@
"noPaymentInfo": "Makseteadet pole vaja!",
"updatePhoneOS": "Võib olla vajalik uuendada oma seadme operatsioonisüsteemi versiooni.",
"wordsPerMinute": "Sõnad minutis",
- "autoIGCToolName": "Käivita Pangea kirjutamisabi automaatselt",
- "autoIGCToolDescription": "Käivita automaatselt Pangea vestluse grammatika- ja tõlkeabi enne sõnumi saatmist.",
"addSpaceToSpaceDescription": "Vali ruum, mida lisada vanemaks",
"chatCapacity": "Vestluse maht",
"spaceCapacity": "Ruumimaht",
@@ -6229,14 +6227,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@addSpaceToSpaceDescription": {
"type": "String",
"placeholders": {}
@@ -8815,7 +8805,6 @@
"numModules": "{num} moodulit",
"coursePlan": "Kursuse plaan",
"editCourseLater": "Saate hiljem redigeerida mallide pealkirju, kirjelduid ja kursuse pilti.",
- "newCourseAccess": "Vaikimisi on kursused privaatsed ning nõuavad administraatori kinnitust, et liituda. Saate neid seadeid igal ajal muuta.",
"createCourse": "Loo kursus",
"stats": "Statistika",
"createGroupChat": "Loo grupivestlus",
@@ -9098,10 +9087,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11096,5 +11081,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Sa lahkusid vestlusest",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Allalaadimine algatatud",
+ "webDownloadPermissionMessage": "Kui teie brauser blokeerib allalaadimisi, lubage palun selle saidi jaoks allalaadimised.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Teie harjut seansi edusamme ei salvestata.",
+ "practiceGrammar": "Harjuta grammatikat",
+ "notEnoughToPractice": "Saada rohkem sõnumeid, et harjutust avada",
+ "constructUseCorGCDesc": "Õige grammatika kategooria harjutus",
+ "constructUseIncGCDesc": "Vale grammatika kategooria harjutus",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Õige grammatika vea harjutamine",
+ "constructUseIncGEDesc": "Vale grammatika vea harjutamine",
+ "fillInBlank": "Täida tühik õige valikuga",
+ "learn": "Õpi",
+ "languageUpdated": "Sihtkeel on uuendatud!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Boti hääl",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Teie taotlus on saadetud kursuse administraatorile! Teid lastakse sisse, kui nad heaks kiidavad.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Kas sul on kutsekood või link avalikule kursusele?",
+ "welcomeUser": "Tere tulemast {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Otsi kasutajaid, et neid sellesse vestlusse kutsuda.",
+ "publicInviteDescSpace": "Otsi kasutajaid, et neid sellesse ruumi kutsuda.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat on sõnumite rakendus, seega on teavitused olulised!",
+ "enableNotificationsDesc": "Luba teavitused",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Kasuta tegevuse pilti vestluse taustana",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Vestle Toega",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Vaikimisi on kursused avalikult otsitavad ja liitumiseks on vajalik administraatori heakskiit. Sa saad neid seadeid igal ajal muuta.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Millist keelt sa õpid?",
+ "searchLanguagesHint": "Otsi sihtkeeli",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Küsimused? Me oleme siin, et aidata!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Midagi läks valesti ja me teeme kõvasti tööd, et see parandada. Kontrolli hiljem uuesti.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Luba kirjutamise abi",
+ "autoIGCToolDescription": "Käivita automaatselt Pangea Chat tööriistad, et parandada saadetud sõnumid sihtkeelde.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Salvestamine ebaõnnestus. Palun kontrollige oma heliõigusi ja proovige uuesti.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idioom",
+ "grammarCopyPOSphrasalv": "Fraasi Verb",
+ "grammarCopyPOScompn": "Kompleks",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_eu.arb b/lib/l10n/intl_eu.arb
index 80f3ffc6e..9bab7424f 100644
--- a/lib/l10n/intl_eu.arb
+++ b/lib/l10n/intl_eu.arb
@@ -1,6 +1,6 @@
{
"@@locale": "eu",
- "@@last_modified": "2026-01-07 14:25:02.696896",
+ "@@last_modified": "2026-02-05 10:09:33.401642",
"about": "Honi buruz",
"@about": {
"type": "String",
@@ -3785,8 +3785,6 @@
"noPaymentInfo": "Ez dago ordainketa informaziorik behar!",
"updatePhoneOS": "Baliteke zure gailuaren OS bertsioa eguneratu behar izatea.",
"wordsPerMinute": "Hitz minutuko",
- "autoIGCToolName": "Exekutatu Pangea idazketa laguntza automatikoki",
- "autoIGCToolDescription": "Exekutatu automatikoki Pangea Txataren gramatika eta itzulpen idazketa laguntza mezu bat bidali aurretik.",
"tooltipInstructionsTitle": "Ez da ziur zer den hori?",
"tooltipInstructionsMobileBody": "Elementuak sakatu eta eutsi tresna-txartelak ikusteko.",
"tooltipInstructionsBrowserBody": "Elementuak gainean mugitu eta ikusi tresna-txartelak.",
@@ -4414,7 +4412,6 @@
"numModules": "{num} modulua",
"coursePlan": "Ikastaro Plana",
"editCourseLater": "Eman dezakezu geroago txantiloiaren izena, deskribapenak eta ikastaroaren irudia editatzeko.",
- "newCourseAccess": "Lehenetsiz, ikastaroak pribatutasunekoak dira eta administratzailearen onespena behar dute parte hartzeko. Edozein momentutan aldatu ditzakezu ezarpen hauek.",
"createCourse": "Sortu ikastaroa",
"stats": "Datuak",
"createGroupChat": "Sortu talde txat bat",
@@ -6206,14 +6203,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8832,10 +8821,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10825,5 +10810,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Txatetik irten zara",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Deskarga hasi da",
+ "webDownloadPermissionMessage": "Zure nabigatzaileak deskargak blokeatzen baditu, mesedez, gaitza itxaroteko deskargak webgune honentzat.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Zure praktika saioaren aurrerapena ez da gorde.",
+ "practiceGrammar": "Gramatika praktikatu",
+ "notEnoughToPractice": "Praktika desblokeatzeko gehiago mezu bidali",
+ "constructUseCorGCDesc": "Gramatika kategoriako praktika zuzena",
+ "constructUseIncGCDesc": "Gramatika kategoriako praktika okerra",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Gramatika akats zuzenketa praktika",
+ "constructUseIncGEDesc": "Gramatika akats okerra praktika",
+ "fillInBlank": "Betekoa bete aukerarik egokienarekin",
+ "learn": "Ikasi",
+ "languageUpdated": "Helmuga hizkuntza eguneratua!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot ahotsa",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Zure eskaera ikastaroaren administratzaileari bidali zaio! Onartzen badute, sartuko zara.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Baduzu gonbidapen kodea edo lotura publiko baten ikastaroarentzako?",
+ "welcomeUser": "Ongi etorri {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Bilatu erabiltzaileak txat honetara gonbidatzeko.",
+ "publicInviteDescSpace": "Bilatu erabiltzaileak espazio honetara gonbidatzeko.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat mezularitza aplikazio bat da, beraz jakinarazpenak garrantzitsuak dira!",
+ "enableNotificationsDesc": "Baimendu jakinarazpenak",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Erabili jarduera irudia txat atzeko plano gisa",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Txatatu Laguntzarekin",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Lehenetsitako, ikastaroak publikoan bilatzeko modukoak dira eta administratzailearen onarpena behar dute bat egiteko. Ezarpen hauek edonon aldatu ditzakezu.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Zer hizkuntza ikasten ari zara?",
+ "searchLanguagesHint": "Bilatu helburu hizkuntzak",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Galderak? Hemen gaude laguntzeko!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Zerbait oker joan da, eta horren konponketan lan gogorra egiten ari gara. Begiratu berriro geroago.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Idazteko laguntza aktibatu",
+ "autoIGCToolDescription": "Automatikoki exekutatu Pangea Chat tresnak helburu hizkuntzara bidalitako mezuak zuzentzeko.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Grabaketa huts egin da. Mesedez, egiaztatu zure audio baimenak eta saiatu berriro.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Konposatu",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_fa.arb b/lib/l10n/intl_fa.arb
index e82c668ba..45ee57de3 100644
--- a/lib/l10n/intl_fa.arb
+++ b/lib/l10n/intl_fa.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:28:12.371773",
+ "@@last_modified": "2026-02-05 10:10:10.668033",
"repeatPassword": "تکرار رمزعبور",
"@repeatPassword": {},
"about": "درباره",
@@ -3205,8 +3205,6 @@
"noPaymentInfo": "نیازی به اطلاعات پرداخت نیست!",
"updatePhoneOS": "ممکن است نیاز باشد نسخه سیستمعامل دستگاه خود را بهروزرسانی کنید.",
"wordsPerMinute": "کلمات در دقیقه",
- "autoIGCToolName": "اجرای خودکار کمک نوشتن پنگئا",
- "autoIGCToolDescription": "به طور خودکار قبل از ارسال پیام من، کمک نگارش گرامر و ترجمه چت پنگئا را اجرا کنید.",
"tooltipInstructionsTitle": "مطمئن نیستید چه کاری انجام میدهد؟",
"tooltipInstructionsMobileBody": "برای مشاهده راهنما، آیتمها را نگه دارید.",
"tooltipInstructionsBrowserBody": "برای مشاهده راهنما، روی آیتمها هاور کنید.",
@@ -3834,7 +3832,6 @@
"numModules": "{num} ماژول",
"coursePlan": "برنامه دوره",
"editCourseLater": "میتوانید بعداً عنوان، توضیحات و تصویر دوره را ویرایش کنید.",
- "newCourseAccess": "به طور پیشفرض، دورهها خصوصی هستند و نیاز به تایید مدیر برای پیوستن دارند. شما میتوانید این تنظیمات را در هر زمان ویرایش کنید.",
"createCourse": "ایجاد دوره",
"stats": "آمار",
"createGroupChat": "ایجاد چت گروهی",
@@ -6938,14 +6935,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9564,10 +9553,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11557,5 +11542,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 شما از چت خارج شدید",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "دانلود آغاز شد",
+ "webDownloadPermissionMessage": "اگر مرورگر شما دانلودها را مسدود میکند، لطفاً دانلودها را برای این سایت فعال کنید.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "پیشرفت جلسه تمرین شما ذخیره نخواهد شد.",
+ "practiceGrammar": "تمرین گرامر",
+ "notEnoughToPractice": "پیامهای بیشتری ارسال کنید تا تمرین را باز کنید",
+ "constructUseCorGCDesc": "تمرین دسته گرامر صحیح",
+ "constructUseIncGCDesc": "تمرین دسته گرامر نادرست",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "تمرین خطای گرامری صحیح",
+ "constructUseIncGEDesc": "تمرین خطای گرامری نادرست",
+ "fillInBlank": "جای خالی را با گزینه صحیح پر کنید",
+ "learn": "یاد بگیرید",
+ "languageUpdated": "زبان هدف بهروزرسانی شد!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "صدای ربات پانژیا",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "درخواست شما به مدیر دوره ارسال شده است! اگر آنها تأیید کنند، شما وارد خواهید شد.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "آیا کد دعوت یا لینکی به یک دوره عمومی دارید؟",
+ "welcomeUser": "خوش آمدید {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "برای دعوت کاربران به این چت، جستجو کنید.",
+ "publicInviteDescSpace": "برای دعوت کاربران به این فضا، جستجو کنید.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "چت پانگهآ یک اپلیکیشن پیامرسان است، بنابراین اعلانها مهم هستند!",
+ "enableNotificationsDesc": "اجازه دادن به اعلانها",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "از تصویر فعالیت به عنوان پسزمینه چت استفاده کنید",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "چت با پشتیبانی",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "به طور پیشفرض، دورهها به صورت عمومی قابل جستجو هستند و برای پیوستن به آنها نیاز به تأیید مدیر دارند. شما میتوانید این تنظیمات را در هر زمان ویرایش کنید.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "شما در حال یادگیری چه زبانی هستید؟",
+ "searchLanguagesHint": "زبانهای هدف را جستجو کنید",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "سوالات؟ ما اینجا هستیم تا کمک کنیم!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "مشکلی پیش آمده و ما در حال تلاش برای رفع آن هستیم. بعداً دوباره بررسی کنید.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "فعالسازی کمکنویس",
+ "autoIGCToolDescription": "بهطور خودکار ابزارهای چت پانژیا را برای اصلاح پیامهای ارسالشده به زبان هدف اجرا کنید.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "ضبط صدا ناموفق بود. لطفاً مجوزهای صوتی خود را بررسی کرده و دوباره تلاش کنید.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "اصطلاح",
+ "grammarCopyPOSphrasalv": "فعل عبارتی",
+ "grammarCopyPOScompn": "ترکیب",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_fi.arb b/lib/l10n/intl_fi.arb
index 95155473a..475407fe6 100644
--- a/lib/l10n/intl_fi.arb
+++ b/lib/l10n/intl_fi.arb
@@ -3285,8 +3285,6 @@
"noPaymentInfo": "Maksutietoja ei tarvita!",
"updatePhoneOS": "Saattaa olla, että sinun täytyy päivittää laitteesi käyttöjärjestelmän versio.",
"wordsPerMinute": "Sanoja minuutissa",
- "autoIGCToolName": "Aja Pangea kirjoitusavustusta automaattisesti",
- "autoIGCToolDescription": "Aja automaattisesti Pangea Chatin kieliopin ja käännöksen kirjoitusavustusta ennen viestini lähettämistä.",
"tooltipInstructionsTitle": "Et ole varma, mitä tämä tekee?",
"tooltipInstructionsMobileBody": "Pidä painettuna kohteita nähdäksesi työkaluvihjeet.",
"tooltipInstructionsBrowserBody": "Vie hiiri kohteen päälle nähdäksesi työkaluvihjeet.",
@@ -3914,7 +3912,6 @@
"numModules": "{num} moduulia",
"coursePlan": "Kurssisuunnitelma",
"editCourseLater": "Voit muokata mallin otsikkoa, kuvauksia ja kurssikuvaa myöhemmin.",
- "newCourseAccess": "Oletuksena kurssit ovat yksityisiä ja vaativat ylläpitäjän hyväksynnän liittyäksesi. Voit muokata näitä asetuksia milloin tahansa.",
"createCourse": "Luo kurssi",
"stats": "Tilastot",
"createGroupChat": "Luo ryhmäkeskustelu",
@@ -4009,7 +4006,7 @@
"playWithAI": "Leiki tekoälyn kanssa nyt",
"courseStartDesc": "Pangea Bot on valmis milloin tahansa!\n\n...mutta oppiminen on parempaa ystävien kanssa!",
"@@locale": "fi",
- "@@last_modified": "2026-01-07 14:23:39.963677",
+ "@@last_modified": "2026-02-05 10:09:16.239112",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -6829,14 +6826,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9455,10 +9444,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11448,5 +11433,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Poistit itsesi keskustelusta",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Lataus aloitettu",
+ "webDownloadPermissionMessage": "Jos selaimesi estää lataukset, ota lataukset käyttöön tälle sivustolle.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Harjoitussession edistystäsi ei tallenneta.",
+ "practiceGrammar": "Harjoittele kielioppia",
+ "notEnoughToPractice": "Lähetä lisää viestejä avataksesi harjoituksen",
+ "constructUseCorGCDesc": "Oikean kielioppikategorian harjoittelu",
+ "constructUseIncGCDesc": "Väärän kielioppikategorian harjoittelu",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Oikean kielioppivirheen harjoittelu",
+ "constructUseIncGEDesc": "Väärän kielioppivirheen harjoittelu",
+ "fillInBlank": "Täytä tyhjä kohta oikealla valinnalla",
+ "learn": "Oppia",
+ "languageUpdated": "Kohdekieli päivitetty!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Botin ääni",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Pyyntösi on lähetetty kurssin ylläpitäjälle! Sinut päästetään sisään, jos he hyväksyvät sen.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Onko sinulla kutsukoodia tai linkkiä julkiseen kurssiin?",
+ "welcomeUser": "Tervetuloa {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Etsi käyttäjiä kutsuaksesi heidät tähän keskusteluun.",
+ "publicInviteDescSpace": "Etsi käyttäjiä kutsuaksesi heidät tähän tilaan.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat on viestintäsovellus, joten ilmoitukset ovat tärkeitä!",
+ "enableNotificationsDesc": "Salli ilmoitukset",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Käytä aktiviteettikuvaa chat-taustana",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chattaa tuen kanssa",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Oletusarvoisesti kurssit ovat julkisesti haettavissa ja vaativat ylläpitäjän hyväksynnän liittymiseen. Voit muokata näitä asetuksia milloin tahansa.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Mitä kieltä opit?",
+ "searchLanguagesHint": "Etsi kohdekieliä",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Kysymyksiä? Olemme täällä auttamassa!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Jotain meni pieleen, ja teemme kovasti töitä sen korjaamiseksi. Tarkista myöhemmin uudelleen.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Ota käyttöön kirjoitusapu",
+ "autoIGCToolDescription": "Suorita automaattisesti Pangea Chat -työkaluja korjataksesi lähetetyt viestit kohdekielelle.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Äänityksen tallentaminen epäonnistui. Tarkista äänioikeutesi ja yritä uudelleen.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idioomi",
+ "grammarCopyPOSphrasalv": "Fraasiverbi",
+ "grammarCopyPOScompn": "Yhdistelmä",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_fil.arb b/lib/l10n/intl_fil.arb
index f70476b4e..d1c809870 100644
--- a/lib/l10n/intl_fil.arb
+++ b/lib/l10n/intl_fil.arb
@@ -2051,8 +2051,6 @@
"noPaymentInfo": "Hindi kailangan ng impormasyon sa pagbabayad!",
"updatePhoneOS": "Maaaring kailangan mong i-update ang bersyon ng OS ng iyong device.",
"wordsPerMinute": "Mga salita kada minuto",
- "autoIGCToolName": "Awtomatikong patakbuhin ang Pangea writing assistance",
- "autoIGCToolDescription": "Awtomatikong patakbuhin ang Pangea Chat grammar at translation writing assistance bago ipadala ang aking mensahe.",
"tooltipInstructionsTitle": "Hindi sigurado kung ano ang ginagawa niyan?",
"tooltipInstructionsMobileBody": "Pindutin at hawakan ang mga item upang makita ang mga tooltip.",
"tooltipInstructionsBrowserBody": "I-hover ang cursor sa mga item upang makita ang mga tooltip.",
@@ -2680,7 +2678,6 @@
"numModules": "{num} mga module",
"coursePlan": "Plano ng Kurso",
"editCourseLater": "Maaari mong i-edit ang pamagat ng template, mga paglalarawan, at larawan ng kurso sa ibang pagkakataon.",
- "newCourseAccess": "Sa default, ang mga kurso ay pribado at nangangailangan ng pag-apruba ng admin upang makasali. Maaari mong i-edit ang mga setting na ito anumang oras.",
"createCourse": "Lumikha ng kurso",
"stats": "Mga Estadistika",
"createGroupChat": "Lumikha ng pangkat na usapan",
@@ -2787,7 +2784,7 @@
"selectAll": "Piliin lahat",
"deselectAll": "Huwag piliin lahat",
"@@locale": "fil",
- "@@last_modified": "2026-01-07 14:26:57.612933",
+ "@@last_modified": "2026-02-05 10:09:53.428313",
"@setCustomPermissionLevel": {
"type": "String",
"placeholders": {}
@@ -7199,14 +7196,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9825,10 +9814,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11801,5 +11786,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Umalis ka sa chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Nagsimula ang pag-download",
+ "webDownloadPermissionMessage": "Kung hinaharang ng iyong browser ang mga pag-download, mangyaring paganahin ang mga pag-download para sa site na ito.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Hindi mase-save ang iyong progreso sa sesyon ng pagsasanay.",
+ "practiceGrammar": "Magsanay ng gramatika",
+ "notEnoughToPractice": "Magpadala ng higit pang mga mensahe upang i-unlock ang pagsasanay",
+ "constructUseCorGCDesc": "Pagsasanay sa tamang kategorya ng gramatika",
+ "constructUseIncGCDesc": "Pagsasanay sa maling kategorya ng gramatika",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Pagsasanay sa tamang pagkakamali sa gramatika",
+ "constructUseIncGEDesc": "Pagsasanay sa maling pagkakamali sa gramatika",
+ "fillInBlank": "Punan ang blangko ng tamang pagpipilian",
+ "learn": "Matuto",
+ "languageUpdated": "Na-update ang target na wika!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Boses ng Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Ang iyong kahilingan ay naipadala sa admin ng kurso! Papayagan ka nilang pumasok kung sila ay mag-aapruba.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Mayroon ka bang invite code o link sa isang pampublikong kurso?",
+ "welcomeUser": "Maligayang pagdating {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Maghanap ng mga gumagamit upang imbitahan sila sa chat na ito.",
+ "publicInviteDescSpace": "Maghanap ng mga gumagamit upang imbitahan sila sa espasyong ito.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Ang Pangea Chat ay isang texting app kaya't mahalaga ang mga notification!",
+ "enableNotificationsDesc": "Pahintulutan ang mga notification",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Gamitin ang larawan ng aktibidad bilang background ng chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Makipag-chat sa Suporta",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Sa default, ang mga kurso ay pampublikong searchable at nangangailangan ng pag-apruba ng admin upang sumali. Maaari mong i-edit ang mga setting na ito anumang oras.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Anong wika ang iyong pinag-aaralan?",
+ "searchLanguagesHint": "Maghanap ng mga target na wika",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "May mga tanong? Nandito kami para tumulong!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "May nangyaring mali, at abala kami sa pag-aayos nito. Suriin muli mamaya.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Paganahin ang tulong sa pagsusulat",
+ "autoIGCToolDescription": "Awtomatikong patakbuhin ang mga tool ng Pangea Chat upang ituwid ang mga ipinadalang mensahe sa target na wika.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Nabigo ang pag-record. Pakisuri ang iyong mga pahintulot sa audio at subukan muli.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idyoma",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Pinagsama",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb
index 39234cc32..30464e9eb 100644
--- a/lib/l10n/intl_fr.arb
+++ b/lib/l10n/intl_fr.arb
@@ -1,6 +1,6 @@
{
"@@locale": "fr",
- "@@last_modified": "2026-01-07 14:29:28.310920",
+ "@@last_modified": "2026-02-05 10:10:24.987990",
"about": "À propos",
"@about": {
"type": "String",
@@ -3615,8 +3615,6 @@
"noPaymentInfo": "Aucune information de paiement nécessaire !",
"updatePhoneOS": "Vous devrez peut-être mettre à jour la version du système d'exploitation de votre appareil.",
"wordsPerMinute": "Mots par minute",
- "autoIGCToolName": "Exécuter automatiquement l'assistance à l'écriture Pangea",
- "autoIGCToolDescription": "Exécuter automatiquement l'assistance à la grammaire et à la traduction de Pangea Chat avant d'envoyer mon message.",
"tooltipInstructionsTitle": "Vous ne savez pas ce que cela fait ?",
"tooltipInstructionsMobileBody": "Appuyez longuement sur les éléments pour voir les infobulles.",
"tooltipInstructionsBrowserBody": "Survolez les éléments pour voir les infobulles.",
@@ -4244,7 +4242,6 @@
"numModules": "{num} modules",
"coursePlan": "Plan de cours",
"editCourseLater": "Vous pouvez modifier le titre du modèle, les descriptions et l'image du cours plus tard.",
- "newCourseAccess": "Par défaut, les cours sont privés et nécessitent l'approbation de l'administrateur pour rejoindre. Vous pouvez modifier ces paramètres à tout moment.",
"createCourse": "Créer un cours",
"stats": "Statistiques",
"createGroupChat": "Créer un chat de groupe",
@@ -6530,14 +6527,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9156,10 +9145,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11149,5 +11134,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Vous avez quitté le chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Téléchargement initié",
+ "webDownloadPermissionMessage": "Si votre navigateur bloque les téléchargements, veuillez activer les téléchargements pour ce site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Les progrès de votre session de pratique ne seront pas enregistrés.",
+ "practiceGrammar": "Pratiquer la grammaire",
+ "notEnoughToPractice": "Envoyez plus de messages pour débloquer la pratique",
+ "constructUseCorGCDesc": "Pratique de la catégorie de grammaire correcte",
+ "constructUseIncGCDesc": "Pratique de la catégorie de grammaire incorrecte",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Pratique de correction des erreurs grammaticales",
+ "constructUseIncGEDesc": "Pratique des erreurs grammaticales incorrectes",
+ "fillInBlank": "Remplissez le blanc avec le choix correct",
+ "learn": "Apprendre",
+ "languageUpdated": "Langue cible mise à jour !",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voix du bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Votre demande a été envoyée à l'administrateur du cours ! Vous serez admis s'ils approuvent.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Avez-vous un code d'invitation ou un lien vers un cours public ?",
+ "welcomeUser": "Bienvenue {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Recherchez des utilisateurs pour les inviter à ce chat.",
+ "publicInviteDescSpace": "Recherchez des utilisateurs pour les inviter à cet espace.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat est une application de messagerie, donc les notifications sont importantes !",
+ "enableNotificationsDesc": "Autoriser les notifications",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Utiliser l'image d'activité comme arrière-plan de chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Discuter avec le support",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Par défaut, les cours sont recherchables publiquement et nécessitent l'approbation d'un administrateur pour rejoindre. Vous pouvez modifier ces paramètres à tout moment.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Quelle langue apprenez-vous ?",
+ "searchLanguagesHint": "Recherchez des langues cibles",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Des questions ? Nous sommes là pour vous aider !",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Quelque chose a mal tourné, et nous travaillons dur pour le réparer. Vérifiez à nouveau plus tard.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Activer l'assistance à l'écriture",
+ "autoIGCToolDescription": "Exécutez automatiquement les outils de Pangea Chat pour corriger les messages envoyés dans la langue cible.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "L'enregistrement a échoué. Veuillez vérifier vos autorisations audio et réessayer.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verbe à particule",
+ "grammarCopyPOScompn": "Composé",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ga.arb b/lib/l10n/intl_ga.arb
index 0fbd10091..ac8fb9bf9 100644
--- a/lib/l10n/intl_ga.arb
+++ b/lib/l10n/intl_ga.arb
@@ -3793,8 +3793,6 @@
"noPaymentInfo": "Níl aon eolas íocaíochta de dhíth!",
"updatePhoneOS": "D'fhéadfadh go mbeadh ort do leagan OS a nuashonrú ar do ghléas.",
"wordsPerMinute": "Focail in aghaidh na nóimead",
- "autoIGCToolName": "Rith cabhair scríbhneoireachta Pangea go huathoibríoch",
- "autoIGCToolDescription": "Rith uathoibríoch cabhair gramadaí agus aistriúcháin Pangea Chat sula seolfaidh mé mo theachtaireacht.",
"tooltipInstructionsTitle": "Níl tú cinnte cad a dhéanann sé sin?",
"tooltipInstructionsMobileBody": "Bain agus coinnigh ar na míreanna chun treoracha a fheiceáil.",
"tooltipInstructionsBrowserBody": "Cliceáil agus coinnigh ar na míreanna chun treoracha a fheiceáil.",
@@ -4422,7 +4420,6 @@
"numModules": "{num} modúl",
"coursePlan": "Plean Cúrsa",
"editCourseLater": "Is féidir leat teideal an phlean, cur síos, agus íomhá an chúrsa a chur in eagar níos déanaí.",
- "newCourseAccess": "De réir réamhshocraithe, tá cúrsaí príobháideach agus iarrtar cead riarthóra chun páirt a ghlacadh. Is féidir leat na socruithe seo a chur in eagar ag am ar bith.",
"createCourse": "Cruthaigh cúrsa",
"stats": "Staitisticí",
"createGroupChat": "Cruthaigh comhrá grúpa",
@@ -4517,7 +4514,7 @@
"playWithAI": "Imir le AI faoi láthair",
"courseStartDesc": "Tá Bot Pangea réidh chun dul am ar bith!\n\n...ach is fearr foghlaim le cairde!",
"@@locale": "ga",
- "@@last_modified": "2026-01-07 14:29:21.686769",
+ "@@last_modified": "2026-02-05 10:10:23.901035",
"@customReaction": {
"type": "String",
"placeholders": {}
@@ -6204,14 +6201,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8830,10 +8819,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10823,5 +10808,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 D'fhág tú an comhrá",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Tosaíodh an íoslódáil",
+ "webDownloadPermissionMessage": "Más blocann do bhrabhsálaí íoslódálacha, le do thoil, gníomhachtaigh íoslódálacha don suíomh seo.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Ní shábhálfar do dhul chun cinn sa seisiún cleachtaidh.",
+ "practiceGrammar": "Cleachtaigh gramadach",
+ "notEnoughToPractice": "Seol níos mó teachtaireachtaí chun cleachtadh a dhíghlasáil",
+ "constructUseCorGCDesc": "Cleachtadh catagóir gramadaí ceart",
+ "constructUseIncGCDesc": "Cleachtadh catagóir gramadaí mícheart",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Cleachtadh ar earráidí gramadaí ceart",
+ "constructUseIncGEDesc": "Cleachtadh ar earráidí gramadaí míchruinn",
+ "fillInBlank": "Líon isteach an folt le rogha cheart",
+ "learn": "Foghlaim",
+ "languageUpdated": "Teanga sprioc nuashonraithe!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "guth Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Tá do hiarratas curtha chuig an riarachán cúrsa! Cuirfear isteach thú má cheadaíonn siad é.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "An bhfuil cód cuireadh nó nasc agat do chúrsa poiblí?",
+ "welcomeUser": "Fáilte {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Cuardaigh úsáideoirí le cuireadh a thabhairt dóibh chuig an gcomhrá seo.",
+ "publicInviteDescSpace": "Cuardaigh úsáideoirí le cuireadh a thabhairt dóibh chuig an spás seo.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Is aip téacsála í Pangea Chat mar sin tá fógraí tábhachtach!",
+ "enableNotificationsDesc": "Cuir fógraí ar cead",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Úsáid íomhá gníomhaíochta mar chúlra comhrá",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Comhrá le Tacaíocht",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "De réir réamhshocraithe, tá cúrsaí inrochtana go poiblí agus éilíonn siad cead ó riarachán chun páirt a ghlacadh. Is féidir leat na socruithe seo a chur in eagar ag am ar bith.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Cén teanga atá á foghlaim agat?",
+ "searchLanguagesHint": "Cuardaigh teangacha sprioc",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Ceisteanna? Táimid anseo chun cabhrú!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Tharla rud éigin mícheart, agus táimid ag obair go dian chun é a shocrú. Seiceáil arís níos déanaí.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Cuir ar chumas cúnamh scríbhneoireachta",
+ "autoIGCToolDescription": "Rith uathoibríoch uirlisí Pangea Chat chun teachtaireachtaí a sheoladh a cheartú go teanga sprioc.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Theip ar an taifeadadh. Seiceáil do cheadanna gutháin le do thoil agus déan iarracht arís.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Frása",
+ "grammarCopyPOSphrasalv": "Gníomhhacht Phrásúil",
+ "grammarCopyPOScompn": "Comhoibriú",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_gl.arb b/lib/l10n/intl_gl.arb
index 75e246ad8..44263a6c5 100644
--- a/lib/l10n/intl_gl.arb
+++ b/lib/l10n/intl_gl.arb
@@ -1,6 +1,6 @@
{
"@@locale": "gl",
- "@@last_modified": "2026-01-07 14:23:32.582541",
+ "@@last_modified": "2026-02-05 10:09:14.434046",
"about": "Acerca de",
"@about": {
"type": "String",
@@ -3786,8 +3786,6 @@
"noPaymentInfo": "Non é necesaria información de pagamento!",
"updatePhoneOS": "Pode que necesites actualizar a versión do sistema operativo do teu dispositivo.",
"wordsPerMinute": "Palabras por minuto",
- "autoIGCToolName": "Executar a asistencia de escritura Pangea automaticamente",
- "autoIGCToolDescription": "Executar automaticamente a asistencia de gramática e tradución de Pangea Chat antes de enviar a miña mensaxe.",
"tooltipInstructionsTitle": "Non estás seguro de para que serve iso?",
"tooltipInstructionsMobileBody": "Prema e mantén os elementos para ver as pistas.",
"tooltipInstructionsBrowserBody": "Pasa o rato por riba dos elementos para ver as pistas.",
@@ -4415,7 +4413,6 @@
"numModules": "{num} módulos",
"coursePlan": "Plan de curso",
"editCourseLater": "Podes editar o título da plantilla, as descricións e a imaxe do curso máis tarde.",
- "newCourseAccess": "Por defecto, os cursos son privados e requiren a aprobación do administrador para unirse. Podes editar estas configuracións en calquera momento.",
"createCourse": "Crear curso",
"stats": "Estadísticas",
"createGroupChat": "Crear chat de grupo",
@@ -6203,14 +6200,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8829,10 +8818,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10822,5 +10807,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Saíches do chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Descarga iniciada",
+ "webDownloadPermissionMessage": "Se o teu navegador bloquea descargas, por favor, habilita as descargas para este sitio.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "O progreso da túa sesión de práctica non se gardará.",
+ "practiceGrammar": "Practicar gramática",
+ "notEnoughToPractice": "Envía máis mensaxes para desbloquear a práctica",
+ "constructUseCorGCDesc": "Práctica da categoría de gramática correcta",
+ "constructUseIncGCDesc": "Práctica da categoría de gramática incorrecta",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Práctica de erro gramatical correcto",
+ "constructUseIncGEDesc": "Práctica de erro gramatical incorrecto",
+ "fillInBlank": "Completa o espazo en branco coa opción correcta",
+ "learn": "Aprender",
+ "languageUpdated": "Idioma de destino actualizado!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voz do bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "A túa solicitude foi enviada ao administrador do curso! Serás admitido se a aproban.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Tes un código de invitación ou un enlace a un curso público?",
+ "welcomeUser": "Benvido {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Busca usuarios para convidalos a este chat.",
+ "publicInviteDescSpace": "Busca usuarios para convidalos a este espazo.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat é unha aplicación de mensaxería, así que as notificacións son importantes!",
+ "enableNotificationsDesc": "Permitir notificacións",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Usa a imaxe de actividade como fondo de chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chatear co Soporte",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Por defecto, os cursos son buscables públicamente e requiren aprobación do administrador para unirse. Podes editar estas configuracións en calquera momento.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Que idioma estás aprendendo?",
+ "searchLanguagesHint": "Busca idiomas de destino",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "¿Preguntas? Estamos aquí para axudar!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Algo saíu mal e estamos traballando duro para solucionalo. Comproba de novo máis tarde.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Activar a asistencia de escritura",
+ "autoIGCToolDescription": "Executar automaticamente as ferramentas de Pangea Chat para corrixir os mensaxes enviados á lingua de destino.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "A gravación fallou. Por favor, verifica os teus permisos de audio e intenta de novo.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verbo Frasal",
+ "grammarCopyPOScompn": "Composto",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_he.arb b/lib/l10n/intl_he.arb
index 06e395102..63f1dae8f 100644
--- a/lib/l10n/intl_he.arb
+++ b/lib/l10n/intl_he.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:24:41.103817",
+ "@@last_modified": "2026-02-05 10:09:28.796405",
"about": "אודות",
"@about": {
"type": "String",
@@ -2371,8 +2371,6 @@
"noPaymentInfo": "אין צורך במידע תשלום!",
"updatePhoneOS": "ייתכן שתצטרך לעדכן את גרסת מערכת ההפעלה של המכשיר שלך.",
"wordsPerMinute": "מילים בדקה",
- "autoIGCToolName": "הרץ אוטומטית את עזר הכתיבה של פאנגיאה",
- "autoIGCToolDescription": "הרץ אוטומטית את עזר הכתיבה של דקדוק ותרגום של פאנגיאה לפני שליחת ההודעה שלי.",
"tooltipInstructionsTitle": "לא בטוח מה זה עושה?",
"tooltipInstructionsMobileBody": "החזק והחלק על פריטים כדי לצפות בטיפים.",
"tooltipInstructionsBrowserBody": "החלק מעל פריטים כדי לצפות בטיפים.",
@@ -3000,7 +2998,6 @@
"numModules": "{num} מודולים",
"coursePlan": "תכנית קורס",
"editCourseLater": "אתה יכול לערוך את כותרת התבנית, תיאורים ותמונת הקורס מאוחר יותר.",
- "newCourseAccess": "ברירת מחדל, קורסים הם פרטיים ודורשים אישור מנהל להצטרפות. תוכל לערוך הגדרות אלה בכל עת.",
"createCourse": "צור קורס",
"stats": "סטטיסטיקות",
"createGroupChat": "צור שיחת קבוצתית",
@@ -7255,14 +7252,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9881,10 +9870,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11874,5 +11859,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 עזבת את הצ'אט",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "ההורדה החלה",
+ "webDownloadPermissionMessage": "אם הדפדפן שלך חוסם הורדות, אנא אפשר הורדות לאתר זה.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "ההתקדמות שלך במפגש האימון לא תישמר.",
+ "practiceGrammar": "אימון דקדוק",
+ "notEnoughToPractice": "שלח יותר הודעות כדי לפתוח אימון",
+ "constructUseCorGCDesc": "אימון בקטגוריית דקדוק נכון",
+ "constructUseIncGCDesc": "אימון בקטגוריית דקדוק לא נכון",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "תרגול תיקון שגיאות דקדוק",
+ "constructUseIncGEDesc": "תרגול שגיאות דקדוק לא נכונות",
+ "fillInBlank": "מלא את החסר עם הבחירה הנכונה",
+ "learn": "ללמוד",
+ "languageUpdated": "שפת היעד עודכנה!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "קול של פנגיאה בוט",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "הבקשה שלך נשלחה למנהל הקורס! תורשה להיכנס אם הם יאשרו.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "האם יש לך קוד הזמנה או קישור לקורס ציבורי?",
+ "welcomeUser": "ברוך הבא {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "חפש משתמשים כדי להזמין אותם לצ'אט הזה.",
+ "publicInviteDescSpace": "חפש משתמשים כדי להזמין אותם למקום הזה.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat היא אפליקציית הודעות, ולכן התראות הן חשובות!",
+ "enableNotificationsDesc": "אפשר התראות",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "השתמש בתמונה של הפעילות כרקע לצ'אט",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "צ'אט עם תמיכה",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "ברירת מחדל, קורסים ניתנים לחיפוש ציבורי ודורשים אישור מנהל כדי להצטרף. אתה יכול לערוך את ההגדרות הללו בכל עת.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "איזו שפה אתה לומד?",
+ "searchLanguagesHint": "חפש שפות יעד",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "שאלות? אנחנו כאן כדי לעזור!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "משהו השתבש, ואנחנו עובדים קשה על תיקון זה. בדוק שוב מאוחר יותר.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "אפשר סיוע בכתיבה",
+ "autoIGCToolDescription": "הרץ אוטומטית את כלי Pangea Chat כדי לתקן הודעות שנשלחו לשפה היעד.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "הקלטה נכשלה. אנא בדוק את הרשאות האודיו שלך ונסה שוב.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "ביטוי",
+ "grammarCopyPOSphrasalv": "פועל פיזי",
+ "grammarCopyPOScompn": "מורכב",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_hi.arb b/lib/l10n/intl_hi.arb
index f0a80fd80..d7758c041 100644
--- a/lib/l10n/intl_hi.arb
+++ b/lib/l10n/intl_hi.arb
@@ -3759,8 +3759,6 @@
"noPaymentInfo": "कोई भुगतान जानकारी आवश्यक नहीं!",
"updatePhoneOS": "आपको अपने डिवाइस का OS संस्करण अपडेट करने की आवश्यकता हो सकती है।",
"wordsPerMinute": "मिनट में शब्द",
- "autoIGCToolName": "स्वचालित रूप से पैंजिया लेखन सहायता चलाएँ",
- "autoIGCToolDescription": "मेरे संदेश भेजने से पहले स्वचालित रूप से पैंजिया चैट व्याकरण और अनुवाद लेखन सहायता चलाएँ।",
"tooltipInstructionsTitle": "क्या यह करता है, इसके बारे में सुनिश्चित नहीं?",
"tooltipInstructionsMobileBody": "आइटम को दबाकर रखें और टूलटिप देखने के लिए होवर करें।",
"tooltipInstructionsBrowserBody": "आइटम पर होवर करें और टूलटिप देखने के लिए होवर करें।",
@@ -4388,7 +4386,6 @@
"numModules": "{num} मॉड्यूल",
"coursePlan": "कोर्स योजना",
"editCourseLater": "आप बाद में टेम्पलेट का शीर्षक, विवरण और कोर्स छवि संपादित कर सकते हैं।",
- "newCourseAccess": "डिफ़ॉल्ट रूप से, कोर्स निजी होते हैं और शामिल होने के लिए व्यवस्थापक अनुमोदन की आवश्यकता होती है। आप इन सेटिंग्स को कभी भी संपादित कर सकते हैं।",
"createCourse": "कोर्स बनाएं",
"stats": "आंकड़े",
"createGroupChat": "समूह चैट बनाएं",
@@ -4483,7 +4480,7 @@
"playWithAI": "अभी के लिए एआई के साथ खेलें",
"courseStartDesc": "पैंजिया बॉट कभी भी जाने के लिए तैयार है!\n\n...लेकिन दोस्तों के साथ सीखना बेहतर है!",
"@@locale": "hi",
- "@@last_modified": "2026-01-07 14:28:46.662693",
+ "@@last_modified": "2026-02-05 10:10:16.696075",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7291,14 +7288,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9917,10 +9906,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11910,5 +11895,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 आप चैट छोड़ चुके हैं",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "डाउनलोड शुरू किया गया",
+ "webDownloadPermissionMessage": "यदि आपका ब्राउज़र डाउनलोड को ब्लॉक करता है, तो कृपया इस साइट के लिए डाउनलोड सक्षम करें।",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "आपकी प्रैक्टिस सत्र की प्रगति सहेजी नहीं जाएगी।",
+ "practiceGrammar": "व्याकरण का अभ्यास करें",
+ "notEnoughToPractice": "अभ्यास अनलॉक करने के लिए अधिक संदेश भेजें",
+ "constructUseCorGCDesc": "सही व्याकरण श्रेणी का अभ्यास",
+ "constructUseIncGCDesc": "गलत व्याकरण श्रेणी का अभ्यास",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "व्याकरण त्रुटि सुधार अभ्यास",
+ "constructUseIncGEDesc": "व्याकरण त्रुटि गलत अभ्यास",
+ "fillInBlank": "सही विकल्प के साथ रिक्त स्थान भरें",
+ "learn": "सीखें",
+ "languageUpdated": "लक्षित भाषा अपडेट की गई!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "पैंगिया बॉट की आवाज़",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "आपका अनुरोध पाठ्यक्रम प्रशासन को भेज दिया गया है! यदि वे स्वीकृत करते हैं, तो आपको अंदर जाने दिया जाएगा।",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "क्या आपके पास एक आमंत्रण कोड या सार्वजनिक पाठ्यक्रम के लिए लिंक है?",
+ "welcomeUser": "स्वागत है {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "इस चैट में आमंत्रित करने के लिए उपयोगकर्ताओं की खोज करें।",
+ "publicInviteDescSpace": "इस स्थान में आमंत्रित करने के लिए उपयोगकर्ताओं की खोज करें।",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea चैट एक टेक्स्टिंग ऐप है इसलिए सूचनाएँ महत्वपूर्ण हैं!",
+ "enableNotificationsDesc": "सूचनाएँ अनुमति दें",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "चैट पृष्ठभूमि के रूप में गतिविधि छवि का उपयोग करें",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "सहायता से चैट करें",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "डिफ़ॉल्ट रूप से, पाठ्यक्रम सार्वजनिक रूप से खोजे जा सकते हैं और शामिल होने के लिए प्रशासक की स्वीकृति की आवश्यकता होती है। आप किसी भी समय इन सेटिंग्स को संपादित कर सकते हैं।",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "आप कौन सी भाषा सीख रहे हैं?",
+ "searchLanguagesHint": "लक्षित भाषाएँ खोजें",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "प्रश्न? हम आपकी मदद के लिए यहाँ हैं!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "कुछ गलत हो गया है, और हम इसे ठीक करने में कड़ी मेहनत कर रहे हैं। बाद में फिर से जांचें।",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "लेखन सहायता सक्षम करें",
+ "autoIGCToolDescription": "लक्षित भाषा में भेजे गए संदेशों को सही करने के लिए स्वचालित रूप से Pangea चैट उपकरण चलाएँ।",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "रिकॉर्डिंग विफल हो गई। कृपया अपनी ऑडियो अनुमति की जांच करें और फिर से प्रयास करें।",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "मुहावरा",
+ "grammarCopyPOSphrasalv": "फ्रेज़ल वर्ब",
+ "grammarCopyPOScompn": "संयुक्त",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_hr.arb b/lib/l10n/intl_hr.arb
index a60833d29..fa3e6d919 100644
--- a/lib/l10n/intl_hr.arb
+++ b/lib/l10n/intl_hr.arb
@@ -1,6 +1,6 @@
{
"@@locale": "hr",
- "@@last_modified": "2026-01-07 14:24:33.836325",
+ "@@last_modified": "2026-02-05 10:09:27.459987",
"about": "Informacije",
"@about": {
"type": "String",
@@ -3541,8 +3541,6 @@
"noPaymentInfo": "Nije potrebna informacija o plaćanju!",
"updatePhoneOS": "Možda ćete morati ažurirati verziju OS-a na svom uređaju.",
"wordsPerMinute": "Riječi po minuti",
- "autoIGCToolName": "Automatski pokreni pomoć za pisanje Pangea",
- "autoIGCToolDescription": "Automatski pokreni pomoć za gramatiku i prijevod Pangea Chat prije slanja moje poruke.",
"tooltipInstructionsTitle": "Niste sigurni što to radi?",
"tooltipInstructionsMobileBody": "Dugim pritiskom na stavke prikazuju se alati za pomoć.",
"tooltipInstructionsBrowserBody": "Pomičite mišem preko stavki za prikazivanje saveta.",
@@ -4170,7 +4168,6 @@
"numModules": "{num} modula",
"coursePlan": "Plan tečaja",
"editCourseLater": "Možete kasnije urediti naslov predloška, opise i sliku tečaja.",
- "newCourseAccess": "Prema zadanim postavkama, tečajevi su privatni i zahtijevaju odobrenje administratora za pridruživanje. Možete ove postavke urediti u bilo koje vrijeme.",
"createCourse": "Stvori tečaj",
"stats": "Statistike",
"createGroupChat": "Stvori grupni razgovor",
@@ -6578,14 +6575,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9204,10 +9193,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11197,5 +11182,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Napustili ste chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Preuzimanje pokrenuto",
+ "webDownloadPermissionMessage": "Ako vaš preglednik blokira preuzimanja, molimo omogućite preuzimanja za ovu stranicu.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Vaš napredak u vježbi neće biti spremljen.",
+ "practiceGrammar": "Vježbajte gramatiku",
+ "notEnoughToPractice": "Pošaljite više poruka da otključate vježbu",
+ "constructUseCorGCDesc": "Vježba ispravne gramatičke kategorije",
+ "constructUseIncGCDesc": "Vježba neispravne gramatičke kategorije",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Prakticiranje ispravne gramatičke greške",
+ "constructUseIncGEDesc": "Prakticiranje pogrešne gramatičke greške",
+ "fillInBlank": "Ispunite prazno mjesto s ispravnim izborom",
+ "learn": "Učite",
+ "languageUpdated": "Ciljani jezik ažuriran!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot glas",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Vaš zahtjev je poslan administratoru tečaja! Bit ćete primljeni ako odobre.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Imate li pozivni kod ili link za javni tečaj?",
+ "welcomeUser": "Dobrodošli {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Pretražite korisnike kako biste ih pozvali u ovaj chat.",
+ "publicInviteDescSpace": "Pretražite korisnike kako biste ih pozvali u ovaj prostor.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat je aplikacija za slanje poruka, stoga su obavijesti važne!",
+ "enableNotificationsDesc": "Dopusti obavijesti",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Koristi sliku aktivnosti kao pozadinu chata",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Razgovarajte s podrškom",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Prema zadanim postavkama, tečajevi su javno pretraživi i zahtijevaju odobrenje administratora za pridruživanje. Ove postavke možete urediti u bilo kojem trenutku.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Koji jezik učite?",
+ "searchLanguagesHint": "Pretraži ciljne jezike",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Imate pitanja? Tu smo da pomognemo!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Nešto je pošlo po zlu i marljivo radimo na rješavanju problema. Provjerite ponovo kasnije.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Omogući pomoć pri pisanju",
+ "autoIGCToolDescription": "Automatski pokreni Pangea Chat alate za ispravljanje poslanih poruka na ciljni jezik.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Snimanje nije uspjelo. Provjerite svoja audio dopuštenja i pokušajte ponovo.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasalni Glagol",
+ "grammarCopyPOScompn": "Složenica",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_hu.arb b/lib/l10n/intl_hu.arb
index 3683bcbe0..7cdb4f9a1 100644
--- a/lib/l10n/intl_hu.arb
+++ b/lib/l10n/intl_hu.arb
@@ -1,6 +1,6 @@
{
"@@locale": "hu",
- "@@last_modified": "2026-01-07 14:23:56.196432",
+ "@@last_modified": "2026-02-05 10:09:19.675804",
"about": "Névjegy",
"@about": {
"type": "String",
@@ -3786,8 +3786,6 @@
"noPaymentInfo": "Nincs szükség fizetési adatokra!",
"updatePhoneOS": "Előfordulhat, hogy frissítenie kell az eszköz operációs rendszerét.",
"wordsPerMinute": "Szavak száma percenként",
- "autoIGCToolName": "A Pangea írássegéd automatikus futtatása",
- "autoIGCToolDescription": "Automatikusan futtassa a Pangea Chat nyelvtani és fordítási írássegédet az üzenetem küldése előtt.",
"tooltipInstructionsTitle": "Nem biztos benne, mit csinál ez?",
"tooltipInstructionsMobileBody": "Hosszan nyomja meg az elemeket a súgók megtekintéséhez.",
"tooltipInstructionsBrowserBody": "Húzza az egérrel az elemek fölé a súgók megtekintéséhez.",
@@ -4415,7 +4413,6 @@
"numModules": "{num} modul",
"coursePlan": "Tanfolyamterv",
"editCourseLater": "Később szerkesztheti a sablon címet, leírásokat és a tanfolyam képét.",
- "newCourseAccess": "Alapértelmezés szerint a tanfolyamok privátak, és admin jóváhagyását igénylik a csatlakozáshoz. Ezeket a beállításokat bármikor szerkesztheti.",
"createCourse": "Tanfolyam létrehozása",
"stats": "Statisztika",
"createGroupChat": "Csoportos beszélgetés létrehozása",
@@ -6207,14 +6204,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8833,10 +8822,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10826,5 +10811,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Elhagytad a csevegést",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Letöltés megkezdődött",
+ "webDownloadPermissionMessage": "Ha a böngésződ blokkolja a letöltéseket, kérlek engedélyezd a letöltéseket ezen az oldalon.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "A gyakorlási session előrehaladása nem lesz mentve.",
+ "practiceGrammar": "Nyelvtan gyakorlása",
+ "notEnoughToPractice": "Több üzenetet kell küldeni a gyakorlás feloldásához",
+ "constructUseCorGCDesc": "Helyes nyelvtani kategória gyakorlása",
+ "constructUseIncGCDesc": "Helytelen nyelvtani kategória gyakorlása",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Helyes nyelvtani hiba gyakorlás",
+ "constructUseIncGEDesc": "Helytelen nyelvtani hiba gyakorlás",
+ "fillInBlank": "Töltsd ki a hiányzó részt a helyes választással",
+ "learn": "Tanulj",
+ "languageUpdated": "Cél nyelv frissítve!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot hang",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "A kérésed el lett küldve a kurzus adminisztrátorának! Be fogsz engedni, ha jóváhagyják.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Van meghívó kódod vagy linked egy nyilvános kurzushoz?",
+ "welcomeUser": "Üdvözöljük {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Keresd meg a felhasználókat, hogy meghívd őket erre a csevegésre.",
+ "publicInviteDescSpace": "Keresd meg a felhasználókat, hogy meghívd őket erre a térre.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "A Pangea Chat egy üzenetküldő alkalmazás, így a értesítések fontosak!",
+ "enableNotificationsDesc": "Értesítések engedélyezése",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Használja az aktivitás képet csevegési háttérként",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Csevegés a Támogatással",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Alapértelmezés szerint a kurzusok nyilvánosan kereshetők, és adminisztrátori jóváhagyás szükséges a csatlakozáshoz. Ezeket a beállításokat bármikor módosíthatja.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Milyen nyelvet tanulsz?",
+ "searchLanguagesHint": "Keresd a célnyelveket",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Kérdése van? Itt vagyunk, hogy segítsünk!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Valami hiba történt, és keményen dolgozunk a javításon. Kérlek, nézd meg később.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Írássegítő engedélyezése",
+ "autoIGCToolDescription": "Automatikusan futtassa a Pangea Chat eszközöket a küldött üzenetek célnyelvre történő javításához.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "A felvétel nem sikerült. Kérjük, ellenőrizze az audio engedélyeit, és próbálja újra.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idióma",
+ "grammarCopyPOSphrasalv": "Frazális ige",
+ "grammarCopyPOScompn": "Összetett",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ia.arb b/lib/l10n/intl_ia.arb
index 40ca83dba..3321a7c8a 100644
--- a/lib/l10n/intl_ia.arb
+++ b/lib/l10n/intl_ia.arb
@@ -1234,8 +1234,6 @@
"noPaymentInfo": "Nulle information de pagamento necessari!",
"updatePhoneOS": "Es possibile que tu necesse actualisar le versione del sistema operative de tu dispositivo.",
"wordsPerMinute": "Palabras per minuto",
- "autoIGCToolName": "Execute automaticemente le assistance de scriber Pangea",
- "autoIGCToolDescription": "Execute automaticamente le assistance de grammatica e traduction de Pangea Chat ante de inviar mi message.",
"tooltipInstructionsTitle": "Nescite que illo face?",
"tooltipInstructionsMobileBody": "Pressa e tene le items pro vider le tooltip.",
"tooltipInstructionsBrowserBody": "Survole le items pro vider le tooltip.",
@@ -1863,7 +1861,6 @@
"numModules": "{num} modulo(s)",
"coursePlan": "Plan de curso",
"editCourseLater": "Tu pote modificar le titulo del curso, descriptiones, e imagine del curso postea.",
- "newCourseAccess": "A default, le cursos es private e require approbation del administrator pro aderir. Tu pote modificar iste configurationes a omne momento.",
"createCourse": "Create un curso",
"stats": "Statisticas",
"createGroupChat": "Create un chat de grupo",
@@ -1958,7 +1955,7 @@
"playWithAI": "Joca con le IA pro ora",
"courseStartDesc": "Pangea Bot es preste a comenzar a qualunque momento!\n\n...ma apprender es melior con amicos!",
"@@locale": "ia",
- "@@last_modified": "2026-01-07 14:24:47.787013",
+ "@@last_modified": "2026-02-05 10:09:29.962506",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7284,14 +7281,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9910,10 +9899,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11903,5 +11888,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Tu lëvë chatin",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download inisiati",
+ "webDownloadPermissionMessage": "Se o teu navegador bloqueia descargas, por favor habilita descargas para este site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Progresul sesiunii tale de practică nu va fi salvat.",
+ "practiceGrammar": "Exersează gramatică",
+ "notEnoughToPractice": "Trimite mai multe mesaje pentru a debloca practica",
+ "constructUseCorGCDesc": "Practică categoria de gramatică corectă",
+ "constructUseIncGCDesc": "Practică categoria de gramatică incorectă",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Praktiko de ĝusta gramatika eraro",
+ "constructUseIncGEDesc": "Praktiko de malĝusta gramatika eraro",
+ "fillInBlank": "Plenigu la malplenan lokon kun la ĝusta elekto",
+ "learn": "Lerni",
+ "languageUpdated": "Celo lingvo ĝisdatigita!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voix du bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Tua peticio est missa ad administratorem cursuum! Te admittent si illi approbant.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?",
+ "welcomeUser": "Bienvenido {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Cerca per utenti per invitarli a questa chat.",
+ "publicInviteDescSpace": "Cerca per utenti per invitarli a questo spazio.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat est un application de messagerie donc les notifications sont importantes !",
+ "enableNotificationsDesc": "Autoriser les notifications",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Usa l'immagine dell'attività come sfondo della chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Fala com o Suporte",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Per default, kursusene er offentlig søkbare og krever admin-godkjenning for å bli med. Du kan redigere disse innstillingene når som helst.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Kia lingvo vi lernas?",
+ "searchLanguagesHint": "Serĉu celajn lingvojn",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Kwestyon? Nou la pou ede!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "N'ayen a fau, e n'ayen a t'awen a t'awen a t'awen. T'awen a t'awen a t'awen.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Abilita l'assistenza alla scrittura",
+ "autoIGCToolDescription": "Esegui automaticamente gli strumenti di Pangea Chat per correggere i messaggi inviati nella lingua target.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Fala falhou. Por favor, verifique suas permissões de áudio e tente novamente.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verbo Phrasal",
+ "grammarCopyPOScompn": "Compuesto",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_id.arb b/lib/l10n/intl_id.arb
index 64cf24c41..cb48fe670 100644
--- a/lib/l10n/intl_id.arb
+++ b/lib/l10n/intl_id.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:24:04.023956",
+ "@@last_modified": "2026-02-05 10:09:21.065759",
"setAsCanonicalAlias": "Atur sebagai alias utama",
"@setAsCanonicalAlias": {
"type": "String",
@@ -3787,8 +3787,6 @@
"noPaymentInfo": "Tidak perlu info pembayaran!",
"updatePhoneOS": "Anda mungkin perlu memperbarui versi OS perangkat Anda.",
"wordsPerMinute": "Kata per menit",
- "autoIGCToolName": "Jalankan otomatis bantuan penulisan Pangea",
- "autoIGCToolDescription": "Jalankan otomatis bantuan penulisan tata bahasa dan terjemahan Chat Pangea sebelum mengirim pesan saya.",
"tooltipInstructionsTitle": "Tidak yakin apa itu?",
"tooltipInstructionsMobileBody": "Tekan dan tahan item untuk melihat tooltip.",
"tooltipInstructionsBrowserBody": "Arahkan kursor ke item untuk melihat tooltip.",
@@ -4416,7 +4414,6 @@
"numModules": "{num} modul",
"coursePlan": "Rencana Kursus",
"editCourseLater": "Anda dapat mengedit judul template, deskripsi, dan gambar kursus nanti.",
- "newCourseAccess": "Secara default, kursus bersifat pribadi dan memerlukan persetujuan admin untuk bergabung. Anda dapat mengedit pengaturan ini kapan saja.",
"createCourse": "Buat kursus",
"stats": "Statistik",
"createGroupChat": "Buat obrolan grup",
@@ -6197,14 +6194,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8823,10 +8812,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10816,5 +10801,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Anda meninggalkan obrolan",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Unduhan dimulai",
+ "webDownloadPermissionMessage": "Jika browser Anda memblokir unduhan, silakan aktifkan unduhan untuk situs ini.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Kemajuan sesi latihan Anda tidak akan disimpan.",
+ "practiceGrammar": "Latihan tata bahasa",
+ "notEnoughToPractice": "Kirim lebih banyak pesan untuk membuka latihan",
+ "constructUseCorGCDesc": "Latihan kategori tata bahasa yang benar",
+ "constructUseIncGCDesc": "Latihan kategori tata bahasa yang salah",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Latihan kesalahan tata bahasa yang benar",
+ "constructUseIncGEDesc": "Latihan kesalahan tata bahasa yang salah",
+ "fillInBlank": "Isi kekosongan dengan pilihan yang benar",
+ "learn": "Belajar",
+ "languageUpdated": "Bahasa target diperbarui!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Suara Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Permintaan Anda telah dikirim ke admin kursus! Anda akan diizinkan masuk jika mereka menyetujuinya.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Apakah Anda memiliki kode undangan atau tautan ke kursus publik?",
+ "welcomeUser": "Selamat datang {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Cari pengguna untuk mengundang mereka ke obrolan ini.",
+ "publicInviteDescSpace": "Cari pengguna untuk mengundang mereka ke ruang ini.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat adalah aplikasi pesan, jadi notifikasi itu penting!",
+ "enableNotificationsDesc": "Izinkan notifikasi",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Gunakan gambar aktivitas sebagai latar belakang obrolan",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat dengan Dukungan",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Secara default, kursus dapat dicari secara publik dan memerlukan persetujuan admin untuk bergabung. Anda dapat mengedit pengaturan ini kapan saja.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Bahasa apa yang Anda pelajari?",
+ "searchLanguagesHint": "Cari bahasa target",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Pertanyaan? Kami di sini untuk membantu!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Ada yang tidak beres, dan kami sedang bekerja keras untuk memperbaikinya. Periksa lagi nanti.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Aktifkan bantuan penulisan",
+ "autoIGCToolDescription": "Secara otomatis menjalankan alat Pangea Chat untuk memperbaiki pesan yang dikirim ke bahasa target.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Perekaman gagal. Silakan periksa izin audio Anda dan coba lagi.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Kata Kerja Phrasal",
+ "grammarCopyPOScompn": "Kombinasi",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ie.arb b/lib/l10n/intl_ie.arb
index af33f1f37..d473493ff 100644
--- a/lib/l10n/intl_ie.arb
+++ b/lib/l10n/intl_ie.arb
@@ -3648,8 +3648,6 @@
"noPaymentInfo": "Nulle information de pagamento necessari!",
"updatePhoneOS": "Tu pote haber de actualizar la version del sistema operative de tu dispositivo",
"wordsPerMinute": "Palabras per minutu",
- "autoIGCToolName": "Execute automaticamente le assistent de scriber Pangea",
- "autoIGCToolDescription": "Execute automaticamente le grammatica e traduction del chat Pangea ante de inviar mi message",
"tooltipInstructionsTitle": "Non es secur de que isto face?",
"tooltipInstructionsMobileBody": "Pressa e tenea items pro vider le tooltip.",
"tooltipInstructionsBrowserBody": "Survole items pro vider le tooltip.",
@@ -4277,7 +4275,6 @@
"numModules": "{num} modulo(s)",
"coursePlan": "Plan de cors",
"editCourseLater": "Tu pote editar le titulo, descriptiones e imagine del cors plus tarde.",
- "newCourseAccess": "Per defaut, los cors es privat e require approbation del admin pro aderir. Tu pote modificar iste parametros a qualunque momento.",
"createCourse": "Createar cors",
"stats": "Statisticas",
"createGroupChat": "Createar chat de gruppo",
@@ -4372,7 +4369,7 @@
"playWithAI": "Joca con AI pro ora",
"courseStartDesc": "Pangea Bot es preste a partir a qualunque momento!\n\n...ma apprender es melior con amicos!",
"@@locale": "ie",
- "@@last_modified": "2026-01-07 14:24:25.335678",
+ "@@last_modified": "2026-02-05 10:09:26.195275",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7180,14 +7177,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9806,10 +9795,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11799,5 +11784,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 T'adhair tú an comhrá",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Íoslódáil tosaíodh",
+ "webDownloadPermissionMessage": "Más blocann do bhrabhsálaí íoslódálacha, le do thoil, gníomhachtaigh íoslódálacha don suíomh seo.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Tua progressio sessionis exercitationis non servabitur.",
+ "practiceGrammar": "Exercitia grammatica",
+ "notEnoughToPractice": "Mitte plura nuntia ad exercitium aperiendum",
+ "constructUseCorGCDesc": "Correcta grammaticae categoriae exercitium",
+ "constructUseIncGCDesc": "Incorrecta grammaticae categoriae exercitium",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Praktika korrekta gramatikfehler",
+ "constructUseIncGEDesc": "Praktika inkorrekt gramatikfehler",
+ "fillInBlank": "Fyll i tomrummet med det korrekta valget",
+ "learn": "Lær",
+ "languageUpdated": "Mål sprog opdateret!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot guth",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Tua iarrtas a chaidh a chur gu rianachd a' chùrsa! Thèid thu a leigeil a-steach ma tha iad a' freagairt gu math.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "¿Tienes un código de invitación o un enlace a un curso público?",
+ "welcomeUser": "Bienvenido {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Cuir fiosrúcháin ar úsáideoirí chun iad a gcuir isteach sa chomhrá seo.",
+ "publicInviteDescSpace": "Cuir fiosrúcháin ar úsáideoirí chun iad a gcuir isteach sa spás seo.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat é uma aplicação de mensagens, por isso as notificações são importantes!",
+ "enableNotificationsDesc": "Permitir notificações",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Úsáid íomhá gníomhaíochta mar chúlra comhrá",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat le Tacaíocht",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "De fao, is eardh a tha ann an coircean a tha ri fhaighinn gu poblach agus tha feum air aontachadh bho rianadair gus freagairt. Faodaidh tu na suidheachaidhean sin a dheasachadh aig àm sam bith.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Cén teanga atá á foghlaim agat?",
+ "searchLanguagesHint": "Cuardaigh teangacha sprioc",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Ceisteanna? Táimid anseo chun cabhrú!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Níl aon rud ag dul i gceart, agus táimid ag obair go dian chun é a shocrú. Seiceáil arís níos déanaí.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Aktivoi kirjoitusavustaja",
+ "autoIGCToolDescription": "Suorita automaattisesti Pangea Chat -työkaluja korjataksesi lähetetyt viestit kohdekielelle.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Faillí an taifeadadh. Seiceáil do cheadanna gutháin agus déan iarracht arís.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Composé",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb
index ca4d47338..066e58676 100644
--- a/lib/l10n/intl_it.arb
+++ b/lib/l10n/intl_it.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:25:44.793254",
+ "@@last_modified": "2026-02-05 10:09:41.124551",
"about": "Informazioni",
"@about": {
"type": "String",
@@ -3764,8 +3764,6 @@
"noPaymentInfo": "Nessuna informazione di pagamento necessaria!",
"updatePhoneOS": "Potresti dover aggiornare la versione del sistema operativo del tuo dispositivo.",
"wordsPerMinute": "Parole al minuto",
- "autoIGCToolName": "Esegui automaticamente l'assistenza alla scrittura Pangea",
- "autoIGCToolDescription": "Esegui automaticamente l'assistenza alla grammatica e alla traduzione di Pangea Chat prima di inviare il mio messaggio.",
"tooltipInstructionsTitle": "Non sei sicuro di cosa faccia?",
"tooltipInstructionsMobileBody": "Tieni premuti gli elementi per visualizzare i suggerimenti.",
"tooltipInstructionsBrowserBody": "Passa il mouse sugli elementi per visualizzare i suggerimenti.",
@@ -4393,7 +4391,6 @@
"numModules": "{num} moduli",
"coursePlan": "Piano del corso",
"editCourseLater": "Puoi modificare in seguito il titolo del corso, le descrizioni e l'immagine del corso.",
- "newCourseAccess": "Per impostazione predefinita, i corsi sono privati e richiedono l'approvazione dell'amministratore per parteciparvi. Puoi modificare queste impostazioni in qualsiasi momento.",
"createCourse": "Crea corso",
"stats": "Statistiche",
"createGroupChat": "Crea chat di gruppo",
@@ -6209,14 +6206,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8835,10 +8824,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10828,5 +10813,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Hai lasciato la chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download avviato",
+ "webDownloadPermissionMessage": "Se il tuo browser blocca i download, abilita i download per questo sito.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "I progressi della tua sessione di pratica non verranno salvati.",
+ "practiceGrammar": "Pratica la grammatica",
+ "notEnoughToPractice": "Invia più messaggi per sbloccare la pratica",
+ "constructUseCorGCDesc": "Pratica della categoria grammaticale corretta",
+ "constructUseIncGCDesc": "Pratica della categoria grammaticale scorretta",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Pratica degli errori grammaticali corretti",
+ "constructUseIncGEDesc": "Pratica degli errori grammaticali scorretti",
+ "fillInBlank": "Compila lo spazio vuoto con la scelta corretta",
+ "learn": "Impara",
+ "languageUpdated": "Lingua target aggiornata!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voce del bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "La tua richiesta è stata inviata all'amministratore del corso! Sarai ammesso se approvano.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Hai un codice di invito o un link per un corso pubblico?",
+ "welcomeUser": "Benvenuto {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Cerca utenti per invitarli a questa chat.",
+ "publicInviteDescSpace": "Cerca utenti per invitarli a questo spazio.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat è un'app di messaggistica, quindi le notifiche sono importanti!",
+ "enableNotificationsDesc": "Consenti notifiche",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Usa l'immagine dell'attività come sfondo della chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chatta con il supporto",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Per impostazione predefinita, i corsi sono ricercabili pubblicamente e richiedono l'approvazione dell'amministratore per unirsi. Puoi modificare queste impostazioni in qualsiasi momento.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Quale lingua stai imparando?",
+ "searchLanguagesHint": "Cerca lingue target",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Domande? Siamo qui per aiutarti!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Qualcosa è andato storto e stiamo lavorando duramente per risolverlo. Controlla di nuovo più tardi.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Abilita assistenza alla scrittura",
+ "autoIGCToolDescription": "Esegui automaticamente gli strumenti di Pangea Chat per correggere i messaggi inviati nella lingua target.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Registrazione fallita. Controlla le tue autorizzazioni audio e riprova.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verbo Frazionale",
+ "grammarCopyPOScompn": "Composto",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb
index b2c854382..360e48bfa 100644
--- a/lib/l10n/intl_ja.arb
+++ b/lib/l10n/intl_ja.arb
@@ -1,6 +1,6 @@
{
"@@locale": "ja",
- "@@last_modified": "2026-01-07 14:28:40.112996",
+ "@@last_modified": "2026-02-05 10:10:15.587333",
"about": "このアプリについて",
"@about": {
"type": "String",
@@ -3128,8 +3128,6 @@
"noPaymentInfo": "支払い情報は不要です!",
"updatePhoneOS": "デバイスのOSバージョンを更新する必要があるかもしれません。",
"wordsPerMinute": "1分あたりの単語数",
- "autoIGCToolName": "Pangeaのライティング支援を自動的に実行",
- "autoIGCToolDescription": "メッセージ送信前にPangeaチャットの文法と翻訳のライティング支援を自動的に実行します。",
"tooltipInstructionsTitle": "それは何のためか分からない?",
"tooltipInstructionsMobileBody": "アイテムを長押ししてツールチップを表示します。",
"tooltipInstructionsBrowserBody": "アイテムにカーソルを合わせてツールチップを表示します。",
@@ -3757,7 +3755,6 @@
"numModules": "{num} モジュール",
"coursePlan": "コースプラン",
"editCourseLater": "テンプレートのタイトル、説明、コース画像は後で編集できます。",
- "newCourseAccess": "デフォルトでは、コースはプライベートで、参加には管理者の承認が必要です。これらの設定はいつでも編集できます。",
"createCourse": "コースを作成",
"stats": "統計",
"createGroupChat": "グループチャットを作成",
@@ -6996,14 +6993,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9622,10 +9611,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11615,5 +11600,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 チャットを退出しました",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "ダウンロードが開始されました",
+ "webDownloadPermissionMessage": "ブラウザがダウンロードをブロックしている場合は、このサイトのダウンロードを有効にしてください。",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "あなたの練習セッションの進捗は保存されません。",
+ "practiceGrammar": "文法を練習する",
+ "notEnoughToPractice": "練習を解除するにはもっとメッセージを送信してください",
+ "constructUseCorGCDesc": "正しい文法カテゴリの練習",
+ "constructUseIncGCDesc": "間違った文法カテゴリの練習",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "文法エラーの正しい練習",
+ "constructUseIncGEDesc": "文法エラーの不正確な練習",
+ "fillInBlank": "正しい選択肢で空欄を埋めてください",
+ "learn": "学ぶ",
+ "languageUpdated": "ターゲット言語が更新されました!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "パンゲアボットの声",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "あなたのリクエストはコース管理者に送信されました! 彼らが承認すれば、入ることができます。",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "招待コードまたは公開コースへのリンクはありますか?",
+ "welcomeUser": "ようこそ {user} さん",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "このチャットに招待するユーザーを検索します。",
+ "publicInviteDescSpace": "このスペースに招待するユーザーを検索します。",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chatはメッセージアプリなので、通知は重要です!",
+ "enableNotificationsDesc": "通知を許可する",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "アクティビティ画像をチャットの背景として使用",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "サポートとチャット",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "デフォルトでは、コースは公開検索可能で、参加するには管理者の承認が必要です。これらの設定はいつでも編集できます。",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "どの言語を学んでいますか?",
+ "searchLanguagesHint": "ターゲット言語を検索",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "質問がありますか?私たちはお手伝いします!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "何かがうまくいかなかったため、私たちは修正作業に取り組んでいます。後で再度確認してください。",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "ライティングアシスタントを有効にする",
+ "autoIGCToolDescription": "送信されたメッセージをターゲット言語に修正するために、Pangea Chatツールを自動的に実行します。",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "録音に失敗しました。オーディオの権限を確認して、再試行してください。",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "イディオム",
+ "grammarCopyPOSphrasalv": "句動詞",
+ "grammarCopyPOScompn": "複合語",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ka.arb b/lib/l10n/intl_ka.arb
index 9b6f413e3..78003f70e 100644
--- a/lib/l10n/intl_ka.arb
+++ b/lib/l10n/intl_ka.arb
@@ -1870,8 +1870,6 @@
"noPaymentInfo": "გადახდის ინფორმაცია საჭირო არაა!",
"updatePhoneOS": "შესაძლოა დაგჭირდეთ თქვენი მოწყობილობის ოპერაციული სისტემის განახლება.",
"wordsPerMinute": "სიტყვები წუთში",
- "autoIGCToolName": "ავტომატურად ჩართეთ პანგეა წერის დახმარება",
- "autoIGCToolDescription": "ავტომატურად ჩართეთ პანგეა ჩატის გრამატიკა და თარგმნის დახმარება ჩემი შეტყობინების გაგზავნამდე.",
"tooltipInstructionsTitle": "არ იცით რა აკეთებს ეს?",
"tooltipInstructionsMobileBody": "დაჭერით და შეინახეთ ინსტრუმენტების სანახავად.",
"tooltipInstructionsBrowserBody": "მოძრაობით მერყეობთ ნივთებზე ინსტრუმენტების სანახავად.",
@@ -2499,7 +2497,6 @@
"numModules": "{num} მოდული",
"coursePlan": "კურსის გეგმა",
"editCourseLater": "შეგიძლიათ მოგვიანებით შეცვალოთ ტემპლეტის სათაური, აღწერები და კურსის სურათი.",
- "newCourseAccess": "ჩაშენებული წესით, კურსები პირადია და საჭიროებს ადმინისტრატორის დამტკიცებას შესასვლელად. შეგიძლიათ ნებისმიერ დროს შეცვალოთ ეს პარამეტრები.",
"createCourse": "შექმენით კურსი",
"stats": "სტატისტიკა",
"createGroupChat": "შექმენით ჯგუფური ჩათი",
@@ -2594,7 +2591,7 @@
"playWithAI": "ამ დროისთვის ითამაშეთ AI-თან",
"courseStartDesc": "Pangea Bot მზადაა ნებისმიერ დროს გასასვლელად!\n\n...მაგრამ სწავლა უკეთესია მეგობრებთან ერთად!",
"@@locale": "ka",
- "@@last_modified": "2026-01-07 14:29:07.353656",
+ "@@last_modified": "2026-02-05 10:10:20.523925",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7236,14 +7233,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9862,10 +9851,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11855,5 +11840,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 თქვენ დატოვეთ ჩატი",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "ჩამოტვირთვა დაწყებულია",
+ "webDownloadPermissionMessage": "თუ თქვენი ბრაუზერი ბლოკავს ჩამოტვირთვებს, გთხოვთ გააქტიუროთ ჩამოტვირთვები ამ ვებსაიტისთვის.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "თქვენი პრაქტიკის სესიის პროგრესი არ დაიშლება.",
+ "practiceGrammar": "პრაქტიკა გრამატიკა",
+ "notEnoughToPractice": "პრაქტიკის გასახსნელად მეტი შეტყობინება გამოაგზავნეთ",
+ "constructUseCorGCDesc": "სწორი გრამატიკული კატეგორიის პრაქტიკა",
+ "constructUseIncGCDesc": "არასწორი გრამატიკული კატეგორიის პრაქტიკა",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "მართებული გრამატიკული შეცდომების პრაქტიკა",
+ "constructUseIncGEDesc": "არასწორი გრამატიკული შეცდომების პრაქტიკა",
+ "fillInBlank": "შეავსეთ ცარიელი ადგილი სწორი არჩევანით",
+ "learn": "სწავლა",
+ "languageUpdated": "მიზნობრივი ენა განახლებულია!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "პანჯეა ბოტის ხმა",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "თქვენი მოთხოვნა გაგზავნილია კურსის ადმინისტრატორთან! თქვენ შეგიშვებენ, თუ ისინი დაამტკიცებენ.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "გაქვთ თუ არა მოწვევის კოდი ან ბმული საჯარო კურსზე?",
+ "welcomeUser": "კეთილი იყოს თქვენი მობრძანება {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "მომხმარებლების ძიება, რათა მათ ამ ჩატში მოიწვიოთ.",
+ "publicInviteDescSpace": "მომხმარებლების ძიება, რათა მათ ამ სივრცეში მოიწვიოთ.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat არის ტექსტური აპლიკაცია, ამიტომ შეტყობინებები მნიშვნელოვანია!",
+ "enableNotificationsDesc": "შეტყობინებების დაშვება",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "გამოიყენეთ აქტივობის სურათი ჩეთის ფონად",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "ჩატი მხარდაჭერასთან",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "ნაგულისხმევად, კურსები საჯაროდ საძიებელია და საჭიროებს ადმინისტრატორის დამტკიცებას გაწვდვისთვის. შეგიძლიათ ამ პარამეტრების რედაქტირება ნებისმიერ დროს.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "რომელი ენა სწავლობთ?",
+ "searchLanguagesHint": "ძებნა მიზნობრივი ენების",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "კითხვები? ჩვენ აქ ვართ, რომ დაგეხმაროთ!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "რამე არასწორად მოხდა, და ჩვენ აქტიურად ვმუშაობთ ამის გამოსასწორებლად. შეამოწმეთ მოგვიანებით.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "წერის დახმარების ჩართვა",
+ "autoIGCToolDescription": "ავტომატურად გაწვდეთ Pangea Chat ინსტრუმენტები გაგზავნილი შეტყობინებების მიზნობრივი ენაზე გასასწორებლად.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "ჩაწერა ვერ მოხერხდა. გთხოვთ, შეამოწმოთ თქვენი აუდიო უფლებები და სცადოთ კიდევ ერთხელ.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "იდიომი",
+ "grammarCopyPOSphrasalv": "ფრაზული ზმნა",
+ "grammarCopyPOScompn": "კომპლექსური",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb
index dc4bac579..63b504369 100644
--- a/lib/l10n/intl_ko.arb
+++ b/lib/l10n/intl_ko.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:23:16.118299",
+ "@@last_modified": "2026-02-05 10:09:10.325473",
"about": "소개",
"@about": {
"type": "String",
@@ -3721,8 +3721,6 @@
"noPaymentInfo": "결제 정보가 필요 없습니다!",
"updatePhoneOS": "기기의 OS 버전을 업데이트해야 할 수 있습니다.",
"wordsPerMinute": "분당 단어 수",
- "autoIGCToolName": "판게아 작문 지원 자동 실행",
- "autoIGCToolDescription": "메시지를 보내기 전에 판게아 채팅 문법 및 번역 작문 지원을 자동으로 실행합니다.",
"tooltipInstructionsTitle": "이게 무슨 기능인지 잘 모르겠나요?",
"tooltipInstructionsMobileBody": "항목을 길게 눌러 툴팁을 볼 수 있습니다.",
"tooltipInstructionsBrowserBody": "항목 위에 마우스를 올려 툴팁을 볼 수 있습니다.",
@@ -4350,7 +4348,6 @@
"numModules": "{num}개 모듈",
"coursePlan": "과정 계획",
"editCourseLater": "템플릿 제목, 설명, 과정 이미지는 나중에 편집할 수 있습니다.",
- "newCourseAccess": "기본적으로 과정은 비공개이며 관리자 승인 후 참여할 수 있습니다. 언제든지 이 설정을 변경할 수 있습니다.",
"createCourse": "과정 생성",
"stats": "통계",
"createGroupChat": "단체 채팅 만들기",
@@ -6314,14 +6311,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8940,10 +8929,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10933,5 +10918,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 채팅을 나갔습니다",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "다운로드가 시작되었습니다",
+ "webDownloadPermissionMessage": "브라우저가 다운로드를 차단하는 경우, 이 사이트에 대한 다운로드를 활성화해 주세요.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "연습 세션 진행 상황이 저장되지 않습니다.",
+ "practiceGrammar": "문법 연습",
+ "notEnoughToPractice": "연습을 잠금 해제하려면 더 많은 메시지를 보내세요.",
+ "constructUseCorGCDesc": "올바른 문법 카테고리 연습",
+ "constructUseIncGCDesc": "잘못된 문법 카테고리 연습",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "문법 오류 수정 연습",
+ "constructUseIncGEDesc": "문법 오류 비정상 연습",
+ "fillInBlank": "올바른 선택으로 빈칸을 채우세요",
+ "learn": "배우다",
+ "languageUpdated": "목표 언어가 업데이트되었습니다!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "판게아 봇 음성",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "귀하의 요청이 과정 관리자에게 전송되었습니다! 그들이 승인하면 들어갈 수 있습니다.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "공개 과정에 대한 초대 코드나 링크가 있습니까?",
+ "welcomeUser": "환영합니다 {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "이 채팅에 초대할 사용자를 검색하세요.",
+ "publicInviteDescSpace": "이 공간에 초대할 사용자를 검색하세요.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat은 문자 메시지 앱이므로 알림이 중요합니다!",
+ "enableNotificationsDesc": "알림 허용",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "활동 이미지를 채팅 배경으로 사용",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "지원팀과 채팅하기",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "기본적으로 과정은 공개적으로 검색 가능하며 참여하려면 관리자 승인이 필요합니다. 언제든지 이러한 설정을 수정할 수 있습니다.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "어떤 언어를 배우고 있나요?",
+ "searchLanguagesHint": "목표 언어 검색",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "질문이 있으신가요? 저희가 도와드리겠습니다!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "문제가 발생했으며, 우리는 이를 해결하기 위해 열심히 작업하고 있습니다. 나중에 다시 확인해 주세요.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "쓰기 지원 활성화",
+ "autoIGCToolDescription": "전송된 메시지를 목표 언어로 수정하기 위해 Pangea Chat 도구를 자동으로 실행합니다.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "녹음에 실패했습니다. 오디오 권한을 확인하고 다시 시도해 주세요.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "관용구",
+ "grammarCopyPOSphrasalv": "구동사",
+ "grammarCopyPOScompn": "복합어",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_lt.arb b/lib/l10n/intl_lt.arb
index fd8eadc44..8a69923f6 100644
--- a/lib/l10n/intl_lt.arb
+++ b/lib/l10n/intl_lt.arb
@@ -3137,8 +3137,6 @@
"noPaymentInfo": "Apmokėjimo informacija nereikalinga!",
"updatePhoneOS": "Gali būti, kad reikės atnaujinti įrenginio operacinės sistemos versiją.",
"wordsPerMinute": "Žodžių per minutę",
- "autoIGCToolName": "Automatiškai paleisti Pangea rašymo pagalbą",
- "autoIGCToolDescription": "Automatiškai paleisti Pangea pokalbio gramatikos ir vertimo rašymo pagalbą prieš išsiunčiant žinutę.",
"tooltipInstructionsTitle": "Nesate tikri, ką tai daro?",
"tooltipInstructionsMobileBody": "Paspauskite ir palaikykite elementus, kad pamatytumėte įrankių patarimus.",
"tooltipInstructionsBrowserBody": "Užveskite pelės žymeklį ant elementų, kad pamatytumėte įrankių patarimus.",
@@ -3766,7 +3764,6 @@
"numModules": "{num} moduliai",
"coursePlan": "Kurso planas",
"editCourseLater": "Vėliau galite redaguoti šablono pavadinimą, aprašymus ir kurso vaizdą.",
- "newCourseAccess": "Pagal numatytuosius nustatymus kursai yra privatūs ir reikalauja administratoriaus patvirtinimo prisijungiant. Šiuos nustatymus galite redaguoti bet kuriuo metu.",
"createCourse": "Sukurti kursą",
"stats": "Statistika",
"createGroupChat": "Sukurti grupinį pokalbį",
@@ -3861,7 +3858,7 @@
"playWithAI": "Žaiskite su dirbtiniu intelektu dabar",
"courseStartDesc": "Pangea botas pasiruošęs bet kada pradėti!\n\n...bet mokymasis yra geresnis su draugais!",
"@@locale": "lt",
- "@@last_modified": "2026-01-07 14:27:30.418081",
+ "@@last_modified": "2026-02-05 10:10:01.069181",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7011,14 +7008,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9637,10 +9626,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11630,5 +11615,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Jūs palikote pokalbį",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Atsisiuntimas pradėtas",
+ "webDownloadPermissionMessage": "Jei jūsų naršyklė blokuoja atsisiuntimus, prašome įgalinti atsisiuntimus šiam tinklalapiui.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Jūsų praktikos sesijos pažanga nebus išsaugota.",
+ "practiceGrammar": "Praktikuoti gramatiką",
+ "notEnoughToPractice": "Siųskite daugiau žinučių, kad atrakintumėte praktiką",
+ "constructUseCorGCDesc": "Teisingos gramatikos kategorijos praktika",
+ "constructUseIncGCDesc": "Neteisingos gramatikos kategorijos praktika",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Teisingos gramatikos klaidų praktika",
+ "constructUseIncGEDesc": "Neteisingos gramatikos klaidų praktika",
+ "fillInBlank": "Užpildykite tuščią vietą teisingu pasirinkimu",
+ "learn": "Mokytis",
+ "languageUpdated": "Tikslo kalba atnaujinta!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot balsas",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Jūsų prašymas buvo išsiųstas kurso administratoriui! Būsite įleistas, jei jie patvirtins.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Ar turite kvietimo kodą arba nuorodą į viešą kursą?",
+ "welcomeUser": "Sveiki atvykę, {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Ieškokite vartotojų, kad juos pakviestumėte į šį pokalbį.",
+ "publicInviteDescSpace": "Ieškokite vartotojų, kad juos pakviestumėte į šią erdvę.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat yra žinučių programa, todėl pranešimai yra svarbūs!",
+ "enableNotificationsDesc": "Leisti pranešimus",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Naudoti veiklos vaizdą kaip pokalbio foną",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Pokalbis su palaikymu",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Pagal numatytuosius nustatymus, kursai yra viešai ieškomi ir reikalauja administratoriaus patvirtinimo prisijungti. Šiuos nustatymus galite redaguoti bet kada.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Kokią kalbą mokotės?",
+ "searchLanguagesHint": "Ieškoti tikslo kalbų",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Klausimai? Mes čia, kad padėtume!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Kažkas nepavyko, ir mes sunkiai dirbame, kad tai išspręstume. Patikrinkite vėliau.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Įgalinti rašymo pagalbą",
+ "autoIGCToolDescription": "Automatiškai paleisti Pangea Chat įrankius, kad ištaisytumėte išsiųstas žinutes į tikslinę kalbą.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Įrašymas nepavyko. Patikrinkite savo garso teises ir bandykite dar kartą.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Sudėtinis",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_lv.arb b/lib/l10n/intl_lv.arb
index a52336e79..55e1affc6 100644
--- a/lib/l10n/intl_lv.arb
+++ b/lib/l10n/intl_lv.arb
@@ -3773,8 +3773,6 @@
"noPaymentInfo": "Nav nepieciešama maksājuma informācija!",
"updatePhoneOS": "Var būt nepieciešams atjaunināt ierīces operētājsistēmas versiju.",
"wordsPerMinute": "Vārdi minūtē",
- "autoIGCToolName": "Automātiski palaist Pangea rakstīšanas palīgu",
- "autoIGCToolDescription": "Automātiski palaist Pangea Čata gramatikas un tulkošanas rakstīšanas palīgu pirms mana ziņojuma nosūtīšanas.",
"tooltipInstructionsTitle": "Neesat pārliecināts, kas tas dara?",
"tooltipInstructionsMobileBody": "Turiet un turiet vienumus, lai skatītu rīku padomus.",
"tooltipInstructionsBrowserBody": "Novietojiet peles kursoru virs vienumiem, lai skatītu rīku padomus.",
@@ -4482,7 +4480,7 @@
"playWithAI": "Tagad spēlējiet ar AI",
"courseStartDesc": "Pangea bots ir gatavs jebkurā laikā!\n\n...bet mācīties ir labāk ar draugiem!",
"@@locale": "lv",
- "@@last_modified": "2026-01-07 14:27:04.364429",
+ "@@last_modified": "2026-02-05 10:09:54.766036",
"analyticsInactiveTitle": "Pieprasījumi neaktīviem lietotājiem nevar tikt nosūtīti",
"analyticsInactiveDesc": "Neaktīvi lietotāji, kuri nav pieteikušies kopš šīs funkcijas ieviešanas, neredzēs jūsu pieprasījumu.\n\nPieprasījuma poga parādīsies, kad viņi atgriezīsies. Jūs varat atkārtoti nosūtīt pieprasījumu vēlāk, noklikšķinot uz pieprasījuma pogas viņu vārdā, kad tā būs pieejama.",
"accessRequestedTitle": "Pieprasījums piekļūt analītikai",
@@ -4495,7 +4493,6 @@
"numModules": "{num} moduļi",
"coursePlan": "Kursa plāns",
"editCourseLater": "Jūs varat vēlāk rediģēt šablona nosaukumu, aprakstus un kursa attēlu.",
- "newCourseAccess": "Pēc noklusējuma kursi ir privāti un prasa administratora apstiprinājumu, lai pievienotos. Jūs varat šīs iestatījumus mainīt jebkurā laikā.",
"createCourse": "Izveidot kursu",
"stats": "Statistika",
"@customReaction": {
@@ -6192,14 +6189,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8818,10 +8807,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10811,5 +10796,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Jūs pametāt čatu",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Lejupielāde uzsākta",
+ "webDownloadPermissionMessage": "Ja jūsu pārlūkprogramma bloķē lejupielādes, lūdzu, iespējot lejupielādes šai vietnei.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Jūsu prakses sesijas progress netiks saglabāts.",
+ "practiceGrammar": "Praktizēt gramatiku",
+ "notEnoughToPractice": "Sūtiet vairāk ziņojumu, lai atbloķētu praksi",
+ "constructUseCorGCDesc": "Pareizas gramatikas kategorijas prakse",
+ "constructUseIncGCDesc": "Nepareizas gramatikas kategorijas prakse",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Pareiza gramatikas kļūdu prakse",
+ "constructUseIncGEDesc": "Nepareiza gramatikas kļūdu prakse",
+ "fillInBlank": "Aizpildiet tukšo vietu ar pareizo izvēli",
+ "learn": "Mācīties",
+ "languageUpdated": "Mērķa valoda atjaunota!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot balss",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Jūsu pieprasījums ir nosūtīts kursa administratoram! Jūs tiksiet iekšā, ja viņi apstiprinās.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Vai jums ir uzaicinājuma kods vai saite uz publisku kursu?",
+ "welcomeUser": "Laipni lūdzam, {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Meklējiet lietotājus, lai viņus aicinātu uz šo čatu.",
+ "publicInviteDescSpace": "Meklējiet lietotājus, lai viņus aicinātu uz šo telpu.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat ir ziņojumapmaiņas lietotne, tāpēc paziņojumi ir svarīgi!",
+ "enableNotificationsDesc": "Atļaut paziņojumus",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Izmantojiet aktivitātes attēlu kā čata fona",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Sarunāties ar atbalstu",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Pēc noklusējuma kursi ir publiski meklējami un prasa administratora apstiprinājumu pievienošanai. Jūs varat rediģēt šos iestatījumus jebkurā laikā.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Kuru valodu tu mācies?",
+ "searchLanguagesHint": "Meklēt mērķa valodas",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Jautājumi? Mēs esam šeit, lai palīdzētu!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Kaut kas nogāja greizi, un mēs smagi strādājam, lai to labotu. Pārbaudiet vēlāk.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Iespējot rakstīšanas palīdzību",
+ "autoIGCToolDescription": "Automātiski palaist Pangea Chat rīkus, lai labotu nosūtītās ziņas mērķa valodā.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Ieraksts neizdevās. Lūdzu, pārbaudiet savas audio atļaujas un mēģiniet vēlreiz.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Frazēts darbības vārds",
+ "grammarCopyPOScompn": "Savienojums",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_nb.arb b/lib/l10n/intl_nb.arb
index 8c947d498..9b520323a 100644
--- a/lib/l10n/intl_nb.arb
+++ b/lib/l10n/intl_nb.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:26:11.727122",
+ "@@last_modified": "2026-02-05 10:09:45.325631",
"about": "Om",
"@about": {
"type": "String",
@@ -2700,8 +2700,6 @@
"noPaymentInfo": "Ingen betalingsinformasjon nødvendig!",
"updatePhoneOS": "Du kan trenge å oppdatere enhetens OS-versjon.",
"wordsPerMinute": "Ord per minutt",
- "autoIGCToolName": "Kjør Pangea skrivehjelp automatisk",
- "autoIGCToolDescription": "Kjør automatisk Pangea Chat grammatikk- og oversettelsesstøtte før jeg sender meldingen min.",
"tooltipInstructionsTitle": "Usikker på hva det gjør?",
"tooltipInstructionsMobileBody": "Trykk og hold på elementer for å se verktøytips.",
"tooltipInstructionsBrowserBody": "Hold musepekeren over elementer for å se verktøytips.",
@@ -3329,7 +3327,6 @@
"numModules": "{num} moduler",
"coursePlan": "Kursplan",
"editCourseLater": "Du kan redigere malens tittel, beskrivelser og kursbilde senere.",
- "newCourseAccess": "Som standard er kurs private og krever administratorgodkjenning for å bli med. Du kan endre disse innstillingene når som helst.",
"createCourse": "Opprett kurs",
"stats": "Statistikk",
"createGroupChat": "Opprett gruppechat",
@@ -7299,14 +7296,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9925,10 +9914,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11918,5 +11903,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Du forlot chatten",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Nedlasting initiert",
+ "webDownloadPermissionMessage": "Hvis nettleseren din blokkerer nedlastinger, vennligst aktiver nedlastinger for dette nettstedet.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Fremdriften i økten din vil ikke bli lagret.",
+ "practiceGrammar": "Øv på grammatikk",
+ "notEnoughToPractice": "Send flere meldinger for å låse opp øving",
+ "constructUseCorGCDesc": "Øvelse i korrekt grammatikkategori",
+ "constructUseIncGCDesc": "Øvelse i ukorrekt grammatikkategori",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Korrekt grammatikkfeil praksis",
+ "constructUseIncGEDesc": "Feil grammatikkfeil praksis",
+ "fillInBlank": "Fyll inn blanketten med riktig valg",
+ "learn": "Lær",
+ "languageUpdated": "Mål språk oppdatert!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot-stemme",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Din forespørsel har blitt sendt til kursadministratoren! Du vil bli sluppet inn hvis de godkjenner.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Har du en invitasjonskode eller lenke til et offentlig kurs?",
+ "welcomeUser": "Velkommen {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Søk etter brukere for å invitere dem til denne chatten.",
+ "publicInviteDescSpace": "Søk etter brukere for å invitere dem til dette rommet.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat er en tekstmelding-app, så varsler er viktige!",
+ "enableNotificationsDesc": "Tillat varsler",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Bruk aktivitetsbilde som chatbakgrunn",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat med støtte",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Som standard er kurs offentlig søkbare og krever administratortillatelse for å bli med. Du kan redigere disse innstillingene når som helst.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Hvilket språk lærer du?",
+ "searchLanguagesHint": "Søk etter målspråk",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Spørsmål? Vi er her for å hjelpe!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Noe gikk galt, og vi jobber hardt med å fikse det. Sjekk igjen senere.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Aktiver skriveassistent",
+ "autoIGCToolDescription": "Kjør Pangea Chat-verktøy automatisk for å korrigere sendte meldinger til målspråket.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Opptak mislyktes. Vennligst sjekk lydinnstillingene dine og prøv igjen.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Sammensatt",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb
index e55bad11f..6dea520bb 100644
--- a/lib/l10n/intl_nl.arb
+++ b/lib/l10n/intl_nl.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:27:48.189369",
+ "@@last_modified": "2026-02-05 10:10:04.986596",
"about": "Over ons",
"@about": {
"type": "String",
@@ -3784,8 +3784,6 @@
"noPaymentInfo": "Geen betalingsinformatie nodig!",
"updatePhoneOS": "U moet mogelijk de OS-versie van uw apparaat bijwerken.",
"wordsPerMinute": "Woorden per minuut",
- "autoIGCToolName": "Voer Pangea schrijfhulp automatisch uit",
- "autoIGCToolDescription": "Voer automatisch Pangea Chat grammatica- en vertaalhulp uit voordat ik mijn bericht verstuur.",
"tooltipInstructionsTitle": "Weet je niet wat dat doet?",
"tooltipInstructionsMobileBody": "Houd items ingedrukt om tooltips te bekijken.",
"tooltipInstructionsBrowserBody": "Houd de muisaanwijzer over items om tooltips te bekijken.",
@@ -4413,7 +4411,6 @@
"numModules": "{num} modules",
"coursePlan": "Cursusplan",
"editCourseLater": "U kunt de titel, beschrijvingen en cursusafbeelding later bewerken.",
- "newCourseAccess": "Standaard zijn cursussen privé en vereist goedkeuring van een beheerder om deel te nemen. U kunt deze instellingen op elk moment aanpassen.",
"createCourse": "Cursus maken",
"stats": "Statistieken",
"createGroupChat": "Groepschat maken",
@@ -6206,14 +6203,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8832,10 +8821,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10825,5 +10810,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Je hebt de chat verlaten",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download gestart",
+ "webDownloadPermissionMessage": "Als uw browser downloads blokkeert, schakel dan downloads voor deze site in.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Uw voortgang in de oefensessie wordt niet opgeslagen.",
+ "practiceGrammar": "Oefen grammatica",
+ "notEnoughToPractice": "Stuur meer berichten om de oefening te ontgrendelen",
+ "constructUseCorGCDesc": "Oefening in de juiste grammaticacategorie",
+ "constructUseIncGCDesc": "Oefening in de onjuiste grammaticacategorie",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Oefening voor correcte grammatica",
+ "constructUseIncGEDesc": "Oefening voor onjuiste grammatica",
+ "fillInBlank": "Vul de lege ruimte in met de juiste keuze",
+ "learn": "Leren",
+ "languageUpdated": "Doeltaal bijgewerkt!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot stem",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Je verzoek is verzonden naar de cursusbeheerder! Je wordt toegelaten als ze goedkeuren.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Heb je een uitnodigingscode of link naar een openbare cursus?",
+ "welcomeUser": "Welkom {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Zoek naar gebruikers om ze uit te nodigen voor deze chat.",
+ "publicInviteDescSpace": "Zoek naar gebruikers om ze uit te nodigen voor deze ruimte.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat is een berichten-app, dus meldingen zijn belangrijk!",
+ "enableNotificationsDesc": "Sta meldingen toe",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Gebruik activiteit afbeelding als chatachtergrond",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat met Ondersteuning",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Standaard zijn cursussen openbaar doorzoekbaar en is goedkeuring van de beheerder vereist om deel te nemen. Je kunt deze instellingen op elk moment bewerken.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Welke taal ben je aan het leren?",
+ "searchLanguagesHint": "Zoek doeltalen",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Vragen? We zijn hier om te helpen!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Er is iets misgegaan en we zijn hard aan het werk om het op te lossen. Kijk later nog eens.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Schrijfassistentie inschakelen",
+ "autoIGCToolDescription": "Voer automatisch Pangea Chat-tools uit om verzonden berichten naar de doeltaal te corrigeren.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Opname mislukt. Controleer uw audiorechten en probeer het opnieuw.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idioom",
+ "grammarCopyPOSphrasalv": "Frazal Werkwoord",
+ "grammarCopyPOScompn": "Samenstelling",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb
index 75961bf4d..aaa6f96a0 100644
--- a/lib/l10n/intl_pl.arb
+++ b/lib/l10n/intl_pl.arb
@@ -1,6 +1,6 @@
{
"@@locale": "pl",
- "@@last_modified": "2026-01-07 14:28:18.089845",
+ "@@last_modified": "2026-02-05 10:10:11.791789",
"about": "O aplikacji",
"@about": {
"type": "String",
@@ -3785,8 +3785,6 @@
"noPaymentInfo": "Brak konieczności podawania informacji o płatności!",
"updatePhoneOS": "Możesz potrzebować zaktualizować wersję systemu operacyjnego swojego urządzenia.",
"wordsPerMinute": "Słów na minutę",
- "autoIGCToolName": "Uruchom automatycznie pomoc w pisaniu Pangea",
- "autoIGCToolDescription": "Automatycznie uruchom pomoc w gramatyce i tłumaczeniu Pangea Chat przed wysłaniem mojej wiadomości.",
"tooltipInstructionsTitle": "Nie jesteś pewien, co to robi?",
"tooltipInstructionsMobileBody": "Przytrzymaj elementy, aby wyświetlić podpowiedzi.",
"tooltipInstructionsBrowserBody": "Najedź kursorem na elementy, aby wyświetlić podpowiedzi.",
@@ -4414,7 +4412,6 @@
"numModules": "{num} modułów",
"coursePlan": "Plan kursu",
"editCourseLater": "Możesz edytować tytuł szablonu, opisy i obraz kursu później.",
- "newCourseAccess": "Domyślnie kursy są prywatne i wymagają zatwierdzenia administratora, aby do nich dołączyć. Możesz edytować te ustawienia w dowolnym momencie.",
"createCourse": "Utwórz kurs",
"stats": "Statystyki",
"createGroupChat": "Utwórz czat grupowy",
@@ -6206,14 +6203,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8832,10 +8821,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10823,5 +10808,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Opuszczono czat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Pobieranie zainicjowane",
+ "webDownloadPermissionMessage": "Jeśli Twoja przeglądarka blokuje pobieranie, włącz pobieranie dla tej strony.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Postęp Twojej sesji ćwiczeń nie zostanie zapisany.",
+ "practiceGrammar": "Ćwicz gramatykę",
+ "notEnoughToPractice": "Wyślij więcej wiadomości, aby odblokować ćwiczenia",
+ "constructUseCorGCDesc": "Ćwiczenie poprawnej kategorii gramatycznej",
+ "constructUseIncGCDesc": "Ćwiczenie niepoprawnej kategorii gramatycznej",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Ćwiczenie poprawnych błędów gramatycznych",
+ "constructUseIncGEDesc": "Ćwiczenie niepoprawnych błędów gramatycznych",
+ "fillInBlank": "Uzupełnij lukę poprawnym wyborem",
+ "learn": "Ucz się",
+ "languageUpdated": "Język docelowy zaktualizowany!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Głos bota Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Twoja prośba została wysłana do administratora kursu! Zostaniesz wpuszczony, jeśli ją zatwierdzą.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Czy masz kod zaproszenia lub link do publicznego kursu?",
+ "welcomeUser": "Witaj {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Szukaj użytkowników, aby zaprosić ich do tej rozmowy.",
+ "publicInviteDescSpace": "Szukaj użytkowników, aby zaprosić ich do tej przestrzeni.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat to aplikacja do wiadomości, więc powiadomienia są ważne!",
+ "enableNotificationsDesc": "Zezwól na powiadomienia",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Użyj obrazu aktywności jako tła czatu",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Czat z pomocą",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Domyślnie kursy są publicznie wyszukiwalne i wymagają zatwierdzenia przez administratora, aby do nich dołączyć. Możesz edytować te ustawienia w dowolnym momencie.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Jakiego języka się uczysz?",
+ "searchLanguagesHint": "Szukaj języków docelowych",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Pytania? Jesteśmy tutaj, aby pomóc!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Coś poszło nie tak, a my ciężko pracujemy nad naprawą. Sprawdź ponownie później.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Włącz pomoc w pisaniu",
+ "autoIGCToolDescription": "Automatycznie uruchom narzędzia Pangea Chat, aby poprawić wysłane wiadomości na docelowy język.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Nagrywanie nie powiodło się. Sprawdź swoje uprawnienia audio i spróbuj ponownie.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Czasownik frazowy",
+ "grammarCopyPOScompn": "Złożony",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb
index 34e340d40..9b8ecb9b7 100644
--- a/lib/l10n/intl_pt.arb
+++ b/lib/l10n/intl_pt.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:25:09.370204",
+ "@@last_modified": "2026-02-05 10:09:34.897101",
"copiedToClipboard": "Copiada para a área de transferência",
"@copiedToClipboard": {
"type": "String",
@@ -3775,8 +3775,6 @@
"noPaymentInfo": "Nenhuma informação de pagamento necessária!",
"updatePhoneOS": "Pode ser necessário atualizar a versão do sistema operacional do seu dispositivo.",
"wordsPerMinute": "Palavras por minuto",
- "autoIGCToolName": "Executar assistência de escrita Pangea automaticamente",
- "autoIGCToolDescription": "Executar automaticamente a assistência de escrita de gramática e tradução do Pangea Chat antes de enviar minha mensagem.",
"tooltipInstructionsTitle": "Não tem certeza do que isso faz?",
"tooltipInstructionsMobileBody": "Pressione e segure itens para ver dicas de ferramenta.",
"tooltipInstructionsBrowserBody": "Passe o mouse sobre os itens para ver dicas de ferramenta.",
@@ -4404,7 +4402,6 @@
"numModules": "{num} módulos",
"coursePlan": "Plano do Curso",
"editCourseLater": "Você pode editar o título do modelo, descrições e imagem do curso posteriormente.",
- "newCourseAccess": "Por padrão, os cursos são privados e requerem aprovação do administrador para participar. Você pode editar essas configurações a qualquer momento.",
"createCourse": "Criar curso",
"stats": "Estatísticas",
"createGroupChat": "Criar chat em grupo",
@@ -7306,14 +7303,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9932,10 +9921,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11925,5 +11910,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Você saiu do chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download iniciado",
+ "webDownloadPermissionMessage": "Se o seu navegador bloquear downloads, por favor, habilite downloads para este site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "O progresso da sua sessão de prática não será salvo.",
+ "practiceGrammar": "Praticar gramática",
+ "notEnoughToPractice": "Envie mais mensagens para desbloquear a prática",
+ "constructUseCorGCDesc": "Prática da categoria de gramática correta",
+ "constructUseIncGCDesc": "Prática da categoria de gramática incorreta",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Prática de erro gramatical correto",
+ "constructUseIncGEDesc": "Prática de erro gramatical incorreto",
+ "fillInBlank": "Preencha a lacuna com a escolha correta",
+ "learn": "Aprender",
+ "languageUpdated": "Idioma de destino atualizado!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voz do Bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?",
+ "welcomeUser": "Bem-vindo {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Procure usuários para convidá-los para este chat.",
+ "publicInviteDescSpace": "Procure usuários para convidá-los para este espaço.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat é um aplicativo de mensagens, então as notificações são importantes!",
+ "enableNotificationsDesc": "Permitir notificações",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Usar imagem da atividade como fundo do chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Converse com o Suporte",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Por padrão, os cursos são pesquisáveis publicamente e requerem aprovação do administrador para participar. Você pode editar essas configurações a qualquer momento.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Qual idioma você está aprendendo?",
+ "searchLanguagesHint": "Pesquise idiomas-alvo",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Dúvidas? Estamos aqui para ajudar!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Algo deu errado, e estamos trabalhando arduamente para corrigir isso. Verifique novamente mais tarde.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Ativar assistência de escrita",
+ "autoIGCToolDescription": "Executar automaticamente as ferramentas do Pangea Chat para corrigir mensagens enviadas para o idioma de destino.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "A gravação falhou. Verifique suas permissões de áudio e tente novamente.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verbo Frasal",
+ "grammarCopyPOScompn": "Composto",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb
index a2fdf12cf..cbe1ca38b 100644
--- a/lib/l10n/intl_pt_BR.arb
+++ b/lib/l10n/intl_pt_BR.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:24:55.031821",
+ "@@last_modified": "2026-02-05 10:09:31.755690",
"about": "Sobre",
"@about": {
"type": "String",
@@ -3532,8 +3532,6 @@
"noPaymentInfo": "Nenhuma informação de pagamento necessária!",
"updatePhoneOS": "Você pode precisar atualizar a versão do sistema operacional do seu dispositivo.",
"wordsPerMinute": "Palavras por minuto",
- "autoIGCToolName": "Executar assistência de escrita Pangea automaticamente",
- "autoIGCToolDescription": "Executar automaticamente a assistência de gramática e tradução do Pangea Chat antes de enviar minha mensagem.",
"tooltipInstructionsTitle": "Não tem certeza do que isso faz?",
"tooltipInstructionsMobileBody": "Pressione e segure os itens para ver dicas de ferramenta.",
"tooltipInstructionsBrowserBody": "Passe o mouse sobre os itens para ver dicas de ferramenta.",
@@ -4161,7 +4159,6 @@
"numModules": "{num} módulos",
"coursePlan": "Plano do curso",
"editCourseLater": "Você pode editar o título do modelo, descrições e a imagem do curso posteriormente.",
- "newCourseAccess": "Por padrão, os cursos são privados e requerem aprovação do administrador para ingressar. Você pode editar essas configurações a qualquer momento.",
"createCourse": "Criar curso",
"stats": "Estatísticas",
"createGroupChat": "Criar chat em grupo",
@@ -6581,14 +6578,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9207,10 +9196,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11183,5 +11168,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Você saiu do chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download iniciado",
+ "webDownloadPermissionMessage": "Se o seu navegador bloquear downloads, por favor, habilite downloads para este site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "O progresso da sua sessão de prática não será salvo.",
+ "practiceGrammar": "Praticar gramática",
+ "notEnoughToPractice": "Envie mais mensagens para desbloquear a prática",
+ "constructUseCorGCDesc": "Prática da categoria de gramática correta",
+ "constructUseIncGCDesc": "Prática da categoria de gramática incorreta",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Prática de erro gramatical correto",
+ "constructUseIncGEDesc": "Prática de erro gramatical incorreto",
+ "fillInBlank": "Preencha a lacuna com a escolha correta",
+ "learn": "Aprender",
+ "languageUpdated": "Idioma de destino atualizado!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voz do Bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?",
+ "welcomeUser": "Bem-vindo {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Procure usuários para convidá-los para este chat.",
+ "publicInviteDescSpace": "Procure usuários para convidá-los para este espaço.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat é um aplicativo de mensagens, então as notificações são importantes!",
+ "enableNotificationsDesc": "Permitir notificações",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Usar imagem da atividade como fundo do chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Converse com o Suporte",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Por padrão, os cursos são pesquisáveis publicamente e requerem aprovação do administrador para ingressar. Você pode editar essas configurações a qualquer momento.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Qual idioma você está aprendendo?",
+ "searchLanguagesHint": "Pesquise idiomas-alvo",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Dúvidas? Estamos aqui para ajudar!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Algo deu errado, e estamos trabalhando duro para consertar. Verifique novamente mais tarde.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Ativar assistência de escrita",
+ "autoIGCToolDescription": "Executar automaticamente as ferramentas do Pangea Chat para corrigir mensagens enviadas para o idioma de destino.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Gravação falhou. Por favor, verifique suas permissões de áudio e tente novamente.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verbo Frasal",
+ "grammarCopyPOScompn": "Composto",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_pt_PT.arb b/lib/l10n/intl_pt_PT.arb
index 3af737159..c74ddde52 100644
--- a/lib/l10n/intl_pt_PT.arb
+++ b/lib/l10n/intl_pt_PT.arb
@@ -2595,8 +2595,6 @@
"noPaymentInfo": "Nenhuma informação de pagamento necessária!",
"updatePhoneOS": "Pode ser necessário atualizar a versão do sistema operacional do seu dispositivo.",
"wordsPerMinute": "Palavras por minuto",
- "autoIGCToolName": "Executar assistência de escrita Pangea automaticamente",
- "autoIGCToolDescription": "Executar automaticamente a assistência de escrita de gramática e tradução do Pangea Chat antes de enviar minha mensagem.",
"tooltipInstructionsTitle": "Não tem certeza do que isso faz?",
"tooltipInstructionsMobileBody": "Pressione e segure itens para visualizar dicas de ferramenta.",
"tooltipInstructionsBrowserBody": "Passe o mouse sobre os itens para visualizar dicas de ferramenta.",
@@ -3224,7 +3222,6 @@
"numModules": "{num} módulos",
"coursePlan": "Plano do Curso",
"editCourseLater": "Pode editar o título do modelo, descrições e imagem do curso mais tarde.",
- "newCourseAccess": "Por padrão, os cursos são privados e requerem aprovação do administrador para participar. Pode editar estas configurações a qualquer momento.",
"createCourse": "Criar curso",
"stats": "Estatísticas",
"createGroupChat": "Criar chat de grupo",
@@ -3331,7 +3328,7 @@
"selectAll": "Selecionar tudo",
"deselectAll": "Desmarcar tudo",
"@@locale": "pt_PT",
- "@@last_modified": "2026-01-07 14:26:44.705293",
+ "@@last_modified": "2026-02-05 10:09:50.725651",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7252,14 +7249,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9878,10 +9867,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11854,5 +11839,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Você saiu do chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Download iniciado",
+ "webDownloadPermissionMessage": "Se o seu navegador bloquear downloads, por favor, habilite downloads para este site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "O progresso da sua sessão de prática não será salvo.",
+ "practiceGrammar": "Praticar gramática",
+ "notEnoughToPractice": "Envie mais mensagens para desbloquear a prática",
+ "constructUseCorGCDesc": "Prática da categoria de gramática correta",
+ "constructUseIncGCDesc": "Prática da categoria de gramática incorreta",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Prática de erro gramatical correto",
+ "constructUseIncGEDesc": "Prática de erro gramatical incorreto",
+ "fillInBlank": "Preencha a lacuna com a escolha correta",
+ "learn": "Aprender",
+ "languageUpdated": "Idioma de destino atualizado!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Voz do Bot Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Sua solicitação foi enviada ao administrador do curso! Você será admitido se eles aprovarem.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Você tem um código de convite ou link para um curso público?",
+ "welcomeUser": "Bem-vindo {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Procure usuários para convidá-los para este chat.",
+ "publicInviteDescSpace": "Procure usuários para convidá-los para este espaço.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat é um aplicativo de mensagens, então as notificações são importantes!",
+ "enableNotificationsDesc": "Permitir notificações",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Usar imagem da atividade como fundo do chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Converse com o Suporte",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Por padrão, os cursos são pesquisáveis publicamente e requerem aprovação do administrador para ingressar. Você pode editar essas configurações a qualquer momento.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Qual idioma você está aprendendo?",
+ "searchLanguagesHint": "Pesquise idiomas-alvo",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Dúvidas? Estamos aqui para ajudar!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Algo deu errado, e estamos trabalhando arduamente para corrigir isso. Verifique novamente mais tarde.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Ativar assistência de escrita",
+ "autoIGCToolDescription": "Executar automaticamente as ferramentas do Pangea Chat para corrigir mensagens enviadas para o idioma de destino.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "A gravação falhou. Por favor, verifique suas permissões de áudio e tente novamente.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verbo Frasal",
+ "grammarCopyPOScompn": "Composto",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb
index 7ab1f3c39..8dc92a39b 100644
--- a/lib/l10n/intl_ro.arb
+++ b/lib/l10n/intl_ro.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:24:11.005786",
+ "@@last_modified": "2026-02-05 10:09:23.119007",
"about": "Despre",
"@about": {
"type": "String",
@@ -3204,8 +3204,6 @@
"noPaymentInfo": "Nicio informație de plată necesară!",
"updatePhoneOS": "Poate fi necesar să actualizați versiunea sistemului de operare al dispozitivului dvs.",
"wordsPerMinute": "Cuvinte pe minut",
- "autoIGCToolName": "Rulează automat asistența de scriere Pangea",
- "autoIGCToolDescription": "Porniți automat asistența pentru scrierea gramaticii și traducerii în Pangea Chat înainte de a trimite mesajul meu.",
"tooltipInstructionsTitle": "Nu ești sigur ce face asta?",
"tooltipInstructionsMobileBody": "Ține apăsat pe elemente pentru a vizualiza sfaturi.",
"tooltipInstructionsBrowserBody": "Poziționează cursorul peste elemente pentru a vizualiza sfaturi.",
@@ -3833,7 +3831,6 @@
"numModules": "{num} module",
"coursePlan": "Plan de curs",
"editCourseLater": "Poți edita titlul, descrierile și imaginea cursului mai târziu.",
- "newCourseAccess": "În mod implicit, cursurile sunt private și necesită aprobarea administratorului pentru a te alătura. Poți edita aceste setări oricând.",
"createCourse": "Creează curs",
"stats": "Statistici",
"createGroupChat": "Creează chat de grup",
@@ -6941,14 +6938,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9567,10 +9556,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11560,5 +11545,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Ai părăsit chat-ul",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Descărcare inițiată",
+ "webDownloadPermissionMessage": "Dacă browserul dvs. blochează descărcările, vă rugăm să activați descărcările pentru acest site.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Progresul sesiunii tale de practică nu va fi salvat.",
+ "practiceGrammar": "Exersează gramatică",
+ "notEnoughToPractice": "Trimite mai multe mesaje pentru a debloca practica",
+ "constructUseCorGCDesc": "Practică categoria de gramatică corectă",
+ "constructUseIncGCDesc": "Practică categoria de gramatică incorectă",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Practică corectă a erorilor de gramatică",
+ "constructUseIncGEDesc": "Practică incorectă a erorilor de gramatică",
+ "fillInBlank": "Completați spațiul gol cu alegerea corectă",
+ "learn": "Învățați",
+ "languageUpdated": "Limba țintă a fost actualizată!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Vocea Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Cererea ta a fost trimisă administratorului cursului! Vei fi lăsat să intri dacă ei aprobă.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Ai un cod de invitație sau un link pentru un curs public?",
+ "welcomeUser": "Bine ai venit {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Caută utilizatori pentru a-i invita în acest chat.",
+ "publicInviteDescSpace": "Caută utilizatori pentru a-i invita în acest spațiu.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat este o aplicație de mesagerie, așa că notificările sunt importante!",
+ "enableNotificationsDesc": "Permite notificările",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Folosește imaginea activității ca fundal pentru chat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chat cu Suportul",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "În mod implicit, cursurile sunt căutabile public și necesită aprobată de administrator pentru a se alătura. Puteți edita aceste setări în orice moment.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Ce limbă înveți?",
+ "searchLanguagesHint": "Caută limbi țintă",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Întrebări? Suntem aici să ajutăm!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Ceva a mers prost și lucrăm din greu pentru a remedia problema. Verifică din nou mai târziu.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Activare asistență la scriere",
+ "autoIGCToolDescription": "Rulați automat instrumentele Pangea Chat pentru a corecta mesajele trimise în limba țintă.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Înregistrarea a eșuat. Vă rugăm să verificați permisiunile audio și să încercați din nou.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Verb Phrastic",
+ "grammarCopyPOScompn": "Compus",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb
index 720485908..777cd260a 100644
--- a/lib/l10n/intl_ru.arb
+++ b/lib/l10n/intl_ru.arb
@@ -1,6 +1,6 @@
{
"@@locale": "ru",
- "@@last_modified": "2026-01-07 14:28:59.154241",
+ "@@last_modified": "2026-02-05 10:10:19.334362",
"about": "О проекте",
"@about": {
"type": "String",
@@ -3132,8 +3132,6 @@
"@invalidUrl": {},
"addLink": "Добавить ссылку",
"@addLink": {},
- "italicText": "Italic",
- "@italicText": {},
"unableToJoinChat": "Невозможно присоединиться к чату. Возможно, другая сторона уже закончила разговор.",
"@unableToJoinChat": {},
"serverLimitReached": "Ограничения сервера. Ожидайте{seconds} секунд...",
@@ -3689,13 +3687,6 @@
"acceptSelection": "Принять исправление",
"why": "Почему?",
"definition": "Определение",
- "exampleSentence": "Приклад речення",
- "reportToTeacher": "Кому ви хочете повідомити про це повідомлення?",
- "reportMessageTitle": "{reportingUserId} повідомив про повідомлення від {reportedUserId} у чаті {roomName}",
- "reportMessageBody": "Повідомлення: {reportedMessage}\nПричина: {reason}",
- "noTeachersFound": "Вчителі не знайдені для повідомлення",
- "trialExpiration": "Ваша безкоштовна пробна версія закінчується {expiration}",
- "freeTrialDesc": "Нові користувачі отримують тижневу безкоштовну пробну версію Pangea Chat",
"activateTrial": "Безкоштовна 7-денна пробна версія",
"successfullySubscribed": "Ви успішно підписалися!",
"clickToManageSubscription": "Натисніть тут, щоб керувати підпискою.",
@@ -3723,8 +3714,6 @@
"noPaymentInfo": "Информация о платеже не требуется!",
"updatePhoneOS": "Возможно, потребуется обновить версию ОС вашего устройства.",
"wordsPerMinute": "Слова в минуту",
- "autoIGCToolName": "Автоматически запускать помощь в написании Pangea",
- "autoIGCToolDescription": "Автоматически запускать помощь в грамматике и переводе Pangea Chat перед отправкой моего сообщения.",
"tooltipInstructionsTitle": "Не уверены, что это делает?",
"tooltipInstructionsMobileBody": "Нажмите и удерживайте элементы, чтобы просмотреть подсказки.",
"tooltipInstructionsBrowserBody": "Наведите курсор на элементы, чтобы просмотреть подсказки.",
@@ -4078,13 +4067,6 @@
"constructUseIncMDesc": "Некорректно в деятельности по грамматике",
"constructUseIgnMDesc": "Игнорируется в деятельности по грамматике",
"constructUseEmojiDesc": "Правильно в деятельности по эмодзи",
- "constructUseCollected": "Thu thập trong trò chuyện",
- "constructUseNanDesc": "Không áp dụng được",
- "xpIntoLevel": "{currentXP} / {maxXP} XP",
- "enableTTSToolName": "Bật chuyển đổi văn bản thành giọng nói",
- "enableTTSToolDescription": "Cho phép ứng dụng tạo ra đầu ra chuyển đổi văn bản thành giọng nói cho các phần của văn bản bằng ngôn ngữ mục tiêu của bạn.",
- "yourUsername": "Tên người dùng của bạn",
- "yourEmail": "Email của bạn",
"iWantToLearn": "Я хочу учиться",
"pleaseEnterEmail": "Пожалуйста, введите действительный адрес электронной почты.",
"myBaseLanguage": "Мой основной язык",
@@ -4352,7 +4334,6 @@
"numModules": "{num} модулей",
"coursePlan": "План курса",
"editCourseLater": "Вы можете редактировать название шаблона, описания и изображение курса позже.",
- "newCourseAccess": "По умолчанию курсы являются приватными и требуют одобрения администратора для присоединения. Вы можете изменить эти настройки в любое время.",
"createCourse": "Создать курс",
"stats": "Статистика",
"createGroupChat": "Создать групповой чат",
@@ -6311,14 +6292,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8937,10 +8910,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10930,5 +10899,198 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Вы покинули чат",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Загрузка начата",
+ "webDownloadPermissionMessage": "Если ваш браузер блокирует загрузки, пожалуйста, разрешите загрузки для этого сайта.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Ваш прогресс в сессии практики не будет сохранен.",
+ "practiceGrammar": "Практика грамматики",
+ "notEnoughToPractice": "Отправьте больше сообщений, чтобы разблокировать практику",
+ "constructUseCorGCDesc": "Практика правильной грамматики",
+ "constructUseIncGCDesc": "Практика неправильной грамматики",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Практика исправления грамматических ошибок",
+ "constructUseIncGEDesc": "Практика неправильных грамматических ошибок",
+ "fillInBlank": "Заполните пропуск правильным вариантом",
+ "learn": "Учить",
+ "languageUpdated": "Целевой язык обновлен!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Голос бота Pangea",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Ваш запрос отправлен администратору курса! Вы будете допущены, если они одобрят.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "italicText": "Курсивный текст",
+ "exampleSentence": "Пример предложения",
+ "reportToTeacher": "Кому вы хотите пожаловаться на это сообщение?",
+ "reportMessageTitle": "{reportingUserId} пожаловался на сообщение от {reportedUserId} в чате {roomName}",
+ "reportMessageBody": "Сообщение: {reportedMessage}\nПричина: {reason}",
+ "noTeachersFound": "Учителя для жалобы не найдены",
+ "trialExpiration": "Ваш бесплатный пробный период истекает {expiration}",
+ "freeTrialDesc": "Новые пользователи получают бесплатную пробную неделю в Pangea Chat",
+ "constructUseCollected": "Собрано в чате",
+ "constructUseNanDesc": "Не применимо",
+ "xpIntoLevel": "{currentXP} / {maxXP} XP",
+ "enableTTSToolName": "Включен преобразователь текста в речь",
+ "enableTTSToolDescription": "Позволяет приложению генерировать озвучивание текста на вашем целевом языке.",
+ "yourUsername": "Ваше имя пользователя",
+ "yourEmail": "Ваш email",
+ "@italicText": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "У вас есть код приглашения или ссылка на публичный курс?",
+ "welcomeUser": "Добро пожаловать {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Ищите пользователей, чтобы пригласить их в этот чат.",
+ "publicInviteDescSpace": "Ищите пользователей, чтобы пригласить их в это пространство.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat - это приложение для обмена сообщениями, поэтому уведомления важны!",
+ "enableNotificationsDesc": "Разрешить уведомления",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Использовать изображение активности в качестве фона чата",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Чат с поддержкой",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "По умолчанию курсы доступны для публичного поиска и требуют одобрения администратора для присоединения. Вы можете изменить эти настройки в любое время.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Какой язык вы изучаете?",
+ "searchLanguagesHint": "Поиск целевых языков",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Вопросы? Мы здесь, чтобы помочь!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Что-то пошло не так, и мы усердно работаем над исправлением. Проверьте позже.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Включить помощь в написании",
+ "autoIGCToolDescription": "Автоматически запускать инструменты Pangea Chat для исправления отправленных сообщений на целевой язык.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Запись не удалась. Пожалуйста, проверьте свои аудиоразрешения и попробуйте снова.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Идиома",
+ "grammarCopyPOSphrasalv": "Фразовый глагол",
+ "grammarCopyPOScompn": "Составное",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb
index ab85fb852..d6437f578 100644
--- a/lib/l10n/intl_sk.arb
+++ b/lib/l10n/intl_sk.arb
@@ -1,6 +1,6 @@
{
"@@locale": "sk",
- "@@last_modified": "2026-01-07 14:24:18.684168",
+ "@@last_modified": "2026-02-05 10:09:24.752898",
"about": "O aplikácii",
"@about": {
"type": "String",
@@ -2408,8 +2408,6 @@
"noPaymentInfo": "Nie sú potrebné žiadne platobné údaje!",
"updatePhoneOS": "Možno budete musieť aktualizovať verziu operačného systému zariadenia",
"wordsPerMinute": "Slová za minútu",
- "autoIGCToolName": "Automaticky spustiť pomoc pri písaní Pangea",
- "autoIGCToolDescription": "Automaticky spustiť gramatickú kontrolu a preklad pomocníka Pangea Chat pred odoslaním správy.",
"tooltipInstructionsTitle": "Neviete, čo to robí?",
"tooltipInstructionsMobileBody": "Stlačte a podržte položky na zobrazenie návodov.",
"tooltipInstructionsBrowserBody": "Nájdite myšou nad položkami na zobrazenie návodov.",
@@ -3037,7 +3035,6 @@
"numModules": "{num} modulov",
"coursePlan": "Plán kurzu",
"editCourseLater": "Môžete neskôr upraviť názov kurzu, popisy a obrázok kurzu.",
- "newCourseAccess": "Štandardne sú kurzy súkromné a vyžadujú schválenie správcu na pripojenie. Tieto nastavenia môžete upraviť kedykoľvek.",
"createCourse": "Vytvoriť kurz",
"stats": "Štatistiky",
"createGroupChat": "Vytvoriť skupinový chat",
@@ -7290,14 +7287,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9916,10 +9905,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11909,5 +11894,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Opustili ste chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Stiahnutie bolo zahájené",
+ "webDownloadPermissionMessage": "Ak váš prehliadač blokuje sťahovanie, prosím, povolte sťahovanie pre túto stránku.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Pokrok vo vašej cvičebnej relácii nebude uložený.",
+ "practiceGrammar": "Cvičiť gramatiku",
+ "notEnoughToPractice": "Odošlite viac správ na odomknutie cvičenia",
+ "constructUseCorGCDesc": "Cvičenie správnej gramatickej kategórie",
+ "constructUseIncGCDesc": "Cvičenie nesprávnej gramatickej kategórie",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Cvičenie na opravu gramatických chýb",
+ "constructUseIncGEDesc": "Cvičenie na nesprávne gramatické chyby",
+ "fillInBlank": "Doplňte prázdne miesto správnou voľbou",
+ "learn": "Učte sa",
+ "languageUpdated": "Cieľový jazyk bol aktualizovaný!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Hlas Pangea Bota",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Vaša žiadosť bola odoslaná administrátorovi kurzu! Budete vpustený, ak ju schvália.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Máte pozývací kód alebo odkaz na verejný kurz?",
+ "welcomeUser": "Vitaj {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Hľadajte používateľov, aby ste ich pozvali do tohto chatu.",
+ "publicInviteDescSpace": "Hľadajte používateľov, aby ste ich pozvali do tohto priestoru.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat je aplikácia na posielanie správ, takže notifikácie sú dôležité!",
+ "enableNotificationsDesc": "Povoliť notifikácie",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Použiť obrázok aktivity ako pozadie chatu",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chatovať s podporou",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Predvolene sú kurzy verejne vyhľadateľné a vyžadujú schválenie administrátora na pripojenie. Tieto nastavenia môžete kedykoľvek upraviť.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Aký jazyk sa učíte?",
+ "searchLanguagesHint": "Hľadajte cieľové jazyky",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Otázky? Sme tu, aby sme pomohli!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Niečo sa pokazilo a my na tom tvrdo pracujeme, aby sme to opravili. Skontrolujte to neskôr.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Povoliť pomoc pri písaní",
+ "autoIGCToolDescription": "Automaticky spustiť nástroje Pangea Chat na opravu odoslaných správ do cieľového jazyka.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Nahrávanie zlyhalo. Skontrolujte svoje povolenia na zvuk a skúste to znova.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idióm",
+ "grammarCopyPOSphrasalv": "Frázové sloveso",
+ "grammarCopyPOScompn": "Zložené",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_sl.arb b/lib/l10n/intl_sl.arb
index f82b3a3a5..9743b3040 100644
--- a/lib/l10n/intl_sl.arb
+++ b/lib/l10n/intl_sl.arb
@@ -1740,8 +1740,6 @@
"noPaymentInfo": "Ni potrebnih plačilnih informacij!",
"updatePhoneOS": "Morda boste morali posodobiti različico operacijskega sistema naprave",
"wordsPerMinute": "Besed na minuto",
- "autoIGCToolName": "Samodejno zaženi pomoč pri pisanju Pangea",
- "autoIGCToolDescription": "Samodejno zaženi pomoč pri slovnici in prevajanju v klepetu Pangea pred pošiljanjem sporočila",
"tooltipInstructionsTitle": "Niste prepričani, kaj to naredi?",
"tooltipInstructionsMobileBody": "Podrsajte in držite elemente za ogled nasvetov orodja",
"tooltipInstructionsBrowserBody": "Premaknite kazalec nad elemente za ogled nasvetov orodja",
@@ -2369,7 +2367,6 @@
"numModules": "{num} modulov",
"coursePlan": "Načrt tečaja",
"editCourseLater": "Lahko kasneje uredite naslov predloge, opise in sliko tečaja.",
- "newCourseAccess": "Privzeto so tečaji zasebni in zahtevajo odobritev administratorja za vstop. Te nastavitve lahko kadar koli uredite.",
"createCourse": "Ustvari tečaj",
"stats": "Statistika",
"createGroupChat": "Ustvari skupinski klepet",
@@ -2464,7 +2461,7 @@
"playWithAI": "Za zdaj igrajte z AI-jem",
"courseStartDesc": "Pangea Bot je pripravljen kadarkoli!\n\n...ampak je bolje učiti se s prijatelji!",
"@@locale": "sl",
- "@@last_modified": "2026-01-07 14:25:29.847675",
+ "@@last_modified": "2026-02-05 10:09:38.721866",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7287,14 +7284,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9913,10 +9902,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11906,5 +11891,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Zapustili ste klepet",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Prenos je bil začet",
+ "webDownloadPermissionMessage": "Če vaš brskalnik blokira prenose, prosimo, omogočite prenose za to spletno mesto.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Napredek vaše seje vadbe ne bo shranjen.",
+ "practiceGrammar": "Vadite slovnico",
+ "notEnoughToPractice": "Pošljite več sporočil, da odklenete vadbo",
+ "constructUseCorGCDesc": "Vadba pravilne slovnice",
+ "constructUseIncGCDesc": "Vadba nepravilne slovnice",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Praksa pravilne rabe slovnice",
+ "constructUseIncGEDesc": "Praksa nepravilne rabe slovnice",
+ "fillInBlank": "Izpolnite prazno mesto s pravilno izbiro",
+ "learn": "Učite se",
+ "languageUpdated": "Ciljni jezik je posodobljen!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Glas Pangea Bota",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Vaša zahteva je bila poslana skrbniku tečaja! Vstopili boste, če jo odobrijo.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Imate kodo za povabilo ali povezavo do javnega tečaja?",
+ "welcomeUser": "Dobrodošli {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Išči uporabnike, da jih povabiš v ta klepet.",
+ "publicInviteDescSpace": "Išči uporabnike, da jih povabiš v ta prostor.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat je aplikacija za sporočanje, zato so obvestila pomembna!",
+ "enableNotificationsDesc": "Dovoli obvestila",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Uporabi sliko dejavnosti kot ozadje klepeta",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Pogovorite se s podporo",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Privzeto so tečaji javno iskalni in zahtevajo odobritev skrbnika za pridružitev. Te nastavitve lahko kadar koli spremenite.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Katero jezika se učiš?",
+ "searchLanguagesHint": "Išči ciljne jezike",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Vprašanja? Tu smo, da pomagamo!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Nekaj je šlo narobe in trdo delamo na tem, da to popravimo. Preverite znova kasneje.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Omogoči pomoč pri pisanju",
+ "autoIGCToolDescription": "Samodejno zaženi orodja Pangea Chat za popravljanje poslanih sporočil v ciljni jezik.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Zapisovanje ni uspelo. Preverite svoje avdio dovoljenja in poskusite znova.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasalni glagol",
+ "grammarCopyPOScompn": "Sestavljenka",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_sr.arb b/lib/l10n/intl_sr.arb
index c863983a5..831875ff3 100644
--- a/lib/l10n/intl_sr.arb
+++ b/lib/l10n/intl_sr.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:29:14.506248",
+ "@@last_modified": "2026-02-05 10:10:22.625655",
"about": "О програму",
"@about": {
"type": "String",
@@ -2821,8 +2821,6 @@
"noPaymentInfo": "Није потребно информације о плаћању!",
"updatePhoneOS": "Можда ће вам бити потребно ажурирати верзију оперативног система уређаја.",
"wordsPerMinute": "Речи по минуту",
- "autoIGCToolName": "Аутоматски покрените Пангее помоћ за писање",
- "autoIGCToolDescription": "Аутоматски покреће Пангее Граматика и помоћ за превођење пре слања моје поруке.",
"tooltipInstructionsTitle": "Нисте сигурни шта то ради?",
"tooltipInstructionsMobileBody": "Држите дуго на ставкама да бисте видели савете.",
"tooltipInstructionsBrowserBody": "Покажите мишем на ставке да бисте видели савете.",
@@ -3450,7 +3448,6 @@
"numModules": "{num} modula",
"coursePlan": "Plan kursa",
"editCourseLater": "Možete kasnije izmeniti naslov šablona, opise i sliku kursa.",
- "newCourseAccess": "Podrazumevano, kursevi su privatni i zahtevaju odobrenje administratora za pridruživanje. Možete u bilo kom trenutku izmeniti ove postavke.",
"createCourse": "Kreiraj kurs",
"stats": "Statistika",
"createGroupChat": "Kreiraj grupni chat",
@@ -7308,14 +7305,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9934,10 +9923,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11927,5 +11912,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Napustili ste chat",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Preuzimanje je pokrenuto",
+ "webDownloadPermissionMessage": "Ako vaš pregledač blokira preuzimanja, molimo omogućite preuzimanja za ovu stranicu.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Vaš napredak u vežbanju neće biti sačuvan.",
+ "practiceGrammar": "Vežbajte gramatiku",
+ "notEnoughToPractice": "Pošaljite više poruka da otključate vežbanje",
+ "constructUseCorGCDesc": "Vežbanje ispravne gramatike",
+ "constructUseIncGCDesc": "Vežbanje nepravilne gramatike",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Vežba ispravne gramatike",
+ "constructUseIncGEDesc": "Vežba nepravilne gramatike",
+ "fillInBlank": "Popunite prazno mesto sa ispravnim izborom",
+ "learn": "Učite",
+ "languageUpdated": "Ciljni jezik je ažuriran!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Glas Pangea Bota",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Vaš zahtev je poslat administratoru kursa! Bićete primljeni ako odobre.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Imate li pozivni kod ili link za javni kurs?",
+ "welcomeUser": "Dobrodošli {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Pretražite korisnike da ih pozovete u ovaj čat.",
+ "publicInviteDescSpace": "Pretražite korisnike da ih pozovete u ovaj prostor.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat je aplikacija za slanje poruka, pa su obaveštenja važna!",
+ "enableNotificationsDesc": "Dozvoli obaveštenja",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Koristi sliku aktivnosti kao pozadinu za čat",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Razgovarajte sa podrškom",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Po defaultu, kursevi su javno pretraživi i zahtevaju odobrenje administratora za pridruživanje. Ove postavke možete izmeniti u bilo kojem trenutku.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Koji jezik učite?",
+ "searchLanguagesHint": "Pretraži ciljne jezike",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Pitanja? Tu smo da pomognemo!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Nešto je pošlo po zlu, i mi marljivo radimo na rešenju. Proverite ponovo kasnije.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Omogući pomoć pri pisanju",
+ "autoIGCToolDescription": "Automatski pokreni Pangea Chat alate za ispravljanje poslatih poruka na ciljni jezik.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Snimanje nije uspelo. Proverite svoja audio dopuštenja i pokušajte ponovo.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasal Verb",
+ "grammarCopyPOScompn": "Kombinacija",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_sv.arb b/lib/l10n/intl_sv.arb
index b70f8acbc..721c6fbe0 100644
--- a/lib/l10n/intl_sv.arb
+++ b/lib/l10n/intl_sv.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:28:25.359543",
+ "@@last_modified": "2026-02-05 10:10:13.035755",
"about": "Om",
"@about": {
"type": "String",
@@ -3446,8 +3446,6 @@
"noPaymentInfo": "Ingen betalningsinformation behövs!",
"updatePhoneOS": "Du kan behöva uppdatera din enhets operativsystemversion.",
"wordsPerMinute": "Ord per minut",
- "autoIGCToolName": "Kör Pangea skrivhjälp automatiskt",
- "autoIGCToolDescription": "Kör automatiskt Pangea Chat grammatik- och översättningshjälp innan jag skickar mitt meddelande.",
"tooltipInstructionsTitle": "Inte säker på vad det gör?",
"tooltipInstructionsMobileBody": "Håll och tryck på objekt för att visa verktygstips.",
"tooltipInstructionsBrowserBody": "Hovra över objekt för att visa verktygstips.",
@@ -4075,7 +4073,6 @@
"numModules": "{num} moduler",
"coursePlan": "Kursplan",
"editCourseLater": "Du kan redigera mallens titel, beskrivningar och kursbild senare.",
- "newCourseAccess": "Som standard är kurser privata och kräver administratörsgodkännande för att gå med. Du kan ändra dessa inställningar när som helst.",
"createCourse": "Skapa kurs",
"stats": "Statistik",
"createGroupChat": "Skapa gruppchatt",
@@ -6684,14 +6681,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9310,10 +9299,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11303,5 +11288,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Du lämnade chatten",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Nedladdning initierad",
+ "webDownloadPermissionMessage": "Om din webbläsare blockerar nedladdningar, vänligen aktivera nedladdningar för den här sidan.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Din övningssession kommer inte att sparas.",
+ "practiceGrammar": "Öva grammatik",
+ "notEnoughToPractice": "Skicka fler meddelanden för att låsa upp övning",
+ "constructUseCorGCDesc": "Övning i korrekt grammatikkategori",
+ "constructUseIncGCDesc": "Övning i inkorrekt grammatikkategori",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Korrekt grammatikfel övning",
+ "constructUseIncGEDesc": "Inkorrekt grammatikfel övning",
+ "fillInBlank": "Fyll i det tomma med rätt val",
+ "learn": "Lär dig",
+ "languageUpdated": "Målspråk uppdaterat!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot röst",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Din begäran har skickats till kursadministratören! Du kommer att släppas in om de godkänner.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Har du en inbjudningskod eller länk till en offentlig kurs?",
+ "welcomeUser": "Välkommen {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Sök efter användare för att bjuda in dem till den här chatten.",
+ "publicInviteDescSpace": "Sök efter användare för att bjuda in dem till det här utrymmet.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat är en meddelandeapp så aviseringar är viktiga!",
+ "enableNotificationsDesc": "Tillåt aviseringar",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Använd aktivitetsbild som chattbakgrund",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Chatta med support",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Som standard är kurser offentligt sökbara och kräver administratörsgodkännande för att gå med. Du kan redigera dessa inställningar när som helst.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Vilket språk lär du dig?",
+ "searchLanguagesHint": "Sök efter målspråk",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Frågor? Vi är här för att hjälpa till!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Något gick fel, och vi arbetar hårt för att åtgärda det. Kolla igen senare.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Aktivera skrivhjälp",
+ "autoIGCToolDescription": "Kör automatiskt Pangea Chat-verktyg för att korrigera skickade meddelanden till målspråket.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Inspelningen misslyckades. Kontrollera dina ljudbehörigheter och försök igen.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Idiom",
+ "grammarCopyPOSphrasalv": "Phrasverb",
+ "grammarCopyPOScompn": "Sammansatt",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_ta.arb b/lib/l10n/intl_ta.arb
index 22033e897..744131c01 100644
--- a/lib/l10n/intl_ta.arb
+++ b/lib/l10n/intl_ta.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:27:42.869267",
+ "@@last_modified": "2026-02-05 10:10:03.555298",
"acceptedTheInvitation": "👍 {username} அழைப்பை ஏற்றுக்கொண்டது",
"@acceptedTheInvitation": {
"type": "String",
@@ -3661,8 +3661,6 @@
"noPaymentInfo": "பணம் செலுத்தும் தகவல் தேவையில்லை!",
"updatePhoneOS": "உங்கள் சாதனத்தின் OS பதிப்பை புதுப்பிக்க வேண்டியிருக்கலாம்.",
"wordsPerMinute": "நிமிடத்திற்கு சொற்கள்",
- "autoIGCToolName": "பங்கேயா எழுத்து உதவியை தானாக இயக்கவும்",
- "autoIGCToolDescription": "எனது செய்தியை அனுப்புவதற்கு முன் தானாக பங்கேயா உரையாடல் இலக்கணம் மற்றும் மொழிபெயர்ப்பு எழுத்து உதவியை இயக்கவும்.",
"tooltipInstructionsTitle": "அது என்ன செய்கிறது என்று தெரியுமா?",
"tooltipInstructionsMobileBody": "உருப்படிகளை அழுத்தி வைத்திருங்கள், கருவி விளக்கங்களைப் பார்க்க.",
"tooltipInstructionsBrowserBody": "உருப்படிகளின் மேல் கரைசல் வைத்து கருவி விளக்கங்களைப் பார்க்க.",
@@ -4290,7 +4288,6 @@
"numModules": "{num} பகுதிகள்",
"coursePlan": "பாட திட்டம்",
"editCourseLater": "பின்னர் நீங்கள் மாதிரி தலைப்பு, விளக்கங்கள் மற்றும் பாடம் படத்தைத் திருத்தலாம்.",
- "newCourseAccess": "இயல்பாக, பாடங்கள் தனிப்பட்டவை மற்றும் சேர்க்க நிர்வாகத்தின் ஒப்புதலை தேவைபடுகின்றன. நீங்கள் எப்போது வேண்டுமானாலும் இந்த அமைப்புகளை மாற்றலாம்.",
"createCourse": "பாடத்தைக் கற்பிக்கவும்",
"stats": "புள்ளிவிவரங்கள்",
"createGroupChat": "குழு உரையாடலை உருவாக்கவும்",
@@ -6430,14 +6427,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9056,10 +9045,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11049,5 +11034,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 நீங்கள் உரையாடலை விட்டுவிட்டீர்கள்",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "பதிவிறக்கம் தொடங்கப்பட்டது",
+ "webDownloadPermissionMessage": "உங்கள் உலாவி பதிவிறக்கங்களை தடுக்கும் என்றால், தயவுசெய்து இந்த தளத்திற்கு பதிவிறக்கங்களை செயல்படுத்தவும்.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "உங்கள் பயிற்சி அமர்வின் முன்னேற்றம் சேமிக்கப்படாது.",
+ "practiceGrammar": "வியாசத்தை பயிற்சி செய்யவும்",
+ "notEnoughToPractice": "பயிற்சியை திறக்க மேலும் செய்திகளை அனுப்பவும்",
+ "constructUseCorGCDesc": "சரியான வியாச வகை பயிற்சி",
+ "constructUseIncGCDesc": "தவறான வியாச வகை பயிற்சி",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "சரியான இலக்கண பிழை பயிற்சி",
+ "constructUseIncGEDesc": "தவறான இலக்கண பிழை பயிற்சி",
+ "fillInBlank": "சரியான தேர்வுடன் காலியை நிரப்பவும்",
+ "learn": "கற்றுக்கொள்ளுங்கள்",
+ "languageUpdated": "இலக்கு மொழி புதுப்பிக்கப்பட்டது!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "பாஙேஆ பாட்டின் குரல்",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "உங்கள் கோரிக்கை பாடம் நிர்வாகிக்கு அனுப்பப்பட்டுள்ளது! அவர்கள் ஒப்புதலளித்தால் நீங்கள் உள்ளே அனுமதிக்கப்படுவீர்கள்.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "உங்களுக்கு ஒரு அழைப்பு குறியீடு அல்லது பொது பாடத்திற்கு இணைப்பு உள்ளதா?",
+ "welcomeUser": "வரவேற்கிறேன் {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "இந்த உரையாடலுக்கு அழைக்க பயனர்களை தேடுங்கள்.",
+ "publicInviteDescSpace": "இந்த இடத்திற்கு அழைக்க பயனர்களை தேடுங்கள்.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "பாஙேஆ சாட் என்பது ஒரு செய்தி அனுப்பும் செயலி ஆகும், எனவே அறிவிப்புகள் முக்கியமானவை!",
+ "enableNotificationsDesc": "அறிவிப்புகளை அனுமதிக்கவும்",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "செயல்பாட்டு படத்தை உரையாடல் பின்னணி ஆக பயன்படுத்தவும்",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "ஆதரவுடன் உரையாடவும்",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "இயல்பாக, பாடங்கள் பொதுவாக தேடக்கூடியவை மற்றும் சேர்வதற்கு நிர்வாகத்தின் அங்கீகாரம் தேவை. நீங்கள் எப்போது வேண்டுமானாலும் இந்த அமைப்புகளை திருத்தலாம்.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "நீங்கள் எது மொழி கற்றுக்கொள்கிறீர்கள்?",
+ "searchLanguagesHint": "இலக்கு மொழிகளை தேடுங்கள்",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "கேள்விகள்? நாங்கள் உதவ இங்கே இருக்கிறோம்!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "எதோ தவறு ஏற்பட்டது, அதை சரிசெய்ய நாங்கள் கடுமையாக வேலை செய்கிறோம். பின்னர் மீண்டும் சரிபார்க்கவும்.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "எழுத்து உதவியை செயல்படுத்தவும்",
+ "autoIGCToolDescription": "அனுப்பிய செய்திகளை இலக்கு மொழிக்கு சரிசெய்ய பாஙோ உரையாடல் கருவிகளை தானாகவே இயக்கவும்.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "பதிவு தோல்வியுற்றது. உங்கள் ஒலிப் அனுமதிகளை சரிபார்க்கவும் மற்றும் மீண்டும் முயற்சிக்கவும்.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "விளக்கம்",
+ "grammarCopyPOSphrasalv": "பொருள் வினை",
+ "grammarCopyPOScompn": "சேர்க்கை",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_te.arb b/lib/l10n/intl_te.arb
index 64aa04147..e1f8f030c 100644
--- a/lib/l10n/intl_te.arb
+++ b/lib/l10n/intl_te.arb
@@ -1196,8 +1196,6 @@
"noPaymentInfo": "చెల్లింపు సమాచారం అవసరం లేదు!",
"updatePhoneOS": "మీ డివైస్ యొక్క OS వెర్షన్ను నవీకరించాల్సి ఉండవచ్చు.",
"wordsPerMinute": "నిమిషానికి పదాలు",
- "autoIGCToolName": "పాంజియా రాయడం సహాయాన్ని స్వయంచాలకంగా నడపండి",
- "autoIGCToolDescription": "నా సందేశాన్ని పంపేముందు స్వయంచాలకంగా పాంజియా చాట్ వ్యాకరణం మరియు అనువాద రాయడం సహాయాన్ని నడపండి.",
"tooltipInstructionsTitle": "అది ఏమిటో తెలియదా?",
"tooltipInstructionsMobileBody": "టూల్టిప్లను చూడటానికి అంశాలను నొక్కి ఉంచండి.",
"tooltipInstructionsBrowserBody": "టూల్టిప్లను చూడటానికి అంశాలపై హోవర్ చేయండి.",
@@ -1825,7 +1823,6 @@
"numModules": "{num} మాడ్యూల్స్",
"coursePlan": "కోర్సు ప్రణాళిక",
"editCourseLater": "మీరు టెంప్లేట్ శీర్షిక, వివరణలు, మరియు కోర్సు చిత్రాన్ని తర్వాత సవరించవచ్చు.",
- "newCourseAccess": "డిఫాల్ట్గా, కోర్సులు ప్రైవేట్గా ఉంటాయి మరియు చేరడానికి అడ్మిన్ ఆమోదం అవసరం. మీరు ఈ సెట్టింగులను ఎప్పుడైనా సవరించవచ్చు.",
"createCourse": "కోర్సు సృష్టించండి",
"stats": "గణాంకాలు",
"createGroupChat": "గుంపు చాట్ సృష్టించండి",
@@ -1920,7 +1917,7 @@
"playWithAI": "ఇప్పుడే AI తో ఆడండి",
"courseStartDesc": "పాంజియా బాట్ ఎప్పుడైనా సిద్ధంగా ఉంటుంది!\n\n...కానీ స్నేహితులతో నేర్చుకోవడం మెరుగైనది!",
"@@locale": "te",
- "@@last_modified": "2026-01-07 14:27:24.330788",
+ "@@last_modified": "2026-02-05 10:09:59.064928",
"@setCustomPermissionLevel": {
"type": "String",
"placeholders": {}
@@ -7298,14 +7295,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9921,10 +9910,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11914,5 +11899,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 మీరు చాట్ను విడిచారు",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "డౌన్లోడ్ ప్రారంభించబడింది",
+ "webDownloadPermissionMessage": "మీ బ్రౌజర్ డౌన్లోడ్లను అడ్డిస్తే, దయచేసి ఈ సైట్ కోసం డౌన్లోడ్లను ప్రారంభించండి.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "మీ ప్రాక్టీస్ సెషన్ పురోగతి సేవ్ చేయబడదు.",
+ "practiceGrammar": "వ్యాకరణాన్ని అభ్యాసం చేయండి",
+ "notEnoughToPractice": "ప్రాక్టీస్ను అన్లాక్ చేయడానికి మరింత సందేశాలు పంపండి",
+ "constructUseCorGCDesc": "సరైన వ్యాకరణ శ్రేణి ప్రాక్టీస్",
+ "constructUseIncGCDesc": "తప్పు వ్యాకరణ శ్రేణి ప్రాక్టీస్",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "సరైన వ్యాకరణ దోషం అభ్యాసం",
+ "constructUseIncGEDesc": "తప్పు వ్యాకరణ దోషం అభ్యాసం",
+ "fillInBlank": "సరైన ఎంపికతో ఖాళీని నింపండి",
+ "learn": "కలవు",
+ "languageUpdated": "లక్ష్య భాష నవీకరించబడింది!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "పాంజియా బాట్ శబ్దం",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "మీ అభ్యర్థన కోర్సు నిర్వాహకుడికి పంపబడింది! వారు ఆమోదిస్తే, మీరు లోపలికి రానున్నారు.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "మీకు పబ్లిక్ కోర్సుకు ఆహ్వాన కోడ్ లేదా లింక్ ఉందా?",
+ "welcomeUser": "స్వాగతం {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "ఈ చాట్లో ఆహ్వానించడానికి వినియోగదారులను శోధించండి.",
+ "publicInviteDescSpace": "ఈ స్థలంలో ఆహ్వానించడానికి వినియోగదారులను శోధించండి.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "పాంజియా చాట్ ఒక సందేశం యాప్ కాబట్టి నోటిఫికేషన్లు ముఖ్యమైనవి!",
+ "enableNotificationsDesc": "నోటిఫికేషన్లను అనుమతించండి",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "చాట్ నేపథ్యంగా కార్యకలాప చిత్రాన్ని ఉపయోగించండి",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "సహాయంతో చాట్ చేయండి",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "డిఫాల్ట్గా, కోర్సులు ప్రజా శోధనకు అందుబాటులో ఉంటాయి మరియు చేరడానికి అడ్మిన్ ఆమోదం అవసరం. మీరు ఈ సెట్టింగులను ఎప్పుడైనా సవరించవచ్చు.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "మీరు ఏ భాష నేర్చుకుంటున్నారు?",
+ "searchLanguagesHint": "లక్ష్య భాషలను శోధించండి",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "ప్రశ్నలు? మేము మీకు సహాయం చేయడానికి ఇక్కడ ఉన్నాము!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "ఏదో తప్పు జరిగింది, మరియు మేము దీన్ని సరిదిద్దడానికి కష్టపడుతున్నాము. తర్వాత మళ్లీ తనిఖీ చేయండి.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "రాయడం సహాయాన్ని ప్రారంభించండి",
+ "autoIGCToolDescription": "సమర్పించిన సందేశాలను లక్ష్య భాషకు సరిదిద్దడానికి పాంజియా చాట్ సాధనాలను ఆటోమేటిక్గా నడపండి.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "రికార్డింగ్ విఫలమైంది. దయచేసి మీ ఆడియో అనుమతులను తనిఖీ చేసి మళ్లీ ప్రయత్నించండి.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "సామెత",
+ "grammarCopyPOSphrasalv": "పదబంధ క్రియ",
+ "grammarCopyPOScompn": "సంకలనం",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_th.arb b/lib/l10n/intl_th.arb
index d810730a4..2f5e88895 100644
--- a/lib/l10n/intl_th.arb
+++ b/lib/l10n/intl_th.arb
@@ -3732,8 +3732,6 @@
"noPaymentInfo": "ไม่จำเป็นต้องมีข้อมูลการชำระเงิน!",
"updatePhoneOS": "คุณอาจจำเป็นต้องอัปเดตเวอร์ชันระบบปฏิบัติการของอุปกรณ์ของคุณ",
"wordsPerMinute": "คำต่อนาที",
- "autoIGCToolName": "เรียกใช้เครื่องมือช่วยเขียน Pangea อัตโนมัติ",
- "autoIGCToolDescription": "เรียกใช้การช่วยเขียนไวยากรณ์และการแปลของ Pangea Chat อัตโนมัติก่อนส่งข้อความของฉัน",
"tooltipInstructionsTitle": "ไม่แน่ใจว่าสิ่งนั้นทำอะไร?",
"tooltipInstructionsMobileBody": "กดค้างไว้เพื่อดูคำแนะนำเครื่องมือ",
"tooltipInstructionsBrowserBody": "วางเมาส์เหนือรายการเพื่อดูคำแนะนำเครื่องมือ",
@@ -4361,7 +4359,6 @@
"numModules": "{num} โมดูล",
"coursePlan": "แผนหลักสูตร",
"editCourseLater": "คุณสามารถแก้ไขชื่อเทมเพลต คำอธิบาย และภาพหลักสูตรในภายหลัง",
- "newCourseAccess": "โดยค่าเริ่มต้น หลักสูตรเป็นส่วนตัวและต้องได้รับการอนุมัติจากผู้ดูแลระบบเพื่อเข้าร่วม คุณสามารถแก้ไขการตั้งค่าเหล่านี้ได้ทุกเมื่อ",
"createCourse": "สร้างหลักสูตร",
"stats": "สถิติ",
"createGroupChat": "สร้างกลุ่มแชท",
@@ -4456,7 +4453,7 @@
"playWithAI": "เล่นกับ AI ชั่วคราว",
"courseStartDesc": "Pangea Bot พร้อมที่จะเริ่มต้นได้ทุกเมื่อ!\n\n...แต่การเรียนรู้ดีกว่ากับเพื่อน!",
"@@locale": "th",
- "@@last_modified": "2026-01-07 14:26:38.383329",
+ "@@last_modified": "2026-02-05 10:09:49.236652",
"@alwaysUse24HourFormat": {
"type": "String",
"placeholders": {}
@@ -7264,14 +7261,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9890,10 +9879,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11883,5 +11868,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 คุณออกจากการสนทนา",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "เริ่มการดาวน์โหลด",
+ "webDownloadPermissionMessage": "หากเบราว์เซอร์ของคุณบล็อกการดาวน์โหลด โปรดเปิดใช้งานการดาวน์โหลดสำหรับเว็บไซต์นี้.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "ความก้าวหน้าของการฝึกฝนของคุณจะไม่ถูกบันทึก",
+ "practiceGrammar": "ฝึกไวยากรณ์",
+ "notEnoughToPractice": "ส่งข้อความเพิ่มเติมเพื่อปลดล็อกการฝึกฝน",
+ "constructUseCorGCDesc": "การฝึกไวยากรณ์หมวดหมู่ที่ถูกต้อง",
+ "constructUseIncGCDesc": "การฝึกไวยากรณ์หมวดหมู่ที่ไม่ถูกต้อง",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "การฝึกฝนข้อผิดพลาดทางไวยากรณ์ที่ถูกต้อง",
+ "constructUseIncGEDesc": "การฝึกฝนข้อผิดพลาดทางไวยากรณ์ที่ไม่ถูกต้อง",
+ "fillInBlank": "กรอกข้อมูลในช่องว่างด้วยตัวเลือกที่ถูกต้อง",
+ "learn": "เรียนรู้",
+ "languageUpdated": "อัปเดตภาษาที่ต้องการแล้ว!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "เสียงของ Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "คำขอของคุณได้ถูกส่งไปยังผู้ดูแลหลักสูตรแล้ว! คุณจะได้รับอนุญาตให้เข้าหากพวกเขาอนุมัติ.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "คุณมีรหัสเชิญหรือลิงก์ไปยังหลักสูตรสาธารณะหรือไม่?",
+ "welcomeUser": "ยินดีต้อนรับ {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "ค้นหาผู้ใช้เพื่อนำไปเชิญเข้าร่วมแชทนี้。",
+ "publicInviteDescSpace": "ค้นหาผู้ใช้เพื่อนำไปเชิญเข้าร่วมพื้นที่นี้。",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat เป็นแอปส่งข้อความ ดังนั้นการแจ้งเตือนจึงสำคัญ!",
+ "enableNotificationsDesc": "อนุญาตการแจ้งเตือน",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "ใช้ภาพกิจกรรมเป็นพื้นหลังแชท",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "แชทกับฝ่ายสนับสนุน",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "ตามค่าเริ่มต้น หลักสูตรจะสามารถค้นหาได้สาธารณะและต้องการการอนุมัติจากผู้ดูแลระบบเพื่อเข้าร่วม คุณสามารถแก้ไขการตั้งค่าเหล่านี้ได้ตลอดเวลา",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "คุณกำลังเรียนภาษาอะไรอยู่?",
+ "searchLanguagesHint": "ค้นหาภาษาที่ต้องการ",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "มีคำถามไหม? เราพร้อมที่จะช่วยเหลือ!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "มีบางอย่างผิดพลาด และเรากำลังทำงานอย่างหนักเพื่อแก้ไข ตรวจสอบอีกครั้งในภายหลัง.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "เปิดใช้งานความช่วยเหลือในการเขียน",
+ "autoIGCToolDescription": "เรียกใช้เครื่องมือ Pangea Chat โดยอัตโนมัติเพื่อแก้ไขข้อความที่ส่งไปยังภาษาที่ต้องการ.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "การบันทึกล้มเหลว โปรดตรวจสอบสิทธิ์เสียงของคุณและลองอีกครั้ง",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "สำนวน",
+ "grammarCopyPOSphrasalv": "กริยาวลี",
+ "grammarCopyPOScompn": "คำผสม",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb
index 392af0e8a..3df573555 100644
--- a/lib/l10n/intl_tr.arb
+++ b/lib/l10n/intl_tr.arb
@@ -1,6 +1,6 @@
{
"@@locale": "tr",
- "@@last_modified": "2026-01-07 14:27:16.503973",
+ "@@last_modified": "2026-02-05 10:09:57.710087",
"about": "Hakkında",
"@about": {
"type": "String",
@@ -3668,8 +3668,6 @@
"noPaymentInfo": "Ödeme bilgisi gerekmez!",
"updatePhoneOS": "Cihazınızın işletim sistemi sürümünü güncellemeniz gerekebilir.",
"wordsPerMinute": "Dakikada kelime",
- "autoIGCToolName": "Pangea yazma yardımını otomatik çalıştır",
- "autoIGCToolDescription": "Mesajımı göndermeden önce Pangea Sohbet dilbilgisi ve çeviri yazma yardımını otomatik olarak çalıştır.",
"tooltipInstructionsTitle": "Bu ne işe yarar bilmiyor musun?",
"tooltipInstructionsMobileBody": "Öğe üzerine basılı tutarak ipuçlarını görüntüleyin.",
"tooltipInstructionsBrowserBody": "İpuçlarını görüntülemek için öğeler üzerinde fare ile durun.",
@@ -4297,7 +4295,6 @@
"numModules": "{num} modül",
"coursePlan": "Kurs Planı",
"editCourseLater": "Şablon başlığı, açıklamalar ve kurs resmi daha sonra düzenleyebilirsiniz.",
- "newCourseAccess": "Varsayılan olarak, kurslar özeldir ve katılmak için yönetici onayı gerekir. Bu ayarları istediğiniz zaman değiştirebilirsiniz.",
"createCourse": "Kurs Oluştur",
"stats": "İstatistikler",
"createGroupChat": "Grup sohbeti oluştur",
@@ -6428,14 +6425,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9054,10 +9043,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11047,5 +11032,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Sohbeti terk ettin",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "İndirme başlatıldı",
+ "webDownloadPermissionMessage": "Tarayıcınız indirmeleri engelliyorsa, lütfen bu site için indirmeleri etkinleştirin.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Pratik oturumunuzun ilerlemesi kaydedilmeyecek.",
+ "practiceGrammar": "Dil bilgisi pratiği yap",
+ "notEnoughToPractice": "Pratik yapmak için daha fazla mesaj gönderin",
+ "constructUseCorGCDesc": "Doğru dil bilgisi kategorisi pratiği",
+ "constructUseIncGCDesc": "Yanlış dil bilgisi kategorisi pratiği",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Doğru dil bilgisi hatası pratiği",
+ "constructUseIncGEDesc": "Yanlış dil bilgisi hatası pratiği",
+ "fillInBlank": "Boşluğu doğru seçimle doldurun",
+ "learn": "Öğren",
+ "languageUpdated": "Hedef dil güncellendi!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot sesi",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Talebiniz kurs yöneticisine gönderildi! Onaylarlarsa içeri alınacaksınız.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Bir davet kodunuz veya halka açık bir kursa bağlantınız var mı?",
+ "welcomeUser": "Hoş geldin {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Bu sohbete davet etmek için kullanıcıları arayın.",
+ "publicInviteDescSpace": "Bu alana davet etmek için kullanıcıları arayın.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat, bir mesajlaşma uygulamasıdır, bu yüzden bildirimler önemlidir!",
+ "enableNotificationsDesc": "Bildirimlere izin ver",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Etkinlik resmini sohbet arka planı olarak kullan",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Destek ile Sohbet Et",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Varsayılan olarak, kurslar herkese açık olarak aranabilir ve katılmak için yönetici onayı gerektirir. Bu ayarları istediğiniz zaman düzenleyebilirsiniz.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Hangi dili öğreniyorsunuz?",
+ "searchLanguagesHint": "Hedef dilleri arayın",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Sorular mı? Yardımcı olmaya buradayız!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Bir şeyler yanlış gitti ve biz bunu düzeltmek için yoğun bir şekilde çalışıyoruz. Lütfen daha sonra tekrar kontrol edin.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Yazma yardımını etkinleştir",
+ "autoIGCToolDescription": "Gönderilen mesajları hedef dile düzeltmek için Pangea Chat araçlarını otomatik olarak çalıştır.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Kayıt başarısız oldu. Lütfen ses izinlerinizi kontrol edin ve tekrar deneyin.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Deyim",
+ "grammarCopyPOSphrasalv": "Deyim Fiili",
+ "grammarCopyPOScompn": "Bileşik",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_uk.arb b/lib/l10n/intl_uk.arb
index 9b8d546e5..d4d7de8bb 100644
--- a/lib/l10n/intl_uk.arb
+++ b/lib/l10n/intl_uk.arb
@@ -1,6 +1,6 @@
{
"@@locale": "uk",
- "@@last_modified": "2026-01-07 14:25:54.221263",
+ "@@last_modified": "2026-02-05 10:09:42.549877",
"about": "Про застосунок",
"@about": {
"type": "String",
@@ -3787,8 +3787,6 @@
"noPaymentInfo": "Інформація про оплату не потрібна!",
"updatePhoneOS": "Можливо, потрібно оновити версію ОС вашого пристрою.",
"wordsPerMinute": "Слів за хвилину",
- "autoIGCToolName": "Автоматично запускати допомогу з написання Pangea",
- "autoIGCToolDescription": "Автоматично запускати допомогу з граматики та перекладу в чаті Pangea перед відправкою мого повідомлення.",
"tooltipInstructionsTitle": "Не впевнений, що це робить?",
"tooltipInstructionsMobileBody": "Натисніть і утримуйте елементи, щоб переглянути підказки.",
"tooltipInstructionsBrowserBody": "Наведіть курсор на елементи, щоб переглянути підказки.",
@@ -4416,7 +4414,6 @@
"numModules": "{num} модулів",
"coursePlan": "План курсу",
"editCourseLater": "Ви можете редагувати назву шаблону, описи та зображення курсу пізніше.",
- "newCourseAccess": "За замовчуванням курси є приватними і потребують схвалення адміністратора для приєднання. Ви можете редагувати ці налаштування в будь-який час.",
"createCourse": "Створити курс",
"stats": "Статистика",
"createGroupChat": "Створити груповий чат",
@@ -6200,14 +6197,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8826,10 +8815,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10819,5 +10804,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Ви вийшли з чату",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Завантаження розпочато",
+ "webDownloadPermissionMessage": "Якщо ваш браузер блокує завантаження, будь ласка, увімкніть завантаження для цього сайту.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Ваш прогрес у сесії практики не буде збережено.",
+ "practiceGrammar": "Практика граматики",
+ "notEnoughToPractice": "Надішліть більше повідомлень, щоб розблокувати практику",
+ "constructUseCorGCDesc": "Практика правильної граматичної категорії",
+ "constructUseIncGCDesc": "Практика неправильної граматичної категорії",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Практика виправлення граматичних помилок",
+ "constructUseIncGEDesc": "Практика неправильних граматичних помилок",
+ "fillInBlank": "Заповніть пропуск правильним вибором",
+ "learn": "Вчити",
+ "languageUpdated": "Цільова мова оновлена!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Голос Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Ваш запит надіслано адміністратору курсу! Ви будете допущені, якщо вони схвалять.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "У вас є код запрошення або посилання на публічний курс?",
+ "welcomeUser": "Ласкаво просимо {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Шукайте користувачів, щоб запросити їх до цього чату.",
+ "publicInviteDescSpace": "Шукайте користувачів, щоб запросити їх до цього простору.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat - це додаток для обміну повідомленнями, тому сповіщення важливі!",
+ "enableNotificationsDesc": "Дозволити сповіщення",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Використовувати зображення активності як фон чату",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Чат з підтримкою",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "За замовчуванням курси є загальнодоступними для пошуку і вимагають схвалення адміністратора для приєднання. Ви можете редагувати ці налаштування в будь-який час.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Яку мову ви вивчаєте?",
+ "searchLanguagesHint": "Шукати цільові мови",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Питання? Ми тут, щоб допомогти!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Щось пішло не так, і ми наполегливо працюємо над виправленням. Перевірте пізніше.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Увімкнути допомогу в написанні",
+ "autoIGCToolDescription": "Автоматично запускати інструменти Pangea Chat для виправлення надісланих повідомлень на цільову мову.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Запис не вдався. Будь ласка, перевірте свої аудіоправа та спробуйте ще раз.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Ідіома",
+ "grammarCopyPOSphrasalv": "Фразове дієслово",
+ "grammarCopyPOScompn": "Складене",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_vi.arb b/lib/l10n/intl_vi.arb
index 77ad1ad05..7c8c9bc73 100644
--- a/lib/l10n/intl_vi.arb
+++ b/lib/l10n/intl_vi.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:27:35.989013",
+ "@@last_modified": "2026-02-05 10:10:02.295528",
"about": "Giới thiệu",
"@about": {
"type": "String",
@@ -2422,8 +2422,6 @@
"noPaymentInfo": "Không cần thông tin thanh toán!",
"updatePhoneOS": "Bạn có thể cần nâng cấp phiên bản hệ điều hành.",
"wordsPerMinute": "Từ mỗi phút",
- "autoIGCToolName": "Tự động chạy hỗ trợ ngôn ngữ",
- "autoIGCToolDescription": "Tự động chạy hỗ trợ ngôn ngữ sau khi gõ tin nhắn",
"chatCapacity": "Giới hạn thành viên trò chuyện",
"roomFull": "Phòng đã đạt giới hạn.",
"chatCapacityHasBeenChanged": "Giới hạn thành viên trò chuyện đã thay đổi",
@@ -3979,7 +3977,6 @@
"numModules": "{num} mô-đun",
"coursePlan": "Kế hoạch khóa học",
"editCourseLater": "Bạn có thể chỉnh sửa tiêu đề mẫu, mô tả và hình ảnh khóa học sau.",
- "newCourseAccess": "Theo mặc định, các khóa học là riêng tư và yêu cầu sự chấp thuận của quản trị viên để tham gia. Bạn có thể chỉnh sửa các cài đặt này bất cứ lúc nào.",
"createCourse": "Tạo khóa học",
"stats": "Thống kê",
"createGroupChat": "Tạo nhóm trò chuyện",
@@ -4369,10 +4366,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -6395,5 +6388,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 Bạn đã rời khỏi cuộc trò chuyện",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "Tải xuống đã được khởi động",
+ "webDownloadPermissionMessage": "Nếu trình duyệt của bạn chặn tải xuống, vui lòng bật tải xuống cho trang web này.",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "Tiến trình phiên thực hành của bạn sẽ không được lưu.",
+ "practiceGrammar": "Thực hành ngữ pháp",
+ "notEnoughToPractice": "Gửi thêm tin nhắn để mở khóa thực hành",
+ "constructUseCorGCDesc": "Thực hành thể loại ngữ pháp đúng",
+ "constructUseIncGCDesc": "Thực hành thể loại ngữ pháp sai",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "Thực hành lỗi ngữ pháp đúng",
+ "constructUseIncGEDesc": "Thực hành lỗi ngữ pháp sai",
+ "fillInBlank": "Điền vào chỗ trống với lựa chọn đúng",
+ "learn": "Học",
+ "languageUpdated": "Ngôn ngữ mục tiêu đã được cập nhật!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Giọng nói của Pangea Bot",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "Yêu cầu của bạn đã được gửi đến quản trị viên khóa học! Bạn sẽ được cho vào nếu họ chấp thuận.",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "Bạn có mã mời hoặc liên kết đến một khóa học công khai không?",
+ "welcomeUser": "Chào mừng {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "Tìm kiếm người dùng để mời họ tham gia trò chuyện này.",
+ "publicInviteDescSpace": "Tìm kiếm người dùng để mời họ tham gia không gian này.",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat là một ứng dụng nhắn tin nên thông báo là rất quan trọng!",
+ "enableNotificationsDesc": "Cho phép thông báo",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "Sử dụng hình ảnh hoạt động làm nền trò chuyện",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "Trò chuyện với Hỗ trợ",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "Theo mặc định, các khóa học có thể tìm kiếm công khai và yêu cầu sự chấp thuận của quản trị viên để tham gia. Bạn có thể chỉnh sửa các cài đặt này bất kỳ lúc nào.",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "Bạn đang học ngôn ngữ nào?",
+ "searchLanguagesHint": "Tìm kiếm ngôn ngữ mục tiêu",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "Câu hỏi? Chúng tôi ở đây để giúp đỡ!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "Đã xảy ra sự cố, và chúng tôi đang nỗ lực khắc phục. Vui lòng kiểm tra lại sau.",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "Bật trợ giúp viết",
+ "autoIGCToolDescription": "Tự động chạy các công cụ Pangea Chat để sửa các tin nhắn đã gửi sang ngôn ngữ mục tiêu.",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "Ghi âm không thành công. Vui lòng kiểm tra quyền truy cập âm thanh của bạn và thử lại.",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "Thành ngữ",
+ "grammarCopyPOSphrasalv": "Động từ cụm",
+ "grammarCopyPOScompn": "Hợp chất",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_yue.arb b/lib/l10n/intl_yue.arb
index 8684f06a8..410706220 100644
--- a/lib/l10n/intl_yue.arb
+++ b/lib/l10n/intl_yue.arb
@@ -1119,8 +1119,6 @@
"noPaymentInfo": "無需付款資料!",
"updatePhoneOS": "您可能需要更新設備的操作系統版本。",
"wordsPerMinute": "每分鐘字數",
- "autoIGCToolName": "自動運行Pangea寫作協助",
- "autoIGCToolDescription": "在發送消息前,自動運行Pangea聊天語法和翻譯寫作協助。",
"tooltipInstructionsTitle": "不確定那是什麼嗎?",
"tooltipInstructionsMobileBody": "長按項目以查看工具提示。",
"tooltipInstructionsBrowserBody": "將滑鼠懸停在項目上以查看工具提示。",
@@ -1749,7 +1747,6 @@
"numModules": "{num} 個模組",
"coursePlan": "課程計劃",
"editCourseLater": "你可以稍後編輯模板標題、描述同課程圖片。",
- "newCourseAccess": "預設情況下,課程係私密嘅,需要管理員批准先可以加入。你可以隨時修改呢啲設定。",
"createCourse": "建立課程",
"stats": "統計數據",
"createGroupChat": "建立群組聊天",
@@ -1856,7 +1853,7 @@
"selectAll": "全選",
"deselectAll": "取消全選",
"@@locale": "yue",
- "@@last_modified": "2026-01-07 14:25:37.722488",
+ "@@last_modified": "2026-02-05 10:09:39.916672",
"@ignoreUser": {
"type": "String",
"placeholders": {}
@@ -6899,14 +6896,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -9521,10 +9510,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -11916,5 +11901,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 你已離開聊天",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "下載已啟動",
+ "webDownloadPermissionMessage": "如果你的瀏覽器阻止下載,請為此網站啟用下載。",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "您的練習進度將不會被保存。",
+ "practiceGrammar": "練習語法",
+ "notEnoughToPractice": "發送更多消息以解鎖練習",
+ "constructUseCorGCDesc": "正確語法類別練習",
+ "constructUseIncGCDesc": "不正確語法類別練習",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "正確語法錯誤練習",
+ "constructUseIncGEDesc": "不正確語法錯誤練習",
+ "fillInBlank": "用正確的選擇填空",
+ "learn": "學習",
+ "languageUpdated": "目標語言已更新!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot 聲音",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "你的請求已經發送給課程管理員!如果他們批准,你將被允許進入。",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "你有邀請碼或公共課程的鏈接嗎?",
+ "welcomeUser": "歡迎 {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "搜尋用戶以邀請他們加入此聊天。",
+ "publicInviteDescSpace": "搜尋用戶以邀請他們加入此空間。",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat 係一個短信應用程式,所以通知非常重要!",
+ "enableNotificationsDesc": "允許通知",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "使用活動圖片作為聊天背景",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "與支援聊天",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "默認情況下,課程是公開可搜索的,並且需要管理員批准才能加入。您可以隨時編輯這些設置。",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "你正在學習什麼語言?",
+ "searchLanguagesHint": "搜尋目標語言",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "有問題嗎?我們在這裡幫助你!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "發生了一些問題,我們正在努力修復。稍後再檢查。",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "啟用寫作輔助",
+ "autoIGCToolDescription": "自動運行 Pangea Chat 工具以將發送的消息更正為目標語言。",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "錄音失敗。請檢查您的音頻權限並重試。",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "成語",
+ "grammarCopyPOSphrasalv": "短語動詞",
+ "grammarCopyPOScompn": "複合詞",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb
index 401c2b283..e141c5b19 100644
--- a/lib/l10n/intl_zh.arb
+++ b/lib/l10n/intl_zh.arb
@@ -1,6 +1,6 @@
{
"@@locale": "zh",
- "@@last_modified": "2026-01-07 14:27:59.550670",
+ "@@last_modified": "2026-02-05 10:10:07.531332",
"about": "关于",
"@about": {
"type": "String",
@@ -3788,8 +3788,6 @@
"noPaymentInfo": "无需支付信息!",
"updatePhoneOS": "您可能需要更新设备的操作系统版本。",
"wordsPerMinute": "每分钟字数",
- "autoIGCToolName": "自动运行Pangea写作辅助",
- "autoIGCToolDescription": "在发送消息前自动运行Pangea聊天语法和翻译写作辅助。",
"tooltipInstructionsTitle": "不确定它的作用吗?",
"tooltipInstructionsMobileBody": "长按项目以查看工具提示。",
"tooltipInstructionsBrowserBody": "将鼠标悬停在项目上以查看工具提示。",
@@ -4417,7 +4415,6 @@
"numModules": "{num} 个模块",
"coursePlan": "课程计划",
"editCourseLater": "您可以稍后编辑模板标题、描述和课程图片。",
- "newCourseAccess": "默认情况下,课程是私有的,需要管理员批准才能加入。您可以随时编辑这些设置。",
"createCourse": "创建课程",
"stats": "统计",
"createGroupChat": "创建群聊",
@@ -6197,14 +6194,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8823,10 +8812,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10816,5 +10801,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 你离开了聊天",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "下载已启动",
+ "webDownloadPermissionMessage": "如果您的浏览器阻止下载,请为此网站启用下载。",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "您的练习会话进度将不会被保存。",
+ "practiceGrammar": "练习语法",
+ "notEnoughToPractice": "发送更多消息以解锁练习",
+ "constructUseCorGCDesc": "正确语法类别练习",
+ "constructUseIncGCDesc": "错误语法类别练习",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "正确语法错误练习",
+ "constructUseIncGEDesc": "不正确语法错误练习",
+ "fillInBlank": "用正确的选项填空",
+ "learn": "学习",
+ "languageUpdated": "目标语言已更新!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "潘吉亚机器人声音",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "您的请求已发送给课程管理员!如果他们批准,您将被允许进入。",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "您是否有邀请代码或公共课程的链接?",
+ "welcomeUser": "欢迎 {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "搜索用户以邀请他们加入此聊天。",
+ "publicInviteDescSpace": "搜索用户以邀请他们加入此空间。",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat 是一款短信应用,因此通知非常重要!",
+ "enableNotificationsDesc": "允许通知",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "将活动图像用作聊天背景",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "与支持聊天",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "默认情况下,课程是公开可搜索的,并且需要管理员批准才能加入。您可以随时编辑这些设置。",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "你正在学习什么语言?",
+ "searchLanguagesHint": "搜索目标语言",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "有问题吗?我们在这里帮助您!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "出现了一些问题,我们正在努力修复。请稍后再检查。",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "启用写作辅助",
+ "autoIGCToolDescription": "自动运行 Pangea Chat 工具以将发送的消息纠正为目标语言。",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "录音失败。请检查您的音频权限并重试。",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "成语",
+ "grammarCopyPOSphrasalv": "短语动词",
+ "grammarCopyPOScompn": "复合词",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/l10n/intl_zh_Hant.arb b/lib/l10n/intl_zh_Hant.arb
index 3ee19351a..0aea18e73 100644
--- a/lib/l10n/intl_zh_Hant.arb
+++ b/lib/l10n/intl_zh_Hant.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2026-01-07 14:26:51.046421",
+ "@@last_modified": "2026-02-05 10:09:52.100652",
"about": "關於",
"@about": {
"type": "String",
@@ -3764,8 +3764,6 @@
"noPaymentInfo": "無需付款資訊!",
"updatePhoneOS": "您可能需要更新設備的作業系統版本。",
"wordsPerMinute": "每分鐘字數",
- "autoIGCToolName": "自動運行Pangea寫作協助",
- "autoIGCToolDescription": "在發送訊息前,自動運行Pangea聊天語法和翻譯寫作協助。",
"tooltipInstructionsTitle": "不確定這是做什麼的嗎?",
"tooltipInstructionsMobileBody": "長按項目以查看工具提示。",
"tooltipInstructionsBrowserBody": "將滑鼠懸停在項目上以查看工具提示。",
@@ -4393,7 +4391,6 @@
"numModules": "{num} 模組",
"coursePlan": "課程計劃",
"editCourseLater": "您可以稍後編輯課程標題、描述和課程圖片。",
- "newCourseAccess": "默認情況下,課程是私有的,需要管理員批准才能加入。您可以隨時編輯這些設置。",
"createCourse": "創建課程",
"stats": "統計數據",
"createGroupChat": "創建群聊",
@@ -6221,14 +6218,6 @@
"type": "String",
"placeholders": {}
},
- "@autoIGCToolName": {
- "type": "String",
- "placeholders": {}
- },
- "@autoIGCToolDescription": {
- "type": "String",
- "placeholders": {}
- },
"@tooltipInstructionsTitle": {
"type": "String",
"placeholders": {}
@@ -8847,10 +8836,6 @@
"type": "String",
"placeholders": {}
},
- "@newCourseAccess": {
- "type": "String",
- "placeholders": {}
- },
"@createCourse": {
"type": "String",
"placeholders": {}
@@ -10823,5 +10808,179 @@
"@voice": {
"type": "String",
"placeholders": {}
+ },
+ "youLeftTheChat": "🚪 你已離開聊天",
+ "@youLeftTheChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "downloadInitiated": "下載已啟動",
+ "webDownloadPermissionMessage": "如果您的瀏覽器阻止下載,請為此網站啟用下載。",
+ "@downloadInitiated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@webDownloadPermissionMessage": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "exitPractice": "您的練習進度將不會被保存。",
+ "practiceGrammar": "練習文法",
+ "notEnoughToPractice": "發送更多訊息以解鎖練習",
+ "constructUseCorGCDesc": "正確文法類別練習",
+ "constructUseIncGCDesc": "不正確文法類別練習",
+ "@exitPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@practiceGrammar": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@notEnoughToPractice": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseCorGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGCDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "constructUseCorGEDesc": "正確語法錯誤練習",
+ "constructUseIncGEDesc": "不正確語法錯誤練習",
+ "fillInBlank": "用正確的選擇填空",
+ "learn": "學習",
+ "languageUpdated": "目標語言已更新!",
+ "@constructUseCorGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@constructUseIncGEDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@fillInBlank": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@learn": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@languageUpdated": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "voiceDropdownTitle": "Pangea Bot 語音",
+ "@voiceDropdownTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "knockDesc": "您的請求已發送給課程管理員!如果他們批准,您將被允許進入。",
+ "@knockDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "joinSpaceOnboardingDesc": "您是否有邀請碼或公共課程的鏈接?",
+ "welcomeUser": "歡迎 {user}",
+ "@joinSpaceOnboardingDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@welcomeUser": {
+ "type": "String",
+ "placeholders": {
+ "user": {
+ "type": "String"
+ }
+ }
+ },
+ "publicInviteDescChat": "搜尋用戶以邀請他們加入此聊天。",
+ "publicInviteDescSpace": "搜尋用戶以邀請他們加入此空間。",
+ "@publicInviteDescChat": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@publicInviteDescSpace": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "enableNotificationsTitle": "Pangea Chat 是一個即時通訊應用程式,因此通知非常重要!",
+ "enableNotificationsDesc": "允許通知",
+ "@enableNotificationsTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@enableNotificationsDesc": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "useActivityImageAsChatBackground": "使用活動圖片作為聊天背景",
+ "@useActivityImageAsChatBackground": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "chatWithSupport": "與支援聊天",
+ "@chatWithSupport": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "newCourseAccess": "預設情況下,課程是公開可搜尋的,並且需要管理員批准才能加入。您可以隨時編輯這些設置。",
+ "@newCourseAccess": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "onboardingLanguagesTitle": "你正在學習什麼語言?",
+ "searchLanguagesHint": "搜尋目標語言",
+ "@onboardingLanguagesTitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@searchLanguagesHint": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "supportSubtitle": "有問題嗎?我們在這裡幫助您!",
+ "@supportSubtitle": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "courseLoadingError": "發生了一些問題,我們正在努力修復。稍後再檢查。",
+ "@courseLoadingError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "autoIGCToolName": "啟用寫作輔助",
+ "autoIGCToolDescription": "自動運行 Pangea Chat 工具以將發送的消息更正為目標語言。",
+ "@autoIGCToolName": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@autoIGCToolDescription": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "emptyAudioError": "錄音失敗。請檢查您的音頻權限並重試。",
+ "@emptyAudioError": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "grammarCopyPOSidiom": "成語",
+ "grammarCopyPOSphrasalv": "片語動詞",
+ "grammarCopyPOScompn": "合成詞",
+ "@grammarCopyPOSidiom": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOSphrasalv": {
+ "type": "String",
+ "placeholders": {}
+ },
+ "@grammarCopyPOScompn": {
+ "type": "String",
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart
index 38dc0583e..86d333ca1 100644
--- a/lib/pages/chat/chat.dart
+++ b/lib/pages/chat/chat.dart
@@ -32,7 +32,6 @@ import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activi
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_update_dispatcher.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_updater_mixin.dart';
-import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/level_up/level_up_banner.dart';
@@ -191,9 +190,12 @@ class ChatController extends State
StreamSubscription? _levelSubscription;
StreamSubscription? _constructsSubscription;
+ StreamSubscription? _tokensSubscription;
+
StreamSubscription? _botAudioSubscription;
final timelineUpdateNotifier = _TimelineUpdateNotifier();
late final ActivityChatController activityController;
+ final ValueNotifier scrollableNotifier = ValueNotifier(false);
// Pangea#
Room get room => sendingClient.getRoomById(roomId) ?? widget.room;
@@ -251,9 +253,14 @@ class ChatController extends State
final Set unfolded = {};
- Event? replyEvent;
+ // #Pangea
+ // Event? replyEvent;
- Event? editEvent;
+ // Event? editEvent;
+
+ ValueNotifier replyEvent = ValueNotifier(null);
+ ValueNotifier editEvent = ValueNotifier(null);
+ // Pangea#
bool _scrolledUp = false;
@@ -477,6 +484,11 @@ class ChatController extends State
);
}
+ void _onTokenUpdate(Set constructs) {
+ if (constructs.isEmpty) return;
+ TokensUtil.clearNewTokenCache();
+ }
+
Future _botAudioListener(SyncUpdate update) async {
if (update.rooms?.join?[roomId]?.timeline?.events == null) return;
final timeline = update.rooms!.join![roomId]!.timeline!;
@@ -490,6 +502,8 @@ class ChatController extends State
if (botAudioEvent == null) return;
final matrix = Matrix.of(context);
+ if (matrix.voiceMessageEventId.value != null) return;
+
matrix.voiceMessageEventId.value = botAudioEvent.eventId;
matrix.audioPlayer?.dispose();
matrix.audioPlayer = AudioPlayer();
@@ -527,6 +541,9 @@ class ChatController extends State
_constructsSubscription =
updater.unlockedConstructsStream.stream.listen(_onUnlockConstructs);
+ _tokensSubscription =
+ updater.newConstructsStream.stream.listen(_onTokenUpdate);
+
_botAudioSubscription = room.client.onSync.stream.listen(_botAudioListener);
activityController = ActivityChatController(
@@ -790,11 +807,13 @@ class ChatController extends State
_levelSubscription?.cancel();
_botAudioSubscription?.cancel();
_constructsSubscription?.cancel();
+ _tokensSubscription?.cancel();
_router.routeInformationProvider.removeListener(_onRouteChanged);
choreographer.timesDismissedIT.removeListener(_onCloseIT);
scrollController.dispose();
inputFocus.dispose();
depressMessageButton.dispose();
+ scrollableNotifier.dispose();
TokensUtil.clearNewTokenCache();
//Pangea#
super.dispose();
@@ -869,7 +888,6 @@ class ChatController extends State
Future sendFakeMessage(Event? edit, Event? reply) async {
if (sendController.text.trim().isEmpty) return null;
final message = sendController.text;
- inputFocus.unfocus();
sendController.setSystemText("", EditTypeEnum.other);
return room.sendFakeMessage(
@@ -885,13 +903,17 @@ class ChatController extends State
// Also, adding PangeaMessageData
Future send() async {
final message = sendController.text;
- final edit = editEvent;
- final reply = replyEvent;
- editEvent = null;
- replyEvent = null;
+ final edit = editEvent.value;
+ final reply = replyEvent.value;
+ editEvent.value = null;
+ replyEvent.value = null;
pendingText = '';
final tempEventId = await sendFakeMessage(edit, reply);
+ if (!inputFocus.hasFocus) {
+ inputFocus.requestFocus();
+ }
+
final content = await choreographer.getMessageContent(message);
choreographer.clear();
@@ -939,7 +961,10 @@ class ChatController extends State
sendController.setSystemText("", EditTypeEnum.other);
}
- final previousEdit = editEvent;
+ final previousEdit = edit;
+ if (showEmojiPicker) {
+ hideEmojiPicker();
+ }
room
.pangeaSendTextEvent(
@@ -991,8 +1016,8 @@ class ChatController extends State
data: {
'roomId': roomId,
'text': message,
- 'inReplyTo': replyEvent?.eventId,
- 'editEventId': editEvent?.eventId,
+ 'inReplyTo': reply?.eventId,
+ 'editEventId': edit?.eventId,
},
);
return;
@@ -1012,8 +1037,8 @@ class ChatController extends State
data: {
'roomId': roomId,
'text': message,
- 'inReplyTo': replyEvent?.eventId,
- 'editEventId': editEvent?.eventId,
+ 'inReplyTo': reply?.eventId,
+ 'editEventId': edit?.eventId,
},
);
});
@@ -1141,8 +1166,8 @@ class ChatController extends State
);
// #Pangea
- final reply = replyEvent;
- replyEvent = null;
+ final reply = replyEvent.value;
+ replyEvent.value = null;
// Pangea#
await room
@@ -1510,7 +1535,7 @@ class ChatController extends State
void replyAction({Event? replyTo}) {
// #Pangea
- replyEvent = replyTo ?? selectedEvents.first;
+ replyEvent.value = replyTo ?? selectedEvents.first;
clearSelectedEvents();
// setState(() {
// replyEvent = replyTo ?? selectedEvents.first;
@@ -1668,9 +1693,9 @@ class ChatController extends State
// selectedEvents.clear();
// });
pendingText = sendController.text;
- editEvent = selectedEvents.first;
+ editEvent.value = selectedEvents.first;
sendController.text =
- editEvent!.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
+ editEvent.value!.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)),
withSenderNamePrefix: false,
hideReply: true,
@@ -1952,15 +1977,17 @@ class ChatController extends State
}
void cancelReplyEventAction() => setState(() {
- if (editEvent != null) {
- // #Pangea
- // sendController.text = pendingText;
- sendController.setSystemText(pendingText, EditTypeEnum.other);
- // Pangea#
- pendingText = '';
- }
- replyEvent = null;
- editEvent = null;
+ // #Pangea
+ // sendController.text = pendingText;
+ sendController.setSystemText(pendingText, EditTypeEnum.other);
+ // Pangea#
+ pendingText = '';
+ // #Pangea
+ // replyEvent = null;
+ // editEvent = null;
+ replyEvent.value = null;
+ editEvent.value = null;
+ // Pangea#
});
// #Pangea
ValueNotifier depressMessageButton = ValueNotifier(false);
@@ -1997,17 +2024,6 @@ class ChatController extends State
bool get _isToolbarOpen =>
MatrixState.pAnyState.isOverlayOpen(RegExp(r'^message_toolbar_overlay$'));
- bool showMessageShimmer(Event event) {
- if (event.type != EventTypes.Message) return false;
- if (event.messageType == MessageTypes.Text) {
- return !InstructionsEnum.clickTextMessages.isToggledOff;
- }
- if (event.messageType == MessageTypes.Audio) {
- return !InstructionsEnum.clickAudioMessages.isToggledOff;
- }
- return false;
- }
-
void showToolbar(
Event event, {
PangeaMessageEvent? pangeaMessageEvent,
@@ -2015,14 +2031,9 @@ class ChatController extends State
MessagePracticeMode? mode,
Event? nextEvent,
Event? prevEvent,
- }) {
+ }) async {
if (event.redacted || event.status == EventStatus.sending) return;
- // Close keyboard, if open
- if (inputFocus.hasFocus && PlatformInfos.isMobile) {
- inputFocus.unfocus();
- return;
- }
// Close emoji picker, if open
if (showEmojiPicker) {
hideEmojiPicker();
@@ -2035,31 +2046,6 @@ class ChatController extends State
return;
}
- final langCode =
- pangeaMessageEvent?.originalSent?.langCode.split('-').first;
-
- if (LanguageMismatchRepo.shouldShowByEvent(event.eventId) &&
- langCode != null &&
- pangeaMessageEvent?.originalSent?.content.langCodeMatchesL2 == false &&
- room.client.allMyAnalyticsRooms.any((r) => r.madeForLang == langCode)) {
- LanguageMismatchRepo.setEvent(event.eventId);
- OverlayUtil.showLanguageMismatchPopup(
- context: context,
- targetId: event.eventId,
- message: L10n.of(context).messageLanguageMismatchMessage,
- targetLanguage: pangeaMessageEvent!.originalSent!.langCode,
- onConfirm: () => showToolbar(
- event,
- pangeaMessageEvent: pangeaMessageEvent,
- selectedToken: selectedToken,
- mode: mode,
- nextEvent: nextEvent,
- prevEvent: prevEvent,
- ),
- );
- return;
- }
-
final overlayEntry = MessageSelectionOverlay(
chatController: this,
event: event,
@@ -2070,14 +2056,8 @@ class ChatController extends State
);
// you've clicked a message so lets turn this off
- InstructionsEnum.clickMessage.setToggledOff(true);
- if (event.messageType == MessageTypes.Text &&
- !InstructionsEnum.clickTextMessages.isToggledOff) {
- InstructionsEnum.clickTextMessages.setToggledOff(true);
- }
- if (event.messageType == MessageTypes.Audio &&
- !InstructionsEnum.clickAudioMessages.isToggledOff) {
- InstructionsEnum.clickAudioMessages.setToggledOff(true);
+ if (!InstructionsEnum.clickMessage.isToggledOff) {
+ InstructionsEnum.clickMessage.setToggledOff(true);
}
if (!kIsWeb) {
@@ -2085,8 +2065,24 @@ class ChatController extends State
}
stopMediaStream.add(null);
- if (buttonEventID == event.eventId) {
+ final isButton = buttonEventID == event.eventId;
+ final keyboardOpen = inputFocus.hasFocus && PlatformInfos.isMobile;
+
+ final delay = keyboardOpen
+ ? const Duration(milliseconds: 500)
+ : isButton
+ ? const Duration(milliseconds: 200)
+ : null;
+
+ if (isButton) {
depressMessageButton.value = true;
+ }
+
+ if (keyboardOpen) {
+ inputFocus.unfocus();
+ }
+
+ if (delay != null) {
OverlayUtil.showOverlay(
context: context,
child: TransparentBackdrop(
@@ -2094,28 +2090,28 @@ class ChatController extends State
onDismiss: clearSelectedEvents,
blurBackground: true,
animateBackground: true,
- backgroundAnimationDuration: const Duration(milliseconds: 200),
+ backgroundAnimationDuration: delay,
),
position: OverlayPositionEnum.centered,
overlayKey: "button_message_backdrop",
);
- Future.delayed(const Duration(milliseconds: 200), () {
- if (_router.state.path != ':roomid') {
- // The user has navigated away from the chat,
- // so we don't want to show the overlay.
- return;
- }
- OverlayUtil.showOverlay(
- context: context,
- child: overlayEntry,
- position: OverlayPositionEnum.centered,
- onDismiss: clearSelectedEvents,
- blurBackground: true,
- backgroundColor: Colors.black,
- overlayKey: "message_toolbar_overlay",
- );
- });
+ await Future.delayed(delay);
+
+ if (_router.state.path != ':roomid') {
+ // The user has navigated away from the chat,
+ // so we don't want to show the overlay.
+ return;
+ }
+ OverlayUtil.showOverlay(
+ context: context,
+ child: overlayEntry,
+ position: OverlayPositionEnum.centered,
+ onDismiss: clearSelectedEvents,
+ blurBackground: true,
+ backgroundColor: Colors.black,
+ overlayKey: "message_toolbar_overlay",
+ );
} else {
OverlayUtil.showOverlay(
context: context,
@@ -2252,7 +2248,7 @@ class ChatController extends State
bool autosend = false,
}) async {
if (shouldShowLanguageMismatchPopupByActivity) {
- return showLanguageMismatchPopup();
+ return showLanguageMismatchPopup(manual: manual);
}
await choreographer.requestWritingAssistance(manual: manual);
@@ -2265,7 +2261,7 @@ class ChatController extends State
}
}
- void showLanguageMismatchPopup() {
+ void showLanguageMismatchPopup({bool manual = false}) {
if (!shouldShowLanguageMismatchPopupByActivity) {
return;
}
@@ -2278,11 +2274,41 @@ class ChatController extends State
message: L10n.of(context).languageMismatchDesc,
targetLanguage: targetLanguage,
onConfirm: () => WidgetsBinding.instance.addPostFrameCallback(
- (_) => onRequestWritingAssistance(manual: false, autosend: true),
+ (_) => onRequestWritingAssistance(manual: manual, autosend: true),
),
);
}
+ Future updateLanguageOnMismatch(String target) async {
+ final messenger = ScaffoldMessenger.of(context);
+ messenger.hideCurrentSnackBar();
+ final resp = await showFutureLoadingDialog(
+ context: context,
+ future: () async {
+ clearSelectedEvents();
+ await MatrixState.pangeaController.userController.updateProfile(
+ (profile) {
+ profile.userSettings.targetLanguage = target;
+ return profile;
+ },
+ waitForDataInSync: true,
+ );
+ },
+ );
+ if (resp.isError) return;
+ if (mounted) {
+ messenger.hideCurrentSnackBar();
+ messenger.showSnackBar(
+ SnackBar(
+ content: Text(
+ L10n.of(context).languageUpdated,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ );
+ }
+ }
+
void _onCloseIT() {
if (choreographer.timesDismissedIT.value >= 3) {
showDisableLanguageToolsPopup();
@@ -2403,6 +2429,8 @@ class ChatController extends State
);
if (reason == null) return;
+
+ clearSelectedEvents();
await showFutureLoadingDialog(
context: context,
future: () => room.sendEvent(
diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart
index d154b0839..734d082e4 100644
--- a/lib/pages/chat/chat_emoji_picker.dart
+++ b/lib/pages/chat/chat_emoji_picker.dart
@@ -49,8 +49,19 @@ class ChatEmojiPicker extends StatelessWidget {
backgroundColor:
theme.colorScheme.onInverseSurface,
),
- bottomActionBarConfig: const BottomActionBarConfig(
- enabled: false,
+ bottomActionBarConfig: BottomActionBarConfig(
+ // #Pangea
+ // enabled: false,
+ showBackspaceButton: false,
+ backgroundColor: Theme.of(context)
+ .colorScheme
+ .surfaceContainer,
+ buttonColor: Theme.of(context)
+ .colorScheme
+ .surfaceContainer,
+ buttonIconColor:
+ Theme.of(context).colorScheme.onSurface,
+ // Pangea#
),
categoryViewConfig: CategoryViewConfig(
backspaceColor: theme.colorScheme.primary,
@@ -68,6 +79,17 @@ class ChatEmojiPicker extends StatelessWidget {
)!,
indicatorColor: theme.colorScheme.onSurface,
),
+ // #Pangea
+ viewOrderConfig: const ViewOrderConfig(
+ middle: EmojiPickerItem.searchBar,
+ top: EmojiPickerItem.categoryBar,
+ bottom: EmojiPickerItem.emojiView,
+ ),
+ searchViewConfig: SearchViewConfig(
+ backgroundColor: theme.colorScheme.surface,
+ buttonIconColor: theme.colorScheme.onSurface,
+ ),
+ // Pangea#
),
),
// #Pangea
diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart
index 4fa2a92a3..990469e8f 100644
--- a/lib/pages/chat/chat_event_list.dart
+++ b/lib/pages/chat/chat_event_list.dart
@@ -51,147 +51,160 @@ class ChatEventList extends StatelessWidget {
controller.room.client.applicationAccountConfig.wallpaperUrl != null;
return SelectionArea(
- child: ListView.custom(
- padding: EdgeInsets.only(
- top: 16,
- bottom: 8,
- left: horizontalPadding,
- right: horizontalPadding,
- ),
- reverse: true,
- controller: controller.scrollController,
- keyboardDismissBehavior: PlatformInfos.isIOS
- ? ScrollViewKeyboardDismissBehavior.onDrag
- : ScrollViewKeyboardDismissBehavior.manual,
- childrenDelegate: SliverChildBuilderDelegate(
- (BuildContext context, int i) {
- // Footer to display typing indicator and read receipts:
- if (i == 0) {
- if (timeline.isRequestingFuture) {
- return const Center(
- child: CircularProgressIndicator.adaptive(strokeWidth: 2),
- );
- }
- if (timeline.canRequestFuture) {
- return Center(
- child: IconButton(
- onPressed: controller.requestFuture,
- icon: const Icon(Icons.refresh_outlined),
- ),
- );
- }
- return Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- SeenByRow(controller),
- TypingIndicators(controller),
- ],
- );
- }
-
- // Request history button or progress indicator:
- // #Pangea
- // if (i == events.length + 1) {
- if (i == events.length + 2) {
- // Pangea#
- if (timeline.isRequestingHistory) {
- return const Center(
- child: CircularProgressIndicator.adaptive(strokeWidth: 2),
- );
- }
- if (timeline.canRequestHistory) {
- return Builder(
- builder: (context) {
- // #Pangea
- // WidgetsBinding.instance
- // .addPostFrameCallback(controller.requestHistory);
- WidgetsBinding.instance.addPostFrameCallback(
- (_) => controller.requestHistory(),
- );
- // Pangea#
- return Center(
- child: IconButton(
- onPressed: controller.requestHistory,
- icon: const Icon(Icons.refresh_outlined),
- ),
- );
- },
- );
- }
- return const SizedBox.shrink();
- }
-
- // #Pangea
- if (i == 1) {
- return ActivityUserSummaries(controller: controller);
- }
- // Pangea#
-
- // #Pangea
- // i--;
- i = i - 2;
- // Pangea#
-
- // The message at this index:
- final event = events[i];
- final animateIn = animateInEventIndex != null &&
- timeline.events.length > animateInEventIndex &&
- event == timeline.events[animateInEventIndex];
-
- return AutoScrollTag(
- key: ValueKey(event.eventId),
- index: i,
- controller: controller.scrollController,
- child: Message(
- event,
- animateIn: animateIn,
- resetAnimateIn: () {
- controller.animateInEventIndex = null;
- },
- onSwipe: () => controller.replyAction(replyTo: event),
- // #Pangea
- onInfoTab: (_) => {},
- // onInfoTab: controller.showEventInfo,
- // Pangea#
- onMention: () => controller.sendController.text +=
- '${event.senderFromMemoryOrFallback.mention} ',
- highlightMarker:
- controller.scrollToEventIdMarker == event.eventId,
- // #Pangea
- // onSelect: controller.onSelectMessage,
- onSelect: (_) {},
- // Pangea#
- scrollToEventId: (String eventId) =>
- controller.scrollToEventId(eventId),
- longPressSelect: controller.selectedEvents.isNotEmpty,
- // #Pangea
- controller: controller,
- isButton: event.eventId == controller.buttonEventID,
- canRefresh: event.eventId == controller.refreshEventID,
- // Pangea#
- selected: controller.selectedEvents
- .any((e) => e.eventId == event.eventId),
- singleSelected:
- controller.selectedEvents.singleOrNull?.eventId ==
- event.eventId,
- onEdit: () => controller.editSelectedEventAction(),
- timeline: timeline,
- displayReadMarker:
- i > 0 && controller.readMarkerEventId == event.eventId,
- nextEvent: i + 1 < events.length ? events[i + 1] : null,
- previousEvent: i > 0 ? events[i - 1] : null,
- wallpaperMode: hasWallpaper,
- scrollController: controller.scrollController,
- colors: colors,
- ),
- );
- },
- // #Pangea
- // childCount: events.length + 2,
- childCount: events.length + 3,
+ // #Pangea
+ // child: ListView.custom(
+ child: NotificationListener(
+ onNotification: (_) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final scrollable =
+ controller.scrollController.position.maxScrollExtent > 0;
+ controller.scrollableNotifier.value = scrollable;
+ });
+ return true;
+ },
+ child: ListView.custom(
// Pangea#
- findChildIndexCallback: (key) =>
- controller.findChildIndexCallback(key, thisEventsKeyMap),
+ padding: EdgeInsets.only(
+ top: 16,
+ bottom: 8,
+ left: horizontalPadding,
+ right: horizontalPadding,
+ ),
+ reverse: true,
+ controller: controller.scrollController,
+ keyboardDismissBehavior: PlatformInfos.isIOS
+ ? ScrollViewKeyboardDismissBehavior.onDrag
+ : ScrollViewKeyboardDismissBehavior.manual,
+ childrenDelegate: SliverChildBuilderDelegate(
+ (BuildContext context, int i) {
+ // Footer to display typing indicator and read receipts:
+ if (i == 0) {
+ if (timeline.isRequestingFuture) {
+ return const Center(
+ child: CircularProgressIndicator.adaptive(strokeWidth: 2),
+ );
+ }
+ if (timeline.canRequestFuture) {
+ return Center(
+ child: IconButton(
+ onPressed: controller.requestFuture,
+ icon: const Icon(Icons.refresh_outlined),
+ ),
+ );
+ }
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SeenByRow(controller),
+ TypingIndicators(controller),
+ ],
+ );
+ }
+
+ // Request history button or progress indicator:
+ // #Pangea
+ // if (i == events.length + 1) {
+ if (i == events.length + 2) {
+ // Pangea#
+ if (timeline.isRequestingHistory) {
+ return const Center(
+ child: CircularProgressIndicator.adaptive(strokeWidth: 2),
+ );
+ }
+ if (timeline.canRequestHistory) {
+ return Builder(
+ builder: (context) {
+ // #Pangea
+ // WidgetsBinding.instance
+ // .addPostFrameCallback(controller.requestHistory);
+ WidgetsBinding.instance.addPostFrameCallback(
+ (_) => controller.requestHistory(),
+ );
+ // Pangea#
+ return Center(
+ child: IconButton(
+ onPressed: controller.requestHistory,
+ icon: const Icon(Icons.refresh_outlined),
+ ),
+ );
+ },
+ );
+ }
+ return const SizedBox.shrink();
+ }
+
+ // #Pangea
+ if (i == 1) {
+ return ActivityUserSummaries(controller: controller);
+ }
+ // Pangea#
+
+ // #Pangea
+ // i--;
+ i = i - 2;
+ // Pangea#
+
+ // The message at this index:
+ final event = events[i];
+ final animateIn = animateInEventIndex != null &&
+ timeline.events.length > animateInEventIndex &&
+ event == timeline.events[animateInEventIndex];
+
+ return AutoScrollTag(
+ key: ValueKey(event.eventId),
+ index: i,
+ controller: controller.scrollController,
+ child: Message(
+ event,
+ animateIn: animateIn,
+ resetAnimateIn: () {
+ controller.animateInEventIndex = null;
+ },
+ onSwipe: () => controller.replyAction(replyTo: event),
+ // #Pangea
+ onInfoTab: (_) => {},
+ // onInfoTab: controller.showEventInfo,
+ // Pangea#
+ onMention: () => controller.sendController.text +=
+ '${event.senderFromMemoryOrFallback.mention} ',
+ highlightMarker:
+ controller.scrollToEventIdMarker == event.eventId,
+ // #Pangea
+ // onSelect: controller.onSelectMessage,
+ onSelect: (_) {},
+ // Pangea#
+ scrollToEventId: (String eventId) =>
+ controller.scrollToEventId(eventId),
+ longPressSelect: controller.selectedEvents.isNotEmpty,
+ // #Pangea
+ controller: controller,
+ isButton: event.eventId == controller.buttonEventID,
+ canRefresh: event.eventId == controller.refreshEventID,
+ // Pangea#
+ selected: controller.selectedEvents
+ .any((e) => e.eventId == event.eventId),
+ singleSelected:
+ controller.selectedEvents.singleOrNull?.eventId ==
+ event.eventId,
+ onEdit: () => controller.editSelectedEventAction(),
+ timeline: timeline,
+ displayReadMarker:
+ i > 0 && controller.readMarkerEventId == event.eventId,
+ nextEvent: i + 1 < events.length ? events[i + 1] : null,
+ previousEvent: i > 0 ? events[i - 1] : null,
+ wallpaperMode: hasWallpaper,
+ scrollController: controller.scrollController,
+ colors: colors,
+ ),
+ );
+ },
+ // #Pangea
+ // childCount: events.length + 2,
+ childCount: events.length + 3,
+ // Pangea#
+ findChildIndexCallback: (key) =>
+ controller.findChildIndexCallback(key, thisEventsKeyMap),
+ ),
),
),
);
diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart
index 1d83d673d..c698dacbf 100644
--- a/lib/pages/chat/chat_view.dart
+++ b/lib/pages/chat/chat_view.dart
@@ -6,6 +6,7 @@ import 'package:badges/badges.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
+import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
@@ -24,6 +25,7 @@ import 'package:fluffychat/pangea/analytics_misc/level_up/star_rain_widget.dart'
import 'package:fluffychat/pangea/chat/widgets/chat_floating_action_button.dart';
import 'package:fluffychat/pangea/chat/widgets/chat_input_bar.dart';
import 'package:fluffychat/pangea/chat/widgets/chat_view_background.dart';
+import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/navigation/navigation_util.dart';
import 'package:fluffychat/utils/account_config.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
@@ -357,7 +359,55 @@ class ChatView extends StatelessWidget {
child: Stack(
// Pangea#
children: [
- if (accountConfig.wallpaperUrl != null)
+ // #Pangea
+ // if (accountConfig.wallpaperUrl != null)
+ // Only use activity image as chat background if enabled in AppConfig
+ if (controller.room.activityPlan != null &&
+ controller.room.activityPlan!.imageURL != null &&
+ AppConfig.useActivityImageAsChatBackground)
+ Opacity(
+ opacity: 0.25,
+ child: ImageFiltered(
+ imageFilter: ui.ImageFilter.blur(
+ sigmaX: accountConfig.wallpaperBlur ?? 0.0,
+ sigmaY: accountConfig.wallpaperBlur ?? 0.0,
+ ),
+ child: controller.room.activityPlan!.imageURL!
+ .toString()
+ .startsWith('mxc')
+ ? MxcImage(
+ uri: controller.room.activityPlan!.imageURL!,
+ fit: BoxFit.cover,
+ height: MediaQuery.sizeOf(context).height,
+ width: MediaQuery.sizeOf(context).width,
+ cacheKey: controller
+ .room.activityPlan!.imageURL
+ .toString(),
+ isThumbnail: false,
+ )
+ : Image.network(
+ controller.room.activityPlan!.imageURL
+ .toString(),
+ fit: BoxFit.cover,
+ height: MediaQuery.sizeOf(context).height,
+ width: MediaQuery.sizeOf(context).width,
+ headers: controller
+ .room.activityPlan!.imageURL
+ .toString()
+ .contains(Environment.cmsApi)
+ ? {
+ 'Authorization':
+ 'Bearer ${MatrixState.pangeaController.userController.accessToken}',
+ }
+ : null,
+ errorBuilder: (context, error, stackTrace) =>
+ Container(),
+ ),
+ ),
+ )
+ // If not enabled, fall through to default wallpaper logic
+ else if (accountConfig.wallpaperUrl != null)
+ // Pangea#
Opacity(
opacity: accountConfig.wallpaperOpacity ?? 0.5,
child: ImageFiltered(
diff --git a/lib/pages/chat/events/audio_player.dart b/lib/pages/chat/events/audio_player.dart
index ee6490a5f..b10397e23 100644
--- a/lib/pages/chat/events/audio_player.dart
+++ b/lib/pages/chat/events/audio_player.dart
@@ -151,6 +151,7 @@ class AudioPlayerState extends State {
audioPlayer.pause();
audioPlayer.dispose();
matrix.voiceMessageEventId.value = matrix.audioPlayer = null;
+ matrix.voiceMessageEventId.removeListener(_onPlayerChange);
// #Pangea
_onAudioStateChanged?.cancel();
// Pangea#
@@ -173,6 +174,14 @@ class AudioPlayerState extends State {
if (currentPlayer != null) {
// #Pangea
currentPlayer.setSpeed(playbackSpeed);
+ _onAudioStateChanged?.cancel();
+ _onAudioStateChanged =
+ matrix.audioPlayer!.playerStateStream.listen((state) {
+ if (state.processingState == ProcessingState.completed) {
+ matrix.audioPlayer!.stop();
+ matrix.audioPlayer!.seek(Duration.zero);
+ }
+ });
// Pangea#
if (currentPlayer.isAtEndPosition) {
currentPlayer.seek(Duration.zero);
@@ -382,10 +391,26 @@ class AudioPlayerState extends State {
return eventWaveForm.map((i) => i > 1024 ? 1024 : i).toList();
}
+ // #Pangea
+ void _onPlayerChange() {
+ if (matrix.audioPlayer == null) return;
+ _onAudioStateChanged?.cancel();
+ _onAudioStateChanged =
+ matrix.audioPlayer?.playerStateStream.listen((state) {
+ if (state.processingState == ProcessingState.completed) {
+ matrix.audioPlayer?.stop();
+ matrix.audioPlayer?.seek(Duration.zero);
+ }
+ });
+ }
+ // Pangea#
+
@override
void initState() {
super.initState();
matrix = Matrix.of(context);
+ WidgetsBinding.instance.addPostFrameCallback((_) => _onPlayerChange());
+ matrix.voiceMessageEventId.addListener(_onPlayerChange);
_waveform = _getWaveform();
// #Pangea
diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart
index 9cddf5570..d09a7b7dd 100644
--- a/lib/pages/chat/events/html_message.dart
+++ b/lib/pages/chat/events/html_message.dart
@@ -11,8 +11,10 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat/chat.dart';
+import 'package:fluffychat/pangea/common/widgets/shimmer_background.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
+import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/pangea/toolbar/layout/reading_assistance_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/message_practice/message_practice_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/message_practice/token_practice_button.dart';
@@ -20,6 +22,7 @@ import 'package:fluffychat/pangea/toolbar/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance/token_emoji_button.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance/token_rendering_util.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance/tokens_util.dart';
+import 'package:fluffychat/pangea/toolbar/reading_assistance/underline_text_widget.dart';
import 'package:fluffychat/utils/event_checkbox_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@@ -173,8 +176,9 @@ class HtmlMessage extends StatelessWidget {
);
String _addTokenTags() {
- final regex = RegExp(r'(<[^>]+>)');
+ if (html.contains("]+>)');
final matches = regex.allMatches(html);
final List result = [];
int lastEnd = 0;
@@ -395,17 +399,6 @@ class HtmlMessage extends StatelessWidget {
if (!allowedHtmlTags.contains(node.localName)) return const TextSpan();
// #Pangea
- final renderer = TokenRenderingUtil(
- existingStyle: pangeaMessageEvent != null
- ? textStyle.merge(
- AppConfig.messageTextStyle(
- pangeaMessageEvent!.event,
- textColor,
- ),
- )
- : textStyle,
- );
-
double fontSize = this.fontSize;
if (readingAssistanceMode == ReadingAssistanceMode.practiceMode) {
fontSize = (overlayController != null && overlayController!.maxWidth > 600
@@ -414,7 +407,22 @@ class HtmlMessage extends StatelessWidget {
this.fontSize;
}
- final underlineColor = Theme.of(context).colorScheme.primary.withAlpha(200);
+ final existingStyle = pangeaMessageEvent != null
+ ? textStyle
+ .merge(
+ AppConfig.messageTextStyle(
+ pangeaMessageEvent!.event,
+ textColor,
+ ),
+ )
+ .copyWith(fontSize: fontSize)
+ : textStyle.copyWith(fontSize: fontSize);
+
+ final renderer = TokenRenderingUtil();
+
+ final underlineColor = pangeaMessageEvent!.ownMessage
+ ? ThemeData.dark().colorScheme.primaryContainer.withAlpha(200)
+ : Theme.of(context).colorScheme.primary.withAlpha(200);
final newTokens =
pangeaMessageEvent != null && !pangeaMessageEvent!.ownMessage
@@ -440,10 +448,17 @@ class HtmlMessage extends StatelessWidget {
: false;
final isNew = token != null && newTokens.contains(token.text);
+ final isFirstNewToken = isNew &&
+ controller.buttonEventID == event.eventId &&
+ newTokens.first == token.text;
+ final showShimmer =
+ !InstructionsEnum.shimmerNewToken.isToggledOff && isFirstNewToken;
+
final tokenWidth = renderer.tokenTextWidthForContainer(
node.text,
Theme.of(context).colorScheme.primary.withAlpha(200),
- fontSize: fontSize,
+ existingStyle,
+ fontSize,
);
return TextSpan(
@@ -472,10 +487,7 @@ class HtmlMessage extends StatelessWidget {
TokenPracticeButton(
token: token,
controller: overlayController!.practiceController,
- textStyle: renderer.style(
- fontSize: fontSize,
- underlineColor: underlineColor,
- ),
+ textStyle: existingStyle,
width: tokenWidth,
textColor: textColor,
),
@@ -496,28 +508,25 @@ class HtmlMessage extends StatelessWidget {
: null,
child: HoverBuilder(
builder: (context, hovered) {
- return RichText(
- textDirection: pangeaMessageEvent?.textDirection,
- text: TextSpan(
- children: [
- LinkifySpan(
- text: node.text.trim(),
- style: renderer.style(
- fontSize: fontSize,
- underlineColor: underlineColor,
- selected: selected,
- highlighted: highlighted,
- isNew: isNew,
- practiceMode: readingAssistanceMode ==
- ReadingAssistanceMode.practiceMode,
- hovered: hovered,
- ),
- linkStyle: linkStyle,
- onOpen: (url) =>
- UrlLauncher(context, url.url)
- .launchUrl(),
- ),
- ],
+ return ShimmerBackground(
+ enabled: showShimmer,
+ borderRadius: BorderRadius.circular(4.0),
+ child: UnderlineText(
+ text: node.text.trim(),
+ style: existingStyle,
+ linkStyle: linkStyle,
+ textDirection:
+ pangeaMessageEvent?.textDirection,
+ underlineColor:
+ TokenRenderingUtil.underlineColor(
+ underlineColor,
+ selected: selected,
+ highlighted: highlighted,
+ isNew: isNew,
+ practiceMode: readingAssistanceMode ==
+ ReadingAssistanceMode.practiceMode,
+ hovered: hovered,
+ ),
),
);
},
@@ -669,10 +678,7 @@ class HtmlMessage extends StatelessWidget {
// const TextSpan(text: '• '),
TextSpan(
text: '• ',
- style: renderer.style(
- underlineColor: underlineColor,
- fontSize: fontSize,
- ),
+ style: existingStyle,
),
// Pangea#
if (node.parent?.localName == 'ol')
@@ -681,10 +687,7 @@ class HtmlMessage extends StatelessWidget {
'${(node.parent?.nodes.whereType().toList().indexOf(node) ?? 0) + (int.tryParse(node.parent?.attributes['start'] ?? '1') ?? 1)}. ',
// #Pangea
// style: textStyle,
- style: renderer.style(
- underlineColor: underlineColor,
- fontSize: fontSize,
- ),
+ style: existingStyle,
// Pangea#
),
if (node.className == 'task-list-item')
diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart
index d3ac2306b..d4ee1d5c1 100644
--- a/lib/pages/chat/events/message.dart
+++ b/lib/pages/chat/events/message.dart
@@ -16,9 +16,7 @@ import 'package:fluffychat/pangea/activity_sessions/activity_summary_widget.dart
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart';
import 'package:fluffychat/pangea/chat/extensions/custom_room_display_extension.dart';
-import 'package:fluffychat/pangea/chat/widgets/request_regeneration_button.dart';
import 'package:fluffychat/pangea/common/widgets/pressable_button.dart';
-import 'package:fluffychat/pangea/common/widgets/shimmer_background.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
@@ -147,6 +145,7 @@ class Message extends StatelessWidget {
valueListenable: controller.activityController.showInstructions,
builder: (context, show, __) {
return ActivitySummary(
+ inChat: true,
activity: event.room.activityPlan!,
room: event.room,
assignedRoles: event.room.hasArchivedActivity
@@ -603,241 +602,223 @@ class Message extends StatelessWidget {
child: ValueListenableBuilder(
valueListenable: controller
.depressMessageButton,
- // #Pangea
- child: ShimmerBackground(
- enabled: controller
- .showMessageShimmer(
- event,
+
+ child: Container(
+ decoration: BoxDecoration(
+ color: noBubble
+ ? Colors.transparent
+ : color,
+ borderRadius:
+ borderRadius,
),
- // Pangea#
- child: Container(
- decoration:
- BoxDecoration(
- color: noBubble
- ? Colors
- .transparent
- : color,
- borderRadius:
- borderRadius,
- ),
- clipBehavior:
- Clip.antiAlias,
- // #Pangea
- child:
- CompositedTransformTarget(
- link: MatrixState
+ clipBehavior:
+ Clip.antiAlias,
+ // #Pangea
+ child:
+ CompositedTransformTarget(
+ link: MatrixState
+ .pAnyState
+ .layerLinkAndKey(
+ event.eventId,
+ )
+ .link,
+ // child: BubbleBackground(
+ // colors: colors,
+ // ignore: noBubble || !ownMessage,
+ // scrollController: scrollController,
+ // Pangea#
+ child: Container(
+ // #Pangea
+ key: MatrixState
.pAnyState
.layerLinkAndKey(
event.eventId,
)
- .link,
- // child: BubbleBackground(
- // colors: colors,
- // ignore: noBubble || !ownMessage,
- // scrollController: scrollController,
+ .key,
// Pangea#
- child: Container(
- // #Pangea
- key: MatrixState
- .pAnyState
- .layerLinkAndKey(
- event.eventId,
- )
- .key,
- // Pangea#
- decoration:
- BoxDecoration(
- borderRadius:
- BorderRadius
- .circular(
- AppConfig
- .borderRadius,
- ),
+ decoration:
+ BoxDecoration(
+ borderRadius:
+ BorderRadius
+ .circular(
+ AppConfig
+ .borderRadius,
),
- constraints:
- const BoxConstraints(
- maxWidth: FluffyThemes
- .columnWidth *
- 1.5,
- ),
- child: Column(
- mainAxisSize:
- MainAxisSize
- .min,
- crossAxisAlignment:
- CrossAxisAlignment
- .start,
- children: [
- if ({
- RelationshipTypes
- .reply,
- RelationshipTypes
- .thread,
- }.contains(
- event
- .relationshipType,
- ))
- FutureBuilder<
- Event?>(
- future: event
- .getReplyEvent(
- timeline,
- ),
- builder: (
- BuildContext
- context,
- snapshot,
- ) {
- final replyEvent = snapshot
- .hasData
- ? snapshot
- .data!
- : Event(
- eventId: event.relationshipEventId!,
- content: {
- 'msgtype': 'm.text',
- 'body': '...',
- },
- // #Pangea
- // senderId: event
- // .senderId,
- senderId: "",
- // Pangea#
- type: 'm.room.message',
- room: event.room,
- status: EventStatus.sent,
- originServerTs: DateTime.now(),
- );
- return Padding(
- padding:
- const EdgeInsets.only(
- left:
- 16,
- right:
- 16,
- top:
- 8,
- ),
+ ),
+ constraints:
+ const BoxConstraints(
+ maxWidth: FluffyThemes
+ .columnWidth *
+ 1.5,
+ ),
+ child: Column(
+ mainAxisSize:
+ MainAxisSize
+ .min,
+ crossAxisAlignment:
+ CrossAxisAlignment
+ .start,
+ children: [
+ if ({
+ RelationshipTypes
+ .reply,
+ RelationshipTypes
+ .thread,
+ }.contains(
+ event
+ .relationshipType,
+ ))
+ FutureBuilder<
+ Event?>(
+ future: event
+ .getReplyEvent(
+ timeline,
+ ),
+ builder: (
+ BuildContext
+ context,
+ snapshot,
+ ) {
+ final replyEvent = snapshot
+ .hasData
+ ? snapshot
+ .data!
+ : Event(
+ eventId:
+ event.relationshipEventId!,
+ content: {
+ 'msgtype': 'm.text',
+ 'body': '...',
+ },
+ // #Pangea
+ // senderId: event
+ // .senderId,
+ senderId:
+ "",
+ // Pangea#
+ type:
+ 'm.room.message',
+ room:
+ event.room,
+ status:
+ EventStatus.sent,
+ originServerTs:
+ DateTime.now(),
+ );
+ return Padding(
+ padding:
+ const EdgeInsets
+ .only(
+ left:
+ 16,
+ right:
+ 16,
+ top: 8,
+ ),
+ child:
+ Material(
+ color: Colors
+ .transparent,
+ borderRadius:
+ ReplyContent.borderRadius,
child:
- Material(
- color:
- Colors.transparent,
+ InkWell(
borderRadius:
ReplyContent.borderRadius,
+ onTap: () =>
+ scrollToEventId(
+ replyEvent.eventId,
+ ),
child:
- InkWell(
- borderRadius:
- ReplyContent.borderRadius,
- onTap: () =>
- scrollToEventId(
- replyEvent.eventId,
- ),
+ AbsorbPointer(
child:
- AbsorbPointer(
- child: ReplyContent(
- replyEvent,
- ownMessage: ownMessage,
- timeline: timeline,
- ),
+ ReplyContent(
+ replyEvent,
+ ownMessage: ownMessage,
+ timeline: timeline,
),
),
),
- );
- },
- ),
- MessageContent(
- displayEvent,
- textColor:
- textColor,
- linkColor:
- linkColor,
- onInfoTab:
- onInfoTab,
- borderRadius:
- borderRadius,
- timeline:
- timeline,
- selected:
- selected,
- // #Pangea
- pangeaMessageEvent:
- pangeaMessageEvent,
- controller:
- controller,
- nextEvent:
- nextEvent,
- prevEvent:
- previousEvent,
- // Pangea#
+ ),
+ );
+ },
),
- if (event
- .hasAggregatedEvents(
- timeline,
- RelationshipTypes
- .edit,
- ))
- Padding(
- padding:
- const EdgeInsets
- .only(
- bottom:
- 8.0,
- left:
- 16.0,
- right:
- 16.0,
- ),
- child: Row(
- mainAxisSize:
- MainAxisSize
- .min,
- spacing:
- 4.0,
- children: [
- Icon(
- Icons
- .edit_outlined,
+ MessageContent(
+ displayEvent,
+ textColor:
+ textColor,
+ linkColor:
+ linkColor,
+ onInfoTab:
+ onInfoTab,
+ borderRadius:
+ borderRadius,
+ timeline:
+ timeline,
+ selected:
+ selected,
+ // #Pangea
+ pangeaMessageEvent:
+ pangeaMessageEvent,
+ controller:
+ controller,
+ nextEvent:
+ nextEvent,
+ prevEvent:
+ previousEvent,
+ // Pangea#
+ ),
+ if (event
+ .hasAggregatedEvents(
+ timeline,
+ RelationshipTypes
+ .edit,
+ ))
+ Padding(
+ padding:
+ const EdgeInsets
+ .only(
+ bottom: 8.0,
+ left: 16.0,
+ right: 16.0,
+ ),
+ child: Row(
+ mainAxisSize:
+ MainAxisSize
+ .min,
+ spacing:
+ 4.0,
+ children: [
+ Icon(
+ Icons
+ .edit_outlined,
+ color: textColor
+ .withAlpha(
+ 164,
+ ),
+ size:
+ 14,
+ ),
+ Text(
+ displayEvent
+ .originServerTs
+ .localizedTimeShort(
+ context,
+ ),
+ style:
+ TextStyle(
color:
textColor.withAlpha(
164,
),
- size:
- 14,
+ fontSize:
+ 11,
),
- Text(
- displayEvent
- .originServerTs
- .localizedTimeShort(
- context,
- ),
- style:
- TextStyle(
- color:
- textColor.withAlpha(
- 164,
- ),
- fontSize:
- 11,
- ),
- ),
- ],
- ),
- )
- // #Pangea
- else if (canRefresh)
- RequestRegenerationButton(
- textColor:
- textColor,
- onPressed: () =>
- controller
- .requestRegeneration(
- event
- .eventId,
- ),
+ ),
+ ],
),
- // Pangea#
- ],
- ),
+ ),
+ ],
),
),
),
diff --git a/lib/pages/chat/events/state_message.dart b/lib/pages/chat/events/state_message.dart
index 41f011305..2975c8a06 100644
--- a/lib/pages/chat/events/state_message.dart
+++ b/lib/pages/chat/events/state_message.dart
@@ -25,9 +25,19 @@ class StateMessage extends StatelessWidget {
padding:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: Text(
- event.calcLocalizedBodyFallback(
- MatrixLocals(L10n.of(context)),
- ),
+ // #Pangea
+ // event.calcLocalizedBodyFallback(
+ // MatrixLocals(L10n.of(context)),
+ // ),
+ (event.type == EventTypes.RoomMember) &&
+ (event.roomMemberChangeType ==
+ RoomMemberChangeType.leave) &&
+ (event.stateKey == event.room.client.userID)
+ ? L10n.of(context).youLeftTheChat
+ : event.calcLocalizedBodyFallback(
+ MatrixLocals(L10n.of(context)),
+ ),
+ // Pangea#
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart
index bf10421aa..501e20663 100644
--- a/lib/pages/chat/recording_dialog.dart
+++ b/lib/pages/chat/recording_dialog.dart
@@ -19,6 +19,8 @@ import 'events/audio_player.dart';
class PermissionException implements Exception {}
+class EmptyAudioException implements Exception {}
+
class RecordingDialog extends StatefulWidget {
const RecordingDialog({
super.key,
@@ -35,6 +37,7 @@ class RecordingDialogState extends State {
// #Pangea
// bool error = false;
Object? error;
+ bool _loading = true;
// Pangea#
final _audioRecorder = AudioRecorder();
@@ -87,7 +90,13 @@ class RecordingDialogState extends State {
path: path ?? '',
);
- setState(() => _duration = Duration.zero);
+ // #Pangea
+ // setState(() => _duration = Duration.zero);
+ setState(() {
+ _duration = Duration.zero;
+ _loading = false;
+ });
+ // Pangea#
_recorderSubscription?.cancel();
_recorderSubscription =
Timer.periodic(const Duration(milliseconds: 100), (_) async {
@@ -136,6 +145,16 @@ class RecordingDialogState extends State {
for (var i = 0; i < amplitudeTimeline.length; i += step) {
waveform.add((amplitudeTimeline[i] / 100 * 1024).round());
}
+
+ // #Pangea
+ if (amplitudeTimeline.isEmpty || amplitudeTimeline.every((e) => e <= 1)) {
+ if (mounted) {
+ setState(() => error = EmptyAudioException());
+ }
+ return;
+ }
+ // Pangea#
+
Navigator.of(context, rootNavigator: false).pop(
RecordingResult(
path: path,
@@ -161,7 +180,7 @@ class RecordingDialogState extends State {
constraints: const BoxConstraints(maxWidth: 250.0),
child: error is PermissionException
? Text(L10n.of(context).recordingPermissionDenied)
- : kIsWeb
+ : kIsWeb && error is! EmptyAudioException
? Text(L10n.of(context).genericWebRecordingError)
: Text(error!.toLocalizedString(context)),
)
@@ -202,7 +221,16 @@ class RecordingDialogState extends State {
const SizedBox(width: 8),
SizedBox(
width: 48,
- child: Text(time),
+ // #Pangea
+ // child: Text(time),
+ child: _loading
+ ? const SizedBox(
+ width: 16,
+ height: 16,
+ child: CircularProgressIndicator.adaptive(),
+ )
+ : Text(time),
+ // Pangea#
),
],
);
diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart
index c7c9f8a8a..8d2df4b2d 100644
--- a/lib/pages/chat/reply_display.dart
+++ b/lib/pages/chat/reply_display.dart
@@ -16,35 +16,57 @@ class ReplyDisplay extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
- return AnimatedContainer(
- duration: FluffyThemes.animationDuration,
- curve: FluffyThemes.animationCurve,
- height: controller.editEvent != null || controller.replyEvent != null
- ? 56
- : 0,
- clipBehavior: Clip.hardEdge,
- decoration: BoxDecoration(
- color: theme.colorScheme.onInverseSurface,
- ),
- child: Row(
- children: [
- IconButton(
- tooltip: L10n.of(context).close,
- icon: const Icon(Icons.close),
- onPressed: controller.cancelReplyEventAction,
+ // #Pangea
+ return ListenableBuilder(
+ listenable:
+ Listenable.merge([controller.replyEvent, controller.editEvent]),
+ builder: (context, __) {
+ final editEvent = controller.editEvent.value;
+ final replyEvent = controller.replyEvent.value;
+ // Pangea#
+ return AnimatedContainer(
+ duration: FluffyThemes.animationDuration,
+ curve: FluffyThemes.animationCurve,
+ // #Pangea
+ // height: controller.editEvent != null || controller.replyEvent != null
+ height: editEvent != null || replyEvent != null
+ // Pangea#
+ ? 56
+ : 0,
+ clipBehavior: Clip.hardEdge,
+ decoration: BoxDecoration(
+ color: theme.colorScheme.onInverseSurface,
),
- Expanded(
- child: controller.replyEvent != null
- ? ReplyContent(
- controller.replyEvent!,
- timeline: controller.timeline!,
- )
- : _EditContent(
- controller.editEvent?.getDisplayEvent(controller.timeline!),
- ),
+ child: Row(
+ children: [
+ IconButton(
+ tooltip: L10n.of(context).close,
+ icon: const Icon(Icons.close),
+ onPressed: controller.cancelReplyEventAction,
+ ),
+ Expanded(
+ // #Pangea
+ // child: controller.replyEvent != null
+ child: replyEvent != null
+ // Pangea#
+ ? ReplyContent(
+ // #Pangea
+ // controller.replyEvent,
+ replyEvent,
+ // Pangea#
+ timeline: controller.timeline!,
+ )
+ : _EditContent(
+ // #Pangea
+ // controller.editEvent?.getDisplayEvent(controller.timeline!),
+ editEvent?.getDisplayEvent(controller.timeline!),
+ // Pangea#
+ ),
+ ),
+ ],
),
- ],
- ),
+ );
+ },
);
}
}
diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart
index d0f2d8948..bd2d0c0a2 100644
--- a/lib/pages/chat_list/chat_list.dart
+++ b/lib/pages/chat_list/chat_list.dart
@@ -111,9 +111,12 @@ class ChatListController extends State
// StreamSubscription? _intentUriStreamSubscription;
// Pangea#
- ActiveFilter activeFilter = AppConfig.separateChatTypes
- ? ActiveFilter.messages
- : ActiveFilter.allChats;
+ // #Pangea
+ // ActiveFilter activeFilter = AppConfig.separateChatTypes
+ // ? ActiveFilter.messages
+ // : ActiveFilter.allChats;
+ ActiveFilter activeFilter = ActiveFilter.allChats;
+ // Pangea#
// #Pangea
String? get activeSpaceId => widget.activeSpaceId;
@@ -693,7 +696,6 @@ class ChatListController extends State
_roomCapacitySubscription?.cancel();
MatrixState.pangeaController.subscriptionController.subscriptionNotifier
.removeListener(_onSubscribe);
- SpaceCodeController.codeNotifier.removeListener(_onCacheSpaceCode);
//Pangea#
scrollController.removeListener(_onScroll);
super.dispose();
@@ -1105,14 +1107,8 @@ class ChatListController extends State
MatrixState.pangeaController.initControllers();
if (mounted) {
SpaceCodeController.joinCachedSpaceCode(context);
- SpaceCodeController.codeNotifier.addListener(_onCacheSpaceCode);
}
}
-
- void _onCacheSpaceCode() {
- if (!mounted) return;
- SpaceCodeController.joinCachedSpaceCode(context);
- }
// Pangea#
void setActiveFilter(ActiveFilter filter) {
diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart
index b8aae527f..7e998777c 100644
--- a/lib/pages/chat_list/chat_list_body.dart
+++ b/lib/pages/chat_list/chat_list_body.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
+import 'package:material_symbols_icons/symbols.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
@@ -9,9 +10,12 @@ import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
import 'package:fluffychat/pages/chat_list/dummy_chat_list_item.dart';
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
+import 'package:fluffychat/pangea/chat_list/support_client_extension.dart';
import 'package:fluffychat/pangea/chat_list/widgets/pangea_chat_list_header.dart';
import 'package:fluffychat/pangea/chat_settings/utils/bot_client_extension.dart';
+import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/course_chats/course_chats_page.dart';
+import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/stream_extension.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/public_room_dialog.dart';
@@ -343,6 +347,59 @@ class ChatListViewBody extends StatelessWidget {
),
),
),
+ if (!client.hasSupportDM &&
+ !InstructionsEnum.dismissSupportChat.isToggledOff &&
+ !controller.isSearchMode)
+ SliverToBoxAdapter(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 1,
+ ),
+ child: Material(
+ borderRadius:
+ BorderRadius.circular(AppConfig.borderRadius),
+ clipBehavior: Clip.hardEdge,
+ child: ListTile(
+ contentPadding: const EdgeInsets.only(
+ left: 16,
+ right: 16,
+ ),
+ leading: Container(
+ alignment: Alignment.center,
+ height: Avatar.defaultSize,
+ width: Avatar.defaultSize,
+ child: const Icon(
+ Symbols.chat_add_on,
+ size: Avatar.defaultSize - 16,
+ ),
+ ),
+ trailing: IconButton(
+ icon: const Icon(Icons.close),
+ onPressed: () => InstructionsEnum.dismissSupportChat
+ .setToggledOff(true),
+ ),
+ title: Text(L10n.of(context).chatWithSupport),
+ subtitle: Text(L10n.of(context).supportSubtitle),
+ onTap: () async {
+ await showFutureLoadingDialog(
+ context: context,
+ future: () async {
+ final roomId = await Matrix.of(context)
+ .client
+ .startDirectChat(
+ Environment.supportUserId,
+ enableEncryption: false,
+ );
+ context.go('/rooms/$roomId');
+ },
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+ const SliverToBoxAdapter(child: SizedBox(height: 75.0)),
// Pangea#
],
),
diff --git a/lib/pages/chat_list/navi_rail_item.dart b/lib/pages/chat_list/navi_rail_item.dart
index e90dacba6..19eb929e8 100644
--- a/lib/pages/chat_list/navi_rail_item.dart
+++ b/lib/pages/chat_list/navi_rail_item.dart
@@ -49,128 +49,135 @@ class NaviRailItem extends StatelessWidget {
// Pangea#
final icon = isSelected ? selectedIcon ?? this.icon : this.icon;
final unreadBadgeFilter = this.unreadBadgeFilter;
- return HoverBuilder(
- builder: (context, hovered) {
- // #Pangea
- // return SizedBox(
- // height: 72,
- return Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- SizedBox(
- height: width - (isColumnMode ? 16.0 : 12.0),
- width: width,
- // width: FluffyThemes.navRailWidth,
- // Pangea#
- child: Stack(
- children: [
- Positioned(
- top: 8,
- bottom: 8,
- left: 0,
- child: AnimatedContainer(
- width: isSelected
- ? FluffyThemes.isColumnMode(context)
- ? 8
- : 4
- : 0,
- duration: FluffyThemes.animationDuration,
- curve: FluffyThemes.animationCurve,
- decoration: BoxDecoration(
- color: theme.colorScheme.primary,
- borderRadius: const BorderRadius.only(
- topRight: Radius.circular(90),
- bottomRight: Radius.circular(90),
+ // #Pangea
+ // return HoverBuilder(
+ return GestureDetector(
+ onTap: onTap,
+ child: HoverBuilder(
+ // Pangea#
+ builder: (context, hovered) {
+ // #Pangea
+ // return SizedBox(
+ // height: 72,
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SizedBox(
+ height: width - (isColumnMode ? 16.0 : 12.0),
+ width: width,
+ // width: FluffyThemes.navRailWidth,
+ // Pangea#
+ child: Stack(
+ children: [
+ Positioned(
+ top: 8,
+ bottom: 8,
+ left: 0,
+ child: AnimatedContainer(
+ width: isSelected
+ ? FluffyThemes.isColumnMode(context)
+ ? 8
+ : 4
+ : 0,
+ duration: FluffyThemes.animationDuration,
+ curve: FluffyThemes.animationCurve,
+ decoration: BoxDecoration(
+ color: theme.colorScheme.primary,
+ borderRadius: const BorderRadius.only(
+ topRight: Radius.circular(90),
+ bottomRight: Radius.circular(90),
+ ),
),
),
),
- ),
- Center(
- child: AnimatedScale(
- scale: hovered ? 1.1 : 1.0,
- duration: FluffyThemes.animationDuration,
- curve: FluffyThemes.animationCurve,
- // #Pangea
- // child: Material(
- // borderRadius: borderRadius,
- // color: isSelected
- // ? theme.colorScheme.primaryContainer
- // : theme.colorScheme.surfaceContainerHigh,
- child: UnreadRoomsBadge(
- filter: unreadBadgeFilter ?? (_) => false,
- badgePosition: BadgePosition.topEnd(
- top: 1,
- end: isColumnMode ? 8 : 4,
- ),
- child: Container(
- alignment: Alignment.center,
- decoration: BoxDecoration(
- color: backgroundColor ??
- (isSelected
- ? theme.colorScheme.primaryContainer
- : theme.colorScheme.surfaceContainerHigh),
- borderRadius: borderRadius,
+ Center(
+ child: AnimatedScale(
+ scale: hovered ? 1.1 : 1.0,
+ duration: FluffyThemes.animationDuration,
+ curve: FluffyThemes.animationCurve,
+ // #Pangea
+ // child: Material(
+ // borderRadius: borderRadius,
+ // color: isSelected
+ // ? theme.colorScheme.primaryContainer
+ // : theme.colorScheme.surfaceContainerHigh,
+ child: UnreadRoomsBadge(
+ filter: unreadBadgeFilter ?? (_) => false,
+ badgePosition: BadgePosition.topEnd(
+ top: 1,
+ end: isColumnMode ? 8 : 4,
),
- margin: EdgeInsets.symmetric(
- horizontal: isColumnMode ? 16.0 : 12.0,
- vertical: isColumnMode ? 8.0 : 6.0,
- ),
- child: TooltipVisibility(
- visible: !expanded,
- // Pangea#
- child: Tooltip(
- message: toolTip,
- child: InkWell(
- borderRadius: borderRadius,
- onTap: onTap,
- // #Pangea
- child: icon,
- // child: unreadBadgeFilter == null
- // ? icon
- // : UnreadRoomsBadge(
- // filter: unreadBadgeFilter,
- // badgePosition: BadgePosition.topEnd(
- // top: -12,
- // end: -8,
- // ),
- // child: icon,
- // ),
- // Pangea#
+ child: Container(
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: backgroundColor ??
+ (isSelected
+ ? theme.colorScheme.primaryContainer
+ : theme.colorScheme.surfaceContainerHigh),
+ borderRadius: borderRadius,
+ ),
+ margin: EdgeInsets.symmetric(
+ horizontal: isColumnMode ? 16.0 : 12.0,
+ vertical: isColumnMode ? 8.0 : 6.0,
+ ),
+ child: TooltipVisibility(
+ visible: !expanded,
+ // Pangea#
+ child: Tooltip(
+ message: toolTip,
+ child: InkWell(
+ borderRadius: borderRadius,
+ // #Pangea
+ // onTap: onTap,
+ child: icon,
+ // child: unreadBadgeFilter == null
+ // ? icon
+ // : UnreadRoomsBadge(
+ // filter: unreadBadgeFilter,
+ // badgePosition: BadgePosition.topEnd(
+ // top: -12,
+ // end: -8,
+ // ),
+ // child: icon,
+ // ),
+ // Pangea#
+ ),
),
),
),
),
),
),
- ),
- ],
+ ],
+ ),
),
- ),
- if (expanded)
- Flexible(
- child: Padding(
- padding: const EdgeInsets.only(right: 16.0),
- child: ListTile(
- title: Text(
- toolTip,
- maxLines: 2,
- overflow: TextOverflow.ellipsis,
- style: theme.textTheme.bodyMedium,
- ),
- onTap: onTap,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(16.0),
- ),
- contentPadding: const EdgeInsets.symmetric(
- horizontal: 8.0,
- vertical: 0.0,
+ if (expanded)
+ Flexible(
+ child: Container(
+ height: width - (isColumnMode ? 16.0 : 12.0),
+ padding: const EdgeInsets.only(right: 16.0),
+ child: ListTile(
+ title: Text(
+ toolTip,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ style: theme.textTheme.bodyMedium,
+ ),
+ onTap: onTap,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(16.0),
+ ),
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 8.0,
+ vertical: 0.0,
+ ),
),
),
),
- ),
- ],
- );
- },
+ ],
+ );
+ },
+ ),
);
}
}
diff --git a/lib/pages/chat_search/chat_search_page.dart b/lib/pages/chat_search/chat_search_page.dart
index 40109d0b6..f4a25e35d 100644
--- a/lib/pages/chat_search/chat_search_page.dart
+++ b/lib/pages/chat_search/chat_search_page.dart
@@ -76,10 +76,25 @@ class ChatSearchController extends State
(result) => (
{
for (final event in result.$1) event.eventId: event,
- }.values.toList(),
+ // #Pangea
+ // }.values.toList(),
+ }
+ .values
+ .toList()
+ .where(
+ (e) => !e.hasAggregatedEvents(
+ timeline,
+ RelationshipTypes.edit,
+ ),
+ )
+ .toList(),
+ // Pangea#
result.$2,
),
)
+ // #Pangea
+ .where((result) => result.$1.isNotEmpty)
+ // Pangea#
.asBroadcastStream();
});
}
diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart
index 98e811692..1354566fc 100644
--- a/lib/pages/login/login.dart
+++ b/lib/pages/login/login.dart
@@ -7,6 +7,7 @@ import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/authentication/p_login.dart';
import 'package:fluffychat/pangea/login/pages/login_options_view.dart';
import 'package:fluffychat/pangea/login/pages/pangea_login_view.dart';
+import 'package:fluffychat/pangea/login/pages/signup.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
@@ -290,6 +291,19 @@ class LoginController extends State {
obscureText: true,
minLines: 1,
maxLines: 1,
+ // #Pangea
+ validator: (value) {
+ if (value.isEmpty) {
+ return L10n.of(context).chooseAStrongPassword;
+ }
+ if (value.length < SignupPageController.minPassLength) {
+ return L10n.of(context).pleaseChooseAtLeastChars(
+ SignupPageController.minPassLength.toString(),
+ );
+ }
+ return null;
+ },
+ // Pangea#
);
if (password == null) return;
final ok = await showOkAlertDialog(
diff --git a/lib/pages/new_private_chat/new_private_chat.dart b/lib/pages/new_private_chat/new_private_chat.dart
index 543031c9f..4c7984ce2 100644
--- a/lib/pages/new_private_chat/new_private_chat.dart
+++ b/lib/pages/new_private_chat/new_private_chat.dart
@@ -9,6 +9,7 @@ import 'package:matrix/matrix.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/new_private_chat/new_private_chat_view.dart';
import 'package:fluffychat/pages/new_private_chat/qr_scanner_modal.dart';
+import 'package:fluffychat/pangea/user/user_search_extension.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/utils/platform_infos.dart';
@@ -52,15 +53,20 @@ class NewPrivateChatController extends State {
}
Future> _searchUser(String searchTerm) async {
- final result =
- await Matrix.of(context).client.searchUserDirectory(searchTerm);
+ // #Pangea
+ // final result =
+ // await Matrix.of(context).client.searchUserDirectory(searchTerm);
+ final result = await Matrix.of(context).client.searchUser(searchTerm);
+ // Pangea#
final profiles = result.results;
- if (searchTerm.isValidMatrixId &&
- searchTerm.sigil == '@' &&
- !profiles.any((profile) => profile.userId == searchTerm)) {
- profiles.add(Profile(userId: searchTerm));
- }
+ // #Pangea
+ // if (searchTerm.isValidMatrixId &&
+ // searchTerm.sigil == '@' &&
+ // !profiles.any((profile) => profile.userId == searchTerm)) {
+ // profiles.add(Profile(userId: searchTerm));
+ // }
+ // Pangea#
return profiles;
}
diff --git a/lib/pages/onboarding/enable_notifications.dart b/lib/pages/onboarding/enable_notifications.dart
new file mode 100644
index 000000000..e3586033a
--- /dev/null
+++ b/lib/pages/onboarding/enable_notifications.dart
@@ -0,0 +1,125 @@
+import 'package:flutter/material.dart';
+
+import 'package:go_router/go_router.dart';
+import 'package:matrix/matrix.dart';
+
+import 'package:fluffychat/l10n/l10n.dart';
+import 'package:fluffychat/pangea/authentication/p_logout.dart';
+import 'package:fluffychat/pangea/common/utils/error_handler.dart';
+import 'package:fluffychat/pangea/login/pages/pangea_login_scaffold.dart';
+import 'package:fluffychat/widgets/local_notifications_extension.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+class EnableNotifications extends StatefulWidget {
+ const EnableNotifications({super.key});
+
+ @override
+ EnableNotificationsController createState() =>
+ EnableNotificationsController();
+}
+
+class EnableNotificationsController extends State {
+ Profile? profile;
+
+ @override
+ void initState() {
+ _setProfile();
+ super.initState();
+ }
+
+ Future _setProfile() async {
+ final client = Matrix.of(context).client;
+ try {
+ profile = await client.getProfileFromUserId(
+ client.userID!,
+ );
+ } catch (e, s) {
+ ErrorHandler.logError(
+ e: e,
+ s: s,
+ data: {
+ 'userId': client.userID,
+ },
+ );
+ } finally {
+ if (mounted) setState(() {});
+ }
+ }
+
+ Future _requestNotificationPermission() async {
+ await Matrix.of(context).requestNotificationPermission();
+ if (mounted) {
+ context.go("/registration/course");
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return PangeaLoginScaffold(
+ customAppBar: AppBar(
+ title: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 450,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ BackButton(
+ onPressed: () => pLogoutAction(
+ context,
+ bypassWarning: true,
+ ),
+ ),
+ const SizedBox(
+ width: 40.0,
+ ),
+ ],
+ ),
+ ),
+ automaticallyImplyLeading: false,
+ ),
+ showAppName: false,
+ mainAssetUrl: profile?.avatarUrl,
+ children: [
+ Column(
+ spacing: 8.0,
+ children: [
+ Text(
+ L10n.of(context).welcomeUser(
+ profile?.displayName ??
+ Matrix.of(context).client.userID?.localpart ??
+ "",
+ ),
+ style: Theme.of(context)
+ .textTheme
+ .titleLarge
+ ?.copyWith(fontWeight: FontWeight.bold),
+ ),
+ Text(
+ L10n.of(context).enableNotificationsTitle,
+ textAlign: TextAlign.center,
+ ),
+ ElevatedButton(
+ onPressed: _requestNotificationPermission,
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Theme.of(context).colorScheme.primaryContainer,
+ foregroundColor:
+ Theme.of(context).colorScheme.onPrimaryContainer,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(L10n.of(context).enableNotificationsDesc),
+ ],
+ ),
+ ),
+ TextButton(
+ child: Text(L10n.of(context).skipForNow),
+ onPressed: () => context.go("/registration/course"),
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/pages/onboarding/space_code_onboarding.dart b/lib/pages/onboarding/space_code_onboarding.dart
new file mode 100644
index 000000000..54c50f557
--- /dev/null
+++ b/lib/pages/onboarding/space_code_onboarding.dart
@@ -0,0 +1,83 @@
+import 'package:flutter/material.dart';
+
+import 'package:go_router/go_router.dart';
+import 'package:matrix/matrix.dart';
+
+import 'package:fluffychat/pages/onboarding/space_code_onboarding_view.dart';
+import 'package:fluffychat/pangea/common/utils/error_handler.dart';
+import 'package:fluffychat/pangea/join_codes/space_code_controller.dart';
+import 'package:fluffychat/pangea/spaces/space_constants.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+class SpaceCodeOnboarding extends StatefulWidget {
+ const SpaceCodeOnboarding({super.key});
+
+ @override
+ State createState() => SpaceCodeOnboardingState();
+}
+
+class SpaceCodeOnboardingState extends State {
+ Profile? profile;
+ Client get client => Matrix.of(context).client;
+
+ final TextEditingController codeController = TextEditingController();
+
+ @override
+ void initState() {
+ _setProfile();
+ codeController.addListener(() {
+ if (mounted) setState(() {});
+ });
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ codeController.dispose();
+ super.dispose();
+ }
+
+ Future _setProfile() async {
+ try {
+ profile = await client.getProfileFromUserId(
+ client.userID!,
+ );
+ } catch (e, s) {
+ ErrorHandler.logError(
+ e: e,
+ s: s,
+ data: {
+ 'userId': client.userID,
+ },
+ );
+ } finally {
+ if (mounted) setState(() {});
+ }
+ }
+
+ Future submitCode() async {
+ String code = codeController.text.trim();
+ if (code.isEmpty) return;
+
+ try {
+ final link = Uri.parse(Uri.parse(code).fragment);
+ if (link.queryParameters.containsKey(SpaceConstants.classCode)) {
+ code = link.queryParameters[SpaceConstants.classCode]!;
+ }
+ } catch (e) {
+ debugPrint("Text input is not a URL: $e");
+ }
+
+ final roomId = await SpaceCodeController.joinSpaceWithCode(context, code);
+ if (roomId != null) {
+ final room = Matrix.of(context).client.getRoomById(roomId);
+ room?.isSpace ?? true
+ ? context.go('/rooms/spaces/$roomId/details')
+ : context.go('/rooms/$roomId');
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) =>
+ SpaceCodeOnboardingView(controller: this);
+}
diff --git a/lib/pages/onboarding/space_code_onboarding_view.dart b/lib/pages/onboarding/space_code_onboarding_view.dart
new file mode 100644
index 000000000..bbd57d5eb
--- /dev/null
+++ b/lib/pages/onboarding/space_code_onboarding_view.dart
@@ -0,0 +1,92 @@
+import 'package:flutter/material.dart';
+
+import 'package:go_router/go_router.dart';
+import 'package:matrix/matrix.dart';
+
+import 'package:fluffychat/l10n/l10n.dart';
+import 'package:fluffychat/pages/onboarding/space_code_onboarding.dart';
+import 'package:fluffychat/pangea/login/pages/pangea_login_scaffold.dart';
+
+class SpaceCodeOnboardingView extends StatelessWidget {
+ final SpaceCodeOnboardingState controller;
+ const SpaceCodeOnboardingView({
+ super.key,
+ required this.controller,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return PangeaLoginScaffold(
+ customAppBar: AppBar(
+ title: ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 450,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ BackButton(
+ onPressed: Navigator.of(context).pop,
+ ),
+ const SizedBox(
+ width: 40.0,
+ ),
+ ],
+ ),
+ ),
+ automaticallyImplyLeading: false,
+ ),
+ showAppName: false,
+ mainAssetUrl: controller.profile?.avatarUrl,
+ children: [
+ Column(
+ spacing: 8.0,
+ children: [
+ Text(
+ L10n.of(context).welcomeUser(
+ controller.profile?.displayName ??
+ controller.client.userID?.localpart ??
+ "",
+ ),
+ style: Theme.of(context)
+ .textTheme
+ .titleLarge
+ ?.copyWith(fontWeight: FontWeight.bold),
+ ),
+ Text(
+ L10n.of(context).joinSpaceOnboardingDesc,
+ textAlign: TextAlign.center,
+ ),
+ TextField(
+ decoration: InputDecoration(
+ hintText: L10n.of(context).enterCodeToJoin,
+ ),
+ controller: controller.codeController,
+ onSubmitted: (_) => controller.submitCode,
+ ),
+ ElevatedButton(
+ onPressed: controller.codeController.text.isNotEmpty
+ ? controller.submitCode
+ : null,
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Theme.of(context).colorScheme.primaryContainer,
+ foregroundColor:
+ Theme.of(context).colorScheme.onPrimaryContainer,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(L10n.of(context).join),
+ ],
+ ),
+ ),
+ TextButton(
+ child: Text(L10n.of(context).skipForNow),
+ onPressed: () => context.go("/rooms"),
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart
index 3cbd371e4..874e7ebe3 100644
--- a/lib/pages/settings/settings_view.dart
+++ b/lib/pages/settings/settings_view.dart
@@ -12,6 +12,7 @@ import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
+import 'package:fluffychat/widgets/local_notifications_extension.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../widgets/mxc_image_viewer.dart';
import 'settings.dart';
@@ -147,9 +148,7 @@ class SettingsView extends StatelessWidget {
displayname,
maxLines: 1,
overflow: TextOverflow.ellipsis,
- style: const TextStyle(
- fontSize: 18,
- ),
+ style: const TextStyle(fontSize: 18),
),
),
TextButton.icon(
@@ -171,25 +170,6 @@ class SettingsView extends StatelessWidget {
// style: const TextStyle(fontSize: 12),
),
),
- // #Pangea
- TextButton.icon(
- onPressed: controller.setStatus,
- icon: const Icon(
- Icons.add,
- size: 14,
- ),
- style: TextButton.styleFrom(
- foregroundColor:
- theme.colorScheme.secondary,
- iconColor: theme.colorScheme.secondary,
- ),
- label: Text(
- L10n.of(context).setStatus,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- ),
- // Pangea#
],
),
),
@@ -252,6 +232,23 @@ class SettingsView extends StatelessWidget {
? theme.colorScheme.surfaceContainerHigh
: null,
onTap: () => context.go('/rooms/settings/notifications'),
+ // #Pangea
+ trailing: ValueListenableBuilder(
+ valueListenable:
+ Matrix.of(context).notifPermissionNotifier,
+ builder: (context, _, __) => FutureBuilder(
+ future: Matrix.of(context).notificationsEnabled,
+ builder: (context, snapshot) {
+ return snapshot.data != false
+ ? const SizedBox()
+ : Icon(
+ Icons.error_outline,
+ color: theme.colorScheme.error,
+ );
+ },
+ ),
+ ),
+ // Pangea#
),
ListTile(
leading: const Icon(Icons.devices_outlined),
@@ -294,7 +291,8 @@ class SettingsView extends StatelessWidget {
// #Pangea
ListTile(
leading: const Icon(Icons.help_outline_outlined),
- title: Text(L10n.of(context).help),
+ title: Text(L10n.of(context).chatWithSupport),
+ trailing: const Icon(Icons.chat_bubble_outline),
onTap: () async {
await showFutureLoadingDialog(
context: context,
diff --git a/lib/pages/settings_3pid/settings_3pid.dart b/lib/pages/settings_3pid/settings_3pid.dart
index 801427f85..9014c4155 100644
--- a/lib/pages/settings_3pid/settings_3pid.dart
+++ b/lib/pages/settings_3pid/settings_3pid.dart
@@ -61,6 +61,9 @@ class Settings3PidController extends State {
auth: auth,
),
),
+ // #Pangea
+ showError: (e) => !e.toString().contains("Request has been canceled"),
+ // Pangea#
);
if (success.error != null) return;
setState(() => request = null);
diff --git a/lib/pages/settings_chat/settings_chat.dart b/lib/pages/settings_chat/settings_chat.dart
index 1c1035559..3d30663ac 100644
--- a/lib/pages/settings_chat/settings_chat.dart
+++ b/lib/pages/settings_chat/settings_chat.dart
@@ -1,5 +1,8 @@
import 'package:flutter/material.dart';
+import 'package:fluffychat/config/app_config.dart';
+import 'package:fluffychat/pangea/user/style_settings_repo.dart';
+import 'package:fluffychat/widgets/matrix.dart';
import 'settings_chat_view.dart';
class SettingsChat extends StatefulWidget {
@@ -10,6 +13,15 @@ class SettingsChat extends StatefulWidget {
}
class SettingsChatController extends State {
+ // #Pangea
+ Future setUseActivityImageBackground(bool value) async {
+ final userId = Matrix.of(context).client.userID!;
+ AppConfig.useActivityImageAsChatBackground = value;
+ setState(() {});
+ await StyleSettingsRepo.setUseActivityImageBackground(userId, value);
+ }
+ // Pangea#
+
@override
Widget build(BuildContext context) => SettingsChatView(this);
}
diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart
index ca75754bd..16db97abc 100644
--- a/lib/pages/settings_chat/settings_chat_view.dart
+++ b/lib/pages/settings_chat/settings_chat_view.dart
@@ -78,7 +78,13 @@ class SettingsChatView extends StatelessWidget {
storeKey: SettingKeys.swipeRightToLeftToReply,
defaultValue: AppConfig.swipeRightToLeftToReply,
),
+
// #Pangea
+ SwitchListTile.adaptive(
+ value: AppConfig.useActivityImageAsChatBackground,
+ title: Text(L10n.of(context).useActivityImageAsChatBackground),
+ onChanged: controller.setUseActivityImageBackground,
+ ),
// Divider(color: theme.dividerColor),
// ListTile(
// title: Text(
diff --git a/lib/pages/settings_notifications/settings_notifications.dart b/lib/pages/settings_notifications/settings_notifications.dart
index 84ab62b60..b0974940c 100644
--- a/lib/pages/settings_notifications/settings_notifications.dart
+++ b/lib/pages/settings_notifications/settings_notifications.dart
@@ -13,6 +13,7 @@ import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'
import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
+import 'package:fluffychat/widgets/local_notifications_extension.dart';
import '../../widgets/matrix.dart';
import 'settings_notifications_view.dart';
@@ -205,6 +206,11 @@ class SettingsNotificationsController extends State {
value,
);
}
+
+ Future requestNotificationPermission() async {
+ await Matrix.of(context).requestNotificationPermission();
+ if (mounted) setState(() {});
+ }
// Pangea#
@override
diff --git a/lib/pages/settings_notifications/settings_notifications_view.dart b/lib/pages/settings_notifications/settings_notifications_view.dart
index d5efd9d31..e4e14aa79 100644
--- a/lib/pages/settings_notifications/settings_notifications_view.dart
+++ b/lib/pages/settings_notifications/settings_notifications_view.dart
@@ -6,6 +6,7 @@ import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/settings_notifications/push_rule_extensions.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
+import 'package:fluffychat/widgets/local_notifications_extension.dart';
import '../../utils/localized_exception_extension.dart';
import '../../widgets/matrix.dart';
import 'settings_notifications.dart';
@@ -49,6 +50,37 @@ class SettingsNotificationsView extends StatelessWidget {
child: Column(
children: [
// #Pangea
+ FutureBuilder(
+ future: Matrix.of(context).notificationsEnabled,
+ builder: (context, snapshot) => AnimatedSize(
+ duration: FluffyThemes.animationDuration,
+ child: snapshot.data != false
+ ? const SizedBox()
+ : Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 8.0,
+ ),
+ child: ListTile(
+ tileColor: theme.colorScheme.primaryContainer,
+ leading: Icon(
+ Icons.error_outline,
+ color: theme.colorScheme.onPrimaryContainer,
+ ),
+ title: Text(
+ L10n.of(context).enableNotificationsTitle,
+ style: TextStyle(
+ color: theme.colorScheme.onPrimaryContainer,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ subtitle: Text(
+ L10n.of(context).enableNotificationsDesc,
+ ),
+ onTap: controller.requestNotificationPermission,
+ ),
+ ),
+ ),
+ ),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart
index 49b0b003c..5a0092a9a 100644
--- a/lib/pages/settings_style/settings_style.dart
+++ b/lib/pages/settings_style/settings_style.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
-import 'package:fluffychat/config/setting_keys.dart';
+import 'package:fluffychat/pangea/user/style_settings_repo.dart';
import 'package:fluffychat/utils/account_config.dart';
import 'package:fluffychat/utils/file_selector.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@@ -157,10 +157,16 @@ class SettingsStyleController extends State {
void changeFontSizeFactor(double d) {
setState(() => AppConfig.fontSizeFactor = d);
- Matrix.of(context).store.setString(
- SettingKeys.fontSizeFactor,
- AppConfig.fontSizeFactor.toString(),
- );
+ // #Pangea
+ // Matrix.of(context).store.setString(
+ // SettingKeys.fontSizeFactor,
+ // AppConfig.fontSizeFactor.toString(),
+ // );
+ StyleSettingsRepo.setFontSizeFactor(
+ Matrix.of(context).client.userID!,
+ AppConfig.fontSizeFactor,
+ );
+ // Pangea#
}
@override
diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart
index 1c11c0f67..6bf3b2a79 100644
--- a/lib/pages/settings_style/settings_style_view.dart
+++ b/lib/pages/settings_style/settings_style_view.dart
@@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:matrix/matrix.dart';
-import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/events/state_message.dart';
@@ -16,7 +15,6 @@ import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import '../../config/app_config.dart';
-import '../../widgets/settings_switch_list_tile.dart';
import 'settings_style.dart';
class SettingsStyleView extends StatelessWidget {
@@ -335,31 +333,31 @@ class SettingsStyleView extends StatelessWidget {
semanticFormatterCallback: (d) => d.toString(),
onChanged: controller.changeFontSizeFactor,
),
- Divider(
- color: theme.dividerColor,
- ),
- ListTile(
- title: Text(
- L10n.of(context).overview,
- style: TextStyle(
- color: theme.colorScheme.secondary,
- fontWeight: FontWeight.bold,
- ),
- ),
- ),
- SettingsSwitchListTile.adaptive(
- title: L10n.of(context).presencesToggle,
- onChanged: (b) => AppConfig.showPresences = b,
- storeKey: SettingKeys.showPresences,
- defaultValue: AppConfig.showPresences,
- ),
- SettingsSwitchListTile.adaptive(
- title: L10n.of(context).separateChatTypes,
- onChanged: (b) => AppConfig.separateChatTypes = b,
- storeKey: SettingKeys.separateChatTypes,
- defaultValue: AppConfig.separateChatTypes,
- ),
// #Pangea
+ // Divider(
+ // color: theme.dividerColor,
+ // ),
+ // ListTile(
+ // title: Text(
+ // L10n.of(context).overview,
+ // style: TextStyle(
+ // color: theme.colorScheme.secondary,
+ // fontWeight: FontWeight.bold,
+ // ),
+ // ),
+ // ),
+ // SettingsSwitchListTile.adaptive(
+ // title: L10n.of(context).presencesToggle,
+ // onChanged: (b) => AppConfig.showPresences = b,
+ // storeKey: SettingKeys.showPresences,
+ // defaultValue: AppConfig.showPresences,
+ // ),
+ // SettingsSwitchListTile.adaptive(
+ // title: L10n.of(context).separateChatTypes,
+ // onChanged: (b) => AppConfig.separateChatTypes = b,
+ // storeKey: SettingKeys.separateChatTypes,
+ // defaultValue: AppConfig.separateChatTypes,
+ // ),
// SettingsSwitchListTile.adaptive(
// title: L10n.of(context).displayNavigationRail,
// onChanged: (b) => AppConfig.displayNavigationRail = b,
diff --git a/lib/pangea/activity_planner/activity_plan_model.dart b/lib/pangea/activity_planner/activity_plan_model.dart
index 978d448a6..4546aaacd 100644
--- a/lib/pangea/activity_planner/activity_plan_model.dart
+++ b/lib/pangea/activity_planner/activity_plan_model.dart
@@ -1,6 +1,9 @@
+import 'dart:math';
+
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
+import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
@@ -44,9 +47,18 @@ class ActivityPlanModel {
_roles = roles,
_imageURL = imageURL;
+ List get placeholderImages => [
+ "${AppConfig.assetsBaseURL}/Space%20template%202.png",
+ "${AppConfig.assetsBaseURL}/Space%20template%203.png",
+ "${AppConfig.assetsBaseURL}/Space%20template%204.png",
+ ];
+
+ String get randomPlaceholder => placeholderImages[
+ Random(title.hashCode).nextInt(placeholderImages.length)];
+
Uri? get imageURL => _imageURL != null
? Uri.tryParse("${Environment.cmsApi}$_imageURL")
- : null;
+ : Uri.tryParse(randomPlaceholder);
Map get roles {
if (_roles != null) return _roles!;
diff --git a/lib/pangea/activity_sessions/activity_participant_indicator.dart b/lib/pangea/activity_sessions/activity_participant_indicator.dart
index 58fd0403f..c5871e820 100644
--- a/lib/pangea/activity_sessions/activity_participant_indicator.dart
+++ b/lib/pangea/activity_sessions/activity_participant_indicator.dart
@@ -2,12 +2,11 @@ import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:matrix/matrix.dart';
-import 'package:shimmer/shimmer.dart';
-import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/bot/widgets/bot_settings_language_icon.dart';
+import 'package:fluffychat/pangea/common/widgets/shimmer_background.dart';
import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
@@ -46,6 +45,7 @@ class ActivityParticipantIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
+ final borderRadius = this.borderRadius ?? BorderRadius.circular(8.0);
return MouseRegion(
cursor: SystemMouseCursors.basic,
child: GestureDetector(
@@ -71,11 +71,11 @@ class ActivityParticipantIndicator extends StatelessWidget {
size: 60.0,
userId: userId,
miniIcon:
- room != null && userId == BotName.byEnvironment
+ room != null && user?.id == BotName.byEnvironment
? BotSettingsLanguageIcon(user: user!)
: null,
presenceOffset:
- room != null && userId == BotName.byEnvironment
+ room != null && user?.id == BotName.byEnvironment
? const Offset(0, 0)
: null,
)
@@ -98,52 +98,53 @@ class ActivityParticipantIndicator extends StatelessWidget {
);
return Opacity(
opacity: opacity,
- child: Container(
- padding: padding ??
- const EdgeInsets.symmetric(
- vertical: 4.0,
- horizontal: 8.0,
- ),
- decoration: BoxDecoration(
- borderRadius: borderRadius ?? BorderRadius.circular(8.0),
- color: (hovered || selected) && selectable
- ? theme.colorScheme.surfaceContainerHighest
- : theme.colorScheme.surface.withAlpha(130),
- ),
- height: 125.0,
- constraints: const BoxConstraints(maxWidth: 100.0),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- shimmer && !selected
- ? Shimmer.fromColors(
- baseColor: AppConfig.gold.withAlpha(20),
- highlightColor: AppConfig.gold.withAlpha(50),
- child: avatar,
- )
- : avatar,
- Text(
- name,
- style: const TextStyle(
- fontSize: 12.0,
+ child: ShimmerBackground(
+ enabled: shimmer,
+ borderRadius: borderRadius,
+ child: Container(
+ alignment: Alignment.center,
+ padding: padding ??
+ const EdgeInsets.symmetric(
+ vertical: 4.0,
+ horizontal: 8.0,
),
- textAlign: TextAlign.center,
- ),
- Text(
- userId?.localpart ?? L10n.of(context).openRoleLabel,
- style: TextStyle(
- fontSize: 12.0,
- color: (Theme.of(context).brightness ==
- Brightness.light
- ? (userId?.localpart?.darkColor ?? name.darkColor)
- : (userId?.localpart?.lightColorText ??
- name.lightColorText)),
+ decoration: BoxDecoration(
+ borderRadius: borderRadius,
+ color: (hovered || selected) && selectable
+ ? theme.colorScheme.surfaceContainerHighest
+ : theme.colorScheme.surface.withAlpha(130),
+ ),
+ height: 125.0,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ avatar,
+ Text(
+ name,
+ style: const TextStyle(
+ fontSize: 12.0,
+ ),
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ textAlign: TextAlign.center,
),
- textAlign: TextAlign.center,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- ],
+ Text(
+ userId?.localpart ?? L10n.of(context).openRoleLabel,
+ style: TextStyle(
+ fontSize: 12.0,
+ color: (Theme.of(context).brightness ==
+ Brightness.light
+ ? (userId?.localpart?.darkColor ??
+ name.darkColor)
+ : (userId?.localpart?.lightColorText ??
+ name.lightColorText)),
+ ),
+ textAlign: TextAlign.center,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
+ ),
),
),
);
diff --git a/lib/pangea/activity_sessions/activity_participant_list.dart b/lib/pangea/activity_sessions/activity_participant_list.dart
index ecc3f7a7f..a29c3f23a 100644
--- a/lib/pangea/activity_sessions/activity_participant_list.dart
+++ b/lib/pangea/activity_sessions/activity_participant_list.dart
@@ -52,51 +52,79 @@ class ActivityParticipantList extends StatelessWidget {
spacing: 12.0,
mainAxisSize: MainAxisSize.min,
children: [
- Wrap(
- alignment: WrapAlignment.center,
- spacing: 12.0,
- runSpacing: 12.0,
- children: availableRoles.map((availableRole) {
- final selected =
- isSelected != null ? isSelected!(availableRole.id) : false;
+ LayoutBuilder(
+ builder: (context, constraints) {
+ const minItemWidth = 125.0;
- final assignedRole = assignedRoles[availableRole.id] ??
- (selected
- ? ActivityRoleModel(
- id: availableRole.id,
- userId: Matrix.of(context).client.userID!,
- role: availableRole.name,
- )
- : null);
+ final rows = (availableRoles.length /
+ (constraints.maxWidth / minItemWidth))
+ .ceil();
- final User? user = participants.participants.firstWhereOrNull(
- (u) => u.id == assignedRole?.userId,
- ) ??
- course?.getParticipants().firstWhereOrNull(
- (u) => u.id == assignedRole?.userId,
+ final entriesPerRow = (availableRoles.length / rows).ceil();
+
+ return Column(
+ spacing: 8.0,
+ children: List.generate(rows, (rowIndex) {
+ final entries = availableRoles
+ .skip(rowIndex * entriesPerRow)
+ .take(entriesPerRow)
+ .toList();
+
+ return Row(
+ spacing: 8.0,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: entries.map((availableRole) {
+ final selected = isSelected != null
+ ? isSelected!(availableRole.id)
+ : false;
+
+ final assignedRole = assignedRoles[availableRole.id] ??
+ (selected
+ ? ActivityRoleModel(
+ id: availableRole.id,
+ userId: Matrix.of(context).client.userID!,
+ role: availableRole.name,
+ )
+ : null);
+
+ final User? user =
+ participants.participants.firstWhereOrNull(
+ (u) => u.id == assignedRole?.userId,
+ ) ??
+ course?.getParticipants().firstWhereOrNull(
+ (u) => u.id == assignedRole?.userId,
+ );
+
+ final selectable = canSelect != null
+ ? canSelect!(availableRole.id)
+ : true;
+
+ final shimmering = isShimmering != null
+ ? isShimmering!(availableRole.id)
+ : false;
+
+ return Expanded(
+ child: ActivityParticipantIndicator(
+ name: availableRole.name,
+ userId: assignedRole?.userId,
+ opacity: getOpacity != null
+ ? getOpacity!(assignedRole)
+ : 1.0,
+ user: user,
+ onTap: onTap != null && selectable
+ ? () => onTap!(availableRole.id)
+ : null,
+ selected: selected,
+ selectable: selectable,
+ shimmer: shimmering,
+ room: room,
+ ),
);
-
- final selectable =
- canSelect != null ? canSelect!(availableRole.id) : true;
-
- final shimmering = isShimmering != null
- ? isShimmering!(availableRole.id)
- : false;
-
- return ActivityParticipantIndicator(
- name: availableRole.name,
- userId: assignedRole?.userId,
- opacity: getOpacity != null ? getOpacity!(assignedRole) : 1.0,
- user: user,
- onTap: onTap != null && selectable
- ? () => onTap!(availableRole.id)
- : null,
- selected: selected,
- selectable: selectable,
- shimmer: shimmering,
- room: room,
+ }).toList(),
+ );
+ }),
);
- }).toList(),
+ },
),
Wrap(
alignment: WrapAlignment.center,
diff --git a/lib/pangea/activity_sessions/activity_room_extension.dart b/lib/pangea/activity_sessions/activity_room_extension.dart
index 19bd114b9..e48a8cfd5 100644
--- a/lib/pangea/activity_sessions/activity_room_extension.dart
+++ b/lib/pangea/activity_sessions/activity_room_extension.dart
@@ -353,7 +353,8 @@ extension ActivityRoomExtension on Room {
bool get isActivitySession =>
(roomType?.startsWith(PangeaRoomTypes.activitySession) == true ||
activityPlan != null) &&
- activityPlan?.isDeprecatedModel == false;
+ activityPlan?.isDeprecatedModel == false &&
+ activityPlan?.activityId != null;
String? get activityId {
if (!isActivitySession) return null;
diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart
index b1f6ccb8c..582fddb85 100644
--- a/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart
+++ b/lib/pangea/activity_sessions/activity_session_chat/activity_chat_extension.dart
@@ -1,3 +1,4 @@
+import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
@@ -12,7 +13,8 @@ extension ActivityMenuLogic on ChatController {
if (AppConfig.showedActivityMenu ||
InstructionsEnum.activityStatsMenu.isToggledOff ||
MatrixState.pAnyState.isOverlayOpen(RegExp(r"^word-zoom-card-.*$")) ||
- timeline == null) {
+ timeline == null ||
+ GoRouterState.of(context).fullPath?.endsWith(':roomid') != true) {
return false;
}
@@ -42,15 +44,9 @@ extension ActivityMenuLogic on ChatController {
return false;
}
- final l1 =
- MatrixState.pangeaController.userController.userL1?.langCodeShort;
final l2 =
MatrixState.pangeaController.userController.userL2?.langCodeShort;
final activityLang = room.activityPlan?.req.targetLanguage.split('-').first;
-
- return activityLang != null &&
- l2 != null &&
- l2 != activityLang &&
- l1 != activityLang;
+ return activityLang != null && l2 != activityLang;
}
}
diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart
index cd2159910..dbd0b0578 100644
--- a/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart
+++ b/lib/pangea/activity_sessions/activity_session_chat/activity_finished_status_message.dart
@@ -7,8 +7,9 @@ import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
+import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
-import 'package:fluffychat/widgets/future_loading_dialog.dart';
+import 'package:fluffychat/pangea/languages/p_language_store.dart';
import 'package:fluffychat/widgets/matrix.dart';
class ActivityFinishedStatusMessage extends StatelessWidget {
@@ -19,25 +20,35 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
required this.controller,
});
- Future _onArchive(BuildContext context) async {
- final resp = await showFutureLoadingDialog(
- context: context,
- future: () => _archiveToAnalytics(context),
+ void _onArchive(BuildContext context) {
+ _archiveToAnalytics();
+ context.go(
+ "/rooms/spaces/${controller.room.courseParent!.id}/details?tab=course",
);
-
- if (!resp.isError) {
- context.go(
- "/rooms/spaces/${controller.room.courseParent!.id}/details?tab=course",
- );
- }
}
- Future _archiveToAnalytics(BuildContext context) async {
- await controller.room.archiveActivity();
- await Matrix.of(context)
- .analyticsDataService
- .updateService
- .sendActivityAnalytics(controller.room.id);
+ Future _archiveToAnalytics() async {
+ try {
+ final activityPlan = controller.room.activityPlan;
+ if (activityPlan == null) {
+ throw Exception("No activity plan found for room");
+ }
+
+ final lang = activityPlan.req.targetLanguage.split("-").first;
+ final langModel = PLanguageStore.byLangCode(lang)!;
+ await controller.room.archiveActivity();
+ await MatrixState
+ .pangeaController.matrixState.analyticsDataService.updateService
+ .sendActivityAnalytics(controller.room.id, langModel);
+ } catch (e, s) {
+ ErrorHandler.logError(
+ e: e,
+ s: s,
+ data: {
+ 'roomId': controller.room.id,
+ },
+ );
+ }
}
ActivitySummaryModel? get summary => controller.room.activitySummary;
diff --git a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart
index bd78b0538..0a05e5c47 100644
--- a/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart
+++ b/lib/pangea/activity_sessions/activity_session_chat/activity_vocab_widget.dart
@@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance/token_rendering_util.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance/tokens_util.dart';
+import 'package:fluffychat/pangea/toolbar/reading_assistance/underline_text_widget.dart';
import 'package:fluffychat/pangea/toolbar/token_rendering_mixin.dart';
import 'package:fluffychat/pangea/toolbar/word_card/word_zoom_widget.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
@@ -105,27 +106,17 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin {
OverlayUtil.showPositionedCard(
overlayKey: target,
context: context,
- cardToShow: StatefulBuilder(
- builder: (context, setState) => WordZoomWidget(
- token: PangeaTokenText(
- content: v.lemma,
- length: v.lemma.characters.length,
- offset: 0,
- ),
- construct: ConstructIdentifier(
- lemma: v.lemma,
- type: ConstructTypeEnum.vocab,
- category: v.pos,
- ),
- langCode: widget.langCode,
- onClose: () {
- MatrixState.pAnyState.closeOverlay(target);
- setState(() => _selectedVocab = null);
- },
- onDismissNewWordOverlay: () {
- if (mounted) setState(() {});
- },
- ),
+ cardToShow: _WordCardWrapper(
+ v: v,
+ langCode: widget.langCode,
+ target: target,
+ onClose: () {
+ if (mounted) {
+ WidgetsBinding.instance.addPostFrameCallback(
+ (_) => setState(() => _selectedVocab = null),
+ );
+ }
+ },
),
transformTargetId: target,
closePrevOverlay: false,
@@ -143,12 +134,6 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin {
tokens,
widget.activityLangCode,
);
- final renderer = TokenRenderingUtil(
- existingStyle: TextStyle(
- color: Theme.of(context).colorScheme.onSurface,
- fontSize: 14.0,
- ),
- );
return Wrap(
spacing: 4.0,
@@ -186,13 +171,14 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin {
color: color,
borderRadius: BorderRadius.circular(20),
),
- child: Text(
- v.lemma,
- style: renderer.style(
- underlineColor: Theme.of(context)
- .colorScheme
- .primary
- .withAlpha(200),
+ child: UnderlineText(
+ text: v.lemma,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.onSurface,
+ fontSize: 14.0,
+ ),
+ underlineColor: TokenRenderingUtil.underlineColor(
+ Theme.of(context).colorScheme.primary.withAlpha(200),
isNew: isNew,
selected: _selectedVocab == v,
hovered: hovered,
@@ -208,3 +194,52 @@ class _VocabChipsState extends State<_VocabChips> with TokenRenderingMixin {
);
}
}
+
+class _WordCardWrapper extends StatefulWidget {
+ final Vocab v;
+ final String langCode;
+ final String target;
+ final VoidCallback onClose;
+
+ const _WordCardWrapper({
+ required this.v,
+ required this.langCode,
+ required this.target,
+ required this.onClose,
+ });
+
+ @override
+ State<_WordCardWrapper> createState() => _WordCardWrapperState();
+}
+
+class _WordCardWrapperState extends State<_WordCardWrapper> {
+ @override
+ void dispose() {
+ widget.onClose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return WordZoomWidget(
+ token: PangeaTokenText(
+ content: widget.v.lemma,
+ length: widget.v.lemma.characters.length,
+ offset: 0,
+ ),
+ construct: ConstructIdentifier(
+ lemma: widget.v.lemma,
+ type: ConstructTypeEnum.vocab,
+ category: widget.v.pos,
+ ),
+ langCode: widget.langCode,
+ onClose: () {
+ MatrixState.pAnyState.closeOverlay(widget.target);
+ widget.onClose();
+ },
+ onDismissNewWordOverlay: () {
+ if (mounted) setState(() {});
+ },
+ );
+ }
+}
diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart
index c2c39fb85..ecfaeae14 100644
--- a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart
+++ b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart
@@ -14,6 +14,7 @@ import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activ
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/bot_join_error_dialog.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/chat_settings/utils/room_summary_extension.dart';
+import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/course_plans/course_activities/activity_summaries_provider.dart';
import 'package:fluffychat/pangea/course_plans/course_activities/course_activity_repo.dart';
import 'package:fluffychat/pangea/course_plans/course_activities/course_activity_translation_request.dart';
@@ -293,8 +294,17 @@ class ActivitySessionStartController extends State
);
}
await Future.wait(futures);
- } catch (e) {
+ } catch (e, s) {
error = e;
+ ErrorHandler.logError(
+ e: e,
+ s: s,
+ data: {
+ "activityId": widget.activityId,
+ "roomId": widget.roomId,
+ "parentId": widget.parentId,
+ },
+ );
} finally {
if (mounted) {
setState(() => loading = false);
diff --git a/lib/pangea/activity_sessions/activity_summary_widget.dart b/lib/pangea/activity_sessions/activity_summary_widget.dart
index df4bb64ed..7ef14d940 100644
--- a/lib/pangea/activity_sessions/activity_summary_widget.dart
+++ b/lib/pangea/activity_sessions/activity_summary_widget.dart
@@ -9,6 +9,7 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix/src/utils/markdown.dart';
+import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
@@ -35,6 +36,8 @@ class ActivitySummary extends StatelessWidget {
final ValueNotifier>? usedVocab;
+ final bool inChat;
+
const ActivitySummary({
super.key,
required this.activity,
@@ -49,6 +52,7 @@ class ActivitySummary extends StatelessWidget {
this.getParticipantOpacity,
this.room,
this.course,
+ this.inChat = false,
});
@override
@@ -63,18 +67,20 @@ class ActivitySummary extends StatelessWidget {
child: Column(
spacing: 4.0,
children: [
- LayoutBuilder(
- builder: (context, constraints) {
- return ImageByUrl(
- imageUrl: activity.imageURL,
- width: min(
- constraints.maxWidth,
- MediaQuery.sizeOf(context).height * 0.5,
- ),
- borderRadius: BorderRadius.circular(20),
- );
- },
- ),
+ (!inChat || !AppConfig.useActivityImageAsChatBackground)
+ ? LayoutBuilder(
+ builder: (context, constraints) {
+ return ImageByUrl(
+ imageUrl: activity.imageURL,
+ width: min(
+ constraints.maxWidth,
+ MediaQuery.sizeOf(context).height * 0.5,
+ ),
+ borderRadius: BorderRadius.circular(20),
+ );
+ },
+ )
+ : const SizedBox.shrink(),
ActivityParticipantList(
activity: activity,
room: room,
@@ -91,17 +97,15 @@ class ActivitySummary extends StatelessWidget {
color: theme.colorScheme.surface.withAlpha(128),
borderRadius: BorderRadius.circular(12.0),
),
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 8.0,
- ),
- child: Column(
- spacing: 4.0,
- mainAxisSize: MainAxisSize.min,
- children: [
- InkWell(
- hoverColor: theme.colorScheme.surfaceTint.withAlpha(55),
- onTap: toggleInstructions,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ InkWell(
+ borderRadius: BorderRadius.circular(12.0),
+ hoverColor: theme.colorScheme.surfaceTint.withAlpha(55),
+ onTap: toggleInstructions,
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
child: Column(
spacing: 4.0,
children: [
@@ -133,69 +137,75 @@ class ActivitySummary extends StatelessWidget {
],
),
),
- if (showInstructions) ...[
- Row(
- spacing: 8.0,
- mainAxisSize: MainAxisSize.min,
+ ),
+ if (showInstructions)
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
children: [
- Text(
- activity.req.mode,
- style: theme.textTheme.bodyMedium,
- ),
Row(
- spacing: 4.0,
+ spacing: 8.0,
mainAxisSize: MainAxisSize.min,
children: [
- const Icon(Icons.school, size: 12.0),
Text(
- activity.req.cefrLevel.string,
+ activity.req.mode,
style: theme.textTheme.bodyMedium,
),
+ Row(
+ spacing: 4.0,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.school, size: 12.0),
+ Text(
+ activity.req.cefrLevel.string,
+ style: theme.textTheme.bodyMedium,
+ ),
+ ],
+ ),
],
),
+ ActivitySessionDetailsRow(
+ icon: Symbols.target,
+ iconSize: 16.0,
+ child: Text(
+ activity.learningObjective,
+ style: theme.textTheme.bodyMedium,
+ ),
+ ),
+ ActivitySessionDetailsRow(
+ icon: Symbols.steps,
+ iconSize: 16.0,
+ child: Html(
+ data: markdown(activity.instructions),
+ style: {
+ "body": Style(
+ margin: Margins.all(0),
+ padding: HtmlPaddings.all(0),
+ fontSize: FontSize(
+ theme.textTheme.bodyMedium!.fontSize!,
+ ),
+ ),
+ },
+ ),
+ ),
+ ActivitySessionDetailsRow(
+ icon: Symbols.dictionary,
+ iconSize: 16.0,
+ child: ActivityVocabWidget(
+ key: ValueKey(
+ "activity-summary-${activity.activityId}",
+ ),
+ vocab: activity.vocab,
+ langCode: activity.req.targetLanguage,
+ targetId: "activity-summary-vocab",
+ usedVocab: usedVocab,
+ activityLangCode: activity.req.targetLanguage,
+ ),
+ ),
],
),
- ActivitySessionDetailsRow(
- icon: Symbols.target,
- iconSize: 16.0,
- child: Text(
- activity.learningObjective,
- style: theme.textTheme.bodyMedium,
- ),
- ),
- ActivitySessionDetailsRow(
- icon: Symbols.steps,
- iconSize: 16.0,
- child: Html(
- data: markdown(activity.instructions),
- style: {
- "body": Style(
- margin: Margins.all(0),
- padding: HtmlPaddings.all(0),
- fontSize: FontSize(
- theme.textTheme.bodyMedium!.fontSize!,
- ),
- ),
- },
- ),
- ),
- ActivitySessionDetailsRow(
- icon: Symbols.dictionary,
- iconSize: 16.0,
- child: ActivityVocabWidget(
- key: ValueKey(
- "activity-summary-${activity.activityId}",
- ),
- vocab: activity.vocab,
- langCode: activity.req.targetLanguage,
- targetId: "activity-summary-vocab",
- usedVocab: usedVocab,
- activityLangCode: activity.req.targetLanguage,
- ),
- ),
- ],
- ],
- ),
+ ),
+ ],
),
),
],
diff --git a/lib/pangea/activity_sessions/activity_user_summaries_widget.dart b/lib/pangea/activity_sessions/activity_user_summaries_widget.dart
index 2553f1a5a..39db710db 100644
--- a/lib/pangea/activity_sessions/activity_user_summaries_widget.dart
+++ b/lib/pangea/activity_sessions/activity_user_summaries_widget.dart
@@ -202,14 +202,11 @@ class ButtonControlledCarouselView extends StatelessWidget {
],
),
Flexible(
- child: SingleChildScrollView(
- child: Text(
- p.displayFeedback(
- user?.calcDisplayname() ??
- p.participantId.localpart ??
- p.participantId,
- ),
- style: const TextStyle(fontSize: 14.0),
+ child: _SummaryText(
+ text: p.displayFeedback(
+ user?.calcDisplayname() ??
+ p.participantId.localpart ??
+ p.participantId,
),
),
),
@@ -290,14 +287,20 @@ class ButtonControlledCarouselView extends StatelessWidget {
(role) => role.userId == p.participantId,
);
final userRoleInfo = availableRoles[userRole.id]!;
- return ActivityParticipantIndicator(
- name: userRoleInfo.name,
- userId: p.participantId,
- user: user,
- borderRadius: BorderRadius.circular(4),
- selected: highlightedRole?.id == userRole.id,
- onTap: () => _scrollToUser(userRole, index, cardWidth),
- room: controller.room,
+ return SizedBox(
+ width: 100.0,
+ height: 125.0,
+ child: Center(
+ child: ActivityParticipantIndicator(
+ name: userRoleInfo.name,
+ userId: p.participantId,
+ user: user,
+ borderRadius: BorderRadius.circular(4),
+ selected: highlightedRole?.id == userRole.id,
+ onTap: () => _scrollToUser(userRole, index, cardWidth),
+ room: controller.room,
+ ),
+ ),
);
},
);
@@ -334,3 +337,38 @@ class SuperlativeTile extends StatelessWidget {
);
}
}
+
+class _SummaryText extends StatefulWidget {
+ final String text;
+ const _SummaryText({
+ required this.text,
+ });
+
+ @override
+ State<_SummaryText> createState() => _SummaryTextState();
+}
+
+class _SummaryTextState extends State<_SummaryText> {
+ final ScrollController _scrollController = ScrollController();
+
+ @override
+ void dispose() {
+ _scrollController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scrollbar(
+ controller: _scrollController,
+ thumbVisibility: true,
+ child: SingleChildScrollView(
+ controller: _scrollController,
+ child: Text(
+ widget.text,
+ style: const TextStyle(fontSize: 14.0),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pangea/analytics_data/analytics_data_service.dart b/lib/pangea/analytics_data/analytics_data_service.dart
index f1312c625..bd371aa81 100644
--- a/lib/pangea/analytics_data/analytics_data_service.dart
+++ b/lib/pangea/analytics_data/analytics_data_service.dart
@@ -14,10 +14,12 @@ import 'package:fluffychat/pangea/analytics_data/level_up_analytics_service.dart
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
+import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_settings/analytics_settings_extension.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
+import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
import 'package:fluffychat/pangea/languages/language_model.dart';
import 'package:fluffychat/pangea/user/analytics_profile_model.dart';
import 'package:fluffychat/widgets/matrix.dart';
@@ -85,6 +87,7 @@ class AnalyticsDataService {
void dispose() {
_syncController?.dispose();
updateDispatcher.dispose();
+ updateService.dispose();
_closeDatabase();
}
@@ -121,7 +124,10 @@ class AnalyticsDataService {
_invalidateCaches();
final analyticsUserId = await _analyticsClientGetter.database.getUserID();
- if (analyticsUserId != client.userID) {
+ final lastUpdated =
+ await _analyticsClientGetter.database.getLastUpdated();
+
+ if (analyticsUserId != client.userID || lastUpdated == null) {
await _clearDatabase();
await _analyticsClientGetter.database.updateUserID(client.userID!);
}
@@ -157,6 +163,7 @@ class AnalyticsDataService {
Logs().i("Analytics database initialized.");
initCompleter.complete();
updateDispatcher.sendConstructAnalyticsUpdate(AnalyticsUpdate([]));
+ updateDispatcher.sendActivityAnalyticsUpdate(null);
}
}
@@ -212,6 +219,8 @@ class AnalyticsDataService {
await _syncController?.waitForSync(analyticsRoomID);
}
+ DerivedAnalyticsDataModel? get cachedDerivedData => _cachedDerivedStats;
+
Future get derivedData async {
await _ensureInitialized();
@@ -232,12 +241,15 @@ class AnalyticsDataService {
int? count,
String? roomId,
DateTime? since,
+ ConstructUseTypeEnum? type,
+ bool filterCapped = true,
}) async {
await _ensureInitialized();
final uses = await _analyticsClientGetter.database.getUses(
count: count,
roomId: roomId,
since: since,
+ type: type,
);
final blocked = blockedConstructs;
@@ -246,12 +258,15 @@ class AnalyticsDataService {
final Map cappedLastUseCache = {};
for (final use in uses) {
if (blocked.contains(use.identifier)) continue;
+ if (use.category == 'other') continue;
+
if (!cappedLastUseCache.containsKey(use.identifier)) {
final constructs = await getConstructUse(use.identifier);
cappedLastUseCache[use.identifier] = constructs.cappedLastUse;
}
final cappedLastUse = cappedLastUseCache[use.identifier];
- if (cappedLastUse != null && use.timeStamp.isAfter(cappedLastUse)) {
+ if (filterCapped &&
+ (cappedLastUse != null && use.timeStamp.isAfter(cappedLastUse))) {
continue;
}
filtered.add(use);
@@ -317,7 +332,8 @@ class AnalyticsDataService {
final existing = cleaned[canonical];
if (existing != null) {
existing.merge(entry);
- } else if (!blocked.contains(canonical)) {
+ } else if (!blocked.contains(canonical) &&
+ canonical.category != 'other') {
cleaned[canonical] = entry;
}
}
@@ -338,7 +354,10 @@ class AnalyticsDataService {
final blocked = blockedConstructs;
final uses = newConstructs
.where(
- (c) => c.constructType == type && !blocked.contains(c.identifier),
+ (c) =>
+ c.constructType == type &&
+ !blocked.contains(c.identifier) &&
+ c.identifier.category != 'other',
)
.toList();
@@ -371,7 +390,9 @@ class AnalyticsDataService {
AnalyticsUpdate update,
) async {
final events = [];
- final updateIds = update.addedConstructs.map((c) => c.identifier).toList();
+ final addedConstructs =
+ update.addedConstructs.where((c) => c.category != 'other').toList();
+ final updateIds = addedConstructs.map((c) => c.identifier).toList();
final prevData = await derivedData;
final prevConstructs = await getConstructUses(updateIds);
@@ -380,9 +401,12 @@ class AnalyticsDataService {
await _ensureInitialized();
final blocked = blockedConstructs;
- _mergeTable.addConstructsByUses(update.addedConstructs, blocked);
+ final newUnusedConstructs =
+ updateIds.where((id) => !hasUsedConstruct(id)).toSet();
+
+ _mergeTable.addConstructsByUses(addedConstructs, blocked);
await _analyticsClientGetter.database.updateLocalAnalytics(
- update.addedConstructs,
+ addedConstructs,
);
final newConstructs = await getConstructUses(updateIds);
@@ -433,10 +457,31 @@ class AnalyticsDataService {
events.add(MorphUnlockedEvent(newUnlockedMorphs));
}
+ for (final entry in newConstructs.entries) {
+ final prevConstruct = prevConstructs[entry.key];
+ if (prevConstruct == null) continue;
+
+ final prevLevel = prevConstruct.lemmaCategory;
+ final newLevel = entry.value.lemmaCategory;
+ if (newLevel.xpNeeded > prevLevel.xpNeeded) {
+ events.add(
+ ConstructLevelUpEvent(
+ entry.key,
+ newLevel,
+ update.targetID,
+ ),
+ );
+ }
+ }
+
if (update.blockedConstruct != null) {
events.add(ConstructBlockedEvent(update.blockedConstruct!));
}
+ if (newUnusedConstructs.isNotEmpty) {
+ events.add(NewConstructsEvent(newUnusedConstructs));
+ }
+
return events;
}
diff --git a/lib/pangea/analytics_data/analytics_database.dart b/lib/pangea/analytics_data/analytics_database.dart
index 8a1f089c5..870bd9f61 100644
--- a/lib/pangea/analytics_data/analytics_database.dart
+++ b/lib/pangea/analytics_data/analytics_database.dart
@@ -10,6 +10,7 @@ import 'package:synchronized/synchronized.dart';
import 'package:fluffychat/pangea/analytics_data/derived_analytics_data_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
+import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
@@ -177,6 +178,12 @@ class AnalyticsDatabase with DatabaseFileStorage {
Future getUserID() => _lastEventTimestampBox.get('user_id');
+ Future getLastUpdated() async {
+ final entry = await _lastEventTimestampBox.get('last_updated');
+ if (entry == null) return null;
+ return DateTime.tryParse(entry);
+ }
+
Future getLastEventTimestamp() async {
final timestampString =
await _lastEventTimestampBox.get('last_event_timestamp');
@@ -197,6 +204,7 @@ class AnalyticsDatabase with DatabaseFileStorage {
int? count,
String? roomId,
DateTime? since,
+ ConstructUseTypeEnum? type,
}) async {
final stopwatch = Stopwatch()..start();
final results = [];
@@ -208,6 +216,9 @@ class AnalyticsDatabase with DatabaseFileStorage {
if (roomId != null && use.metadata.roomId != roomId) {
return true; // skip but continue
}
+ if (type != null && use.useType != type) {
+ return true; // skip but continue
+ }
results.add(use);
return count == null || results.length < count;
@@ -395,10 +406,7 @@ class AnalyticsDatabase with DatabaseFileStorage {
);
}
- for (final u in usesForKey) {
- model.addUse(u);
- }
-
+ model.addUses(usesForKey);
updates[key] = model;
}
@@ -480,6 +488,15 @@ class AnalyticsDatabase with DatabaseFileStorage {
});
}
+ Future updateLastUpdated(DateTime timestamp) {
+ return _transaction(() async {
+ await _lastEventTimestampBox.put(
+ 'last_updated',
+ timestamp.toIso8601String(),
+ );
+ });
+ }
+
Future updateXPOffset(int offset) {
return _transaction(() async {
final stats = await getDerivedStats();
@@ -575,6 +592,8 @@ class AnalyticsDatabase with DatabaseFileStorage {
);
});
+ await updateLastUpdated(DateTime.now());
+
stopwatch.stop();
Logs().i(
"Server analytics update took ${stopwatch.elapsedMilliseconds} ms",
@@ -629,6 +648,8 @@ class AnalyticsDatabase with DatabaseFileStorage {
}
});
+ await updateLastUpdated(DateTime.now());
+
stopwatch.stop();
Logs().i("Local analytics update took ${stopwatch.elapsedMilliseconds} ms");
}
diff --git a/lib/pangea/analytics_data/analytics_sync_controller.dart b/lib/pangea/analytics_data/analytics_sync_controller.dart
index 53ccd760a..f895c5657 100644
--- a/lib/pangea/analytics_data/analytics_sync_controller.dart
+++ b/lib/pangea/analytics_data/analytics_sync_controller.dart
@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart';
+import 'package:fluffychat/pangea/analytics_data/analytics_update_dispatcher.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
@@ -52,6 +53,13 @@ class AnalyticsSyncController {
if (constructEvents.isEmpty) return;
await dataService.updateServerAnalytics(constructEvents);
+
+ // Server updates do not usually need to update the UI, since usually they are only
+ // transfering local data to the server. However, if a user if using multiple devices,
+ // we do need to update the UI when new data comes from the server.
+ dataService.updateDispatcher.sendConstructAnalyticsUpdate(
+ AnalyticsUpdate([]),
+ );
}
Future waitForSync(String analyticsRoomId) async {
diff --git a/lib/pangea/analytics_data/analytics_update_dispatcher.dart b/lib/pangea/analytics_data/analytics_update_dispatcher.dart
index 84d697020..acf7052fc 100644
--- a/lib/pangea/analytics_data/analytics_update_dispatcher.dart
+++ b/lib/pangea/analytics_data/analytics_update_dispatcher.dart
@@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_update_events.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
+import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
import 'package:fluffychat/pangea/lemmas/user_set_lemma_info.dart';
class LevelUpdate {
@@ -28,14 +29,26 @@ class AnalyticsUpdate {
});
}
+class ConstructLevelUpdate {
+ final ConstructIdentifier constructId;
+ final ConstructLevelEnum level;
+ final String? targetID;
+
+ ConstructLevelUpdate({
+ required this.constructId,
+ required this.level,
+ this.targetID,
+ });
+}
+
class AnalyticsUpdateDispatcher {
final AnalyticsDataService dataService;
final StreamController constructUpdateStream =
StreamController.broadcast();
- final StreamController activityAnalyticsStream =
- StreamController.broadcast();
+ final StreamController activityAnalyticsStream =
+ StreamController.broadcast();
final StreamController> unlockedConstructsStream =
StreamController>.broadcast();
@@ -43,6 +56,12 @@ class AnalyticsUpdateDispatcher {
final StreamController levelUpdateStream =
StreamController.broadcast();
+ final StreamController> newConstructsStream =
+ StreamController>.broadcast();
+
+ final StreamController constructLevelUpdateStream =
+ StreamController.broadcast();
+
final StreamController>
_lemmaInfoUpdateStream = StreamController<
MapEntry>.broadcast();
@@ -54,6 +73,7 @@ class AnalyticsUpdateDispatcher {
activityAnalyticsStream.close();
unlockedConstructsStream.close();
levelUpdateStream.close();
+ constructLevelUpdateStream.close();
_lemmaInfoUpdateStream.close();
}
@@ -65,7 +85,7 @@ class AnalyticsUpdateDispatcher {
.map((update) => update.value);
void sendActivityAnalyticsUpdate(
- String activityAnalytics,
+ String? activityAnalytics,
) =>
activityAnalyticsStream.add(activityAnalytics);
@@ -98,6 +118,12 @@ class AnalyticsUpdateDispatcher {
case final ConstructBlockedEvent e:
_onBlockedConstruct(e.blockedConstruct);
break;
+ case final ConstructLevelUpEvent e:
+ _onConstructLevelUp(e.constructId, e.level, e.targetID);
+ break;
+ case final NewConstructsEvent e:
+ _onNewConstruct(e.newConstructs);
+ break;
}
}
@@ -131,10 +157,29 @@ class AnalyticsUpdateDispatcher {
constructUpdateStream.add(update);
}
+ void _onConstructLevelUp(
+ ConstructIdentifier constructId,
+ ConstructLevelEnum level,
+ String? targetID,
+ ) {
+ constructLevelUpdateStream.add(
+ ConstructLevelUpdate(
+ constructId: constructId,
+ level: level,
+ targetID: targetID,
+ ),
+ );
+ }
+
void _onBlockedConstruct(ConstructIdentifier constructId) {
final update = AnalyticsStreamUpdate(
blockedConstruct: constructId,
);
constructUpdateStream.add(update);
}
+
+ void _onNewConstruct(Set constructIds) {
+ if (constructIds.isEmpty) return;
+ newConstructsStream.add(constructIds);
+ }
}
diff --git a/lib/pangea/analytics_data/analytics_update_events.dart b/lib/pangea/analytics_data/analytics_update_events.dart
index 511de32a5..2e7a02ce6 100644
--- a/lib/pangea/analytics_data/analytics_update_events.dart
+++ b/lib/pangea/analytics_data/analytics_update_events.dart
@@ -1,4 +1,5 @@
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
+import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
sealed class AnalyticsUpdateEvent {}
@@ -13,6 +14,17 @@ class MorphUnlockedEvent extends AnalyticsUpdateEvent {
MorphUnlockedEvent(this.unlocked);
}
+class ConstructLevelUpEvent extends AnalyticsUpdateEvent {
+ final ConstructIdentifier constructId;
+ final ConstructLevelEnum level;
+ final String? targetID;
+ ConstructLevelUpEvent(
+ this.constructId,
+ this.level,
+ this.targetID,
+ );
+}
+
class XPGainedEvent extends AnalyticsUpdateEvent {
final int points;
final String? targetID;
@@ -23,3 +35,8 @@ class ConstructBlockedEvent extends AnalyticsUpdateEvent {
final ConstructIdentifier blockedConstruct;
ConstructBlockedEvent(this.blockedConstruct);
}
+
+class NewConstructsEvent extends AnalyticsUpdateEvent {
+ final Set newConstructs;
+ NewConstructsEvent(this.newConstructs);
+}
diff --git a/lib/pangea/analytics_data/analytics_update_service.dart b/lib/pangea/analytics_data/analytics_update_service.dart
index cdb832650..e0cd2d66c 100644
--- a/lib/pangea/analytics_data/analytics_update_service.dart
+++ b/lib/pangea/analytics_data/analytics_update_service.dart
@@ -23,9 +23,19 @@ class AnalyticsUpdateService {
final AnalyticsDataService dataService;
- AnalyticsUpdateService(this.dataService);
+ AnalyticsUpdateService(this.dataService) {
+ _periodicTimer = Timer.periodic(
+ const Duration(minutes: 5),
+ (_) => sendLocalAnalyticsToAnalyticsRoom(),
+ );
+ }
Completer? _updateCompleter;
+ Timer? _periodicTimer;
+
+ void dispose() {
+ _periodicTimer?.cancel();
+ }
LanguageModel? get _l2 => MatrixState.pangeaController.userController.userL2;
@@ -50,8 +60,9 @@ class AnalyticsUpdateService {
Future addAnalytics(
String? targetID,
- List newConstructs,
- ) async {
+ List newConstructs, {
+ bool forceUpdate = false,
+ }) async {
await dataService.updateDispatcher.sendConstructAnalyticsUpdate(
AnalyticsUpdate(
newConstructs,
@@ -63,7 +74,9 @@ class AnalyticsUpdateService {
final lastUpdated = await dataService.getLastUpdatedAnalytics();
final difference = DateTime.now().difference(lastUpdated ?? DateTime.now());
- if (localConstructCount > _maxMessagesCached || difference.inMinutes > 10) {
+ if (forceUpdate ||
+ localConstructCount > _maxMessagesCached ||
+ difference.inMinutes > 10) {
sendLocalAnalyticsToAnalyticsRoom();
}
}
@@ -115,12 +128,14 @@ class AnalyticsUpdateService {
await future;
}
- Future sendActivityAnalytics(String roomId) async {
- final analyticsRoom = await _getAnalyticsRoom();
+ Future sendActivityAnalytics(String roomId, LanguageModel lang) async {
+ final analyticsRoom = await _getAnalyticsRoom(l2Override: lang);
if (analyticsRoom == null) return;
await analyticsRoom.addActivityRoomId(roomId);
- dataService.updateDispatcher.sendActivityAnalyticsUpdate(roomId);
+ if (lang.langCodeShort == _l2?.langCodeShort) {
+ dataService.updateDispatcher.sendActivityAnalyticsUpdate(roomId);
+ }
}
Future blockConstruct(ConstructIdentifier constructId) async {
diff --git a/lib/pangea/analytics_data/analytics_updater_mixin.dart b/lib/pangea/analytics_data/analytics_updater_mixin.dart
index fa90aab51..d24628010 100644
--- a/lib/pangea/analytics_data/analytics_updater_mixin.dart
+++ b/lib/pangea/analytics_data/analytics_updater_mixin.dart
@@ -3,12 +3,14 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart';
+import 'package:fluffychat/pangea/analytics_data/analytics_update_dispatcher.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/common/utils/overlay.dart';
import 'package:fluffychat/widgets/matrix.dart';
mixin AnalyticsUpdater on State {
StreamSubscription? _analyticsSubscription;
+ StreamSubscription? _constructLevelSubscription;
@override
void initState() {
@@ -16,11 +18,14 @@ mixin AnalyticsUpdater on State {
final updater = Matrix.of(context).analyticsDataService.updateDispatcher;
_analyticsSubscription =
updater.constructUpdateStream.stream.listen(_onAnalyticsUpdate);
+ _constructLevelSubscription =
+ updater.constructLevelUpdateStream.stream.listen(_onConstructLevelUp);
}
@override
void dispose() {
_analyticsSubscription?.cancel();
+ _constructLevelSubscription?.cancel();
super.dispose();
}
@@ -38,4 +43,15 @@ mixin AnalyticsUpdater on State {
OverlayUtil.showPointsGained(update.targetID!, update.points, context);
}
}
+
+ void _onConstructLevelUp(ConstructLevelUpdate update) {
+ if (update.targetID != null) {
+ OverlayUtil.showGrowthAnimation(
+ context,
+ update.targetID!,
+ update.level,
+ update.constructId,
+ );
+ }
+ }
}
diff --git a/lib/pangea/analytics_data/construct_merge_table.dart b/lib/pangea/analytics_data/construct_merge_table.dart
index b84786568..77cb2b065 100644
--- a/lib/pangea/analytics_data/construct_merge_table.dart
+++ b/lib/pangea/analytics_data/construct_merge_table.dart
@@ -1,5 +1,3 @@
-import 'package:collection/collection.dart';
-
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
@@ -7,7 +5,6 @@ import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
class ConstructMergeTable {
Map> lemmaTypeGroups = {};
- Map otherToSpecific = {};
final Map caseInsensitive = {};
void addConstructs(
@@ -27,6 +24,8 @@ class ConstructMergeTable {
for (final use in uses) {
final id = use.identifier;
if (exclude.contains(id)) continue;
+ if (id.category == 'other') continue;
+
final composite = id.compositeKey;
(lemmaTypeGroups[composite] ??= {}).add(id);
}
@@ -34,6 +33,8 @@ class ConstructMergeTable {
for (final use in uses) {
final id = use.identifier;
if (exclude.contains(id)) continue;
+ if (id.category == 'other') continue;
+
final group = lemmaTypeGroups[id.compositeKey];
if (group == null) continue;
final matches = group.where((m) => m != id && m.string == id.string);
@@ -42,20 +43,6 @@ class ConstructMergeTable {
caseInsensitive[id] = id;
}
}
-
- for (final use in uses) {
- if (exclude.contains(use.identifier)) continue;
- final id = use.identifier;
- final composite = id.compositeKey;
- if (id.category == 'other' && !otherToSpecific.containsKey(id)) {
- final specific = lemmaTypeGroups[composite]!.firstWhereOrNull(
- (k) => k.category != 'other',
- );
- if (specific != null) {
- otherToSpecific[id] = caseInsensitive[specific] ?? specific;
- }
- }
- }
}
void removeConstruct(ConstructIdentifier id) {
@@ -68,17 +55,6 @@ class ConstructMergeTable {
lemmaTypeGroups.remove(composite);
}
- if (id.category != 'other') {
- final otherId = ConstructIdentifier(
- lemma: id.lemma,
- type: id.type,
- category: 'other',
- );
- otherToSpecific.remove(otherId);
- } else {
- otherToSpecific.remove(id);
- }
-
final caseEntry = caseInsensitive[id];
if (caseEntry != null && caseEntry != id) {
caseInsensitive.remove(caseEntry);
@@ -87,8 +63,7 @@ class ConstructMergeTable {
}
ConstructIdentifier resolve(ConstructIdentifier key) {
- final specific = otherToSpecific[key] ?? key;
- return caseInsensitive[specific] ?? specific;
+ return caseInsensitive[key] ?? key;
}
List groupedIds(
@@ -96,10 +71,12 @@ class ConstructMergeTable {
Set exclude,
) {
final keys = [];
- if (!exclude.contains(id)) {
- keys.add(id);
+ if (exclude.contains(id) || id.category == 'other') {
+ return keys;
}
+ keys.add(id);
+
// if this key maps to a different case variant, include that as well
final differentCase = caseInsensitive[id];
if (differentCase != null && differentCase != id) {
@@ -108,28 +85,6 @@ class ConstructMergeTable {
}
}
- // if this is an broad ('other') key, find the specific key it maps to
- // and include it if available
- if (id.category == 'other') {
- final specificKey = otherToSpecific[id];
- if (specificKey != null) {
- keys.add(specificKey);
- }
- return keys;
- }
-
- // if this is a specific key, and there existing an 'other' construct
- // in the same group, and that 'other' construct maps to this specific key,
- // include the 'other' construct as well
- final otherEntry = lemmaTypeGroups[id.compositeKey]
- ?.firstWhereOrNull((k) => k.category == 'other');
- if (otherEntry == null) {
- return keys;
- }
-
- if (otherToSpecific[otherEntry] == id) {
- keys.add(otherEntry);
- }
return keys;
}
@@ -152,7 +107,6 @@ class ConstructMergeTable {
void clear() {
lemmaTypeGroups.clear();
- otherToSpecific.clear();
caseInsensitive.clear();
}
}
diff --git a/lib/pangea/analytics_data/derived_analytics_data_model.dart b/lib/pangea/analytics_data/derived_analytics_data_model.dart
index 04a0ccf92..384af243e 100644
--- a/lib/pangea/analytics_data/derived_analytics_data_model.dart
+++ b/lib/pangea/analytics_data/derived_analytics_data_model.dart
@@ -1,7 +1,6 @@
import 'dart:math';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
-import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
class DerivedAnalyticsDataModel {
@@ -30,7 +29,7 @@ class DerivedAnalyticsDataModel {
return progress >= 0 ? progress : 0;
}
- static final double D = Environment.isStagingEnvironment ? 500 : 1500;
+ static const double D = 300;
static int calculateXpWithLevel(int level) {
// If level <= 1, XP should be 0 or negative by this math.
diff --git a/lib/pangea/analytics_data/level_up_analytics_service.dart b/lib/pangea/analytics_data/level_up_analytics_service.dart
index 2004638d7..532a5252c 100644
--- a/lib/pangea/analytics_data/level_up_analytics_service.dart
+++ b/lib/pangea/analytics_data/level_up_analytics_service.dart
@@ -84,10 +84,19 @@ class LevelUpAnalyticsService {
ownMessage: room.client.userID == event.senderId,
);
- messages.add({
- 'sent': pangeaEvent.originalSent?.text ?? pangeaEvent.body,
- 'written': pangeaEvent.originalWrittenContent,
- });
+ if (pangeaEvent.isAudioMessage) {
+ final stt = pangeaEvent.getSpeechToTextLocal();
+ if (stt == null) continue;
+ messages.add({
+ 'sent': stt.transcript.text,
+ 'written': stt.transcript.text,
+ });
+ } else {
+ messages.add({
+ 'sent': pangeaEvent.originalSent?.text ?? pangeaEvent.body,
+ 'written': pangeaEvent.originalWrittenContent,
+ });
+ }
} catch (e, s) {
ErrorHandler.logError(
e: e,
diff --git a/lib/pangea/analytics_details_popup/analytics_details_popup.dart b/lib/pangea/analytics_details_popup/analytics_details_popup.dart
index c816dfb80..7bfa27c6a 100644
--- a/lib/pangea/analytics_details_popup/analytics_details_popup.dart
+++ b/lib/pangea/analytics_details_popup/analytics_details_popup.dart
@@ -2,7 +2,9 @@ import 'dart:async';
import 'package:flutter/material.dart';
+import 'package:diacritic/diacritic.dart';
import 'package:go_router/go_router.dart';
+import 'package:material_symbols_icons/symbols.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart';
@@ -16,9 +18,13 @@ import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
+import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
+import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/pangea/morphs/morph_repo.dart';
+import 'package:fluffychat/pangea/token_info_feedback/show_token_feedback_dialog.dart';
+import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_request.dart';
import 'package:fluffychat/widgets/matrix.dart';
class ConstructAnalyticsView extends StatefulWidget {
@@ -47,6 +53,7 @@ class ConstructAnalyticsViewState extends State {
FocusNode searchFocusNode = FocusNode();
ConstructLevelEnum? selectedConstructLevel;
StreamSubscription? _constructUpdateSub;
+ final ValueNotifier reloadNotifier = ValueNotifier(0);
@override
void initState() {
@@ -70,6 +77,7 @@ class ConstructAnalyticsViewState extends State {
searchController.dispose();
_constructUpdateSub?.cancel();
searchFocusNode.dispose();
+ reloadNotifier.dispose();
super.dispose();
}
@@ -106,7 +114,11 @@ class ConstructAnalyticsViewState extends State {
vocab = data.values.toList();
vocab!.sort(
- (a, b) => a.lemma.toLowerCase().compareTo(b.lemma.toLowerCase()),
+ (a, b) {
+ final normalizedA = removeDiacritics(a.lemma).toLowerCase();
+ final normalizedB = removeDiacritics(b.lemma).toLowerCase();
+ return normalizedA.compareTo(normalizedB);
+ },
);
} finally {
if (mounted) setState(() {});
@@ -156,6 +168,29 @@ class ConstructAnalyticsViewState extends State {
});
}
+ Future onFlagTokenInfo(
+ PangeaToken token,
+ LemmaInfoResponse lemmaInfo,
+ String phonetics,
+ ) async {
+ final requestData = TokenInfoFeedbackRequestData(
+ userId: Matrix.of(context).client.userID!,
+ detectedLanguage: MatrixState.pangeaController.userController.userL2Code!,
+ tokens: [token],
+ selectedToken: 0,
+ wordCardL1: MatrixState.pangeaController.userController.userL1Code!,
+ lemmaInfo: lemmaInfo,
+ phonetics: phonetics,
+ );
+
+ await TokenFeedbackUtil.showTokenFeedbackDialog(
+ context,
+ requestData: requestData,
+ langCode: MatrixState.pangeaController.userController.userL2Code!,
+ onUpdated: () => reloadNotifier.value++,
+ );
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -176,79 +211,77 @@ class ConstructAnalyticsViewState extends State {
: MorphDetailsView(constructId: widget.construct!)
: widget.construct == null
? VocabAnalyticsListView(controller: this)
- : VocabDetailsView(constructId: widget.construct!),
+ : VocabDetailsView(
+ constructId: widget.construct!,
+ controller: this,
+ ),
),
],
),
),
),
floatingActionButton:
- widget.view == ConstructTypeEnum.vocab && widget.construct == null
- ? _buildVocabPracticeButton(context)
- : null,
+ widget.construct == null ? _PracticeButton(view: widget.view) : null,
);
}
}
-Widget _buildVocabPracticeButton(BuildContext context) {
- // Check if analytics is loaded first
- if (MatrixState
- .pangeaController.matrixState.analyticsDataService.isInitializing) {
+class _PracticeButton extends StatelessWidget {
+ final ConstructTypeEnum view;
+ const _PracticeButton({required this.view});
+
+ void _showSnackbar(BuildContext context, String message) {
+ ScaffoldMessenger.of(context).hideCurrentSnackBar();
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(message),
+ behavior: SnackBarBehavior.floating,
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final analyticsService = Matrix.of(context).analyticsDataService;
+ if (analyticsService.isInitializing) {
+ return FloatingActionButton.extended(
+ onPressed: () => _showSnackbar(
+ context,
+ L10n.of(context).loadingPleaseWait,
+ ),
+ label: Text(view.practiceButtonText(context)),
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ foregroundColor:
+ Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
+ );
+ }
+
+ final count = analyticsService.numConstructs(view);
+ final enabled = count >= 10;
+
return FloatingActionButton.extended(
- onPressed: () {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Text(
- 'Loading vocabulary data...',
- ),
- behavior: SnackBarBehavior.floating,
+ onPressed: enabled
+ ? () => context.go("/rooms/analytics/${view.name}/practice")
+ : () => _showSnackbar(
+ context,
+ L10n.of(context).notEnoughToPractice,
+ ),
+ backgroundColor:
+ enabled ? null : Theme.of(context).colorScheme.surfaceContainer,
+ foregroundColor: enabled
+ ? null
+ : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
+ label: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ enabled ? Symbols.fitness_center : Icons.lock_outline,
+ size: 18,
),
- );
- },
- label: Text(L10n.of(context).practiceVocab),
- backgroundColor: Theme.of(context).colorScheme.surface,
- foregroundColor:
- Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
+ const SizedBox(width: 4),
+ Text(view.practiceButtonText(context)),
+ ],
+ ),
);
}
-
- final vocabCount = MatrixState
- .pangeaController.matrixState.analyticsDataService
- .numConstructs(ConstructTypeEnum.vocab);
- final hasEnoughVocab = vocabCount >= 10;
-
- return FloatingActionButton.extended(
- onPressed: hasEnoughVocab
- ? () {
- context.go(
- "/rooms/analytics/${ConstructTypeEnum.vocab.name}/practice",
- );
- }
- : () {
- ScaffoldMessenger.of(context).hideCurrentSnackBar();
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(
- L10n.of(context).mustHave10Words,
- ),
- behavior: SnackBarBehavior.floating,
- ),
- );
- },
- backgroundColor:
- hasEnoughVocab ? null : Theme.of(context).colorScheme.surfaceContainer,
- foregroundColor: hasEnoughVocab
- ? null
- : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.5),
- label: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (!hasEnoughVocab) ...[
- const Icon(Icons.lock_outline, size: 18),
- const SizedBox(width: 4),
- ],
- Text(L10n.of(context).practiceVocab),
- ],
- ),
- );
}
diff --git a/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart b/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart
index 4089a46b1..794cdbe5f 100644
--- a/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart
+++ b/lib/pangea/analytics_details_popup/construct_xp_progress_bar.dart
@@ -41,32 +41,16 @@ class ConstructXPProgressBar extends StatelessWidget {
return Column(
spacing: 8.0,
children: [
- LayoutBuilder(
- builder: (context, constraints) {
- double availableGap =
- constraints.maxWidth - (categories.length * iconSize);
- const totalPoints = AnalyticsConstants.xpForFlower;
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- ...categories.map(
- (c) {
- final gapPercent = (c.xpNeeded / totalPoints);
- final gap = availableGap * gapPercent;
- availableGap -= gap;
- return Container(
- width: iconSize + gap,
- alignment: Alignment.centerRight,
- child: Opacity(
- opacity: level == c ? 1.0 : 0.4,
- child: c.icon(iconSize),
- ),
- );
- },
- ),
- ],
- );
- },
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ ...categories.map(
+ (c) => Opacity(
+ opacity: level == c ? 1.0 : 0.4,
+ child: c.icon(iconSize),
+ ),
+ ),
+ ],
),
AnimatedProgressBar(
height: 20.0,
diff --git a/lib/pangea/analytics_details_popup/lemma_usage_dots.dart b/lib/pangea/analytics_details_popup/lemma_usage_dots.dart
index aa4e78e83..7ccd9849f 100644
--- a/lib/pangea/analytics_details_popup/lemma_usage_dots.dart
+++ b/lib/pangea/analytics_details_popup/lemma_usage_dots.dart
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
-import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
@@ -24,16 +23,19 @@ class LemmaUsageDots extends StatelessWidget {
});
/// Find lemma uses for the given exercise type, to create dot list
- List sortedUses(LearningSkillsEnum category) {
- final List useList = [];
+ List sortedUses(LearningSkillsEnum category) {
+ final List useList = [];
for (final OneConstructUse use in construct.cappedUses) {
- if (use.xp == 0) {
- continue;
- }
// If the use type matches the given category, save to list
// Usage with positive XP is saved as true, else false
if (category == use.useType.skillsEnumType) {
- useList.add(use.xp > 0);
+ useList.add(
+ switch (use.xp) {
+ > 0 => AppConfig.success,
+ < 0 => Colors.red,
+ _ => Colors.grey[400]!,
+ },
+ );
}
}
return useList;
@@ -42,13 +44,13 @@ class LemmaUsageDots extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List dots = [];
- for (final bool use in sortedUses(category)) {
+ for (final Color color in sortedUses(category)) {
dots.add(
Container(
width: 15.0,
height: 15.0,
decoration: BoxDecoration(
- color: use ? AppConfig.success : Colors.red,
+ color: color,
shape: BoxShape.circle,
),
),
@@ -71,9 +73,11 @@ class LemmaUsageDots extends StatelessWidget {
),
title: dots.isEmpty
? Text(
- L10n.of(context).noDataFound,
- style: const TextStyle(
- fontStyle: FontStyle.italic,
+ "-",
+ style: TextStyle(
+ fontSize: 32,
+ fontWeight: FontWeight.bold,
+ color: textColor.withAlpha(100),
),
)
: Wrap(
diff --git a/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart b/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart
index f923391e0..9be8139e0 100644
--- a/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart
+++ b/lib/pangea/analytics_details_popup/morph_analytics_list_view.dart
@@ -75,6 +75,7 @@ class MorphAnalyticsListView extends StatelessWidget {
childCount: controller.features.length,
),
),
+ const SliverToBoxAdapter(child: SizedBox(height: 75.0)),
],
),
),
diff --git a/lib/pangea/analytics_details_popup/morph_details_view.dart b/lib/pangea/analytics_details_popup/morph_details_view.dart
index 6b22469a7..ddfe948d4 100644
--- a/lib/pangea/analytics_details_popup/morph_details_view.dart
+++ b/lib/pangea/analytics_details_popup/morph_details_view.dart
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_usage_content.dart';
+import 'package:fluffychat/pangea/analytics_details_popup/construct_xp_progress_bar.dart';
import 'package:fluffychat/pangea/analytics_details_popup/morph_meaning_widget.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
-import 'package:fluffychat/pangea/lemmas/construct_xp_widget.dart';
import 'package:fluffychat/pangea/morphs/morph_feature_display.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
import 'package:fluffychat/pangea/morphs/morph_tag_display.dart';
@@ -54,11 +54,7 @@ class MorphDetailsView extends StatelessWidget {
),
const Divider(),
if (construct != null) ...[
- ConstructXpWidget(
- icon: construct.lemmaCategory.icon(30.0),
- level: construct.lemmaCategory,
- points: construct.points,
- ),
+ ConstructXPProgressBar(construct: construct.id),
Padding(
padding: const EdgeInsets.all(20.0),
child: AnalyticsDetailsUsageContent(
diff --git a/lib/pangea/analytics_details_popup/morph_meaning_widget.dart b/lib/pangea/analytics_details_popup/morph_meaning_widget.dart
index fd34f5c9d..d9c619b32 100644
--- a/lib/pangea/analytics_details_popup/morph_meaning_widget.dart
+++ b/lib/pangea/analytics_details_popup/morph_meaning_widget.dart
@@ -16,12 +16,14 @@ class MorphMeaningWidget extends StatefulWidget {
final MorphFeaturesEnum feature;
final String tag;
final TextStyle? style;
+ final bool blankErrorFeedback;
const MorphMeaningWidget({
super.key,
required this.feature,
required this.tag,
this.style,
+ this.blankErrorFeedback = false,
});
@override
@@ -91,12 +93,13 @@ class MorphMeaningWidgetState extends State {
);
if (result.isError) {
- return L10n.of(context).meaningNotFound;
+ return widget.blankErrorFeedback ? '' : L10n.of(context).meaningNotFound;
}
final morph = result.result!.getFeatureByCode(widget.feature.name);
final data = morph?.getTagByCode(widget.tag);
- return data?.l1Description ?? L10n.of(context).meaningNotFound;
+ return data?.l1Description ??
+ (widget.blankErrorFeedback ? '' : L10n.of(context).meaningNotFound);
}
void _toggleEditMode(bool value) => setState(() => _editMode = value);
diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart
index 6de39e654..79dda8441 100644
--- a/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart
+++ b/lib/pangea/analytics_details_popup/vocab_analytics_details_view.dart
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/l10n/l10n.dart';
+import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_usage_content.dart';
import 'package:fluffychat/pangea/analytics_details_popup/construct_xp_progress_bar.dart';
import 'package:fluffychat/pangea/analytics_details_popup/word_text_with_audio_button.dart';
@@ -12,8 +13,6 @@ import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
import 'package:fluffychat/pangea/lemmas/lemma.dart';
import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart';
-import 'package:fluffychat/pangea/token_info_feedback/show_token_feedback_dialog.dart';
-import 'package:fluffychat/pangea/token_info_feedback/token_info_feedback_request.dart';
import 'package:fluffychat/pangea/toolbar/word_card/word_zoom_widget.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@@ -23,10 +22,12 @@ import 'package:fluffychat/widgets/matrix.dart';
/// Displays information about selected lemma, and its usage
class VocabDetailsView extends StatelessWidget {
final ConstructIdentifier constructId;
+ final ConstructAnalyticsViewState controller;
const VocabDetailsView({
super.key,
required this.constructId,
+ required this.controller,
});
Future _blockLemma(BuildContext context) async {
@@ -82,35 +83,25 @@ class VocabDetailsView extends StatelessWidget {
maxWidth: 600.0,
showBorder: false,
child: Column(
- spacing: 16.0,
+ spacing: 20.0,
children: [
- WordZoomWidget(
- token: tokenText,
- langCode:
- MatrixState.pangeaController.userController.userL2Code!,
- construct: constructId,
- onClose: Navigator.of(context).pop,
- onFlagTokenInfo:
- (LemmaInfoResponse lemmaInfo, String phonetics) {
- final requestData = TokenInfoFeedbackRequestData(
- userId: Matrix.of(context).client.userID!,
- detectedLanguage:
- MatrixState.pangeaController.userController.userL2Code!,
- tokens: [token],
- selectedToken: 0,
- wordCardL1:
- MatrixState.pangeaController.userController.userL1Code!,
- lemmaInfo: lemmaInfo,
- phonetics: phonetics,
- );
-
- TokenFeedbackUtil.showTokenFeedbackDialog(
- context,
- requestData: requestData,
- langCode:
- MatrixState.pangeaController.userController.userL2Code!,
- );
- },
+ const SizedBox(),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
+ child: WordZoomWidget(
+ token: tokenText,
+ langCode:
+ MatrixState.pangeaController.userController.userL2Code!,
+ construct: constructId,
+ onClose: Navigator.of(context).pop,
+ onFlagTokenInfo: (
+ LemmaInfoResponse lemmaInfo,
+ String phonetics,
+ ) =>
+ controller.onFlagTokenInfo(token, lemmaInfo, phonetics),
+ reloadNotifier: controller.reloadNotifier,
+ maxWidth: double.infinity,
+ ),
),
if (construct != null)
Column(
diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart b/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart
index 564069291..91f81c172 100644
--- a/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart
+++ b/lib/pangea/analytics_details_popup/vocab_analytics_list_tile.dart
@@ -77,7 +77,7 @@ class VocabAnalyticsListTile extends StatelessWidget {
},
),
Container(
- alignment: Alignment.topCenter,
+ alignment: Alignment.center,
padding: const EdgeInsets.only(top: 4),
height: (maxWidth - padding * 2) * 0.4,
child: ShrinkableText(
diff --git a/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart b/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart
index 45181eea2..541b3ff9c 100644
--- a/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart
+++ b/lib/pangea/analytics_details_popup/vocab_analytics_list_view.dart
@@ -115,6 +115,7 @@ class VocabAnalyticsListView extends StatelessWidget {
}
}
+ final filteredVocab = _filteredVocab;
return Column(
children: [
AnimatedContainer(
@@ -178,7 +179,7 @@ class VocabAnalyticsListView extends StatelessWidget {
),
// Grid of vocab tiles
- if (vocab == null)
+ if (filteredVocab == null)
const SliverFillRemaining(
hasScrollBody: false,
child: Center(
@@ -186,7 +187,7 @@ class VocabAnalyticsListView extends StatelessWidget {
),
)
else
- vocab.isEmpty
+ filteredVocab.isEmpty
? SliverToBoxAdapter(
child: controller.selectedConstructLevel != null
? Padding(
@@ -209,7 +210,7 @@ class VocabAnalyticsListView extends StatelessWidget {
),
delegate: SliverChildBuilderDelegate(
(context, index) {
- final vocabItem = _filteredVocab![index];
+ final vocabItem = filteredVocab[index];
return VocabAnalyticsListTile(
onTap: () {
TtsController.tryToSpeak(
@@ -232,7 +233,7 @@ class VocabAnalyticsListView extends StatelessWidget {
selected: vocabItem.id == selectedConstruct,
);
},
- childCount: _filteredVocab!.length,
+ childCount: filteredVocab.length,
),
),
const SliverToBoxAdapter(child: SizedBox(height: 75.0)),
diff --git a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart
index f616a1be6..ea0739580 100644
--- a/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart
+++ b/lib/pangea/analytics_downloads/analytics_dowload_dialog.dart
@@ -1,3 +1,4 @@
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:csv/csv.dart';
@@ -44,12 +45,18 @@ class AnalyticsDownloadDialogState extends State {
String? get _statusText {
if (_downloading) return L10n.of(context).downloading;
- if (_downloaded) return L10n.of(context).downloadComplete;
+ if (_downloaded) return L10n.of(context).downloadInitiated;
return null;
}
void _setDownloadType(DownloadType type) {
- if (mounted) setState(() => _downloadType = type);
+ if (mounted) {
+ setState(() {
+ _downloadType = type;
+ _downloaded = false;
+ _error = null;
+ });
+ }
}
Future _downloadAnalytics() async {
@@ -426,7 +433,8 @@ class AnalyticsDownloadDialogState extends State {
padding: const EdgeInsets.all(8.0),
child: SegmentedButton(
selected: {_downloadType},
- onSelectionChanged: (c) => _setDownloadType(c.first),
+ onSelectionChanged:
+ _downloading ? null : (c) => _setDownloadType(c.first),
segments: [
ButtonSegment(
value: DownloadType.csv,
@@ -462,6 +470,21 @@ class AnalyticsDownloadDialogState extends State {
)
: const SizedBox(),
),
+ AnimatedSize(
+ duration: FluffyThemes.animationDuration,
+ child: kIsWeb && _downloaded
+ ? Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Text(
+ L10n.of(context).webDownloadPermissionMessage,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ color: Theme.of(context).disabledColor,
+ ),
+ ),
+ )
+ : const SizedBox(),
+ ),
AnimatedSize(
duration: FluffyThemes.animationDuration,
child: _error != null
diff --git a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart
index ddccf10e9..a176fff8d 100644
--- a/lib/pangea/analytics_downloads/space_analytics_summary_model.dart
+++ b/lib/pangea/analytics_downloads/space_analytics_summary_model.dart
@@ -97,7 +97,6 @@ class SpaceAnalyticsSummaryModel {
Set blockedConstructs,
int numCompletedActivities,
) {
- int totalXP = 0;
int numWordsTyped = 0;
int numChoicesCorrect = 0;
int numChoicesIncorrect = 0;
@@ -114,7 +113,9 @@ class SpaceAnalyticsSummaryModel {
mergeTable.addConstructsByUses(e.content.uses, blockedConstructs);
for (final use in e.content.uses) {
- totalXP += use.xp;
+ final id = use.identifier;
+ if (blockedConstructs.contains(id)) continue;
+
allUses.add(use);
if (use.useType.summaryEnumType ==
@@ -132,8 +133,7 @@ class SpaceAnalyticsSummaryModel {
sentEventIds.add(use.metadata.eventId!);
}
- final id = use.identifier;
- final existing = id.type == ConstructTypeEnum.vocab
+ final existing = use.identifier.type == ConstructTypeEnum.vocab
? aggregatedVocab[id]
: aggregatedMorph[id];
@@ -189,6 +189,10 @@ class SpaceAnalyticsSummaryModel {
}
}
+ final totalXP = cleanedVocab.values
+ .fold(0, (sum, entry) => sum + entry.points) +
+ cleanedMorph.values.fold(0, (sum, entry) => sum + entry.points);
+
final level = DerivedAnalyticsDataModel.calculateLevelWithXp(totalXP);
final uniqueVocabCount = cleanedVocab.length;
final uniqueMorphCount = cleanedMorph.length;
diff --git a/lib/pangea/analytics_misc/analytics_constants.dart b/lib/pangea/analytics_misc/analytics_constants.dart
index d680c1fd7..96fb33a13 100644
--- a/lib/pangea/analytics_misc/analytics_constants.dart
+++ b/lib/pangea/analytics_misc/analytics_constants.dart
@@ -2,7 +2,7 @@ class AnalyticsConstants {
static const int xpPerLevel = 500;
static const int vocabUseMaxXP = 30;
static const int morphUseMaxXP = 500;
- static const int xpForGreens = 30;
+ static const int xpForGreens = 50;
static const int xpForFlower = 100;
static const String seedSvgFileName = "Seed.svg";
static const String leafSvgFileName = "Leaf.svg";
diff --git a/lib/pangea/analytics_misc/client_analytics_extension.dart b/lib/pangea/analytics_misc/client_analytics_extension.dart
index e4d36c11d..ba2be21d1 100644
--- a/lib/pangea/analytics_misc/client_analytics_extension.dart
+++ b/lib/pangea/analytics_misc/client_analytics_extension.dart
@@ -6,10 +6,12 @@ import 'package:flutter/foundation.dart';
import 'package:collection/collection.dart';
import 'package:matrix/matrix.dart';
+import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
+import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/languages/language_model.dart';
import 'package:fluffychat/widgets/matrix.dart';
@@ -76,6 +78,14 @@ extension AnalyticsClientExtension on Client {
topic: "This room stores learning analytics for $userID.",
preset: CreateRoomPreset.publicChat,
visibility: Visibility.private,
+ initialState: [
+ StateEvent(
+ type: EventTypes.RoomJoinRules,
+ content: {
+ ModelKey.joinRule: JoinRules.knock.name,
+ },
+ ),
+ ],
);
if (getRoomById(roomID) == null) {
// Wait for room actually appears in sync
@@ -169,4 +179,38 @@ extension AnalyticsClientExtension on Client {
)
.isNotEmpty;
}
+
+ Future getEventByConstructUse(
+ OneConstructUse use,
+ ) async {
+ if (use.metadata.eventId == null || use.metadata.roomId == null) {
+ return null;
+ }
+
+ final room = getRoomById(use.metadata.roomId!);
+ if (room == null) return null;
+
+ try {
+ final event = await room.getEventById(use.metadata.eventId!);
+ if (event == null) return null;
+
+ final timeline = await room.getTimeline();
+ return PangeaMessageEvent(
+ event: event,
+ timeline: timeline,
+ ownMessage: event.senderId == userID,
+ );
+ } catch (e, s) {
+ ErrorHandler.logError(
+ e: e,
+ s: s,
+ data: {
+ "roomID": use.metadata.roomId,
+ "eventID": use.metadata.eventId,
+ "userID": userID,
+ },
+ );
+ return null;
+ }
+ }
}
diff --git a/lib/pangea/analytics_misc/construct_type_enum.dart b/lib/pangea/analytics_misc/construct_type_enum.dart
index 910d3cfbe..ad7e3f0a2 100644
--- a/lib/pangea/analytics_misc/construct_type_enum.dart
+++ b/lib/pangea/analytics_misc/construct_type_enum.dart
@@ -4,20 +4,15 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
-import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
-import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
-import 'package:fluffychat/pangea/morphs/parts_of_speech_enum.dart';
enum ConstructTypeEnum {
/// for vocabulary words
vocab,
/// for morphs, actually called "Grammar" in the UI... :P
- morph,
-}
+ morph;
-extension ConstructExtension on ConstructTypeEnum {
String get string {
switch (this) {
case ConstructTypeEnum.vocab:
@@ -37,25 +32,6 @@ extension ConstructExtension on ConstructTypeEnum {
}
}
- int get maxXPPerLemma {
- switch (this) {
- case ConstructTypeEnum.vocab:
- return AnalyticsConstants.vocabUseMaxXP;
- case ConstructTypeEnum.morph:
- return AnalyticsConstants.morphUseMaxXP;
- }
- }
-
- String? getDisplayCopy(String category, BuildContext context) {
- switch (this) {
- case ConstructTypeEnum.morph:
- return MorphFeaturesEnumExtension.fromString(category)
- .getDisplayCopy(context);
- case ConstructTypeEnum.vocab:
- return getVocabCategoryName(category, context);
- }
- }
-
ProgressIndicatorEnum get indicator {
switch (this) {
case ConstructTypeEnum.morph:
@@ -64,9 +40,17 @@ extension ConstructExtension on ConstructTypeEnum {
return ProgressIndicatorEnum.wordsUsed;
}
}
-}
-class ConstructTypeUtil {
+ String practiceButtonText(BuildContext context) {
+ final l10n = L10n.of(context);
+ switch (this) {
+ case ConstructTypeEnum.vocab:
+ return l10n.practiceVocab;
+ case ConstructTypeEnum.morph:
+ return l10n.practiceGrammar;
+ }
+ }
+
static ConstructTypeEnum fromString(String? string) {
switch (string) {
case 'v':
diff --git a/lib/pangea/analytics_misc/construct_use_model.dart b/lib/pangea/analytics_misc/construct_use_model.dart
index d1ef0eaeb..e3a3ddda5 100644
--- a/lib/pangea/analytics_misc/construct_use_model.dart
+++ b/lib/pangea/analytics_misc/construct_use_model.dart
@@ -140,8 +140,8 @@ class ConstructUses {
_uses.sort((a, b) => a.timeStamp.compareTo(b.timeStamp));
}
- void addUse(OneConstructUse use) {
- _uses.add(use);
+ void addUses(List uses) {
+ _uses.addAll(uses);
_sortUses();
}
diff --git a/lib/pangea/analytics_misc/construct_use_type_enum.dart b/lib/pangea/analytics_misc/construct_use_type_enum.dart
index 9dd3ba4c0..ec5cae6d8 100644
--- a/lib/pangea/analytics_misc/construct_use_type_enum.dart
+++ b/lib/pangea/analytics_misc/construct_use_type_enum.dart
@@ -82,6 +82,14 @@ enum ConstructUseTypeEnum {
// vocab lemma audio activity
corLA,
incLA,
+
+ // grammar category activity
+ corGC,
+ incGC,
+
+ // grammar error activity
+ corGE,
+ incGE,
}
extension ConstructUseTypeExtension on ConstructUseTypeEnum {
@@ -163,6 +171,14 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
return L10n.of(context).constructUseCorLADesc;
case ConstructUseTypeEnum.incLA:
return L10n.of(context).constructUseIncLADesc;
+ case ConstructUseTypeEnum.corGC:
+ return L10n.of(context).constructUseCorGCDesc;
+ case ConstructUseTypeEnum.incGC:
+ return L10n.of(context).constructUseIncGCDesc;
+ case ConstructUseTypeEnum.corGE:
+ return L10n.of(context).constructUseCorGEDesc;
+ case ConstructUseTypeEnum.incGE:
+ return L10n.of(context).constructUseIncGEDesc;
}
}
@@ -203,6 +219,10 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.corM:
case ConstructUseTypeEnum.incM:
case ConstructUseTypeEnum.ignM:
+ case ConstructUseTypeEnum.corGC:
+ case ConstructUseTypeEnum.incGC:
+ case ConstructUseTypeEnum.corGE:
+ case ConstructUseTypeEnum.incGE:
return ActivityTypeEnum.morphId.icon;
case ConstructUseTypeEnum.em:
return ActivityTypeEnum.emoji.icon;
@@ -235,6 +255,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.corM:
case ConstructUseTypeEnum.corLM:
case ConstructUseTypeEnum.corLA:
+ case ConstructUseTypeEnum.corGC:
+ case ConstructUseTypeEnum.corGE:
return 5;
case ConstructUseTypeEnum.pvm:
@@ -275,6 +297,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.incM:
case ConstructUseTypeEnum.incLM:
case ConstructUseTypeEnum.incLA:
+ case ConstructUseTypeEnum.incGC:
+ case ConstructUseTypeEnum.incGE:
return -1;
case ConstructUseTypeEnum.incPA:
@@ -326,6 +350,10 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.corLA:
case ConstructUseTypeEnum.incLA:
case ConstructUseTypeEnum.bonus:
+ case ConstructUseTypeEnum.corGC:
+ case ConstructUseTypeEnum.incGC:
+ case ConstructUseTypeEnum.corGE:
+ case ConstructUseTypeEnum.incGE:
return false;
}
}
@@ -369,6 +397,10 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.click:
case ConstructUseTypeEnum.corLM:
case ConstructUseTypeEnum.incLM:
+ case ConstructUseTypeEnum.corGC:
+ case ConstructUseTypeEnum.incGC:
+ case ConstructUseTypeEnum.corGE:
+ case ConstructUseTypeEnum.incGE:
return LearningSkillsEnum.reading;
case ConstructUseTypeEnum.pvm:
return LearningSkillsEnum.speaking;
@@ -398,6 +430,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.corMM:
case ConstructUseTypeEnum.corLM:
case ConstructUseTypeEnum.corLA:
+ case ConstructUseTypeEnum.corGC:
+ case ConstructUseTypeEnum.corGE:
return SpaceAnalyticsSummaryEnum.numChoicesCorrect;
case ConstructUseTypeEnum.incIt:
@@ -410,6 +444,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.incMM:
case ConstructUseTypeEnum.incLM:
case ConstructUseTypeEnum.incLA:
+ case ConstructUseTypeEnum.incGC:
+ case ConstructUseTypeEnum.incGE:
return SpaceAnalyticsSummaryEnum.numChoicesIncorrect;
case ConstructUseTypeEnum.ignIt:
diff --git a/lib/pangea/analytics_misc/constructs_model.dart b/lib/pangea/analytics_misc/constructs_model.dart
index 5f0ee420c..cb053585b 100644
--- a/lib/pangea/analytics_misc/constructs_model.dart
+++ b/lib/pangea/analytics_misc/constructs_model.dart
@@ -117,7 +117,7 @@ class OneConstructUse {
debugger(when: kDebugMode && json['constructType'] == null);
final ConstructTypeEnum constructType = json['constructType'] != null
- ? ConstructTypeUtil.fromString(json['constructType'])
+ ? ConstructTypeEnum.fromString(json['constructType'])
: ConstructTypeEnum.vocab;
final useType = ConstructUseTypeUtil.fromString(json['useType']);
diff --git a/lib/pangea/analytics_misc/example_message_util.dart b/lib/pangea/analytics_misc/example_message_util.dart
new file mode 100644
index 000000000..f9c5713aa
--- /dev/null
+++ b/lib/pangea/analytics_misc/example_message_util.dart
@@ -0,0 +1,158 @@
+import 'package:flutter/material.dart';
+
+import 'package:matrix/matrix.dart';
+
+import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
+import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
+import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
+import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
+
+class ExampleMessageUtil {
+ static Future?> getExampleMessage(
+ ConstructUses construct,
+ Client client, {
+ String? form,
+ }) async {
+ for (final use in construct.cappedUses) {
+ if (form != null && use.form != form) continue;
+
+ final event = await client.getEventByConstructUse(use);
+ if (event == null) continue;
+
+ final spans = _buildExampleMessage(use.form, event);
+ if (spans != null) return spans;
+ }
+
+ return null;
+ }
+
+ static Future>> getExampleMessages(
+ ConstructUses construct,
+ Client client,
+ int maxMessages,
+ ) async {
+ final List> allSpans = [];
+ for (final use in construct.cappedUses) {
+ if (allSpans.length >= maxMessages) break;
+ final event = await client.getEventByConstructUse(use);
+ if (event == null) continue;
+
+ final spans = _buildExampleMessage(use.form, event);
+ if (spans != null) {
+ allSpans.add(spans);
+ }
+ }
+ return allSpans;
+ }
+
+ static List? _buildExampleMessage(
+ String? form,
+ PangeaMessageEvent messageEvent,
+ ) {
+ String? text;
+ List? tokens;
+ int targetTokenIndex = -1;
+
+ if (messageEvent.isAudioMessage) {
+ final stt = messageEvent.getSpeechToTextLocal();
+ if (stt == null) return null;
+
+ tokens = stt.transcript.sttTokens.map((t) => t.token).toList();
+ targetTokenIndex = tokens.indexWhere((t) => t.text.content == form);
+ text = stt.transcript.text;
+ } else {
+ tokens = messageEvent.messageDisplayRepresentation?.tokens;
+ if (tokens == null || tokens.isEmpty) return null;
+
+ targetTokenIndex = tokens.indexWhere((t) => t.text.content == form);
+ text = messageEvent.messageDisplayText;
+ }
+
+ if (targetTokenIndex == -1) {
+ return null;
+ }
+
+ final targetToken = tokens[targetTokenIndex];
+
+ const maxContextChars = 100;
+
+ final targetStart = targetToken.text.offset;
+ final targetEnd = targetStart + targetToken.text.content.characters.length;
+
+ final totalChars = text.characters.length;
+
+ final beforeAvailable = targetStart;
+ final afterAvailable = totalChars - targetEnd;
+
+ // ---------- Dynamic budget split ----------
+ int beforeBudget = maxContextChars ~/ 2;
+ int afterBudget = maxContextChars - beforeBudget;
+
+ if (beforeAvailable < beforeBudget) {
+ afterBudget += beforeBudget - beforeAvailable;
+ beforeBudget = beforeAvailable;
+ } else if (afterAvailable < afterBudget) {
+ beforeBudget += afterBudget - afterAvailable;
+ afterBudget = afterAvailable;
+ }
+
+ // ---------- BEFORE ----------
+ int beforeStartOffset = 0;
+ bool trimmedBefore = false;
+
+ if (beforeAvailable > beforeBudget) {
+ final desiredStart = targetStart - beforeBudget;
+
+ for (int i = 0; i < targetTokenIndex; i++) {
+ final token = tokens[i];
+ final tokenEnd =
+ token.text.offset + token.text.content.characters.length;
+
+ if (tokenEnd > desiredStart) {
+ beforeStartOffset = token.text.offset;
+ trimmedBefore = true;
+ break;
+ }
+ }
+ }
+
+ final before = text.characters
+ .skip(beforeStartOffset)
+ .take(targetStart - beforeStartOffset)
+ .toString();
+
+ // ---------- AFTER ----------
+ int afterEndOffset = totalChars;
+ bool trimmedAfter = false;
+
+ if (afterAvailable > afterBudget) {
+ final desiredEnd = targetEnd + afterBudget;
+
+ for (int i = targetTokenIndex + 1; i < tokens.length; i++) {
+ final token = tokens[i];
+ if (token.text.offset >= desiredEnd) {
+ afterEndOffset = token.text.offset;
+ trimmedAfter = true;
+ break;
+ }
+ }
+ }
+
+ final after = text.characters
+ .skip(targetEnd)
+ .take(afterEndOffset - targetEnd)
+ .toString()
+ .trimRight();
+
+ return [
+ if (trimmedBefore) const TextSpan(text: '… '),
+ TextSpan(text: before),
+ TextSpan(
+ text: targetToken.text.content,
+ style: const TextStyle(fontWeight: FontWeight.bold),
+ ),
+ TextSpan(text: after),
+ if (trimmedAfter) const TextSpan(text: '…'),
+ ];
+ }
+}
diff --git a/lib/pangea/analytics_misc/gain_points_animation.dart b/lib/pangea/analytics_misc/gain_points_animation.dart
index 221828fb9..1580a73a8 100644
--- a/lib/pangea/analytics_misc/gain_points_animation.dart
+++ b/lib/pangea/analytics_misc/gain_points_animation.dart
@@ -41,7 +41,7 @@ class PointsGainedAnimationState extends State
@override
void initState() {
super.initState();
- if (widget.points == 0) return;
+ if (_points == 0) return;
_controller = AnimationController(
duration: const Duration(milliseconds: duration),
@@ -77,18 +77,19 @@ class PointsGainedAnimationState extends State
);
}
+ int get _points => widget.points.clamp(-25, 25);
+
void initParticleTrajectories() {
- for (int i = 0; i < widget.points.abs(); i++) {
- final angle =
- (i - widget.points.abs() / 2) / widget.points.abs() * (pi / 3) +
- (_random.nextDouble() - 0.5) * pi / 6 +
- pi / 2;
+ for (int i = 0; i < _points.abs(); i++) {
+ final angle = (i - _points.abs() / 2) / _points.abs() * (pi / 3) +
+ (_random.nextDouble() - 0.5) * pi / 6 +
+ pi / 2;
final speedMultiplier =
0.75 + _random.nextDouble() / 4; // Random speed multiplier.
final speed = _particleSpeed *
speedMultiplier *
- (widget.points > 0 ? 2 : 1); // Exponential speed.
+ (_points > 0 ? 2 : 1); // Exponential speed.
_trajectories.add(
Offset(
speed * cos(angle) * (widget.invert ? -1 : 1),
@@ -106,7 +107,7 @@ class PointsGainedAnimationState extends State
@override
Widget build(BuildContext context) {
- if (widget.points == 0 ||
+ if (_points == 0 ||
_controller == null ||
_fadeAnimation == null ||
_progressAnimation == null) {
@@ -118,10 +119,10 @@ class PointsGainedAnimationState extends State
return const SizedBox();
}
- final textColor = widget.points > 0 ? gainColor : loseColor;
+ final textColor = _points > 0 ? gainColor : loseColor;
final plusWidget = Text(
- widget.points > 0 ? "+" : "-",
+ _points > 0 ? "+" : "-",
style: BotStyle.text(
context,
big: true,
@@ -139,7 +140,7 @@ class PointsGainedAnimationState extends State
child: IgnorePointer(
ignoring: _controller!.isAnimating,
child: Stack(
- children: List.generate(widget.points.abs(), (index) {
+ children: List.generate(_points.abs(), (index) {
return AnimatedBuilder(
animation: _controller!,
builder: (context, child) {
diff --git a/lib/pangea/analytics_misc/growth_animation.dart b/lib/pangea/analytics_misc/growth_animation.dart
new file mode 100644
index 000000000..7cd42c4fa
--- /dev/null
+++ b/lib/pangea/analytics_misc/growth_animation.dart
@@ -0,0 +1,97 @@
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+
+import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+/// Tracks active growth animations for offset calculation
+class GrowthAnimationTracker {
+ static int _activeCount = 0;
+
+ static int get activeCount => _activeCount;
+
+ static double? startAnimation() {
+ if (_activeCount >= 5) return null;
+ final index = _activeCount;
+ _activeCount++;
+ if (index == 0) return 0;
+ final side = index.isOdd ? 1 : -1;
+ return side * ((index + 1) ~/ 2) * 20.0;
+ }
+
+ static void endAnimation() {
+ _activeCount = (_activeCount - 1).clamp(0, 999);
+ }
+}
+
+class GrowthAnimation extends StatefulWidget {
+ final String targetID;
+ final ConstructLevelEnum level;
+
+ const GrowthAnimation({
+ super.key,
+ required this.targetID,
+ required this.level,
+ });
+
+ @override
+ State createState() => _GrowthAnimationState();
+}
+
+class _GrowthAnimationState extends State
+ with SingleTickerProviderStateMixin {
+ late final AnimationController _controller;
+ late final double? _horizontalOffset;
+ late final double _wiggleAmplitude;
+ late final double _wiggleFrequency;
+ final Random _random = Random();
+
+ static const _durationMs = 1600;
+ static const _riseDistance = 72.0;
+
+ @override
+ void initState() {
+ super.initState();
+ _horizontalOffset = GrowthAnimationTracker.startAnimation();
+ _wiggleAmplitude = 4.0 + _random.nextDouble() * 4.0;
+ _wiggleFrequency = 1.5 + _random.nextDouble() * 1.0;
+
+ _controller = AnimationController(
+ duration: const Duration(milliseconds: _durationMs),
+ vsync: this,
+ )..forward().then((_) {
+ if (mounted) {
+ MatrixState.pAnyState.closeOverlay(widget.targetID);
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ GrowthAnimationTracker.endAnimation();
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (_horizontalOffset == null) return const SizedBox.shrink();
+ return AnimatedBuilder(
+ animation: _controller,
+ builder: (context, child) {
+ final t = _controller.value;
+ final dy = -_riseDistance * Curves.easeOut.transform(t);
+ final opacity = t < 0.5 ? t * 2 : (1.0 - t) * 2;
+ final wiggle = sin(t * pi * _wiggleFrequency) * _wiggleAmplitude;
+ return Transform.translate(
+ offset: Offset(_horizontalOffset! + wiggle, dy),
+ child: Opacity(
+ opacity: opacity.clamp(0.0, 1.0),
+ child: widget.level.icon(24),
+ ),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart b/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart
index 65f4a7a09..0719943e3 100644
--- a/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart
+++ b/lib/pangea/analytics_misc/lemma_emoji_setter_mixin.dart
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_tile.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_navigation_util.dart';
-import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
diff --git a/lib/pangea/analytics_page/activity_archive.dart b/lib/pangea/analytics_page/activity_archive.dart
index ef5e2f6f9..c76bdff96 100644
--- a/lib/pangea/analytics_page/activity_archive.dart
+++ b/lib/pangea/analytics_page/activity_archive.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
+import 'package:collection/collection.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
@@ -25,47 +26,58 @@ class ActivityArchive extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final Room? analyticsRoom = Matrix.of(context).client.analyticsRoomLocal();
- final archive = analyticsRoom?.archivedActivities ?? [];
- final selectedRoomId = GoRouterState.of(context).pathParameters['roomid'];
- return Scaffold(
- body: SafeArea(
- child: Padding(
- padding: const EdgeInsetsGeometry.all(16.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- const LearningProgressIndicators(
- selected: ProgressIndicatorEnum.activities,
- ),
- Expanded(
- child: MaxWidthBody(
- withScrolling: false,
- child: ListView.builder(
- physics: const ClampingScrollPhysics(),
- itemCount: archive.length + 1,
- itemBuilder: (BuildContext context, int i) {
- if (i == 0) {
- return InstructionsInlineTooltip(
- instructionsEnum: archive.isEmpty
- ? InstructionsEnum.noSavedActivitiesYet
- : InstructionsEnum.activityAnalyticsList,
- padding: const EdgeInsets.all(8.0),
- );
- }
- i--;
- return AnalyticsActivityItem(
- room: archive[i],
- selected: archive[i].id == selectedRoomId,
- );
- },
+ return StreamBuilder(
+ stream: Matrix.of(context)
+ .analyticsDataService
+ .updateDispatcher
+ .activityAnalyticsStream
+ .stream,
+ builder: (context, _) {
+ final Room? analyticsRoom =
+ Matrix.of(context).client.analyticsRoomLocal();
+ final archive = analyticsRoom?.archivedActivities ?? [];
+ final selectedRoomId =
+ GoRouterState.of(context).pathParameters['roomid'];
+ return Scaffold(
+ body: SafeArea(
+ child: Padding(
+ padding: const EdgeInsetsGeometry.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const LearningProgressIndicators(
+ selected: ProgressIndicatorEnum.activities,
),
- ),
+ Expanded(
+ child: MaxWidthBody(
+ withScrolling: false,
+ child: ListView.builder(
+ physics: const ClampingScrollPhysics(),
+ itemCount: archive.length + 1,
+ itemBuilder: (BuildContext context, int i) {
+ if (i == 0) {
+ return InstructionsInlineTooltip(
+ instructionsEnum: archive.isEmpty
+ ? InstructionsEnum.noSavedActivitiesYet
+ : InstructionsEnum.activityAnalyticsList,
+ padding: const EdgeInsets.all(8.0),
+ );
+ }
+ i--;
+ return AnalyticsActivityItem(
+ room: archive[i],
+ selected: archive[i].id == selectedRoomId,
+ );
+ },
+ ),
+ ),
+ ),
+ ],
),
- ],
+ ),
),
- ),
- ),
+ );
+ },
);
}
}
@@ -82,7 +94,11 @@ class AnalyticsActivityItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final objective = room.activityPlan?.learningObjective ?? '';
- final cefrLevel = room.activityPlan?.req.cefrLevel;
+ final cefrLevel = room.activitySummary?.summary?.participants
+ .firstWhereOrNull(
+ (p) => p.participantId == room.client.userID,
+ )
+ ?.cefrLevel;
final theme = Theme.of(context);
return Padding(
@@ -122,7 +138,7 @@ class AnalyticsActivityItem extends StatelessWidget {
vertical: 4,
),
child: Text(
- cefrLevel.string,
+ cefrLevel.toUpperCase(),
style: const TextStyle(fontSize: 14.0),
),
)
diff --git a/lib/pangea/analytics_practice/analytics_practice_constants.dart b/lib/pangea/analytics_practice/analytics_practice_constants.dart
new file mode 100644
index 000000000..a8e06083c
--- /dev/null
+++ b/lib/pangea/analytics_practice/analytics_practice_constants.dart
@@ -0,0 +1,5 @@
+class AnalyticsPracticeConstants {
+ static const int timeForBonus = 60;
+ static const int practiceGroupSize = 10;
+ static const int errorBufferSize = 5;
+}
diff --git a/lib/pangea/analytics_practice/analytics_practice_page.dart b/lib/pangea/analytics_practice/analytics_practice_page.dart
new file mode 100644
index 000000000..1bcc52d01
--- /dev/null
+++ b/lib/pangea/analytics_practice/analytics_practice_page.dart
@@ -0,0 +1,561 @@
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:flutter/material.dart';
+
+import 'package:collection/collection.dart';
+
+import 'package:fluffychat/l10n/l10n.dart';
+import 'package:fluffychat/pangea/analytics_data/analytics_data_service.dart';
+import 'package:fluffychat/pangea/analytics_data/analytics_updater_mixin.dart';
+import 'package:fluffychat/pangea/analytics_data/derived_analytics_data_model.dart';
+import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
+import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
+import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
+import 'package:fluffychat/pangea/analytics_misc/example_message_util.dart';
+import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart';
+import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_repo.dart';
+import 'package:fluffychat/pangea/analytics_practice/analytics_practice_view.dart';
+import 'package:fluffychat/pangea/common/utils/async_state.dart';
+import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
+import 'package:fluffychat/pangea/lemmas/lemma_info_repo.dart';
+import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
+import 'package:fluffychat/pangea/practice_activities/message_activity_request.dart';
+import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
+import 'package:fluffychat/pangea/practice_activities/practice_generation_repo.dart';
+import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart';
+import 'package:fluffychat/pangea/toolbar/message_practice/practice_record_controller.dart';
+import 'package:fluffychat/widgets/future_loading_dialog.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+class SelectedMorphChoice {
+ final MorphFeaturesEnum feature;
+ final String tag;
+
+ const SelectedMorphChoice({
+ required this.feature,
+ required this.tag,
+ });
+}
+
+class VocabPracticeChoice {
+ final String choiceId;
+ final String choiceText;
+ final String? choiceEmoji;
+
+ const VocabPracticeChoice({
+ required this.choiceId,
+ required this.choiceText,
+ this.choiceEmoji,
+ });
+}
+
+class _PracticeQueueEntry {
+ final MessageActivityRequest request;
+ final Completer completer;
+
+ _PracticeQueueEntry({
+ required this.request,
+ required this.completer,
+ });
+}
+
+class SessionLoader extends AsyncLoader {
+ final ConstructTypeEnum type;
+ SessionLoader({required this.type});
+
+ @override
+ Future fetch() =>
+ AnalyticsPracticeSessionRepo.get(type);
+}
+
+class AnalyticsPractice extends StatefulWidget {
+ static bool bypassExitConfirmation = true;
+
+ final ConstructTypeEnum type;
+ const AnalyticsPractice({
+ super.key,
+ required this.type,
+ });
+
+ @override
+ AnalyticsPracticeState createState() => AnalyticsPracticeState();
+}
+
+class AnalyticsPracticeState extends State
+ with AnalyticsUpdater {
+ late final SessionLoader _sessionLoader;
+
+ final ValueNotifier>
+ activityState = ValueNotifier(const AsyncState.idle());
+
+ final Queue<_PracticeQueueEntry> _queue = Queue();
+
+ final ValueNotifier activityTarget =
+ ValueNotifier(null);
+
+ final ValueNotifier progressNotifier = ValueNotifier(0.0);
+ final ValueNotifier enableChoicesNotifier = ValueNotifier(true);
+
+ final ValueNotifier selectedMorphChoice =
+ ValueNotifier(null);
+
+ final ValueNotifier hintPressedNotifier = ValueNotifier(false);
+
+ final Map> _choiceTexts = {};
+ final Map> _choiceEmojis = {};
+
+ StreamSubscription? _languageStreamSubscription;
+
+ @override
+ void initState() {
+ super.initState();
+ _sessionLoader = SessionLoader(type: widget.type);
+ _startSession();
+ _languageStreamSubscription = MatrixState
+ .pangeaController.userController.languageStream.stream
+ .listen((_) => _onLanguageUpdate());
+ }
+
+ @override
+ void dispose() {
+ _languageStreamSubscription?.cancel();
+ _sessionLoader.dispose();
+ activityState.dispose();
+ activityTarget.dispose();
+ progressNotifier.dispose();
+ enableChoicesNotifier.dispose();
+ selectedMorphChoice.dispose();
+ hintPressedNotifier.dispose();
+ super.dispose();
+ }
+
+ MultipleChoicePracticeActivityModel? get _currentActivity =>
+ activityState.value is AsyncLoaded
+ ? (activityState.value
+ as AsyncLoaded)
+ .value
+ : null;
+
+ bool get _isComplete => _sessionLoader.value?.isComplete ?? false;
+
+ ValueNotifier> get sessionState =>
+ _sessionLoader.state;
+
+ AnalyticsDataService get _analyticsService =>
+ Matrix.of(context).analyticsDataService;
+
+ List filteredChoices(
+ MultipleChoicePracticeActivityModel activity,
+ ) {
+ final content = activity.multipleChoiceContent;
+ final choices = content.choices.toList();
+ final answer = content.answers.first;
+ final filtered = [];
+
+ final seenTexts = {};
+ for (final id in choices) {
+ final text = getChoiceText(activity.storageKey, id);
+
+ if (seenTexts.contains(text)) {
+ if (id != answer) {
+ continue;
+ }
+
+ final index = filtered.indexWhere(
+ (choice) => choice.choiceText == text,
+ );
+ if (index != -1) {
+ filtered[index] = VocabPracticeChoice(
+ choiceId: id,
+ choiceText: text,
+ choiceEmoji: getChoiceEmoji(activity.storageKey, id),
+ );
+ }
+ continue;
+ }
+
+ seenTexts.add(text);
+ filtered.add(
+ VocabPracticeChoice(
+ choiceId: id,
+ choiceText: text,
+ choiceEmoji: getChoiceEmoji(activity.storageKey, id),
+ ),
+ );
+ }
+
+ return filtered;
+ }
+
+ String getChoiceText(String key, String choiceId) {
+ if (widget.type == ConstructTypeEnum.morph) {
+ return choiceId;
+ }
+ if (_choiceTexts.containsKey(key) &&
+ _choiceTexts[key]!.containsKey(choiceId)) {
+ return _choiceTexts[key]![choiceId]!;
+ }
+ final cId = ConstructIdentifier.fromString(choiceId);
+ return cId?.lemma ?? choiceId;
+ }
+
+ String? getChoiceEmoji(String key, String choiceId) {
+ if (widget.type == ConstructTypeEnum.morph) return null;
+ return _choiceEmojis[key]?[choiceId];
+ }
+
+ String choiceTargetId(String choiceId) =>
+ '${widget.type.name}-choice-card-${choiceId.replaceAll(' ', '_')}';
+
+ void _clearState() {
+ activityState.value = const AsyncState.loading();
+ activityTarget.value = null;
+ selectedMorphChoice.value = null;
+ hintPressedNotifier.value = false;
+ enableChoicesNotifier.value = true;
+ progressNotifier.value = 0.0;
+ _queue.clear();
+ _choiceTexts.clear();
+ _choiceEmojis.clear();
+ activityState.value = const AsyncState.idle();
+
+ AnalyticsPractice.bypassExitConfirmation = true;
+ }
+
+ void updateElapsedTime(int seconds) {
+ if (_sessionLoader.isLoaded) {
+ _sessionLoader.value!.setElapsedSeconds(seconds);
+ }
+ }
+
+ void _playAudio() {
+ if (activityTarget.value == null) return;
+ if (widget.type != ConstructTypeEnum.vocab) return;
+ TtsController.tryToSpeak(
+ activityTarget.value!.target.tokens.first.vocabConstructID.lemma,
+ langCode: MatrixState.pangeaController.userController.userL2!.langCode,
+ );
+ }
+
+ Future _waitForAnalytics() async {
+ if (!_analyticsService.initCompleter.isCompleted) {
+ MatrixState.pangeaController.initControllers();
+ await _analyticsService.initCompleter.future;
+ }
+ }
+
+ Future _onLanguageUpdate() async {
+ try {
+ _clearState();
+ await _analyticsService
+ .updateDispatcher.constructUpdateStream.stream.first
+ .timeout(const Duration(seconds: 10));
+ await reloadSession();
+ } catch (e) {
+ if (mounted) {
+ activityState.value = AsyncState.error(
+ L10n.of(context).oopsSomethingWentWrong,
+ );
+ }
+ }
+ }
+
+ Future _startSession() async {
+ await _waitForAnalytics();
+ await _sessionLoader.load();
+ if (_sessionLoader.isError) {
+ AnalyticsPractice.bypassExitConfirmation = true;
+ return;
+ }
+
+ progressNotifier.value = _sessionLoader.value!.progress;
+ await _continueSession();
+ }
+
+ Future reloadSession() async {
+ _clearState();
+ _sessionLoader.reset();
+ await _startSession();
+ }
+
+ Future reloadCurrentActivity() async {
+ if (activityTarget.value == null) return;
+
+ try {
+ activityState.value = const AsyncState.loading();
+ selectedMorphChoice.value = null;
+ hintPressedNotifier.value = false;
+
+ final req = activityTarget.value!;
+ final res = await _fetchActivity(req);
+
+ if (!mounted) return;
+ activityState.value = AsyncState.loaded(res);
+ _playAudio();
+ } catch (e) {
+ if (!mounted) return;
+ activityState.value = AsyncState.error(e);
+ }
+ }
+
+ Future _completeSession() async {
+ _sessionLoader.value!.finishSession();
+ setState(() {});
+
+ final bonus = _sessionLoader.value!.state.allBonusUses;
+ await _analyticsService.updateService.addAnalytics(
+ null,
+ bonus,
+ forceUpdate: true,
+ );
+ AnalyticsPractice.bypassExitConfirmation = true;
+ }
+
+ bool _continuing = false;
+
+ Future _continueSession() async {
+ if (_continuing) return;
+ _continuing = true;
+ enableChoicesNotifier.value = true;
+
+ try {
+ if (activityState.value
+ is AsyncIdle) {
+ await _initActivityData();
+ } else {
+ // Keep trying to load activities from the queue until one succeeds or queue is empty
+ while (_queue.isNotEmpty) {
+ activityState.value = const AsyncState.loading();
+ selectedMorphChoice.value = null;
+ hintPressedNotifier.value = false;
+ final nextActivityCompleter = _queue.removeFirst();
+
+ try {
+ final activity = await nextActivityCompleter.completer.future;
+ activityTarget.value = nextActivityCompleter.request;
+ _playAudio();
+ activityState.value = AsyncState.loaded(activity);
+ AnalyticsPractice.bypassExitConfirmation = false;
+ return;
+ } catch (e) {
+ // Completer failed, skip to next
+ continue;
+ }
+ }
+ // Queue is empty, complete the session
+ await _completeSession();
+ }
+ } catch (e) {
+ AnalyticsPractice.bypassExitConfirmation = true;
+ activityState.value = AsyncState.error(e);
+ } finally {
+ _continuing = false;
+ }
+ }
+
+ Future _initActivityData() async {
+ final requests = _sessionLoader.value!.activityRequests;
+ if (requests.isEmpty) {
+ throw L10n.of(context).noActivityRequest;
+ }
+
+ for (var i = 0; i < requests.length; i++) {
+ try {
+ activityState.value = const AsyncState.loading();
+ final req = requests[i];
+ final res = await _fetchActivity(req);
+ if (!mounted) return;
+ activityTarget.value = req;
+ _playAudio();
+ activityState.value = AsyncState.loaded(res);
+ AnalyticsPractice.bypassExitConfirmation = false;
+ // Fill queue with remaining requests
+ _fillActivityQueue(requests.skip(i + 1).toList());
+ return;
+ } catch (e) {
+ await recordSkippedUse(requests[i]);
+ // Try next request
+ continue;
+ }
+ }
+ AnalyticsPractice.bypassExitConfirmation = true;
+ if (!mounted) return;
+ activityState.value =
+ AsyncState.error(L10n.of(context).oopsSomethingWentWrong);
+ return;
+ }
+
+ Future _fillActivityQueue(
+ List requests,
+ ) async {
+ for (final request in requests) {
+ final completer = Completer();
+ _queue.add(
+ _PracticeQueueEntry(
+ request: request,
+ completer: completer,
+ ),
+ );
+ try {
+ final res = await _fetchActivity(request);
+ if (!mounted) return;
+ completer.complete(res);
+ } catch (e) {
+ if (!mounted) return;
+ completer.completeError(e);
+ await recordSkippedUse(request);
+ }
+ }
+ }
+
+ Future _fetchActivity(
+ MessageActivityRequest req,
+ ) async {
+ final result = await PracticeRepo.getPracticeActivity(
+ req,
+ messageInfo: {},
+ );
+
+ if (result.isError ||
+ result.result is! MultipleChoicePracticeActivityModel) {
+ throw L10n.of(context).oopsSomethingWentWrong;
+ }
+
+ final activityModel = result.result as MultipleChoicePracticeActivityModel;
+
+ // Prefetch lemma info for meaning activities before marking ready
+ if (activityModel is VocabMeaningPracticeActivityModel) {
+ final choices = activityModel.multipleChoiceContent.choices.toList();
+ await _fetchLemmaInfo(activityModel.storageKey, choices);
+ }
+
+ return activityModel;
+ }
+
+ Future _fetchLemmaInfo(
+ String requestKey,
+ List choiceIds,
+ ) async {
+ final texts = {};
+ final emojis = {};
+
+ for (final id in choiceIds) {
+ final cId = ConstructIdentifier.fromString(id);
+ if (cId == null) continue;
+
+ final res = await cId.getLemmaInfo({});
+ if (res.isError) {
+ LemmaInfoRepo.clearCache(cId.lemmaInfoRequest({}));
+ throw L10n.of(context).oopsSomethingWentWrong;
+ }
+
+ texts[id] = res.result!.meaning;
+ emojis[id] = res.result!.emoji.firstOrNull;
+ }
+
+ _choiceTexts.putIfAbsent(requestKey, () => {});
+ _choiceEmojis.putIfAbsent(requestKey, () => {});
+
+ _choiceTexts[requestKey]!.addAll(texts);
+ _choiceEmojis[requestKey]!.addAll(emojis);
+ }
+
+ Future recordSkippedUse(MessageActivityRequest request) async {
+ // Record a 0 XP use so that activity isn't chosen again soon
+ _sessionLoader.value!.incrementSkippedActivities();
+ final token = request.target.tokens.first;
+
+ final use = OneConstructUse(
+ useType: ConstructUseTypeEnum.ignPA,
+ constructType: widget.type,
+ metadata: ConstructUseMetaData(
+ roomId: null,
+ timeStamp: DateTime.now(),
+ ),
+ category: token.pos,
+ lemma: token.lemma.text,
+ form: token.lemma.text,
+ xp: 0,
+ );
+
+ await _analyticsService.updateService.addAnalytics(null, [use]);
+ }
+
+ void onHintPressed() {
+ hintPressedNotifier.value = !hintPressedNotifier.value;
+ }
+
+ Future onSelectChoice(
+ String choiceContent,
+ ) async {
+ if (_currentActivity == null) return;
+ final activity = _currentActivity!;
+
+ // Track the selection for display
+ if (activity is MorphPracticeActivityModel) {
+ selectedMorphChoice.value = SelectedMorphChoice(
+ feature: activity.morphFeature,
+ tag: choiceContent,
+ );
+ }
+ final isCorrect = activity.multipleChoiceContent.isCorrect(choiceContent);
+ if (isCorrect) {
+ enableChoicesNotifier.value = false;
+ }
+
+ // Update activity record
+ PracticeRecordController.onSelectChoice(
+ choiceContent,
+ activity.tokens.first,
+ activity,
+ );
+
+ final use = activity.constructUse(choiceContent);
+ _sessionLoader.value!.submitAnswer(use);
+ await _analyticsService.updateService
+ .addAnalytics(choiceTargetId(choiceContent), [use]);
+
+ if (!activity.multipleChoiceContent.isCorrect(choiceContent)) return;
+
+ _playAudio();
+
+ // Display the fact that the choice was correct before loading the next activity
+ await Future.delayed(const Duration(milliseconds: 1000));
+
+ // Then mark this activity as completed, and either load the next or complete the session
+ _sessionLoader.value!.completeActivity();
+ progressNotifier.value = _sessionLoader.value!.progress;
+
+ if (_queue.isEmpty) {
+ await _completeSession();
+ } else if (_isComplete) {
+ await _completeSession();
+ } else {
+ await _continueSession();
+ }
+ }
+
+ Future?> getExampleMessage(
+ MessageActivityRequest activityRequest,
+ ) async {
+ final target = activityRequest.target;
+ final token = target.tokens.first;
+ final construct = target.targetTokenConstructID(token);
+
+ if (widget.type == ConstructTypeEnum.morph) {
+ return activityRequest.morphExampleInfo?.exampleMessage;
+ }
+
+ return ExampleMessageUtil.getExampleMessage(
+ await _analyticsService.getConstructUse(construct),
+ Matrix.of(context).client,
+ );
+ }
+
+ Future get derivedAnalyticsData =>
+ _analyticsService.derivedData;
+
+ @override
+ Widget build(BuildContext context) => AnalyticsPracticeView(this);
+}
diff --git a/lib/pangea/analytics_practice/analytics_practice_session_model.dart b/lib/pangea/analytics_practice/analytics_practice_session_model.dart
new file mode 100644
index 000000000..ebeccdce0
--- /dev/null
+++ b/lib/pangea/analytics_practice/analytics_practice_session_model.dart
@@ -0,0 +1,280 @@
+import 'package:flutter/painting.dart';
+
+import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
+import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
+import 'package:fluffychat/pangea/analytics_practice/analytics_practice_constants.dart';
+import 'package:fluffychat/pangea/practice_activities/message_activity_request.dart';
+import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
+
+class MorphExampleInfo {
+ final List exampleMessage;
+
+ const MorphExampleInfo({
+ required this.exampleMessage,
+ });
+
+ Map toJson() {
+ final segments =