inserted localized strings everywhere

This commit is contained in:
Kenton Hamaluik
2020-02-28 10:34:56 -07:00
parent 94f1a962b2
commit d1aa36d891
33 changed files with 609 additions and 217 deletions

View File

@ -37,7 +37,6 @@ android {
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "ca.hamaluik.timecop"
minSdkVersion 16
targetSdkVersion 28

View File

@ -7,7 +7,7 @@
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="timecop"
android:label="Time Cop"
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"

41
l10n/de.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "Über",
"appDescription": "Eine Zeiterfassungs-App, die Ihre Privatsphäre respektiert und die Arbeit erledigt, ohne zu ausgefallen zu werden.",
"appLegalese": "Copyright © Kenton Hamaluik, 2020",
"readme": "Liesmich",
"changeLog": "Änderungsprotokoll",
"sourceCode": "Quellcode",
"whatAreYouDoing": "Was tust du?",
"projects": "Projekte",
"export": "Export",
"noProject": "(kein Projekt)",
"confirmDelete": "Löschen bestätigen",
"deleteTimerConfirm": "Möchten Sie diesen Timer wirklich löschen?",
"cancel": "Stornieren",
"delete": "Löschen",
"runningTimers": "Timer ausführen",
"timeCopDatabase": "Time Cop-Datenbank ({Datum})",
"filter": "Filter",
"from": "Von",
"to": "Zu",
"includeProjects": "Projekte einschließen",
"project": "Projekt",
"description": "Beschreibung",
"timeH": "Zeit (Stunden)",
"timeCopEntries": "Time Cop-Einträge ({Datum})",
"createNewProject": "Neues Projekt erstellen",
"editProject": "Projekt bearbeiten",
"pleaseEnterAName": "Bitte geben Sie einen Namen ein",
"projectName": "Projektname",
"create": "Erstellen",
"save": "speichern",
"areYouSureYouWantToDeletePrefix": "Sind Sie sicher, dass Sie löschen möchten “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "Timer bearbeiten",
"whatWereYouDoing": "Was hast du gemacht?",
"startTime": "Startzeit",
"endTime": "Endzeit",
"duration": "Dauer",
"appName": "Time Cop",
"logoSemantics": "Time Cop Logo"
}

42
l10n/en.json Normal file
View File

@ -0,0 +1,42 @@
{
"about": "About",
"appDescription": "A time tracking app that respects your privacy and gets the job done without getting too fancy.",
"appLegalese": "Copyright © Kenton Hamaluik, 2020",
"readme": "Read Me",
"changeLog": "Change Log",
"sourceCode": "Source Code",
"whatAreYouDoing": "What are you doing?",
"projects": "Projects",
"export": "Export",
"noProject": "(no project)",
"noDescription": "(no description)",
"confirmDelete": "Confirm Delete",
"deleteTimerConfirm": "Are you sure you want to delete this timer?",
"cancel": "Cancel",
"delete": "Delete",
"runningTimers": "Running Timers",
"timeCopDatabase": "Time Cop Database ({date})",
"filter": "Filter",
"from": "From",
"to": "To",
"includeProjects": "Include Projects",
"project": "Project",
"description": "Description",
"timeH": "Time (hours)",
"timeCopEntries": "Time Cop Entries ({date})",
"createNewProject": "Create New Project",
"editProject": "Edit Project",
"pleaseEnterAName": "Please enter a name",
"projectName": "Project Name",
"create": "Create",
"save": "Save",
"areYouSureYouWantToDeletePrefix": "Are you sure you want to delete “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "Edit Timer",
"whatWereYouDoing": "What were you doing?",
"startTime": "Start Time",
"endTime": "End Time",
"duration": "Duration",
"appName": "Time Cop",
"logoSemantics": "Time Cop Logo"
}

41
l10n/es.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "Acerca de",
"appDescription": "Una aplicación de seguimiento del tiempo que respeta su privacidad y hace el trabajo sin ser demasiado elegante.",
"appLegalese": "Copyright © Kenton Hamaluik, 2020",
"readme": "Léame",
"changeLog": "Cambiar registro",
"sourceCode": "Código fuente",
"whatAreYouDoing": "¿Qué estás haciendo?",
"projects": "Proyectos",
"export": "Exportar",
"noProject": "(sin proyecto)",
"confirmDelete": "Confirmar eliminación",
"deleteTimerConfirm": "¿Seguro que quieres eliminar este temporizador?",
"cancel": "Cancelar",
"delete": "Eliminar",
"runningTimers": "Running Timers",
"timeCopDatabase": "Base de datos de Time Cop ({date})",
"filter": "Filtrar",
"from": "Desde",
"to": "A",
"includeProjects": "Incluir proyectos",
"project": "Proyecto",
"description": "Descripción",
"timeH": "Tiempo (horas)",
"timeCopEntries": "Entradas de Time Cop ({date})",
"createNewProject": "Crear nuevo proyecto",
"editProject": "Editar proyecto",
"pleaseEnterAName": "Por favor ingrese un nombre",
"projectName": "Nombre del proyecto",
"create": "Crear",
"save": "Salvar",
"areYouSureYouWantToDeletePrefix": "Estas seguro que quieres borrarlo “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "Editar temporizador",
"whatWereYouDoing": "¿Que estabas haciendo?",
"startTime": "Hora de inicio",
"endTime": "Hora de finalización",
"duration": "Duración",
"appName": "Time Cop",
"logoSemantics": "Logotipo de Time Cop"
}

41
l10n/fr.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "Sur",
"appDescription": "Une application de suivi du temps qui respecte votre vie privée et fait le travail sans trop de fantaisie.",
"appLegalese": "Copyright © Kenton Hamaluik, 2020",
"readme": "Lisezmoi",
"changeLog": "Journal des modifications",
"sourceCode": "Code source",
"whatAreYouDoing": "Que faites-vous?",
"projects": "Projets",
"export": "Exportation",
"noProject": "(pas de projet)",
"confirmDelete": "Confirmation de la suppression",
"deleteTimerConfirm": "Voulez-vous vraiment supprimer cette minuterie?",
"cancel": "Annuler",
"delete": "Supprimer",
"runningTimers": "Minuteurs de course",
"timeCopDatabase": "Base de données Time Cop ({date})",
"filter": "Filtre",
"from": "De",
"to": "À",
"includeProjects": "Inclure des projets",
"project": "Projet",
"description": "La description",
"timeH": "Temps (heures)",
"timeCopEntries": "Entrées Time Cop ({date})",
"createNewProject": "Créer un nouveau projet",
"editProject": "Modifier le projet",
"pleaseEnterAName": "Veuillez saisir un nom",
"projectName": "nom du projet",
"create": "Créer",
"save": "sauvegarder",
"areYouSureYouWantToDeletePrefix": "Etes-vous sûr que vous voulez supprimer “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "Modifier la minuterie",
"whatWereYouDoing": "Que faisiez-vous?",
"startTime": "Heure de début",
"endTime": "Heure de fin",
"duration": "Durée",
"appName": "Time Cop",
"logoSemantics": "Logo de Time Cop"
}

41
l10n/hi.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "के बारे में",
"appDescription": "एक समय ट्रैकिंग ऐप जो आपकी गोपनीयता का सम्मान करता है और बहुत अधिक फैंसी प्राप्त किए बिना काम करता है।",
"appLegalese": "कॉपीराइट © केंटन हामालिक, 2020",
"readme": "रीडमी",
"changeLog": "लॉग बदलें",
"sourceCode": "सोर्स कोड",
"whatAreYouDoing": "तुम क्या कर रहे हो?",
"projects": "परियोजनाओं",
"export": "निर्यात",
"noProject": "(कोई परियोजना नहीं)",
"confirmDelete": "हटाने की पुष्टि करें",
"deleteTimerConfirm": "क्या आप वाकई इस टाइमर को हटाना चाहते हैं?",
"cancel": "रद्द करना",
"delete": "हटाएं",
"runningTimers": "रनिंग टाइमर",
"timeCopDatabase": "समय कॉप डेटाबेस ({दिनांक})",
"filter": "फ़िल्टर",
"from": "से",
"to": "सेवा",
"includeProjects": "परियोजनाओं को शामिल करें",
"project": "परियोजना",
"description": "विवरण",
"timeH": "समय (घंटे)",
"timeCopEntries": "समय कॉप प्रविष्टियाँ ({दिनांक})",
"createNewProject": "नया प्रोजेक्ट बनाएं",
"editProject": "प्रोजेक्ट संपादित करें",
"pleaseEnterAName": "कृपया एक नाम दर्ज करें",
"projectName": "परियोजना का नाम",
"create": "सृजन करना",
"save": "सहेजें",
"areYouSureYouWantToDeletePrefix": "क्या आप पक्का इसे हटाना चाहते हैं “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "टाइमर संपादित करें",
"whatWereYouDoing": "तुम क्या कर रहे थे?",
"startTime": "समय शुरू",
"endTime": "अंतिम समय",
"duration": "अवधि",
"appName": "समय कॉप",
"logoSemantics": "टाइम कॉप लोगो"
}

41
l10n/id.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "Tentang",
"appDescription": "Aplikasi pelacakan waktu yang menghormati privasi Anda dan menyelesaikan pekerjaan tanpa menjadi terlalu mewah.",
"appLegalese": "Hak Cipta © Kenton Hamaluik, 2020",
"readme": "Baca aku",
"changeLog": "Ubah Log",
"sourceCode": "Kode sumber",
"whatAreYouDoing": "Apa yang sedang kamu lakukan?",
"projects": "Proyek",
"export": "Ekspor",
"noProject": "(tidak ada proyek)",
"confirmDelete": "Konfirmasikan Hapus",
"deleteTimerConfirm": "Anda yakin ingin menghapus penghitung waktu ini?",
"cancel": "Membatalkan",
"delete": "Menghapus",
"runningTimers": "Pengatur Waktu Berlari",
"timeCopDatabase": "Database Time Cop ({date})",
"filter": "Saring",
"from": "Dari",
"to": "Untuk",
"includeProjects": "Sertakan Proyek",
"project": "Proyek",
"description": "Deskripsi",
"timeH": "Waktu (jam)",
"timeCopEntries": "Entri Cop Waktu ({date})",
"createNewProject": "Buat Proyek Baru",
"editProject": "Edit Proyek",
"pleaseEnterAName": "Silakan masukkan nama",
"projectName": "Nama Proyek",
"create": "Membuat",
"save": "Menyimpan",
"areYouSureYouWantToDeletePrefix": "Anda yakin ingin menghapus “",
"areYouSureYouWantToDeletePostfix": "”",
"editTimer": "Edit Timer",
"whatWereYouDoing": "Apa yang kamu lakukan?",
"startTime": "Waktu mulai",
"endTime": "Akhir waktu",
"duration": "Durasi",
"appName": "Time Cop",
"logoSemantics": "Logo Time Cop"
}

41
l10n/ja.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "約",
"appDescription": "あなたのプライバシーを尊重し、凝りすぎずに仕事を終わらせる時間追跡アプリ。",
"appLegalese": "Copyright©Kenton Hamaluik、2020",
"readme": "Readme",
"changeLog": "変更ログ",
"sourceCode": "ソースコード",
"whatAreYouDoing": "何してるの?",
"projects": "プロジェクト",
"export": "書き出す",
"noProject": "(プロジェクトなし)",
"confirmDelete": "削除を確認",
"deleteTimerConfirm": "このタイマーを削除してもよろしいですか?",
"cancel": "キャンセル",
"delete": "削除",
"runningTimers": "タイマーの実行",
"timeCopDatabase": "タイムコップデータベース({日付}",
"filter": "フィルタ",
"from": "から",
"to": "に",
"includeProjects": "プロジェクトを含める",
"project": "事業",
"description": "説明",
"timeH": "時間(時間)",
"timeCopEntries": "タイムコップエントリ({日付}",
"createNewProject": "新しいプロジェクトを作成",
"editProject": "プロジェクトを編集",
"pleaseEnterAName": "名前を入力してください",
"projectName": "プロジェクト名",
"create": "作成する",
"save": "セーブ",
"areYouSureYouWantToDeletePrefix": "消去してもよろしいですか “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "タイマーを編集",
"whatWereYouDoing": "何してた?",
"startTime": "始まる時間",
"endTime": "終了時間",
"duration": "期間",
"appName": "タイムコップ",
"logoSemantics": "タイムコップのロゴ"
}

41
l10n/ko.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "約",
"appDescription": "あなたのプライバシーを尊重し、凝りすぎずに仕事を終わらせる時間追跡アプリ。",
"appLegalese": "Copyright©Kenton Hamaluik、2020",
"readme": "Readme",
"changeLog": "変更ログ",
"sourceCode": "ソースコード",
"whatAreYouDoing": "何してるの?",
"projects": "プロジェクト",
"export": "書き出す",
"noProject": "(プロジェクトなし)",
"confirmDelete": "削除を確認",
"deleteTimerConfirm": "このタイマーを削除してもよろしいですか?",
"cancel": "キャンセル",
"delete": "削除",
"runningTimers": "タイマーの実行",
"timeCopDatabase": "タイムコップデータベース({日付}",
"filter": "フィルタ",
"from": "から",
"to": "に",
"includeProjects": "プロジェクトを含める",
"project": "事業",
"description": "説明",
"timeH": "時間(時間)",
"timeCopEntries": "タイムコップエントリ({日付}",
"createNewProject": "新しいプロジェクトを作成",
"editProject": "プロジェクトを編集",
"pleaseEnterAName": "名前を入力してください",
"projectName": "プロジェクト名",
"create": "作成する",
"save": "セーブ",
"areYouSureYouWantToDeletePrefix": "消去してもよろしいですか “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "タイマーを編集",
"whatWereYouDoing": "何してた?",
"startTime": "始まる時間",
"endTime": "終了時間",
"duration": "期間",
"appName": "タイムコップ",
"logoSemantics": "タイムコップのロゴ"
}

41
l10n/pt.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "Sobre",
"appDescription": "Um aplicativo de rastreamento de tempo que respeita sua privacidade e faz o trabalho sem ser muito sofisticado.",
"appLegalese": "Direitos autorais © Kenton Hamaluik, 2020",
"readme": "Leia-me",
"changeLog": "Change Log",
"sourceCode": "Código fonte",
"whatAreYouDoing": "O que você está fazendo?",
"projects": "Projetos",
"export": "Exportação",
"noProject": "(nenhum projeto)",
"confirmDelete": "Confirmar exclusão",
"deleteTimerConfirm": "Tem certeza de que deseja excluir este temporizador?",
"cancel": "Cancelar",
"delete": "Excluir",
"runningTimers": "Temporizadores",
"timeCopDatabase": "Banco de dados Time Cop ({date})",
"filter": "Filtro",
"from": "A partir de",
"to": "Para",
"includeProjects": "Incluir Projetos",
"project": "Projeto",
"description": "Descrição",
"timeH": "Tempo (horas)",
"timeCopEntries": "Entradas de registro de horas ({date})",
"createNewProject": "Criar novo projeto",
"editProject": "Editar projeto",
"pleaseEnterAName": "Por favor insira um nome",
"projectName": "Nome do Projeto",
"create": "Crio",
"save": "Salve ",
"areYouSureYouWantToDeletePrefix": "Tem certeza de que deseja excluir “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "Editar temporizador",
"whatWereYouDoing": "O que você estava fazendo?",
"startTime": "Hora de início",
"endTime": "Fim do tempo",
"duration": "Duração",
"appName": "Time Cop",
"logoSemantics": "Time Cop Logo"
}

41
l10n/ru.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "Около",
"appDescription": "Приложение для отслеживания времени, которое уважает вашу конфиденциальность и выполняет свою работу, не становясь слишком модным.",
"appLegalese": "Copyright © Кентон Хамалук, 2020",
"readme": "Прочти меня",
"changeLog": "Журнал изменений",
"sourceCode": "Исходный код",
"whatAreYouDoing": "Что делаешь?",
"projects": "проектов",
"export": "экспорт",
"noProject": "(нет проекта)",
"confirmDelete": "Подтвердите удаление",
"deleteTimerConfirm": "Вы уверены, что хотите удалить этот таймер?",
"cancel": "Отмена",
"delete": "удалять",
"runningTimers": "Таймеры бега",
"timeCopDatabase": "База данных Time Cop ({дата})",
"filter": "Фильтр",
"from": "Из",
"to": "к",
"includeProjects": "Включить проекты",
"project": "проект",
"description": "Описание",
"timeH": "Время (часы)",
"timeCopEntries": "Записи временного полицейского ({дата})",
"createNewProject": "Создать новый проект",
"editProject": "Редактировать проект",
"pleaseEnterAName": "Пожалуйста, введите имя",
"projectName": "название проекта",
"create": "Создайте",
"save": "Сохранить",
"areYouSureYouWantToDeletePrefix": "Вы уверены, что хотите удалить “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "Редактировать таймер",
"whatWereYouDoing": "Что ты делал?",
"startTime": "Начальное время",
"endTime": "Время окончания",
"duration": "продолжительность",
"appName": "Time Cop",
"logoSemantics": "Time Cop Logo"
}

41
l10n/zh.json Normal file
View File

@ -0,0 +1,41 @@
{
"about": "关于",
"appDescription": "时间跟踪应用程序尊重您的隐私并在不花哨的情况下完成工作。",
"appLegalese": "版权所有©Kenton Hamaluik2020年",
"readme": "自述文件",
"changeLog": "变更记录",
"sourceCode": "源代码",
"whatAreYouDoing": "你在做什么?",
"projects": "专案",
"export": "出口",
"noProject": "(无项目)",
"confirmDelete": "确认删除",
"deleteTimerConfirm": "您确定要删除此计时器吗?",
"cancel": "取消",
"delete": "删除",
"runningTimers": "运行计时器",
"timeCopDatabase": "时间警察数据库({date}",
"filter": "过滤",
"from": "从",
"to": "至",
"includeProjects": "包括项目",
"project": "项目",
"description": "描述",
"timeH": "时间(小时)",
"timeCopEntries": "时间警察条目({date}",
"createNewProject": "建立新专案",
"editProject": "编辑专案",
"pleaseEnterAName": "请输入一个名字",
"projectName": "项目名",
"create": "创建",
"save": "保存",
"areYouSureYouWantToDeletePrefix": "你确定你要删除 “",
"areYouSureYouWantToDeletePostfix": "”?",
"editTimer": "编辑计时器",
"whatWereYouDoing": "你在做什么?",
"startTime": "开始时间",
"endTime": "时间结束",
"duration": "持续时间",
"appName": "时间警察",
"logoSemantics": "时间警察徽标"
}

View File

@ -26,17 +26,5 @@ class ProjectColour extends StatelessWidget {
return Icon(FontAwesomeIcons.circle, size: 24, color: Theme.of(context).disabledColor);
}
return Icon(FontAwesomeIcons.solidCircle, size: 24, color: project.colour);
//return Padding(
// padding: EdgeInsets.all(8.0),
// child: AspectRatio(
// aspectRatio: 1.0,
// child: Container(
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// color: project.colour,
// ),
// ),
// )
//);
}
}

View File

@ -0,0 +1,75 @@
// Copyright 2020 Kenton Hamaluik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:timecop/data_providers/l10n_provider.dart';
class JSONL10NProvider extends L10NProvider {
Map<String, String> _translations;
JSONL10NProvider._internal(this._translations)
: assert(_translations != null);
static Future<JSONL10NProvider> load(Locale locale) async {
String jsonString = await rootBundle.loadString("l10n/${locale.languageCode}.json");
dynamic rawJson = json.decode(jsonString);
Map<String, dynamic> jsonMap = rawJson as Map<String, dynamic>;
Map<String, String> strings = jsonMap.map((String key, dynamic value) => MapEntry(key, value.toString()));
return JSONL10NProvider._internal(strings);
}
@override String get about => _translations['about'] ?? "-about-";
@override String get appDescription => _translations['appDescription'] ?? "-appDescription-";
@override String get appLegalese => _translations['appLegalese'] ?? "-appLegalese-";
@override String get appName => _translations['appName'] ?? "-appName-";
@override String get areYouSureYouWantToDeletePostfix => _translations['areYouSureYouWantToDeletePostfix'] ?? "-areYouSureYouWantToDeletePostfix-";
@override String get areYouSureYouWantToDeletePrefix => _translations['areYouSureYouWantToDeletePrefix'] ?? "-areYouSureYouWantToDeletePrefix-";
@override String get cancel => _translations['cancel'] ?? "-cancel-";
@override String get changeLog => _translations['changeLog'] ?? "-changeLog-";
@override String get confirmDelete => _translations['confirmDelete'] ?? "-confirmDelete-";
@override String get create => _translations['create'] ?? "-create-";
@override String get createNewProject => _translations['createNewProject'] ?? "-createNewProject-";
@override String get delete => _translations['delete'] ?? "-delete-";
@override String get deleteTimerConfirm => _translations['deleteTimerConfirm'] ?? "-deleteTimerConfirm-";
@override String get description => _translations['description'] ?? "-description-";
@override String get duration => _translations['duration'] ?? "-duration-";
@override String get editProject => _translations['editProject'] ?? "-editProject-";
@override String get editTimer => _translations['editTimer'] ?? "-editTimer-";
@override String get endTime => _translations['endTime'] ?? "-endTime-";
@override String get export => _translations['export'] ?? "-export-";
@override String get filter => _translations['filter'] ?? "-filter-";
@override String get from => _translations['from'] ?? "-from-";
@override String get includeProjects => _translations['includeProjects'] ?? "-includeProjects-";
@override String get logoSemantics => _translations['logoSemantics'] ?? "-logoSemantics-";
@override String get noDescription => _translations['noDescription'] ?? "-noDescription-";
@override String get noProject => _translations['noProject'] ?? "-noProject-";
@override String get pleaseEnterAName => _translations['pleaseEnterAName'] ?? "-pleaseEnterAName-";
@override String get project => _translations['project'] ?? "-project-";
@override String get projectName => _translations['projectName'] ?? "-projectName-";
@override String get projects => _translations['projects'] ?? "-projects-";
@override String get readme => _translations['readme'] ?? "-readme-";
@override String get runningTimers => _translations['runningTimers'] ?? "-runningTimers-";
@override String get save => _translations['save'] ?? "-save-";
@override String get sourceCode => _translations['sourceCode'] ?? "-sourceCode-";
@override String get startTime => _translations['startTime'] ?? "-startTime-";
@override String get timeH => _translations['timeH'] ?? "-timeH-";
@override String get to => _translations['to'] ?? "-to-";
@override String get whatAreYouDoing => _translations['whatAreYouDoing'] ?? "-whatAreYouDoing-";
@override String get whatWereYouDoing => _translations['whatWereYouDoing'] ?? "-whatWereYouDoing-";
@override String timeCopDatabase(String date) => _translations['timeCopDatabase']?.replaceAll(r"{date}", date) ?? "-timeCopDatabase-";
@override String timeCopEntries(String date) => _translations['timeCopEntries']?.replaceAll(r"{date}", date) ?? "-timeCopEntries-";
}

View File

@ -1,55 +0,0 @@
// Copyright 2020 Kenton Hamaluik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:timecop/data_providers/l10n_provider.dart';
class L10nEn extends L10NProvider {
@override String get about => "About";
@override String get appDescription => "A time tracking app that respects your privacy and gets the job done without getting too fancy.";
@override String get appLegalese => "Copyright © Kenton Hamaluik, 2020";
@override String get appName => "Time Cop";
@override String get areYouSureYouWantToDeletePostfix => "”?";
@override String get areYouSureYouWantToDeletePrefix => "Are you sure you want to delete “";
@override String get cancel => "Cancel";
@override String get changeLog => "Change Log";
@override String get confirmDelete => "Confirm Delete";
@override String get create => "Create";
@override String get createNewProject => "Create New Project";
@override String get delete => "Delete";
@override String get deleteTimerConfirm => "Are you sure you want to delete this timer?";
@override String get description => "Description";
@override String get duration => "Duration";
@override String get editProject => "Edit Project";
@override String get editTimer => "Edit Timer";
@override String get endTime => "End Time";
@override String get export => "Export";
@override String get filter => "Filter";
@override String get from => "From";
@override String get includeProjects => "Include Projects";
@override String get logoSemantics => "Time Cop Logo";
@override String get noProject => "(no project)";
@override String get pleaseEnterAName => "Please enter a name";
@override String get project => "Project";
@override String get projectName => "Project Name";
@override String get projects => "Projects";
@override String get readme => "Readme";
@override String get runningTimers => "Running Timers";
@override String get save => "Save";
@override String get sourceCode => "Source Code";
@override String get startTime => "Start Time";
@override String get timeH => "Time (hours)";
@override String get to => "To";
@override String get whatAreYouDoing => "What are you doing?";
@override String get whatWereYouDoing => "What were you doing?";
}

View File

@ -1,55 +0,0 @@
// Copyright 2020 Kenton Hamaluik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'package:timecop/data_providers/l10n_provider.dart';
class L10nFr extends L10NProvider {
@override String get about => "Sur";
@override String get appDescription => "Une application de suivi du temps qui respecte votre vie privée et fait le travail sans trop de fantaisie.";
@override String get appLegalese => "Copyright © Kenton Hamaluik, 2020";
@override String get appName => "Time Cop";
@override String get areYouSureYouWantToDeletePostfix => "”?";
@override String get areYouSureYouWantToDeletePrefix => "Etes-vous sûr que vous voulez supprimer “";
@override String get cancel => "Annuler";
@override String get changeLog => "Journal des modifications";
@override String get confirmDelete => "Confirmation de la suppression";
@override String get create => "Créer";
@override String get createNewProject => "Créer un nouveau projet";
@override String get delete => "Supprimer";
@override String get deleteTimerConfirm => "Voulez-vous vraiment supprimer cette minuterie?";
@override String get description => "La description";
@override String get duration => "Durée";
@override String get editProject => "Modifier le projet";
@override String get editTimer => "Modifier la minuterie";
@override String get endTime => "Heure de fin";
@override String get export => "Exportation";
@override String get filter => "Filtre";
@override String get from => "De";
@override String get includeProjects => "Inclure des projets";
@override String get logoSemantics => "Logo de Time Cop";
@override String get noProject => "(pas de projet)";
@override String get pleaseEnterAName => "Veuillez saisir un nom";
@override String get project => "Projet";
@override String get projectName => "nom du projet";
@override String get projects => "Projets";
@override String get readme => "Lisezmoi";
@override String get runningTimers => "Minuteurs de course";
@override String get save => "sauvegarder";
@override String get sourceCode => "Code source";
@override String get startTime => "Heure de début";
@override String get timeH => "Temps (heures)";
@override String get to => "À";
@override String get whatAreYouDoing => "Que faites-vous?";
@override String get whatWereYouDoing => "Que faisiez-vous?";
}

View File

@ -50,4 +50,7 @@ abstract class L10NProvider {
String get to;
String get whatAreYouDoing;
String get whatWereYouDoing;
String get noDescription;
String timeCopDatabase(String date);
String timeCopEntries(String date);
}

View File

@ -14,8 +14,7 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:timecop/data_providers/l10n/l10n_en.dart';
import 'package:timecop/data_providers/l10n/l10n_fr.dart';
import 'package:timecop/data_providers/json_l10n_provider.dart';
import 'package:timecop/data_providers/l10n_provider.dart';
class L10N {
@ -27,12 +26,9 @@ class L10N {
assert(tr != null);
static Future<L10N> load(Locale locale) async {
print('loading locale ${locale.toString()}');
Intl.defaultLocale = locale.languageCode;
switch(locale.languageCode) {
case "fr": return L10N._internal(locale, L10nFr());
default: return L10N._internal(locale, L10nEn());
}
L10NProvider tr = await JSONL10NProvider.load(locale);
return L10N._internal(locale, tr);
}
static L10N of(BuildContext context) {

View File

@ -27,15 +27,14 @@ import 'package:timecop/data_providers/settings_provider.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/screens/dashboard/DashboardScreen.dart';
import 'blocs/theme/bloc.dart';
import 'package:intl/date_symbol_data_local.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final SettingsProvider settings = await SettingsProvider.load();
final DataProvider data = await DatabaseProvider.open();
// setup intl date formats
await initializeDateFormatting();
// setup intl date formats?
//await initializeDateFormatting();
assert(settings != null);
@ -110,7 +109,7 @@ class _TimeCopAppState extends State<_TimeCopApp> with WidgetsBindingObserver {
RepositoryProvider<SettingsProvider>.value(value: widget.settings),
],
child: MaterialApp(
title: 'TimeCop',
title: 'Time Cop',
theme: BlocProvider.of<ThemeBloc>(context).state.theme,
home: DashboardScreen(),
localizationsDelegates: [

View File

@ -42,17 +42,17 @@ class TimerEntry extends Equatable {
static String formatDuration(Duration d) {
if(d.inHours > 0) {
return
d.inHours.toString() + "h "
+ (d.inMinutes - (d.inHours * 60)).toString() + "m "
+ (d.inSeconds - (d.inMinutes * 60)).toString() + "s";
d.inHours.toString() + ":"
+ (d.inMinutes - (d.inHours * 60)).toString().padLeft(2, "0") + ":"
+ (d.inSeconds - (d.inMinutes * 60)).toString();
}
else if(d.inMinutes > 0) {
return
d.inMinutes.toString() + "m "
+ (d.inSeconds - (d.inMinutes * 60)).toString() + "s";
d.inMinutes.toString() + ":"
+ (d.inSeconds - (d.inMinutes * 60)).toString().padLeft(2, "0");
}
else {
return d.inSeconds.toString() + "s";
return d.inSeconds.toString();
}
}

View File

@ -17,6 +17,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timecop/l10n.dart';
import 'package:url_launcher/url_launcher.dart';
class AboutScreen extends StatelessWidget {
@ -25,32 +26,32 @@ class AboutScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AboutPage(
title: Text('About'),
title: Text(L10N.of(context).tr.about),
applicationVersion: 'v{{ version }}-{{ buildNumber }}',
applicationDescription: Text(
'A time tracking app that respects your privacy and gets the job done without getting too fancy.',
L10N.of(context).tr.appDescription,
textAlign: TextAlign.justify,
),
applicationIcon: SvgPicture.asset(
"icon.no-bg.pink.svg",
semanticsLabel: "Time Cop Logo",
semanticsLabel: L10N.of(context).tr.logoSemantics,
height: 100,
),
applicationLegalese: 'Copyright © Kenton Hamaluik, {{ year }}',
applicationLegalese: L10N.of(context).tr.appLegalese,
children: <Widget>[
MarkdownPageListTile(
filename: 'README.md',
title: Text('Readme'),
title: Text(L10N.of(context).tr.readme),
icon: Icon(FontAwesomeIcons.readme),
),
MarkdownPageListTile(
filename: 'CHANGELOG.md',
title: Text('Changelog'),
title: Text(L10N.of(context).tr.changeLog),
icon: Icon(FontAwesomeIcons.boxes),
),
ListTile(
leading: Icon(FontAwesomeIcons.code),
title: Text("Source Code"),
title: Text(L10N.of(context).tr.sourceCode),
onTap: () => launch("https://github.com/hamaluik/timecop"),
),
LicensesPageListTile(

View File

@ -15,6 +15,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/screens/dashboard/bloc/dashboard_bloc.dart';
import 'package:timecop/screens/dashboard/components/ProjectSelectField.dart';
import 'package:timecop/screens/dashboard/components/RunningTimers.dart';
@ -35,10 +36,10 @@ class DashboardScreen extends StatelessWidget {
padding: EdgeInsets.all(12.0),
child: SvgPicture.asset(
"icon.no-bg.svg",
semanticsLabel: "Time Cop Logo",
semanticsLabel: L10N.of(context).tr.logoSemantics,
)
),
title: Text("Time Cop"),
title: Text(L10N.of(context).tr.appName),
actions: <Widget>[
PopupMenu(),
],

View File

@ -14,6 +14,7 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/screens/about/AboutScreen.dart';
import 'package:timecop/screens/export/ExportScreen.dart';
import 'package:timecop/screens/projects/ProjectsScreen.dart';
@ -53,21 +54,21 @@ class PopupMenu extends StatelessWidget {
PopupMenuItem(
child: ListTile(
leading: Icon(FontAwesomeIcons.layerGroup),
title: Text("Projects"),
title: Text(L10N.of(context).tr.projects),
),
value: MenuItem.projects,
),
PopupMenuItem(
child: ListTile(
leading: Icon(FontAwesomeIcons.fileExport),
title: Text("Export"),
title: Text(L10N.of(context).tr.export),
),
value: MenuItem.export,
),
PopupMenuItem(
child: ListTile(
leading: Icon(FontAwesomeIcons.dna),
title: Text("About"),
title: Text(L10N.of(context).tr.about),
),
value: MenuItem.about,
),

View File

@ -19,6 +19,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timecop/blocs/projects/bloc.dart';
import 'package:timecop/blocs/timers/bloc.dart';
import 'package:timecop/components/ProjectColour.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/models/timer_entry.dart';
import 'package:timecop/screens/timer/TimerEditor.dart';
@ -30,9 +31,9 @@ class RunningTimerRow extends StatelessWidget {
assert(now != null),
super(key: key);
static String formatDescription(String description) {
static String formatDescription(BuildContext context, String description) {
if(description == null || description.trim().isEmpty) {
return "(no description)";
return L10N.of(context).tr.noDescription;
}
return description;
}
@ -51,7 +52,7 @@ class RunningTimerRow extends StatelessWidget {
actionExtentRatio: 0.15,
child: ListTile(
leading: ProjectColour(project: BlocProvider.of<ProjectsBloc>(context).getProjectByID(timer.projectID)),
title: Text(formatDescription(timer.description), style: styleDescription(context, timer.description)),
title: Text(formatDescription(context, timer.description), style: styleDescription(context, timer.description)),
trailing: Text(timer.formatTime(), style: TextStyle(fontFamily: "FiraMono")),
onTap: () => Navigator.of(context).push(MaterialPageRoute<TimerEditor>(
builder: (BuildContext context) => TimerEditor(timer: timer,),
@ -67,15 +68,15 @@ class RunningTimerRow extends StatelessWidget {
bool delete = await showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text("Confirm Delete"),
content: Text("Are you sure you want to delete this timer?"),
title: Text(L10N.of(context).tr.confirmDelete),
content: Text(L10N.of(context).tr.deleteTimerConfirm),
actions: <Widget>[
FlatButton(
child: const Text("Cancel"),
child: Text(L10N.of(context).tr.cancel),
onPressed: () => Navigator.of(context).pop(false),
),
FlatButton(
child: const Text("Delete"),
child: Text(L10N.of(context).tr.delete),
onPressed: () => Navigator.of(context).pop(true),
),
],

View File

@ -15,6 +15,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:timecop/blocs/timers/bloc.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/models/timer_entry.dart';
import 'RunningTimerRow.dart';
@ -45,7 +46,7 @@ class RunningTimers extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
"Running Timers",
L10N.of(context).tr.runningTimers,
style: TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.w800

View File

@ -19,6 +19,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timecop/blocs/projects/bloc.dart';
import 'package:timecop/blocs/timers/bloc.dart';
import 'package:timecop/components/ProjectColour.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/models/project.dart';
import 'package:timecop/models/timer_entry.dart';
import 'package:timecop/screens/timer/TimerEditor.dart';
@ -29,9 +30,9 @@ class StoppedTimerRow extends StatelessWidget {
: assert(timer != null),
super(key: key);
static String formatDescription(String description) {
static String formatDescription(BuildContext context, String description) {
if(description == null || description.trim().isEmpty) {
return "(no description)";
return L10N.of(context).tr.noDescription;
}
return description;
}
@ -53,7 +54,7 @@ class StoppedTimerRow extends StatelessWidget {
actionExtentRatio: 0.15,
child: ListTile(
leading: ProjectColour(project: BlocProvider.of<ProjectsBloc>(context).getProjectByID(timer.projectID)),
title: Text(formatDescription(timer.description), style: styleDescription(context, timer.description)),
title: Text(formatDescription(context, timer.description), style: styleDescription(context, timer.description)),
trailing: Text(timer.formatTime(), style: TextStyle(fontFamily: "FiraMono")),
onTap: () => Navigator.of(context).push(MaterialPageRoute<TimerEditor>(
builder: (BuildContext context) => TimerEditor(timer: timer,),
@ -69,29 +70,15 @@ class StoppedTimerRow extends StatelessWidget {
bool delete = await showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text("Confirm Delete"),
content: Text("Are you sure you want to delete this timer?"),
// TODO: this doesn't show up for some reason?
/*content: SingleChildScrollView(
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(text: "Are you "),
TextSpan(text: "sure", style: TextStyle(fontWeight: FontWeight.bold)),
TextSpan(text: " you want to delete this timer?\n"),
TextSpan(text: formatDescription(timer.description) + "\n", style: TextStyle(fontStyle: FontStyle.italic)),
TextSpan(text: formatDuration(timer.endTime.difference(timer.startTime)), style: TextStyle(fontFamily: "FiraMono")),
]
),
),
),*/
title: Text(L10N.of(context).tr.confirmDelete),
content: Text(L10N.of(context).tr.deleteTimerConfirm),
actions: <Widget>[
FlatButton(
child: const Text("Cancel"),
child: Text(L10N.of(context).tr.cancel),
onPressed: () => Navigator.of(context).pop(false),
),
FlatButton(
child: const Text("Delete"),
child: Text(L10N.of(context).tr.delete),
onPressed: () => Navigator.of(context).pop(true),
),
],

View File

@ -16,20 +16,17 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:timecop/blocs/timers/bloc.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/models/timer_entry.dart';
import 'StoppedTimerRow.dart';
class DayGrouping {
final DateTime date;
List<TimerEntry> entries = [];
//static DateFormat _dateFormat = DateFormat('yMMMMd');
static DateFormat _dateFormat = DateFormat('yMMMMd');
DayGrouping(this.date);
Widget rows(BuildContext context) {
DateFormat _dateFormat = DateFormat('yMMMd', L10N.of(context).locale.toString());
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[

View File

@ -28,6 +28,7 @@ import 'package:timecop/blocs/projects/projects_bloc.dart';
import 'package:timecop/blocs/projects/projects_state.dart';
import 'package:timecop/blocs/timers/bloc.dart';
import 'package:timecop/components/ProjectColour.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/models/project.dart';
class ExportScreen extends StatefulWidget {
@ -43,10 +44,6 @@ class _ExportScreenState extends State<ExportScreen> {
List<Project> selectedProjects = [];
static DateFormat _dateFormat = DateFormat("EE, MMM d, yyyy");
int countTimers() {
}
@override
void initState() {
super.initState();
@ -59,7 +56,7 @@ class _ExportScreenState extends State<ExportScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Export"),
title: Text(L10N.of(context).tr.export),
actions: <Widget>[
IconButton(
icon: Icon(FontAwesomeIcons.database),
@ -73,7 +70,7 @@ class _ExportScreenState extends State<ExportScreen> {
File copiedDB = await File(dbPath).copy(p.join(directory.path, "timecop.db"));
dbPath = copiedDB.path;
}
await FlutterShare.shareFile(title: 'Time Cop Database (${_dateFormat.format(DateTime.now())})', filePath: dbPath);
await FlutterShare.shareFile(title: L10N.of(context).tr.timeCopDatabase(_dateFormat.format(DateTime.now())), filePath: dbPath);
},
)
],
@ -86,7 +83,7 @@ class _ExportScreenState extends State<ExportScreen> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
"Filter",
L10N.of(context).tr.filter,
style: TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.w800
@ -100,7 +97,7 @@ class _ExportScreenState extends State<ExportScreen> {
actionExtentRatio: 0.15,
child: ListTile(
leading: Icon(FontAwesomeIcons.calendar),
title: Text("From"),
title: Text(L10N.of(context).tr.from),
trailing: Text(_startDate == null ? "" : _dateFormat.format(_startDate)),
onTap: () async {
await DatePicker.showDatePicker(
@ -138,7 +135,7 @@ class _ExportScreenState extends State<ExportScreen> {
actionExtentRatio: 0.15,
child: ListTile(
leading: Icon(FontAwesomeIcons.calendar),
title: Text("To"),
title: Text(L10N.of(context).tr.to),
trailing: Text(_endDate == null ? "" : _dateFormat.format(_endDate)),
onTap: () async {
await DatePicker.showDatePicker(
@ -180,7 +177,7 @@ class _ExportScreenState extends State<ExportScreen> {
children: <Widget>[
ListTile(
title: Text(
"Include Projects",
L10N.of(context).tr.includeProjects,
style: TextStyle(
color: Theme.of(context).accentColor,
fontSize: Theme.of(context).textTheme.body1.fontSize,
@ -218,7 +215,7 @@ class _ExportScreenState extends State<ExportScreen> {
child: ListView(
children: <Project>[null].followedBy(state.projects).map((project) => ListTile(
leading: ProjectColour(project: project,),
title: Text(project?.name ?? "(no project)"),
title: Text(project?.name ?? L10N.of(context).tr.noProject),
trailing: Checkbox(
value: selectedProjects.any((p) => p?.id == project?.id),
activeColor: Theme.of(context).accentColor,
@ -258,7 +255,7 @@ class _ExportScreenState extends State<ExportScreen> {
assert(projects != null);
List<List<String>> data = <List<String>>[
<String>["Project", "Task", "Time (h)"],
<String>[L10N.of(context).tr.project, L10N.of(context).tr.description, L10N.of(context).tr.timeH],
]
.followedBy(
timers.state.timers
@ -284,7 +281,7 @@ class _ExportScreenState extends State<ExportScreen> {
final String localPath = '${directory.path}/timecop.csv';
File file = File(localPath);
await file.writeAsString(csv, flush: true);
await FlutterShare.shareFile(title: 'Time Cop Entries (${_dateFormat.format(DateTime.now())})', filePath: localPath);
await FlutterShare.shareFile(title: L10N.of(context).tr.timeCopEntries(_dateFormat.format(DateTime.now())), filePath: localPath);
}
),
);

View File

@ -17,6 +17,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
import 'package:timecop/blocs/projects/bloc.dart';
import 'package:timecop/blocs/projects/projects_bloc.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/models/project.dart';
class ProjectEditor extends StatefulWidget {
@ -59,14 +60,14 @@ class _ProjectEditorState extends State<ProjectEditor> {
shrinkWrap: true,
children: <Widget>[
Text(
widget.project == null ? "Create New Project" : "Edit Project",
widget.project == null ? L10N.of(context).tr.createNewProject : L10N.of(context).tr.editProject,
style: Theme.of(context).textTheme.title,
),
TextFormField(
controller: _nameController,
validator: (String value) => value.trim().isEmpty ? "Please enter a name" : null,
validator: (String value) => value.trim().isEmpty ? L10N.of(context).tr.pleaseEnterAName : null,
decoration: InputDecoration(
hintText: "Project Name",
hintText: L10N.of(context).tr.projectName,
),
),
MaterialColorPicker(
@ -78,11 +79,11 @@ class _ProjectEditorState extends State<ProjectEditor> {
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlatButton(
child: Text("Cancel"),
child: Text(L10N.of(context).tr.cancel),
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
child: Text(widget.project == null ? "Create" : "Save"),
child: Text(widget.project == null ? L10N.of(context).tr.create: L10N.of(context).tr.save),
onPressed: () async {
bool valid = _formKey.currentState.validate();
if(!valid) return;

View File

@ -18,6 +18,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:timecop/blocs/projects/bloc.dart';
import 'package:timecop/components/ProjectColour.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/screens/projects/ProjectEditor.dart';
class ProjectsScreen extends StatelessWidget {
@ -30,7 +31,7 @@ class ProjectsScreen extends StatelessWidget {
return Scaffold(
appBar: AppBar(
title: Text("Projects"),
title: Text(L10N.of(context).tr.projects),
),
body: BlocBuilder<ProjectsBloc, ProjectsState>(
bloc: projectsBloc,
@ -56,26 +57,26 @@ class ProjectsScreen extends StatelessWidget {
bool delete = await showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text("Confirm Delete"),
title: Text(L10N.of(context).tr.confirmDelete),
content: RichText(
textAlign: TextAlign.justify,
text: TextSpan(
style: TextStyle(color: Theme.of(context).textTheme.body1.color),
children: <TextSpan>[
TextSpan(text: "Are you sure you want to delete “"),
TextSpan(text: L10N.of(context).tr.areYouSureYouWantToDeletePrefix),
TextSpan(text: "", style: TextStyle(color: project.colour)),
TextSpan(text: project.name, style: TextStyle(fontStyle: FontStyle.italic)),
TextSpan(text: "”?"),
TextSpan(text: L10N.of(context).tr.areYouSureYouWantToDeletePostfix),
]
)
),
actions: <Widget>[
FlatButton(
child: const Text("Cancel"),
child: Text(L10N.of(context).tr.cancel),
onPressed: () => Navigator.of(context).pop(false),
),
FlatButton(
child: const Text("Delete"),
child: Text(L10N.of(context).tr.delete),
onPressed: () => Navigator.of(context).pop(true),
),
],

View File

@ -23,6 +23,7 @@ import 'package:intl/intl.dart';
import 'package:timecop/blocs/projects/bloc.dart';
import 'package:timecop/blocs/timers/bloc.dart';
import 'package:timecop/components/ProjectColour.dart';
import 'package:timecop/l10n.dart';
import 'package:timecop/models/project.dart';
import 'package:timecop/models/timer_entry.dart';
@ -73,7 +74,7 @@ class _TimerEditorState extends State<TimerEditor> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Edit Timer"),
title: Text(L10N.of(context).tr.editTimer),
),
body: Form(
key: _formKey,
@ -101,7 +102,7 @@ class _TimerEditorState extends State<TimerEditor> {
ProjectColour(project: null),
Padding(
padding: EdgeInsets.fromLTRB(4.0, 0, 0, 0),
child: Text("(no project)", style: TextStyle(color: Theme.of(context).disabledColor)),
child: Text(L10N.of(context).tr.noProject, style: TextStyle(color: Theme.of(context).disabledColor)),
),
],
),
@ -129,13 +130,13 @@ class _TimerEditorState extends State<TimerEditor> {
child: TextFormField(
controller: _descriptionController,
decoration: InputDecoration(
labelText: "Description",
hintText: "What were you doing?",
labelText: L10N.of(context).tr.description,
hintText: L10N.of(context).tr.whatWereYouDoing,
),
),
),
ListTile(
title: Text("Start Time"),
title: Text(L10N.of(context).tr.startTime),
trailing: Text(_dateFormat.format(_startTime)),
onTap: () async {
await DatePicker.showDateTimePicker(
@ -155,7 +156,7 @@ class _TimerEditorState extends State<TimerEditor> {
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.15,
child: ListTile(
title: Text("End Time"),
title: Text(L10N.of(context).tr.endTime),
trailing: Text(_endTime == null ? "" : _dateFormat.format(_endTime)),
onTap: () async {
await DatePicker.showDateTimePicker(
@ -193,7 +194,7 @@ class _TimerEditorState extends State<TimerEditor> {
initialData: DateTime.now(),
stream: _updateTimerStreamController.stream,
builder: (BuildContext context, AsyncSnapshot<DateTime> snapshot) => ListTile(
title: Text("Duration"),
title: Text(L10N.of(context).tr.duration),
trailing: Text(TimerEntry.formatDuration(
_endTime == null
? snapshot.data.difference(_startTime)

View File

@ -47,6 +47,7 @@ flutter:
- CHANGELOG.md
- icon.no-bg.svg
- icon.no-bg.pink.svg
- l10n/
fonts:
- family: PublicSans
fonts: