feat: integration of apple signin
This commit is contained in:
parent
a9389070bf
commit
4409aef9e2
|
|
@ -9,12 +9,14 @@
|
|||
"cont": "ቀጥል",
|
||||
"register": "ይመዝገቡ",
|
||||
"login_with_google": "በጉግል ይግቡ",
|
||||
"login_with_apple": "በአፕል ይግቡ",
|
||||
"or": "ወይም",
|
||||
"login_with_phone": "በስልክ ቁጥር ይግቡ",
|
||||
"create_account": "አዲስ መለያ ይፍጠሩ",
|
||||
"already_have_account": "መለያ አለዎት?",
|
||||
"login": " ይግቡ ",
|
||||
"register_with_google": "በጉግል ይመዝገቡ",
|
||||
"register_with_apple": "በአፕል ይመዝገቡ",
|
||||
"register_with_phone": "በስልክ ቁጥር ይመዝገቡ",
|
||||
"enter_phone_number": "የስልክ ቁጥርዎን ያስገቡ። የማረጋገጫ ኮድ እንልክልዎታለን።",
|
||||
"login_with_email": "በኢሜይል ይግቡ",
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@
|
|||
"cont": "Continue",
|
||||
"register": "Register",
|
||||
"login_with_google": "Login with Google",
|
||||
"login_with_apple": "Login with Apple",
|
||||
"or": "Or",
|
||||
"login_with_phone": "Login with phone number",
|
||||
"create_account": "Create an account",
|
||||
"already_have_account": "Already have an account?",
|
||||
"login": "Login",
|
||||
"register_with_google": "Register with Google",
|
||||
"register_with_apple": "Register with Apple",
|
||||
"register_with_phone": "Register with phone number",
|
||||
"enter_phone_number": "Enter your phone number. We will send you a confirmation code there.",
|
||||
"login_with_email": "Login with email",
|
||||
|
|
@ -190,7 +192,7 @@
|
|||
"finish_all_practice_previouse_course": "Finish the previous course practice to take this",
|
||||
"track_journey": "Track your learning journey and see your growth over time.",
|
||||
"learn_english": "Learn English",
|
||||
"keep_momentum":"Great job! Keep the momentum.",
|
||||
"keep_momentum": "Great job! Keep the momentum.",
|
||||
"completed_practices": "Completed Practices",
|
||||
"total_practices": "Total Practices",
|
||||
"progress_percentage": "Progress Percentage"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 60;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
|
@ -66,6 +66,7 @@
|
|||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
ACEEA7C32CFC6E9900D60211 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
A8C2A6C7D1D99F7BA12EAF94 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
F1F6AAAC52D909E27AEDEFC0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
F9793345F00B89E38C23EBB8 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
|
@ -163,6 +164,7 @@
|
|||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
8E1B3E492C540A0B00F51C11 /* GoogleService-Info.plist */,
|
||||
ACEEA7C32CFC6E9900D60211 /* Runner.entitlements */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
|
|
@ -236,6 +238,11 @@
|
|||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1100;
|
||||
SystemCapabilities = {
|
||||
com.apple.SignInWithApple = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -249,7 +256,7 @@
|
|||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
packageReferences = (
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
|
||||
);
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
|
|
@ -345,14 +352,10 @@
|
|||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
|
|
@ -366,14 +369,10 @@
|
|||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
|
|
@ -509,6 +508,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
|
|
@ -698,6 +698,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
|
|
@ -727,6 +728,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
|
|
@ -785,7 +787,7 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
|
|
|
|||
10
ios/Runner/Runner.entitlements
Normal file
10
ios/Runner/Runner.entitlements
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -56,6 +56,7 @@ import 'package:yimaru_app/services/localization_service.dart';
|
|||
import 'package:yimaru_app/ui/views/landing/landing_view.dart';
|
||||
import 'package:yimaru_app/ui/views/course_module/course_module_view.dart';
|
||||
import 'package:yimaru_app/services/onboarding_service.dart';
|
||||
import 'package:yimaru_app/services/apple_auth_service.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
||||
import 'package:yimaru_app/ui/views/payment/payment_view.dart';
|
||||
// @stacked-import
|
||||
|
|
@ -124,6 +125,7 @@ import 'package:yimaru_app/ui/views/payment/payment_view.dart';
|
|||
LazySingleton(classType: LearnService),
|
||||
LazySingleton(classType: LocalizationService),
|
||||
LazySingleton(classType: OnboardingService),
|
||||
LazySingleton(classType: AppleAuthService),
|
||||
// @stacked-service
|
||||
],
|
||||
bottomsheets: [
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import 'package:stacked_services/src/navigation/navigation_service.dart';
|
|||
import 'package:stacked_shared/stacked_shared.dart';
|
||||
|
||||
import '../services/api_service.dart';
|
||||
import '../services/apple_auth_service.dart';
|
||||
import '../services/audio_player_service.dart';
|
||||
import '../services/authentication_service.dart';
|
||||
import '../services/course_service.dart';
|
||||
|
|
@ -67,4 +68,5 @@ Future<void> setupLocator(
|
|||
locator.registerLazySingleton(() => LearnService());
|
||||
locator.registerLazySingleton(() => LocalizationService());
|
||||
locator.registerLazySingleton(() => OnboardingService());
|
||||
locator.registerLazySingleton(() => AppleAuthService());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,34 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Apple auth
|
||||
Future<Map<String, dynamic>> appleAuth(Map<String, dynamic> data) async {
|
||||
try {
|
||||
Response response = await _service.dio.post(
|
||||
'$kBaseUrl/$kAppleAuthUrl',
|
||||
data: data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'message': 'Logged in successfully',
|
||||
'data': User.fromJson(response.data['data']),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': '${response.data['message']}, ${response.data['error']}'
|
||||
};
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
'message': e.response?.data.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Verify otp
|
||||
Future<Map<String, dynamic>> verifyOtp(Map<String, dynamic> data) async {
|
||||
try {
|
||||
|
|
|
|||
36
lib/services/apple_auth_service.dart
Normal file
36
lib/services/apple_auth_service.dart
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
class AppleAuthService with ListenableServiceMixin {
|
||||
AuthorizationCredentialAppleID? _appleCredential;
|
||||
|
||||
AuthorizationCredentialAppleID? get appleCredential => _appleCredential;
|
||||
|
||||
AppleAuthService() {
|
||||
listenToReactiveValues([_appleCredential]);
|
||||
}
|
||||
|
||||
bool get isSupported => Platform.isIOS;
|
||||
|
||||
Future<void> appleAuth() async {
|
||||
if (!isSupported) {
|
||||
throw UnsupportedError('Apple Sign-In is only available on iOS.');
|
||||
}
|
||||
|
||||
_appleCredential = await SignInWithApple.getAppleIDCredential(
|
||||
scopes: [
|
||||
AppleIDAuthorizationScopes.email,
|
||||
AppleIDAuthorizationScopes.fullName,
|
||||
],
|
||||
);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void logout() {
|
||||
_appleCredential = null;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
@ -101,6 +101,8 @@ String kLessonProgressUrl = 'api/v1/progress/videos';
|
|||
|
||||
String kGoogleAuthUrl = 'api/v1/auth/google/android';
|
||||
|
||||
String kAppleAuthUrl = 'api/v1/auth/apple';
|
||||
|
||||
String kCourseProgressUrl = 'api/v1/progress/courses';
|
||||
|
||||
String kAssessmentsUrl = 'api/v1/assessment/questions';
|
||||
|
|
@ -124,5 +126,4 @@ String kTelegramSupportLink = 'https://t.me/yimaruacademy2026';
|
|||
|
||||
String kErrorUrl = 'https://api.yimaruacademy.com/payment/error';
|
||||
|
||||
String kSuccessUrl =
|
||||
'https://api.yimaruacademy.com/payment/success';
|
||||
String kSuccessUrl = 'https://api.yimaruacademy.com/payment/success';
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ enum Voice { sample, recorded }
|
|||
enum ResponseStatus { success, failure }
|
||||
|
||||
// Login method
|
||||
enum LoginMethod { phone, email, google }
|
||||
enum LoginMethod { phone, email, google, apple }
|
||||
|
||||
// Sign-up method
|
||||
enum SignUpMethod { phone, email, google }
|
||||
enum SignUpMethod { phone, email, google, apple }
|
||||
|
||||
// Learn practice
|
||||
enum LearnPractices { course, module, lesson }
|
||||
|
|
@ -61,7 +61,9 @@ enum StateObjects {
|
|||
profileCompletion,
|
||||
learnSubscription,
|
||||
learnSubscriptions,
|
||||
loginWithApple,
|
||||
registerWithGoogle,
|
||||
registerWithApple,
|
||||
learnPracticeSample,
|
||||
learnPracticeAnswer,
|
||||
loginWithPhoneNumber,
|
||||
|
|
|
|||
|
|
@ -14,204 +14,7 @@ class CodegenLoader extends AssetLoader{
|
|||
return Future.value(mapLocales[locale.toString()]);
|
||||
}
|
||||
|
||||
static const Map<String,dynamic> _am = {
|
||||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
"dont_have_account": "መለያ የለዎትም?",
|
||||
"email": "ኢሜይል",
|
||||
"password": "የይለፍ ቃል",
|
||||
"forgot_password": "የይለፍ ቃል ረሱ?",
|
||||
"cont": "ቀጥል",
|
||||
"register": "ይመዝገቡ",
|
||||
"login_with_google": "በጉግል ይግቡ",
|
||||
"or": "ወይም",
|
||||
"login_with_phone": "በስልክ ቁጥር ይግቡ",
|
||||
"create_account": "አዲስ መለያ ይፍጠሩ",
|
||||
"already_have_account": "መለያ አለዎት?",
|
||||
"login": " ይግቡ ",
|
||||
"register_with_google": "በጉግል ይመዝገቡ",
|
||||
"register_with_phone": "በስልክ ቁጥር ይመዝገቡ",
|
||||
"enter_phone_number": "የስልክ ቁጥርዎን ያስገቡ። የማረጋገጫ ኮድ እንልክልዎታለን።",
|
||||
"login_with_email": "በኢሜይል ይግቡ",
|
||||
"create_password": "የይለፍ ቃል ይፍጠሩ",
|
||||
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
||||
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
||||
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
||||
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||
"terms_of_services": "የአገልግሎት ውሎች",
|
||||
"and": "እና",
|
||||
"privacy_policy": "የግላዊነት ፖሊሲ",
|
||||
"register_with_email": "በኢሜል ይመዝገቡ",
|
||||
"verification_code": "የማረጋገጫ ኮድ",
|
||||
"resend_code": "ኮዱን እንደገና ላክ",
|
||||
"code_sent_to_phone": "ኮዱ ወደ ስልክ ቁጥርዎ ተልኳል",
|
||||
"code_sent_to_email": "ኮዱ ወደ ኢሜል ተልኳል",
|
||||
"resend_code_in": "ኮዱን እንደገና ለመላክ የቀረው ጊዜ",
|
||||
"reset_password": " የይለፍ ቃልን ይቀይሩ",
|
||||
"enter_email_reset_code": "ኢሜይልዎን ያስገቡ። የይለፍ ቃል መለወጫ ኮድ እንልክልዎታለን።",
|
||||
"please_wait": "እባክዎ ይጠብቁ",
|
||||
"reset_code_sent": "የመቀየሪያ ኮድ በተሳካ ሁኔታ ተልኳል",
|
||||
"reset_code": " የመቀየሪያ ኮድ ",
|
||||
"new_password": "አዲስ የይለፍ ቃል",
|
||||
"logged_in_successfully": "በተሳካ ሁኔታ ገብተዋል",
|
||||
"view_course": " ኮርሱን ይመልከቱ",
|
||||
"continue_learning": "መማርን ይቀጥሉ",
|
||||
"start_learning": "ትምህርትን ይጀምሩ",
|
||||
"completed": "ተጠናቋል",
|
||||
"take_practice": "ልምምድ ያድርጉ",
|
||||
"your_current_level": "የአሁኑ ደረጃዎ",
|
||||
"overall_progress": "አጠቃላይ እድገት",
|
||||
"great_work": "በርቱ! በጣም ጥሩ እየሰሩ ነው",
|
||||
"view_module": "ሞጁሉን ይመልከቱ",
|
||||
"progress": "እድገት",
|
||||
"keep_going": "ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": "በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"practice": "ልምምድ",
|
||||
"start": "ጀምር",
|
||||
"in_progress": "በሂደት ላይ",
|
||||
"hello": "ሰላም",
|
||||
"ready_to_learn": " ዛሬ እንግሊዝኛ ለመማር ተዘጋጅተዋል? ",
|
||||
"learn": "ይማሩ ",
|
||||
"course": "ኮርስ",
|
||||
"profile": " ፕሮፋይል ",
|
||||
"speaking_partner": "የንግግር ጓደኛ",
|
||||
"practice_what_you_learned": "አሁን የተማሩትን እንለማመድ",
|
||||
"practice_questions": "ጥቂት ጥያቄዎችን እጠይቃለሁ እና መልስ መስጠት ይችላሉ",
|
||||
"start_practice": "ልምምድ ጀምር",
|
||||
"almost_there": "ሊጨርሱ ተቃርበዋል",
|
||||
"finish_session": "እድገትዎን ለማየት ክፍለ ጊዜውን ያጠናቅቁ",
|
||||
"continue_practice": "ልምምዱን ይቀጥሉ",
|
||||
"end_session": "ክፍለ ጊዜውን ያብቁ ",
|
||||
"tap_start_to_listen": "ለማዳመጥ የጀምር ቁልፉን ይጫኑ",
|
||||
"practice_speaking": "ንግግርን ይለማመዱ",
|
||||
"tap_microphone": "ለመናገር ማይክሮፎኑን ይጫኑ",
|
||||
"reply": "እንደገና አዳምጥ",
|
||||
"cancel": "ይቅር",
|
||||
"you_are_speaking": "እየተናገሩ ነው",
|
||||
"practice_completed": "ልምምዱ ተጠናቅቋል",
|
||||
"great_improvement": "በዚህኛው በራስ መተማመንዎ ጨምሯል፤ ትልቅ መሻሻል ነው",
|
||||
"practice_again": "እንደገና ይለማመዱ",
|
||||
"conversation_review": "የንግግር ግምገማ",
|
||||
"result": "ውጤት",
|
||||
"quick_tip": "ጠቃሚ ምክር",
|
||||
"retry": "እንደገና ይሞክሩ",
|
||||
"completed_a1": "እንኳን ደስ አለዎት! A1 ደረጃን አጠናቅቀዋል",
|
||||
"analyzing_speaking": "የንግግር ችሎታዎን እየገመገምን ነው",
|
||||
"view_profile": "ፕሮፋይሎን ይመልከቱ ",
|
||||
"hi": "ሰላም",
|
||||
"edit_profile": "መገለጫ ያስተካክሉ",
|
||||
"first_name": "የመጀመሪያ ስም",
|
||||
"last_name": "የአባት ስም",
|
||||
"gender": "ፆታ",
|
||||
"male": "ወንድ",
|
||||
"female": "ሴት",
|
||||
"phone_number": "የስልክ ቁጥር",
|
||||
"country": "ሀገር",
|
||||
"region": "ክልል",
|
||||
"select_region": "ክልል ይምረጡ",
|
||||
"enter_your_city": "ከተማዎን ያስገቡ",
|
||||
"occupation": "የስራ መስክ",
|
||||
"select_occupation": "ሙያዎን ይምረጡ",
|
||||
"save_changes": "ለውጦችን ያስቀምጡ",
|
||||
"my_progress": "የእኔ እድገት",
|
||||
"track_your_achievement": "ስኬቶችዎን እና ተከታታይ የትምህርት ጉዞዎን ይከታተሉ",
|
||||
"account_and_privacy": "መለያ እና ግላዊነት",
|
||||
"manage_settings": "ቅንብሮችን እና የመተግበሪያ ምርጫዎችን ያስተዳድሩ",
|
||||
"support": "ድጋፍ",
|
||||
"get_help": "በስልክ ወይም በቴሌግራም እገዛ ያግኙ",
|
||||
"logout": "ውጣ",
|
||||
"app_settings": "የመተግበሪያ ቅንብሮች",
|
||||
"legal_and_information": "ሕጋዊ እና መረጃ",
|
||||
"change_language": "ቋንቋ ቀይር",
|
||||
"terms_and_conditions": "ውሎች እና ሁኔታዎች",
|
||||
"delete_account": "መለያ ሰርዝ",
|
||||
"language_preference": "የቋንቋ ምርጫ",
|
||||
"choose_your_language": "ለውጦችን አስቀምጥ",
|
||||
"switch_language_anytime": "ቋንቋዎችን በማንኛውም ጊዜ መቀየር ይችላሉ",
|
||||
"need_help": "እገዛ ይፈልጋሉ?",
|
||||
"call_support": "የስልክ ድጋፍ",
|
||||
"talk_with_support": "በቀጥታ ከድጋፍ ቡድናችን ጋር ይነጋገሩ",
|
||||
"telegram_support": "የቴሌግራም ድጋፍ",
|
||||
"chat_via_telegram": "በቴሌግራም በፍጥነት ይወያዩ",
|
||||
"call_our_support": "ከ3 ጠዋት እስከ 12 ማታ ድረስ የድጋፍ ቡድናችንን ይደውሉ",
|
||||
"tap_to_call": "ለመደወል ይንኩ",
|
||||
"join_telegram": "በቴሌግራም የይማሩ አካዳሚን ይቀላቀሉ",
|
||||
"connect_with_support_team": "ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
||||
"open_in_telegram": "በቴሌግራም ይክፈቱ",
|
||||
"search_for": "ፈልጉት",
|
||||
"current_level": "የአሁኑ ደረጃ",
|
||||
"keep_up_the_great_work": "በጣም ጥሩ እየሰራህ ነው! ቀጥልበት፣ አስደናቂ ነህ።",
|
||||
"no_practice_available": "ምንም ልምምድ አልተገኘም!",
|
||||
"begin_module_practice": "የሞጁሉን ልምምድ ጀምር",
|
||||
"lets_practice_lesson": "እንለማመድ",
|
||||
"lets_quickly_review": "በዚህ ሞጁል ውስጥ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"lets_practice_module": "አሁን የተማርከውን እንለማመድ!",
|
||||
"ask_you_few_actions": "ጥቂት ጥያቄዎችን እጠይቅሃለሁ፣ አንተም በተፈጥሮ መልስ ልትሰጥ ትችላለህ።",
|
||||
"begin_level_practice": "የደረጃ ልምምድን ጀምር",
|
||||
"lets_practice_course": "የኮርሱን ልምምድ እንለማመድ",
|
||||
"lets_quick_review": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"speaking": "እየተናገረ ነው",
|
||||
"you_have_finished_practice": "ልምምድህን አጠናቀቅህ",
|
||||
"view_results": "ውጤቶቼን እይ",
|
||||
"sample_answer": "ናሙና መልስ",
|
||||
"your_answer": "መልስህ",
|
||||
"sound_confident": "በዚህ ጊዜ የበለጠ እምነት ያለህ ይመስላል — በጣም ጥሩ መሻሻል ነው!",
|
||||
"you_have_completed": "አያይ! አጠናቀቅህ",
|
||||
"yes": "አዎ",
|
||||
"no": "አይ",
|
||||
"want_to_quit": "ለመውጣት እርግጠኛ ነህ?",
|
||||
"required_field": "ይህ መስክ ያስፈልጋል",
|
||||
"enter_full_name": "ሙሉ ስምህን አስገባ",
|
||||
"invalid_email": "የማይሰራ የኢሜይል ቅርጸት",
|
||||
"phone_must_start_with": "የስልክ ቁጥር በ251 መጀመር አለበት",
|
||||
"phone_must_be": "የስልክ ቁጥር 12 አሃዞች መሆን አለበት",
|
||||
"what_should_we_call_you": "ምን ብለን እንጠራህ?",
|
||||
"name_for_personalization": "በመማር ጉዞህ ውስጥ ለግል ለማድረግ ስምህን እንጠቀማለን።",
|
||||
"choose_your_gender": "ጾታህን ምረጥ",
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"age_range": "በየትኛው የእድሜ ክልል ውስጥ ነህ?",
|
||||
"age_for_personalization": "በእድሜህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"educational_background": "አሁን ያለህ የትምህርት ደረጃ ምንድን ነው?",
|
||||
"education_for_personalization": "ይህ ትምህርቶችን ከልምድህ ጋር እንዲስማሙ ለማድረግ ይረዳናል።",
|
||||
"your_occupation": "ስራህ ምንድን ነው?",
|
||||
"occupation_for_personalization": "በስራህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"location": "ከየት ነህ?",
|
||||
"select_country_region": "አገርህን እና ክልልህን ከተቆልቋይ ዝርዝሩ ምረጥ",
|
||||
"select_country": "አገር ምረጥ",
|
||||
"learning_goal": "የመማር ዓላማህን ምረጥ",
|
||||
"language_goal": "እንግሊዝኛህን ለማሻሻል ዋና ዓላማህ ምንድን ነው?",
|
||||
"your_goal": "ዓላማህ የመማር ጉዞህን እንዲስማማ ለማድረግ ይረዳናል።",
|
||||
"write_your_goal": "ዓላማህን ጻፍ…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||
"skip": "ዝለል",
|
||||
"finish_level": "ደረጃውን አጠናቅቅ",
|
||||
"likely_speaker": "አንተ ምናልባት ተናጋሪ ነህ",
|
||||
"great_job": "በጣም ጥሩ ስራ! ለመሻሻል ቀጣዩ ደረጃህ ይኸው ነው።",
|
||||
"lets_start_practice": "ልምምድህን እንጀምር",
|
||||
"welcome_abroad": "እንኳን ደህና መጣህ",
|
||||
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
||||
"finish": "አጠናቅቅ",
|
||||
"finish_all_practice_lesson": "ይህን ልምምድ ለመውሰድ የቀድሞውን የትምህርት ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_module": "የሞጁሉን ልምምድ ለመውሰድ የትምህርት ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_course": "የኮርሱን ልምምድ ለመውሰድ የሞጁል ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_module": "ይህን ልምምድ ለመውሰድ የቀድሞውን የሞጁል ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_course": "ይህን ለመውሰድ የቀድሞውን የኮርስ ልምምድ ያጠናቅቁ",
|
||||
"track_journey": "የትምህርት ጉዞዎን ይከታተሉ እና በጊዜ ሂደት ያሳዩትን እድገት ይመልከቱ።",
|
||||
"learn_english": "እንግሊዝኛ ይማሩ",
|
||||
"keep_momentum": "በጣም ጥሩ ስራ! በዚሁ ብርታት ይቀጥሉ።",
|
||||
"completed_practices": "የተጠናቀቁ ልምምዶች",
|
||||
"total_practices": "ጠቅላላ ልምምዶች",
|
||||
"progress_percentage": "የእድገት መቶኛ"
|
||||
};
|
||||
static const Map<String,dynamic> _en = {
|
||||
static const Map<String,dynamic> _en = {
|
||||
"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
|
|
@ -222,12 +25,14 @@ static const Map<String,dynamic> _en = {
|
|||
"cont": "Continue",
|
||||
"register": "Register",
|
||||
"login_with_google": "Login with Google",
|
||||
"login_with_apple": "Login with Apple",
|
||||
"or": "Or",
|
||||
"login_with_phone": "Login with phone number",
|
||||
"create_account": "Create an account",
|
||||
"already_have_account": "Already have an account?",
|
||||
"login": "Login",
|
||||
"register_with_google": "Register with Google",
|
||||
"register_with_apple": "Register with Apple",
|
||||
"register_with_phone": "Register with phone number",
|
||||
"enter_phone_number": "Enter your phone number. We will send you a confirmation code there.",
|
||||
"login_with_email": "Login with email",
|
||||
|
|
@ -408,5 +213,204 @@ static const Map<String,dynamic> _en = {
|
|||
"total_practices": "Total Practices",
|
||||
"progress_percentage": "Progress Percentage"
|
||||
};
|
||||
static const Map<String, Map<String,dynamic>> mapLocales = {"am": _am, "en": _en};
|
||||
static const Map<String,dynamic> _am = {
|
||||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
"dont_have_account": "መለያ የለዎትም?",
|
||||
"email": "ኢሜይል",
|
||||
"password": "የይለፍ ቃል",
|
||||
"forgot_password": "የይለፍ ቃል ረሱ?",
|
||||
"cont": "ቀጥል",
|
||||
"register": "ይመዝገቡ",
|
||||
"login_with_google": "በጉግል ይግቡ",
|
||||
"login_with_apple": "በአፕል ይግቡ",
|
||||
"or": "ወይም",
|
||||
"login_with_phone": "በስልክ ቁጥር ይግቡ",
|
||||
"create_account": "አዲስ መለያ ይፍጠሩ",
|
||||
"already_have_account": "መለያ አለዎት?",
|
||||
"login": " ይግቡ ",
|
||||
"register_with_google": "በጉግል ይመዝገቡ",
|
||||
"register_with_apple": "በአፕል ይመዝገቡ",
|
||||
"register_with_phone": "በስልክ ቁጥር ይመዝገቡ",
|
||||
"enter_phone_number": "የስልክ ቁጥርዎን ያስገቡ። የማረጋገጫ ኮድ እንልክልዎታለን።",
|
||||
"login_with_email": "በኢሜይል ይግቡ",
|
||||
"create_password": "የይለፍ ቃል ይፍጠሩ",
|
||||
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
||||
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
||||
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
||||
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||
"terms_of_services": "የአገልግሎት ውሎች",
|
||||
"and": "እና",
|
||||
"privacy_policy": "የግላዊነት ፖሊሲ",
|
||||
"register_with_email": "በኢሜል ይመዝገቡ",
|
||||
"verification_code": "የማረጋገጫ ኮድ",
|
||||
"resend_code": "ኮዱን እንደገና ላክ",
|
||||
"code_sent_to_phone": "ኮዱ ወደ ስልክ ቁጥርዎ ተልኳል",
|
||||
"code_sent_to_email": "ኮዱ ወደ ኢሜል ተልኳል",
|
||||
"resend_code_in": "ኮዱን እንደገና ለመላክ የቀረው ጊዜ",
|
||||
"reset_password": " የይለፍ ቃልን ይቀይሩ",
|
||||
"enter_email_reset_code": "ኢሜይልዎን ያስገቡ። የይለፍ ቃል መለወጫ ኮድ እንልክልዎታለን።",
|
||||
"please_wait": "እባክዎ ይጠብቁ",
|
||||
"reset_code_sent": "የመቀየሪያ ኮድ በተሳካ ሁኔታ ተልኳል",
|
||||
"reset_code": " የመቀየሪያ ኮድ ",
|
||||
"new_password": "አዲስ የይለፍ ቃል",
|
||||
"logged_in_successfully": "በተሳካ ሁኔታ ገብተዋል",
|
||||
"view_course": " ኮርሱን ይመልከቱ",
|
||||
"continue_learning": "መማርን ይቀጥሉ",
|
||||
"start_learning": "ትምህርትን ይጀምሩ",
|
||||
"completed": "ተጠናቋል",
|
||||
"take_practice": "ልምምድ ያድርጉ",
|
||||
"your_current_level": "የአሁኑ ደረጃዎ",
|
||||
"overall_progress": "አጠቃላይ እድገት",
|
||||
"great_work": "በርቱ! በጣም ጥሩ እየሰሩ ነው",
|
||||
"view_module": "ሞጁሉን ይመልከቱ",
|
||||
"progress": "እድገት",
|
||||
"keep_going": "ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": "በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"practice": "ልምምድ",
|
||||
"start": "ጀምር",
|
||||
"in_progress": "በሂደት ላይ",
|
||||
"hello": "ሰላም",
|
||||
"ready_to_learn": " ዛሬ እንግሊዝኛ ለመማር ተዘጋጅተዋል? ",
|
||||
"learn": "ይማሩ ",
|
||||
"course": "ኮርስ",
|
||||
"profile": " ፕሮፋይል ",
|
||||
"speaking_partner": "የንግግር ጓደኛ",
|
||||
"practice_what_you_learned": "አሁን የተማሩትን እንለማመድ",
|
||||
"practice_questions": "ጥቂት ጥያቄዎችን እጠይቃለሁ እና መልስ መስጠት ይችላሉ",
|
||||
"start_practice": "ልምምድ ጀምር",
|
||||
"almost_there": "ሊጨርሱ ተቃርበዋል",
|
||||
"finish_session": "እድገትዎን ለማየት ክፍለ ጊዜውን ያጠናቅቁ",
|
||||
"continue_practice": "ልምምዱን ይቀጥሉ",
|
||||
"end_session": "ክፍለ ጊዜውን ያብቁ ",
|
||||
"tap_start_to_listen": "ለማዳመጥ የጀምር ቁልፉን ይጫኑ",
|
||||
"practice_speaking": "ንግግርን ይለማመዱ",
|
||||
"tap_microphone": "ለመናገር ማይክሮፎኑን ይጫኑ",
|
||||
"reply": "እንደገና አዳምጥ",
|
||||
"cancel": "ይቅር",
|
||||
"you_are_speaking": "እየተናገሩ ነው",
|
||||
"practice_completed": "ልምምዱ ተጠናቅቋል",
|
||||
"great_improvement": "በዚህኛው በራስ መተማመንዎ ጨምሯል፤ ትልቅ መሻሻል ነው",
|
||||
"practice_again": "እንደገና ይለማመዱ",
|
||||
"conversation_review": "የንግግር ግምገማ",
|
||||
"result": "ውጤት",
|
||||
"quick_tip": "ጠቃሚ ምክር",
|
||||
"retry": "እንደገና ይሞክሩ",
|
||||
"completed_a1": "እንኳን ደስ አለዎት! A1 ደረጃን አጠናቅቀዋል",
|
||||
"analyzing_speaking": "የንግግር ችሎታዎን እየገመገምን ነው",
|
||||
"view_profile": "ፕሮፋይሎን ይመልከቱ ",
|
||||
"hi": "ሰላም",
|
||||
"edit_profile": "መገለጫ ያስተካክሉ",
|
||||
"first_name": "የመጀመሪያ ስም",
|
||||
"last_name": "የአባት ስም",
|
||||
"gender": "ፆታ",
|
||||
"male": "ወንድ",
|
||||
"female": "ሴት",
|
||||
"phone_number": "የስልክ ቁጥር",
|
||||
"country": "ሀገር",
|
||||
"region": "ክልል",
|
||||
"select_region": "ክልል ይምረጡ",
|
||||
"enter_your_city": "ከተማዎን ያስገቡ",
|
||||
"occupation": "የስራ መስክ",
|
||||
"select_occupation": "ሙያዎን ይምረጡ",
|
||||
"save_changes": "ለውጦችን ያስቀምጡ",
|
||||
"my_progress": "የእኔ እድገት",
|
||||
"track_your_achievement": "ስኬቶችዎን እና ተከታታይ የትምህርት ጉዞዎን ይከታተሉ",
|
||||
"account_and_privacy": "መለያ እና ግላዊነት",
|
||||
"manage_settings": "ቅንብሮችን እና የመተግበሪያ ምርጫዎችን ያስተዳድሩ",
|
||||
"support": "ድጋፍ",
|
||||
"get_help": "በስልክ ወይም በቴሌግራም እገዛ ያግኙ",
|
||||
"logout": "ውጣ",
|
||||
"app_settings": "የመተግበሪያ ቅንብሮች",
|
||||
"legal_and_information": "ሕጋዊ እና መረጃ",
|
||||
"change_language": "ቋንቋ ቀይር",
|
||||
"terms_and_conditions": "ውሎች እና ሁኔታዎች",
|
||||
"delete_account": "መለያ ሰርዝ",
|
||||
"language_preference": "የቋንቋ ምርጫ",
|
||||
"choose_your_language": "ለውጦችን አስቀምጥ",
|
||||
"switch_language_anytime": "ቋንቋዎችን በማንኛውም ጊዜ መቀየር ይችላሉ",
|
||||
"need_help": "እገዛ ይፈልጋሉ?",
|
||||
"call_support": "የስልክ ድጋፍ",
|
||||
"talk_with_support": "በቀጥታ ከድጋፍ ቡድናችን ጋር ይነጋገሩ",
|
||||
"telegram_support": "የቴሌግራም ድጋፍ",
|
||||
"chat_via_telegram": "በቴሌግራም በፍጥነት ይወያዩ",
|
||||
"call_our_support": "ከ3 ጠዋት እስከ 12 ማታ ድረስ የድጋፍ ቡድናችንን ይደውሉ",
|
||||
"tap_to_call": "ለመደወል ይንኩ",
|
||||
"join_telegram": "በቴሌግራም የይማሩ አካዳሚን ይቀላቀሉ",
|
||||
"connect_with_support_team": "ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
||||
"open_in_telegram": "በቴሌግራም ይክፈቱ",
|
||||
"search_for": "ፈልጉት",
|
||||
"current_level": "የአሁኑ ደረጃ",
|
||||
"keep_up_the_great_work": "በጣም ጥሩ እየሰራህ ነው! ቀጥልበት፣ አስደናቂ ነህ።",
|
||||
"no_practice_available": "ምንም ልምምድ አልተገኘም!",
|
||||
"begin_module_practice": "የሞጁሉን ልምምድ ጀምር",
|
||||
"lets_practice_lesson": "እንለማመድ",
|
||||
"lets_quickly_review": "በዚህ ሞጁል ውስጥ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"lets_practice_module": "አሁን የተማርከውን እንለማመድ!",
|
||||
"ask_you_few_actions": "ጥቂት ጥያቄዎችን እጠይቅሃለሁ፣ አንተም በተፈጥሮ መልስ ልትሰጥ ትችላለህ።",
|
||||
"begin_level_practice": "የደረጃ ልምምድን ጀምር",
|
||||
"lets_practice_course": "የኮርሱን ልምምድ እንለማመድ",
|
||||
"lets_quick_review": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"speaking": "እየተናገረ ነው",
|
||||
"you_have_finished_practice": "ልምምድህን አጠናቀቅህ",
|
||||
"view_results": "ውጤቶቼን እይ",
|
||||
"sample_answer": "ናሙና መልስ",
|
||||
"your_answer": "መልስህ",
|
||||
"sound_confident": "በዚህ ጊዜ የበለጠ እምነት ያለህ ይመስላል — በጣም ጥሩ መሻሻል ነው!",
|
||||
"you_have_completed": "አያይ! አጠናቀቅህ",
|
||||
"yes": "አዎ",
|
||||
"no": "አይ",
|
||||
"want_to_quit": "ለመውጣት እርግጠኛ ነህ?",
|
||||
"required_field": "ይህ መስክ ያስፈልጋል",
|
||||
"enter_full_name": "ሙሉ ስምህን አስገባ",
|
||||
"invalid_email": "የማይሰራ የኢሜይል ቅርጸት",
|
||||
"phone_must_start_with": "የስልክ ቁጥር በ251 መጀመር አለበት",
|
||||
"phone_must_be": "የስልክ ቁጥር 12 አሃዞች መሆን አለበት",
|
||||
"what_should_we_call_you": "ምን ብለን እንጠራህ?",
|
||||
"name_for_personalization": "በመማር ጉዞህ ውስጥ ለግል ለማድረግ ስምህን እንጠቀማለን።",
|
||||
"choose_your_gender": "ጾታህን ምረጥ",
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"age_range": "በየትኛው የእድሜ ክልል ውስጥ ነህ?",
|
||||
"age_for_personalization": "በእድሜህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"educational_background": "አሁን ያለህ የትምህርት ደረጃ ምንድን ነው?",
|
||||
"education_for_personalization": "ይህ ትምህርቶችን ከልምድህ ጋር እንዲስማሙ ለማድረግ ይረዳናል።",
|
||||
"your_occupation": "ስራህ ምንድን ነው?",
|
||||
"occupation_for_personalization": "በስራህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"location": "ከየት ነህ?",
|
||||
"select_country_region": "አገርህን እና ክልልህን ከተቆልቋይ ዝርዝሩ ምረጥ",
|
||||
"select_country": "አገር ምረጥ",
|
||||
"learning_goal": "የመማር ዓላማህን ምረጥ",
|
||||
"language_goal": "እንግሊዝኛህን ለማሻሻል ዋና ዓላማህ ምንድን ነው?",
|
||||
"your_goal": "ዓላማህ የመማር ጉዞህን እንዲስማማ ለማድረግ ይረዳናል።",
|
||||
"write_your_goal": "ዓላማህን ጻፍ…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||
"skip": "ዝለል",
|
||||
"finish_level": "ደረጃውን አጠናቅቅ",
|
||||
"likely_speaker": "አንተ ምናልባት ተናጋሪ ነህ",
|
||||
"great_job": "በጣም ጥሩ ስራ! ለመሻሻል ቀጣዩ ደረጃህ ይኸው ነው።",
|
||||
"lets_start_practice": "ልምምድህን እንጀምር",
|
||||
"welcome_abroad": "እንኳን ደህና መጣህ",
|
||||
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
||||
"finish": "አጠናቅቅ",
|
||||
"finish_all_practice_lesson": "ይህን ልምምድ ለመውሰድ የቀድሞውን የትምህርት ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_module": "የሞጁሉን ልምምድ ለመውሰድ የትምህርት ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_course": "የኮርሱን ልምምድ ለመውሰድ የሞጁል ልምምዶችን ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_module": "ይህን ልምምድ ለመውሰድ የቀድሞውን የሞጁል ልምምድ ያጠናቅቁ",
|
||||
"finish_all_practice_previouse_course": "ይህን ለመውሰድ የቀድሞውን የኮርስ ልምምድ ያጠናቅቁ",
|
||||
"track_journey": "የትምህርት ጉዞዎን ይከታተሉ እና በጊዜ ሂደት ያሳዩትን እድገት ይመልከቱ።",
|
||||
"learn_english": "እንግሊዝኛ ይማሩ",
|
||||
"keep_momentum": "በጣም ጥሩ ስራ! በዚሁ ብርታት ይቀጥሉ።",
|
||||
"completed_practices": "የተጠናቀቁ ልምምዶች",
|
||||
"total_practices": "ጠቅላላ ልምምዶች",
|
||||
"progress_percentage": "የእድገት መቶኛ"
|
||||
};
|
||||
static const Map<String, Map<String,dynamic>> mapLocales = {"en": _en, "am": _am};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ abstract class LocaleKeys {
|
|||
static const cont = 'cont';
|
||||
static const register = 'register';
|
||||
static const login_with_google = 'login_with_google';
|
||||
static const login_with_apple = 'login_with_apple';
|
||||
static const or = 'or';
|
||||
static const login_with_phone = 'login_with_phone';
|
||||
static const create_account = 'create_account';
|
||||
static const already_have_account = 'already_have_account';
|
||||
static const login = 'login';
|
||||
static const register_with_google = 'register_with_google';
|
||||
static const register_with_apple = 'register_with_apple';
|
||||
static const register_with_phone = 'register_with_phone';
|
||||
static const enter_phone_number = 'enter_phone_number';
|
||||
static const login_with_email = 'login_with_email';
|
||||
|
|
@ -43,10 +45,10 @@ abstract class LocaleKeys {
|
|||
static const reset_code = 'reset_code';
|
||||
static const new_password = 'new_password';
|
||||
static const logged_in_successfully = 'logged_in_successfully';
|
||||
static const view_course = 'view_course';
|
||||
static const continue_learning = 'continue_learning';
|
||||
static const start_learning = 'start_learning';
|
||||
static const completed = 'completed';
|
||||
static const view_course = 'view_course';
|
||||
static const take_practice = 'take_practice';
|
||||
static const your_current_level = 'your_current_level';
|
||||
static const overall_progress = 'overall_progress';
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import 'package:yimaru_app/app/app.router.dart';
|
|||
import 'package:yimaru_app/models/user.dart';
|
||||
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/apple_auth_service.dart';
|
||||
import '../../../services/authentication_service.dart';
|
||||
import '../../../services/google_auth_service.dart';
|
||||
import '../../../services/localization_service.dart';
|
||||
|
|
@ -25,19 +26,23 @@ class LoginViewModel extends ReactiveViewModel
|
|||
|
||||
final _googleAuthService = locator<GoogleAuthService>();
|
||||
|
||||
final _appleAuthService = locator<AppleAuthService>();
|
||||
|
||||
final _localizationService = locator<LocalizationService>();
|
||||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_googleAuthService, _localizationService];
|
||||
[_googleAuthService, _appleAuthService, _localizationService];
|
||||
|
||||
// Google user
|
||||
GoogleSignInAccount? get _googleUser => _googleAuthService.googleUser;
|
||||
|
||||
GoogleSignInAccount? get googleUser => _googleUser;
|
||||
|
||||
bool get isAppleSignInAvailable => _appleAuthService.isSupported;
|
||||
|
||||
// Languages
|
||||
Map<String, dynamic> get _selectedLanguage =>
|
||||
_localizationService.selectedLanguage;
|
||||
|
|
@ -235,6 +240,50 @@ class LoginViewModel extends ReactiveViewModel
|
|||
Future<void> signInWithGoogle() async => await runBusyFuture(_googleAuth(),
|
||||
busyObject: StateObjects.loginWithGoogle);
|
||||
|
||||
// Sign-in with Apple
|
||||
Future<void> _appleAuth() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _appleAuthService.appleAuth();
|
||||
|
||||
final credential = _appleAuthService.appleCredential;
|
||||
final identityToken = credential?.identityToken;
|
||||
|
||||
if (identityToken == null || identityToken.isEmpty) {
|
||||
showErrorToast('Apple login failed. Please try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
'id_token': identityToken,
|
||||
'email': credential?.email,
|
||||
'first_name': credential?.givenName,
|
||||
'last_name': credential?.familyName,
|
||||
};
|
||||
|
||||
data.removeWhere((_, value) => value == null || value == '');
|
||||
|
||||
Map<String, dynamic> response = await _apiService.appleAuth(data);
|
||||
|
||||
if (response['status'] == ResponseStatus.success) {
|
||||
User user = response['data'] as User;
|
||||
Map<String, dynamic> data = {
|
||||
'userId': user.userId,
|
||||
'accessToken': user.accessToken,
|
||||
'refreshToken': user.refreshToken
|
||||
};
|
||||
await _authenticationService.saveUserCredential(data);
|
||||
clearUserData();
|
||||
await replaceWithStartUp();
|
||||
showSuccessToast(response['message']);
|
||||
} else {
|
||||
showErrorToast(response['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> signInWithApple() async => await runBusyFuture(_appleAuth(),
|
||||
busyObject: StateObjects.loginWithApple);
|
||||
|
||||
// Login with phone
|
||||
Future<void> loginWithPhoneNumber() async =>
|
||||
await runBusyFuture(_loginWithPhoneNumber(),
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ class LoginWithEmailScreen extends ViewModelWidget<LoginViewModel> {
|
|||
Stack(children: [
|
||||
_buildScaffold(context: context, viewModel: viewModel),
|
||||
_buildLoginWithEmailState(viewModel),
|
||||
_buildLoginWithGoogleState(viewModel)
|
||||
_buildLoginWithGoogleState(viewModel),
|
||||
_buildLoginWithAppleState(viewModel)
|
||||
]);
|
||||
|
||||
Widget _buildScaffold(
|
||||
|
|
@ -232,6 +233,8 @@ class LoginWithEmailScreen extends ViewModelWidget<LoginViewModel> {
|
|||
List<Widget> _buildLowerColumnChildren(LoginViewModel viewModel) => [
|
||||
_buildContinueButton(viewModel),
|
||||
_buildLoginWithGoogleButton(viewModel),
|
||||
if (viewModel.isAppleSignInAvailable)
|
||||
_buildLoginWithAppleButton(viewModel),
|
||||
_buildOptionTextDivider(),
|
||||
_buildLoginWithPhoneButton(viewModel),
|
||||
verticalSpaceMedium
|
||||
|
|
@ -265,6 +268,18 @@ class LoginWithEmailScreen extends ViewModelWidget<LoginViewModel> {
|
|||
onTap: () async => await viewModel.signInWithGoogle(),
|
||||
);
|
||||
|
||||
Widget _buildLoginWithAppleButton(LoginViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
borderRadius: 12,
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.login_with_apple.tr(),
|
||||
leadingIcon: Icons.apple,
|
||||
onTap: () async => await viewModel.signInWithApple(),
|
||||
);
|
||||
|
||||
Widget _buildOptionTextDivider() => const OptionTextDivider();
|
||||
|
||||
Widget _buildLoginWithPhoneButton(LoginViewModel viewModel) =>
|
||||
|
|
@ -287,4 +302,9 @@ class LoginWithEmailScreen extends ViewModelWidget<LoginViewModel> {
|
|||
viewModel.busy(StateObjects.loginWithGoogle)
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
|
||||
Widget _buildLoginWithAppleState(LoginViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.loginWithApple)
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
|||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/user.dart';
|
||||
import '../../../services/apple_auth_service.dart';
|
||||
import '../../../services/google_auth_service.dart';
|
||||
import '../../../services/localization_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
|
@ -25,19 +26,23 @@ class RegisterViewModel extends ReactiveViewModel
|
|||
|
||||
final _googleAuthService = locator<GoogleAuthService>();
|
||||
|
||||
final _appleAuthService = locator<AppleAuthService>();
|
||||
|
||||
final _localizationService = locator<LocalizationService>();
|
||||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_googleAuthService, _localizationService];
|
||||
[_googleAuthService, _appleAuthService, _localizationService];
|
||||
|
||||
// Google user
|
||||
GoogleSignInAccount? get _googleUser => _googleAuthService.googleUser;
|
||||
|
||||
GoogleSignInAccount? get googleUser => _googleUser;
|
||||
|
||||
bool get isAppleSignInAvailable => _appleAuthService.isSupported;
|
||||
|
||||
// Languages
|
||||
Map<String, dynamic> get _selectedLanguage =>
|
||||
_localizationService.selectedLanguage;
|
||||
|
|
@ -337,6 +342,50 @@ class RegisterViewModel extends ReactiveViewModel
|
|||
}
|
||||
}
|
||||
|
||||
// Register with Apple
|
||||
Future<void> registerWithApple() async => await runBusyFuture(_appleLogin(),
|
||||
busyObject: StateObjects.registerWithApple);
|
||||
|
||||
Future<void> _appleLogin() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
await _appleAuthService.appleAuth();
|
||||
|
||||
final credential = _appleAuthService.appleCredential;
|
||||
final identityToken = credential?.identityToken;
|
||||
|
||||
if (identityToken == null || identityToken.isEmpty) {
|
||||
showErrorToast('Apple login failed. Please try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
'id_token': identityToken,
|
||||
'email': credential?.email,
|
||||
'first_name': credential?.givenName,
|
||||
'last_name': credential?.familyName,
|
||||
};
|
||||
|
||||
data.removeWhere((_, value) => value == null || value == '');
|
||||
|
||||
Map<String, dynamic> response = await _apiService.appleAuth(data);
|
||||
|
||||
if (response['status'] == ResponseStatus.success) {
|
||||
User user = response['data'] as User;
|
||||
Map<String, dynamic> data = {
|
||||
'userId': user.userId,
|
||||
'accessToken': user.accessToken,
|
||||
'refreshToken': user.refreshToken
|
||||
};
|
||||
await _authenticationService.saveUserCredential(data);
|
||||
clearUserData();
|
||||
await replaceWithStartUp();
|
||||
showSuccessToast(response['message']);
|
||||
} else {
|
||||
showErrorToast(response['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> verifyOtp() async =>
|
||||
await runBusyFuture(_verifyOtp(), busyObject: StateObjects.verifyOtp);
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ class RegisterWithEmailScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
Stack(
|
||||
children: [
|
||||
_buildScaffold(context: context, viewModel: viewModel),
|
||||
_buildRegisterWithEmailState(viewModel)
|
||||
_buildRegisterWithEmailState(viewModel),
|
||||
_buildRegisterWithGoogleState(viewModel),
|
||||
_buildRegisterWithAppleState(viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -191,6 +193,8 @@ class RegisterWithEmailScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
List<Widget> _buildLowerColumnChildren(RegisterViewModel viewModel) => [
|
||||
_buildContinueButton(viewModel),
|
||||
_buildRegisterWithGoogleButton(viewModel),
|
||||
if (viewModel.isAppleSignInAvailable)
|
||||
_buildRegisterWithAppleButton(viewModel),
|
||||
_buildOptionTextDivider(),
|
||||
_buildRegisterWithEmailButton(viewModel),
|
||||
verticalSpaceMedium
|
||||
|
|
@ -225,6 +229,18 @@ class RegisterWithEmailScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
onTap: () async => await viewModel.registerWithGoogle(),
|
||||
);
|
||||
|
||||
Widget _buildRegisterWithAppleButton(RegisterViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
borderRadius: 12,
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.register_with_apple.tr(),
|
||||
leadingIcon: Icons.apple,
|
||||
onTap: () async => await viewModel.registerWithApple(),
|
||||
);
|
||||
|
||||
Widget _buildOptionTextDivider() => const OptionTextDivider();
|
||||
|
||||
Widget _buildRegisterWithEmailButton(RegisterViewModel viewModel) =>
|
||||
|
|
@ -243,4 +259,14 @@ class RegisterWithEmailScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
viewModel.busy(StateObjects.register)
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
|
||||
Widget _buildRegisterWithGoogleState(RegisterViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.registerWithGoogle)
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
|
||||
Widget _buildRegisterWithAppleState(RegisterViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.registerWithApple)
|
||||
? const PageLoadingIndicator()
|
||||
: Container();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import google_sign_in_ios
|
|||
import package_info_plus
|
||||
import record_macos
|
||||
import shared_preferences_foundation
|
||||
import sign_in_with_apple
|
||||
import sqflite_darwin
|
||||
import url_launcher_macos
|
||||
import video_player_avfoundation
|
||||
|
|
@ -35,6 +36,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
||||
|
|
|
|||
28
pubspec.lock
28
pubspec.lock
|
|
@ -1557,6 +1557,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
sign_in_with_apple:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sign_in_with_apple
|
||||
sha256: "5568378c3cc5993931955357d85e4c3344fa4365006915bdef965fa3de1dc0a5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
sign_in_with_apple_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple_platform_interface
|
||||
sha256: "981bca52cf3bb9c3ad7ef44aace2d543e5c468bb713fd8dda4275ff76dfa6659"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
sign_in_with_apple_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple_web
|
||||
sha256: f316400827f52cafcf50d00e1a2e8a0abc534ca1264e856a81c5f06bd5b10fed
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
@ -2011,5 +2035,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.3 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
dart: ">=3.11.0 <4.0.0"
|
||||
flutter: ">=3.41.0"
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ dependencies:
|
|||
flutter_phone_direct_caller: ^2.2.1
|
||||
flutter_local_notifications: ^20.1.0
|
||||
internet_connection_checker_plus: ^2.9.1+2
|
||||
sign_in_with_apple: ^8.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user