mirror of
https://github.com/hamaluik/timecop.git
synced 2025-08-24 07:00:45 +08:00
inserted localized strings everywhere
This commit is contained in:
@ -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
|
||||
|
@ -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
41
l10n/de.json
Normal 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
42
l10n/en.json
Normal 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
41
l10n/es.json
Normal 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
41
l10n/fr.json
Normal 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
41
l10n/hi.json
Normal 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
41
l10n/id.json
Normal 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
41
l10n/ja.json
Normal 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
41
l10n/ko.json
Normal 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
41
l10n/pt.json
Normal 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
41
l10n/ru.json
Normal 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
41
l10n/zh.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"about": "关于",
|
||||
"appDescription": "时间跟踪应用程序尊重您的隐私并在不花哨的情况下完成工作。",
|
||||
"appLegalese": "版权所有©Kenton Hamaluik,2020年",
|
||||
"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": "时间警察徽标"
|
||||
}
|
@ -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,
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
//);
|
||||
}
|
||||
}
|
75
lib/data_providers/json_l10n_provider.dart
Normal file
75
lib/data_providers/json_l10n_provider.dart
Normal 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-";
|
||||
}
|
@ -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?";
|
||||
}
|
@ -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?";
|
||||
}
|
@ -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);
|
||||
}
|
@ -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) {
|
||||
|
@ -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: [
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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(),
|
||||
],
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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),
|
||||
),
|
||||
],
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
),
|
||||
],
|
||||
|
@ -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>[
|
||||
|
@ -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);
|
||||
}
|
||||
),
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
),
|
||||
],
|
||||
|
@ -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)
|
||||
|
@ -47,6 +47,7 @@ flutter:
|
||||
- CHANGELOG.md
|
||||
- icon.no-bg.svg
|
||||
- icon.no-bg.pink.svg
|
||||
- l10n/
|
||||
fonts:
|
||||
- family: PublicSans
|
||||
fonts:
|
||||
|
Reference in New Issue
Block a user