dev i18n
This commit is contained in:
parent
be2335e3bc
commit
27bcd339ea
@ -4,6 +4,7 @@ import Ionicons from '@expo/vector-icons/Ionicons';
|
|||||||
import { useFocusEffect } from 'expo-router';
|
import { useFocusEffect } from 'expo-router';
|
||||||
import { navigate } from 'expo-router/build/global-state/routing';
|
import { navigate } from 'expo-router/build/global-state/routing';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CategoryItem } from '../../components/categoryItem';
|
import { CategoryItem } from '../../components/categoryItem';
|
||||||
import { ThemedText } from '../../components/ThemedText';
|
import { ThemedText } from '../../components/ThemedText';
|
||||||
import { ThemedView } from '../../components/ThemedView';
|
import { ThemedView } from '../../components/ThemedView';
|
||||||
@ -17,6 +18,7 @@ const categoryRepository = new CategoryRepository(new SQLiteDataService<Category
|
|||||||
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
|
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
|
||||||
|
|
||||||
export default function CategoryScreen() {
|
export default function CategoryScreen() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]);
|
||||||
|
|
||||||
@ -42,14 +44,18 @@ export default function CategoryScreen() {
|
|||||||
<ThemedView style={styles.headerContainer}>
|
<ThemedView style={styles.headerContainer}>
|
||||||
<ThemedText type="title" style={{
|
<ThemedText type="title" style={{
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
}}>Taskeep!</ThemedText>
|
}}>
|
||||||
|
{t('app_name')}
|
||||||
|
</ThemedText>
|
||||||
<Pressable onPress={() => {
|
<Pressable onPress={() => {
|
||||||
navigate('../categoryForm', {})
|
navigate('../categoryForm', {})
|
||||||
}}>
|
}}>
|
||||||
<Ionicons name='add-circle-sharp' size={32} color="#fff" />
|
<Ionicons name='add-circle-sharp' size={32} color="#fff" />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
<ThemedText style={{ padding: 24 }} type="subtitle">Your Categories</ThemedText>
|
<ThemedText style={{ padding: 24 }} type="subtitle">
|
||||||
|
{t('your_categories')}
|
||||||
|
</ThemedText>
|
||||||
<ScrollView style={styles.scrollView}>
|
<ScrollView style={styles.scrollView}>
|
||||||
{categories.map((cat, index) => (
|
{categories.map((cat, index) => (
|
||||||
<CategoryItem categories={categories} key={index} category={cat} onUpdate={() => {
|
<CategoryItem categories={categories} key={index} category={cat} onUpdate={() => {
|
||||||
|
|||||||
@ -38,25 +38,25 @@ export default function HomeScreen() {
|
|||||||
|
|
||||||
useEffect(() => { // Initial data fetch
|
useEffect(() => { // Initial data fetch
|
||||||
(async () => {
|
(async () => {
|
||||||
console.log("Fetching categories...");
|
console.log(t('fetching_categories'));
|
||||||
try {
|
try {
|
||||||
const fetchedCategories = await categoryRepository.findAll();
|
const fetchedCategories = await categoryRepository.findAll();
|
||||||
if (fetchedCategories.length === 0) {
|
if (fetchedCategories.length === 0) {
|
||||||
// If no categories, create a default one
|
// If no categories, create a default one
|
||||||
const defaultCategory = new Category("Uncategorized", "🤷♂️");
|
const defaultCategory = new Category(t('default_category_name'), t('default_category_icon'));
|
||||||
const createdCat = await categoryRepository.create(defaultCategory);
|
const createdCat = await categoryRepository.create(defaultCategory);
|
||||||
setCategories([createdCat]);
|
setCategories([createdCat]);
|
||||||
} else {
|
} else {
|
||||||
setCategories(fetchedCategories);
|
setCategories(fetchedCategories);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching categories:", error);
|
console.error(t('error_fetching_categories'), error);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
console.log("Fetching tasks...");
|
console.log(t('fetching_tasks'));
|
||||||
RefreshTasks();
|
RefreshTasks();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching categories or tasks:", error);
|
console.error(t('error_fetching_tasks'), error);
|
||||||
}
|
}
|
||||||
setReady(true);
|
setReady(true);
|
||||||
})();
|
})();
|
||||||
@ -76,24 +76,23 @@ export default function HomeScreen() {
|
|||||||
const fetchedTasks = await taskRepository.findAll();
|
const fetchedTasks = await taskRepository.findAll();
|
||||||
fetchedTasks.sort((a, b) => {
|
fetchedTasks.sort((a, b) => {
|
||||||
return (a.daysToRedo! * 24 * 3600 * 1000 - (Date.now() - a.lastDone!)) - ((b.daysToRedo! * 24 * 3600 * 1000) - (Date.now() - b.lastDone!));
|
return (a.daysToRedo! * 24 * 3600 * 1000 - (Date.now() - a.lastDone!)) - ((b.daysToRedo! * 24 * 3600 * 1000) - (Date.now() - b.lastDone!));
|
||||||
})
|
});
|
||||||
console.log("Tasks fetched:", fetchedTasks);
|
console.log(t('tasks_fetched'), fetchedTasks);
|
||||||
|
|
||||||
|
|
||||||
setTasks(fetchedTasks);
|
setTasks(fetchedTasks);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error refreshing tasks:", error);
|
console.error(t('error_refreshing_tasks'), error);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const RefreshCategories = async () => {
|
const RefreshCategories = async () => {
|
||||||
try {
|
try {
|
||||||
const fetchedCategories = await categoryRepository.findAll();
|
const fetchedCategories = await categoryRepository.findAll();
|
||||||
setCategories(fetchedCategories);
|
setCategories(fetchedCategories);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error refreshing categories:", error);
|
console.error(t('error_refreshing_categories'), error);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ready) {
|
if (ready) {
|
||||||
@ -104,9 +103,9 @@ export default function HomeScreen() {
|
|||||||
}
|
}
|
||||||
}, [ready]);
|
}, [ready]);
|
||||||
|
|
||||||
//On layout focus to refresh tasks
|
// On layout focus to refresh tasks
|
||||||
useFocusEffect(React.useCallback(() => {
|
useFocusEffect(React.useCallback(() => {
|
||||||
console.log("focus");
|
console.log(t('focus_event'));
|
||||||
(async () => {
|
(async () => {
|
||||||
await RefreshTasks();
|
await RefreshTasks();
|
||||||
await RefreshCategories();
|
await RefreshCategories();
|
||||||
@ -116,18 +115,18 @@ export default function HomeScreen() {
|
|||||||
return (
|
return (
|
||||||
<ThemedView style={styles.mainContainer}>
|
<ThemedView style={styles.mainContainer}>
|
||||||
<ThemedView style={styles.headerContainer}>
|
<ThemedView style={styles.headerContainer}>
|
||||||
<ThemedText type="title" style={
|
<ThemedText type="title" style={{ color: '#fff' }}>
|
||||||
{
|
{t('app_name')}
|
||||||
color: '#fff',
|
</ThemedText>
|
||||||
}
|
|
||||||
}>Taskeep!</ThemedText>
|
|
||||||
<Pressable onPress={() => {
|
<Pressable onPress={() => {
|
||||||
navigate('./taskForm', {})
|
navigate('./taskForm', {});
|
||||||
}}>
|
}}>
|
||||||
<Ionicons name='add-circle-sharp' size={32} color="#fff" />
|
<Ionicons name='add-circle-sharp' size={32} color="#fff" />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
<ThemedText style={{ padding: 24 }} type="subtitle">Your Tasks</ThemedText>
|
<ThemedText style={{ padding: 24 }} type="subtitle">
|
||||||
|
{t('your_tasks')}
|
||||||
|
</ThemedText>
|
||||||
<ScrollView style={styles.scrollView}>
|
<ScrollView style={styles.scrollView}>
|
||||||
{tasks.map((task, index) => (
|
{tasks.map((task, index) => (
|
||||||
<TaskItem categories={categories} key={Date.now() + "-" + task.id} task={task} onUpdate={() => {
|
<TaskItem categories={categories} key={Date.now() + "-" + task.id} task={task} onUpdate={() => {
|
||||||
@ -136,10 +135,10 @@ export default function HomeScreen() {
|
|||||||
))}
|
))}
|
||||||
{tasks.length === 0 && (<>
|
{tasks.length === 0 && (<>
|
||||||
<ThemedText type="defaultSemiBold" style={{ textAlign: 'center', marginTop: 20 }}>
|
<ThemedText type="defaultSemiBold" style={{ textAlign: 'center', marginTop: 20 }}>
|
||||||
No tasks available.
|
{t('no_tasks_available')}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
<ThemedText type="default" style={{ textAlign: 'center', marginTop: 20 }}>
|
<ThemedText type="default" style={{ textAlign: 'center', marginTop: 20 }}>
|
||||||
Add a task to get started!
|
{t('add_task_to_get_started')}
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
</>)}
|
</>)}
|
||||||
<View style={{ height: 50 }} />
|
<View style={{ height: 50 }} />
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
import { Link, Stack } from 'expo-router';
|
import { Link, Stack } from 'expo-router';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import { ThemedText } from '../components/ThemedText';
|
import { ThemedText } from '../components/ThemedText';
|
||||||
import { ThemedView } from '../components/ThemedView';
|
import { ThemedView } from '../components/ThemedView';
|
||||||
|
|
||||||
export default function NotFoundScreen() {
|
export default function NotFoundScreen() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
<Stack.Screen options={{ title: t('oops') }} />
|
||||||
<ThemedView style={styles.container}>
|
<ThemedView style={styles.container}>
|
||||||
<ThemedText type="title">This screen does not exist.</ThemedText>
|
<ThemedText type="title">{t('screen_does_not_exist')}</ThemedText>
|
||||||
<Link href="/" style={styles.link}>
|
<Link href="/" style={styles.link}>
|
||||||
<ThemedText type="link">Go to home screen!</ThemedText>
|
<ThemedText type="link">{t('go_to_home_screen')}</ThemedText>
|
||||||
</Link>
|
</Link>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -3,12 +3,9 @@ import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native
|
|||||||
import { useFonts } from 'expo-font';
|
import { useFonts } from 'expo-font';
|
||||||
import { Stack } from 'expo-router';
|
import { Stack } from 'expo-router';
|
||||||
import { StatusBar } from 'expo-status-bar';
|
import { StatusBar } from 'expo-status-bar';
|
||||||
import { registerWidgetTaskHandler } from 'react-native-android-widget';
|
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
import 'react-native-reanimated';
|
import 'react-native-reanimated';
|
||||||
import { widgetTaskHandler } from './widgets/widget-task-handler';
|
import "../i18n.js";
|
||||||
|
|
||||||
registerWidgetTaskHandler(widgetTaskHandler);
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useColorScheme } from '../hooks/useColorScheme';
|
import { useColorScheme } from '../hooks/useColorScheme';
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { toast } from '@backpackapp-io/react-native-toast';
|
|||||||
import Ionicons from '@expo/vector-icons/Ionicons';
|
import Ionicons from '@expo/vector-icons/Ionicons';
|
||||||
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
|
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native';
|
import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native';
|
||||||
import { EmojiPopup } from 'react-native-emoji-popup';
|
import { EmojiPopup } from 'react-native-emoji-popup';
|
||||||
import { ThemedView } from '../..//components/ThemedView';
|
import { ThemedView } from '../..//components/ThemedView';
|
||||||
@ -13,6 +14,7 @@ import { SQLiteDataService } from '../../services/data/sqliteDataService';
|
|||||||
const categoryRepository = new CategoryRepository(new SQLiteDataService<Category>(CategoryQuery));
|
const categoryRepository = new CategoryRepository(new SQLiteDataService<Category>(CategoryQuery));
|
||||||
|
|
||||||
export default function CategoryForm() {
|
export default function CategoryForm() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { category: categoryJson } = useLocalSearchParams();
|
const { category: categoryJson } = useLocalSearchParams();
|
||||||
const category: Category = categoryJson ? JSON.parse(categoryJson as string) : null;
|
const category: Category = categoryJson ? JSON.parse(categoryJson as string) : null;
|
||||||
@ -30,7 +32,7 @@ export default function CategoryForm() {
|
|||||||
<ThemedView style={styles.headerContainer}>
|
<ThemedView style={styles.headerContainer}>
|
||||||
<ThemedText type="title" style={{
|
<ThemedText type="title" style={{
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
}}> Taskeep! </ThemedText>
|
}}> {t('app_name')} </ThemedText>
|
||||||
< Pressable onPress={() => {
|
< Pressable onPress={() => {
|
||||||
router.back();
|
router.back();
|
||||||
}
|
}
|
||||||
@ -39,16 +41,16 @@ export default function CategoryForm() {
|
|||||||
</Pressable>
|
</Pressable>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
< ScrollView style={styles.scrollView} >
|
< ScrollView style={styles.scrollView} >
|
||||||
<ThemedText type="subtitle">Add a new category</ThemedText>
|
<ThemedText type="subtitle">{t('add_new_category')}</ThemedText>
|
||||||
<ThemedView style={styles.stepContainer}>
|
<ThemedView style={styles.stepContainer}>
|
||||||
<EmojiPopup onEmojiSelected={emoji => setNewCategory({ ...newCategory, icon: emoji })} >
|
<EmojiPopup onEmojiSelected={emoji => setNewCategory({ ...newCategory, icon: emoji })} >
|
||||||
<ThemedView style={styles.addIconView}>
|
<ThemedView style={styles.addIconView}>
|
||||||
{newCategory.icon != "" ? <ThemedText>{newCategory.icon}</ThemedText> : <Ionicons name={newCategory.icon || "happy-outline"} size={24} color="#ccc" />}
|
{newCategory.icon != "" ? <ThemedText>{newCategory.icon}</ThemedText> : <Ionicons name={newCategory.icon || "happy-outline"} size={24} color="#ccc" />}
|
||||||
<ThemedText>{newCategory.icon != "" ? "Change" : "Select"} icon *</ThemedText>
|
<ThemedText>{newCategory.icon != "" ? t('change_icon') : t('select_icon')} *</ThemedText>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
</EmojiPopup>
|
</EmojiPopup>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Category title *"
|
placeholder={t('category_title_placeholder')}
|
||||||
style={styles.inputText}
|
style={styles.inputText}
|
||||||
placeholderTextColor="#333"
|
placeholderTextColor="#333"
|
||||||
value={newCategory.title}
|
value={newCategory.title}
|
||||||
@ -64,22 +66,22 @@ export default function CategoryForm() {
|
|||||||
...category,
|
...category,
|
||||||
...newCategory
|
...newCategory
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
toast.success("Category updated successfully!");
|
toast.success(t('category_updated_successfully'));
|
||||||
router.back();
|
router.back();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Create new task
|
// Create new task
|
||||||
categoryRepository.create(new Category(newCategory.title, newCategory.icon)).then(() => {
|
categoryRepository.create(new Category(newCategory.title, newCategory.icon)).then(() => {
|
||||||
toast.success("Category added successfully!");
|
toast.success(t('category_added_successfully'));
|
||||||
router.back();
|
router.back();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error("Please select an icon and enter a title for the category.");
|
toast.error(t('select_icon_and_enter_title'));
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Ionicons name='checkmark-circle-outline' size={24} color="#fff" />
|
<Ionicons name='checkmark-circle-outline' size={24} color="#fff" />
|
||||||
<ThemedText style={styles.addButtonText}>{category?.id ? "Update" : "Add"} Category</ThemedText>
|
<ThemedText style={styles.addButtonText}>{category?.id ? t('update_category') : t('add_category')}</ThemedText>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
< View style={{ height: 50 }} />
|
< View style={{ height: 50 }} />
|
||||||
</ScrollView >
|
</ScrollView >
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { toast } from '@backpackapp-io/react-native-toast';
|
|||||||
import Ionicons from '@expo/vector-icons/Ionicons';
|
import Ionicons from '@expo/vector-icons/Ionicons';
|
||||||
import { Stack, useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router';
|
import { Stack, useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native';
|
import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native';
|
||||||
import Dropdown from 'react-native-input-select';
|
import Dropdown from 'react-native-input-select';
|
||||||
import { ThemedText } from '../../components/ThemedText';
|
import { ThemedText } from '../../components/ThemedText';
|
||||||
@ -45,6 +46,7 @@ const timeUnitToDays = (num: number, unit: string): number => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function TasksForm() {
|
export default function TasksForm() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { task: taskJson } = useLocalSearchParams();
|
const { task: taskJson } = useLocalSearchParams();
|
||||||
const task: Task = taskJson ? JSON.parse(taskJson as string) : null;
|
const task: Task = taskJson ? JSON.parse(taskJson as string) : null;
|
||||||
@ -99,7 +101,9 @@ export default function TasksForm() {
|
|||||||
<ThemedView style={styles.headerContainer}>
|
<ThemedView style={styles.headerContainer}>
|
||||||
<ThemedText type="title" style={{
|
<ThemedText type="title" style={{
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
}}> Taskeep! </ThemedText>
|
}}>
|
||||||
|
{t('app_name')}
|
||||||
|
</ThemedText>
|
||||||
< Pressable onPress={() => {
|
< Pressable onPress={() => {
|
||||||
router.back();
|
router.back();
|
||||||
}
|
}
|
||||||
@ -108,10 +112,10 @@ export default function TasksForm() {
|
|||||||
</Pressable>
|
</Pressable>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
< ScrollView style={styles.scrollView} >
|
< ScrollView style={styles.scrollView} >
|
||||||
<ThemedText type="subtitle">Add a new task</ThemedText>
|
<ThemedText type="subtitle">{t('add_new_task')}</ThemedText>
|
||||||
<ThemedView style={styles.stepContainer}>
|
<ThemedView style={styles.stepContainer}>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Task title *"
|
placeholder={t('task_title_placeholder')}
|
||||||
style={styles.inputText}
|
style={styles.inputText}
|
||||||
placeholderTextColor="#333"
|
placeholderTextColor="#333"
|
||||||
value={newTask.title}
|
value={newTask.title}
|
||||||
@ -119,7 +123,7 @@ export default function TasksForm() {
|
|||||||
/>
|
/>
|
||||||
<View style={{ flexDirection: 'row', gap: 8, height: 60 }}>
|
<View style={{ flexDirection: 'row', gap: 8, height: 60 }}>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder={`${timeUnits.find(unit => unit.value === selectedTimeUnit)?.label} after which task is due *`}
|
placeholder={`${timeUnits.find(unit => unit.value === selectedTimeUnit)?.label} ${t('after_which_task_is_due')}`}
|
||||||
style={styles.inputText}
|
style={styles.inputText}
|
||||||
placeholderTextColor="#333"
|
placeholderTextColor="#333"
|
||||||
keyboardType='numeric'
|
keyboardType='numeric'
|
||||||
@ -159,7 +163,7 @@ export default function TasksForm() {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placeholder="Select category *"
|
placeholder={t('select_category')}
|
||||||
options={
|
options={
|
||||||
categories.map(category => ({
|
categories.map(category => ({
|
||||||
label: `${category.icon} ${category.title}`,
|
label: `${category.icon} ${category.title}`,
|
||||||
@ -219,7 +223,7 @@ export default function TasksForm() {
|
|||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Ionicons name='checkmark-circle-outline' size={24} color="#fff" />
|
<Ionicons name='checkmark-circle-outline' size={24} color="#fff" />
|
||||||
<ThemedText style={styles.addButtonText}>{task?.id ? "Update" : "Add"} Task</ThemedText>
|
<ThemedText style={styles.addButtonText}>{task?.id ? t('update_task') : t('add_task')}</ThemedText>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
< View style={{ height: 50 }} />
|
< View style={{ height: 50 }} />
|
||||||
</ScrollView >
|
</ScrollView >
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ColorProp, FlexWidget, IconWidget, ListWidget, SvgWidget, TextWidget, } from 'react-native-android-widget';
|
import { ColorProp, FlexWidget, IconWidget, ListWidget, SvgWidget, TextWidget, } from 'react-native-android-widget';
|
||||||
import { getClockProgressSVG } from '../../components/clockProgress';
|
import { getClockProgressSVG } from '../../components/clockProgress';
|
||||||
import { Category } from '../../models/category';
|
import { Category } from '../../models/category';
|
||||||
@ -16,6 +17,7 @@ const adaptDaysToGoToUnit = (t: Task) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?: Category[] }) {
|
export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?: Category[] }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const demoTasks: Task[] = [
|
const demoTasks: Task[] = [
|
||||||
new Task('Task 1', 1, 1, new Date().getTime() - 1000 * 60 * 60 * 24 * 0, 5), // 2 days ago
|
new Task('Task 1', 1, 1, new Date().getTime() - 1000 * 60 * 60 * 24 * 0, 5), // 2 days ago
|
||||||
@ -50,7 +52,7 @@ export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?:
|
|||||||
width: 'match_parent',
|
width: 'match_parent',
|
||||||
}}>
|
}}>
|
||||||
<TextWidget
|
<TextWidget
|
||||||
text="Hello, Tasks!"
|
text={t('hello_tasks')}
|
||||||
style={{
|
style={{
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
@ -110,14 +112,14 @@ export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?:
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextWidget
|
<TextWidget
|
||||||
text={`${category ? (category.icon + " " + category.title) : 'Uncategorized'}`}
|
text={`${category ? (category.icon + " " + category.title) : t('default_category_name')}`}
|
||||||
style={{
|
style={{
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextWidget
|
<TextWidget
|
||||||
text={parseInt(timeLeft.toString()) > 0 ? timeLeft + " left" : "Overdue"}
|
text={parseInt(timeLeft.toString()) > 0 ? timeLeft + " " + t('left') : t('overdue')}
|
||||||
style={{
|
style={{
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@ -137,7 +139,7 @@ export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?:
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
{(tasks && tasks.length === 0) &&
|
{(tasks && tasks.length === 0) &&
|
||||||
<TextWidget text='No tasks created.' style={{
|
<TextWidget text={t('no_tasks_created')} style={{
|
||||||
color: '#ffffff'
|
color: '#ffffff'
|
||||||
}}>
|
}}>
|
||||||
</TextWidget>}
|
</TextWidget>}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Alert, ColorSchemeName, Pressable, StyleSheet, useColorScheme, type Vie
|
|||||||
|
|
||||||
import { Text } from '@react-navigation/elements';
|
import { Text } from '@react-navigation/elements';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useThemeColor } from '../hooks/useThemeColor';
|
import { useThemeColor } from '../hooks/useThemeColor';
|
||||||
import { Category, CategoryQuery } from '../models/category';
|
import { Category, CategoryQuery } from '../models/category';
|
||||||
import { Task, TaskQuery } from '../models/task';
|
import { Task, TaskQuery } from '../models/task';
|
||||||
@ -24,6 +25,7 @@ const categoryRepository = new CategoryRepository(new SQLiteDataService<Category
|
|||||||
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
|
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
|
||||||
|
|
||||||
export function CategoryItem({ category, categories, onUpdate, lightColor, darkColor, ...otherProps }: CategoryItemProps) {
|
export function CategoryItem({ category, categories, onUpdate, lightColor, darkColor, ...otherProps }: CategoryItemProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const colorScheme = useColorScheme();
|
const colorScheme = useColorScheme();
|
||||||
@ -32,11 +34,11 @@ export function CategoryItem({ category, categories, onUpdate, lightColor, darkC
|
|||||||
//select edit or remove
|
//select edit or remove
|
||||||
console.log("Long pressed task:", category);
|
console.log("Long pressed task:", category);
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Select action",
|
t('select_action'),
|
||||||
"Choose an action for the task",
|
t('choose_action_for_category'),
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: "Edit",
|
text: t('edit'),
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
// Navigate to edit task form
|
// Navigate to edit task form
|
||||||
console.log("Editing category:", category);
|
console.log("Editing category:", category);
|
||||||
@ -45,16 +47,16 @@ export function CategoryItem({ category, categories, onUpdate, lightColor, darkC
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "Remove",
|
text: t('remove'),
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
// Remove the task
|
// Remove the task
|
||||||
taskRepository.findAll().then((tasks) => {
|
taskRepository.findAll().then((tasks) => {
|
||||||
const tasksInCategory = tasks.filter(task => task.category === category.id);
|
const tasksInCategory = tasks.filter(task => task.category === category.id);
|
||||||
if (tasksInCategory.length > 0) {
|
if (tasksInCategory.length > 0) {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Cannot delete category",
|
t('cannot_delete_category'),
|
||||||
"This category has tasks assigned to it. Please remove or reassign tasks before deleting the category.",
|
t('category_has_tasks'),
|
||||||
[{ text: "OK" }]
|
[{ text: t('ok') }]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
categoryRepository.delete(category.id!).then(() => {
|
categoryRepository.delete(category.id!).then(() => {
|
||||||
@ -69,7 +71,7 @@ export function CategoryItem({ category, categories, onUpdate, lightColor, darkC
|
|||||||
},
|
},
|
||||||
style: "destructive"
|
style: "destructive"
|
||||||
},
|
},
|
||||||
{ text: "Cancel", style: "cancel" }
|
{ text: t('cancel'), style: "cancel" }
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { toast } from '@backpackapp-io/react-native-toast';
|
|||||||
import { Text } from '@react-navigation/elements';
|
import { Text } from '@react-navigation/elements';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useThemeColor } from '../hooks/useThemeColor';
|
import { useThemeColor } from '../hooks/useThemeColor';
|
||||||
import { Category } from '../models/category';
|
import { Category } from '../models/category';
|
||||||
import { Task, TaskQuery } from '../models/task';
|
import { Task, TaskQuery } from '../models/task';
|
||||||
@ -34,6 +35,7 @@ const adaptDaysToGoToUnit = (t: Task) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ...otherProps }: TaskItemProps) {
|
export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ...otherProps }: TaskItemProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const daysLeft = useRef(task.daysToRedo! * 24 * 3600 * 1000 - (Date.now() - task.lastDone!));
|
const daysLeft = useRef(task.daysToRedo! * 24 * 3600 * 1000 - (Date.now() - task.lastDone!));
|
||||||
@ -46,11 +48,11 @@ export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ..
|
|||||||
//select edit or remove
|
//select edit or remove
|
||||||
console.log("Long pressed task:", task);
|
console.log("Long pressed task:", task);
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Select action",
|
t('select_action'),
|
||||||
"Choose an action for the task",
|
t('choose_action_for_task'),
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: "Edit",
|
text: t('edit'),
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
// Navigate to edit task form
|
// Navigate to edit task form
|
||||||
console.log("Editing task:", task);
|
console.log("Editing task:", task);
|
||||||
@ -59,22 +61,22 @@ export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ..
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "Remove",
|
text: t('remove'),
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
// Remove the task
|
// Remove the task
|
||||||
console.log("Removing task:", task);
|
console.log("Removing task:", task);
|
||||||
if (!task?.id) return;
|
if (!task?.id) return;
|
||||||
taskRepository.delete(task.id).then(() => {
|
taskRepository.delete(task.id).then(() => {
|
||||||
toast.success("Task removed successfully");
|
toast.success(t('task_removed_successfully'));
|
||||||
onUpdate?.();
|
onUpdate?.();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
toast.success("Error removing task");
|
toast.error(t('error_removing_task'));
|
||||||
console.error("Error removing task:", error);
|
console.error("Error removing task:", error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
style: "destructive"
|
style: "destructive"
|
||||||
},
|
},
|
||||||
{ text: "Cancel", style: "cancel" }
|
{ text: t('cancel'), style: "cancel" }
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,10 +97,10 @@ export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ..
|
|||||||
<Pressable style={styles(colorScheme).checkButton} onPress={() => {
|
<Pressable style={styles(colorScheme).checkButton} onPress={() => {
|
||||||
|
|
||||||
taskRepository.update(task!.id!, { ...task, lastDone: Date.now() }).then(() => {
|
taskRepository.update(task!.id!, { ...task, lastDone: Date.now() }).then(() => {
|
||||||
toast.success("Task updated successfully");
|
toast.success(t('task_updated_successfully'));
|
||||||
onUpdate?.();
|
onUpdate?.();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
toast.error("Error updating task");
|
toast.error(t('error_updating_task'));
|
||||||
console.error("Error updating task:", error);
|
console.error("Error updating task:", error);
|
||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
26
i18n.js
26
i18n.js
@ -1,23 +1,31 @@
|
|||||||
|
import * as Localization from 'expo-localization';
|
||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
import * as RNLocalize from 'react-native-localize';
|
import ar from './translations/ar.json';
|
||||||
|
import de from './translations/de.json';
|
||||||
import en from './translations/en.json';
|
import en from './translations/en.json';
|
||||||
|
import es from './translations/es.json';
|
||||||
|
import fr from './translations/fr.json';
|
||||||
|
import it from './translations/it.json';
|
||||||
|
import ja from './translations/ja.json';
|
||||||
|
import pt from './translations/pt.json';
|
||||||
|
import ru from './translations/ru.json';
|
||||||
|
import zh from './translations/zh.json';
|
||||||
|
|
||||||
const resources = {
|
registerWidgetTaskHandler(widgetTaskHandler);
|
||||||
en: { translation: en },
|
|
||||||
};
|
|
||||||
|
|
||||||
const fallback = { languageTag: 'en', isRTL: false };
|
// Initialize i18n
|
||||||
|
const resources = { en, es, fr, de, it, pt, zh, ja, ru, ar };
|
||||||
const { languageTag } = RNLocalize.findBestLanguageTag(Object.keys(resources)) || fallback;
|
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
resources,
|
resources,
|
||||||
lng: languageTag,
|
lng: Localization.locale.split('-')[0], // Use the device's language
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
interpolation: { escapeValue: false },
|
interpolation: {
|
||||||
|
escapeValue: false, // React already escapes values
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
@ -1,8 +1,8 @@
|
|||||||
|
import 'expo-router/entry';
|
||||||
import { registerWidgetTaskHandler } from 'react-native-android-widget';
|
import { registerWidgetTaskHandler } from 'react-native-android-widget';
|
||||||
import { widgetTaskHandler } from './app/widgets/widget-task-handler';
|
import { widgetTaskHandler } from './app/widgets/widget-task-handler';
|
||||||
|
|
||||||
registerWidgetTaskHandler(widgetTaskHandler);
|
registerWidgetTaskHandler(widgetTaskHandler);
|
||||||
|
|
||||||
import 'expo-router/entry';
|
console.log(registerWidgetTaskHandler);
|
||||||
import './i18n.js';
|
|
||||||
|
|
||||||
|
|||||||
22
package-lock.json
generated
22
package-lock.json
generated
@ -23,6 +23,7 @@
|
|||||||
"expo-haptics": "~14.1.4",
|
"expo-haptics": "~14.1.4",
|
||||||
"expo-image": "~2.3.2",
|
"expo-image": "~2.3.2",
|
||||||
"expo-linking": "~7.1.7",
|
"expo-linking": "~7.1.7",
|
||||||
|
"expo-localization": "~16.1.6",
|
||||||
"expo-navigation-bar": "~4.2.7",
|
"expo-navigation-bar": "~4.2.7",
|
||||||
"expo-notifications": "~0.31.4",
|
"expo-notifications": "~0.31.4",
|
||||||
"expo-router": "~5.1.3",
|
"expo-router": "~5.1.3",
|
||||||
@ -37,7 +38,7 @@
|
|||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-i18next": "^15.6.1",
|
"react-i18next": "^15.6.1",
|
||||||
"react-native": "0.79.5",
|
"react-native": "0.79.5",
|
||||||
"react-native-android-widget": "^0.17.0",
|
"react-native-android-widget": "^0.17.1",
|
||||||
"react-native-emoji-popup": "^0.3.2",
|
"react-native-emoji-popup": "^0.3.2",
|
||||||
"react-native-gesture-handler": "~2.24.0",
|
"react-native-gesture-handler": "~2.24.0",
|
||||||
"react-native-input-select": "^2.1.7",
|
"react-native-input-select": "^2.1.7",
|
||||||
@ -7533,6 +7534,19 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-localization": {
|
||||||
|
"version": "16.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-16.1.6.tgz",
|
||||||
|
"integrity": "sha512-v4HwNzs8QvyKHwl40MvETNEKr77v1o9/eVC8WCBY++DIlBAvonHyJe2R9CfqpZbC4Tlpl7XV+07nLXc8O5PQsA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"rtl-detect": "^1.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-modules-autolinking": {
|
"node_modules/expo-modules-autolinking": {
|
||||||
"version": "2.1.14",
|
"version": "2.1.14",
|
||||||
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.14.tgz",
|
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.14.tgz",
|
||||||
@ -12686,6 +12700,12 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rtl-detect": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/run-parallel": {
|
"node_modules/run-parallel": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
"expo-haptics": "~14.1.4",
|
"expo-haptics": "~14.1.4",
|
||||||
"expo-image": "~2.3.2",
|
"expo-image": "~2.3.2",
|
||||||
"expo-linking": "~7.1.7",
|
"expo-linking": "~7.1.7",
|
||||||
|
"expo-localization": "~16.1.6",
|
||||||
"expo-navigation-bar": "~4.2.7",
|
"expo-navigation-bar": "~4.2.7",
|
||||||
"expo-notifications": "~0.31.4",
|
"expo-notifications": "~0.31.4",
|
||||||
"expo-router": "~5.1.3",
|
"expo-router": "~5.1.3",
|
||||||
@ -40,7 +41,7 @@
|
|||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-i18next": "^15.6.1",
|
"react-i18next": "^15.6.1",
|
||||||
"react-native": "0.79.5",
|
"react-native": "0.79.5",
|
||||||
"react-native-android-widget": "^0.17.0",
|
"react-native-android-widget": "^0.17.1",
|
||||||
"react-native-emoji-popup": "^0.3.2",
|
"react-native-emoji-popup": "^0.3.2",
|
||||||
"react-native-gesture-handler": "~2.24.0",
|
"react-native-gesture-handler": "~2.24.0",
|
||||||
"react-native-input-select": "^2.1.7",
|
"react-native-input-select": "^2.1.7",
|
||||||
|
|||||||
33
translations/ar.json
Normal file
33
translations/ar.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "مرحبًا",
|
||||||
|
"hello_world": "مرحبًا، العالم!",
|
||||||
|
"fetching_categories": "جارٍ تحميل الفئات...",
|
||||||
|
"default_category_name": "غير مصنف",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "خطأ أثناء تحميل الفئات:",
|
||||||
|
"fetching_tasks": "جارٍ تحميل المهام...",
|
||||||
|
"error_fetching_tasks": "خطأ أثناء تحميل المهام:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "مهامك",
|
||||||
|
"no_tasks_available": "لا توجد مهام متاحة.",
|
||||||
|
"add_task_to_get_started": "أضف مهمة للبدء!",
|
||||||
|
"tasks_fetched": "تم تحميل المهام:",
|
||||||
|
"error_refreshing_tasks": "خطأ أثناء تحديث المهام:",
|
||||||
|
"error_refreshing_categories": "خطأ أثناء تحديث الفئات:",
|
||||||
|
"focus_event": "تم تشغيل حدث التركيز.",
|
||||||
|
"oops": "عذرًا!",
|
||||||
|
"screen_does_not_exist": "هذه الشاشة غير موجودة.",
|
||||||
|
"go_to_home_screen": "العودة إلى الشاشة الرئيسية!",
|
||||||
|
"your_categories": "فئاتك",
|
||||||
|
"add_new_category": "إضافة فئة جديدة",
|
||||||
|
"category_title_placeholder": "عنوان الفئة *",
|
||||||
|
"add_new_task": "إضافة مهمة جديدة",
|
||||||
|
"task_title_placeholder": "عنوان المهمة *",
|
||||||
|
"cannot_delete_category": "لا يمكن حذف الفئة",
|
||||||
|
"category_has_tasks": "تحتوي هذه الفئة على مهام مخصصة. يرجى إزالة المهام أو إعادة تعيينها قبل حذف الفئة.",
|
||||||
|
"ok": "موافق",
|
||||||
|
"hello_tasks": "مرحبًا، المهام!",
|
||||||
|
"no_tasks_created": "لم يتم إنشاء مهام.",
|
||||||
|
"left": "متبقي",
|
||||||
|
"overdue": "متأخر"
|
||||||
|
}
|
||||||
33
translations/de.json
Normal file
33
translations/de.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Willkommen",
|
||||||
|
"hello_world": "Hallo, Welt!",
|
||||||
|
"fetching_categories": "Kategorien werden geladen...",
|
||||||
|
"default_category_name": "Nicht kategorisiert",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "Fehler beim Laden der Kategorien:",
|
||||||
|
"fetching_tasks": "Aufgaben werden geladen...",
|
||||||
|
"error_fetching_tasks": "Fehler beim Laden der Aufgaben:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "Ihre Aufgaben",
|
||||||
|
"no_tasks_available": "Keine Aufgaben verfügbar.",
|
||||||
|
"add_task_to_get_started": "Fügen Sie eine Aufgabe hinzu, um zu beginnen!",
|
||||||
|
"tasks_fetched": "Aufgaben geladen:",
|
||||||
|
"error_refreshing_tasks": "Fehler beim Aktualisieren der Aufgaben:",
|
||||||
|
"error_refreshing_categories": "Fehler beim Aktualisieren der Kategorien:",
|
||||||
|
"focus_event": "Fokusereignis ausgelöst.",
|
||||||
|
"oops": "Hoppla!",
|
||||||
|
"screen_does_not_exist": "Dieser Bildschirm existiert nicht.",
|
||||||
|
"go_to_home_screen": "Zur Startseite gehen!",
|
||||||
|
"your_categories": "Ihre Kategorien",
|
||||||
|
"add_new_category": "Neue Kategorie hinzufügen",
|
||||||
|
"category_title_placeholder": "Kategorietitel *",
|
||||||
|
"add_new_task": "Neue Aufgabe hinzufügen",
|
||||||
|
"task_title_placeholder": "Aufgabentitel *",
|
||||||
|
"cannot_delete_category": "Kategorie kann nicht gelöscht werden",
|
||||||
|
"category_has_tasks": "Diese Kategorie hat zugewiesene Aufgaben. Bitte entfernen oder weisen Sie die Aufgaben neu zu, bevor Sie die Kategorie löschen.",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "Hallo, Aufgaben!",
|
||||||
|
"no_tasks_created": "Keine Aufgaben erstellt.",
|
||||||
|
"left": "verbleibend",
|
||||||
|
"overdue": "Überfällig"
|
||||||
|
}
|
||||||
@ -1,4 +1,33 @@
|
|||||||
{
|
{
|
||||||
"welcome": "Welcome",
|
"welcome": "Welcome",
|
||||||
"hello_world": "Hello, World!"
|
"hello_world": "Hello, World!",
|
||||||
|
"fetching_categories": "Fetching categories...",
|
||||||
|
"default_category_name": "Uncategorized",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "Error fetching categories:",
|
||||||
|
"fetching_tasks": "Fetching tasks...",
|
||||||
|
"error_fetching_tasks": "Error fetching tasks:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "Your Tasks",
|
||||||
|
"no_tasks_available": "No tasks available.",
|
||||||
|
"add_task_to_get_started": "Add a task to get started!",
|
||||||
|
"tasks_fetched": "Tasks fetched:",
|
||||||
|
"error_refreshing_tasks": "Error refreshing tasks:",
|
||||||
|
"error_refreshing_categories": "Error refreshing categories:",
|
||||||
|
"focus_event": "Focus event triggered.",
|
||||||
|
"oops": "Oops!",
|
||||||
|
"screen_does_not_exist": "This screen does not exist.",
|
||||||
|
"go_to_home_screen": "Go to home screen!",
|
||||||
|
"your_categories": "Your Categories",
|
||||||
|
"add_new_category": "Add a new category",
|
||||||
|
"category_title_placeholder": "Category title *",
|
||||||
|
"add_new_task": "Add a new task",
|
||||||
|
"task_title_placeholder": "Task title *",
|
||||||
|
"cannot_delete_category": "Cannot delete category",
|
||||||
|
"category_has_tasks": "This category has tasks assigned to it. Please remove or reassign tasks before deleting the category.",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "Hello, Tasks!",
|
||||||
|
"no_tasks_created": "No tasks created.",
|
||||||
|
"left": "left",
|
||||||
|
"overdue": "Overdue"
|
||||||
}
|
}
|
||||||
33
translations/es.json
Normal file
33
translations/es.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Bienvenido",
|
||||||
|
"hello_world": "Hola, Mundo!",
|
||||||
|
"fetching_categories": "Cargando categorías...",
|
||||||
|
"default_category_name": "Sin categoría",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "Error al cargar categorías:",
|
||||||
|
"fetching_tasks": "Cargando tareas...",
|
||||||
|
"error_fetching_tasks": "Error al cargar tareas:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "Tus Tareas",
|
||||||
|
"no_tasks_available": "No hay tareas disponibles.",
|
||||||
|
"add_task_to_get_started": "¡Agrega una tarea para comenzar!",
|
||||||
|
"tasks_fetched": "Tareas cargadas:",
|
||||||
|
"error_refreshing_tasks": "Error al actualizar tareas:",
|
||||||
|
"error_refreshing_categories": "Error al actualizar categorías:",
|
||||||
|
"focus_event": "Evento de enfoque activado.",
|
||||||
|
"oops": "¡Ups!",
|
||||||
|
"screen_does_not_exist": "Esta pantalla no existe.",
|
||||||
|
"go_to_home_screen": "¡Ir a la pantalla principal!",
|
||||||
|
"your_categories": "Tus Categorías",
|
||||||
|
"add_new_category": "Agregar una nueva categoría",
|
||||||
|
"category_title_placeholder": "Título de la categoría *",
|
||||||
|
"add_new_task": "Agregar una nueva tarea",
|
||||||
|
"task_title_placeholder": "Título de la tarea *",
|
||||||
|
"cannot_delete_category": "No se puede eliminar la categoría",
|
||||||
|
"category_has_tasks": "Esta categoría tiene tareas asignadas. Por favor, elimina o reasigna las tareas antes de eliminar la categoría.",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "¡Hola, Tareas!",
|
||||||
|
"no_tasks_created": "No se han creado tareas.",
|
||||||
|
"left": "restante",
|
||||||
|
"overdue": "Atrasado"
|
||||||
|
}
|
||||||
33
translations/fr.json
Normal file
33
translations/fr.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Bienvenue",
|
||||||
|
"hello_world": "Bonjour, Monde!",
|
||||||
|
"fetching_categories": "Chargement des catégories...",
|
||||||
|
"default_category_name": "Non catégorisé",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "Erreur lors du chargement des catégories :",
|
||||||
|
"fetching_tasks": "Chargement des tâches...",
|
||||||
|
"error_fetching_tasks": "Erreur lors du chargement des tâches :",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "Vos Tâches",
|
||||||
|
"no_tasks_available": "Aucune tâche disponible.",
|
||||||
|
"add_task_to_get_started": "Ajoutez une tâche pour commencer!",
|
||||||
|
"tasks_fetched": "Tâches chargées :",
|
||||||
|
"error_refreshing_tasks": "Erreur lors de l'actualisation des tâches :",
|
||||||
|
"error_refreshing_categories": "Erreur lors de l'actualisation des catégories :",
|
||||||
|
"focus_event": "Événement de focus déclenché.",
|
||||||
|
"oops": "Oups!",
|
||||||
|
"screen_does_not_exist": "Cet écran n'existe pas.",
|
||||||
|
"go_to_home_screen": "Aller à l'écran d'accueil!",
|
||||||
|
"your_categories": "Vos Catégories",
|
||||||
|
"add_new_category": "Ajouter une nouvelle catégorie",
|
||||||
|
"category_title_placeholder": "Titre de la catégorie *",
|
||||||
|
"add_new_task": "Ajouter une nouvelle tâche",
|
||||||
|
"task_title_placeholder": "Titre de la tâche *",
|
||||||
|
"cannot_delete_category": "Impossible de supprimer la catégorie",
|
||||||
|
"category_has_tasks": "Cette catégorie a des tâches assignées. Veuillez supprimer ou réassigner les tâches avant de supprimer la catégorie.",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "Bonjour, Tâches!",
|
||||||
|
"no_tasks_created": "Aucune tâche créée.",
|
||||||
|
"left": "restant",
|
||||||
|
"overdue": "En retard"
|
||||||
|
}
|
||||||
0
translations/index.ts
Normal file
0
translations/index.ts
Normal file
33
translations/it.json
Normal file
33
translations/it.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Benvenuto",
|
||||||
|
"hello_world": "Ciao, Mondo!",
|
||||||
|
"fetching_categories": "Caricamento delle categorie...",
|
||||||
|
"default_category_name": "Non categorizzato",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "Errore durante il caricamento delle categorie:",
|
||||||
|
"fetching_tasks": "Caricamento delle attività...",
|
||||||
|
"error_fetching_tasks": "Errore durante il caricamento delle attività:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "Le tue attività",
|
||||||
|
"no_tasks_available": "Nessuna attività disponibile.",
|
||||||
|
"add_task_to_get_started": "Aggiungi un'attività per iniziare!",
|
||||||
|
"tasks_fetched": "Attività caricate:",
|
||||||
|
"error_refreshing_tasks": "Errore durante l'aggiornamento delle attività:",
|
||||||
|
"error_refreshing_categories": "Errore durante l'aggiornamento delle categorie:",
|
||||||
|
"focus_event": "Evento di focus attivato.",
|
||||||
|
"oops": "Ops!",
|
||||||
|
"screen_does_not_exist": "Questa schermata non esiste.",
|
||||||
|
"go_to_home_screen": "Vai alla schermata principale!",
|
||||||
|
"your_categories": "Le tue categorie",
|
||||||
|
"add_new_category": "Aggiungi una nuova categoria",
|
||||||
|
"category_title_placeholder": "Titolo della categoria *",
|
||||||
|
"add_new_task": "Aggiungi una nuova attività",
|
||||||
|
"task_title_placeholder": "Titolo dell'attività *",
|
||||||
|
"cannot_delete_category": "Impossibile eliminare la categoria",
|
||||||
|
"category_has_tasks": "Questa categoria ha attività assegnate. Rimuovi o riassegna le attività prima di eliminare la categoria.",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "Ciao, Attività!",
|
||||||
|
"no_tasks_created": "Nessuna attività creata.",
|
||||||
|
"left": "rimanente",
|
||||||
|
"overdue": "In ritardo"
|
||||||
|
}
|
||||||
33
translations/ja.json
Normal file
33
translations/ja.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "ようこそ",
|
||||||
|
"hello_world": "こんにちは、世界!",
|
||||||
|
"fetching_categories": "カテゴリを読み込んでいます...",
|
||||||
|
"default_category_name": "未分類",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "カテゴリの読み込み中にエラーが発生しました:",
|
||||||
|
"fetching_tasks": "タスクを読み込んでいます...",
|
||||||
|
"error_fetching_tasks": "タスクの読み込み中にエラーが発生しました:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "あなたのタスク",
|
||||||
|
"no_tasks_available": "利用可能なタスクはありません。",
|
||||||
|
"add_task_to_get_started": "タスクを追加して開始してください!",
|
||||||
|
"tasks_fetched": "タスクが読み込まれました:",
|
||||||
|
"error_refreshing_tasks": "タスクの更新中にエラーが発生しました:",
|
||||||
|
"error_refreshing_categories": "カテゴリの更新中にエラーが発生しました:",
|
||||||
|
"focus_event": "フォーカスイベントがトリガーされました。",
|
||||||
|
"oops": "おっと!",
|
||||||
|
"screen_does_not_exist": "この画面は存在しません。",
|
||||||
|
"go_to_home_screen": "ホーム画面に戻る!",
|
||||||
|
"your_categories": "あなたのカテゴリ",
|
||||||
|
"add_new_category": "新しいカテゴリを追加",
|
||||||
|
"category_title_placeholder": "カテゴリのタイトル *",
|
||||||
|
"add_new_task": "新しいタスクを追加",
|
||||||
|
"task_title_placeholder": "タスクのタイトル *",
|
||||||
|
"cannot_delete_category": "カテゴリを削除できません",
|
||||||
|
"category_has_tasks": "このカテゴリには割り当てられたタスクがあります。カテゴリを削除する前に、タスクを削除または再割り当てしてください。",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "こんにちは、タスク!",
|
||||||
|
"no_tasks_created": "タスクが作成されていません。",
|
||||||
|
"left": "残り",
|
||||||
|
"overdue": "期限切れ"
|
||||||
|
}
|
||||||
33
translations/pt.json
Normal file
33
translations/pt.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Bem-vindo",
|
||||||
|
"hello_world": "Olá, Mundo!",
|
||||||
|
"fetching_categories": "Carregando categorias...",
|
||||||
|
"default_category_name": "Não categorizado",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "Erro ao carregar categorias:",
|
||||||
|
"fetching_tasks": "Carregando tarefas...",
|
||||||
|
"error_fetching_tasks": "Erro ao carregar tarefas:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "Suas Tarefas",
|
||||||
|
"no_tasks_available": "Nenhuma tarefa disponível.",
|
||||||
|
"add_task_to_get_started": "Adicione uma tarefa para começar!",
|
||||||
|
"tasks_fetched": "Tarefas carregadas:",
|
||||||
|
"error_refreshing_tasks": "Erro ao atualizar tarefas:",
|
||||||
|
"error_refreshing_categories": "Erro ao atualizar categorias:",
|
||||||
|
"focus_event": "Evento de foco acionado.",
|
||||||
|
"oops": "Ops!",
|
||||||
|
"screen_does_not_exist": "Esta tela não existe.",
|
||||||
|
"go_to_home_screen": "Ir para a tela inicial!",
|
||||||
|
"your_categories": "Suas Categorias",
|
||||||
|
"add_new_category": "Adicionar uma nova categoria",
|
||||||
|
"category_title_placeholder": "Título da categoria *",
|
||||||
|
"add_new_task": "Adicionar uma nova tarefa",
|
||||||
|
"task_title_placeholder": "Título da tarefa *",
|
||||||
|
"cannot_delete_category": "Não é possível excluir a categoria",
|
||||||
|
"category_has_tasks": "Esta categoria tem tarefas atribuídas. Remova ou reatribua as tarefas antes de excluir a categoria.",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "Olá, Tarefas!",
|
||||||
|
"no_tasks_created": "Nenhuma tarefa criada.",
|
||||||
|
"left": "restante",
|
||||||
|
"overdue": "Atrasado"
|
||||||
|
}
|
||||||
33
translations/ru.json
Normal file
33
translations/ru.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "Добро пожаловать",
|
||||||
|
"hello_world": "Привет, мир!",
|
||||||
|
"fetching_categories": "Загрузка категорий...",
|
||||||
|
"default_category_name": "Без категории",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "Ошибка при загрузке категорий:",
|
||||||
|
"fetching_tasks": "Загрузка задач...",
|
||||||
|
"error_fetching_tasks": "Ошибка при загрузке задач:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "Ваши задачи",
|
||||||
|
"no_tasks_available": "Нет доступных задач.",
|
||||||
|
"add_task_to_get_started": "Добавьте задачу, чтобы начать!",
|
||||||
|
"tasks_fetched": "Задачи загружены:",
|
||||||
|
"error_refreshing_tasks": "Ошибка при обновлении задач:",
|
||||||
|
"error_refreshing_categories": "Ошибка при обновлении категорий:",
|
||||||
|
"focus_event": "Событие фокуса вызвано.",
|
||||||
|
"oops": "Упс!",
|
||||||
|
"screen_does_not_exist": "Этот экран не существует.",
|
||||||
|
"go_to_home_screen": "Перейти на главный экран!",
|
||||||
|
"your_categories": "Ваши категории",
|
||||||
|
"add_new_category": "Добавить новую категорию",
|
||||||
|
"category_title_placeholder": "Название категории *",
|
||||||
|
"add_new_task": "Добавить новую задачу",
|
||||||
|
"task_title_placeholder": "Название задачи *",
|
||||||
|
"cannot_delete_category": "Невозможно удалить категорию",
|
||||||
|
"category_has_tasks": "В этой категории есть назначенные задачи. Удалите или переназначьте задачи перед удалением категории.",
|
||||||
|
"ok": "OK",
|
||||||
|
"hello_tasks": "Привет, задачи!",
|
||||||
|
"no_tasks_created": "Задачи не созданы.",
|
||||||
|
"left": "осталось",
|
||||||
|
"overdue": "Просрочено"
|
||||||
|
}
|
||||||
33
translations/zh.json
Normal file
33
translations/zh.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"welcome": "欢迎",
|
||||||
|
"hello_world": "你好,世界!",
|
||||||
|
"fetching_categories": "正在加载类别...",
|
||||||
|
"default_category_name": "未分类",
|
||||||
|
"default_category_icon": "🤷♂️",
|
||||||
|
"error_fetching_categories": "加载类别时出错:",
|
||||||
|
"fetching_tasks": "正在加载任务...",
|
||||||
|
"error_fetching_tasks": "加载任务时出错:",
|
||||||
|
"app_name": "Taskeep!",
|
||||||
|
"your_tasks": "你的任务",
|
||||||
|
"no_tasks_available": "没有可用的任务。",
|
||||||
|
"add_task_to_get_started": "添加一个任务以开始!",
|
||||||
|
"tasks_fetched": "任务已加载:",
|
||||||
|
"error_refreshing_tasks": "刷新任务时出错:",
|
||||||
|
"error_refreshing_categories": "刷新类别时出错:",
|
||||||
|
"focus_event": "触发了焦点事件。",
|
||||||
|
"oops": "哎呀!",
|
||||||
|
"screen_does_not_exist": "此屏幕不存在。",
|
||||||
|
"go_to_home_screen": "回到主屏幕!",
|
||||||
|
"your_categories": "你的类别",
|
||||||
|
"add_new_category": "添加新类别",
|
||||||
|
"category_title_placeholder": "类别标题 *",
|
||||||
|
"add_new_task": "添加新任务",
|
||||||
|
"task_title_placeholder": "任务标题 *",
|
||||||
|
"cannot_delete_category": "无法删除类别",
|
||||||
|
"category_has_tasks": "此类别有分配的任务。请在删除类别之前删除或重新分配任务。",
|
||||||
|
"ok": "好的",
|
||||||
|
"hello_tasks": "你好,任务!",
|
||||||
|
"no_tasks_created": "没有创建任务。",
|
||||||
|
"left": "剩余",
|
||||||
|
"overdue": "逾期"
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user