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 && activel1.langCode != LanguageKeys.unknownLanguage &&
activel2.langCode != LanguageKeys.unknownLanguage activel2.langCode != LanguageKeys.unknownLanguage
? L10n.of(context).writeAMessageFlag( ? L10n.of(context).writeAMessageFlag(
activel1.languageEmoji ?? activel1.getDisplayName(context) ?? activel1.langCode,
activel1.getDisplayName(context) ?? activel2.getDisplayName(context) ?? activel2.langCode,
activel1.langCode,
activel2.languageEmoji ??
activel2.getDisplayName(context) ??
activel2.langCode,
) )
: L10n.of(context).writeAMessage; : 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/constants/model_keys.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.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'; import 'package:fluffychat/widgets/matrix.dart';
extension AnalyticsClientExtension on Client { extension AnalyticsClientExtension on Client {
/// Get the logged in user's analytics room matching /// Get the logged in user's analytics room matching
/// a given langCode. If not present, create it. /// a given langCode. If not present, create it.
Future<Room?> getMyAnalyticsRoom(String langCode) async { Future<Room?> getMyAnalyticsRoom(LanguageModel lang) async {
final Room? analyticsRoom = analyticsRoomLocal(langCode); final Room? analyticsRoom = analyticsRoomLocal(lang);
if (analyticsRoom != null) return analyticsRoom; if (analyticsRoom != null) return analyticsRoom;
return _makeAnalyticsRoom(langCode); return _makeAnalyticsRoom(lang);
} }
/// Get local analytics room for a given langCode and /// Get local analytics room for a given langCode and
/// optional userId (if not specified, uses current user). /// optional userId (if not specified, uses current user).
/// If user is invited to the room, joins the room. /// If user is invited to the room, joins the room.
Room? analyticsRoomLocal([String? langCode, String? userIdParam]) { Room? analyticsRoomLocal([LanguageModel? lang, String? userIdParam]) {
langCode ??= lang ??= MatrixState.pangeaController.languageController.userL2;
MatrixState.pangeaController.languageController.userL2?.langCode;
if (langCode == null) { if (lang == null) {
debugger(when: kDebugMode); debugger(when: kDebugMode);
return null; return null;
} }
@ -37,7 +37,7 @@ extension AnalyticsClientExtension on Client {
final Room? analyticsRoom = rooms.firstWhereOrNull((e) { final Room? analyticsRoom = rooms.firstWhereOrNull((e) {
return e.isAnalyticsRoom && return e.isAnalyticsRoom &&
e.isAnalyticsRoomOfUser(userIdParam ?? userID!) && e.isAnalyticsRoomOfUser(userIdParam ?? userID!) &&
e.isMadeForLang(langCode!); e.isMadeForLang(lang!.langCodeShort);
}); });
if (analyticsRoom != null && if (analyticsRoom != null &&
analyticsRoom.membership == Membership.invite) { analyticsRoom.membership == Membership.invite) {
@ -47,7 +47,7 @@ extension AnalyticsClientExtension on Client {
e: error, e: error,
s: stackTrace, s: stackTrace,
data: { data: {
"langCode": langCode, "langCode": lang!.langCodeShort,
"userIdParam": userIdParam, "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. /// If the room does not appear immediately after creation, this method waits for it to appear in sync.
/// Returns the created [Room] object. /// Returns the created [Room] object.
Future<Room?> _makeAnalyticsRoom(String langCode) async { Future<Room?> _makeAnalyticsRoom(LanguageModel lang) async {
if (userID == null || userID == BotName.byEnvironment) { if (userID == null || userID == BotName.byEnvironment) {
return null; return null;
} }
@ -70,9 +70,9 @@ extension AnalyticsClientExtension on Client {
final String roomID = await createRoom( final String roomID = await createRoom(
creationContent: { creationContent: {
'type': PangeaRoomTypes.analytics, '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.", topic: "This room stores learning analytics for $userID.",
preset: CreateRoomPreset.publicChat, preset: CreateRoomPreset.publicChat,
visibility: Visibility.private, 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/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.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 /// A minimized version of AnalyticsController that get the logged in user's analytics
class GetAnalyticsController extends BaseController { 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; Client get _client => _pangeaController.matrixState.client;
// the minimum XP required for a given level // the minimum XP required for a given level
@ -282,13 +283,13 @@ class GetAnalyticsController extends BaseController {
Future<DateTime?> myAnalyticsLastUpdated() async { Future<DateTime?> myAnalyticsLastUpdated() async {
// this function gets called soon after login, so first // 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 // 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) { if (_pangeaController.matrixState.client.prevBatch == null) {
await _pangeaController.matrixState.client.onSync.stream.first; 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; if (analyticsRoom == null) return null;
final DateTime? lastUpdated = await analyticsRoom.analyticsLastUpdated( final DateTime? lastUpdated = await analyticsRoom.analyticsLastUpdated(
_client.userID!, _client.userID!,
@ -298,8 +299,8 @@ class GetAnalyticsController extends BaseController {
/// Get all the construct analytics events for the logged in user /// Get all the construct analytics events for the logged in user
Future<List<ConstructAnalyticsEvent>> _allMyConstructs() async { Future<List<ConstructAnalyticsEvent>> _allMyConstructs() async {
if (_l2Code == null) return []; if (_l2 == null) return [];
final Room? analyticsRoom = _client.analyticsRoomLocal(_l2Code!); final Room? analyticsRoom = _client.analyticsRoomLocal(_l2!);
if (analyticsRoom == null) return []; if (analyticsRoom == null) return [];
return await analyticsRoom.getAnalyticsEvents(userId: _client.userID!) ?? return await analyticsRoom.getAnalyticsEvents(userId: _client.userID!) ??
[]; [];
@ -310,7 +311,7 @@ class GetAnalyticsController extends BaseController {
ConstructTypeEnum? constructType, ConstructTypeEnum? constructType,
}) { }) {
final index = _cache.indexWhere( final index = _cache.indexWhere(
(e) => e.type == constructType && e.langCode == _l2Code, (e) => e.type == constructType && e.langCode == _l2?.langCodeShort,
); );
if (index > -1) { if (index > -1) {
@ -330,11 +331,11 @@ class GetAnalyticsController extends BaseController {
required List<OneConstructUse> uses, required List<OneConstructUse> uses,
ConstructTypeEnum? constructType, ConstructTypeEnum? constructType,
}) { }) {
if (_l2Code == null) return; if (_l2 == null) return;
final entry = AnalyticsCacheEntry( final entry = AnalyticsCacheEntry(
type: constructType, type: constructType,
uses: List.from(uses), uses: List.from(uses),
langCode: _l2Code!, langCode: _l2!.langCodeShort,
); );
_cache.add(entry); _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/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
enum AnalyticsUpdateType { server, local } enum AnalyticsUpdateType { server, local }
@ -30,8 +31,6 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
Client get _client => _pangeaController.matrixState.client; 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 /// the last time that matrix analytics events were updated for the user's current l2
DateTime? lastUpdated; DateTime? lastUpdated;
@ -62,8 +61,9 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
// Listen for changes to the user's language settings // Listen for changes to the user's language settings
_languageStream ??= _languageStream ??=
_pangeaController.userController.stateStream.listen((update) { _pangeaController.userController.stateStream.listen((update) {
if (update is Map<String, dynamic>) { if (update is Map<String, dynamic> &&
final previousL2 = update['prev_target_lang']; update['prev_target_lang'] is LanguageModel) {
final LanguageModel previousL2 = update['prev_target_lang'];
_onUpdateLanguages(previousL2); _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( await sendLocalAnalyticsToAnalyticsRoom(
l2Override: previousL2, l2Override: previousL2,
); );
@ -361,7 +361,7 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
/// since the last update and notifies the [analyticsUpdateStream]. /// since the last update and notifies the [analyticsUpdateStream].
Future<void> sendLocalAnalyticsToAnalyticsRoom({ Future<void> sendLocalAnalyticsToAnalyticsRoom({
onLogout = false, onLogout = false,
String? l2Override, LanguageModel? l2Override,
}) async { }) async {
if (_pangeaController.matrixState.client.userID == null) return; if (_pangeaController.matrixState.client.userID == null) return;
if (_pangeaController.getAnalytics.messagesSinceUpdate.isEmpty) 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. /// 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. /// 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 // if there's no cached construct data, there's nothing to send
final cachedConstructs = _pangeaController.getAnalytics.messagesSinceUpdate; final cachedConstructs = _pangeaController.getAnalytics.messagesSinceUpdate;
final bool onlyDraft = cachedConstructs.length == 1 && final bool onlyDraft = cachedConstructs.length == 1 &&
@ -408,11 +408,11 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
if (cachedConstructs.isEmpty || onlyDraft) return; if (cachedConstructs.isEmpty || onlyDraft) return;
// if missing important info, don't send analytics. Could happen if user just signed up. // if missing important info, don't send analytics. Could happen if user just signed up.
final l2Code = l2Override ?? _userL2; final l2 = l2Override ?? _pangeaController.languageController.userL2;
if (l2Code == null || _client.userID == null) return; if (l2 == null || _client.userID == null) return;
// analytics room for the user and current target language // 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 // and send cached analytics data to the room
await analyticsRoom?.sendConstructsEvent( await analyticsRoom?.sendConstructsEvent(

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

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

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

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

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

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

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

@ -1,29 +1,19 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.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/constants/language_constants.dart';
import 'package:fluffychat/pangea/learning_settings/enums/l2_support_enum.dart'; import 'package:fluffychat/pangea/learning_settings/enums/l2_support_enum.dart';
import '../../common/utils/error_handler.dart';
class LanguageModel { class LanguageModel {
final String langCode; final String langCode;
final String languageFlag;
final String displayName; final String displayName;
final String? languageEmoji;
final bool l1;
final L2SupportEnum l2Support; final L2SupportEnum l2Support;
LanguageModel({ LanguageModel({
required this.langCode, required this.langCode,
required this.languageFlag,
required this.displayName, required this.displayName,
required this.l1,
this.l2Support = L2SupportEnum.na, this.l2Support = L2SupportEnum.na,
this.languageEmoji,
}); });
factory LanguageModel.fromJson(json) { factory LanguageModel.fromJson(json) {
@ -35,12 +25,7 @@ class LanguageModel {
return LanguageModel( return LanguageModel(
langCode: code, langCode: code,
languageFlag: json['language_flag'] ?? "", displayName: json['language_name'],
displayName: _LanguageLocal.getDisplayName(
code != LanguageKeys.unknownLanguage ? code : json['language_name'],
),
l1: json["l1"] ?? !code.contains("es") && !code.contains("en"),
languageEmoji: json['language_emoji'],
l2Support: json['l2_support'] != null l2Support: json['l2_support'] != null
? L2SupportEnum.na.fromStorageString(json['l2_support']) ? L2SupportEnum.na.fromStorageString(json['l2_support'])
: L2SupportEnum.na, : L2SupportEnum.na,
@ -50,9 +35,6 @@ class LanguageModel {
toJson() => { toJson() => {
'language_code': langCode, 'language_code': langCode,
'language_name': displayName, 'language_name': displayName,
'language_flag': languageFlag,
'l1': l1,
'language_emoji': languageEmoji,
'l2_support': l2Support.storageString, 'l2_support': l2Support.storageString,
}; };
@ -63,11 +45,6 @@ class LanguageModel {
if (codeOrName.isEmpty) return LanguageKeys.unknownLanguage; if (codeOrName.isEmpty) return LanguageKeys.unknownLanguage;
if (codeOrName == LanguageKeys.unknownLanguage) return codeOrName; 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; if (url == null) return LanguageKeys.unknownLanguage;
final List<String> split = url.split('/'); final List<String> split = url.split('/');
@ -77,672 +54,30 @@ class LanguageModel {
//PTODO - add flag for unknown //PTODO - add flag for unknown
static LanguageModel get unknown => LanguageModel( static LanguageModel get unknown => LanguageModel(
langCode: LanguageKeys.unknownLanguage, langCode: LanguageKeys.unknownLanguage,
languageFlag: "",
displayName: "Unknown", displayName: "Unknown",
l1: false,
); );
static LanguageModel multiLingual([BuildContext? context]) => LanguageModel( static LanguageModel multiLingual([BuildContext? context]) => LanguageModel(
displayName: context != null displayName: context != null
? L10n.of(context).multiLingualSpace ? L10n.of(context).multiLingualSpace
: "Multilingual Space", : "Multilingual Space",
l1: false,
langCode: LanguageKeys.multiLanguage, langCode: LanguageKeys.multiLanguage,
languageFlag: 'assets/colors.png',
languageEmoji: "🌎",
); );
String? getDisplayName(BuildContext context) { String? getDisplayName(BuildContext context) {
switch (langCode) { return displayName;
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;
} }
}
class _LanguageLocal { String get langCodeShort => langCode.split('-').first;
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"},
};
static String getDisplayName(String key, [native = false]) { @override
final Map<String, String>? item = isoLangs[key]; bool operator ==(Object other) {
if (item == null) { if (other is LanguageModel) {
// debugger(when: kDebugMode); return langCode == other.langCode;
// ErrorHandler.logError(m: "Bad language key $key", s: StackTrace.current);
} }
if (item == null || return false;
(native && !item.containsKey("nativeName") ||
(!native && !item.containsKey("name")))) {
return key;
}
return (native ? item["nativeName"]! : item["name"]!).split(",")[0];
} }
static String langCodeFromName(String? name) { @override
if (name == null) return LanguageKeys.unknownLanguage; int get hashCode => langCode.hashCode;
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;
}
} }

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

@ -103,104 +103,89 @@ class SettingsLearningView extends StatelessWidget {
initialLevel: controller.cefrLevel, initialLevel: controller.cefrLevel,
onChanged: controller.setCefrLevel, onChanged: controller.setCefrLevel,
), ),
const Divider(height: 1), for (final toolSetting in ToolSetting.values
Column( .where((tool) => tool.isAvailableSetting))
children: [ Column(
ListTile( children: [
title: Text( ProfileSettingsSwitchListTile.adaptive(
L10n.of(context) defaultValue: controller
.toggleToolSettingsDescription, .getToolSetting(toolSetting),
), title: toolSetting.toolName(context),
), subtitle: toolSetting ==
for (final toolSetting in ToolSetting.values ToolSetting.enableTTS &&
.where((tool) => tool.isAvailableSetting)) !controller
Column( .tts.isLanguageFullySupported
children: [ ? null
ProfileSettingsSwitchListTile.adaptive( : toolSetting
defaultValue: controller .toolDescription(context),
.getToolSetting(toolSetting), onChange: (bool value) =>
title: toolSetting.toolName(context), controller.updateToolSetting(
subtitle: toolSetting == toolSetting,
ToolSetting.enableTTS && value,
!controller.tts ),
.isLanguageFullySupported enabled:
? null toolSetting == ToolSetting.enableTTS
: toolSetting
.toolDescription(context),
onChange: (bool value) =>
controller.updateToolSetting(
toolSetting,
value,
),
enabled: toolSetting ==
ToolSetting.enableTTS
? controller ? controller
.tts.isLanguageFullySupported .tts.isLanguageFullySupported
: true, : 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 == subtitle: RichText(
ToolSetting.enableTTS && text: TextSpan(
!controller text: L10n.of(context)
.tts.isLanguageFullySupported) .couldNotFindTTS,
ListTile( style: DefaultTextStyle.of(context)
trailing: const Padding( .style,
padding: EdgeInsets.symmetric( children: [
horizontal: 16.0, if (PlatformInfos.isWindows ||
), PlatformInfos.isAndroid)
child: Icon(Icons.info_outlined), TextSpan(
), text: L10n.of(context)
subtitle: RichText( .ttsInstructionsHyperlink,
text: TextSpan( style: const TextStyle(
text: L10n.of(context) color: Colors.blue,
.couldNotFindTTS, fontWeight: FontWeight.bold,
style: decoration: TextDecoration
DefaultTextStyle.of(context) .underline,
.style, ),
children: [ recognizer:
if (PlatformInfos.isWindows || TapGestureRecognizer()
PlatformInfos.isAndroid) ..onTap = () {
TextSpan( launchUrlString(
text: L10n.of(context) PlatformInfos
.ttsInstructionsHyperlink, .isWindows
style: const TextStyle( ? AppConfig
color: Colors.blue, .windowsTTSDownloadInstructions
fontWeight: : AppConfig
FontWeight.bold, .androidTTSDownloadInstructions,
decoration: );
TextDecoration },
.underline, ),
), ],
recognizer:
TapGestureRecognizer()
..onTap = () {
launchUrlString(
PlatformInfos
.isWindows
? AppConfig
.windowsTTSDownloadInstructions
: AppConfig
.androidTTSDownloadInstructions,
);
},
),
],
),
),
), ),
], ),
), ),
SwitchListTile.adaptive( ],
value: controller.publicProfile, ),
onChanged: controller.setPublicProfile, SwitchListTile.adaptive(
title: Text( value: controller.publicProfile,
L10n.of(context).publicProfileTitle, onChanged: controller.setPublicProfile,
), title: Text(
subtitle: Text( L10n.of(context).publicProfileTitle,
L10n.of(context).publicProfileDesc, ),
), subtitle: Text(
activeColor: AppConfig.activeToggleColor, L10n.of(context).publicProfileDesc,
), ),
], activeColor: AppConfig.activeToggleColor,
), ),
], ],
), ),
@ -213,7 +198,7 @@ class SettingsLearningView extends StatelessWidget {
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
onPressed: controller.submit, 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 => List<LanguageModel> get targetOptions =>
_langList.where((element) => element.l2).toList(); _langList.where((element) => element.l2).toList();
List<LanguageModel> get baseOptions => List<LanguageModel> get baseOptions => _langList.toList();
_langList.where((element) => element.l1).toList();
static Future<void> initialize() async { static Future<void> initialize() async {
try { try {
_langList = await _getCachedFlags(); _langList = await _getCachedLanguages();
if (await _shouldFetch || if (await _shouldFetch ||
_langList.isEmpty || _langList.isEmpty ||
_langList.every((lang) => !lang.l2)) { _langList.every((lang) => !lang.l2)) {
_langList = await LanguageRepo.fetchLanguages(); _langList = await LanguageRepo.fetchLanguages();
await _saveFlags(_langList); await _saveLanguages(_langList);
await saveLastFetchDate(); await saveLastFetchDate();
} }
_langList.removeWhere( _langList.removeWhere(
(element) => (element) => element.langCode == LanguageKeys.unknownLanguage,
LanguageModel.codeFromNameOrCode(element.langCode) ==
LanguageKeys.unknownLanguage,
); );
// remove any duplicates
_langList = _langList.toSet().toList();
_langList.sort((a, b) => a.displayName.compareTo(b.displayName)); _langList.sort((a, b) => a.displayName.compareTo(b.displayName));
_langList.insert(0, LanguageModel.multiLingual()); _langList.insert(0, LanguageModel.multiLingual());
} catch (err, stack) { } catch (err, stack) {
@ -77,27 +77,27 @@ class PangeaLanguage {
return (now - lastFetched) >= fetchIntervalInMilliseconds ? true : false; return (now - lastFetched) >= fetchIntervalInMilliseconds ? true : false;
} }
static Future<void> _saveFlags(List<LanguageModel> langFlags) async { static Future<void> _saveLanguages(List<LanguageModel> languages) async {
final Map flagMap = { final Map languagesMaps = {
PrefKey.flags: langFlags.map((e) => e.toJson()).toList(), 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 { static Future<List<LanguageModel>> _getCachedLanguages() async {
final Map<dynamic, dynamic>? flagsMap = final Map<dynamic, dynamic>? languagesMap =
await MyShared.readJson(PrefKey.flags); await MyShared.readJson(PrefKey.languagesKey);
if (flagsMap == null) { if (languagesMap == null) {
return []; return [];
} }
final List<LanguageModel> flags = []; final List<LanguageModel> languages = [];
final List mapList = flagsMap[PrefKey.flags] as List; final List mapList = languagesMap[PrefKey.languagesKey] as List;
for (final element in mapList) { for (final element in mapList) {
flags.add(LanguageModel.fromJson(element)); languages.add(LanguageModel.fromJson(element));
} }
return flags; return languages;
} }
static LanguageModel? byLangCode(String langCode) { static LanguageModel? byLangCode(String langCode) {

@ -48,7 +48,11 @@ class PLanguageDropdownState extends State<PLanguageDropdown> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final List<LanguageModel> sortedLanguages = widget.languages; final List<LanguageModel> sortedLanguages = widget.languages;
final String systemLang = Localizations.localeOf(context).languageCode; 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) { int sortLanguages(LanguageModel a, LanguageModel b) {
final String aLang = a.langCode; 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/config/environment.dart';
import 'package:fluffychat/pangea/common/network/urls.dart'; import 'package:fluffychat/pangea/common/network/urls.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.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/default_morph_mapping.dart';
import 'package:fluffychat/pangea/morphs/morph_models.dart'; import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/widgets/matrix.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 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 /// if not, we can make it async and update uses of this function
/// to be async as well /// to be async as well
static Future<MorphFeaturesAndTags> get([String? languageCode]) async { static Future<MorphFeaturesAndTags> get([LanguageModel? language]) async {
languageCode ??= language ??= MatrixState.pangeaController.languageController.userL2;
MatrixState.pangeaController.languageController.userL2?.langCode;
if (languageCode == null) { if (language == null) {
return defaultMorphMapping; return defaultMorphMapping;
} }
// does not differ based on locale
final langCodeShort = language.langCodeShort;
// check if we have a cached morphs for this language code // check if we have a cached morphs for this language code
final cachedJson = _morphsStorage.read(languageCode); final cachedJson = _morphsStorage.read(langCodeShort);
if (cachedJson != null) { if (cachedJson != null) {
return MorphsRepo.fromJson(cachedJson); return MorphsRepo.fromJson(cachedJson);
} }
// check if we have a cached call for this language code // 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 (cachedCall != null) {
if (DateTime.now().difference(cachedCall.time).inMinutes < if (DateTime.now().difference(cachedCall.time).inMinutes <
_cacheDurationMinutes) { _cacheDurationMinutes) {
return cachedCall.future; return cachedCall.future;
} else { } else {
shortTermCache.remove(languageCode); shortTermCache.remove(langCodeShort);
} }
} }
// fetch the morphs but don't wait for it // fetch the morphs but don't wait for it
final future = _fetch(languageCode); final future = _fetch(langCodeShort);
shortTermCache[languageCode] = _APICallCacheItem(DateTime.now(), future); shortTermCache[langCodeShort] = _APICallCacheItem(DateTime.now(), future);
return future; return future;
} }
} }

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

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

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

Loading…
Cancel
Save