Unborked locales (#1741)

* feat: localized languages

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ggurdin <ggurdin@gmail.com>
Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>
pull/1605/head
wcjord 9 months ago committed by GitHub
parent ed93d59902
commit c72a2da7fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -70,12 +70,8 @@ class ChatInputRow extends StatelessWidget {
activel1.langCode != LanguageKeys.unknownLanguage &&
activel2.langCode != LanguageKeys.unknownLanguage
? L10n.of(context).writeAMessageFlag(
activel1.languageEmoji ??
activel1.getDisplayName(context) ??
activel1.langCode,
activel2.languageEmoji ??
activel2.getDisplayName(context) ??
activel2.langCode,
activel1.getDisplayName(context) ?? activel1.langCode,
activel2.getDisplayName(context) ?? activel2.langCode,
)
: L10n.of(context).writeAMessage;
}

@ -11,25 +11,25 @@ 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/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
import 'package:fluffychat/widgets/matrix.dart';
extension AnalyticsClientExtension on Client {
/// Get the logged in user's analytics room matching
/// a given langCode. If not present, create it.
Future<Room?> getMyAnalyticsRoom(String langCode) async {
final Room? analyticsRoom = analyticsRoomLocal(langCode);
Future<Room?> getMyAnalyticsRoom(LanguageModel lang) async {
final Room? analyticsRoom = analyticsRoomLocal(lang);
if (analyticsRoom != null) return analyticsRoom;
return _makeAnalyticsRoom(langCode);
return _makeAnalyticsRoom(lang);
}
/// Get local analytics room for a given langCode and
/// optional userId (if not specified, uses current user).
/// If user is invited to the room, joins the room.
Room? analyticsRoomLocal([String? langCode, String? userIdParam]) {
langCode ??=
MatrixState.pangeaController.languageController.userL2?.langCode;
Room? analyticsRoomLocal([LanguageModel? lang, String? userIdParam]) {
lang ??= MatrixState.pangeaController.languageController.userL2;
if (langCode == null) {
if (lang == null) {
debugger(when: kDebugMode);
return null;
}
@ -37,7 +37,7 @@ extension AnalyticsClientExtension on Client {
final Room? analyticsRoom = rooms.firstWhereOrNull((e) {
return e.isAnalyticsRoom &&
e.isAnalyticsRoomOfUser(userIdParam ?? userID!) &&
e.isMadeForLang(langCode!);
e.isMadeForLang(lang!.langCodeShort);
});
if (analyticsRoom != null &&
analyticsRoom.membership == Membership.invite) {
@ -47,7 +47,7 @@ extension AnalyticsClientExtension on Client {
e: error,
s: stackTrace,
data: {
"langCode": langCode,
"langCode": lang!.langCodeShort,
"userIdParam": userIdParam,
},
),
@ -62,7 +62,7 @@ extension AnalyticsClientExtension on Client {
///
/// If the room does not appear immediately after creation, this method waits for it to appear in sync.
/// Returns the created [Room] object.
Future<Room?> _makeAnalyticsRoom(String langCode) async {
Future<Room?> _makeAnalyticsRoom(LanguageModel lang) async {
if (userID == null || userID == BotName.byEnvironment) {
return null;
}
@ -70,9 +70,9 @@ extension AnalyticsClientExtension on Client {
final String roomID = await createRoom(
creationContent: {
'type': PangeaRoomTypes.analytics,
ModelKey.langCode: langCode,
ModelKey.langCode: lang.langCodeShort,
},
name: "$userID $langCode Analytics",
name: "$userID ${lang.langCodeShort} Analytics",
topic: "This room stores learning analytics for $userID.",
preset: CreateRoomPreset.publicChat,
visibility: Visibility.private,

@ -18,6 +18,7 @@ import 'package:fluffychat/pangea/common/controllers/base_controller.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
/// A minimized version of AnalyticsController that get the logged in user's analytics
class GetAnalyticsController extends BaseController {
@ -42,7 +43,7 @@ class GetAnalyticsController extends BaseController {
);
}
String? get _l2Code => _pangeaController.languageController.userL2?.langCode;
LanguageModel? get _l2 => _pangeaController.languageController.userL2;
Client get _client => _pangeaController.matrixState.client;
// the minimum XP required for a given level
@ -282,13 +283,13 @@ class GetAnalyticsController extends BaseController {
Future<DateTime?> myAnalyticsLastUpdated() async {
// this function gets called soon after login, so first
// make sure that the user's l2 is loaded, if the user has set their l2
if (_client.userID != null && _l2Code == null) {
if (_client.userID != null && _l2 == null) {
if (_pangeaController.matrixState.client.prevBatch == null) {
await _pangeaController.matrixState.client.onSync.stream.first;
}
if (_l2Code == null) return null;
if (_l2 == null) return null;
}
final Room? analyticsRoom = _client.analyticsRoomLocal(_l2Code!);
final Room? analyticsRoom = _client.analyticsRoomLocal(_l2!);
if (analyticsRoom == null) return null;
final DateTime? lastUpdated = await analyticsRoom.analyticsLastUpdated(
_client.userID!,
@ -298,8 +299,8 @@ class GetAnalyticsController extends BaseController {
/// Get all the construct analytics events for the logged in user
Future<List<ConstructAnalyticsEvent>> _allMyConstructs() async {
if (_l2Code == null) return [];
final Room? analyticsRoom = _client.analyticsRoomLocal(_l2Code!);
if (_l2 == null) return [];
final Room? analyticsRoom = _client.analyticsRoomLocal(_l2!);
if (analyticsRoom == null) return [];
return await analyticsRoom.getAnalyticsEvents(userId: _client.userID!) ??
[];
@ -310,7 +311,7 @@ class GetAnalyticsController extends BaseController {
ConstructTypeEnum? constructType,
}) {
final index = _cache.indexWhere(
(e) => e.type == constructType && e.langCode == _l2Code,
(e) => e.type == constructType && e.langCode == _l2?.langCodeShort,
);
if (index > -1) {
@ -330,11 +331,11 @@ class GetAnalyticsController extends BaseController {
required List<OneConstructUse> uses,
ConstructTypeEnum? constructType,
}) {
if (_l2Code == null) return;
if (_l2 == null) return;
final entry = AnalyticsCacheEntry(
type: constructType,
uses: List.from(uses),
langCode: _l2Code!,
langCode: _l2!.langCodeShort,
);
_cache.add(entry);
}

@ -14,6 +14,7 @@ import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
enum AnalyticsUpdateType { server, local }
@ -30,8 +31,6 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
Client get _client => _pangeaController.matrixState.client;
String? get _userL2 => _pangeaController.languageController.activeL2Code();
/// the last time that matrix analytics events were updated for the user's current l2
DateTime? lastUpdated;
@ -62,8 +61,9 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
// Listen for changes to the user's language settings
_languageStream ??=
_pangeaController.userController.stateStream.listen((update) {
if (update is Map<String, dynamic>) {
final previousL2 = update['prev_target_lang'];
if (update is Map<String, dynamic> &&
update['prev_target_lang'] is LanguageModel) {
final LanguageModel previousL2 = update['prev_target_lang'];
_onUpdateLanguages(previousL2);
}
});
@ -147,7 +147,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
);
}
Future<void> _onUpdateLanguages(String? previousL2) async {
Future<void> _onUpdateLanguages(LanguageModel? previousL2) async {
await sendLocalAnalyticsToAnalyticsRoom(
l2Override: previousL2,
);
@ -361,7 +361,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
/// since the last update and notifies the [analyticsUpdateStream].
Future<void> sendLocalAnalyticsToAnalyticsRoom({
onLogout = false,
String? l2Override,
LanguageModel? l2Override,
}) async {
if (_pangeaController.matrixState.client.userID == null) return;
if (_pangeaController.getAnalytics.messagesSinceUpdate.isEmpty) return;
@ -400,7 +400,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
/// Updates the analytics by sending cached analytics data to the analytics room.
/// The analytics room is determined based on the user's current target language.
Future<void> _updateAnalytics({String? l2Override}) async {
Future<void> _updateAnalytics({LanguageModel? l2Override}) async {
// if there's no cached construct data, there's nothing to send
final cachedConstructs = _pangeaController.getAnalytics.messagesSinceUpdate;
final bool onlyDraft = cachedConstructs.length == 1 &&
@ -408,11 +408,11 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
if (cachedConstructs.isEmpty || onlyDraft) return;
// if missing important info, don't send analytics. Could happen if user just signed up.
final l2Code = l2Override ?? _userL2;
if (l2Code == null || _client.userID == null) return;
final l2 = l2Override ?? _pangeaController.languageController.userL2;
if (l2 == null || _client.userID == null) return;
// analytics room for the user and current target language
final Room? analyticsRoom = await _client.getMyAnalyticsRoom(l2Code);
final Room? analyticsRoom = await _client.getMyAnalyticsRoom(l2);
// and send cached analytics data to the room
await analyticsRoom?.sendConstructsEvent(

@ -362,6 +362,7 @@ class ITChoices extends StatelessWidget {
maxWidth: 300,
borderColor: borderColor,
transformTargetId: controller.choreographer.itBarTransformTargetKey,
isScrollable: choiceFeedback == null,
);
}

@ -1,9 +1,11 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart';
import '../../../config/app_config.dart';
import '../../../widgets/matrix.dart';
import '../../bot/utils/bot_style.dart';
import '../../common/controllers/pangea_controller.dart';
@ -90,40 +92,37 @@ class ITFeedbackCardView extends StatelessWidget {
@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: [
Text(
controller.widget.req.chosenContinuance,
),
const SizedBox(height: 10),
Text(
"",
style: TextStyle(
fontSize:
AppConfig.fontSizeFactor * AppConfig.messageFontSize * 1.5,
fontWeight: FontWeight.bold,
),
),
Container(
constraints: const BoxConstraints(
minHeight: 30,
),
child: Text(
controller.res?.text ?? "loading",
style: BotStyle.text(context),
),
),
],
),
const characterWidth = 10.0;
return Container(
constraints: const BoxConstraints(maxWidth: 300),
alignment: Alignment.center,
child: Wrap(
alignment: WrapAlignment.center,
children: [
Text(
controller.widget.req.chosenContinuance,
style: BotStyle.text(context),
),
const SizedBox(width: 10),
Text(
"",
style: BotStyle.text(context),
),
const SizedBox(width: 10),
controller.res?.text != null
? Text(
controller.res!.text,
style: BotStyle.text(context),
)
: TextLoadingShimmer(
width: min(
140,
characterWidth *
controller.widget.req.chosenContinuance.length,
),
),
],
),
);
}

@ -24,7 +24,7 @@ class PApiUrls {
static String appVersion = "${PApiUrls.choreoEndpoint}/version";
/// ---------------------- Languages --------------------------------------
static String getLanguages = "${PApiUrls.choreoEndpoint}/languages";
static String getLanguages = "${PApiUrls.choreoEndpoint}/languages_v2";
/// ---------------------- Users --------------------------------------
static String paymentLink = "${PApiUrls.subscriptionEndpoint}/payment_link";

@ -97,6 +97,7 @@ class OverlayUtil {
Color? borderColor,
bool closePrevOverlay = true,
String? overlayKey,
bool isScrollable = true,
}) {
try {
final LayerLinkAndKey layerLinkAndKey =
@ -142,6 +143,7 @@ class OverlayUtil {
borderColor: borderColor,
maxHeight: maxHeight,
maxWidth: maxWidth,
isScrollable: isScrollable,
),
);

@ -5,6 +5,7 @@ class OverlayContainer extends StatelessWidget {
final Color? borderColor;
final double maxHeight;
final double maxWidth;
final bool isScrollable;
const OverlayContainer({
super.key,
@ -12,10 +13,17 @@ class OverlayContainer extends StatelessWidget {
this.borderColor,
required this.maxHeight,
required this.maxWidth,
this.isScrollable = true,
});
@override
Widget build(BuildContext context) {
final content = Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [cardToShow],
);
return Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
decoration: BoxDecoration(
@ -35,14 +43,7 @@ class OverlayContainer extends StatelessWidget {
minHeight: 100,
minWidth: 100,
),
//PTODO - position card above input/message
// margin: const EdgeInsets.all(10),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [cardToShow],
),
),
child: isScrollable ? SingleChildScrollView(child: content) : content,
);
}
}

@ -103,8 +103,8 @@ class PangeaRepresentation {
ChoreoRecord? choreo,
}) {
final List<OneConstructUse> uses = [];
final l2 = MatrixState.pangeaController.languageController.activeL2Code();
if (langCode != l2) return uses;
final l2 = MatrixState.pangeaController.languageController.userL2;
if (langCode.split("-")[0] != l2?.langCodeShort) return uses;
// missing vital info so return
if (event?.roomId == null && metadata?.roomId == null) {

@ -9,7 +9,7 @@ class LanguageKeys {
class PrefKey {
static const lastFetched = 'p_lang_lastfetched';
static const flags = 'p_lang_flag';
static const languagesKey = 'p_lang_flag';
}
final LanguageDetection unknownLanguageDetection = LanguageDetection(

@ -1,29 +1,19 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
import 'package:fluffychat/pangea/learning_settings/enums/l2_support_enum.dart';
import '../../common/utils/error_handler.dart';
class LanguageModel {
final String langCode;
final String languageFlag;
final String displayName;
final String? languageEmoji;
final bool l1;
final L2SupportEnum l2Support;
LanguageModel({
required this.langCode,
required this.languageFlag,
required this.displayName,
required this.l1,
this.l2Support = L2SupportEnum.na,
this.languageEmoji,
});
factory LanguageModel.fromJson(json) {
@ -35,12 +25,7 @@ class LanguageModel {
return LanguageModel(
langCode: code,
languageFlag: json['language_flag'] ?? "",
displayName: _LanguageLocal.getDisplayName(
code != LanguageKeys.unknownLanguage ? code : json['language_name'],
),
l1: json["l1"] ?? !code.contains("es") && !code.contains("en"),
languageEmoji: json['language_emoji'],
displayName: json['language_name'],
l2Support: json['l2_support'] != null
? L2SupportEnum.na.fromStorageString(json['l2_support'])
: L2SupportEnum.na,
@ -50,9 +35,6 @@ class LanguageModel {
toJson() => {
'language_code': langCode,
'language_name': displayName,
'language_flag': languageFlag,
'l1': l1,
'language_emoji': languageEmoji,
'l2_support': l2Support.storageString,
};
@ -63,11 +45,6 @@ class LanguageModel {
if (codeOrName.isEmpty) return LanguageKeys.unknownLanguage;
if (codeOrName == LanguageKeys.unknownLanguage) return codeOrName;
if (_LanguageLocal.isoLangs.containsKey(codeOrName)) return codeOrName;
final String code = _LanguageLocal.langCodeFromName(codeOrName);
if (code != LanguageKeys.unknownLanguage) return code;
if (url == null) return LanguageKeys.unknownLanguage;
final List<String> split = url.split('/');
@ -77,672 +54,30 @@ class LanguageModel {
//PTODO - add flag for unknown
static LanguageModel get unknown => LanguageModel(
langCode: LanguageKeys.unknownLanguage,
languageFlag: "",
displayName: "Unknown",
l1: false,
);
static LanguageModel multiLingual([BuildContext? context]) => LanguageModel(
displayName: context != null
? L10n.of(context).multiLingualSpace
: "Multilingual Space",
l1: false,
langCode: LanguageKeys.multiLanguage,
languageFlag: 'assets/colors.png',
languageEmoji: "🌎",
);
String? getDisplayName(BuildContext context) {
switch (langCode) {
case 'ab':
return L10n.of(context).abDisplayName;
case 'aa':
return L10n.of(context).aaDisplayName;
case 'af':
return L10n.of(context).afDisplayName;
case 'ak':
return L10n.of(context).akDisplayName;
case 'sq':
return L10n.of(context).sqDisplayName;
case 'am':
return L10n.of(context).amDisplayName;
case 'ar':
return L10n.of(context).arDisplayName;
case 'an':
return L10n.of(context).anDisplayName;
case 'hy':
return L10n.of(context).hyDisplayName;
case 'as':
return L10n.of(context).asDisplayName;
case 'av':
return L10n.of(context).avDisplayName;
case 'ae':
return L10n.of(context).aeDisplayName;
case 'ay':
return L10n.of(context).ayDisplayName;
case 'az':
return L10n.of(context).azDisplayName;
case 'bm':
return L10n.of(context).bmDisplayName;
case 'ba':
return L10n.of(context).baDisplayName;
case 'eu':
return L10n.of(context).euDisplayName;
case 'be':
return L10n.of(context).beDisplayName;
case 'bn':
return L10n.of(context).bnDisplayName;
case 'bh':
return L10n.of(context).bhDisplayName;
case 'bi':
return L10n.of(context).biDisplayName;
case 'bs':
return L10n.of(context).bsDisplayName;
case 'br':
return L10n.of(context).brDisplayName;
case 'bg':
return L10n.of(context).bgDisplayName;
case 'my':
return L10n.of(context).myDisplayName;
case 'ca':
return L10n.of(context).caDisplayName;
case 'ch':
return L10n.of(context).chDisplayName;
case 'ce':
return L10n.of(context).ceDisplayName;
case 'ny':
return L10n.of(context).nyDisplayName;
case 'zh':
return L10n.of(context).zhDisplayName;
case 'cv':
return L10n.of(context).cvDisplayName;
case 'kw':
return L10n.of(context).kwDisplayName;
case 'co':
return L10n.of(context).coDisplayName;
case 'cr':
return L10n.of(context).crDisplayName;
case 'hr':
return L10n.of(context).hrDisplayName;
case 'cs':
return L10n.of(context).csDisplayName;
case 'da':
return L10n.of(context).daDisplayName;
case 'dv':
return L10n.of(context).dvDisplayName;
case 'nl':
return L10n.of(context).nlDisplayName;
case 'en':
return L10n.of(context).enDisplayName;
case 'eo':
return L10n.of(context).eoDisplayName;
case 'et':
return L10n.of(context).etDisplayName;
case 'ee':
return L10n.of(context).eeDisplayName;
case 'fo':
return L10n.of(context).foDisplayName;
case 'fj':
return L10n.of(context).fjDisplayName;
case 'fi':
return L10n.of(context).fiDisplayName;
case 'fr':
return L10n.of(context).frDisplayName;
case 'ff':
return L10n.of(context).ffDisplayName;
case 'gl':
return L10n.of(context).glDisplayName;
case 'ka':
return L10n.of(context).kaDisplayName;
case 'de':
return L10n.of(context).deDisplayName;
case 'el':
return L10n.of(context).elDisplayName;
case 'gn':
return L10n.of(context).gnDisplayName;
case 'gu':
return L10n.of(context).guDisplayName;
case 'ht':
return L10n.of(context).htDisplayName;
case 'ha':
return L10n.of(context).haDisplayName;
case 'he':
return L10n.of(context).heDisplayName;
case 'hz':
return L10n.of(context).hzDisplayName;
case 'hi':
return L10n.of(context).hiDisplayName;
case 'ho':
return L10n.of(context).hoDisplayName;
case 'hu':
return L10n.of(context).huDisplayName;
case 'ia':
return L10n.of(context).iaDisplayName;
case 'id':
return L10n.of(context).idDisplayName;
case 'ie':
return L10n.of(context).ieDisplayName;
case 'ga':
return L10n.of(context).gaDisplayName;
case 'ig':
return L10n.of(context).igDisplayName;
case 'ik':
return L10n.of(context).ikDisplayName;
case 'io':
return L10n.of(context).ioDisplayName;
case 'is':
return L10n.of(context).isDisplayName;
case 'it':
return L10n.of(context).itDisplayName;
case 'iu':
return L10n.of(context).iuDisplayName;
case 'ja':
return L10n.of(context).jaDisplayName;
case 'jv':
return L10n.of(context).jvDisplayName;
case 'kl':
return L10n.of(context).klDisplayName;
case 'kn':
return L10n.of(context).knDisplayName;
case 'kr':
return L10n.of(context).krDisplayName;
case 'ks':
return L10n.of(context).ksDisplayName;
case 'kk':
return L10n.of(context).kkDisplayName;
case 'km':
return L10n.of(context).kmDisplayName;
case 'ki':
return L10n.of(context).kiDisplayName;
case 'rw':
return L10n.of(context).rwDisplayName;
case 'ky':
return L10n.of(context).kyDisplayName;
case 'kv':
return L10n.of(context).kvDisplayName;
case 'kg':
return L10n.of(context).kgDisplayName;
case 'ko':
return L10n.of(context).koDisplayName;
case 'ku':
return L10n.of(context).kuDisplayName;
case 'kj':
return L10n.of(context).kjDisplayName;
case 'la':
return L10n.of(context).laDisplayName;
case 'lb':
return L10n.of(context).lbDisplayName;
case 'lg':
return L10n.of(context).lgDisplayName;
case 'li':
return L10n.of(context).liDisplayName;
case 'ln':
return L10n.of(context).lnDisplayName;
case 'lo':
return L10n.of(context).loDisplayName;
case 'lt':
return L10n.of(context).ltDisplayName;
case 'lu':
return L10n.of(context).luDisplayName;
case 'lv':
return L10n.of(context).lvDisplayName;
case 'gv':
return L10n.of(context).gvDisplayName;
case 'mk':
return L10n.of(context).mkDisplayName;
case 'mg':
return L10n.of(context).mgDisplayName;
case 'ms':
return L10n.of(context).msDisplayName;
case 'ml':
return L10n.of(context).mlDisplayName;
case 'mt':
return L10n.of(context).mtDisplayName;
case 'mi':
return L10n.of(context).miDisplayName;
case 'mr':
return L10n.of(context).mrDisplayName;
case 'mh':
return L10n.of(context).mhDisplayName;
case 'mn':
return L10n.of(context).mnDisplayName;
case 'na':
return L10n.of(context).naDisplayName;
case 'nv':
return L10n.of(context).nvDisplayName;
case 'nb':
return L10n.of(context).nbDisplayName;
case 'nd':
return L10n.of(context).ndDisplayName;
case 'ne':
return L10n.of(context).neDisplayName;
case 'ng':
return L10n.of(context).ngDisplayName;
case 'nn':
return L10n.of(context).nnDisplayName;
case 'no':
return L10n.of(context).noDisplayName;
case 'ii':
return L10n.of(context).iiDisplayName;
case 'nr':
return L10n.of(context).nrDisplayName;
case 'oc':
return L10n.of(context).ocDisplayName;
case 'oj':
return L10n.of(context).ojDisplayName;
case 'cu':
return L10n.of(context).cuDisplayName;
case 'om':
return L10n.of(context).omDisplayName;
case 'or':
return L10n.of(context).orDisplayName;
case 'os':
return L10n.of(context).osDisplayName;
case 'pa':
return L10n.of(context).paDisplayName;
case 'pi':
return L10n.of(context).piDisplayName;
case 'fa':
return L10n.of(context).faDisplayName;
case 'pl':
return L10n.of(context).plDisplayName;
case 'ps':
return L10n.of(context).psDisplayName;
case 'pt':
return L10n.of(context).ptDisplayName;
case 'qu':
return L10n.of(context).quDisplayName;
case 'rm':
return L10n.of(context).rmDisplayName;
case 'rn':
return L10n.of(context).rnDisplayName;
case 'ro':
return L10n.of(context).roDisplayName;
case 'ru':
return L10n.of(context).ruDisplayName;
case 'sa':
return L10n.of(context).saDisplayName;
case 'sc':
return L10n.of(context).scDisplayName;
case 'sd':
return L10n.of(context).sdDisplayName;
case 'se':
return L10n.of(context).seDisplayName;
case 'sm':
return L10n.of(context).smDisplayName;
case 'sg':
return L10n.of(context).sgDisplayName;
case 'sr':
return L10n.of(context).srDisplayName;
case 'gd':
return L10n.of(context).gdDisplayName;
case 'sn':
return L10n.of(context).snDisplayName;
case 'si':
return L10n.of(context).siDisplayName;
case 'sk':
return L10n.of(context).skDisplayName;
case 'sl':
return L10n.of(context).slDisplayName;
case 'so':
return L10n.of(context).soDisplayName;
case 'st':
return L10n.of(context).stDisplayName;
case 'es':
return L10n.of(context).esDisplayName;
case 'su':
return L10n.of(context).suDisplayName;
case 'sw':
return L10n.of(context).swDisplayName;
case 'ss':
return L10n.of(context).ssDisplayName;
case 'sv':
return L10n.of(context).svDisplayName;
case 'ta':
return L10n.of(context).taDisplayName;
case 'te':
return L10n.of(context).teDisplayName;
case 'tg':
return L10n.of(context).tgDisplayName;
case 'th':
return L10n.of(context).thDisplayName;
case 'ti':
return L10n.of(context).tiDisplayName;
case 'bo':
return L10n.of(context).boDisplayName;
case 'tk':
return L10n.of(context).tkDisplayName;
case 'tl':
return L10n.of(context).tlDisplayName;
case 'tn':
return L10n.of(context).tnDisplayName;
case 'to':
return L10n.of(context).toDisplayName;
case 'tr':
return L10n.of(context).trDisplayName;
case 'ts':
return L10n.of(context).tsDisplayName;
case 'tt':
return L10n.of(context).ttDisplayName;
case 'tw':
return L10n.of(context).twDisplayName;
case 'ty':
return L10n.of(context).tyDisplayName;
case 'ug':
return L10n.of(context).ugDisplayName;
case 'uk':
return L10n.of(context).ukDisplayName;
case 'ur':
return L10n.of(context).urDisplayName;
case 'uz':
return L10n.of(context).uzDisplayName;
case 've':
return L10n.of(context).veDisplayName;
case 'vi':
return L10n.of(context).viDisplayName;
case 'vo':
return L10n.of(context).voDisplayName;
case 'wa':
return L10n.of(context).waDisplayName;
case 'cy':
return L10n.of(context).cyDisplayName;
case 'wo':
return L10n.of(context).woDisplayName;
case 'fy':
return L10n.of(context).fyDisplayName;
case 'xh':
return L10n.of(context).xhDisplayName;
case 'yi':
return L10n.of(context).yiDisplayName;
case 'yo':
return L10n.of(context).yoDisplayName;
case 'za':
return L10n.of(context).zaDisplayName;
case 'unk':
return L10n.of(context).unkDisplayName;
case 'zu':
return L10n.of(context).zuDisplayName;
case 'haw':
return L10n.of(context).hawDisplayName;
case 'hmn':
return L10n.of(context).hmnDisplayName;
case 'multi':
return L10n.of(context).multiDisplayName;
case 'ceb':
return L10n.of(context).cebDisplayName;
case 'dz':
return L10n.of(context).dzDisplayName;
case 'iw':
return L10n.of(context).iwDisplayName;
case 'jw':
return L10n.of(context).jwDisplayName;
case 'mo':
return L10n.of(context).moDisplayName;
case 'sh':
return L10n.of(context).shDisplayName;
}
debugger(when: kDebugMode);
ErrorHandler.logError(
m: "No Display name found",
s: StackTrace.current,
data: {
"langCode": langCode,
},
);
return null;
return displayName;
}
}
class _LanguageLocal {
static const isoLangs = {
"ab": {"name": "Abkhaz", "nativeName": "аҧсуа"},
"aa": {"name": "Afar", "nativeName": "Afaraf"},
"af": {"name": "Afrikaans", "nativeName": "Afrikaans"},
"ak": {"name": "Akan", "nativeName": "Akan"},
"sq": {"name": "Albanian", "nativeName": "Shqip"},
"am": {"name": "Amharic", "nativeName": "አማርኛ"},
"ar": {"name": "Arabic", "nativeName": "العربية"},
"an": {"name": "Aragonese", "nativeName": "Aragonés"},
"hy": {"name": "Armenian", "nativeName": "Հայերեն"},
"as": {"name": "Assamese", "nativeName": "অসমীয়া"},
"av": {"name": "Avaric", "nativeName": "авар мацӀ, магӀарул мацӀ"},
"ae": {"name": "Avestan", "nativeName": "avesta"},
"ay": {"name": "Aymara", "nativeName": "aymar aru"},
"az": {"name": "Azerbaijani", "nativeName": "azərbaycan dili"},
"bm": {"name": "Bambara", "nativeName": "bamanankan"},
"ba": {"name": "Bashkir", "nativeName": "башҡорт теле"},
"eu": {"name": "Basque", "nativeName": "euskara, euskera"},
"be": {"name": "Belarusian", "nativeName": "Беларуская"},
"bn": {"name": "Bengali", "nativeName": "বাংলা"},
"bh": {"name": "Bihari", "nativeName": "भोजपुरी"},
"bi": {"name": "Bislama", "nativeName": "Bislama"},
"bs": {"name": "Bosnian", "nativeName": "bosanski jezik"},
"br": {"name": "Breton", "nativeName": "brezhoneg"},
"bg": {"name": "Bulgarian", "nativeName": "български език"},
"my": {"name": "Burmese", "nativeName": "ဗမာစာ"},
"ca": {"name": "Catalan, Valencian", "nativeName": "Català"},
"ch": {"name": "Chamorro", "nativeName": "Chamoru"},
"ce": {"name": "Chechen", "nativeName": "нохчийн мотт"},
"ny": {
"name": "Chichewa, Chewa, Nyanja",
"nativeName": "chiCheŵa, chinyanja",
},
"zh": {"name": "Chinese", "nativeName": "中文 (Zhōngwén), 汉语, 漢語"},
"cv": {"name": "Chuvash", "nativeName": "чӑваш чӗлхи"},
"kw": {"name": "Cornish", "nativeName": "Kernewek"},
"co": {"name": "Corsican", "nativeName": "corsu, lingua corsa"},
"cr": {"name": "Cree", "nativeName": "ᓀᐦᐃᔭᐍᐏᐣ"},
"hr": {"name": "Croatian", "nativeName": "hrvatski"},
"cs": {"name": "Czech", "nativeName": "česky, čeština"},
"da": {"name": "Danish", "nativeName": "dansk"},
"dv": {"name": "Divehi; Dhivehi; Maldivian;", "nativeName": "ދިވެހި"},
"nl": {"name": "Dutch", "nativeName": "Nederlands, Vlaams"},
"en": {"name": "English", "nativeName": "English"},
"eo": {"name": "Esperanto", "nativeName": "Esperanto"},
"et": {"name": "Estonian", "nativeName": "eesti, eesti keel"},
"ee": {"name": "Ewe", "nativeName": "Evegbe"},
"fo": {"name": "Faroese", "nativeName": "føroyskt"},
"fj": {"name": "Fijian", "nativeName": "vosa Vakaviti"},
"fi": {"name": "Finnish", "nativeName": "suomi, suomen kieli"},
"fr": {"name": "French", "nativeName": "français, langue française"},
"ff": {
"name": "Fula; Fulah; Pulaar; Pular",
"nativeName": "Fulfulde, Pulaar, Pular",
},
"gl": {"name": "Galician", "nativeName": "Galego"},
"ka": {"name": "Georgian", "nativeName": "ქართული"},
"de": {"name": "German", "nativeName": "Deutsch"},
"el": {"name": "Greek, Modern", "nativeName": "Ελληνικά"},
"gn": {"name": "Guaraní", "nativeName": "Avañeẽ"},
"gu": {"name": "Gujarati", "nativeName": "ગુજરાતી"},
"ht": {"name": "Haitian, Haitian Creole", "nativeName": "Kreyòl ayisyen"},
"ha": {"name": "Hausa", "nativeName": "Hausa, هَوُسَ"},
"he": {"name": "Hebrew (modern)", "nativeName": "עברית"},
"hz": {"name": "Herero", "nativeName": "Otjiherero"},
"hi": {"name": "Hindi", "nativeName": "हिन्दी, हिंदी"},
"ho": {"name": "Hiri Motu", "nativeName": "Hiri Motu"},
"hu": {"name": "Hungarian", "nativeName": "Magyar"},
"ia": {"name": "Interlingua", "nativeName": "Interlingua"},
"id": {"name": "Indonesian", "nativeName": "Bahasa Indonesia"},
"ie": {
"name": "Interlingue",
"nativeName": "Originally called Occidental; then Interlingue after WWII",
},
"ga": {"name": "Irish", "nativeName": "Gaeilge"},
"ig": {"name": "Igbo", "nativeName": "Asụsụ Igbo"},
"ik": {"name": "Inupiaq", "nativeName": "Iñupiaq, Iñupiatun"},
"io": {"name": "Ido", "nativeName": "Ido"},
"is": {"name": "Icelandic", "nativeName": "Íslenska"},
"it": {"name": "Italian", "nativeName": "Italiano"},
"iu": {"name": "Inuktitut", "nativeName": "ᐃᓄᒃᑎᑐᑦ"},
"ja": {"name": "Japanese", "nativeName": "日本語 (にほんご/にっぽんご)"},
"jv": {"name": "Javanese", "nativeName": "basa Jawa"},
"kl": {
"name": "Kalaallisut, Greenlandic",
"nativeName": "kalaallisut, kalaallit oqaasii",
},
"kn": {"name": "Kannada", "nativeName": "ಕನ್ನಡ"},
"kr": {"name": "Kanuri", "nativeName": "Kanuri"},
"ks": {"name": "Kashmiri", "nativeName": "कश्मीरी, كشميري"},
"kk": {"name": "Kazakh", "nativeName": "Қазақ тілі"},
"km": {"name": "Khmer", "nativeName": "ភាសាខ្មែរ"},
"ki": {"name": "Kikuyu, Gikuyu", "nativeName": "Gĩkũyũ"},
"rw": {"name": "Kinyarwanda", "nativeName": "Ikinyarwanda"},
"ky": {"name": "Kirghiz, Kyrgyz", "nativeName": "кыргыз тили"},
"kv": {"name": "Komi", "nativeName": "коми кыв"},
"kg": {"name": "Kongo", "nativeName": "KiKongo"},
"ko": {"name": "Korean", "nativeName": "한국어 (韓國語), 조선말 (朝鮮語)"},
"ku": {"name": "Kurdish", "nativeName": "Kurdî, كوردی"},
"kj": {"name": "Kwanyama, Kuanyama", "nativeName": "Kuanyama"},
"la": {"name": "Latin", "nativeName": "latine, lingua latina"},
"lb": {
"name": "Luxembourgish, Letzeburgesch",
"nativeName": "Lëtzebuergesch",
},
"lg": {"name": "Luganda", "nativeName": "Luganda"},
"li": {
"name": "Limburgish, Limburgan, Limburger",
"nativeName": "Limburgs",
},
"ln": {"name": "Lingala", "nativeName": "Lingála"},
"lo": {"name": "Lao", "nativeName": "ພາສາລາວ"},
"lt": {"name": "Lithuanian", "nativeName": "lietuvių kalba"},
"lu": {"name": "Luba-Katanga", "nativeName": ""},
"lv": {"name": "Latvian", "nativeName": "latviešu valoda"},
"gv": {"name": "Manx", "nativeName": "Gaelg, Gailck"},
"mk": {"name": "Macedonian", "nativeName": "македонски јазик"},
"mg": {"name": "Malagasy", "nativeName": "Malagasy fiteny"},
"ms": {"name": "Malay", "nativeName": "bahasa Melayu, بهاس ملايو"},
"ml": {"name": "Malayalam", "nativeName": "മലയാളം"},
"mt": {"name": "Maltese", "nativeName": "Malti"},
"mi": {"name": "Māori", "nativeName": "te reo Māori"},
"mr": {"name": "Marathi (Marāṭhī)", "nativeName": "मराठी"},
"mh": {"name": "Marshallese", "nativeName": "Kajin M̧ajeļ"},
"mn": {"name": "Mongolian", "nativeName": "монгол"},
"na": {"name": "Nauru", "nativeName": "Ekakairũ Naoero"},
"nv": {"name": "Navajo, Navaho", "nativeName": "Diné bizaad, Dinék'ehǰí"},
"nb": {"name": "Norwegian Bokmål", "nativeName": "Norsk bokmål"},
"nd": {"name": "North Ndebele", "nativeName": "isiNdebele"},
"ne": {"name": "Nepali", "nativeName": "नेपाली"},
"ng": {"name": "Ndonga", "nativeName": "Owambo"},
"nn": {"name": "Norwegian Nynorsk", "nativeName": "Norsk nynorsk"},
"no": {"name": "Norwegian", "nativeName": "Norsk"},
"ii": {"name": "Nuosu", "nativeName": "ꆈꌠ꒿ Nuosuhxop"},
"nr": {"name": "South Ndebele", "nativeName": "isiNdebele"},
"oc": {"name": "Occitan", "nativeName": "Occitan"},
"oj": {"name": "Ojibwe, Ojibwa", "nativeName": "ᐊᓂᔑᓈᐯᒧᐎᓐ"},
"cu": {
"name":
"Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic",
"nativeName": "ѩзыкъ словѣньскъ",
},
"om": {"name": "Oromo", "nativeName": "Afaan Oromoo"},
"or": {"name": "Oriya", "nativeName": "ଓଡ଼ିଆ"},
"os": {"name": "Ossetian, Ossetic", "nativeName": "ирон æвзаг"},
"pa": {"name": "Panjabi, Punjabi", "nativeName": "ਪੰਜਾਬੀ, پنجابی"},
"pi": {"name": "Pāli", "nativeName": "पाऴि"},
"fa": {"name": "Persian", "nativeName": "فارسی"},
"pl": {"name": "Polish", "nativeName": "polski"},
"ps": {"name": "Pashto, Pushto", "nativeName": "پښتو"},
"pt": {"name": "Portuguese", "nativeName": "Português"},
"qu": {"name": "Quechua", "nativeName": "Runa Simi, Kichwa"},
"rm": {"name": "Romansh", "nativeName": "rumantsch grischun"},
"rn": {"name": "Kirundi", "nativeName": "kiRundi"},
"ro": {"name": "Romanian, Moldavian, Moldovan", "nativeName": "română"},
"ru": {"name": "Russian", "nativeName": "русский язык"},
"sa": {"name": "Sanskrit (Saṁskṛta)", "nativeName": "संस्कृतम्"},
"sc": {"name": "Sardinian", "nativeName": "sardu"},
"sd": {"name": "Sindhi", "nativeName": "सिन्धी, سنڌي، سندھی"},
"se": {"name": "Northern Sami", "nativeName": "Davvisámegiella"},
"sm": {"name": "Samoan", "nativeName": "gagana faa Samoa"},
"sg": {"name": "Sango", "nativeName": "yângâ tî sängö"},
"sr": {"name": "Serbian", "nativeName": "српски језик"},
"gd": {"name": "Scottish Gaelic, Gaelic", "nativeName": "Gàidhlig"},
"sn": {"name": "Shona", "nativeName": "chiShona"},
"si": {"name": "Sinhala, Sinhalese", "nativeName": "සිංහල"},
"sk": {"name": "Slovak", "nativeName": "Slovenčina"},
"sl": {"name": "Slovene", "nativeName": "Slovenščina"},
"so": {"name": "Somali", "nativeName": "Soomaaliga, af Soomaali"},
"st": {"name": "Southern Sotho", "nativeName": "Sesotho"},
"es": {"name": "Spanish, Castilian", "nativeName": "Español, Castellano"},
"su": {"name": "Sundanese", "nativeName": "Basa Sunda"},
"sw": {"name": "Swahili", "nativeName": "Kiswahili"},
"ss": {"name": "Swati", "nativeName": "SiSwati"},
"sv": {"name": "Swedish", "nativeName": "svenska"},
"ta": {"name": "Tamil", "nativeName": "தமிழ்"},
"te": {"name": "Telugu", "nativeName": "తెలుగు"},
"tg": {"name": "Tajik", "nativeName": "тоҷикӣ, toğikī, تاجیکی"},
"th": {"name": "Thai", "nativeName": "ไทย"},
"ti": {"name": "Tigrinya", "nativeName": "ትግርኛ"},
"bo": {
"name": "Tibetan Standard, Tibetan, Central",
"nativeName": "བོད་ཡིག",
},
"tk": {"name": "Turkmen", "nativeName": "Türkmen, Түркмен"},
"tl": {"name": "Tagalog", "nativeName": "Wikang Tagalog, ᜏᜒᜃᜅ᜔ ᜆᜄᜎᜓᜄ᜔"},
"tn": {"name": "Tswana", "nativeName": "Setswana"},
"to": {"name": "Tonga (Tonga Islands)", "nativeName": "faka Tonga"},
"tr": {"name": "Turkish", "nativeName": "Türkçe"},
"ts": {"name": "Tsonga", "nativeName": "Xitsonga"},
"tt": {"name": "Tatar", "nativeName": "татарча, tatarça, تاتارچا"},
"tw": {"name": "Twi", "nativeName": "Twi"},
"ty": {"name": "Tahitian", "nativeName": "Reo Tahiti"},
"ug": {"name": "Uighur, Uyghur", "nativeName": "Uyƣurqə, ئۇيغۇرچە"},
"uk": {"name": "Ukrainian", "nativeName": "українська"},
"ur": {"name": "Urdu", "nativeName": "اردو"},
"uz": {"name": "Uzbek", "nativeName": "zbek, Ўзбек, أۇزبېك"},
"ve": {"name": "Venda", "nativeName": "Tshivenḓa"},
"vi": {"name": "Vietnamese", "nativeName": "Tiếng Việt"},
"vo": {"name": "Volapük", "nativeName": "Volapük"},
"wa": {"name": "Walloon", "nativeName": "Walon"},
"cy": {"name": "Welsh", "nativeName": "Cymraeg"},
"wo": {"name": "Wolof", "nativeName": "Wollof"},
"fy": {"name": "Western Frisian", "nativeName": "Frysk"},
"xh": {"name": "Xhosa", "nativeName": "isiXhosa"},
"yi": {"name": "Yiddish", "nativeName": "ייִדיש"},
"yo": {"name": "Yoruba", "nativeName": "Yorùbá"},
"za": {"name": "Zhuang, Chuang", "nativeName": "Saw cueŋƅ, Saw cuengh"},
"unk": {"name": "Unknown", "nativeName": "Saw cueŋƅ, Saw cuengh"},
"zu": {"name": "Zulu", "nativeName": "Zulu"},
"haw": {"name": "Hawaiian", "nativeName": "Hawaiian"},
"hmn": {"name": "Hmong", "nativeName": "Hmong"},
'multi': {"name": "Multi", "nativeName": "Multi"},
"ceb": {"name": "Cebuano", "nativeName": "Cebuano"},
"dz": {"name": "Dzongkha", "nativeName": "Dzongkha"},
"iw": {"name": "Hebrew", "nativeName": "Hebrew"},
"jw": {"name": "Javanese", "nativeName": "Javanese"},
"mo": {"name": "Moldavian", "nativeName": "Moldavian"},
"sh": {"name": "Serbo-Croatian", "nativeName": "Serbo-Croatian"},
};
String get langCodeShort => langCode.split('-').first;
static String getDisplayName(String key, [native = false]) {
final Map<String, String>? item = isoLangs[key];
if (item == null) {
// debugger(when: kDebugMode);
// ErrorHandler.logError(m: "Bad language key $key", s: StackTrace.current);
@override
bool operator ==(Object other) {
if (other is LanguageModel) {
return langCode == other.langCode;
}
if (item == null ||
(native && !item.containsKey("nativeName") ||
(!native && !item.containsKey("name")))) {
return key;
}
return (native ? item["nativeName"]! : item["name"]!).split(",")[0];
return false;
}
static String langCodeFromName(String? name) {
if (name == null) return LanguageKeys.unknownLanguage;
if (isoLangs.containsKey(name)) return name;
final String searchName = name.toLowerCase().split(" ")[0];
for (final entry in isoLangs.entries) {
if (entry.value.containsKey("name") &&
entry.value["name"]!.toLowerCase().contains(searchName)) {
return entry.key;
}
}
// debugger(when: kDebugMode);
// ErrorHandler.logError(m: "Bad language name $name", s: StackTrace.current);
return LanguageKeys.unknownLanguage;
}
@override
int get hashCode => langCode.hashCode;
}

@ -131,8 +131,8 @@ class SettingsLearningController extends State<SettingsLearning> {
LanguageModel? get selectedTargetLanguage {
return userL2 ??
((selectedSourceLanguage?.langCode != 'en')
? PangeaLanguage.byLangCode('en')!
: PangeaLanguage.byLangCode('es')!);
? PangeaLanguage.byLangCode('en')
: PangeaLanguage.byLangCode('es'));
}
LanguageModel? get userL1 => _profile.userSettings.sourceLanguage != null

@ -103,104 +103,89 @@ class SettingsLearningView extends StatelessWidget {
initialLevel: controller.cefrLevel,
onChanged: controller.setCefrLevel,
),
const Divider(height: 1),
Column(
children: [
ListTile(
title: Text(
L10n.of(context)
.toggleToolSettingsDescription,
),
),
for (final toolSetting in ToolSetting.values
.where((tool) => tool.isAvailableSetting))
Column(
children: [
ProfileSettingsSwitchListTile.adaptive(
defaultValue: controller
.getToolSetting(toolSetting),
title: toolSetting.toolName(context),
subtitle: toolSetting ==
ToolSetting.enableTTS &&
!controller.tts
.isLanguageFullySupported
? null
: toolSetting
.toolDescription(context),
onChange: (bool value) =>
controller.updateToolSetting(
toolSetting,
value,
),
enabled: toolSetting ==
ToolSetting.enableTTS
for (final toolSetting in ToolSetting.values
.where((tool) => tool.isAvailableSetting))
Column(
children: [
ProfileSettingsSwitchListTile.adaptive(
defaultValue: controller
.getToolSetting(toolSetting),
title: toolSetting.toolName(context),
subtitle: toolSetting ==
ToolSetting.enableTTS &&
!controller
.tts.isLanguageFullySupported
? null
: toolSetting
.toolDescription(context),
onChange: (bool value) =>
controller.updateToolSetting(
toolSetting,
value,
),
enabled:
toolSetting == ToolSetting.enableTTS
? controller
.tts.isLanguageFullySupported
: true,
),
if (toolSetting == ToolSetting.enableTTS &&
!controller
.tts.isLanguageFullySupported)
ListTile(
trailing: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 16.0,
),
child: Icon(Icons.info_outlined),
),
if (toolSetting ==
ToolSetting.enableTTS &&
!controller
.tts.isLanguageFullySupported)
ListTile(
trailing: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 16.0,
),
child: Icon(Icons.info_outlined),
),
subtitle: RichText(
text: TextSpan(
text: L10n.of(context)
.couldNotFindTTS,
style:
DefaultTextStyle.of(context)
.style,
children: [
if (PlatformInfos.isWindows ||
PlatformInfos.isAndroid)
TextSpan(
text: L10n.of(context)
.ttsInstructionsHyperlink,
style: const TextStyle(
color: Colors.blue,
fontWeight:
FontWeight.bold,
decoration:
TextDecoration
.underline,
),
recognizer:
TapGestureRecognizer()
..onTap = () {
launchUrlString(
PlatformInfos
.isWindows
? AppConfig
.windowsTTSDownloadInstructions
: AppConfig
.androidTTSDownloadInstructions,
);
},
),
],
),
),
subtitle: RichText(
text: TextSpan(
text: L10n.of(context)
.couldNotFindTTS,
style: DefaultTextStyle.of(context)
.style,
children: [
if (PlatformInfos.isWindows ||
PlatformInfos.isAndroid)
TextSpan(
text: L10n.of(context)
.ttsInstructionsHyperlink,
style: const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
decoration: TextDecoration
.underline,
),
recognizer:
TapGestureRecognizer()
..onTap = () {
launchUrlString(
PlatformInfos
.isWindows
? AppConfig
.windowsTTSDownloadInstructions
: AppConfig
.androidTTSDownloadInstructions,
);
},
),
],
),
],
),
SwitchListTile.adaptive(
value: controller.publicProfile,
onChanged: controller.setPublicProfile,
title: Text(
L10n.of(context).publicProfileTitle,
),
subtitle: Text(
L10n.of(context).publicProfileDesc,
),
activeColor: AppConfig.activeToggleColor,
),
],
),
),
],
),
SwitchListTile.adaptive(
value: controller.publicProfile,
onChanged: controller.setPublicProfile,
title: Text(
L10n.of(context).publicProfileTitle,
),
subtitle: Text(
L10n.of(context).publicProfileDesc,
),
activeColor: AppConfig.activeToggleColor,
),
],
),
@ -213,7 +198,7 @@ class SettingsLearningView extends StatelessWidget {
width: double.infinity,
child: ElevatedButton(
onPressed: controller.submit,
child: Text(L10n.of(context).submit),
child: Text(L10n.of(context).saveChanges),
),
),
),

@ -21,25 +21,25 @@ class PangeaLanguage {
List<LanguageModel> get targetOptions =>
_langList.where((element) => element.l2).toList();
List<LanguageModel> get baseOptions =>
_langList.where((element) => element.l1).toList();
List<LanguageModel> get baseOptions => _langList.toList();
static Future<void> initialize() async {
try {
_langList = await _getCachedFlags();
_langList = await _getCachedLanguages();
if (await _shouldFetch ||
_langList.isEmpty ||
_langList.every((lang) => !lang.l2)) {
_langList = await LanguageRepo.fetchLanguages();
await _saveFlags(_langList);
await _saveLanguages(_langList);
await saveLastFetchDate();
}
_langList.removeWhere(
(element) =>
LanguageModel.codeFromNameOrCode(element.langCode) ==
LanguageKeys.unknownLanguage,
(element) => element.langCode == LanguageKeys.unknownLanguage,
);
// remove any duplicates
_langList = _langList.toSet().toList();
_langList.sort((a, b) => a.displayName.compareTo(b.displayName));
_langList.insert(0, LanguageModel.multiLingual());
} catch (err, stack) {
@ -77,27 +77,27 @@ class PangeaLanguage {
return (now - lastFetched) >= fetchIntervalInMilliseconds ? true : false;
}
static Future<void> _saveFlags(List<LanguageModel> langFlags) async {
final Map flagMap = {
PrefKey.flags: langFlags.map((e) => e.toJson()).toList(),
static Future<void> _saveLanguages(List<LanguageModel> languages) async {
final Map languagesMaps = {
PrefKey.languagesKey: languages.map((e) => e.toJson()).toList(),
};
await MyShared.saveJson(PrefKey.flags, flagMap);
await MyShared.saveJson(PrefKey.languagesKey, languagesMaps);
}
static Future<List<LanguageModel>> _getCachedFlags() async {
final Map<dynamic, dynamic>? flagsMap =
await MyShared.readJson(PrefKey.flags);
if (flagsMap == null) {
static Future<List<LanguageModel>> _getCachedLanguages() async {
final Map<dynamic, dynamic>? languagesMap =
await MyShared.readJson(PrefKey.languagesKey);
if (languagesMap == null) {
return [];
}
final List<LanguageModel> flags = [];
final List mapList = flagsMap[PrefKey.flags] as List;
final List<LanguageModel> languages = [];
final List mapList = languagesMap[PrefKey.languagesKey] as List;
for (final element in mapList) {
flags.add(LanguageModel.fromJson(element));
languages.add(LanguageModel.fromJson(element));
}
return flags;
return languages;
}
static LanguageModel? byLangCode(String langCode) {

@ -48,7 +48,11 @@ class PLanguageDropdownState extends State<PLanguageDropdown> {
Widget build(BuildContext context) {
final List<LanguageModel> sortedLanguages = widget.languages;
final String systemLang = Localizations.localeOf(context).languageCode;
final List<String> languagePriority = [systemLang, 'en', 'es'];
// if there is no initial language, the system language should be the first in the list
// otherwise, display in alphabetical order
final List<String> languagePriority =
widget.initialLanguage == null ? [systemLang] : [];
int sortLanguages(LanguageModel a, LanguageModel b) {
final String aLang = a.langCode;

@ -9,6 +9,7 @@ import 'package:http/http.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/network/urls.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
import 'package:fluffychat/pangea/morphs/default_morph_mapping.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -76,34 +77,36 @@ class MorphsRepo {
/// if the morphs are not yet fetched. we'll see if this works well
/// if not, we can make it async and update uses of this function
/// to be async as well
static Future<MorphFeaturesAndTags> get([String? languageCode]) async {
languageCode ??=
MatrixState.pangeaController.languageController.userL2?.langCode;
static Future<MorphFeaturesAndTags> get([LanguageModel? language]) async {
language ??= MatrixState.pangeaController.languageController.userL2;
if (languageCode == null) {
if (language == null) {
return defaultMorphMapping;
}
// does not differ based on locale
final langCodeShort = language.langCodeShort;
// check if we have a cached morphs for this language code
final cachedJson = _morphsStorage.read(languageCode);
final cachedJson = _morphsStorage.read(langCodeShort);
if (cachedJson != null) {
return MorphsRepo.fromJson(cachedJson);
}
// check if we have a cached call for this language code
final _APICallCacheItem? cachedCall = shortTermCache[languageCode];
final _APICallCacheItem? cachedCall = shortTermCache[langCodeShort];
if (cachedCall != null) {
if (DateTime.now().difference(cachedCall.time).inMinutes <
_cacheDurationMinutes) {
return cachedCall.future;
} else {
shortTermCache.remove(languageCode);
shortTermCache.remove(langCodeShort);
}
}
// fetch the morphs but don't wait for it
final future = _fetch(languageCode);
shortTermCache[languageCode] = _APICallCacheItem(DateTime.now(), future);
final future = _fetch(langCodeShort);
shortTermCache[langCodeShort] = _APICallCacheItem(DateTime.now(), future);
return future;
}
}

@ -92,13 +92,15 @@ class STTToken {
int get length => token.text.length;
Color color(BuildContext context) {
if (confidence == null) {
return Theme.of(context).colorScheme.onSurface;
}
if (confidence! > thresholdForGreen) {
return AppConfig.success;
}
return AppConfig.warning;
// turning off the color coding for now
// whisper doesn't include word-level confidence
// if (confidence == null) {
return Theme.of(context).colorScheme.onSurface;
// }
// if (confidence! > thresholdForGreen) {
// return AppConfig.success;
// }
// return AppConfig.warning;
}
factory STTToken.fromJson(Map<String, dynamic> json) {

@ -1,7 +1,6 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -112,20 +111,20 @@ class MessageSpeechToTextCardState extends State<MessageSpeechToTextCard> {
setColor: false,
),
// gesturRecognizer that sets selectedToken on click
recognizer: TapGestureRecognizer()
..onTap = () {
debugPrint('Token tapped');
debugPrint(token.toJson().toString());
if (mounted) {
setState(() {
if (selectedToken == token) {
selectedToken = null;
} else {
selectedToken = token;
}
});
}
},
// recognizer: TapGestureRecognizer()
// ..onTap = () {
// debugPrint('Token tapped');
// debugPrint(token.toJson().toString());
// if (mounted) {
// setState(() {
// if (selectedToken == token) {
// selectedToken = null;
// } else {
// selectedToken = token;
// }
// });
// }
// },
),
);

@ -80,12 +80,12 @@ class UserController extends BaseController {
Profile Function(Profile) update, {
waitForDataInSync = false,
}) async {
final prevTargetLang = profile.userSettings.targetLanguage;
final prevTargetLang = _pangeaController.languageController.userL2;
final Profile updatedProfile = update(profile);
await updatedProfile.saveProfileData(waitForDataInSync: waitForDataInSync);
if (prevTargetLang != updatedProfile.userSettings.targetLanguage) {
if (prevTargetLang != _pangeaController.languageController.userL2) {
setState({'prev_target_lang': prevTargetLang});
}
}

Loading…
Cancel
Save