Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/translations #4

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions lib/common/app-translation/app-translation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:get/get.dart';

import '../../screens/home/title/title.translation.dart' as home_title;
import '../../screens/home/todo/add-task/add-task.translation.dart'
as home_add_task;
import '../../screens/home/todo/filter-panel/filter-panel.translation.dart'
as home_filter_panel;
import '../items-left/items-left.translation.dart' as items_left;

class AppTranslation extends GetxService {
AppTranslation();

Map<String, Map<String, String>> translationsKeys =
<String, Map<String, String>>{
'en': en,
'uk': uk,
'ru': ru,
};

void _combineTranslations(Map<String, String> enTranslations,
Map<String, String> ukTranslations, Map<String, String> ruTranslations) {
translationsKeys['en']!.addAll(enTranslations);
translationsKeys['uk']!.addAll(ukTranslations);
translationsKeys['ru']!.addAll(ruTranslations);
}

Map<String, Map<String, String>> combineAllTranslations() {
_combineTranslations(
home_title.en,
home_title.uk,
home_title.ru,
);

_combineTranslations(
home_add_task.en,
home_add_task.uk,
home_add_task.ru,
);

_combineTranslations(
home_filter_panel.en,
home_filter_panel.uk,
home_filter_panel.ru,
);

_combineTranslations(
home_filter_panel.en,
home_filter_panel.uk,
home_filter_panel.ru,
);

_combineTranslations(
items_left.en,
items_left.uk,
items_left.ru,
);

return translationsKeys;
}
}

final Map<String, String> en = <String, String>{};
final Map<String, String> uk = <String, String>{};
final Map<String, String> ru = <String, String>{};
14 changes: 14 additions & 0 deletions lib/common/app-translation/translation.extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:get/get.dart';

abstract class Translation {}

extension Translations on Translation {
String get st => '$this';
String get tr => st.tr;
}

extension MapEnumExtensions on Map<Translation, String> {
Map<String, String> get st => Map<String, String>.fromEntries(entries.map(
(MapEntry<Translation, String> entry) =>
MapEntry<String, String>(entry.key.st, entry.value)));
}
30 changes: 30 additions & 0 deletions lib/common/items-left/items-left.extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:get/get.dart';
import 'package:intl/intl.dart';

import '../app-translation/translation.extensions.dart';
import 'items-left.translation.dart';

extension ItemsLeftFormatting on int {
String get itemsLeft => _getPlural(
this,
one: ItemsLeftTranslationNames.oneItem.tr,
few: ItemsLeftTranslationNames.fewItems.tr,
many: ItemsLeftTranslationNames.manyItems.tr,
).trim();

String _getPlural(int value,
{required String one, required String few, required String many}) {
if (value > 0) {
return Intl.plural(
value,
zero: '',
one: '$value $one',
few: '$value $few',
many: '$value $many',
other: '$value $many',
locale: Get.locale?.languageCode,
);
}
return '';
}
}
25 changes: 25 additions & 0 deletions lib/common/items-left/items-left.translation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import '../../../../common/app-translation/translation.extensions.dart';

enum ItemsLeftTranslationNames implements Translation {
oneItem,
fewItems,
manyItems,
}

final Map<String, String> en = <ItemsLeftTranslationNames, String>{
ItemsLeftTranslationNames.oneItem: 'item left',
ItemsLeftTranslationNames.fewItems: 'items left',
ItemsLeftTranslationNames.manyItems: 'items left',
}.st;

final Map<String, String> ru = <ItemsLeftTranslationNames, String>{
ItemsLeftTranslationNames.oneItem: 'элемент остался',
ItemsLeftTranslationNames.fewItems: 'элемента осталось',
ItemsLeftTranslationNames.manyItems: 'элементов осталось',
}.st;

final Map<String, String> uk = <ItemsLeftTranslationNames, String>{
ItemsLeftTranslationNames.oneItem: 'елемент залишився',
ItemsLeftTranslationNames.fewItems: 'елементи залишились',
ItemsLeftTranslationNames.manyItems: 'елементів залишилось',
}.st;
8 changes: 7 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'common/app-translation/app-translation.dart';
import 'screens/home/home.binding.dart';
import 'screens/home/home.screen.dart';

void main() {
Future<void> main() async {
await HomeBinding().dependencies();

runApp(const MyApp());
}

@@ -14,5 +17,8 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) => GetMaterialApp(
initialBinding: HomeBinding(),
home: const HomeScreen(),
translationsKeys: Get.find<AppTranslation>().combineAllTranslations(),
locale: const Locale('en'),
fallbackLocale: const Locale('en'),
);
}
4 changes: 4 additions & 0 deletions lib/screens/home/home.binding.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:get/get.dart';
import '../../common/app-translation/app-translation.dart';
import 'language-switcher/language-switcher.controller.dart';
import 'todo/add-task/add-task.controller.dart';
import 'todo/filter-panel/filter-panel.controller.dart';
import 'todo/task-list-item/task-list-item.controller.dart';
@@ -8,7 +10,9 @@ class HomeBinding extends Bindings {
@override
Future<void> dependencies() async {
Get
..lazyPut(AppTranslation.new)
..lazyPut(TodoService.new)
..lazyPut(LanguageSwitcherController.new)
..lazyPut(() => AddTaskController(Get.find()))
..lazyPut(() => FilterPanelController(Get.find()))
..lazyPut(() => TaskListItemController(Get.find(), Get.find()));
7 changes: 7 additions & 0 deletions lib/screens/home/home.screen.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'language-switcher/language-switcher.dart';
import 'todo/todo.widget.dart';
import '../home/title/title.widget.dart';

@@ -10,6 +11,12 @@ class HomeScreen extends StatelessWidget {
body: ListView(
children: const <Widget>[
Center(child: TitleWidget()),
Padding(
padding: EdgeInsets.only(bottom: 20),
child: Center(
child: LanguageSwitcher(),
),
),
TodoWidget(),
],
),
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:get/get.dart';
import 'language-switcher.enum.dart';

class LanguageSwitcherController extends GetxController {
final Rx<Language> currentLanguage = Language.en.obs;

List<Language> get languages => Language.values;

void updateLocale(Language language) {
currentLanguage.value = language;
Get.updateLocale(language.locale);
}
}
25 changes: 25 additions & 0 deletions lib/screens/home/language-switcher/language-switcher.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'language-switcher.controller.dart';
import 'language-switcher.enum.dart';

class LanguageSwitcher extends GetView<LanguageSwitcherController> {
const LanguageSwitcher({super.key});

@override
Widget build(BuildContext context) => Obx(() => DropdownButton<Language>(
value: controller.currentLanguage.value,
onChanged: (Language? language) {
if (language != null) {
controller.updateLocale(language);
}
},
items: controller.languages
.map<DropdownMenuItem<Language>>(
(Language language) => DropdownMenuItem<Language>(
value: language,
child: Text(language.title),
))
.toList(),
));
}
12 changes: 12 additions & 0 deletions lib/screens/home/language-switcher/language-switcher.enum.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'dart:ui';

enum Language {
en(Locale('en'), 'English'),
ru(Locale('ru'), 'Русский'),
uk(Locale('uk'), 'Українська');

const Language(this.locale, this.title);

final Locale locale;
final String title;
}
17 changes: 17 additions & 0 deletions lib/screens/home/title/title.translation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import '../../../common/app-translation/translation.extensions.dart';

enum TitleTranslationNames implements Translation {
title,
}

final Map<String, String> en = <TitleTranslationNames, String>{
TitleTranslationNames.title: 'todos',
}.st;

final Map<String, String> ru = <TitleTranslationNames, String>{
TitleTranslationNames.title: 'Список задач',
}.st;

final Map<String, String> uk = <TitleTranslationNames, String>{
TitleTranslationNames.title: 'Список завдань',
}.st;
12 changes: 8 additions & 4 deletions lib/screens/home/title/title.widget.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import 'package:flutter/material.dart';

import 'title.translation.dart';
import '../../../common/app-translation/translation.extensions.dart';

class TitleWidget extends StatelessWidget {
const TitleWidget({super.key});

@override
Widget build(BuildContext context) => const Padding(
padding: EdgeInsets.only(top: 50, bottom: 20),
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.only(top: 50, bottom: 20),
child: Text(
'todos',
style: TextStyle(
TitleTranslationNames.title.tr,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 80,
fontWeight: FontWeight.w300,
color: Colors.red,
17 changes: 17 additions & 0 deletions lib/screens/home/todo/add-task/add-task.translation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import '../../../../common/app-translation/translation.extensions.dart';

enum AddTaskTranslationNames implements Translation {
title,
}

final Map<String, String> en = <AddTaskTranslationNames, String>{
AddTaskTranslationNames.title: 'What needs to be done?',
}.st;

final Map<String, String> ru = <AddTaskTranslationNames, String>{
AddTaskTranslationNames.title: 'Что нужно сделать?',
}.st;

final Map<String, String> uk = <AddTaskTranslationNames, String>{
AddTaskTranslationNames.title: 'Що потрібно зробити?',
}.st;
4 changes: 3 additions & 1 deletion lib/screens/home/todo/add-task/add-task.widget.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../../common/app-translation/translation.extensions.dart';
import '../../../../common/input/input.widget.dart';
import 'add-task.controller.dart';
import 'add-task.translation.dart';

class AddTaskWidget extends GetView<AddTaskController> {
const AddTaskWidget({super.key});
@@ -16,7 +18,7 @@ class AddTaskWidget extends GetView<AddTaskController> {
child: InputWidget(
textController: controller.textController,
focusNode: controller.focusNode,
hintText: 'What needs to be done?',
hintText: AddTaskTranslationNames.title.tr,
onSubmitted: controller.addTask,
),
);
15 changes: 10 additions & 5 deletions lib/screens/home/todo/filter-panel/filter-panel.enum.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import '../../../../common/app-translation/translation.extensions.dart';
import 'filter-panel.translation.dart';

enum FilterType {
all('All'),
active('Active'),
completed('Completed');
all(FilterPanelTranslationNames.all),
active(FilterPanelTranslationNames.active),
completed(FilterPanelTranslationNames.completed);

const FilterType(this.translationKey);

const FilterType(this.type);
final FilterPanelTranslationNames translationKey;

final String type;
String get type => translationKey.tr;
}
29 changes: 29 additions & 0 deletions lib/screens/home/todo/filter-panel/filter-panel.translation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import '../../../../common/app-translation/translation.extensions.dart';

enum FilterPanelTranslationNames implements Translation {
clearItems,
all,
active,
completed
}

final Map<String, String> en = <FilterPanelTranslationNames, String>{
FilterPanelTranslationNames.clearItems: 'Clear completed',
FilterPanelTranslationNames.all: 'All',
FilterPanelTranslationNames.active: 'Active',
FilterPanelTranslationNames.completed: 'Completed',
}.st;

final Map<String, String> ru = <FilterPanelTranslationNames, String>{
FilterPanelTranslationNames.clearItems: 'Очистить выполненные',
FilterPanelTranslationNames.all: 'Все',
FilterPanelTranslationNames.active: 'Активные',
FilterPanelTranslationNames.completed: 'Выполненные',
}.st;

final Map<String, String> uk = <FilterPanelTranslationNames, String>{
FilterPanelTranslationNames.clearItems: 'Очистити виконані',
FilterPanelTranslationNames.all: 'Усі',
FilterPanelTranslationNames.active: 'Активні',
FilterPanelTranslationNames.completed: 'Виконані',
}.st;
23 changes: 11 additions & 12 deletions lib/screens/home/todo/filter-panel/filter-panel.widget.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../../common/app-translation/translation.extensions.dart';
import '../../../../common/button/button.widget.dart';
import '../../../../common/items-left/items-left.extensions.dart';
import 'filter-panel-buttons/filter-panel-buttons.dart';
import 'filter-panel.controller.dart';
import 'filter-panel.translation.dart';

class FilterPanelWidget extends GetView<FilterPanelController> {
const FilterPanelWidget({super.key});

@override
Widget build(BuildContext context) => Obx(() {
final bool isMobile = MediaQuery.of(context).size.width < 600;

if (controller.tasks.isEmpty) {
return const SizedBox();
}
@@ -23,23 +24,21 @@ class FilterPanelWidget extends GetView<FilterPanelController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'${controller.activeCount} items left',
controller.activeCount.itemsLeft,
style: const TextStyle(fontSize: 14, color: Colors.grey),
),
if (!isMobile) const FilterPanelButtons(),
ButtonWidget(
onTap: controller.clearCompleted,
title: 'Clear completed',
title: FilterPanelTranslationNames.clearItems.tr,
),
],
),
if (isMobile)
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FilterPanelButtons(),
],
),
const Row(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need Row here?

mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FilterPanelButtons(),
],
),
],
),
);
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
@@ -256,6 +256,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.2.0"
intl:
dependency: "direct main"
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
jsdaddy_custom_lints:
dependency: "direct dev"
description:
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ dependencies:
sdk: flutter
get: ^4.6.6
cupertino_icons: ^1.0.8
intl: ^0.19.0

dev_dependencies:
flutter_test: