diff --git a/assets/images/home/Chart-1.png b/assets/images/home/Chart-1.png new file mode 100644 index 0000000..7e46da9 Binary files /dev/null and b/assets/images/home/Chart-1.png differ diff --git a/assets/images/home/Chart_new.png b/assets/images/home/Chart_new.png index a87757f..b3d17ac 100644 Binary files a/assets/images/home/Chart_new.png and b/assets/images/home/Chart_new.png differ diff --git a/lib/languages.dart b/lib/languages.dart index 9c874b3..92ab4fd 100644 --- a/lib/languages.dart +++ b/lib/languages.dart @@ -3,9 +3,299 @@ import 'package:get/get.dart'; class Languages extends Translations { @override Map> get keys => { - 'ko_KR': {'title': '제목'}, - 'en_US': {'title': 'title'}, - 'ja_JP': {'title': '題名'}, - 'vi_VN': {'title': 'Tiêu đề'}, + 'ko_KR': { + // ---------------------- HOME ---------------------- + 'login': '로그인', + 'signup': '회원가입', + 'email': '이메일', + 'password': '비밀번호', + 'forgot_password': '비밀번호를 잊으셨나요?', + 'message1': "발음 연습해볼까요? 오늘도 화이팅!", + 'message2': "오늘은 어떤 단어를 배워볼까요?", + 'message3': "오늘도 화이팅!", + 'message4': "꾸준함이 능력! 꾸준히 노력하자!", + 'homeLanguage': '한국어', + 'homeHeaderGuest': '로그인이 필요합니다', + 'speakingAbility1': "발음", + 'speakingAbility2': "높낮이", + 'home_script_title': "대본으로 학습하기", + 'home_script_subtitle': "대본 입력 및 발음 테스트", + 'home_live_script_title': "실시간 발음테스트", + 'home_live_script_subtitle': "실시간 대본 입력 및 발음 테스트", + 'home_chart_title': "진행한 학습", + // ---------------------- MYPAGE ---------------------- + 'mypage_title': '마이페이지', + 'mypage_menu1': '시스템 및 학습 언어 설정', + 'mypage_menu2': '계정 관리', + 'mypage_menu3': '버전 정보', + 'mypage_menu4': '문의', + 'mypage_user_title': '님 반가워요!', + 'mypage_go_login': '로그인', + // ---------------------- MYPAGE - 언어 설정 ---------------------- + 'language_setting_title': '언어 설정', + 'language_setting_system': '시스템 언어', + 'language_setting_learning': '학습 언어', + + 'korean': '한국어', + 'english': '영어', + // ---------------------- Login ---------------------- + 'google_login': '구글 계정으로 로그인', + 'google_login_fail_title': '구글 로그인에 실패했습니다', + 'google_login_fail_content': '다시 시도해주세요', + 'signout_fail_title': '회원 탈퇴 실패', + 'signout_fail_content': '회원 탈퇴에 실패했습니다. 다시 시도해주세요.', + 'email_login': '이메일 계정으로 로그인', + // ---------------------- MYPAGE ---------------------- + 'check_network_connection': '네트워크 연결을 확인해주세요', + 'appbar_back': '뒤로', + ///// --- -밑에서 부터 추가하면 됨 + 'appbar_done': '완료', + 'language_setting_snackbar_title': '언어 설정이 변경', + 'language_setting_snackbar_content': '언어 설정이 변경되었습니다.', + 'language_setting_snackbar_fail_title': '언어 설정 변경 실패', + 'language_setting_snackbar_fail_content': + '언어 설정 변경에 실패했습니다. 다시 시도해주세요.', + // --------------------- live script --------------------- + 'live_script_title': '실시간 발음 테스트', + 'live_script_hint': '대본을 입력하세요...', + 'analyze_appbar_title': '발음 및 빠르기 분석', + 'analyze_home': '홈으로', + 'analyze_exmaple_title': '발음이 틀린 글씨는 빨간색으로 표시됩니다 ex)', + 'analyze_exmaple_content': '강아지는 ', + 'analyze_exmaple_content2': '뛴다', + 'analyze_exmaple_alert': '문장이 빠르면 빨강, 느리면 보라색 밑줄로 표시됩니다.', + 'profile_account_title': '계정 관리', + 'profile_account_logout': '로그아웃', + 'profile_account_logout_alert_title': '로그아웃', + 'profile_account_logout_alert_content': '정말로 로그아웃하시겠습니까?', + 'profile_account_logout_alert_cancel': '취소', + 'profile_account_signout': '회원 탈퇴', + 'profile_account_signout_alert_title': '회원 탈퇴', + 'profile_account_signout_alert_content': '정말로 탈퇴하시겠습니까?', + 'profile_account_signout_alert_cancel': '취소', + 'profile_account_signout_alert_confirm': '탈퇴', + 'login_fail_snackar_title': '로그인 실패', + 'login_fail_snackar_content': '로그인에 실패했습니다. 다시 시도해주세요.', + 'fail_study_title': '학습 실패', + 'fail_study_content': '다시 녹음해주세요.', + 'fail_study_retry': '다시하기.', + 'fail_study_next_word': '다음 단어', + 'fail_word': '틀린단어 없음', + 'no_data': '데이터가 없습니다.', + 'word_type_2_height': '문장의 높낮이를 참고하세요', + 'word_type_12_height': '아래의 혀, 입술 모양을 따라 말해보세요!', + 'result_title': '결과', + 'result_content': '결과를 확인하세요', + 'pitch': '발음', + 'pitch_null': '너무 빠르거나 느립니다. 다시 녹음해주세요.', + 'accuracy': '정확도', + 'cofirm': '확인', + // -------------------------------------------------------- 12:56 + 'success_study_title': '학습 완료', + 'success_study_content': '다음 단어로 이동하시겠습니까?', + // 문장 테스트 결과 + 'sentence_test_result_title': '문장 테스트 결과', + 'sentence_test_result_sentence': '문장', + 'sentence_test_result_user_sentence': '사용자 발음', + 'sentence_test_result_wrong': '틀린 부분', + // 알 수 없음 + 'unknown': '알 수 없음', + 'sentence_test_result_speed': '속도', + 'sentence_test_result_volume': '볼륨', + 'sentence_test_result_pitch': '변동성', + 'sentence_test_result_stability': '일정함', + 'sentence_test_result_pitch_high': '변동 폭 큼', + 'next': '다음', + 'sentence_test_result_speed_very_slow': '엄청 느림', + 'sentence_test_result_speed_slow': '느림', + 'sentence_test_result_speed_normal': '보통', + 'sentence_test_result_speed_fast': '빠름', + 'sentence_test_result_speed_very_fast': '엄청 빠름', + 'sound_pitch': '목소리 높이', + 'sound_pitch_stability': '변동 일정', + 'sound_pitch_high': '변동', + 'sound_speed': '속도', + 'study_main_title_1': '음소 교정', + 'study_main_subtitle_1': '음소 교정 및 발음 테스트', + 'study_main_title_2': '단어 교정', + 'study_main_subtitle_2': '단어 교정 및 발음 테스트', + 'study_main_title_3': '문장 교정', + 'study_main_subtitle_3': '문장 교정 및 발음 테스트', + 'study_main_title_4': '문단 교정', + 'study_main_subtitle_4': '대본 입력 및 발음 테스트', + 'row_card_badge_1': "음소", + 'row_card_badge_2': "단어", + 'row_card_badge_3': "문장", + 'row_card_badge_4': "문단", + 'go_study': '학습 하러가기', + 'no_study_record': '등록된 학습 기록이 없습니다.', + 'voice_recognition': '음성 인식을 통해 발음이 여기에 표시됩니다...', + 'email_signup': '이메일 회원가입', + 'email_login_2': '이메일 로그인', + 'go_signup': '회원가입 하러가기', + 'signup_fail_title': '회원가입 실패', + 'signup_fail_content': '회원가입에 실패했습니다. 다시 시도해주세요.', + 'email_address': '이메일 주소를 입력해주세요', + 'password_input': '비밀번호를 입력해주세요', + 'email_address_input': '올바른 이메일 주소를 입력해주세요', + 'password_input_2': '비밀번호는 10자리 이상이어야 합니다.', + 'password_input_3': '비밀번호는 문자와 숫자가 섞여있어야 합니다.', + 'study_main_title_5': '문단 교정', + }, + 'en_US': { + // ---------------------- HOME ---------------------- + 'login': 'Login', + 'signup': 'Sign Up', + 'email': 'Email', + 'password': 'Password', + 'forgot_password': 'Forgot Password?', + 'message1': "Let's practice pronunciation! Fighting today too!", + 'message2': "What word shall we learn today?", + 'message3': "Fighting today too!", + 'message4': "Consistency is ability! Let's make steady efforts!", + 'homeLanguage': 'English', + 'homeHeaderGuest': 'Login required', + 'speakingAbility1': "Pronun", + 'speakingAbility2': "Pitch", + 'home_script_title': "Learn with Scripts", + 'home_script_subtitle': "Script test", + 'home_live_script_title': "live Test", + 'home_live_script_subtitle': "live script test", + 'home_chart_title': "Progressed Learning", + // ---------------------- MYPAGE ---------------------- + 'mypage_title': 'My Page', + 'mypage_menu1': 'System and Learning Language Settings', + 'mypage_menu2': 'Account Management', + 'mypage_menu3': 'Version Information', + 'mypage_menu4': 'Contact', + 'mypage_user_title': 'Hello,', + 'mypage_go_login': 'Login', + // ---------------------- MYPAGE - Language Settings ---------------------- + 'language_setting_title': 'Language Settings', + 'language_setting_system': 'System Language', + 'language_setting_learning': 'Learning Language', + 'korean': 'Korean', + 'english': 'English', + // ---------------------- Login ---------------------- + 'google_login': 'Login with Google Account', + 'google_login_fail_title': 'Google Login Failed', + 'google_login_fail_content': 'Please try again.', + 'signout_fail_title': 'Sign Out Failed', + 'signout_fail_content': 'Failed to sign out. Please try again.', + 'email_login': 'Login with Email Account', + // ---------------------- MYPAGE ---------------------- + 'check_network_connection': 'Please check your network connection', + 'appbar_back': 'Back', + // ---------------------- Additional Translations ---------------------- + 'appbar_done': 'Done', + 'language_setting_snackbar_title': 'Language Setting Changed', + 'language_setting_snackbar_content': + 'Language setting has been changed.', + 'language_setting_snackbar_fail_title': + 'Language Setting Change Failed', + 'language_setting_snackbar_fail_content': + 'Failed to change language setting. Please try again.', + // --------------------- Live Script --------------------- + 'live_script_title': 'Real-time Pronunciation Test', + 'live_script_hint': 'Enter the script...', + 'analyze_appbar_title': 'Pronunciation and Speed Analysis', + 'analyze_home': 'Back to Home', + 'analyze_exmaple_title': + 'Incorrect pronunciation is highlighted in red, for example:', + 'analyze_exmaple_content': 'The dog ', + 'analyze_exmaple_content2': 'runs', + 'analyze_exmaple_alert': + 'If the sentence is fast, it will be marked with a red underline; if it is slow, with a purple underline.', + 'profile_account_title': 'Account Management', + 'profile_account_logout': 'Logout', + 'profile_account_logout_alert_title': 'Logout', + 'profile_account_logout_alert_content': + 'Are you sure you want to log out?', + 'profile_account_logout_alert_cancel': 'Cancel', + 'profile_account_signout': 'Sign Out', + 'profile_account_signout_alert_title': 'Sign Out', + 'profile_account_signout_alert_content': + 'Are you sure you want to sign out?', + 'profile_account_signout_alert_cancel': 'Cancel', + 'profile_account_signout_alert_confirm': 'Sign Out', + 'login_fail_snackar_title': 'Login Failed', + 'login_fail_snackar_content': 'Login failed. Please try again.', + // Fail Study + 'fail_study_title': 'Study Failed', + 'fail_study_content': 'Please record again.', + 'fail_study_retry': 'Retry', + 'fail_study_next_word': 'Next Word', + // Fail Word + 'fail_word': 'No Incorrect Words', + 'no_data': 'No Data Available.', + // Word Types + 'word_type_2_height': 'Refer to the pitch of the sentence.', + 'word_type_12_height': + 'Speak following the tongue and lip shapes below!', + // Results + 'result_title': 'Results', + 'result_content': 'Check your results', + 'pitch': 'Pronunciation', + 'pitch_null': 'Too fast or too slow. Please record again.', + 'accuracy': 'Accuracy', + 'confirm': 'Confirm', + ////// ------------------------------------------------ 12:56 + /// // Success Study + 'success_study_title': 'Study Completed', + 'success_study_content': + 'Would you like to move on to the next word?', + // Sentence Test Result + 'sentence_test_result_title': 'Sentence Test Result', + 'sentence_test_result_sentence': 'Sentence', + 'sentence_test_result_user_sentence': 'User Pronunciation', + 'sentence_test_result_wrong': 'Incorrect Part', + // Unknown + 'unknown': 'Unknown', + 'sentence_test_result_speed': 'Speed', + 'sentence_test_result_volume': 'Volume', + 'sentence_test_result_pitch': 'Pitch', + 'sentence_test_result_stability': 'Stability', + 'sentence_test_result_pitch_high': 'High Variability', + 'next': 'Next', + 'sentence_test_result_speed_very_slow': 'Very Slow', + 'sentence_test_result_speed_slow': 'Slow', + 'sentence_test_result_speed_normal': 'Normal', + 'sentence_test_result_speed_fast': 'Fast', + 'sentence_test_result_speed_very_fast': 'Very Fast', + 'sound_pitch': 'Voice Pitch', + 'sound_pitch_stability': 'Pitch Stability', + 'sound_pitch_high': 'High Variability', + 'sound_speed': 'Speed', + // Study Main Titles and Subtitles + 'study_main_title_1': 'Phoneme Correction', + 'study_main_subtitle_1': 'Phoneme Correction and Pronunciation Test', + 'study_main_title_2': 'Word Correction', + 'study_main_subtitle_2': 'Word Correction and Pronunciation Test', + 'study_main_title_3': 'Sentence Correction', + 'study_main_subtitle_3': 'Sentence Correction and Pronunciation Test', + 'study_main_title_4': 'Paragraph Correction', + 'study_main_subtitle_4': 'Script Input and Pronunciation Test', + 'row_card_badge_1': 'Phoneme', + 'row_card_badge_2': 'Word', + 'row_card_badge_3': 'Sentence', + 'row_card_badge_4': 'Paragraph', + 'go_study': 'Go to Study', + 'no_study_record': 'No registered study records.', + 'voice_recognition': + 'Pronunciation through voice recognition will be displayed here...', + // Email Signup and Login + 'email_signup': 'Email Sign Up', + 'email_login_2': 'Email Login', + 'go_signup': 'Go to Sign Up', + 'signup_fail_title': 'Sign Up Failed', + 'signup_fail_content': 'Failed to sign up. Please try again.', + 'email_address': 'Please enter your email address', + 'password_input': 'Please enter your password', + 'email_address_input': 'Please enter a valid email address', + 'password_input_2': 'Password must be at least 10 characters.', + 'password_input_3': + 'Password must contain a mix of letters and numbers.', + 'study_main_title_5': 'Paragraph Correction', + }, }; } diff --git a/lib/main_app.dart b/lib/main_app.dart index e60d734..e947375 100644 --- a/lib/main_app.dart +++ b/lib/main_app.dart @@ -1,5 +1,5 @@ +import 'package:earlips/languages.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:get/get.dart'; import 'package:earlips/bindings/root_binding.dart'; @@ -21,15 +21,9 @@ class MainApp extends StatelessWidget { return GetMaterialApp( title: "earlips", - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: const [ - Locale('ko', 'KR'), - Locale('en', 'US'), - ], + translations: Languages(), + locale: Get.deviceLocale, + fallbackLocale: const Locale('en', 'US'), theme: ThemeData( useMaterial3: true, fontFamily: 'Pretendard', diff --git a/lib/services/auth/auth_service.dart b/lib/services/auth/auth_service.dart index 543ad26..c614aef 100644 --- a/lib/services/auth/auth_service.dart +++ b/lib/services/auth/auth_service.dart @@ -73,8 +73,8 @@ class AuthService { } catch (error) { // 에러 발생시 에러 메시지 출력 Get.snackbar( - "구글 로그인 실패", - "구글 로그인에 실패했습니다. 다시 시도해주세요.", + 'google_login_fail_title'.tr, + 'google_login_fail_content'.tr, snackPosition: SnackPosition.TOP, ); } @@ -100,8 +100,8 @@ class AuthService { } catch (error) { // 에러 발생시 에러 메시지 출력 Get.snackbar( - "회원 탈퇴 실패", - "회원 탈퇴에 실패했습니다. 다시 시도해주세요.", + 'signout_fail_title'.tr, + 'signout_fail_content'.tr, snackPosition: SnackPosition.TOP, ); } diff --git a/lib/utilities/style/color_styles.dart b/lib/utilities/style/color_styles.dart index f499116..1538302 100644 --- a/lib/utilities/style/color_styles.dart +++ b/lib/utilities/style/color_styles.dart @@ -16,7 +16,6 @@ class ColorSystem { static const Color white = Color(0xFFFFFFFF); static const Color green = Color(0xFF63DC68); static const Color green2 = Color(0xFFD5E9AA); - static const Color yellow = Color(0xFFFFC100); static const Color red = Color(0xFFFB5D5D); static const Color background = Color(0xFFF0F4F8); diff --git a/lib/utilities/validators/auth_validators.dart b/lib/utilities/validators/auth_validators.dart index 6e4315e..4e56b08 100644 --- a/lib/utilities/validators/auth_validators.dart +++ b/lib/utilities/validators/auth_validators.dart @@ -1,8 +1,10 @@ +import 'package:get/get_utils/src/extensions/internacionalization.dart'; + class AuthValidators { // Email Validator static String? emailValidator(String? value) { if (value == null || value.isEmpty) { - return '이메일 주소를 입력해주세요'; + return 'email_address'.tr; } // Email 정규식 final RegExp emailRegex = RegExp( @@ -10,7 +12,7 @@ class AuthValidators { ); if (!emailRegex.hasMatch(value)) { - return '올바른 이메일 주소를 입력해주세요'; + return 'email_address_input'.tr; } return null; } @@ -19,12 +21,12 @@ class AuthValidators { // 대충.. 10자리 이상, 문자와 숫자가 섞여있어야 함 static String? passwordValidator(String? value) { if (value == null || value.isEmpty) { - return '비밀번호를 입력해주세요'; + return 'password_input'.tr; } else if (value.length < 10) { - return '비밀번호는 10자리 이상이어야 합니다'; + return 'password_input_2'.tr; } else if (!RegExp(r'^(?=.*?[a-zA-Z])(?=.*?[0-9]).{10,}$') .hasMatch(value)) { - return '비밀번호는 문자와 숫자가 섞여있어야 합니다'; + return 'password_input_3'.tr; } return null; } diff --git a/lib/viewModels/Paragraph/learning_session_screen_viewmodel.dart b/lib/viewModels/Paragraph/learning_session_screen_viewmodel.dart index 377e418..491cdfd 100644 --- a/lib/viewModels/Paragraph/learning_session_screen_viewmodel.dart +++ b/lib/viewModels/Paragraph/learning_session_screen_viewmodel.dart @@ -1,20 +1,15 @@ import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:get/get_rx/src/rx_types/rx_types.dart'; import 'package:get/get_state_manager/src/simple/get_controllers.dart'; - - class LearningSessionScreenViewModel extends GetxController { final FirebaseFirestore _firestore = FirebaseFirestore.instance; - final FirebaseAuth _auth = FirebaseAuth.instance; final RxBool isLoading = false.obs; // 로딩 상태 관리 final RxList paragraphs = [].obs; // Paragraph 객체 리스트 // Firestore에서 paragraphs 컬렉션의 데이터를 가져오는 함수 Future fetchParagraphs() async { - final uid = _auth.currentUser?.uid; // 사용자 UID 가져오기 try { isLoading(true); // 로딩 시작 final QuerySnapshot paragraphSnapshot = await _firestore @@ -22,21 +17,18 @@ class LearningSessionScreenViewModel extends GetxController { .limit(5) // 예제로 5개의 문서만 가져오기 .get(); - final List fetchedParagraphs = paragraphSnapshot.docs - .map((doc) { + final List fetchedParagraphs = + paragraphSnapshot.docs.map((doc) { // doc.data() 호출 결과를 Map으로 타입 캐스팅 final data = doc.data() as Map?; // Null-safety를 고려하여, 필드에 접근하기 전에 null 체크 final title = data?['title'] as String? ?? ''; // title이 없으면 빈 문자열 할당 final text = data?['text'] as String? ?? ''; // text가 없으면 빈 문자열 할당 return Paragraph(title: title, text: text); - }) - .toList(); - + }).toList(); paragraphs.value = fetchedParagraphs; // 상태 업데이트 - } catch (e) { - print("Error fetching paragraphs: $e"); // 오류 처리 + } catch (_) { } finally { isLoading(false); // 로딩 종료 } diff --git a/lib/viewModels/auth/email_login_viewmodel.dart b/lib/viewModels/auth/email_login_viewmodel.dart index bc032f8..66157ec 100644 --- a/lib/viewModels/auth/email_login_viewmodel.dart +++ b/lib/viewModels/auth/email_login_viewmodel.dart @@ -39,8 +39,11 @@ class EmailLoginViewModel extends GetxController { // 로그인 성공시 홈화면으로 이동 Get.offAllNamed('/'); } on FirebaseAuthException catch (_) { - Get.snackbar('로그인 실패', "로그인에 실패했습니다. 다시 시도해주세요.", - snackPosition: SnackPosition.TOP); + Get.snackbar( + 'login_fail_snackar_title'.tr, + 'login_fail_snackar_message'.tr, + snackPosition: SnackPosition.TOP, + ); } } } diff --git a/lib/viewModels/auth/email_signup_viewmodel.dart b/lib/viewModels/auth/email_signup_viewmodel.dart index 99e0bf5..68ca6b1 100644 --- a/lib/viewModels/auth/email_signup_viewmodel.dart +++ b/lib/viewModels/auth/email_signup_viewmodel.dart @@ -55,8 +55,8 @@ class EmailSignupViewModel extends GetxController { Get.back(); } on FirebaseAuthException catch (_) { Get.snackbar( - "회원가입 실패", - "회원가입에 실패했습니다. 다시 시도해주세요.", + "signup_fail_title".tr, + "signup_fail_content".tr, snackPosition: SnackPosition.TOP, ); } diff --git a/lib/viewModels/record/record_viewmodel.dart b/lib/viewModels/record/record_viewmodel.dart index 025b373..6d9dbc2 100644 --- a/lib/viewModels/record/record_viewmodel.dart +++ b/lib/viewModels/record/record_viewmodel.dart @@ -46,7 +46,6 @@ class RecordViewModel extends GetxController { final filePath = '${directory.path}/${DateTime.now().millisecondsSinceEpoch}.aac'; - print(filePath); try { await _audioRecorder.startRecorder( toFile: filePath, @@ -54,7 +53,6 @@ class RecordViewModel extends GetxController { ); isRecording.value = true; - print('Recording started'); } catch (_) {} } diff --git a/lib/viewModels/root/root_viewmodel.dart b/lib/viewModels/root/root_viewmodel.dart index 1f1be0c..8ee573f 100644 --- a/lib/viewModels/root/root_viewmodel.dart +++ b/lib/viewModels/root/root_viewmodel.dart @@ -1,11 +1,6 @@ import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; -import 'package:get/get_rx/src/rx_types/rx_types.dart'; - -import 'package:get/get_state_manager/src/simple/get_controllers.dart'; import 'package:earlips/utilities/app_routes.dart'; - class RootViewModel extends GetxController { late final RxInt _selectedIndex; @@ -15,7 +10,6 @@ class RootViewModel extends GetxController { void onInit() { super.onInit(); _selectedIndex = 0.obs; - } void changeIndex(int index) { @@ -25,5 +19,4 @@ class RootViewModel extends GetxController { void onTapBed() { Get.toNamed(Routes.HOME); } - } diff --git a/lib/viewModels/script/analyze_viewmodel.dart b/lib/viewModels/script/analyze_viewmodel.dart index 40ab365..3e735ed 100644 --- a/lib/viewModels/script/analyze_viewmodel.dart +++ b/lib/viewModels/script/analyze_viewmodel.dart @@ -1,7 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_sound/flutter_sound.dart'; -import 'package:http/http.dart' as http; -import 'dart:convert'; // JSON 데이터를 다루기 위해 필요 + class AnalyzeViewModel with ChangeNotifier { List userWord = []; List userSenten = []; @@ -10,17 +8,15 @@ class AnalyzeViewModel with ChangeNotifier { void updateData(Map data) { userWord = List.from(data['user_word'] as List? ?? []); - userSenten = List.from(data['user_sentence'] as List? ?? []); + userSenten = + List.from(data['user_sentence'] as List? ?? []); wrongWordIndexes = List.from(data['wrong'] as List? ?? []); wrongFastIndexes = (data['speed'] as List? ?? []).map((e) { - if (e is double) { return e; } else if (e is int) { return e.toDouble(); } else { - // 로그 출력 또는 오류 처리 - print("Warning: Invalid type in 'speed' list, defaulting to 0.0"); return 0.0; // 기본값 } }).toList(); diff --git a/lib/viewModels/script/create_script_viewmodel.dart b/lib/viewModels/script/create_script_viewmodel.dart index 211f4bf..a4e9ee7 100644 --- a/lib/viewModels/script/create_script_viewmodel.dart +++ b/lib/viewModels/script/create_script_viewmodel.dart @@ -24,8 +24,6 @@ class CreateScriptViewModel extends ChangeNotifier { bool get isLoggedIn => FirebaseAuth.instance.currentUser != null; final FirebaseAuth _auth = FirebaseAuth.instance; - - bool handDone = false; TextEditingController writedTextController = TextEditingController(); // 사용자 입력을 위한 컨트롤러 @@ -74,20 +72,16 @@ class CreateScriptViewModel extends ChangeNotifier { final path = await _audioRecorder!.stopRecorder(); audioFilePath = path!; _isRecording = false; - print('Recording stopped, file path: $path'); return path; // 녹음이 중지된 파일의 경로를 반환합니다. } catch (e) { - print('Error stopping recorder: $e'); return null; } } Future sendTextAndAudio() async { - String url = '${dotenv.env['API_URL']!}/script'; String textToSend = writedTextController.text; if (audioFilePath.isEmpty) { - print('Audio file is not available.'); return; } @@ -101,7 +95,6 @@ class CreateScriptViewModel extends ChangeNotifier { if (response.statusCode == 200) { final respStr = await response.stream.bytesToString(); final jsonResponse = json.decode(respStr); - print('Server response: $respStr'); if (jsonResponse != null) { final analyzeViewModel = Get.find(); @@ -114,24 +107,14 @@ class CreateScriptViewModel extends ChangeNotifier { 'speed': jsonResponse['speed'], }); - Get.to(() => AnalyzeScreen()); // AnalyzeScreen으로 이동 - } else { - print('Received null or invalid data from the server.'); - } - } else { - print( - 'Failed to send data and audio. Status code: ${response.statusCode}'); - } - } catch (e) { - print(e.toString()); - } + Get.to(() => const AnalyzeScreen()); // AnalyzeScreen으로 이동 + } else {} + } else {} + } catch (_) {} if (!isLoggedIn) { - print('User is not logged in.'); - }else { - print('User is logged in.'); + } else { saveDataToFirestore(); } - } void _handleStatus(String status) { @@ -163,7 +146,6 @@ class CreateScriptViewModel extends ChangeNotifier { Future startListening() async { bool available = await speechToText.initialize(); if (!available) { - print("The user has denied the use of speech recognition."); return; } speechToText.listen( @@ -184,18 +166,17 @@ class CreateScriptViewModel extends ChangeNotifier { } void complete() async { - print("완료버튼 눌렀습니다."); await sendTextAndAudio(); // 비동기 호출로 수정 } Future saveDataToFirestore() async { final uid = _auth.currentUser?.uid; if (!isLoggedIn) { - print("User is not logged in."); return; } final textToSend = writedTextController.text; - final title = textToSend.length > 5 ? textToSend.substring(0, 5) + '...' : textToSend; + final title = + textToSend.length > 5 ? '${textToSend.substring(0, 5)}...' : textToSend; final dateFormat = Timestamp.now(); // 현재 시간을 Timestamp 형태로 저장 final documentReference = FirebaseFirestore.instance @@ -204,18 +185,14 @@ class CreateScriptViewModel extends ChangeNotifier { .collection('paragraph') .doc(); // 새 문서 ID를 자동 생성 - await documentReference.set({ - 'dateFormat': dateFormat, - 'text': textToSend, - 'title': title, - }).then((_) { - print("Data saved to Firestore successfully."); - print(dateFormat); - print(textToSend); - print(uid); - }).catchError((error) { - print("Failed to save data to Firestore: $error"); - }); + await documentReference + .set({ + 'dateFormat': dateFormat, + 'text': textToSend, + 'title': title, + }) + .then((_) {}) + .catchError((_) {}); } @override diff --git a/lib/viewModels/script/learning_session_screen_viewmodel.dart b/lib/viewModels/script/learning_session_screen_viewmodel.dart index 7ca51ae..2aafbc8 100644 --- a/lib/viewModels/script/learning_session_screen_viewmodel.dart +++ b/lib/viewModels/script/learning_session_screen_viewmodel.dart @@ -2,11 +2,8 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:get/get_rx/src/rx_types/rx_types.dart'; import 'package:get/get_state_manager/src/simple/get_controllers.dart'; - import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:intl/intl.dart'; - - class LearningSessionScreenViewModel extends GetxController { final FirebaseFirestore _firestore = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; @@ -27,26 +24,26 @@ class LearningSessionScreenViewModel extends GetxController { .orderBy('dateFormat', descending: true) // DateTime 기준으로 정렬 .get(); - final List fetchedParagraphs = paragraphSnapshot.docs - .map((doc) { + final List fetchedParagraphs = + paragraphSnapshot.docs.map((doc) { // doc.data() 호출 결과를 Map으로 타입 캐스팅 final data = doc.data() as Map?; // Null-safety를 고려하여, 필드에 접근하기 전에 null 체크 final title = data?['title'] as String? ?? ''; // title이 없으면 빈 문자열 할당 final text = data?['text'] as String? ?? ''; // text가 없으면 빈 문자열 할당 // dateFormat 필드를 DateTime으로 변환 - final dateFormat = data?['dateFormat']?.toDate() ?? DateTime.now(); // dateFormat이 없으면 현재 시간 할당 + final dateFormat = data?['dateFormat']?.toDate() ?? + DateTime.now(); // dateFormat이 없으면 현재 시간 할당 // DateTime 객체를 원하는 문자열 형식으로 변환 final formattedDate = DateFormat('yyyy/MM/dd').format(dateFormat); - return Paragraph(title: title, text: text, dateFormat: formattedDate.toString()); - }) - .toList(); + return Paragraph( + title: title, text: text, dateFormat: formattedDate.toString()); + }).toList(); paragraphs.value = fetchedParagraphs; // 상태 업데이트 - } catch (e) { - print("Error fetching paragraphs: $e"); // 오류 처리 + } catch (_) { } finally { isLoading(false); // 로딩 종료 } @@ -58,5 +55,6 @@ class Paragraph { final String title; final String text; final String dateFormat; - Paragraph({required this.title, required this.text, required this.dateFormat}); + Paragraph( + {required this.title, required this.text, required this.dateFormat}); } diff --git a/lib/viewModels/user/user_viewmodel.dart b/lib/viewModels/user/user_viewmodel.dart index 61a515a..658fc0d 100644 --- a/lib/viewModels/user/user_viewmodel.dart +++ b/lib/viewModels/user/user_viewmodel.dart @@ -2,11 +2,12 @@ import 'dart:math'; import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:get/get.dart'; +import 'package:intl/intl.dart'; class UserViewModel extends GetxController { final FirebaseAuth _auth = FirebaseAuth.instance; @@ -53,22 +54,11 @@ class UserViewModel extends GetxController { Future getUserData() async { if (uid != null) { - // // users 컬렉션의 하위 컬렉션에 단어 완료 정보 삽입 - // for (final word in wordList) { - // await FirebaseFirestore.instance - // .collection('users') - // .doc(uid) - // .collection('words') - // .doc(word.id.toString()) - // .set(UserWord(wordId: word.id, isDone: false).toMap()); - // } final doc = await _firestore.collection('users').doc(uid).get(); userData.value = doc.data() ?? {}; nickname.value = userData.value['nickname'] ?? ''; - print('nickname: ${nickname.value}'); systemLanguage.value = userData.value['systemLanguage'] ?? '한국어'; learningLanguage.value = userData.value['learningLanguage'] ?? '한국어'; - speakingScore.value = userData.value['speakingScore'] ?? 86; pitchScore.value = userData.value['pitchScore'] ?? 50; circleNumber.value = userData.value['circleNumber'] ?? 80; @@ -90,7 +80,8 @@ class UserViewModel extends GetxController { } // 언어 설정 업데이트 - Future updateLanguageSettings() async { + Future updateLanguageSettings(value) async { + //value try { if (uid != null) { await _firestore.collection('users').doc(uid).update({ @@ -106,10 +97,15 @@ class UserViewModel extends GetxController { key: 'learningLanguage', value: learningLanguage.value, ); + if (value == '한국어') { + Get.updateLocale(const Locale('ko', 'KR')); + } else { + Get.updateLocale(const Locale('en', 'US')); + } Get.snackbar( - "언어 설정 변경", - "언어 설정이 변경되었습니다.", + 'language_setting_snackbar_title'.tr, + "language_setting_snackbar_content".tr, snackPosition: SnackPosition.TOP, ); } else { @@ -123,15 +119,20 @@ class UserViewModel extends GetxController { value: learningLanguage.value, ); Get.snackbar( - "언어 설정 변경", - "언어 설정이 변경되었습니다.", + "language_setting_snackbar_title".tr, + "language_setting_snackbar_content".tr, snackPosition: SnackPosition.TOP, ); + if (value == '한국어') { + Get.updateLocale(const Locale('ko', 'KR')); + } else { + Get.updateLocale(const Locale('en', 'US')); + } } } catch (e) { Get.snackbar( - "언어 설정 변경 실패", - "언어 설정을 변경하는 중 오류가 발생했습니다.", + "language_setting_snackbar_fail_title".tr, + "language_setting_snackbar_fail_content".tr, snackPosition: SnackPosition.TOP, ); } diff --git a/lib/viewModels/word/word_viewmodel.dart b/lib/viewModels/word/word_viewmodel.dart index b9adef8..b862113 100644 --- a/lib/viewModels/word/word_viewmodel.dart +++ b/lib/viewModels/word/word_viewmodel.dart @@ -164,9 +164,7 @@ class WordViewModel extends GetxController { }); } }); - } catch (e) { - print("Transaction failed: $e"); - } + } catch (_) {} // 로컬에서 단어 데이터 업데이트 final index = wordList.indexWhere((element) => element.wordCard.id == word.id); diff --git a/lib/views/auth/auth_card_widget.dart b/lib/views/auth/auth_card_widget.dart index fc71feb..ca4c7db 100644 --- a/lib/views/auth/auth_card_widget.dart +++ b/lib/views/auth/auth_card_widget.dart @@ -1,6 +1,7 @@ import 'package:earlips/utilities/style/color_styles.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; class AuthCard extends StatelessWidget { final String iconPath; @@ -25,14 +26,14 @@ class AuthCard extends StatelessWidget { ), elevation: 0, child: Padding( - padding: EdgeInsets.all(label == '구글 계정으로 로그인' ? 15 : 17), + padding: EdgeInsets.all(label == 'google_login'.tr ? 15 : 17), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(width: 20), SvgPicture.asset( iconPath, - height: label == "구글 계정으로 로그인" ? 30 : 25, + height: label == 'google_login'.tr ? 30 : 25, width: 30, ), const SizedBox(width: 20), diff --git a/lib/views/auth/auth_dialog.dart b/lib/views/auth/auth_dialog.dart index ec7888c..0100fe3 100644 --- a/lib/views/auth/auth_dialog.dart +++ b/lib/views/auth/auth_dialog.dart @@ -19,7 +19,7 @@ Future authDialog({ onCancel: () => Get.back(), onConfirm: () async { /// title == '로그아웃' ? 로그아웃 : 회원탈퇴 - title == '로그아웃' + title == 'profile_account_logout'.tr ? await authService.signOut() : await authService.withdraw(); Get.offAllNamed('/'); diff --git a/lib/views/auth/email_login_screen.dart b/lib/views/auth/email_login_screen.dart index cd69f59..d88916d 100644 --- a/lib/views/auth/email_login_screen.dart +++ b/lib/views/auth/email_login_screen.dart @@ -13,7 +13,7 @@ class EmailLoginScreen extends StatelessWidget { return Scaffold( appBar: AppBar( - title: const Text("이메일 로그인"), + title: Text("email_login_2".tr), ), body: Padding( padding: const EdgeInsets.all(20.0), @@ -24,26 +24,26 @@ class EmailLoginScreen extends StatelessWidget { children: [ TextFormField( controller: controller.emailController, - decoration: const InputDecoration(hintText: '이메일'), + decoration: InputDecoration(hintText: 'email'.tr), validator: (value) => controller.emailValidator(value), ), TextFormField( controller: controller.passwordController, obscureText: true, - decoration: const InputDecoration(hintText: '비밀번호'), + decoration: InputDecoration(hintText: 'password'.tr), validator: (value) => controller.passwordValidator(value), ), const SizedBox(height: 20), ElevatedButton( // 로그인 메소드 호출 onPressed: controller.signInWithEmailAndPassword, - child: const Text('Login'), + child: Text('login'.tr), ), const SizedBox(height: 10), // 회원가입 화면으로 이동 TextButton( onPressed: () => Get.to(() => const EmailSignupScreen()), - child: const Text("회원가입 하러가기"), + child: Text("go_signup".tr), ), ], ), diff --git a/lib/views/auth/email_signup_screen.dart b/lib/views/auth/email_signup_screen.dart index aa2227f..9138bbe 100644 --- a/lib/views/auth/email_signup_screen.dart +++ b/lib/views/auth/email_signup_screen.dart @@ -12,7 +12,7 @@ class EmailSignupScreen extends StatelessWidget { return Scaffold( appBar: AppBar( - title: const Text("이메일 회원가입"), + title: Text("email_signup".tr), ), body: Padding( padding: const EdgeInsets.all(20.0), @@ -24,21 +24,21 @@ class EmailSignupScreen extends StatelessWidget { TextFormField( // 이메일 메소드 호출 controller: controller.emailController, - decoration: const InputDecoration(hintText: '이메일'), + decoration: InputDecoration(hintText: 'email'.tr), validator: (value) => controller.emailValidator(value), ), TextFormField( // 비밀번호 메소드 호출 controller: controller.passwordController, obscureText: true, - decoration: const InputDecoration(hintText: '비밀번호'), + decoration: InputDecoration(hintText: 'password'.tr), validator: (value) => controller.passwordValidator(value), ), const SizedBox(height: 20), ElevatedButton( // 회원가입 메소드 호출 onPressed: controller.registerWithEmailAndPassword, - child: const Text('회원 가입'), + child: Text('signup'.tr), ), ], ), diff --git a/lib/views/auth/login_screen.dart b/lib/views/auth/login_screen.dart index 48b1a8a..f0a06ff 100644 --- a/lib/views/auth/login_screen.dart +++ b/lib/views/auth/login_screen.dart @@ -27,12 +27,12 @@ class LoginScreen extends StatelessWidget { children: [ AuthCard( iconPath: "assets/icons/google.svg", - label: "구글 계정으로 로그인", + label: "google_login".tr, onTap: () => _authService.signInWithGoogle(), ), AuthCard( iconPath: "assets/icons/mailbox.svg", - label: "이메일로 로그인", + label: "email_login".tr, onTap: () => Get.to(() => const EmailLoginScreen()), ), const SizedBox(height: 80), diff --git a/lib/views/base/default_back_appbar.dart b/lib/views/base/default_back_appbar.dart index 36248f5..46a3b07 100644 --- a/lib/views/base/default_back_appbar.dart +++ b/lib/views/base/default_back_appbar.dart @@ -28,9 +28,9 @@ class DefaultBackAppbar extends StatelessWidget { ), ), icon: SvgPicture.asset("assets/icons/back.svg"), - label: const Text( - "뒤로", - style: TextStyle( + label: Text( + 'appbar_back'.tr, + style: const TextStyle( color: ColorSystem.gray5, fontSize: 14, fontWeight: FontWeight.w600, diff --git a/lib/views/home/home_head_widget.dart b/lib/views/home/home_head_widget.dart index 8660fca..794fafa 100644 --- a/lib/views/home/home_head_widget.dart +++ b/lib/views/home/home_head_widget.dart @@ -1,13 +1,13 @@ import 'dart:math'; - import 'package:earlips/viewModels/user/user_viewmodel.dart'; import 'package:flutter/material.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; final messages = [ - "발음 연습해볼까요? 오늘도 화이팅!", - "오늘도 화이팅!", - "귀와 입을 이어요, 이어립스", - "꾸준함이 능력! 꾸준히 노력하자!", + 'message1', + 'message2', + 'message3', + 'message4', ]; class HomeHeaderWidget extends StatelessWidget { @@ -30,7 +30,7 @@ class HomeHeaderWidget extends StatelessWidget { margin: const EdgeInsets.only(left: 20.0, top: 20.0), child: Text( - randomMessage, + randomMessage.tr, style: const TextStyle( fontSize: 21, fontWeight: FontWeight.w600, @@ -53,11 +53,10 @@ class HomeHeaderWidget extends StatelessWidget { ), ), // Use ViewModel data - Text( isLoggedIn ? '${vm.learningLanguage.value} - ${vm.nickname.value}' - : 'korean - 로그인이 필요합니다', + : '${'homeLanguage'.tr} - ${'homeHeaderGuest'.tr}', style: const TextStyle( fontSize: 12, ), diff --git a/lib/views/home/home_screen.dart b/lib/views/home/home_screen.dart index e2a782d..c423a82 100644 --- a/lib/views/home/home_screen.dart +++ b/lib/views/home/home_screen.dart @@ -1,17 +1,14 @@ +import 'package:earlips/utilities/style/color_styles.dart'; import 'package:earlips/viewModels/user/user_viewmodel.dart'; import 'package:earlips/views/home/home_head_widget.dart'; +import 'package:earlips/views/home/widget/bottom_widget.dart'; +import 'package:earlips/views/home/widget/mid_widget.dart'; +import 'package:earlips/views/home/widget/top_widget.dart'; import 'package:flutter/material.dart'; import 'package:earlips/viewModels/home/home_viewmodel.dart'; import 'package:earlips/views/base/base_screen.dart'; import 'package:get/get.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:earlips/views/home/widget/study_chart.dart'; -import 'package:percent_indicator/percent_indicator.dart'; -//dart.ui -import 'dart:ui'; import 'package:firebase_auth/firebase_auth.dart'; -import 'package:earlips/views/script/learning_session_screen.dart'; -import '../realtime/real_create_script_screen.dart'; class HomeScreen extends BaseScreen { final User? user = @@ -21,30 +18,28 @@ class HomeScreen extends BaseScreen { @override Widget buildBody(BuildContext context) { - // Inject the HomeViewModel using GetX final viewModel = Get.put(UserViewModel()); return Scaffold( - backgroundColor: const Color(0xFFF0F4F8), + backgroundColor: ColorSystem.background, body: SafeArea( top: true, child: StreamBuilder( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { viewModel.onInit(); - final bool isLoggedIn = snapshot.hasData; return SingleChildScrollView( child: Column( children: [ const SizedBox(height: 15), HomeHeaderWidget(isLoggedIn: isLoggedIn, vm: viewModel), - _Top( + TopWidget( isLoggedIn: isLoggedIn, vm: viewModel, ), - const _Middle(), + const MidWidget(), // 로그인 상태에 따라 _Bottom 클래스의 컨테이너 색상을 변경 - _Bottom(isLoggedIn: isLoggedIn), + BottomWidget(isLoggedIn: isLoggedIn), ], ), ); @@ -54,299 +49,3 @@ class HomeScreen extends BaseScreen { ); } } - -class _Top extends StatelessWidget { - final bool isLoggedIn; - final UserViewModel vm; - const _Top({required this.isLoggedIn, required this.vm}); - - @override - Widget build(BuildContext context) { - return Center( - child: Stack( - children: [ - Container( - margin: const EdgeInsets.all(20.0), - decoration: BoxDecoration( - color: const Color(0xFFFFFFFF), - borderRadius: BorderRadius.circular(30.0), - ), - height: Get.height * 0.19, - child: Stack( - children: [ - Row(children: [ - _Circle(vm: vm), - _SpeakingAbility(vm: vm), - ]), - if (!isLoggedIn) // 로그인 안 됐을 때만 블러 효과와 자물쇠 아이콘 표시 - Positioned.fill( - child: ClipRRect( - borderRadius: BorderRadius.circular(30.0), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), - child: Container( - alignment: Alignment.center, - color: Colors.grey.withOpacity(0.1), - child: Icon( - Icons.lock_outline, - size: 60, - color: Colors.white.withOpacity(1.0), - ), - ), - ), - ), - ), - ], - ), - ), - ], - )); - } -} - -class _Middle extends StatelessWidget { - const _Middle({super.key}); - - @override - Widget build(BuildContext context) { - return Row(children: [ - InkWell( - onTap: () { - Get.to(() => LearningSessionScreen()); - }, - child: Container( - margin: const EdgeInsets.only(left: 20.0), - height: Get.height * 0.21, - width: Get.width * 0.43, - decoration: BoxDecoration( - color: const Color(0xFFFFFFFF), - borderRadius: BorderRadius.circular(30.0), - ), - child: Column( - children: [ - Container( - alignment: Alignment.center, - margin: const EdgeInsets.only(top: 20.0, bottom: 10.0), - width: 90, - height: 90, - child: Image.asset('assets/images/home/Chart_new.png'), - ), - const Text( - "대본으로 학습하기", - style: TextStyle( - fontFamily: 'Pretendard-Bold', - fontSize: 15, - fontWeight: FontWeight.bold, - ), - ), - const Text( - "대본 입력 및 발음 테스트", - style: TextStyle( - fontFamily: 'Pretendard-Regular', - fontSize: 12, - ), - ), - ], - ), - ), - ), - InkWell( - onTap: () { - Get.to(() => - RealCreateScriptPage()); // Adjust the screen name as necessary - }, - child: Container( - margin: const EdgeInsets.only(left: 20.0), - height: Get.height * 0.21, - width: Get.width * 0.43, - decoration: BoxDecoration( - color: const Color(0xFFFFFFFF), - borderRadius: BorderRadius.circular(30.0), - ), - child: Column( - children: [ - Container( - alignment: Alignment.center, - margin: const EdgeInsets.only(top: 20.0, bottom: 10.0), - child: SvgPicture.asset( - 'assets/images/home/three_circle.svg', - width: 85, - height: 85, - ), - ), - const Text( - "실시간 발음테스트", - style: TextStyle( - fontFamily: 'Pretendard-Bold', - fontSize: 15, - fontWeight: FontWeight.bold, - ), - ), - const Text( - "음성 인식 및 발음 테스트", - style: TextStyle( - fontFamily: 'Pretendard-Regular', - fontSize: 12, - ), - ), - ], - ), - ), - ) - ]); - } -} - -class _Bottom extends StatelessWidget { - final bool isLoggedIn; - const _Bottom({required this.isLoggedIn}); - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(20.0), - child: Stack( - children: [ - Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: const Color(0xFFFFFFFF), // 기본 배경색 - borderRadius: BorderRadius.circular(30.0), - ), - child: Column( - children: [ - Container( - margin: const EdgeInsets.only(top: 20.0, left: 20.0), - alignment: Alignment.centerLeft, - child: const Text( - "진행한 학습", - style: TextStyle( - fontFamily: 'Pretendard-Bold', - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - Container( - alignment: Alignment.centerLeft, - margin: const EdgeInsets.only(left: 15.0), - child: LineChartSample2()), - ], - ), - ), - if (!isLoggedIn) // 로그인 안 됐을 때만 블러 효과와 자물쇠 아이콘 표시 - Positioned.fill( - child: ClipRRect( - borderRadius: BorderRadius.circular(30.0), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), - child: Container( - alignment: Alignment.center, - color: Colors.grey.withOpacity(0.1), - child: Icon( - Icons.lock_outline, - size: 60, - color: Colors.white.withOpacity(0.6), - ), - ), - ), - ), - ), - ], - ), - ); - } -} - -class _Circle extends StatelessWidget { - final UserViewModel vm; - const _Circle({super.key, required this.vm}); - - @override - Widget build(BuildContext context) { - // HomeViewModel 인스턴스 접근 - final homeViewModel = Get.find(); - - return Container( - alignment: Alignment.centerLeft, - margin: const EdgeInsets.only(left: 20.0), - child: Stack( - alignment: Alignment.center, - children: [ - SvgPicture.asset( - 'assets/images/home/circle.svg', - width: 90, - height: 90, - ), - Obx(() => Text( - '${vm.circleNumber.value}', // Observable 값을 사용 - style: const TextStyle( - fontFamily: 'Pretendard-Bold', - color: Colors.white, - fontSize: 28, - fontWeight: FontWeight.w900, - ), - )), - ], - ), - ); - } -} - -class _SpeakingAbility extends StatelessWidget { - final UserViewModel vm; - const _SpeakingAbility({super.key, required this.vm}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - margin: const EdgeInsets.only(top: 35.0, right: 25.0, left: 40.0), - child: const Text( - "Speaking Ability", - style: TextStyle( - fontSize: 20, - fontFamily: 'Pretendard-Bold', - fontWeight: FontWeight.bold, - ), - ), - ), - Container( - margin: const EdgeInsets.only(top: 8.0, right: 25.0, left: 40.0), - child: Row( - children: [ - Obx(() => Text( - "발음 ${vm.speakingScore.value}", - style: const TextStyle( - fontFamily: 'Pretendard-Regular', - fontSize: 15, - ), - )), - const SizedBox(width: 30), - Obx(() => Text( - "높낮이 ${vm.pitchScore.value}", - style: const TextStyle( - fontFamily: 'Pretendard-Regular', - fontSize: 15, - ), - )), - ], - ), - ), - Obx( - () => Container( - margin: const EdgeInsets.only(top: 20.0, left: 20.0), - child: LinearPercentIndicator( - barRadius: const Radius.circular(10.0), - width: 163.0, - lineHeight: 8.0, - percent: vm.linialPersent.value, - progressColor: const Color(0xFF4EC040), - ), - ), - ) - ], - ); - } -} diff --git a/lib/views/home/widget/app_resources.dart b/lib/views/home/widget/app_resources.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/views/home/widget/bottom_widget.dart b/lib/views/home/widget/bottom_widget.dart new file mode 100644 index 0000000..ec8b183 --- /dev/null +++ b/lib/views/home/widget/bottom_widget.dart @@ -0,0 +1,65 @@ +import 'dart:ui'; +import 'package:earlips/views/home/widget/study_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; + +class BottomWidget extends StatelessWidget { + final bool isLoggedIn; + const BottomWidget({super.key, required this.isLoggedIn}); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.all(20.0), + child: Stack( + children: [ + Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: const Color(0xFFFFFFFF), // 기본 배경색 + borderRadius: BorderRadius.circular(30.0), + ), + child: Column( + children: [ + Container( + margin: const EdgeInsets.only(top: 20.0, left: 20.0), + alignment: Alignment.centerLeft, + child: Text( + 'home_chart_title'.tr, + style: const TextStyle( + fontFamily: 'Pretendard-Bold', + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(left: 15.0), + child: const LineChartSample2()), + ], + ), + ), + if (!isLoggedIn) // 로그인 안 됐을 때만 블러 효과와 자물쇠 아이콘 표시 + Positioned.fill( + child: ClipRRect( + borderRadius: BorderRadius.circular(30.0), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), + child: Container( + alignment: Alignment.center, + color: Colors.grey.withOpacity(0.1), + child: Icon( + Icons.lock_outline, + size: 60, + color: Colors.white.withOpacity(0.6), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/home/widget/mid_widget.dart b/lib/views/home/widget/mid_widget.dart new file mode 100644 index 0000000..891e2a9 --- /dev/null +++ b/lib/views/home/widget/mid_widget.dart @@ -0,0 +1,97 @@ +import 'dart:ui'; + +import 'package:earlips/views/realtime/real_create_script_screen.dart'; +import 'package:earlips/views/script/learning_session_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; + +class MidWidget extends StatelessWidget { + const MidWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Row(children: [ + InkWell( + onTap: () { + Get.to(() => LearningSessionScreen()); + }, + child: Container( + margin: const EdgeInsets.only(left: 20.0), + height: Get.height * 0.21, + width: Get.width * 0.43, + decoration: BoxDecoration( + color: const Color(0xFFFFFFFF), + borderRadius: BorderRadius.circular(30.0), + ), + child: Column( + children: [ + Container( + alignment: Alignment.center, + margin: const EdgeInsets.only(top: 20.0, bottom: 10.0), + width: 90, + height: 90, + child: Image.asset('assets/images/home/Chart_new.png'), + ), + Text( + 'home_script_title'.tr, + style: const TextStyle( + fontFamily: 'Pretendard-Bold', + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'home_script_subtitle'.tr, + style: const TextStyle( + fontSize: 12, + ), + ), + ], + ), + ), + ), + InkWell( + onTap: () { + Get.to(() => + RealCreateScriptPage()); // Adjust the screen name as necessary + }, + child: Container( + margin: const EdgeInsets.only(left: 20.0), + height: Get.height * 0.21, + width: Get.width * 0.43, + decoration: BoxDecoration( + color: const Color(0xFFFFFFFF), + borderRadius: BorderRadius.circular(30.0), + ), + child: Column( + children: [ + Container( + alignment: Alignment.center, + margin: const EdgeInsets.only(top: 20.0, bottom: 10.0), + child: SvgPicture.asset( + 'assets/images/home/three_circle.svg', + width: 85, + height: 85, + ), + ), + Text( + 'home_live_script_title'.tr, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + Text( + 'home_live_script_subtitle'.tr, + style: const TextStyle( + fontSize: 12, + ), + ), + ], + ), + ), + ) + ]); + } +} diff --git a/lib/views/home/widget/study_chart.dart b/lib/views/home/widget/study_chart.dart index 6fb5855..d3eea1e 100644 --- a/lib/views/home/widget/study_chart.dart +++ b/lib/views/home/widget/study_chart.dart @@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'dart:math'; class AppColors { static const Color contentColorCyan = Color(0xff23b6e6); @@ -13,24 +12,24 @@ class AppColors { class GradientColors { static List get primaryGradient => [ - AppColors.contentColorCyan, - AppColors.contentColorBlue, - ]; + AppColors.contentColorCyan, + AppColors.contentColorBlue, + ]; } - class ChartTitleWidgets { - static Widget bottomTitleWidgets(double value, TitleMeta meta, DateTime startDate, DateTime endDate) { - final style = TextStyle( + static Widget bottomTitleWidgets( + double value, TitleMeta meta, DateTime startDate, DateTime endDate) { + const style = TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ); String text; if (value == 0) { - text = DateFormat('MMM d').format(startDate).toUpperCase(); + text = DateFormat('MM/d').format(startDate).toUpperCase(); } else if (value == 30) { - text = DateFormat('MMM d').format(endDate).toUpperCase(); + text = DateFormat('MM/d').format(endDate).toUpperCase(); } else { return Container(); } @@ -41,7 +40,8 @@ class ChartTitleWidgets { ); } - static Widget leftTitleWidgets(double value, TitleMeta meta, double maxYValue) { + static Widget leftTitleWidgets( + double value, TitleMeta meta, double maxYValue) { const style = TextStyle( fontWeight: FontWeight.bold, fontSize: 13, @@ -54,6 +54,8 @@ class ChartTitleWidgets { } class LineChartSample2 extends StatefulWidget { + const LineChartSample2({super.key}); + @override _LineChartSample2State createState() => _LineChartSample2State(); } @@ -68,23 +70,22 @@ class _LineChartSample2State extends State { @override Widget build(BuildContext context) { - final startDate = DateTime.now().subtract(Duration(days: 30)); + final startDate = DateTime.now().subtract(const Duration(days: 30)); final endDate = DateTime.now(); - return Container( - height: Get.height * 0.20, - width: Get.width * 0.75, - child: LineChartComponent( - dataSpots: viewModel.flSpots, - gradientColors: GradientColors.primaryGradient, - maxYValue: viewModel.maxYValue.value, // Obx 내부에서 계산된 maxYValue를 사용 - startDate: startDate, - endDate: endDate, - ), - ); - } + return SizedBox( + height: Get.height * 0.20, + width: Get.width * 0.75, + child: LineChartComponent( + dataSpots: viewModel.flSpots, + gradientColors: GradientColors.primaryGradient, + maxYValue: viewModel.maxYValue.value, // Obx 내부에서 계산된 maxYValue를 사용 + startDate: startDate, + endDate: endDate, + ), + ); + } } - class LineChartComponent extends StatelessWidget { final List dataSpots; final List gradientColors; @@ -93,13 +94,13 @@ class LineChartComponent extends StatelessWidget { final DateTime endDate; const LineChartComponent({ - Key? key, + super.key, required this.dataSpots, required this.gradientColors, required this.maxYValue, required this.startDate, required this.endDate, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -107,7 +108,8 @@ class LineChartComponent extends StatelessWidget { aspectRatio: 1.70, child: Padding( padding: const EdgeInsets.all(10.0), - child: Obx(() => LineChart(mainData()), + child: Obx( + () => LineChart(mainData()), ), ), ); @@ -115,9 +117,9 @@ class LineChartComponent extends StatelessWidget { LineChartData mainData() { return LineChartData( - gridData: FlGridData( - // Grid 설정은 이전과 동일하게 유지 - ), + gridData: const FlGridData( + // Grid 설정은 이전과 동일하게 유지 + ), titlesData: FlTitlesData( // 타이틀 데이터 설정, 이전과 동일하게 유지하되 startDate와 endDate를 사용 bottomTitles: AxisTitles( @@ -125,15 +127,17 @@ class LineChartComponent extends StatelessWidget { showTitles: true, reservedSize: 30, interval: 1, - getTitlesWidget: (value, meta) => ChartTitleWidgets.bottomTitleWidgets(value, meta, startDate, endDate), + getTitlesWidget: (value, meta) => + ChartTitleWidgets.bottomTitleWidgets( + value, meta, startDate, endDate), ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - interval: 1, - getTitlesWidget: (value, meta) => ChartTitleWidgets.leftTitleWidgets(value, meta, maxYValue), + getTitlesWidget: (value, meta) => + ChartTitleWidgets.leftTitleWidgets(value, meta, maxYValue), reservedSize: 42, ), ), @@ -143,36 +147,38 @@ class LineChartComponent extends StatelessWidget { rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), + ), + borderData: FlBorderData( + show: true, + border: Border.all(color: const Color(0xffffffff)), + ), + minX: 0, + maxX: 30, + minY: 0, + maxY: maxYValue, // 이거에 따라서 그래프 높이가 달라짐 + lineBarsData: [ + LineChartBarData( + show: dataSpots.isNotEmpty, + spots: dataSpots, + isCurved: true, + gradient: LinearGradient( + colors: gradientColors, + ), + barWidth: 5, + isStrokeCapRound: true, + dotData: const FlDotData( + show: false, + ), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: gradientColors + .map((color) => color.withOpacity(0.3)) + .toList(), ), - borderData: FlBorderData( - show: true, - border: Border.all(color: const Color(0xffffffff)), - ), - minX: 0, - maxX: 30, - minY: 0, - maxY: maxYValue, // 이거에 따라서 그래프 높이가 달라짐 - lineBarsData: [ - LineChartBarData( - show: dataSpots.isNotEmpty, - spots: dataSpots, - isCurved: true, - gradient: LinearGradient( - colors: gradientColors, - ), - barWidth: 5, - isStrokeCapRound: true, - dotData: const FlDotData( - show: false, - ), - belowBarData: BarAreaData( - show: true, - gradient: LinearGradient( - colors: gradientColors.map((color) => color.withOpacity(0.3)).toList(), - ), - ), - ), - ], + ), + ), + ], ); } } diff --git a/lib/views/home/widget/top_widget.dart b/lib/views/home/widget/top_widget.dart new file mode 100644 index 0000000..98fb71b --- /dev/null +++ b/lib/views/home/widget/top_widget.dart @@ -0,0 +1,148 @@ +import 'dart:ui'; + +import 'package:earlips/viewModels/home/home_viewmodel.dart'; +import 'package:earlips/viewModels/user/user_viewmodel.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:percent_indicator/percent_indicator.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; + +class TopWidget extends StatelessWidget { + final bool isLoggedIn; + final UserViewModel vm; + const TopWidget({super.key, required this.isLoggedIn, required this.vm}); + + @override + Widget build(BuildContext context) { + return Center( + child: Stack( + children: [ + Container( + margin: const EdgeInsets.all(20.0), + decoration: BoxDecoration( + color: const Color(0xFFFFFFFF), + borderRadius: BorderRadius.circular(30.0), + ), + height: Get.height * 0.19, + child: Stack( + children: [ + Row(children: [ + _Circle(vm: vm), + _SpeakingAbility(vm: vm), + ]), + if (!isLoggedIn) // 로그인 안 됐을 때만 블러 효과와 자물쇠 아이콘 표시 + Positioned.fill( + child: ClipRRect( + borderRadius: BorderRadius.circular(30.0), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), + child: Container( + alignment: Alignment.center, + color: Colors.grey.withOpacity(0.1), + child: Icon( + Icons.lock_outline, + size: 60, + color: Colors.white.withOpacity(1.0), + ), + ), + ), + ), + ), + ], + ), + ), + ], + )); + } +} + +class _Circle extends StatelessWidget { + final UserViewModel vm; + const _Circle({super.key, required this.vm}); + + @override + Widget build(BuildContext context) { + // HomeViewModel 인스턴스 접근 + final homeViewModel = Get.find(); + + return Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(left: 20.0), + child: Stack( + alignment: Alignment.center, + children: [ + SvgPicture.asset( + 'assets/images/home/circle.svg', + width: 90, + height: 90, + ), + Obx(() => Text( + '${vm.circleNumber.value}', // Observable 값을 사용 + style: const TextStyle( + fontFamily: 'Pretendard-Bold', + color: Colors.white, + fontSize: 28, + fontWeight: FontWeight.w900, + ), + )), + ], + ), + ); + } +} + +class _SpeakingAbility extends StatelessWidget { + final UserViewModel vm; + const _SpeakingAbility({super.key, required this.vm}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + margin: const EdgeInsets.only(top: 35.0, right: 25.0, left: 40.0), + child: const Text( + "Speaking Ability", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + Container( + margin: const EdgeInsets.only(top: 8.0, right: 25.0, left: 40.0), + child: Row( + children: [ + Obx(() => Text( + "${'speakingAbility1'.tr} ${vm.speakingScore.value}", + style: const TextStyle( + fontSize: 15, + ), + )), + const SizedBox(width: 30), + Obx(() => Text( + "${'speakingAbility2'.tr} ${vm.pitchScore.value}", + style: const TextStyle( + fontSize: 15, + ), + )), + ], + ), + ), + Obx( + () => Container( + margin: const EdgeInsets.only(top: 20.0, left: 20.0), + child: LinearPercentIndicator( + barRadius: const Radius.circular(10.0), + width: 163.0, + lineHeight: 8.0, + percent: vm.linialPersent.value, + progressColor: const Color(0xFF4EC040), + ), + ), + ) + ], + ); + } +} diff --git a/lib/views/paragraph/analyze_screen.dart b/lib/views/paragraph/analyze_screen.dart index f806d8e..7c1c7df 100644 --- a/lib/views/paragraph/analyze_screen.dart +++ b/lib/views/paragraph/analyze_screen.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:earlips/views/base/default_back_appbar.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; @@ -9,7 +10,7 @@ import 'package:earlips/viewModels/script/analyze_viewmodel.dart'; import '../../utilities/app_routes.dart'; class AnalyzeScreen extends StatefulWidget { - AnalyzeScreen({Key? key}) : super(key: key); + const AnalyzeScreen({super.key}); @override _AnalyzeScreenState createState() => _AnalyzeScreenState(); } @@ -31,9 +32,11 @@ class _AnalyzeScreenState extends State { FocusScope.of(context).requestFocus(FocusNode()); }, child: Scaffold( - appBar: AppBar( - title: Text('발음 및 빠르기 분석'), - centerTitle: true, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: DefaultBackAppbar( + title: 'analyze_appbar_title'.tr, + ), ), body: SingleChildScrollView( child: Column( @@ -42,14 +45,14 @@ class _AnalyzeScreenState extends State { alignment: Alignment.center, width: Get.width - 40, height: Get.height * 0.2, - margin: EdgeInsets.all(20.0), - padding: EdgeInsets.all(10.0), + margin: const EdgeInsets.all(20.0), + padding: const EdgeInsets.all(10.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15.0), border: Border.all(color: Colors.white), ), - child: _TopText(), // 이 부분은 상태를 표시하지 않으므로 그대로 유지합니다. + child: const _TopText(), // 이 부분은 상태를 표시하지 않으므로 그대로 유지합니다. ), Stack( children: [ @@ -57,14 +60,15 @@ class _AnalyzeScreenState extends State { alignment: Alignment.center, width: Get.width - 40, height: Get.height * 0.5, - margin: EdgeInsets.all(20.0), - padding: EdgeInsets.all(10.0), + margin: const EdgeInsets.all(20.0), + padding: const EdgeInsets.all(10.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15.0), border: Border.all(color: Colors.white), ), - child: TextStylingWidget(viewModel: viewModel), // viewModel을 전달합니다. + child: TextStylingWidget( + viewModel: viewModel), // viewModel을 전달합니다. ), ], ), @@ -78,8 +82,8 @@ class _AnalyzeScreenState extends State { onPressed: () { Get.toNamed(Routes.HOME); }, - child: Icon(Icons.home), - tooltip: '홈으로', + tooltip: 'analyze_home'.tr, + child: const Icon(Icons.home), ), ), ), @@ -90,7 +94,8 @@ class _AnalyzeScreenState extends State { class TextStylingWidget extends StatelessWidget { final AnalyzeViewModel viewModel; // viewModel을 받기 위한 생성자 파라미터를 추가합니다. - TextStylingWidget({required this.viewModel}); // 생성자를 통해 viewModel을 초기화합니다. + const TextStylingWidget( + {super.key, required this.viewModel}); // 생성자를 통해 viewModel을 초기화합니다. @override Widget build(BuildContext context) { @@ -98,14 +103,13 @@ class TextStylingWidget extends StatelessWidget { child: RichText( text: TextSpan( children: _buildTextSpans(), - style: TextStyle(color: Colors.black, fontSize: 16), + style: const TextStyle(color: Colors.black, fontSize: 16), ), textAlign: TextAlign.start, ), ); } - List _buildTextSpans() { List spans = []; int globalWordIndex = 0; @@ -115,7 +119,8 @@ class TextStylingWidget extends StatelessWidget { List wordSpans = []; for (String word in words) { - final bool isWrongWord = viewModel.wrongWordIndexes.contains(globalWordIndex); + final bool isWrongWord = + viewModel.wrongWordIndexes.contains(globalWordIndex); wordSpans.add(TextSpan( text: "$word ", style: TextStyle( @@ -136,20 +141,20 @@ class TextStylingWidget extends StatelessWidget { spans.add(TextSpan( children: wordSpans, style: TextStyle( - decoration: viewModel.wrongFastIndexes[i] != 0 ? TextDecoration.underline : TextDecoration.none, + decoration: viewModel.wrongFastIndexes[i] != 0 + ? TextDecoration.underline + : TextDecoration.none, decorationColor: underlineColor, // 밑줄 색상을 지정합니다. decorationStyle: TextDecorationStyle.solid, - decorationThickness: 3.0, + decorationThickness: 3.0, //밑줄을 밑으로 내리기 위한 값 - ), )); - spans.add(TextSpan(text: "\n")); + spans.add(const TextSpan(text: "\n")); } return spans; } - } class _TopText extends StatelessWidget { @@ -158,36 +163,36 @@ class _TopText extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 발음이 틀린 글씨에 대한 설명 RichText( text: TextSpan( - style: TextStyle(fontSize: 16, color: Colors.black), + style: const TextStyle(fontSize: 16, color: Colors.black), children: [ - TextSpan(text: '발음이 틀린 글씨는 빨간색으로 표시됩니다 ex)'), + TextSpan(text: 'analyze_exmaple_title'.tr), // 예시에 적용할 스타일 TextSpan( - text: '강아지는 ', - style: TextStyle(color: Colors.red), + text: 'analyze_exmaple_content'.tr, + style: const TextStyle(color: Colors.red), ), TextSpan( - text: '뛴다', + text: 'analyze_exmaple_content2'.tr, ), ], ), ), - Padding( + const Padding( padding: EdgeInsets.only(top: 20), ), // 문장의 빠르기에 대한 설명 RichText( text: TextSpan( - style: TextStyle(fontSize: 16, color: Colors.black), + style: const TextStyle(fontSize: 16, color: Colors.black), children: [ - TextSpan(text: '문장이 빠르면 빨강, 느리면 보라색 밑줄로 표시됩니다.'), + TextSpan(text: 'analyze_exmaple_alert'.tr), // 예시에 적용할 스타일 //들여쓰기 ], @@ -197,6 +202,4 @@ class _TopText extends StatelessWidget { ), ); } - } - diff --git a/lib/views/paragraph/create_script_screen.dart b/lib/views/paragraph/create_script_screen.dart index 00f90c9..8ad7c7a 100644 --- a/lib/views/paragraph/create_script_screen.dart +++ b/lib/views/paragraph/create_script_screen.dart @@ -7,8 +7,7 @@ import 'package:get/get.dart'; class CreateScriptPage extends StatelessWidget { final String title; final String text; - const CreateScriptPage({Key? key, required this.title, required this.text}) : super(key: key); - + const CreateScriptPage({super.key, required this.title, required this.text}); @override Widget build(BuildContext context) { @@ -50,17 +49,13 @@ class CreateScriptPage extends StatelessWidget { //가장자리 둥글게 decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), - color: Colors.white, - ), - + child: Text( text, - style: const TextStyle(fontSize: 16, - fontFamily: 'Pretendard', - fontWeight: FontWeight.bold - ), + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), ), ), ), diff --git a/lib/views/paragraph/learning_session_screen.dart b/lib/views/paragraph/learning_session_screen.dart index d1e74fd..4ee7100 100644 --- a/lib/views/paragraph/learning_session_screen.dart +++ b/lib/views/paragraph/learning_session_screen.dart @@ -5,15 +5,15 @@ import 'package:get/get.dart'; import 'package:earlips/views/paragraph/create_script_screen.dart'; class LearningSessionScreen extends StatefulWidget { - LearningSessionScreen({Key? key}) : super(key: key); + const LearningSessionScreen({super.key}); @override State createState() => _LearningSessionScreenState(); } class _LearningSessionScreenState extends State { - final viewModel = Get.put( - LearningSessionScreenViewModel()); // ViewModel 인스턴스 생성 + final viewModel = + Get.put(LearningSessionScreenViewModel()); // ViewModel 인스턴스 생성 @override void initState() { @@ -26,11 +26,11 @@ class _LearningSessionScreenState extends State { return Scaffold( appBar: PreferredSize( preferredSize: const Size.fromHeight(kToolbarHeight), - child: BlueBackAppbar(title: "문단교정"), + child: BlueBackAppbar(title: "study_main_title_5".tr), ), body: Obx(() { if (viewModel.isLoading.value) { - return Center(child: CircularProgressIndicator()); + return const Center(child: CircularProgressIndicator()); } else { return ListView.separated( padding: const EdgeInsets.fromLTRB(25, 20, 25, 20), @@ -61,9 +61,8 @@ class _LearningSessionScreenState extends State { ), onTap: () { // title과 text만 다음 페이지로 전달 - Get.to(() => - CreateScriptPage( - title: paragraph.title, text: paragraph.text)); + Get.to(() => CreateScriptPage( + title: paragraph.title, text: paragraph.text)); }, ), ); @@ -75,4 +74,3 @@ class _LearningSessionScreenState extends State { ); } } - diff --git a/lib/views/profile/profile_account/profile_account_screen.dart b/lib/views/profile/profile_account/profile_account_screen.dart index 74abafe..f6376e3 100644 --- a/lib/views/profile/profile_account/profile_account_screen.dart +++ b/lib/views/profile/profile_account/profile_account_screen.dart @@ -2,6 +2,7 @@ import 'package:earlips/utilities/style/color_styles.dart'; import 'package:earlips/views/auth/auth_dialog.dart'; import 'package:earlips/views/base/default_back_appbar.dart'; import 'package:flutter/material.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; class ProfileAccountScreen extends StatelessWidget { const ProfileAccountScreen({super.key}); @@ -9,10 +10,10 @@ class ProfileAccountScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: const PreferredSize( - preferredSize: Size.fromHeight(kToolbarHeight), + appBar: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), child: DefaultBackAppbar( - title: "계정 관리", + title: "profile_account_title".tr, ), ), body: Container( @@ -24,29 +25,31 @@ class ProfileAccountScreen extends StatelessWidget { children: [ ListTile( contentPadding: EdgeInsets.zero, - title: const Text('로그아웃', - style: TextStyle(fontSize: 16, color: Color(0xff151515))), + title: Text('profile_account_logout'.tr, + style: const TextStyle( + fontSize: 16, color: Color(0xff151515))), onTap: () { authDialog( context: context, - title: '로그아웃', - content: '로그아웃 하시겠습니까?', - textConfirm: '로그아웃', - textCancel: '취소', + title: 'profile_account_logout_alert_title'.tr, + content: 'profile_account_logout_alert_content'.tr, + textConfirm: 'profile_account_logout_alert_title'.tr, + textCancel: 'profile_account_logout_alert_cancel'.tr, ); }, ), ListTile( contentPadding: EdgeInsets.zero, - title: const Text('회원 탈퇴', - style: TextStyle(fontSize: 16, color: Color(0xff151515))), + title: Text('profile_account_signout'.tr, + style: const TextStyle( + fontSize: 16, color: Color(0xff151515))), onTap: () { authDialog( context: context, - title: '회원탈퇴', - content: '탈퇴 하시겠습니까?', - textConfirm: '탈퇴', - textCancel: '취소', + title: 'profile_account_signout'.tr, + content: 'profile_account_signout_alert_content'.tr, + textConfirm: 'profile_account_signout_alert_confirm'.tr, + textCancel: 'profile_account_signout_alert_cancel'.tr, ); }, ), diff --git a/lib/views/profile/profile_header_widget.dart b/lib/views/profile/profile_header_widget.dart index 8e274dd..f2208b6 100644 --- a/lib/views/profile/profile_header_widget.dart +++ b/lib/views/profile/profile_header_widget.dart @@ -27,14 +27,14 @@ class ProfileHeader extends StatelessWidget { children: [ // 유저 정보가 있으면 이메일을 보여주고, 없으면 로그인 버튼 보여줌 user != null - ? Text("${user!.displayName}님 반가워요!", + ? Text("${user!.displayName}${'mypage_user_title'.tr}", style: const TextStyle( color: ColorSystem.black, fontSize: 20, fontWeight: FontWeight.bold)) - : const Text( - "로그인 하러가기", - style: TextStyle( + : Text( + "mypage_go_login".tr, + style: const TextStyle( color: ColorSystem.main, fontSize: 20, fontWeight: FontWeight.bold), diff --git a/lib/views/profile/profile_language_setting/profile_language_setting.dart b/lib/views/profile/profile_language_setting/profile_language_setting.dart index ee18617..7f75937 100644 --- a/lib/views/profile/profile_language_setting/profile_language_setting.dart +++ b/lib/views/profile/profile_language_setting/profile_language_setting.dart @@ -3,7 +3,6 @@ import 'package:earlips/viewModels/user/user_viewmodel.dart'; import 'package:earlips/views/base/default_back_appbar.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:easy_localization/easy_localization.dart'; class ProfileLanguageScreen extends StatelessWidget { const ProfileLanguageScreen({super.key}); @@ -13,15 +12,11 @@ class ProfileLanguageScreen extends StatelessWidget { final userViewModel = Get.find(); userViewModel.loadLanguageSettings(); - String title = tr('language_settings'); - String systemLanguage = tr('system_language'); - String learningLanguage = tr('learning_language'); - return Scaffold( - appBar: const PreferredSize( - preferredSize: Size.fromHeight(kToolbarHeight), + appBar: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), child: DefaultBackAppbar( - title: "언어 설정", + title: 'language_setting_title'.tr, ), ), body: Container( @@ -33,30 +28,28 @@ class ProfileLanguageScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLanguageSettingSection( - title: systemLanguage, + title: 'language_setting_system'.tr, selectedLanguage: userViewModel.systemLanguage, onLanguageSelected: (value) { userViewModel.systemLanguage.value = value!; - userViewModel.updateLanguageSettings(); + userViewModel.updateLanguageSettings(value); }, languageOptions: [ - const DropdownMenuItem(value: '한국어', child: Text('한국어')), - const DropdownMenuItem( - value: 'English', child: Text('English')), + DropdownMenuItem(value: '한국어', child: Text('korean'.tr)), + DropdownMenuItem(value: 'English', child: Text('english'.tr)), ], ), const SizedBox(height: 20), _buildLanguageSettingSection( - title: learningLanguage, + title: 'language_setting_learning'.tr, selectedLanguage: userViewModel.learningLanguage, onLanguageSelected: (value) { userViewModel.learningLanguage.value = value!; - userViewModel.updateLanguageSettings(); + userViewModel.updateLanguageSettings(value); }, languageOptions: [ - const DropdownMenuItem(value: '한국어', child: Text('한국어')), - const DropdownMenuItem( - value: 'English', child: Text('English')), + DropdownMenuItem(value: '한국어', child: Text('korean'.tr)), + DropdownMenuItem(value: 'English', child: Text('english'.tr)), ], ), ], @@ -85,12 +78,8 @@ class ProfileLanguageScreen extends StatelessWidget { items: languageOptions, onChanged: (newValue) { onLanguageSelected(newValue); - // Call your language change logic here - // For example: - // userViewModel.systemLanguage.value = newValue!; - // userViewModel.updateLanguageSettings(); }, - hint: Text(tr('select_language') ?? 'fallback_language'), + hint: Text('select_language'.tr), )), ], ); diff --git a/lib/views/profile/profile_screen.dart b/lib/views/profile/profile_screen.dart index 0006dfc..5caf9cd 100644 --- a/lib/views/profile/profile_screen.dart +++ b/lib/views/profile/profile_screen.dart @@ -5,6 +5,7 @@ import 'package:earlips/views/profile/profile_setting_row_btn_widget.dart'; import 'package:earlips/views/profile/profile_setting_row_box_widget.dart'; import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; class ProfileScreen extends StatelessWidget { const ProfileScreen({super.key}); @@ -25,26 +26,26 @@ class ProfileScreen extends StatelessWidget { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { // 에러 발생시 안내 - return const Center(child: Text('네트워크 상태를 확인해주세요.')); + return Center(child: Text('check_network_connection'.tr)); } else if (snapshot.hasData) { // --------------------- 로그인 된 상태 --------------------- return Column( children: [ ProfileHeader( user: snapshot.hasData ? snapshot.data : null), - const ProfileSettingRowBtnWidget( + ProfileSettingRowBtnWidget( iconImg: 'assets/icons/icon-setting1.svg', - text: '시스템 및 학습 언어 설정', + text: 'mypage_menu1'.tr, routeLinkText: '/profile/language-setting'), - const ProfileSettingRowBtnWidget( + ProfileSettingRowBtnWidget( iconImg: 'assets/icons/icon-setting.svg', - text: '계정 관리', + text: 'mypage_menu2'.tr, routeLinkText: '/profile/account'), const ProfileDividerWidget(), - const ProfileSettingRowBoxWidget( - text: "버전 정보", routerLinkText: null), - const ProfileSettingRowBoxWidget( - text: "문의", routerLinkText: null), + ProfileSettingRowBoxWidget( + text: "mypage_menu3".tr, routerLinkText: null), + ProfileSettingRowBoxWidget( + text: "mypage_menu4".tr, routerLinkText: null), ], ); } else { @@ -52,15 +53,15 @@ class ProfileScreen extends StatelessWidget { return Column( children: [ ProfileHeader(), - const ProfileSettingRowBtnWidget( + ProfileSettingRowBtnWidget( iconImg: 'assets/icons/icon-setting1.svg', - text: '시스템 및 학습 언어 설정', + text: 'mypage_menu1'.tr, routeLinkText: '/profile/language-setting'), const ProfileDividerWidget(), - const ProfileSettingRowBoxWidget( - text: "버전 정보", routerLinkText: null), - const ProfileSettingRowBoxWidget( - text: "문의", routerLinkText: null), + ProfileSettingRowBoxWidget( + text: "mypage_menu3".tr, routerLinkText: null), + ProfileSettingRowBoxWidget( + text: "mypage_menu4".tr, routerLinkText: null), ], ); } diff --git a/lib/views/profile/profile_setting_row_box_widget.dart b/lib/views/profile/profile_setting_row_box_widget.dart index 5839903..f132ce7 100644 --- a/lib/views/profile/profile_setting_row_box_widget.dart +++ b/lib/views/profile/profile_setting_row_box_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; class ProfileSettingRowBoxWidget extends StatelessWidget { final String text; @@ -30,16 +31,16 @@ class ProfileSettingRowBoxWidget extends StatelessWidget { ), ), if (routerLinkText == null) const Spacer(), - if (text == '버전 정보') + if (text == 'mypage_menu3'.tr) const Text( - "1.0.0", + "2.0.1", style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xff90909F), ), ), - if (text == "문의") + if (text == "mypage_menu4".tr) const Text( "earlips@gmail.com", style: TextStyle( diff --git a/lib/views/realtime/real_create_script_screen.dart b/lib/views/realtime/real_create_script_screen.dart index c9e1e6c..abf1aff 100644 --- a/lib/views/realtime/real_create_script_screen.dart +++ b/lib/views/realtime/real_create_script_screen.dart @@ -3,8 +3,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:speech_to_text/speech_to_text.dart' as stt; import 'package:permission_handler/permission_handler.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; class RealCreateScriptPage extends StatefulWidget { + const RealCreateScriptPage({super.key}); + @override _RealCreateScriptPageState createState() => _RealCreateScriptPageState(); } @@ -20,35 +23,29 @@ class _RealCreateScriptPageState extends State { super.initState(); requestPermission(); speechToText.initialize( - onError: (val) => print('Error: $val'), onStatus: (status) { - print('Status changed: $status'); // 모든 상태 변화 로깅 handleStatus(status); // 상태 처리를 위한 함수 호출 }, ); } + void handleStatus(String status) { - print('Handling Status: $status'); // 현재 처리 중인 상태 로깅 - print('handDone: $handDone'); - if(handDone) { + if (handDone) { return; } if (status == 'done') { - print("Status is 'done'. Stopping and restarting listening."); stopListening(); // 잠시 후 다시 시작하기 위해 delay를 사용 - Future.delayed(Duration(milliseconds:100), () { + Future.delayed(const Duration(milliseconds: 100), () { startListening(); }); if (status == 'notListening') { - print("Status is 'notListening'. Restarting listening."); startListening(); } } // 필요한 경우 여기에 다른 상태에 대한 처리를 추가할 수 있습니다. } - @override @override void dispose() { @@ -63,7 +60,6 @@ class _RealCreateScriptPageState extends State { super.dispose(); } - Future requestPermission() async { var microphoneStatus = await Permission.microphone.status; if (!microphoneStatus.isGranted) { @@ -83,46 +79,40 @@ class _RealCreateScriptPageState extends State { } Future startListening() async { - bool available = await speechToText.initialize(onError: (error) => print(error), onStatus: (status) { - print(status); + bool available = await speechToText.initialize(onStatus: (status) { handleStatus(status); }); if (!available) { - print("The user has denied the use of speech recognition or an error occurred during initialization."); return; } speechToText.listen( - onResult: (result) - { - if (mounted) { - setState(() { - print('onResult: ${result.finalResult}'); - if (result.finalResult) { - // 기존 텍스트에 이어서 새로 인식된 텍스트를 추가합니다. - textEditingController.text += result.recognizedWords + " "; + onResult: (result) { + if (mounted) { + setState(() { + if (result.finalResult) { + // 기존 텍스트에 이어서 새로 인식된 텍스트를 추가합니다. + textEditingController.text += "${result.recognizedWords} "; + } + }); } - } - ); - } }, listenFor: const Duration(minutes: 5), pauseFor: const Duration(seconds: 3), ); - if (mounted) { setState(() => isRecording = true);} + if (mounted) { + setState(() => isRecording = true); + } } Future stopListening() async { - bool available = await speechToText.initialize(onError: (error) => print(error), onStatus: (status) { - print(status); + bool available = await speechToText.initialize(onStatus: (status) { handleStatus(status); }); speechToText.stop(); - print('멈출라고'); if (mounted) { setState(() => isRecording = false); } - } @override @@ -133,10 +123,10 @@ class _RealCreateScriptPageState extends State { }, child: Scaffold( appBar: AppBar( - title: Text('실시간 발음 테스트'), + title: Text('live_script_title'.tr), centerTitle: true, leading: IconButton( - icon: Icon(Icons.arrow_back), + icon: const Icon(Icons.arrow_back), onPressed: () { // 뒤로 가기 버튼을 눌렀을 때 텍스트 필드를 초기화합니다. textEditingController.text = ""; @@ -148,13 +138,13 @@ class _RealCreateScriptPageState extends State { body: Stack( children: [ Padding( - padding: EdgeInsets.fromLTRB(25, 20, 25, 100), + padding: const EdgeInsets.fromLTRB(25, 20, 25, 100), child: TextField( controller: textEditingController, expands: true, maxLines: null, decoration: InputDecoration( - hintText: '음성 인식을 통해 발음이 여기에 표시됩니다...', + hintText: 'voice_recognition'.tr, fillColor: Colors.white, filled: true, border: OutlineInputBorder( @@ -180,7 +170,7 @@ class _RealCreateScriptPageState extends State { borderRadius: BorderRadius.circular(40), onTap: toggleRecording, child: Padding( - padding: EdgeInsets.all(20), + padding: const EdgeInsets.all(20), child: Icon( isRecording ? Icons.stop : Icons.mic, size: 30, @@ -196,4 +186,4 @@ class _RealCreateScriptPageState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/script/analyze_screen.dart b/lib/views/script/analyze_screen.dart index f806d8e..d64ec90 100644 --- a/lib/views/script/analyze_screen.dart +++ b/lib/views/script/analyze_screen.dart @@ -1,15 +1,15 @@ import 'dart:async'; +import 'package:earlips/utilities/style/color_styles.dart'; +import 'package:earlips/views/base/default_back_appbar.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; -import 'package:speech_to_text/speech_to_text.dart' as stt; -import 'package:permission_handler/permission_handler.dart'; import 'package:earlips/viewModels/script/analyze_viewmodel.dart'; import '../../utilities/app_routes.dart'; class AnalyzeScreen extends StatefulWidget { - AnalyzeScreen({Key? key}) : super(key: key); + const AnalyzeScreen({super.key}); @override _AnalyzeScreenState createState() => _AnalyzeScreenState(); } @@ -21,7 +21,6 @@ class _AnalyzeScreenState extends State { void initState() { super.initState(); viewModel = Get.put(AnalyzeViewModel()); // 여기서 viewModel을 등록 - print(viewModel.userSenten); } @override @@ -31,9 +30,11 @@ class _AnalyzeScreenState extends State { FocusScope.of(context).requestFocus(FocusNode()); }, child: Scaffold( - appBar: AppBar( - title: Text('발음 및 빠르기 분석'), - centerTitle: true, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: DefaultBackAppbar( + title: 'analyze_appbar_title'.tr, + ), ), body: SingleChildScrollView( child: Column( @@ -42,14 +43,14 @@ class _AnalyzeScreenState extends State { alignment: Alignment.center, width: Get.width - 40, height: Get.height * 0.2, - margin: EdgeInsets.all(20.0), - padding: EdgeInsets.all(10.0), + margin: const EdgeInsets.all(20.0), + padding: const EdgeInsets.all(10.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15.0), border: Border.all(color: Colors.white), ), - child: _TopText(), // 이 부분은 상태를 표시하지 않으므로 그대로 유지합니다. + child: const _TopText(), // 이 부분은 상태를 표시하지 않으므로 그대로 유지합니다. ), Stack( children: [ @@ -57,14 +58,15 @@ class _AnalyzeScreenState extends State { alignment: Alignment.center, width: Get.width - 40, height: Get.height * 0.5, - margin: EdgeInsets.all(20.0), - padding: EdgeInsets.all(10.0), + margin: const EdgeInsets.all(20.0), + padding: const EdgeInsets.all(10.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15.0), border: Border.all(color: Colors.white), ), - child: TextStylingWidget(viewModel: viewModel), // viewModel을 전달합니다. + child: TextStylingWidget( + viewModel: viewModel), // viewModel을 전달합니다. ), ], ), @@ -78,8 +80,8 @@ class _AnalyzeScreenState extends State { onPressed: () { Get.toNamed(Routes.HOME); }, - child: Icon(Icons.home), - tooltip: '홈으로', + tooltip: 'analyze_home'.tr, + child: const Icon(Icons.home), ), ), ), @@ -90,7 +92,8 @@ class _AnalyzeScreenState extends State { class TextStylingWidget extends StatelessWidget { final AnalyzeViewModel viewModel; // viewModel을 받기 위한 생성자 파라미터를 추가합니다. - TextStylingWidget({required this.viewModel}); // 생성자를 통해 viewModel을 초기화합니다. + const TextStylingWidget( + {super.key, required this.viewModel}); // 생성자를 통해 viewModel을 초기화합니다. @override Widget build(BuildContext context) { @@ -98,14 +101,13 @@ class TextStylingWidget extends StatelessWidget { child: RichText( text: TextSpan( children: _buildTextSpans(), - style: TextStyle(color: Colors.black, fontSize: 16), + style: const TextStyle(color: Colors.black, fontSize: 16), ), textAlign: TextAlign.start, ), ); } - List _buildTextSpans() { List spans = []; int globalWordIndex = 0; @@ -115,7 +117,8 @@ class TextStylingWidget extends StatelessWidget { List wordSpans = []; for (String word in words) { - final bool isWrongWord = viewModel.wrongWordIndexes.contains(globalWordIndex); + final bool isWrongWord = + viewModel.wrongWordIndexes.contains(globalWordIndex); wordSpans.add(TextSpan( text: "$word ", style: TextStyle( @@ -136,20 +139,20 @@ class TextStylingWidget extends StatelessWidget { spans.add(TextSpan( children: wordSpans, style: TextStyle( - decoration: viewModel.wrongFastIndexes[i] != 0 ? TextDecoration.underline : TextDecoration.none, + decoration: viewModel.wrongFastIndexes[i] != 0 + ? TextDecoration.underline + : TextDecoration.none, decorationColor: underlineColor, // 밑줄 색상을 지정합니다. decorationStyle: TextDecorationStyle.solid, - decorationThickness: 3.0, + decorationThickness: 3.0, //밑줄을 밑으로 내리기 위한 값 - ), )); - spans.add(TextSpan(text: "\n")); + spans.add(const TextSpan(text: "\n")); } return spans; } - } class _TopText extends StatelessWidget { @@ -158,36 +161,43 @@ class _TopText extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.all(10.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 발음이 틀린 글씨에 대한 설명 RichText( text: TextSpan( - style: TextStyle(fontSize: 16, color: Colors.black), + style: const TextStyle(fontSize: 16, color: ColorSystem.black), children: [ - TextSpan(text: '발음이 틀린 글씨는 빨간색으로 표시됩니다 ex)'), + TextSpan(text: 'analyze_exmaple_title'.tr), // 예시에 적용할 스타일 TextSpan( - text: '강아지는 ', - style: TextStyle(color: Colors.red), + text: 'analyze_exmaple_content'.tr, + style: const TextStyle( + color: ColorSystem.red, + fontSize: 16, + fontWeight: FontWeight.w500), ), TextSpan( - text: '뛴다', + text: 'analyze_exmaple_content2'.tr, + style: const TextStyle( + color: ColorSystem.black, + fontSize: 16, + fontWeight: FontWeight.w500), ), ], ), ), - Padding( + const Padding( padding: EdgeInsets.only(top: 20), ), // 문장의 빠르기에 대한 설명 RichText( text: TextSpan( - style: TextStyle(fontSize: 16, color: Colors.black), + style: const TextStyle(fontSize: 16, color: ColorSystem.black), children: [ - TextSpan(text: '문장이 빠르면 빨강, 느리면 보라색 밑줄로 표시됩니다.'), + TextSpan(text: 'analyze_exmaple_alert'.tr), // 예시에 적용할 스타일 //들여쓰기 ], @@ -197,6 +207,4 @@ class _TopText extends StatelessWidget { ), ); } - } - diff --git a/lib/views/script/create_script_screen.dart b/lib/views/script/create_script_screen.dart index 6aa6625..c11a4ab 100644 --- a/lib/views/script/create_script_screen.dart +++ b/lib/views/script/create_script_screen.dart @@ -1,28 +1,28 @@ -import 'package:earlips/utilities/style/color_styles.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:earlips/viewModels/script/create_script_viewmodel.dart'; import 'package:get/get.dart'; -import 'package:earlips/viewModels/script/learning_session_screen_viewmodel.dart'; + class CreateScriptPage extends StatelessWidget { const CreateScriptPage({super.key}); @override Widget build(BuildContext context) { - return ChangeNotifierProvider( create: (_) => CreateScriptViewModel(), child: Consumer( builder: (context, model, child) => Scaffold( appBar: AppBar( - title: const Text('대본으로 학습하기'), + title: Text( + 'live_script_title'.tr, + ), centerTitle: true, actions: [ TextButton( onPressed: model.complete, - child: const Text( - '완료', - style: TextStyle( + child: Text( + 'appbar_done'.tr, + style: const TextStyle( color: Colors.black, fontSize: 20, ), @@ -45,7 +45,7 @@ class CreateScriptPage extends StatelessWidget { expands: true, maxLines: null, decoration: InputDecoration( - hintText: '대본을 입력하세요...', + hintText: 'live_script_hint'.tr, fillColor: Colors.white, filled: true, border: OutlineInputBorder( diff --git a/lib/views/script/learning_session_screen.dart b/lib/views/script/learning_session_screen.dart index 75f506a..8934748 100644 --- a/lib/views/script/learning_session_screen.dart +++ b/lib/views/script/learning_session_screen.dart @@ -1,3 +1,4 @@ +import 'package:earlips/views/base/default_back_appbar.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -8,14 +9,15 @@ import 'package:earlips/viewModels/script/learning_session_screen_viewmodel.dart import 'package:intl/intl.dart'; // DateFormat을 사용하기 위해 추가 class LearningSessionScreen extends StatefulWidget { - LearningSessionScreen({Key? key}) : super(key: key); + const LearningSessionScreen({super.key}); @override State createState() => _LearningSessionScreenState(); } class _LearningSessionScreenState extends State { - final viewModel = Get.put(LearningSessionScreenViewModel()); // ViewModel 인스턴스 생성 + final viewModel = + Get.put(LearningSessionScreenViewModel()); // ViewModel 인스턴스 생성 @override void initState() { @@ -36,19 +38,15 @@ class _LearningSessionScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text('대본으로 학습하기'), - centerTitle: true, - leading: IconButton( - icon: Icon(Icons.arrow_back), - onPressed: () { - Navigator.of(context).pop(); - }, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(kToolbarHeight), + child: DefaultBackAppbar( + title: 'live_script_title'.tr, ), ), body: Obx(() { if (viewModel.isLoading.value) { - return Center(child: CircularProgressIndicator()); + return const Center(child: CircularProgressIndicator()); } else { return StreamBuilder( stream: FirebaseAuth.instance.authStateChanges(), @@ -56,16 +54,17 @@ class _LearningSessionScreenState extends State { final bool isLoggedIn = snapshot.hasData; if (!isLoggedIn) { // 로그인하지 않았을 경우 더미 데이터로 UI 구성 - final dummyDate = DateFormat('yyyy/MM/dd').format(DateTime.now()); + final dummyDate = + DateFormat('yyyy/MM/dd').format(DateTime.now()); final dummyParagraph = Paragraph( title: "로그인을 하면 생성한 대본이 기록됩니다!", text: "로그인을 하면 생성한 대본이 저장되어 대본을 토대로 학습할 수 있습니다!", dateFormat: dummyDate, ); return Container( - margin: const EdgeInsets.only(left: 20, top: 20), - height: Get.height * 0.15, - width: Get.width*0.9, + margin: const EdgeInsets.only(left: 20, top: 20), + height: Get.height * 0.15, + width: Get.width * 0.9, child: _buildParagraphContainer(dummyParagraph)); } else { // 로그인 했을 경우의 UI 구성 @@ -77,7 +76,8 @@ class _LearningSessionScreenState extends State { var paragraph = viewModel.paragraphs[index]; return _buildParagraphContainer(paragraph); }, - separatorBuilder: (context, index) => const SizedBox(height: 20), + separatorBuilder: (context, index) => + const SizedBox(height: 20), ), ); } @@ -86,7 +86,7 @@ class _LearningSessionScreenState extends State { } }), floatingActionButton: FloatingActionButton( - onPressed: () => Get.to(() => CreateScriptPage()), + onPressed: () => Get.to(() => const CreateScriptPage()), child: const Icon(Icons.add), ), ); @@ -114,7 +114,8 @@ class _LearningSessionScreenState extends State { child: SmallCard(text: paragraph.dateFormat), ), ListTile( - contentPadding: const EdgeInsets.only(left: 20, right: 20, bottom: 30), + contentPadding: + const EdgeInsets.only(left: 20, right: 20, bottom: 30), title: Container( alignment: Alignment.center, child: Text( @@ -126,7 +127,8 @@ class _LearningSessionScreenState extends State { ), ), onTap: () { - Get.to(() => UserScriptScreen(title: paragraph.title, text: paragraph.text)); + Get.to(() => UserScriptScreen( + title: paragraph.title, text: paragraph.text)); }, ), ], diff --git a/lib/views/script/user_script_screen.dart b/lib/views/script/user_script_screen.dart index 7fd7165..9eda242 100644 --- a/lib/views/script/user_script_screen.dart +++ b/lib/views/script/user_script_screen.dart @@ -7,8 +7,11 @@ import 'package:get/get.dart'; class UserScriptScreen extends StatelessWidget { final String title; final String text; - const UserScriptScreen({Key? key, required this.title, required this.text, }) : super(key: key); - + const UserScriptScreen({ + super.key, + required this.title, + required this.text, + }); @override Widget build(BuildContext context) { @@ -22,9 +25,9 @@ class UserScriptScreen extends StatelessWidget { actions: [ TextButton( onPressed: model.complete, - child: const Text( - '완료', - style: TextStyle( + child: Text( + 'appbar_done'.tr, + style: const TextStyle( color: Colors.black, fontSize: 20, ), @@ -41,27 +44,21 @@ class UserScriptScreen extends StatelessWidget { Expanded( flex: 1, child: SingleChildScrollView( - child: Container( - child: Padding( - padding: const EdgeInsets.all(0.0), - child: Container( - margin: const EdgeInsets.all(10.0), - padding: const EdgeInsets.all(20.0), - //가장자리 둥글게 - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15.0), - - color: Colors.white, - - ), + child: Padding( + padding: const EdgeInsets.all(0.0), + child: Container( + margin: const EdgeInsets.all(10.0), + padding: const EdgeInsets.all(20.0), + //가장자리 둥글게 + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15.0), + color: Colors.white, + ), - child: Text( - text, - style: const TextStyle(fontSize: 16, - fontFamily: 'Pretendard', - fontWeight: FontWeight.bold - ), - ), + child: Text( + text, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), ), ), ), diff --git a/lib/views/study/date_study_screen.dart b/lib/views/study/date_study_screen.dart index e684bbd..ba7c260 100644 --- a/lib/views/study/date_study_screen.dart +++ b/lib/views/study/date_study_screen.dart @@ -37,7 +37,7 @@ class DateStudyScreen extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text('등록된 학습 기록이 없습니다.'), + Text('no_study_record'.tr), const SizedBox(height: 20), // 학습하러가기 ElevatedButton( @@ -51,9 +51,9 @@ class DateStudyScreen extends StatelessWidget { onPressed: () { Get.back(); }, - child: const Text( - '학습하러가기', - style: TextStyle( + child: Text( + 'go_study'.tr, + style: const TextStyle( color: Colors.white, fontSize: 16.0, ), @@ -94,7 +94,9 @@ class DateStudyScreen extends StatelessWidget { title: Container( alignment: Alignment.center, child: Text( - session.text.length > 13 ? '${session.text.substring(0, 13)}...' : session.text, + session.text.length > 13 + ? '${session.text.substring(0, 13)}...' + : session.text, style: const TextStyle( fontSize: 24.0, fontWeight: FontWeight.bold, @@ -108,12 +110,12 @@ class DateStudyScreen extends StatelessWidget { () => WordScreen( // session type에 따라 다른 tttle title: session.type == 0 - ? '음소' + ? 'row_card_badge_1'.tr : (session.type == 1 - ? '단어' + ? 'row_card_badge_2'.tr : (session.type == 2 - ? '문장' - : '문단')), + ? 'row_card_badge_3'.tr + : 'row_card_badge_4'.tr)), type: 0, ), arguments: session.index); diff --git a/lib/views/study/study_main.dart b/lib/views/study/study_main.dart index 4222b02..3becf03 100644 --- a/lib/views/study/study_main.dart +++ b/lib/views/study/study_main.dart @@ -60,9 +60,9 @@ class StudyMain extends BaseScreen { mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( - "로그인이 필요합니다.", - style: TextStyle( + Text( + "homeHeaderGuest".tr, + style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: ColorSystem.black, diff --git a/lib/views/study/widget/contribution.dart b/lib/views/study/widget/contribution.dart index 675e8e7..3ed1ecb 100644 --- a/lib/views/study/widget/contribution.dart +++ b/lib/views/study/widget/contribution.dart @@ -81,16 +81,6 @@ class _ContributeState extends State { ], ), ), - - // DecorationItem( - // date: DateTime.now().add(const Duration(days: 3)), - // decoration: const Text( - // 'Holiday', - // style: TextStyle( - // color: Colors.brown, - // fontWeight: FontWeight.w600, - // ), - // )), ), ), ], diff --git a/lib/views/study/widget/study_card_widget.dart b/lib/views/study/widget/study_card_widget.dart index 6c8c94d..2719d15 100644 --- a/lib/views/study/widget/study_card_widget.dart +++ b/lib/views/study/widget/study_card_widget.dart @@ -43,7 +43,6 @@ class StudyCardWidget extends StatelessWidget { Text( title, style: const TextStyle( - fontFamily: 'Pretendard-Bold', fontSize: 15, fontWeight: FontWeight.bold, ), @@ -51,7 +50,6 @@ class StudyCardWidget extends StatelessWidget { Text( subtitle, style: const TextStyle( - fontFamily: 'Pretendard-Regular', fontSize: 12, ), ), diff --git a/lib/views/study/widget/study_main_body_widget.dart b/lib/views/study/widget/study_main_body_widget.dart index 8f301dc..5fbc976 100644 --- a/lib/views/study/widget/study_main_body_widget.dart +++ b/lib/views/study/widget/study_main_body_widget.dart @@ -29,24 +29,24 @@ class StudyNainBodyWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ StudyCardWidget( - title: "음소 교정", - subtitle: "옴소 교정 및 발음 테스트", + title: "study_main_title_1".tr, + subtitle: "study_main_subtitle_1".tr, imagePath: "assets/images/study/1.png", onTap: () { - Get.to(() => const WordScreen( - title: "음소 교정", + Get.to(() => WordScreen( + title: "study_main_title_1".tr, type: 0, )); }, imgSize: 85, ), StudyCardWidget( - title: "단어 교정", - subtitle: "단어 교정 및 발음 테스트", + title: 'study_main_title_2'.tr, + subtitle: "study_main_subtitle_2".tr, imagePath: "assets/images/study/2.png", onTap: () { - Get.to(() => const WordScreen( - title: "단어 교정", + Get.to(() => WordScreen( + title: 'study_main_title_2'.tr, type: 1, )); }, @@ -62,24 +62,23 @@ class StudyNainBodyWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ StudyCardWidget( - title: "문장 교정", - subtitle: "문장 교정 및 발음 테스트", + title: "study_main_title_3".tr, + subtitle: "study_main_subtitle_3".tr, imagePath: "assets/images/study/3.png", onTap: () { - Get.to(() => const WordScreen( - title: "문장 교정", + Get.to(() => WordScreen( + title: "study_main_title_3".tr, type: 2, )); }, imgSize: 85, ), StudyCardWidget( - title: "문단 교정", - subtitle: "대본 입력 및 발음 테스트", + title: "study_main_title_4".tr, + subtitle: "study_main_subtitle_4".tr, imagePath: "assets/images/study/4.png", onTap: () { - Get.to(() => LearningSessionScreen( - )); + Get.to(() => LearningSessionScreen()); }, imgSize: 85, ), diff --git a/lib/views/study/widget/study_row_card_widget.dart b/lib/views/study/widget/study_row_card_widget.dart index 267bf29..c719b8c 100644 --- a/lib/views/study/widget/study_row_card_widget.dart +++ b/lib/views/study/widget/study_row_card_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; class SmallCard extends StatelessWidget { final int type; @@ -9,15 +10,15 @@ class SmallCard extends StatelessWidget { String get name { switch (type) { case 0: - return '음소'; + return 'row_card_badge_1'.tr; case 1: - return '단어'; + return 'row_card_badge_2'.tr; case 2: - return '문장'; + return 'row_card_badge_3'.tr; case 3: - return '문단'; + return 'row_card_badge_4'.tr; default: - return '음소'; + return 'row_card_badge_1'.tr; } } diff --git a/lib/views/test/sound_recorder.dart b/lib/views/test/sound_recorder.dart deleted file mode 100644 index b9f78bb..0000000 --- a/lib/views/test/sound_recorder.dart +++ /dev/null @@ -1 +0,0 @@ -// TODO Implement this library. \ No newline at end of file diff --git a/lib/views/test/test.dart b/lib/views/test/test.dart index c398ec8..bc0db95 100644 --- a/lib/views/test/test.dart +++ b/lib/views/test/test.dart @@ -5,6 +5,8 @@ import 'dart:async'; import 'dart:io'; class SimpleRecorder extends StatefulWidget { + const SimpleRecorder({super.key}); + @override _SimpleRecorderState createState() => _SimpleRecorderState(); } @@ -31,7 +33,8 @@ class _SimpleRecorderState extends State { if (!_isRecorderInitialized || _isRecording) return; final directory = await getApplicationDocumentsDirectory(); - final filePath = '${directory.path}/${DateTime.now().millisecondsSinceEpoch}.aac'; + final filePath = + '${directory.path}/${DateTime.now().millisecondsSinceEpoch}.aac'; await _audioRecorder!.startRecorder( toFile: filePath, @@ -66,7 +69,7 @@ class _SimpleRecorderState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Simple Recorder'), + title: const Text('Simple Recorder'), ), body: Center( child: Column( @@ -74,11 +77,11 @@ class _SimpleRecorderState extends State { children: [ ElevatedButton( onPressed: _isRecording ? null : _startRecording, - child: Text('Start Recording'), + child: const Text('Start Recording'), ), ElevatedButton( onPressed: _isRecording ? _stopRecording : null, - child: Text('Stop Recording'), + child: const Text('Stop Recording'), ), ], ), @@ -87,4 +90,4 @@ class _SimpleRecorderState extends State { } } -void main() => runApp(MaterialApp(home: SimpleRecorder())); +void main() => runApp(const MaterialApp(home: SimpleRecorder())); diff --git a/lib/views/word/widget/blue_back_appbar.dart b/lib/views/word/widget/blue_back_appbar.dart index d41e7d9..6f3c763 100644 --- a/lib/views/word/widget/blue_back_appbar.dart +++ b/lib/views/word/widget/blue_back_appbar.dart @@ -30,9 +30,9 @@ class BlueBackAppbar extends DefaultBackAppbar { ), ), icon: SvgPicture.asset("assets/icons/back_white.svg"), - label: const Text( - "뒤로", - style: TextStyle( + label: Text( + "appbar_back".tr, + style: const TextStyle( color: ColorSystem.white, fontSize: 14, fontWeight: FontWeight.w600, diff --git a/lib/views/word/widget/fail_word_dialog_widget.dart b/lib/views/word/widget/fail_word_dialog_widget.dart index 321221c..daca3d3 100644 --- a/lib/views/word/widget/fail_word_dialog_widget.dart +++ b/lib/views/word/widget/fail_word_dialog_widget.dart @@ -7,8 +7,8 @@ Future FailWordDialogWidget( WordViewModel wordViewModel, PageController pageController) { return Get.dialog( AlertDialog( - title: const Text('학습 실패'), - content: const Text('다시 한번 녹음해주세요.'), + title: Text('fail_study_title'.tr), + content: Text('fail_study_content'.tr), backgroundColor: ColorSystem.white, surfaceTintColor: ColorSystem.white, actions: [ @@ -16,7 +16,7 @@ Future FailWordDialogWidget( onPressed: () async { Get.back(); }, - child: const Text('다시하기'), + child: Text('fail_study_retry'.tr), ), ElevatedButton( onPressed: () async { @@ -32,13 +32,12 @@ Future FailWordDialogWidget( duration: const Duration(milliseconds: 300), curve: Curves.ease, ); - // currentIndex 증가 wordViewModel.currentIndex.value = wordViewModel.currentIndex.value + 1; } }, - child: const Text('다음 단어'), + child: Text('fail_study_next_word'.tr), ), ], ), diff --git a/lib/views/word/widget/highlight_mistake_text_widget.dart b/lib/views/word/widget/highlight_mistake_text_widget.dart index d48fe59..db6766b 100644 --- a/lib/views/word/widget/highlight_mistake_text_widget.dart +++ b/lib/views/word/widget/highlight_mistake_text_widget.dart @@ -1,4 +1,6 @@ +import 'package:earlips/utilities/style/color_styles.dart'; import 'package:flutter/material.dart'; +import 'package:get/get_utils/src/extensions/internacionalization.dart'; class HighlightMistakesTextWidget extends StatelessWidget { final List userWords; // 사용자가 발음한 단어 리스트 @@ -13,7 +15,7 @@ class HighlightMistakesTextWidget extends StatelessWidget { @override Widget build(BuildContext context) { return wrongIndices[0] == -1 - ? const Text("틀린단어 없음") + ? Text("fail_word".tr) : RichText( text: TextSpan( children: userWords.asMap().entries.map((entry) { @@ -24,8 +26,8 @@ class HighlightMistakesTextWidget extends StatelessWidget { text: "$word ", // 단어와 공백을 포함시킵니다. style: TextStyle( color: isWrong - ? Colors.red - : Colors.black, // 잘못된 부분은 빨간색으로, 그 외는 검정색으로 표시 + ? ColorSystem.red + : ColorSystem.black, // 잘못된 부분은 빨간색으로, 그 외는 검정색으로 표시 ), ); }).toList(), diff --git a/lib/views/word/widget/sentence_alert_widget.dart b/lib/views/word/widget/sentence_alert_widget.dart index 6731837..220d879 100644 --- a/lib/views/word/widget/sentence_alert_widget.dart +++ b/lib/views/word/widget/sentence_alert_widget.dart @@ -9,8 +9,8 @@ Future SentenceAlertWidget(RecordViewModel model, WordViewModel wordViewModel, PageController pageController) { return Get.dialog( AlertDialog( - title: const Text('문장 테스트 결과', - style: TextStyle( + title: Text('sentence_test_result_title'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 20, )), @@ -22,9 +22,9 @@ Future SentenceAlertWidget(RecordViewModel model, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - '문장', - style: TextStyle( + Text( + 'sentence_test_result_sentence'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 16, fontWeight: FontWeight.w600, @@ -43,9 +43,9 @@ Future SentenceAlertWidget(RecordViewModel model, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - '사용자 발음', - style: TextStyle( + Text( + 'sentence_test_result_user_sentence'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 16, fontWeight: FontWeight.w600, @@ -61,16 +61,16 @@ Future SentenceAlertWidget(RecordViewModel model, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - '틀린 부분', - style: TextStyle( + Text( + 'sentence_test_result_wrong'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 16, fontWeight: FontWeight.w600, ), ), Text( - '${model.response['wrong'] != null && model.response['wrong'][0] != -1 ? model.response['wrong'].join(", ") : "알 수 없음"} 번째 단어', + '${model.response['wrong'] != null && model.response['wrong'][0] != -1 ? model.response['wrong'].join(", ") : "unknown".tr}', style: const TextStyle( color: ColorSystem.black, fontSize: 14, @@ -82,24 +82,24 @@ Future SentenceAlertWidget(RecordViewModel model, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - '볼륨', - style: TextStyle( + Text( + 'sentence_test_result_volume'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 16, fontWeight: FontWeight.w600, ), ), - Text('${model.response['loudness'] ?? '알 수 없음'}'), + Text('${model.response['loudness'] ?? 'unknown'.tr}'), ], ), const SizedBox(height: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - '변동성', - style: TextStyle( + Text( + 'sentence_test_result_pitch'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 16, fontWeight: FontWeight.w600, @@ -107,8 +107,10 @@ Future SentenceAlertWidget(RecordViewModel model, ), Text( model.response['variance'] == null - ? "알 수 없음" - : (model.response['variance'] == 1 ? "일정함" : "변동 폭 큼"), + ? "unknown".tr + : (model.response['variance'] == 1 + ? "sentence_test_result_stability".tr + : "sentence_test_result_pitch_high".tr), style: const TextStyle( color: ColorSystem.black, fontSize: 14, @@ -120,9 +122,9 @@ Future SentenceAlertWidget(RecordViewModel model, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - '속도', - style: TextStyle( + Text( + 'sentence_test_result_speed'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 16, fontWeight: FontWeight.w600, @@ -142,9 +144,9 @@ Future SentenceAlertWidget(RecordViewModel model, ), actions: [ TextButton( - child: const Text( - '다음', - style: TextStyle( + child: Text( + 'next'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 16, ), @@ -156,7 +158,6 @@ Future SentenceAlertWidget(RecordViewModel model, wordViewModel.currentIndex.value < wordViewModel.wordList.length - 1 ? Get.back() : Get.offAllNamed('/'); - // 다음 단어로 넘어가기 if (wordViewModel.currentIndex.value < wordViewModel.wordList.length - 1) { @@ -179,16 +180,16 @@ Future SentenceAlertWidget(RecordViewModel model, String _getSpeedDescription(double speed) { switch (speed) { case 0: - return '엄청 느림'; + return 'sentence_test_result_speed_very_slow'.tr; case 0.5: - return '느림'; + return 'sentence_test_result_speed_slow'.tr; case 1: - return '평범'; + return 'sentence_test_result_speed_normal'.tr; case 1.5: - return '약간 빠름'; + return 'sentence_test_result_speed_fast'.tr; case 2: - return '완전 빠름'; + return 'sentence_test_result_speed_very_fast'.tr; default: - return '알 수 없음'; // 속도 값이 주어진 범위에 없는 경우 + return 'unknown'.tr; // 속도 값이 주어진 범위에 없는 경우 } } diff --git a/lib/views/word/widget/sentence_guide_widget.dart b/lib/views/word/widget/sentence_guide_widget.dart index 2116dde..001a456 100644 --- a/lib/views/word/widget/sentence_guide_widget.dart +++ b/lib/views/word/widget/sentence_guide_widget.dart @@ -61,9 +61,9 @@ class PronunciationGuidelinesWidget extends StatelessWidget { bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - getTitlesWidget: (value, meta) => const Text('목소리 높이', - style: - TextStyle(fontSize: 14, fontWeight: FontWeight.w600)), + getTitlesWidget: (value, meta) => Text('sound_pitch'.tr, + style: const TextStyle( + fontSize: 14, fontWeight: FontWeight.w600)), ), ), ), @@ -91,7 +91,7 @@ class PronunciationGuidelinesWidget extends StatelessWidget { PieChartSectionData( value: 100, color: ColorSystem.main, - title: '변동 일정', + title: 'sound_pitch_stability'.tr, titleStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, @@ -102,7 +102,7 @@ class PronunciationGuidelinesWidget extends StatelessWidget { PieChartSectionData( value: variance == -1 ? 100 : 0, color: ColorSystem.gray4, - title: '변동 일정', + title: 'sound_pitch_stability'.tr, titleStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, @@ -113,7 +113,7 @@ class PronunciationGuidelinesWidget extends StatelessWidget { PieChartSectionData( value: variance == -1 ? 100 : 0, color: Colors.red.shade400, - title: '변동', + title: 'sound_pitch_high'.tr, titleStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, @@ -162,7 +162,7 @@ class PronunciationGuidelinesWidget extends StatelessWidget { interval: 1, // Show labels for every speed value reservedSize: 30, getTitlesWidget: (value, meta) => Text( - '${value.toInt()}배', + '${value.toInt()}x', style: const TextStyle(fontSize: 14), ), ), @@ -170,7 +170,7 @@ class PronunciationGuidelinesWidget extends StatelessWidget { bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - getTitlesWidget: (value, meta) => const Text('속도'), + getTitlesWidget: (value, meta) => Text('sound_speed'.tr), ), ), ), diff --git a/lib/views/word/widget/sucess_word_dialog_widget.dart b/lib/views/word/widget/sucess_word_dialog_widget.dart index bac5266..407aa6a 100644 --- a/lib/views/word/widget/sucess_word_dialog_widget.dart +++ b/lib/views/word/widget/sucess_word_dialog_widget.dart @@ -6,8 +6,8 @@ Future SucessWordDialogWidget( WordViewModel wordViewModel, PageController pageController) { return Get.dialog( AlertDialog( - title: const Text('학습 완료'), - content: const Text('다음으로 넘어가려면 아래 버튼을 눌러주세요.'), + title: Text('success_study_title'.tr), + content: Text('success_study_content'.tr), actions: [ ElevatedButton( onPressed: () async { @@ -31,7 +31,7 @@ Future SucessWordDialogWidget( wordViewModel.currentIndex.value + 1; } }, - child: const Text('다음 단어'), + child: Text('fail_study_next_word'.tr), ), ], ), diff --git a/lib/views/word/widget/word_result_dialog_widget.dart b/lib/views/word/widget/word_result_dialog_widget.dart index ecb602b..9f42f9b 100644 --- a/lib/views/word/widget/word_result_dialog_widget.dart +++ b/lib/views/word/widget/word_result_dialog_widget.dart @@ -15,9 +15,9 @@ Future WordResultDialogWidget(RecordViewModel model, ), insetPadding: const EdgeInsets.all(20), surfaceTintColor: ColorSystem.white, - title: const Text( - '결과', - style: TextStyle( + title: Text( + 'result_title'.tr, + style: const TextStyle( color: ColorSystem.black, fontSize: 20, ), @@ -33,8 +33,8 @@ Future WordResultDialogWidget(RecordViewModel model, children: [ Text( model.response['pronunciation'] != null - ? '발음: ${model.response['pronunciation']}' - : '너무 빠르거나 느립니다. 다시 녹음해주세요', + ? '${'pitch'.tr}: ${model.response['pronunciation']}' + : 'pitch_null'.tr, style: const TextStyle( color: ColorSystem.black, fontSize: 16, @@ -42,7 +42,7 @@ Future WordResultDialogWidget(RecordViewModel model, ), Text( model.response['similarity'] != null - ? '정확도: ${model.response['similarity']}' + ? '${'accuracy'.tr}: ${model.response['similarity']}' : "", style: const TextStyle( color: ColorSystem.black, @@ -71,10 +71,10 @@ Future WordResultDialogWidget(RecordViewModel model, borderRadius: BorderRadius.circular(20), border: Border.all(color: ColorSystem.black), ), - child: const Text( - '확인', + child: Text( + 'cofirm'.tr, textAlign: TextAlign.center, - style: TextStyle( + style: const TextStyle( color: ColorSystem.black, ), ), diff --git a/lib/views/word/widget/word_youtube_widget.dart b/lib/views/word/widget/word_youtube_widget.dart index d64c111..458ac63 100644 --- a/lib/views/word/widget/word_youtube_widget.dart +++ b/lib/views/word/widget/word_youtube_widget.dart @@ -20,7 +20,7 @@ class _YoutubeWordPlayerState extends State { if (wordViewModel.isLoading.value) { return const Center(child: CircularProgressIndicator()); } else { - return const Center(child: Text('아직 단어가 없습니다.')); + return Center(child: Text('no_data'.tr)); } } else { final videoId = wordViewModel diff --git a/lib/views/word/word_screen.dart b/lib/views/word/word_screen.dart index 6f69dfc..93ed47a 100644 --- a/lib/views/word/word_screen.dart +++ b/lib/views/word/word_screen.dart @@ -59,8 +59,8 @@ class WordScreen extends StatelessWidget { ), Text( type == 2 - ? "문장의 높낮이를 참고하세요" - : "아래의 혀, 입술 모양을 따라 말해보세요!", + ? "word_type_2_height".tr + : "word_type_12_height".tr, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600,