diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index 5f7c5fc8e..9775beb3c 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -434,4 +434,8 @@ class CurrentITStep { } } } + + // get continuance with highest level + Continuance get best => + continuances.reduce((a, b) => a.level > b.level ? a : b); } diff --git a/lib/pangea/choreographer/widgets/it_bar.dart b/lib/pangea/choreographer/widgets/it_bar.dart index 883761a9a..9fa092215 100644 --- a/lib/pangea/choreographer/widgets/it_bar.dart +++ b/lib/pangea/choreographer/widgets/it_bar.dart @@ -1,18 +1,20 @@ import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/controllers/it_controller.dart'; import 'package:fluffychat/pangea/choreographer/widgets/it_bar_buttons.dart'; +import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart'; import 'package:fluffychat/pangea/choreographer/widgets/translation_finished_flow.dart'; import 'package:fluffychat/pangea/constants/choreo_constants.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + import '../../../config/app_config.dart'; +import '../../controllers/it_feedback_controller.dart'; import '../../models/it_response_model.dart'; import '../../utils/overlay.dart'; +import '../../widgets/igc/word_data_card.dart'; import 'choice_array.dart'; class ITBar extends StatelessWidget { @@ -225,17 +227,31 @@ class ITChoices extends StatelessWidget { ]) => OverlayUtil.showPositionedCard( context: context, - cardToShow: WordDataCard( - word: controller.currentITStep!.continuances[index].text, - wordLang: controller.targetLangCode, - fullText: sourceText ?? controller.choreographer.currentText, - fullTextLang: sourceText != null - ? controller.sourceLangCode - : controller.targetLangCode, - hasInfo: controller.currentITStep!.continuances[index].hasInfo, - choiceFeedback: choiceFeedback, - room: controller.choreographer.chatController.room, - ), + cardToShow: choiceFeedback == null + ? WordDataCard( + word: controller.currentITStep!.continuances[index].text, + wordLang: controller.targetLangCode, + fullText: sourceText ?? controller.choreographer.currentText, + fullTextLang: sourceText != null + ? controller.sourceLangCode + : controller.targetLangCode, + hasInfo: controller.currentITStep!.continuances[index].hasInfo, + choiceFeedback: choiceFeedback, + room: controller.choreographer.chatController.room, + ) + : ITFeedbackCard( + req: ITFeedbackRequestModel( + sourceText: sourceText!, + currentText: controller.choreographer.currentText, + chosenContinuance: + controller.currentITStep!.continuances[index].text, + bestContinuance: controller.currentITStep!.best.text, + feedbackLang: controller.targetLangCode, + sourceTextLang: controller.sourceLangCode, + targetLang: controller.targetLangCode, + ), + choiceFeedback: choiceFeedback, + ), cardSize: const Size(300, 300), borderColor: borderColor, transformTargetId: controller.choreographer.itBarTransformTargetKey, diff --git a/lib/pangea/choreographer/widgets/it_feedback_card.dart b/lib/pangea/choreographer/widgets/it_feedback_card.dart new file mode 100644 index 000000000..cec2e9e2f --- /dev/null +++ b/lib/pangea/choreographer/widgets/it_feedback_card.dart @@ -0,0 +1,218 @@ +import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart'; +import 'package:fluffychat/pangea/widgets/igc/why_button.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart'; + +import '../../../config/app_config.dart'; +import '../../../widgets/matrix.dart'; +import '../../controllers/it_feedback_controller.dart'; +import '../../controllers/pangea_controller.dart'; +import '../../utils/bot_style.dart'; +import '../../widgets/common/bot_face_svg.dart'; +import '../../widgets/igc/card_error_widget.dart'; +import '../../widgets/igc/card_header.dart'; + +class ITFeedbackCard extends StatefulWidget { + final ITFeedbackRequestModel req; + final String choiceFeedback; + + const ITFeedbackCard({ + super.key, + required this.req, + required this.choiceFeedback, + }); + + @override + State createState() => ITFeedbackCardController(); +} + +class ITFeedbackCardController extends State { + final PangeaController controller = MatrixState.pangeaController; + + Object? error; + bool isLoadingFeedback = false; + bool isTranslating = false; + ITFeedbackResponseModel? res; + String? translatedFeedback; + + Response get noLanguages => Response("", 405); + + @override + void initState() { + if (!mounted) return; + //any setup? + super.initState(); + } + + Future getFeedback() async { + setState(() { + isLoadingFeedback = true; + }); + controller.itFeedback + .get(widget.req) + .then((value) { + res = value; + }) + .catchError((e) => error = e) + .whenComplete( + () => setState(() { + isLoadingFeedback = false; + }), + ); + } + + Future translateFeedback() async { + setState(() { + isTranslating = true; + }); + FullTextTranslationRepo.translate( + accessToken: await controller.userController.accessToken, + request: FullTextTranslationRequestModel( + text: res!.text, + tgtLang: controller.languageController.userL1?.langCode ?? + widget.req.sourceTextLang, + userL1: controller.languageController.userL1?.langCode ?? + widget.req.sourceTextLang, + userL2: controller.languageController.userL2?.langCode ?? + widget.req.targetLang, + ), + ) + .then((value) { + translatedFeedback = value.bestTranslation; + }) + .catchError((e) => error = e) + .whenComplete( + () => setState(() { + isTranslating = false; + }), + ); + } + + void handleGetExplanationButtonPress() { + if (isLoadingFeedback) return; + getFeedback(); + } + + @override + Widget build(BuildContext context) => error == null + ? ITFeedbackCardView(controller: this) + : CardErrorWidget(error: error); +} + +class ITFeedbackCardView extends StatelessWidget { + const ITFeedbackCardView({ + super.key, + required this.controller, + }); + + final ITFeedbackCardController controller; + + @override + Widget build(BuildContext context) { + final ScrollController scrollController = ScrollController(); + + return Scrollbar( + thumbVisibility: true, + controller: scrollController, + child: SingleChildScrollView( + controller: scrollController, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CardHeader( + text: controller.widget.req.chosenContinuance, + botExpression: BotExpression.down, + ), + Text( + controller.widget.choiceFeedback, + style: BotStyle.text(context), + ), + const SizedBox(height: 20), + if (controller.res == null) + WhyButton( + onPress: controller.handleGetExplanationButtonPress, + loading: controller.isLoadingFeedback, + ), + if (controller.res != null) + Text( + controller.res!.text, + style: BotStyle.text(context), + ), + // if res is not null, show a button to translate the text + if (controller.res != null && controller.translatedFeedback == null) + Column( + children: [ + const SizedBox(height: 10), + TranslateButton( + onPress: controller.translateFeedback, + loading: controller.isTranslating, + ), + ], + ), + if (controller.translatedFeedback != null) + //add little line to separate the text from the translation + Column( + children: [ + const Divider( + color: AppConfig.primaryColor, + thickness: 2, + height: 20, // Set the space around the divider + indent: 20, // Set the starting space (left padding) + endIndent: 20, // Set the ending space (right padding) + ), + Text( + controller.translatedFeedback!, + style: BotStyle.text(context), + ), + ], + ), + ], + ), + ), + ); + } +} + +// button to translate the text +class TranslateButton extends StatelessWidget { + const TranslateButton({ + super.key, + required this.onPress, + required this.loading, + }); + + final VoidCallback onPress; + final bool loading; + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: loading ? null : onPress, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + AppConfig.primaryColor.withOpacity(0.1), + ), + ), + child: SizedBox( + width: 150, // set the width of the button contents here + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (!loading) const Icon(Icons.translate), + if (loading) + const Center( + child: SizedBox( + width: 24.0, + height: 24.0, + child: CircularProgressIndicator(), + ), + ), + // const SizedBox(width: 8), + // Text(L10n.of(context)!.translate), + ], + ), + ), + ); + } +} diff --git a/lib/pangea/config/environment.dart b/lib/pangea/config/environment.dart index e55f66423..b33854fd7 100644 --- a/lib/pangea/config/environment.dart +++ b/lib/pangea/config/environment.dart @@ -27,7 +27,7 @@ class Environment { } static String get choreoApi { - // return "http://localhost:8000/choreo"; + return "http://localhost:8000/choreo"; return dotenv.env['CHOREO_API'] ?? 'Not found'; } diff --git a/lib/pangea/constants/model_keys.dart b/lib/pangea/constants/model_keys.dart index 66f9d24a8..acec26c80 100644 --- a/lib/pangea/constants/model_keys.dart +++ b/lib/pangea/constants/model_keys.dart @@ -76,4 +76,15 @@ class ModelKey { static const String targetWord = "target_word"; static const String baseExampleSentence = "base_example_sentence"; static const String targetExampleSentence = "target_example_sentence"; + + //add goldTranslation, goldContinuance, chosenContinuance + static const String goldTranslation = "gold_translation"; + static const String goldContinuance = "gold_continuance"; + static const String chosenContinuance = "chosen_continuance"; + + // sourceText, currentText, bestContinuance, feedback_lang + static const String sourceText = "src"; + static const String currentText = "current"; + static const String bestContinuance = "best_continuance"; + static const String feedbackLang = "feedback_lang"; } diff --git a/lib/pangea/controllers/it_feedback_controller.dart b/lib/pangea/controllers/it_feedback_controller.dart new file mode 100644 index 000000000..ffef9123f --- /dev/null +++ b/lib/pangea/controllers/it_feedback_controller.dart @@ -0,0 +1,153 @@ +import 'dart:convert'; + +import 'package:collection/collection.dart'; +import 'package:fluffychat/pangea/config/environment.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart'; + +import '../constants/model_keys.dart'; +import '../network/requests.dart'; +import '../network/urls.dart'; +import 'pangea_controller.dart'; + +class ITFeedbackController { + late PangeaController _pangeaController; + + final List<_ITFeedbackCacheItem> _feedback = []; + + ITFeedbackController(PangeaController pangeaController) { + _pangeaController = pangeaController; + } + + _ITFeedbackCacheItem? _getLocal( + ITFeedbackRequestModel req, + ) => + _feedback.firstWhereOrNull( + (e) => + e.chosenContinuance == req.chosenContinuance && + e.sourceText == req.sourceText, + ); + + Future get( + ITFeedbackRequestModel req, + ) { + final _ITFeedbackCacheItem? localItem = _getLocal(req); + + if (localItem != null) return localItem.data; + + _feedback.add( + _ITFeedbackCacheItem( + chosenContinuance: req.chosenContinuance, + sourceText: req.sourceText, + data: _get(req), + ), + ); + + return _feedback.last.data; + } + + Future _get( + ITFeedbackRequestModel request, + ) async { + try { + final ITFeedbackResponseModel res = await _ITFeedbackRepo.get( + await _pangeaController.userController.accessToken, + request, + ); + return res; + } catch (err, stack) { + debugPrint( + "error getting contextual definition for ${request.chosenContinuance} in '${request.sourceText}'", + ); + ErrorHandler.logError(e: err, s: stack, data: request.toJson()); + return null; + } + } +} + +class _ITFeedbackCacheItem { + String chosenContinuance; + String sourceText; + Future data; + + _ITFeedbackCacheItem({ + required this.chosenContinuance, + required this.sourceText, + required this.data, + }); +} + +class _ITFeedbackRepo { + static Future get( + String accessToken, + ITFeedbackRequestModel request, + ) async { + final Requests req = Requests( + choreoApiKey: Environment.choreoApiKey, + accessToken: accessToken, + ); + + final Response res = await req.post( + url: PApiUrls.itFeedback, + body: request.toJson(), + ); + + final ITFeedbackResponseModel response = ITFeedbackResponseModel.fromJson( + jsonDecode( + utf8.decode(res.bodyBytes).toString(), + ), + ); + + if (response.text.isEmpty) { + ErrorHandler.logError( + e: Exception( + "empty text in contextual definition response", + ), + ); + } + + return response; + } +} + +class ITFeedbackRequestModel { + final String sourceText; + final String currentText; + final String bestContinuance; + final String chosenContinuance; + final String feedbackLang; + final String sourceTextLang; + final String targetLang; + + ITFeedbackRequestModel({ + required this.sourceText, + required this.currentText, + required this.bestContinuance, + required this.chosenContinuance, + required this.feedbackLang, + required this.sourceTextLang, + required this.targetLang, + }); + + Map toJson() => { + ModelKey.sourceText: sourceText, + ModelKey.currentText: currentText, + ModelKey.bestContinuance: bestContinuance, + ModelKey.chosenContinuance: chosenContinuance, + ModelKey.feedbackLang: feedbackLang, + ModelKey.srcLang: sourceTextLang, + ModelKey.tgtLang: targetLang, + }; +} + +class ITFeedbackResponseModel { + String text; + + ITFeedbackResponseModel({required this.text}); + + factory ITFeedbackResponseModel.fromJson( + Map json, + ) => + ITFeedbackResponseModel(text: json[ModelKey.text]); +} diff --git a/lib/pangea/controllers/pangea_controller.dart b/lib/pangea/controllers/pangea_controller.dart index 68b1caa5a..122169082 100644 --- a/lib/pangea/controllers/pangea_controller.dart +++ b/lib/pangea/controllers/pangea_controller.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'dart:math'; +import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/controllers/class_controller.dart'; import 'package:fluffychat/pangea/controllers/contextual_definition_controller.dart'; @@ -26,6 +27,7 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import '../../config/app_config.dart'; import '../utils/firebase_analytics.dart'; import '../utils/p_store.dart'; +import 'it_feedback_controller.dart'; import 'message_analytics_controller.dart'; class PangeaController { @@ -40,6 +42,7 @@ class PangeaController { late LocalSettings localSettings; late MessageDataController messageData; late ContextualDefinitionController definitions; + late ITFeedbackController itFeedback; late InstructionsController instructions; late SubscriptionController subscriptionController; @@ -86,6 +89,7 @@ class PangeaController { definitions = ContextualDefinitionController(this); instructions = InstructionsController(this); subscriptionController = SubscriptionController(this); + itFeedback = ITFeedbackController(this); PAuthGaurd.pController = this; } diff --git a/lib/pangea/network/urls.dart b/lib/pangea/network/urls.dart index 0cd4c2e9c..c37919503 100644 --- a/lib/pangea/network/urls.dart +++ b/lib/pangea/network/urls.dart @@ -60,6 +60,8 @@ class PApiUrls { static String similarity = "${Environment.choreoApi}/similarity"; static String topicInfo = "${Environment.choreoApi}/vocab_list"; + static String itFeedback = "${Environment.choreoApi}/translation/feedback"; + static String firstStep = "/it_initialstep"; static String subseqStep = "/it_step"; diff --git a/lib/pangea/widgets/igc/span_card.dart b/lib/pangea/widgets/igc/span_card.dart index a060ed2e1..ce1523d01 100644 --- a/lib/pangea/widgets/igc/span_card.dart +++ b/lib/pangea/widgets/igc/span_card.dart @@ -1,10 +1,5 @@ import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/enum/span_data_type.dart'; import 'package:fluffychat/pangea/models/span_data.dart'; @@ -12,6 +7,10 @@ import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/match_copy.dart'; import 'package:fluffychat/pangea/widgets/igc/card_error_widget.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + import '../../../widgets/matrix.dart'; import '../../choreographer/widgets/choice_array.dart'; import '../../controllers/pangea_controller.dart'; @@ -19,6 +18,7 @@ import '../../enum/span_choice_type.dart'; import '../../models/span_card_model.dart'; import '../common/bot_face_svg.dart'; import 'card_header.dart'; +import 'why_button.dart'; const wordMatchResultsCount = 5; @@ -314,37 +314,13 @@ class PromptAndFeedback extends StatelessWidget { ), const SizedBox(height: 8), if (controller.selectedChoice?.feedback == null) - TextButton( - onPressed: () { + WhyButton( + onPress: () { if (!controller.fetchingData) { controller.getSpanDetails(); } }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - AppConfig.primaryColor.withOpacity(0.1), - ), - ), - child: SizedBox( - width: 150, // set the width of the button contents here - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (!controller.fetchingData) - const Icon(Icons.lightbulb_outline), - if (controller.fetchingData) - const Center( - child: SizedBox( - width: 24.0, - height: 24.0, - child: CircularProgressIndicator(), - ), - ), - const SizedBox(width: 8), - Text(L10n.of(context)!.why), - ], - ), - ), + loading: controller.fetchingData, ), ], if (!controller.fetchingData && diff --git a/lib/pangea/widgets/igc/why_button.dart b/lib/pangea/widgets/igc/why_button.dart new file mode 100644 index 000000000..7cb367065 --- /dev/null +++ b/lib/pangea/widgets/igc/why_button.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +import '../../../config/app_config.dart'; + +class WhyButton extends StatelessWidget { + const WhyButton({ + super.key, + required this.onPress, + required this.loading, + }); + + final VoidCallback onPress; + final bool loading; + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: loading ? null : onPress, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + AppConfig.primaryColor.withOpacity(0.1), + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), // Border radius + side: const BorderSide( + color: AppConfig.primaryColor, // Replace with your color + style: BorderStyle.solid, + width: 2.0, + ), + ), + ), + ), + child: SizedBox( + width: 150, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (!loading) const Icon(Icons.lightbulb_outline), + if (loading) + const Center( + child: SizedBox( + width: 24.0, + height: 24.0, + child: CircularProgressIndicator(), + ), + ), + const SizedBox(width: 8), + Text(L10n.of(context)!.why), + ], + ), + ), + ); + } +} diff --git a/needed-translations.txt b/needed-translations.txt index 2877ad298..cdb97658e 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -764,7 +764,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "bn": [ @@ -1537,7 +1538,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "bo": [ @@ -2310,7 +2312,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ca": [ @@ -3078,7 +3081,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "cs": [ @@ -3846,7 +3850,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "de": [ @@ -4614,7 +4619,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "el": [ @@ -5387,7 +5393,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "eo": [ @@ -6155,39 +6162,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" - ], - - "es": [ - "notAnImage", - "importNow", - "importEmojis", - "importFromZipFile", - "importZipFile", - "exportEmotePack", - "savedEmotePack", - "sendTypingNotifications", - "reportToTeacher", - "reportMessageTitle", - "reportMessageBody", - "noTeachersFound", - "noAddToSpacePermissions", - "alreadyInSpace", - "yourGlobalUserIdIs", - "noUsersFoundWithQuery", - "searchChatsRooms", - "groupName", - "createGroupAndInviteUsers", - "groupCanBeFoundViaSearch", - "inNoSpaces", - "createClass", - "createExchange", - "viewArchive", - "trialExpiration", - "freeTrialDesc", - "activateTrial", - "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "et": [ @@ -6955,7 +6931,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "eu": [ @@ -7723,7 +7700,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "fa": [ @@ -8491,7 +8469,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "fi": [ @@ -9259,7 +9238,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "fr": [ @@ -10027,7 +10007,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ga": [ @@ -10795,7 +10776,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "gl": [ @@ -11563,7 +11545,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "he": [ @@ -12331,7 +12314,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "hi": [ @@ -13104,7 +13088,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "hr": [ @@ -13872,7 +13857,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "hu": [ @@ -14640,7 +14626,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "id": [ @@ -15408,7 +15395,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ie": [ @@ -16178,7 +16166,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "it": [ @@ -16946,7 +16935,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ja": [ @@ -17714,7 +17704,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ko": [ @@ -18482,7 +18473,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "lt": [ @@ -19250,7 +19242,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "lv": [ @@ -20023,7 +20016,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "nb": [ @@ -20791,7 +20785,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "nl": [ @@ -21559,7 +21554,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "pl": [ @@ -22327,7 +22323,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "pt": [ @@ -23100,7 +23097,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "pt_BR": [ @@ -23868,7 +23866,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "pt_PT": [ @@ -24636,7 +24635,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ro": [ @@ -25404,7 +25404,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ru": [ @@ -26172,7 +26173,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "sk": [ @@ -26941,7 +26943,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "sl": [ @@ -27712,7 +27715,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "sr": [ @@ -28480,7 +28484,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "sv": [ @@ -29248,7 +29253,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "ta": [ @@ -30021,7 +30027,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "th": [ @@ -30794,7 +30801,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "tr": [ @@ -31562,7 +31570,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "uk": [ @@ -32330,7 +32339,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "vi": [ @@ -33101,7 +33111,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "zh": [ @@ -33869,7 +33880,8 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ], "zh_Hant": [ @@ -34637,6 +34649,7 @@ "freeTrialDesc", "activateTrial", "successfullySubscribed", - "clickToManageSubscription" + "clickToManageSubscription", + "emptyInviteWarning" ] }