diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json new file mode 100644 index 0000000..6e6f10c --- /dev/null +++ b/assets/translations/en-US.json @@ -0,0 +1,83 @@ +{ + "greeting_time": "Good Afternoon", + "greeting_user": "Hello {user}", + "create_room": "Create Room", + "join_room": "Join Room", + "create_quiz": "Create Quiz", + "ready_new_challenge": "Ready for a new challenge?", + "search_or_select_category": "Search or select by category", + "search_for_quizzes": "Search for quizzes...", + "quiz_recommendation": "Recommended Quiz", + "log_in": "Log In", + "sign_in": "Sign In", + "email": "Email", + "enter_your_email": "Enter Your Email", + "password": "Password", + "enter_your_password": "Enter Your Password", + "or": "OR", + "register_title": "Register", + "full_name": "Full Name", + "birth_date": "Birth Date", + "phone_optional": "Phone Number (Optional)", + "verify_password": "Verify Password", + "register_button": "Register", + "nav_home": "Home", + "nav_search": "Search", + "nav_library": "Library", + "nav_history": "History", + "nav_profile": "Profile", + "quiz_popular": "Popular Quiz", + "see_all": "See All", + + "library_title": "Quiz Library", + "library_description": "A collection of quiz questions created for study.", + "no_quiz_available": "No quizzes available yet.", + "quiz_count_label": "Quizzes", + "quiz_count_named": "{total} Quizzes", + + "history_title": "Quiz History", + "history_subtitle": "Review the quizzes you've taken", + "no_history": "You don't have any quiz history yet", + "score_label": "Score: {correct}/{total}", + "duration_minutes": "{minute} minutes", + + "edit_profile": "Edit Profile", + "logout": "Logout", + "total_quiz": "Total Quiz", + "avg_score": "Average Score", + + "history_detail_title": "Quiz Detail", + "correct_answer": "Correct", + "score": "Score", + "time_taken": "Time", + "duration_seconds": "{second}s", + + "your_answer": "Your answer: {answer}", + "question_type_option": "Multiple Choice", + "question_type_fill": "Fill in the Blank", + "question_type_true_false": "True / False", + "question_type_unknown": "Unknown Type", + + "enter_room_code": "Enter Room Code", + "room_code_hint": "AB123C", + "join_now": "Join Now", + + "create_quiz_title": "Create Quiz", + "save_all": "Save All", + "mode_generate": "Generate", + "mode_manual": "Manual", + + "quiz_play_title": "Answer Quiz", + "ready_in": "Ready in {second}", + "question_indicator": "Question {current} of {total}", + "yes": "Yes", + "no": "No", + "next": "Next", + + "quiz_preview_title": "Preview Quiz", + "quiz_title_label": "Title", + "quiz_description_label": "Short Description", + "quiz_subject_label": "Subject", + "make_quiz_public": "Make Quiz Public", + "save_quiz": "Save Quiz" +} diff --git a/assets/translations/id-ID.json b/assets/translations/id-ID.json new file mode 100644 index 0000000..6bb4eb6 --- /dev/null +++ b/assets/translations/id-ID.json @@ -0,0 +1,83 @@ +{ + "greeting_time": "Selamat Siang", + "greeting_user": "Halo {user}", + "create_room": "Buat Ruangan", + "join_room": "Gabung Ruangan", + "create_quiz": "Buat Kuis", + "ready_new_challenge": "Siap untuk tantangan baru?", + "search_or_select_category": "Cari atau pilih berdasarkan kategori", + "search_for_quizzes": "Cari kuis...", + "quiz_recommendation": "Rekomendasi Kuis", + "log_in": "Masuk", + "sign_in": "Masuk", + "email": "Email", + "enter_your_email": "Masukkan Email Anda", + "password": "Kata Sandi", + "enter_your_password": "Masukkan Kata Sandi Anda", + "or": "ATAU", + "register_title": "Daftar", + "full_name": "Nama Lengkap", + "birth_date": "Tanggal Lahir", + "phone_optional": "Nomor Telepon (Opsional)", + "verify_password": "Verifikasi Kata Sandi", + "register_button": "Daftar", + "nav_home": "Beranda", + "nav_search": "Cari", + "nav_library": "Pustaka", + "nav_history": "Riwayat", + "nav_profile": "Profil", + "quiz_popular": "Kuis Populer", + "see_all": "Lihat Semua", + + "library_title": "Pustaka Kuis", + "library_description": "Kumpulan pertanyaan kuis untuk belajar.", + "no_quiz_available": "Belum ada kuis yang tersedia.", + "quiz_count_label": "Kuis", + "quiz_count_named": "{total} Kuis", + + "history_title": "Riwayat Kuis", + "history_subtitle": "Tinjau kuis yang telah kamu kerjakan", + "no_history": "Kamu belum memiliki riwayat kuis", + "score_label": "Skor: {correct}/{total}", + "duration_minutes": "{minute} menit", + + "edit_profile": "Edit Profil", + "logout": "Keluar", + "total_quiz": "Total Kuis", + "avg_score": "Skor Rata-rata", + + "history_detail_title": "Detail Kuis", + "correct_answer": "Benar", + "score": "Skor", + "time_taken": "Waktu", + "duration_seconds": "{second} detik", + + "your_answer": "Jawaban kamu: {answer}", + "question_type_option": "Pilihan Ganda", + "question_type_fill": "Isian Kosong", + "question_type_true_false": "Benar / Salah", + "question_type_unknown": "Tipe Tidak Dikenal", + + "enter_room_code": "Masukkan Kode Ruangan", + "room_code_hint": "AB123C", + "join_now": "Gabung Sekarang", + + "create_quiz_title": "Buat Kuis", + "save_all": "Simpan Semua", + "mode_generate": "Otomatis", + "mode_manual": "Manual", + + "quiz_play_title": "Kerjakan Kuis", + "ready_in": "Siap dalam {second}", + "question_indicator": "Pertanyaan {current} dari {total}", + "yes": "Ya", + "no": "Tidak", + "next": "Berikutnya", + + "quiz_preview_title": "Pratinjau Kuis", + "quiz_title_label": "Judul", + "quiz_description_label": "Deskripsi Singkat", + "quiz_subject_label": "Mata Pelajaran", + "make_quiz_public": "Jadikan Kuis Publik", + "save_quiz": "Simpan Kuis" +} diff --git a/assets/translations/ms-MY.json b/assets/translations/ms-MY.json new file mode 100644 index 0000000..a5ec604 --- /dev/null +++ b/assets/translations/ms-MY.json @@ -0,0 +1,83 @@ +{ + "greeting_time": "Selamat Tengah Hari", + "greeting_user": "Hai {user}", + "create_room": "Cipta Bilik", + "join_room": "Sertai Bilik", + "create_quiz": "Cipta Kuiz", + "ready_new_challenge": "Bersedia untuk cabaran baharu?", + "search_or_select_category": "Cari atau pilih mengikut kategori", + "search_for_quizzes": "Cari kuiz...", + "quiz_recommendation": "Kuiz Disyorkan", + "log_in": "Log Masuk", + "sign_in": "Daftar Masuk", + "email": "E-mel", + "enter_your_email": "Masukkan E-mel Anda", + "password": "Kata Laluan", + "enter_your_password": "Masukkan Kata Laluan Anda", + "or": "ATAU", + "register_title": "Daftar", + "full_name": "Nama Penuh", + "birth_date": "Tarikh Lahir", + "phone_optional": "Nombor Telefon (Pilihan)", + "verify_password": "Sahkan Kata Laluan", + "register_button": "Daftar", + "nav_home": "Laman Utama", + "nav_search": "Cari", + "nav_library": "Perpustakaan", + "nav_history": "Sejarah", + "nav_profile": "Profil", + "quiz_popular": "Kuiz Popular", + "see_all": "Lihat Semua", + + "library_title": "Perpustakaan Kuiz", + "library_description": "Koleksi soalan kuiz untuk pembelajaran.", + "no_quiz_available": "Tiada kuiz tersedia lagi.", + "quiz_count_label": "Kuiz", + "quiz_count_named": "{total} Kuiz", + + "history_title": "Sejarah Kuiz", + "history_subtitle": "Semak semula kuiz yang telah anda jawab", + "no_history": "Anda belum mempunyai sejarah kuiz", + "score_label": "Skor: {correct}/{total}", + "duration_minutes": "{minute} minit", + + "edit_profile": "Edit Profil", + "logout": "Log Keluar", + "total_quiz": "Jumlah Kuiz", + "avg_score": "Skor Purata", + + "history_detail_title": "Butiran Kuiz", + "correct_answer": "Betul", + "score": "Skor", + "time_taken": "Masa", + "duration_seconds": "{second} saat", + + "your_answer": "Jawapan anda: {answer}", + "question_type_option": "Pilihan Berganda", + "question_type_fill": "Isi Tempat Kosong", + "question_type_true_false": "Betul / Salah", + "question_type_unknown": "Jenis Tidak Diketahui", + + "enter_room_code": "Masukkan Kod Bilik", + "room_code_hint": "AB123C", + "join_now": "Sertai Sekarang", + + "create_quiz_title": "Cipta Kuiz", + "save_all": "Simpan Semua", + "mode_generate": "Jana", + "mode_manual": "Manual", + + "quiz_play_title": "Jawab Kuiz", + "ready_in": "Bersedia dalam {second}", + "question_indicator": "Soalan {current} daripada {total}", + "yes": "Ya", + "no": "Tidak", + "next": "Seterusnya", + + "quiz_preview_title": "Pratonton Kuiz", + "quiz_title_label": "Tajuk", + "quiz_description_label": "Deskripsi Ringkas", + "quiz_subject_label": "Subjek", + "make_quiz_public": "Jadikan Kuiz Umum", + "save_quiz": "Simpan Kuiz" +} diff --git a/lib/app/app.dart b/lib/app/app.dart index 9bcc073..1e707e3 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:quiz_app/app/bindings/initial_bindings.dart'; @@ -9,7 +10,10 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( + localizationsDelegates: context.localizationDelegates, debugShowCheckedModeBanner: false, + supportedLocales: context.supportedLocales, + locale: context.locale, title: 'Quiz App', initialBinding: InitialBindings(), initialRoute: AppRoutes.splashScreen, diff --git a/lib/component/widget/quiz_item_wa_component.dart b/lib/component/widget/quiz_item_wa_component.dart index e6c92ed..a9b4b8e 100644 --- a/lib/component/widget/quiz_item_wa_component.dart +++ b/lib/component/widget/quiz_item_wa_component.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -54,7 +55,7 @@ class QuizItemWAComponent extends StatelessWidget { const SizedBox(height: 16), if (isOptionType && options != null) _buildOptions(), const SizedBox(height: 12), - _buildAnswerIndicator(), + _buildAnswerIndicator(context), const SizedBox(height: 16), const Divider(height: 24, color: AppColors.shadowPrimary), _buildMetadata(), @@ -109,7 +110,7 @@ class QuizItemWAComponent extends StatelessWidget { ); } - Widget _buildAnswerIndicator() { + Widget _buildAnswerIndicator(BuildContext context) { final icon = isCorrect ? LucideIcons.checkCircle2 : LucideIcons.xCircle; final color = isCorrect ? AppColors.primaryBlue : Colors.red; @@ -124,7 +125,7 @@ class QuizItemWAComponent extends StatelessWidget { Icon(icon, color: color, size: 18), const SizedBox(width: 8), Text( - 'Jawabanmu: $userAnswerText', + context.tr('your_answer', namedArgs: {'answer': userAnswerText}), style: AppTextStyles.statValue, ), ], diff --git a/lib/component/widget/recomendation_component.dart b/lib/component/widget/recomendation_component.dart index 8d73c25..c250a6a 100644 --- a/lib/component/widget/recomendation_component.dart +++ b/lib/component/widget/recomendation_component.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:quiz_app/component/quiz_container_component.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; @@ -21,7 +22,7 @@ class RecomendationComponent extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildSectionTitle(title), + _buildSectionTitle(context, title), const SizedBox(height: 10), datas.isNotEmpty // ? Text("yeay ${datas.length}") @@ -53,7 +54,7 @@ class RecomendationComponent extends StatelessWidget { // ); // } - Widget _buildSectionTitle(String title) { + Widget _buildSectionTitle(BuildContext context, String title) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Row( @@ -66,7 +67,7 @@ class RecomendationComponent extends StatelessWidget { GestureDetector( onTap: allOnTap, child: Text( - "Lihat semua", + context.tr('see_all'), style: TextStyle(fontSize: 14, color: Colors.blue.shade700), ), ), diff --git a/lib/core/endpoint/api_endpoint.dart b/lib/core/endpoint/api_endpoint.dart index d518cfb..2188098 100644 --- a/lib/core/endpoint/api_endpoint.dart +++ b/lib/core/endpoint/api_endpoint.dart @@ -1,6 +1,6 @@ class APIEndpoint { - // static const String baseUrl = "http://192.168.1.9:5000"; - static const String baseUrl = "http://172.16.106.133:5000"; + static const String baseUrl = "http://192.168.1.9:5000"; + // static const String baseUrl = "http://172.16.106.133:5000"; static const String api = "$baseUrl/api"; static const String login = "/login"; diff --git a/lib/feature/detail_quiz/view/detail_quix_view.dart b/lib/feature/detail_quiz/view/detail_quix_view.dart index d2e3afd..5042b96 100644 --- a/lib/feature/detail_quiz/view/detail_quix_view.dart +++ b/lib/feature/detail_quiz/view/detail_quix_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -81,6 +82,7 @@ class DetailQuizView extends GetView { const SizedBox(height: 20), const Divider(thickness: 1.2, color: AppColors.borderLight), const SizedBox(height: 20), + // Soal Section ListView.builder( shrinkWrap: true, @@ -169,13 +171,13 @@ class DetailQuizView extends GetView { String _mapQuestionTypeToText(String? type) { switch (type) { case 'option': - return 'Pilihan Ganda'; + return tr('question_type_option'); case 'fill_the_blank': - return 'Isian Kosong'; + return tr('question_type_fill'); case 'true_false': - return 'Benar / Salah'; + return tr('question_type_true_false'); default: - return 'Tipe Tidak Diketahui'; + return tr('question_type_unknown'); } } } diff --git a/lib/feature/history/view/detail_history_view.dart b/lib/feature/history/view/detail_history_view.dart index 5b1b217..413ca42 100644 --- a/lib/feature/history/view/detail_history_view.dart +++ b/lib/feature/history/view/detail_history_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get_state_manager/get_state_manager.dart'; import 'package:lucide_icons/lucide_icons.dart'; @@ -18,7 +19,7 @@ class DetailHistoryView extends GetView { backgroundColor: AppColors.background, elevation: 0, title: Text( - 'Detail history', + context.tr('history_detail_title'), style: AppTextStyles.title.copyWith(fontSize: 24), ), centerTitle: true, @@ -31,7 +32,7 @@ class DetailHistoryView extends GetView { } return ListView( children: [ - quizMetaInfo(), + quizMetaInfo(context), ...quizListings(), ], ); @@ -55,7 +56,7 @@ class DetailHistoryView extends GetView { .toList(); } - Widget quizMetaInfo() { + Widget quizMetaInfo(BuildContext context) { final quiz = controller.quizAnswer; return Container( @@ -106,20 +107,20 @@ class DetailHistoryView extends GetView { children: [ _buildStatItem( icon: LucideIcons.checkCircle2, - label: 'Benar', + label: context.tr('correct_answer'), value: "${quiz.totalCorrect}/${quiz.questionListings.length}", color: Colors.green, ), _buildStatItem( icon: LucideIcons.award, - label: 'Skor', + label: context.tr('score'), value: quiz.totalScore.toString(), color: Colors.blueAccent, ), _buildStatItem( icon: LucideIcons.clock3, - label: 'Waktu', - value: '${quiz.totalSolveTime}s', + label: context.tr('time_taken'), + value: tr('duration_seconds', namedArgs: {"second": quiz.totalSolveTime.toString()}), color: Colors.orange, ), ], diff --git a/lib/feature/history/view/history_view.dart b/lib/feature/history/view/history_view.dart index 1582446..cd41db1 100644 --- a/lib/feature/history/view/history_view.dart +++ b/lib/feature/history/view/history_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -19,10 +20,10 @@ class HistoryView extends GetView { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Riwayat Kuis", style: AppTextStyles.title.copyWith(fontSize: 24)), + Text(context.tr("history_title"), style: AppTextStyles.title.copyWith(fontSize: 24)), const SizedBox(height: 8), Text( - "Lihat kembali hasil kuis yang telah kamu kerjakan", + context.tr("history_subtitle"), style: AppTextStyles.subtitle, ), const SizedBox(height: 20), @@ -38,7 +39,7 @@ class HistoryView extends GetView { if (historyList.isEmpty) { return Expanded( child: Center( - child: Text("You don't have any quiz history yet", style: AppTextStyles.body), + child: Text(context.tr("no_history"), style: AppTextStyles.body), ), ); } @@ -102,13 +103,13 @@ class HistoryView extends GetView { const Icon(Icons.check_circle, size: 14, color: Colors.green), const SizedBox(width: 4), Text( - "Skor: ${item.totalCorrect}/${item.totalQuestion}", + tr('score_label', namedArgs: {'correct': item.totalCorrect.toString(), 'total': item.totalQuestion.toString()}), style: AppTextStyles.caption, ), const SizedBox(width: 16), const Icon(Icons.timer, size: 14, color: Colors.grey), const SizedBox(width: 4), - Text("3 menit", style: AppTextStyles.caption), + Text(tr("duration_minutes", namedArgs: {"minute": "3"}), style: AppTextStyles.caption), ], ), ], diff --git a/lib/feature/home/view/component/button_option.dart b/lib/feature/home/view/component/button_option.dart index f42006c..acd579e 100644 --- a/lib/feature/home/view/component/button_option.dart +++ b/lib/feature/home/view/component/button_option.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; class ButtonOption extends StatelessWidget { @@ -19,22 +20,22 @@ class ButtonOption extends StatelessWidget { margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 20), child: Row( children: [ - Expanded(child: _buildCreateButton()), + Expanded(child: _buildCreateButton(context)), const SizedBox(width: 12), - Expanded(child: _buildRoomButtons()), + Expanded(child: _buildRoomButtons(context)), ], ), ); } - Widget _buildCreateButton() { + Widget _buildCreateButton(BuildContext context) { return InkWell( onTap: onCreate, borderRadius: BorderRadius.circular(16), child: SizedBox( height: double.infinity, child: _buildButtonContainer( - label: 'Buat Quiz', + label: context.tr("create_quiz"), gradientColors: [Color(0xFF0052CC), Color(0xFF0367D3)], icon: Icons.create, ), @@ -42,7 +43,7 @@ class ButtonOption extends StatelessWidget { ); } - Widget _buildRoomButtons() { + Widget _buildRoomButtons(BuildContext context) { return Column( children: [ Expanded( @@ -50,7 +51,7 @@ class ButtonOption extends StatelessWidget { onTap: onCreateRoom, borderRadius: BorderRadius.circular(16), child: _buildButtonContainer( - label: 'Buat Room', + label: context.tr("create_room"), gradientColors: [Color(0xFF36B37E), Color(0xFF22C39F)], icon: Icons.meeting_room, ), @@ -62,7 +63,7 @@ class ButtonOption extends StatelessWidget { onTap: onJoinRoom, borderRadius: BorderRadius.circular(16), child: _buildButtonContainer( - label: 'Join Room', + label: context.tr("join_room"), gradientColors: [Color(0xFFFFAB00), Color(0xFFFFC107)], icon: Icons.group, ), diff --git a/lib/feature/home/view/component/search_component.dart b/lib/feature/home/view/component/search_component.dart index c02235e..653560d 100644 --- a/lib/feature/home/view/component/search_component.dart +++ b/lib/feature/home/view/component/search_component.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/data/models/subject/subject_model.dart'; @@ -27,22 +28,22 @@ class SearchComponent extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildTitleSection(), + _buildTitleSection(context), const SizedBox(height: 12), _buildCategoryRow(), const SizedBox(height: 12), - _buildSearchInput(), + _buildSearchInput(context), ], ), ); } - Widget _buildTitleSection() { + Widget _buildTitleSection(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ + children: [ Text( - "Ready for a new challenge?", + context.tr("ready_new_challenge"), style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -51,7 +52,7 @@ class SearchComponent extends StatelessWidget { ), SizedBox(height: 5), Text( - "Search or select by category", + context.tr("search_or_select_category"), style: TextStyle( fontSize: 14, color: Color(0xFF6B778C), // Soft gray text @@ -98,7 +99,7 @@ class SearchComponent extends StatelessWidget { ); } - Widget _buildSearchInput() { + Widget _buildSearchInput(BuildContext context) { return GestureDetector( onTap: () => onSearchTap(), child: Container( @@ -115,11 +116,11 @@ class SearchComponent extends StatelessWidget { ], ), child: Row( - children: const [ + children: [ Icon(Icons.search, color: Color(0xFF6B778C)), SizedBox(width: 8), Text( - "Search for quizzes...", + context.tr("search_for_quizzes"), style: TextStyle( color: Color(0xFF6B778C), fontSize: 16, diff --git a/lib/feature/home/view/component/user_gretings.dart b/lib/feature/home/view/component/user_gretings.dart index 8a3ee05..7b55af5 100644 --- a/lib/feature/home/view/component/user_gretings.dart +++ b/lib/feature/home/view/component/user_gretings.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; class UserGretingsComponent extends StatelessWidget { @@ -34,11 +35,11 @@ class UserGretingsComponent extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Selamat Siang", + context.tr("greeting_time"), style: TextStyle(fontWeight: FontWeight.bold), ), Text( - "Hello $userName", + context.tr("greeting_user", namedArgs: {"user": userName}), style: TextStyle(fontWeight: FontWeight.w500), ), ], diff --git a/lib/feature/home/view/home_page.dart b/lib/feature/home/view/home_page.dart index caab0ec..d815de6 100644 --- a/lib/feature/home/view/home_page.dart +++ b/lib/feature/home/view/home_page.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -50,7 +51,7 @@ class HomeView extends GetView { const SizedBox(height: 20), Obx( () => RecomendationComponent( - title: "Quiz Rekomendasi", + title: context.tr("quiz_recommendation"), datas: controller.data.toList(), itemOnTap: controller.onRecommendationTap, allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation), diff --git a/lib/feature/join_room/view/join_room_view.dart b/lib/feature/join_room/view/join_room_view.dart index 8e8c860..663529a 100644 --- a/lib/feature/join_room/view/join_room_view.dart +++ b/lib/feature/join_room/view/join_room_view.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/component/global_button.dart'; import 'package:quiz_app/component/global_text_field.dart'; @@ -41,9 +41,9 @@ class JoinRoomView extends GetView { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - "Masukkan Kode Room", - style: TextStyle( + Text( + context.tr("enter_room_code"), + style: const TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: Colors.black87, @@ -52,18 +52,12 @@ class JoinRoomView extends GetView { const SizedBox(height: 16), GlobalTextField( controller: controller.codeController, - hintText: "AB123C", + hintText: context.tr("room_code_hint"), textInputType: TextInputType.text, - // Uncomment if needed: - // maxLength: 6, - // inputFormatters: [ - // FilteringTextInputFormatter.allow(RegExp(r'[A-Z0-9]')), - // UpperCaseTextFormatter(), - // ], ), const SizedBox(height: 30), GlobalButton( - text: "Gabung Sekarang", + text: context.tr("join_now"), onPressed: controller.joinRoom, ), ], diff --git a/lib/feature/library/view/library_view.dart b/lib/feature/library/view/library_view.dart index 74dc05a..a583c9b 100644 --- a/lib/feature/library/view/library_view.dart +++ b/lib/feature/library/view/library_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:quiz_app/component/widget/loading_widget.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/feature/library/controller/library_controller.dart'; @@ -17,18 +18,18 @@ class LibraryView extends GetView { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Library Soal', - style: TextStyle( + Text( + context.tr('library_title'), + style: const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 24, ), ), const SizedBox(height: 8), - const Text( - "Kumpulan soal-soal kuis yang sudah dibuat untuk dipelajari.", - style: TextStyle( + Text( + context.tr('library_description'), + style: const TextStyle( color: Colors.grey, fontSize: 14, ), @@ -41,10 +42,10 @@ class LibraryView extends GetView { } if (controller.quizs.isEmpty) { - return const Center( + return Center( child: Text( - "Belum ada soal tersedia.", - style: TextStyle(color: Colors.grey, fontSize: 14), + context.tr('no_quiz_available'), + style: const TextStyle(color: Colors.grey, fontSize: 14), ), ); } @@ -53,7 +54,10 @@ class LibraryView extends GetView { itemCount: controller.quizs.length, itemBuilder: (context, index) { final quiz = controller.quizs[index]; - return InkWell(onTap: () => controller.goToDetail(index), child: _buildQuizCard(quiz)); + return InkWell( + onTap: () => controller.goToDetail(index), + child: _buildQuizCard(context, quiz), + ); }, ); }), @@ -65,7 +69,7 @@ class LibraryView extends GetView { ); } - Widget _buildQuizCard(QuizListingModel quiz) { + Widget _buildQuizCard(BuildContext context, QuizListingModel quiz) { return Container( margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.all(16), @@ -74,7 +78,7 @@ class LibraryView extends GetView { borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.05), + color: Colors.black.withOpacity(0.05), blurRadius: 6, offset: const Offset(0, 2), ), @@ -129,7 +133,7 @@ class LibraryView extends GetView { const Icon(Icons.list, size: 14, color: Colors.grey), const SizedBox(width: 4), Text( - '${quiz.totalQuiz} Quizzes', + context.tr('quiz_count_named', namedArgs: {'total': quiz.totalQuiz.toString()}), style: const TextStyle(fontSize: 12, color: Colors.grey), ), const SizedBox(width: 12), diff --git a/lib/feature/listing_quiz/view/listing_quiz_view.dart b/lib/feature/listing_quiz/view/listing_quiz_view.dart index 270e402..e4f0b8a 100644 --- a/lib/feature/listing_quiz/view/listing_quiz_view.dart +++ b/lib/feature/listing_quiz/view/listing_quiz_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -27,7 +28,12 @@ class ListingsQuizView extends GetView { } if (controller.quizzes.isEmpty) { - return const Center(child: Text('Tidak ada kuis tersedia.')); + return Center( + child: Text( + context.tr('no_quiz_available'), + textAlign: TextAlign.center, + ), + ); } return ListView.builder( diff --git a/lib/feature/login/view/login_page.dart b/lib/feature/login/view/login_page.dart index 85d7258..74f8f0f 100644 --- a/lib/feature/login/view/login_page.dart +++ b/lib/feature/login/view/login_page.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -24,26 +25,26 @@ class LoginView extends GetView { const SizedBox(height: 40), const AppName(), const SizedBox(height: 40), - const LabelTextField( - label: "Log In", + LabelTextField( + label: context.tr("log_in"), fontSize: 28, fontWeight: FontWeight.bold, color: Color(0xFF172B4D), ), const SizedBox(height: 24), - const LabelTextField( - label: "Email", + LabelTextField( + label: context.tr("email"), color: Color(0xFF6B778C), fontSize: 14, ), const SizedBox(height: 6), GlobalTextField( controller: controller.emailController, - hintText: "Masukkan email anda", + hintText: context.tr("enter_your_email"), ), const SizedBox(height: 20), - const LabelTextField( - label: "Password", + LabelTextField( + label: context.tr("password"), color: Color(0xFF6B778C), fontSize: 14, ), @@ -54,18 +55,18 @@ class LoginView extends GetView { isPassword: true, obscureText: controller.isPasswordHidden.value, onToggleVisibility: controller.togglePasswordVisibility, - hintText: "Masukkan password anda", + hintText: context.tr("enter_your_password"), ), ), const SizedBox(height: 32), Obx(() => GlobalButton( onPressed: controller.loginWithEmail, - text: "Masuk", + text: context.tr("sign_in"), type: controller.isButtonEnabled.value, )), const SizedBox(height: 24), - const LabelTextField( - label: "OR", + LabelTextField( + label: context.tr("or"), alignment: Alignment.center, color: Color(0xFF6B778C), ), diff --git a/lib/feature/navigation/views/navbar_view.dart b/lib/feature/navigation/views/navbar_view.dart index 2f52203..2f0f781 100644 --- a/lib/feature/navigation/views/navbar_view.dart +++ b/lib/feature/navigation/views/navbar_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/feature/history/view/history_view.dart'; @@ -31,29 +32,29 @@ class NavbarView extends GetView { }), bottomNavigationBar: Obx( () => BottomNavigationBar( - type: BottomNavigationBarType.fixed, // <=== ini tambahan penting! + type: BottomNavigationBarType.fixed, currentIndex: controller.selectedIndex.value, onTap: controller.changePage, - items: const [ + items: [ BottomNavigationBarItem( - icon: Icon(Icons.home), - label: 'Home', + icon: const Icon(Icons.home), + label: context.tr('nav_home'), ), BottomNavigationBarItem( - icon: Icon(Icons.search), - label: 'Search', + icon: const Icon(Icons.search), + label: context.tr('nav_search'), ), BottomNavigationBarItem( - icon: Icon(Icons.menu_book), - label: 'Library', + icon: const Icon(Icons.menu_book), + label: context.tr('nav_library'), ), BottomNavigationBarItem( - icon: Icon(Icons.history), - label: 'History', + icon: const Icon(Icons.history), + label: context.tr('nav_history'), ), BottomNavigationBarItem( - icon: Icon(Icons.person), - label: 'Profile', + icon: const Icon(Icons.person), + label: context.tr('nav_profile'), ), ], ), diff --git a/lib/feature/play_quiz_multiplayer/view/play_quiz_multiplayer.dart b/lib/feature/play_quiz_multiplayer/view/play_quiz_multiplayer.dart index 9055503..c4761fd 100644 --- a/lib/feature/play_quiz_multiplayer/view/play_quiz_multiplayer.dart +++ b/lib/feature/play_quiz_multiplayer/view/play_quiz_multiplayer.dart @@ -54,7 +54,6 @@ class PlayQuizMultiplayerView extends GetView { if (question.type == 'option') _buildOptionQuestion(), if (question.type == 'fill_in_the_blank') _buildFillInBlankQuestion(), if (question.type == 'true_false') _buildTrueFalseQuestion(), - const Spacer(), SizedBox( width: double.infinity, diff --git a/lib/feature/profile/view/profile_view.dart b/lib/feature/profile/view/profile_view.dart index da67a4d..748bc70 100644 --- a/lib/feature/profile/view/profile_view.dart +++ b/lib/feature/profile/view/profile_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/feature/profile/controller/profile_controller.dart'; @@ -28,11 +29,20 @@ class ProfileView extends GetView { style: const TextStyle(fontSize: 14, color: Colors.grey), ), const SizedBox(height: 24), - _buildStats(), + _buildStats(context), const SizedBox(height: 32), - _buildActionButton("Edit Profil", Icons.edit, controller.editProfile), + _buildActionButton( + context.tr("edit_profile"), + Icons.edit, + controller.editProfile, + ), const SizedBox(height: 12), - _buildActionButton("Logout", Icons.logout, controller.logout, isDestructive: true), + _buildActionButton( + context.tr("logout"), + Icons.logout, + controller.logout, + isDestructive: true, + ), ], ); }), @@ -57,7 +67,7 @@ class ProfileView extends GetView { } } - Widget _buildStats() { + Widget _buildStats(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), decoration: BoxDecoration( @@ -74,9 +84,15 @@ class ProfileView extends GetView { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildStatItem("Total Quiz", controller.totalQuizzes.value.toString()), + _buildStatItem( + context.tr("total_quiz"), + controller.totalQuizzes.value.toString(), + ), const SizedBox(width: 16), - _buildStatItem("Skor Rata-rata", "${controller.avgScore.value}%"), + _buildStatItem( + context.tr("avg_score"), + "${controller.avgScore.value}%", + ), ], ), ); diff --git a/lib/feature/quiz_creation/view/quiz_creation_view.dart b/lib/feature/quiz_creation/view/quiz_creation_view.dart index f99f694..53dcefd 100644 --- a/lib/feature/quiz_creation/view/quiz_creation_view.dart +++ b/lib/feature/quiz_creation/view/quiz_creation_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/component/global_button.dart'; import 'package:quiz_app/feature/quiz_creation/controller/quiz_creation_controller.dart'; @@ -16,9 +17,9 @@ class QuizCreationView extends GetView { appBar: AppBar( backgroundColor: AppColors.background, elevation: 0, - title: const Text( - 'Create Quiz', - style: TextStyle( + title: Text( + context.tr('create_quiz_title'), + style: const TextStyle( fontWeight: FontWeight.bold, color: AppColors.darkText, ), @@ -36,13 +37,14 @@ class QuizCreationView extends GetView { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildModeSelector(), + _buildModeSelector(context), const SizedBox(height: 20), - Obx( - () => controller.isGenerate.value ? GenerateComponent() : CustomQuestionComponent(), - ), + Obx(() => controller.isGenerate.value ? const GenerateComponent() : const CustomQuestionComponent()), const SizedBox(height: 30), - GlobalButton(text: "simpan semua", onPressed: controller.onDone) + GlobalButton( + text: context.tr('save_all'), + onPressed: controller.onDone, + ) ], ), ), @@ -51,7 +53,7 @@ class QuizCreationView extends GetView { ); } - Widget _buildModeSelector() { + Widget _buildModeSelector(BuildContext context) { return Container( decoration: BoxDecoration( color: AppColors.background, @@ -60,8 +62,8 @@ class QuizCreationView extends GetView { ), child: Row( children: [ - _buildModeButton('Generate', controller.isGenerate, true), - _buildModeButton('Manual', controller.isGenerate, false), + _buildModeButton(context.tr('mode_generate'), controller.isGenerate, true), + _buildModeButton(context.tr('mode_manual'), controller.isGenerate, false), ], ), ); @@ -71,17 +73,18 @@ class QuizCreationView extends GetView { return Expanded( child: InkWell( onTap: () => controller.onCreationTypeChange(base), - child: Obx( - () => Container( + child: Obx(() { + final selected = isSelected.value == base; + return Container( padding: const EdgeInsets.symmetric(vertical: 14), decoration: BoxDecoration( - color: isSelected.value == base ? AppColors.primaryBlue : Colors.transparent, + color: selected ? AppColors.primaryBlue : Colors.transparent, borderRadius: base - ? BorderRadius.only( + ? const BorderRadius.only( topLeft: Radius.circular(10), bottomLeft: Radius.circular(10), ) - : BorderRadius.only( + : const BorderRadius.only( topRight: Radius.circular(10), bottomRight: Radius.circular(10), ), @@ -90,12 +93,12 @@ class QuizCreationView extends GetView { child: Text( label, style: TextStyle( - color: isSelected.value == base ? Colors.white : AppColors.softGrayText, + color: selected ? Colors.white : AppColors.softGrayText, fontWeight: FontWeight.w600, ), ), - ), - ), + ); + }), ), ); } diff --git a/lib/feature/quiz_play/view/quiz_play_view.dart b/lib/feature/quiz_play/view/quiz_play_view.dart index 5fc9989..929f277 100644 --- a/lib/feature/quiz_play/view/quiz_play_view.dart +++ b/lib/feature/quiz_play/view/quiz_play_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; @@ -18,27 +19,26 @@ class QuizPlayView extends GetView { padding: const EdgeInsets.all(16), child: Obx(() { if (!controller.isStarting.value) { - return Center( - child: Text( - "Ready in ${controller.prepareDuration}", - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - )); + return Text( + context.tr('ready_in', namedArgs: {'second': controller.prepareDuration.toString()}), + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildCustomAppBar(), + _buildCustomAppBar(context), const SizedBox(height: 20), _buildProgressBar(), const SizedBox(height: 20), - _buildQuestionIndicator(), + _buildQuestionIndicator(context), const SizedBox(height: 12), _buildQuestionText(), const SizedBox(height: 30), - _buildAnswerSection(), + _buildAnswerSection(context), const Spacer(), - _buildNextButton(), + _buildNextButton(context), ], ); }), @@ -47,7 +47,7 @@ class QuizPlayView extends GetView { ); } - Widget _buildCustomAppBar() { + Widget _buildCustomAppBar(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: const BoxDecoration( @@ -55,9 +55,9 @@ class QuizPlayView extends GetView { ), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: const [ + children: [ Text( - 'Kerjakan Soal', + context.tr('quiz_play_title'), style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, @@ -79,9 +79,15 @@ class QuizPlayView extends GetView { ); } - Widget _buildQuestionIndicator() { + Widget _buildQuestionIndicator(BuildContext context) { return Text( - 'Soal ${controller.currentIndex.value + 1} dari ${controller.quizData.questionListings.length}', + context.tr( + 'question_indicator', + namedArgs: { + 'current': (controller.currentIndex.value + 1).toString(), + 'total': controller.quizData.questionListings.length.toString(), + }, + ), style: const TextStyle( fontSize: 16, color: Colors.grey, @@ -101,7 +107,7 @@ class QuizPlayView extends GetView { ); } - Widget _buildAnswerSection() { + Widget _buildAnswerSection(BuildContext context) { final question = controller.currentQuestion; if (question is OptionQuestion) { @@ -131,8 +137,8 @@ class QuizPlayView extends GetView { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - _buildTrueFalseButton('Ya', true, controller.choosenAnswerTOF), - _buildTrueFalseButton('Tidak', false, controller.choosenAnswerTOF), + _buildTrueFalseButton(context.tr('yes'), true, controller.choosenAnswerTOF), + _buildTrueFalseButton(context.tr('no'), false, controller.choosenAnswerTOF), ], ); } else { @@ -159,7 +165,7 @@ class QuizPlayView extends GetView { }); } - Widget _buildNextButton() { + Widget _buildNextButton(BuildContext context) { return Obx(() { final isEnabled = controller.isAnswerSelected.value; @@ -171,8 +177,8 @@ class QuizPlayView extends GetView { minimumSize: const Size(double.infinity, 50), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), - child: const Text( - 'Next', + child: Text( + context.tr('next'), style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ); diff --git a/lib/feature/quiz_preview/view/quiz_preview.dart b/lib/feature/quiz_preview/view/quiz_preview.dart index 8b5209c..668263c 100644 --- a/lib/feature/quiz_preview/view/quiz_preview.dart +++ b/lib/feature/quiz_preview/view/quiz_preview.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/component/global_button.dart'; import 'package:quiz_app/component/global_text_field.dart'; @@ -15,25 +16,25 @@ class QuizPreviewPage extends GetView { Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, - appBar: _buildAppBar(), + appBar: _buildAppBar(context), body: SafeArea( child: Padding( padding: const EdgeInsets.all(20.0), - child: _buildContent(), + child: _buildContent(context), ), ), ); } - PreferredSizeWidget _buildAppBar() { + PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( backgroundColor: AppColors.background, elevation: 0, centerTitle: true, iconTheme: const IconThemeData(color: AppColors.darkText), - title: const Text( - 'Preview Quiz', - style: TextStyle( + title: Text( + context.tr('quiz_preview_title'), + style: const TextStyle( color: AppColors.darkText, fontWeight: FontWeight.bold, ), @@ -41,25 +42,25 @@ class QuizPreviewPage extends GetView { ); } - Widget _buildContent() { + Widget _buildContent(BuildContext context) { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const LabelTextField(label: "Judul"), + LabelTextField(label: context.tr("quiz_title_label")), GlobalTextField(controller: controller.titleController), const SizedBox(height: 20), - const LabelTextField(label: "Deskripsi Singkat"), + LabelTextField(label: context.tr("quiz_description_label")), GlobalTextField(controller: controller.descriptionController), const SizedBox(height: 20), - const LabelTextField(label: "Mata Pelajaran"), + LabelTextField(label: context.tr("quiz_subject_label")), Obx(() => SubjectDropdownComponent( data: controller.subjects.toList(), onItemTap: controller.onSubjectTap, selectedIndex: controller.subjectIndex.value, )), const SizedBox(height: 20), - _buildPublicCheckbox(), + _buildPublicCheckbox(context), const SizedBox(height: 30), const Divider(thickness: 1.2, color: AppColors.borderLight), const SizedBox(height: 20), @@ -67,7 +68,7 @@ class QuizPreviewPage extends GetView { const SizedBox(height: 30), GlobalButton( onPressed: controller.onSaveQuiz, - text: "Simpan Kuis", + text: context.tr("save_quiz"), ), ], ), @@ -83,7 +84,7 @@ class QuizPreviewPage extends GetView { ); } - Widget _buildPublicCheckbox() { + Widget _buildPublicCheckbox(BuildContext context) { return Obx(() => GestureDetector( onTap: controller.isPublic.toggle, child: Row( @@ -96,9 +97,9 @@ class QuizPreviewPage extends GetView { onChanged: (val) => controller.isPublic.value = val ?? false, ), const SizedBox(width: 8), - const Text( - "Buat Kuis Public", - style: TextStyle( + Text( + context.tr("make_quiz_public"), + style: const TextStyle( fontSize: 16, color: AppColors.darkText, fontWeight: FontWeight.w500, diff --git a/lib/feature/register/view/register_page.dart b/lib/feature/register/view/register_page.dart index e36cbdd..6a72f87 100644 --- a/lib/feature/register/view/register_page.dart +++ b/lib/feature/register/view/register_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/component/app_name.dart'; import 'package:quiz_app/component/global_button.dart'; @@ -9,6 +10,7 @@ import 'package:quiz_app/feature/register/controller/register_controller.dart'; class RegisterView extends GetView { const RegisterView({super.key}); + @override Widget build(BuildContext context) { return Scaffold( @@ -22,47 +24,52 @@ class RegisterView extends GetView { padding: const EdgeInsets.symmetric(vertical: 40), child: AppName(), ), - LabelTextField(label: "Register", fontSize: 24), + LabelTextField( + label: context.tr('register_title'), + fontSize: 24, + ), const SizedBox(height: 10), - LabelTextField(label: "Full Name"), + LabelTextField(label: context.tr('full_name')), GlobalTextField(controller: controller.nameController), const SizedBox(height: 10), - LabelTextField(label: "Email"), + LabelTextField(label: context.tr('email')), GlobalTextField(controller: controller.emailController), const SizedBox(height: 10), - LabelTextField(label: "Birth Date"), + LabelTextField(label: context.tr('birth_date')), GlobalTextField( controller: controller.bDateController, hintText: "12-08-2001", ), - LabelTextField(label: "Nomer Telepon (Opsional)"), + LabelTextField(label: context.tr('phone_optional')), GlobalTextField( controller: controller.phoneController, hintText: "085708570857", ), const SizedBox(height: 10), - LabelTextField(label: "Password"), + LabelTextField(label: context.tr('password')), Obx( () => GlobalTextField( - controller: controller.passwordController, - isPassword: true, - obscureText: controller.isPasswordHidden.value, - onToggleVisibility: controller.togglePasswordVisibility), + controller: controller.passwordController, + isPassword: true, + obscureText: controller.isPasswordHidden.value, + onToggleVisibility: controller.togglePasswordVisibility, + ), ), const SizedBox(height: 10), - LabelTextField(label: "Verify Password"), + LabelTextField(label: context.tr('verify_password')), Obx( () => GlobalTextField( - controller: controller.confirmPasswordController, - isPassword: true, - obscureText: controller.isConfirmPasswordHidden.value, - onToggleVisibility: controller.toggleConfirmPasswordVisibility), + controller: controller.confirmPasswordController, + isPassword: true, + obscureText: controller.isConfirmPasswordHidden.value, + onToggleVisibility: controller.toggleConfirmPasswordVisibility, + ), ), const SizedBox(height: 40), GlobalButton( onPressed: controller.onRegister, - text: "Register", - ) + text: context.tr('register_button'), + ), ], ), ), diff --git a/lib/feature/search/view/search_view.dart b/lib/feature/search/view/search_view.dart index 43a9521..ba4856d 100644 --- a/lib/feature/search/view/search_view.dart +++ b/lib/feature/search/view/search_view.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/const/enums/listing_type.dart'; @@ -31,7 +32,7 @@ class SearchView extends GetView { ] else ...[ Obx( () => RecomendationComponent( - title: "Quiz Rekomendasi", + title: context.tr('quiz_recommendation'), datas: controller.recommendationQData.toList(), itemOnTap: controller.goToDetailPage, allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation), @@ -40,7 +41,7 @@ class SearchView extends GetView { const SizedBox(height: 30), Obx( () => RecomendationComponent( - title: "Quiz Populer", + title: context.tr('quiz_popular'), datas: controller.recommendationQData.toList(), itemOnTap: controller.goToDetailPage, allOnTap: () => controller.goToListingsQuizPage(ListingType.populer), diff --git a/lib/main.dart b/lib/main.dart index c62a826..1ecc580 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:quiz_app/app/app.dart'; @@ -14,7 +15,21 @@ void main() { DeviceOrientation.portraitDown, ]); - runApp(MyApp()); + WidgetsFlutterBinding.ensureInitialized(); + + await EasyLocalization.ensureInitialized(); + + runApp( + EasyLocalization( + supportedLocales: [ + Locale('en', 'US'), + Locale('id', 'ID'), + ], + path: 'assets/translations', + fallbackLocale: Locale('en', 'US'), + child: MyApp(), + ), + ); }, (e, stackTrace) { logC.e("issue message $e || $stackTrace"); }); diff --git a/pubspec.lock b/pubspec.lock index f41ffd9..2e87f83 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -73,6 +81,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + sha256: "0f5239c7b8ab06c66440cfb0e9aa4b4640429c6668d5a42fe389c5de42220b12" + url: "https://pub.dev" + source: hosted + version: "3.0.7+1" + easy_logger: + dependency: transitive + description: + name: easy_logger + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.dev" + source: hosted + version: "0.0.2" fake_async: dependency: transitive description: @@ -118,6 +142,11 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -208,6 +237,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + intl: + dependency: transitive + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" leak_tracker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f304327..823951f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: lucide_icons: ^0.257.0 google_fonts: ^6.1.0 socket_io_client: ^3.1.2 + easy_localization: ^3.0.7+1 dev_dependencies: flutter_test: @@ -69,6 +70,7 @@ flutter: assets: - assets/ - assets/logo/ + - assets/translations/ # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg