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 { navigate } from 'expo-router/build/global-state/routing';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CategoryItem } from '../../components/categoryItem';
|
||||
import { ThemedText } from '../../components/ThemedText';
|
||||
import { ThemedView } from '../../components/ThemedView';
|
||||
@ -17,6 +18,7 @@ const categoryRepository = new CategoryRepository(new SQLiteDataService<Category
|
||||
const taskRepository = new TaskRepository(new SQLiteDataService<Task>(TaskQuery));
|
||||
|
||||
export default function CategoryScreen() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
|
||||
@ -42,14 +44,18 @@ export default function CategoryScreen() {
|
||||
<ThemedView style={styles.headerContainer}>
|
||||
<ThemedText type="title" style={{
|
||||
color: '#fff',
|
||||
}}>Taskeep!</ThemedText>
|
||||
}}>
|
||||
{t('app_name')}
|
||||
</ThemedText>
|
||||
<Pressable onPress={() => {
|
||||
navigate('../categoryForm', {})
|
||||
}}>
|
||||
<Ionicons name='add-circle-sharp' size={32} color="#fff" />
|
||||
</Pressable>
|
||||
</ThemedView>
|
||||
<ThemedText style={{ padding: 24 }} type="subtitle">Your Categories</ThemedText>
|
||||
<ThemedText style={{ padding: 24 }} type="subtitle">
|
||||
{t('your_categories')}
|
||||
</ThemedText>
|
||||
<ScrollView style={styles.scrollView}>
|
||||
{categories.map((cat, index) => (
|
||||
<CategoryItem categories={categories} key={index} category={cat} onUpdate={() => {
|
||||
|
||||
@ -38,25 +38,25 @@ export default function HomeScreen() {
|
||||
|
||||
useEffect(() => { // Initial data fetch
|
||||
(async () => {
|
||||
console.log("Fetching categories...");
|
||||
console.log(t('fetching_categories'));
|
||||
try {
|
||||
const fetchedCategories = await categoryRepository.findAll();
|
||||
if (fetchedCategories.length === 0) {
|
||||
// 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);
|
||||
setCategories([createdCat]);
|
||||
} else {
|
||||
setCategories(fetchedCategories);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching categories:", error);
|
||||
console.error(t('error_fetching_categories'), error);
|
||||
}
|
||||
try {
|
||||
console.log("Fetching tasks...");
|
||||
console.log(t('fetching_tasks'));
|
||||
RefreshTasks();
|
||||
} catch (error) {
|
||||
console.error("Error fetching categories or tasks:", error);
|
||||
console.error(t('error_fetching_tasks'), error);
|
||||
}
|
||||
setReady(true);
|
||||
})();
|
||||
@ -76,24 +76,23 @@ export default function HomeScreen() {
|
||||
const fetchedTasks = await taskRepository.findAll();
|
||||
fetchedTasks.sort((a, b) => {
|
||||
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);
|
||||
} catch (error) {
|
||||
console.error("Error refreshing tasks:", error);
|
||||
}
|
||||
console.error(t('error_refreshing_tasks'), error);
|
||||
}
|
||||
};
|
||||
|
||||
const RefreshCategories = async () => {
|
||||
try {
|
||||
const fetchedCategories = await categoryRepository.findAll();
|
||||
setCategories(fetchedCategories);
|
||||
} catch (error) {
|
||||
console.error("Error refreshing categories:", error);
|
||||
}
|
||||
console.error(t('error_refreshing_categories'), error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (ready) {
|
||||
@ -104,9 +103,9 @@ export default function HomeScreen() {
|
||||
}
|
||||
}, [ready]);
|
||||
|
||||
//On layout focus to refresh tasks
|
||||
// On layout focus to refresh tasks
|
||||
useFocusEffect(React.useCallback(() => {
|
||||
console.log("focus");
|
||||
console.log(t('focus_event'));
|
||||
(async () => {
|
||||
await RefreshTasks();
|
||||
await RefreshCategories();
|
||||
@ -116,18 +115,18 @@ export default function HomeScreen() {
|
||||
return (
|
||||
<ThemedView style={styles.mainContainer}>
|
||||
<ThemedView style={styles.headerContainer}>
|
||||
<ThemedText type="title" style={
|
||||
{
|
||||
color: '#fff',
|
||||
}
|
||||
}>Taskeep!</ThemedText>
|
||||
<ThemedText type="title" style={{ color: '#fff' }}>
|
||||
{t('app_name')}
|
||||
</ThemedText>
|
||||
<Pressable onPress={() => {
|
||||
navigate('./taskForm', {})
|
||||
navigate('./taskForm', {});
|
||||
}}>
|
||||
<Ionicons name='add-circle-sharp' size={32} color="#fff" />
|
||||
</Pressable>
|
||||
</ThemedView>
|
||||
<ThemedText style={{ padding: 24 }} type="subtitle">Your Tasks</ThemedText>
|
||||
<ThemedText style={{ padding: 24 }} type="subtitle">
|
||||
{t('your_tasks')}
|
||||
</ThemedText>
|
||||
<ScrollView style={styles.scrollView}>
|
||||
{tasks.map((task, index) => (
|
||||
<TaskItem categories={categories} key={Date.now() + "-" + task.id} task={task} onUpdate={() => {
|
||||
@ -136,10 +135,10 @@ export default function HomeScreen() {
|
||||
))}
|
||||
{tasks.length === 0 && (<>
|
||||
<ThemedText type="defaultSemiBold" style={{ textAlign: 'center', marginTop: 20 }}>
|
||||
No tasks available.
|
||||
{t('no_tasks_available')}
|
||||
</ThemedText>
|
||||
<ThemedText type="default" style={{ textAlign: 'center', marginTop: 20 }}>
|
||||
Add a task to get started!
|
||||
{t('add_task_to_get_started')}
|
||||
</ThemedText>
|
||||
</>)}
|
||||
<View style={{ height: 50 }} />
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
import { Link, Stack } from 'expo-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { ThemedText } from '../components/ThemedText';
|
||||
import { ThemedView } from '../components/ThemedView';
|
||||
|
||||
export default function NotFoundScreen() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
||||
<Stack.Screen options={{ title: t('oops') }} />
|
||||
<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}>
|
||||
<ThemedText type="link">Go to home screen!</ThemedText>
|
||||
<ThemedText type="link">{t('go_to_home_screen')}</ThemedText>
|
||||
</Link>
|
||||
</ThemedView>
|
||||
</>
|
||||
|
||||
@ -3,12 +3,9 @@ import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native
|
||||
import { useFonts } from 'expo-font';
|
||||
import { Stack } from 'expo-router';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
import { registerWidgetTaskHandler } from 'react-native-android-widget';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import 'react-native-reanimated';
|
||||
import { widgetTaskHandler } from './widgets/widget-task-handler';
|
||||
|
||||
registerWidgetTaskHandler(widgetTaskHandler);
|
||||
import "../i18n.js";
|
||||
|
||||
import React from 'react';
|
||||
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 { Stack, useLocalSearchParams, useRouter } from 'expo-router';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native';
|
||||
import { EmojiPopup } from 'react-native-emoji-popup';
|
||||
import { ThemedView } from '../..//components/ThemedView';
|
||||
@ -13,6 +14,7 @@ import { SQLiteDataService } from '../../services/data/sqliteDataService';
|
||||
const categoryRepository = new CategoryRepository(new SQLiteDataService<Category>(CategoryQuery));
|
||||
|
||||
export default function CategoryForm() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { category: categoryJson } = useLocalSearchParams();
|
||||
const category: Category = categoryJson ? JSON.parse(categoryJson as string) : null;
|
||||
@ -30,7 +32,7 @@ export default function CategoryForm() {
|
||||
<ThemedView style={styles.headerContainer}>
|
||||
<ThemedText type="title" style={{
|
||||
color: '#fff',
|
||||
}}> Taskeep! </ThemedText>
|
||||
}}> {t('app_name')} </ThemedText>
|
||||
< Pressable onPress={() => {
|
||||
router.back();
|
||||
}
|
||||
@ -39,16 +41,16 @@ export default function CategoryForm() {
|
||||
</Pressable>
|
||||
</ThemedView>
|
||||
< ScrollView style={styles.scrollView} >
|
||||
<ThemedText type="subtitle">Add a new category</ThemedText>
|
||||
<ThemedText type="subtitle">{t('add_new_category')}</ThemedText>
|
||||
<ThemedView style={styles.stepContainer}>
|
||||
<EmojiPopup onEmojiSelected={emoji => setNewCategory({ ...newCategory, icon: emoji })} >
|
||||
<ThemedView style={styles.addIconView}>
|
||||
{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>
|
||||
</EmojiPopup>
|
||||
<TextInput
|
||||
placeholder="Category title *"
|
||||
placeholder={t('category_title_placeholder')}
|
||||
style={styles.inputText}
|
||||
placeholderTextColor="#333"
|
||||
value={newCategory.title}
|
||||
@ -64,22 +66,22 @@ export default function CategoryForm() {
|
||||
...category,
|
||||
...newCategory
|
||||
}).then(() => {
|
||||
toast.success("Category updated successfully!");
|
||||
toast.success(t('category_updated_successfully'));
|
||||
router.back();
|
||||
});
|
||||
} else {
|
||||
// Create new task
|
||||
categoryRepository.create(new Category(newCategory.title, newCategory.icon)).then(() => {
|
||||
toast.success("Category added successfully!");
|
||||
toast.success(t('category_added_successfully'));
|
||||
router.back();
|
||||
});
|
||||
}
|
||||
} 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" />
|
||||
<ThemedText style={styles.addButtonText}>{category?.id ? "Update" : "Add"} Category</ThemedText>
|
||||
<ThemedText style={styles.addButtonText}>{category?.id ? t('update_category') : t('add_category')}</ThemedText>
|
||||
</Pressable>
|
||||
< View style={{ height: 50 }} />
|
||||
</ScrollView >
|
||||
|
||||
@ -2,6 +2,7 @@ import { toast } from '@backpackapp-io/react-native-toast';
|
||||
import Ionicons from '@expo/vector-icons/Ionicons';
|
||||
import { Stack, useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native';
|
||||
import Dropdown from 'react-native-input-select';
|
||||
import { ThemedText } from '../../components/ThemedText';
|
||||
@ -45,6 +46,7 @@ const timeUnitToDays = (num: number, unit: string): number => {
|
||||
}
|
||||
|
||||
export default function TasksForm() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { task: taskJson } = useLocalSearchParams();
|
||||
const task: Task = taskJson ? JSON.parse(taskJson as string) : null;
|
||||
@ -99,7 +101,9 @@ export default function TasksForm() {
|
||||
<ThemedView style={styles.headerContainer}>
|
||||
<ThemedText type="title" style={{
|
||||
color: '#fff',
|
||||
}}> Taskeep! </ThemedText>
|
||||
}}>
|
||||
{t('app_name')}
|
||||
</ThemedText>
|
||||
< Pressable onPress={() => {
|
||||
router.back();
|
||||
}
|
||||
@ -108,10 +112,10 @@ export default function TasksForm() {
|
||||
</Pressable>
|
||||
</ThemedView>
|
||||
< ScrollView style={styles.scrollView} >
|
||||
<ThemedText type="subtitle">Add a new task</ThemedText>
|
||||
<ThemedText type="subtitle">{t('add_new_task')}</ThemedText>
|
||||
<ThemedView style={styles.stepContainer}>
|
||||
<TextInput
|
||||
placeholder="Task title *"
|
||||
placeholder={t('task_title_placeholder')}
|
||||
style={styles.inputText}
|
||||
placeholderTextColor="#333"
|
||||
value={newTask.title}
|
||||
@ -119,7 +123,7 @@ export default function TasksForm() {
|
||||
/>
|
||||
<View style={{ flexDirection: 'row', gap: 8, height: 60 }}>
|
||||
<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}
|
||||
placeholderTextColor="#333"
|
||||
keyboardType='numeric'
|
||||
@ -159,7 +163,7 @@ export default function TasksForm() {
|
||||
/>
|
||||
</View>
|
||||
<Dropdown
|
||||
placeholder="Select category *"
|
||||
placeholder={t('select_category')}
|
||||
options={
|
||||
categories.map(category => ({
|
||||
label: `${category.icon} ${category.title}`,
|
||||
@ -219,7 +223,7 @@ export default function TasksForm() {
|
||||
}
|
||||
}}>
|
||||
<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>
|
||||
< View style={{ height: 50 }} />
|
||||
</ScrollView >
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ColorProp, FlexWidget, IconWidget, ListWidget, SvgWidget, TextWidget, } from 'react-native-android-widget';
|
||||
import { getClockProgressSVG } from '../../components/clockProgress';
|
||||
import { Category } from '../../models/category';
|
||||
@ -16,6 +17,7 @@ const adaptDaysToGoToUnit = (t: Task) => {
|
||||
};
|
||||
|
||||
export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?: Category[] }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const demoTasks: Task[] = [
|
||||
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',
|
||||
}}>
|
||||
<TextWidget
|
||||
text="Hello, Tasks!"
|
||||
text={t('hello_tasks')}
|
||||
style={{
|
||||
color: '#ffffff',
|
||||
fontSize: 24,
|
||||
@ -110,14 +112,14 @@ export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?:
|
||||
}}
|
||||
/>
|
||||
<TextWidget
|
||||
text={`${category ? (category.icon + " " + category.title) : 'Uncategorized'}`}
|
||||
text={`${category ? (category.icon + " " + category.title) : t('default_category_name')}`}
|
||||
style={{
|
||||
color: '#ffffff',
|
||||
fontSize: 12,
|
||||
}}
|
||||
/>
|
||||
<TextWidget
|
||||
text={parseInt(timeLeft.toString()) > 0 ? timeLeft + " left" : "Overdue"}
|
||||
text={parseInt(timeLeft.toString()) > 0 ? timeLeft + " " + t('left') : t('overdue')}
|
||||
style={{
|
||||
color: '#ffffff',
|
||||
fontSize: 16,
|
||||
@ -137,7 +139,7 @@ export function HelloWidget({ tasks, categories }: { tasks: Task[], categories?:
|
||||
)
|
||||
})}
|
||||
{(tasks && tasks.length === 0) &&
|
||||
<TextWidget text='No tasks created.' style={{
|
||||
<TextWidget text={t('no_tasks_created')} style={{
|
||||
color: '#ffffff'
|
||||
}}>
|
||||
</TextWidget>}
|
||||
|
||||
@ -2,6 +2,7 @@ import { Alert, ColorSchemeName, Pressable, StyleSheet, useColorScheme, type Vie
|
||||
|
||||
import { Text } from '@react-navigation/elements';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useThemeColor } from '../hooks/useThemeColor';
|
||||
import { Category, CategoryQuery } from '../models/category';
|
||||
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));
|
||||
|
||||
export function CategoryItem({ category, categories, onUpdate, lightColor, darkColor, ...otherProps }: CategoryItemProps) {
|
||||
const { t } = useTranslation();
|
||||
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||
const router = useRouter();
|
||||
const colorScheme = useColorScheme();
|
||||
@ -32,11 +34,11 @@ export function CategoryItem({ category, categories, onUpdate, lightColor, darkC
|
||||
//select edit or remove
|
||||
console.log("Long pressed task:", category);
|
||||
Alert.alert(
|
||||
"Select action",
|
||||
"Choose an action for the task",
|
||||
t('select_action'),
|
||||
t('choose_action_for_category'),
|
||||
[
|
||||
{
|
||||
text: "Edit",
|
||||
text: t('edit'),
|
||||
onPress: () => {
|
||||
// Navigate to edit task form
|
||||
console.log("Editing category:", category);
|
||||
@ -45,16 +47,16 @@ export function CategoryItem({ category, categories, onUpdate, lightColor, darkC
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Remove",
|
||||
text: t('remove'),
|
||||
onPress: () => {
|
||||
// Remove the task
|
||||
taskRepository.findAll().then((tasks) => {
|
||||
const tasksInCategory = tasks.filter(task => task.category === category.id);
|
||||
if (tasksInCategory.length > 0) {
|
||||
Alert.alert(
|
||||
"Cannot delete category",
|
||||
"This category has tasks assigned to it. Please remove or reassign tasks before deleting the category.",
|
||||
[{ text: "OK" }]
|
||||
t('cannot_delete_category'),
|
||||
t('category_has_tasks'),
|
||||
[{ text: t('ok') }]
|
||||
);
|
||||
} else {
|
||||
categoryRepository.delete(category.id!).then(() => {
|
||||
@ -69,7 +71,7 @@ export function CategoryItem({ category, categories, onUpdate, lightColor, darkC
|
||||
},
|
||||
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 { useRouter } from 'expo-router';
|
||||
import { useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useThemeColor } from '../hooks/useThemeColor';
|
||||
import { Category } from '../models/category';
|
||||
import { Task, TaskQuery } from '../models/task';
|
||||
@ -34,6 +35,7 @@ const adaptDaysToGoToUnit = (t: Task) => {
|
||||
};
|
||||
|
||||
export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ...otherProps }: TaskItemProps) {
|
||||
const { t } = useTranslation();
|
||||
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||
const router = useRouter();
|
||||
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
|
||||
console.log("Long pressed task:", task);
|
||||
Alert.alert(
|
||||
"Select action",
|
||||
"Choose an action for the task",
|
||||
t('select_action'),
|
||||
t('choose_action_for_task'),
|
||||
[
|
||||
{
|
||||
text: "Edit",
|
||||
text: t('edit'),
|
||||
onPress: () => {
|
||||
// Navigate to edit task form
|
||||
console.log("Editing task:", task);
|
||||
@ -59,22 +61,22 @@ export function TaskItem({ task, categories, onUpdate, lightColor, darkColor, ..
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Remove",
|
||||
text: t('remove'),
|
||||
onPress: () => {
|
||||
// Remove the task
|
||||
console.log("Removing task:", task);
|
||||
if (!task?.id) return;
|
||||
taskRepository.delete(task.id).then(() => {
|
||||
toast.success("Task removed successfully");
|
||||
toast.success(t('task_removed_successfully'));
|
||||
onUpdate?.();
|
||||
}).catch((error) => {
|
||||
toast.success("Error removing task");
|
||||
toast.error(t('error_removing_task'));
|
||||
console.error("Error removing task:", error);
|
||||
});
|
||||
},
|
||||
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={() => {
|
||||
|
||||
taskRepository.update(task!.id!, { ...task, lastDone: Date.now() }).then(() => {
|
||||
toast.success("Task updated successfully");
|
||||
toast.success(t('task_updated_successfully'));
|
||||
onUpdate?.();
|
||||
}).catch((error) => {
|
||||
toast.error("Error updating task");
|
||||
toast.error(t('error_updating_task'));
|
||||
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 { 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 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 = {
|
||||
en: { translation: en },
|
||||
};
|
||||
registerWidgetTaskHandler(widgetTaskHandler);
|
||||
|
||||
const fallback = { languageTag: 'en', isRTL: false };
|
||||
|
||||
const { languageTag } = RNLocalize.findBestLanguageTag(Object.keys(resources)) || fallback;
|
||||
// Initialize i18n
|
||||
const resources = { en, es, fr, de, it, pt, zh, ja, ru, ar };
|
||||
|
||||
i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
lng: languageTag,
|
||||
lng: Localization.locale.split('-')[0], // Use the device's language
|
||||
fallbackLng: 'en',
|
||||
interpolation: { escapeValue: false },
|
||||
interpolation: {
|
||||
escapeValue: false, // React already escapes values
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@ -1,8 +1,8 @@
|
||||
import 'expo-router/entry';
|
||||
import { registerWidgetTaskHandler } from 'react-native-android-widget';
|
||||
import { widgetTaskHandler } from './app/widgets/widget-task-handler';
|
||||
|
||||
registerWidgetTaskHandler(widgetTaskHandler);
|
||||
|
||||
import 'expo-router/entry';
|
||||
import './i18n.js';
|
||||
console.log(registerWidgetTaskHandler);
|
||||
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@ -23,6 +23,7 @@
|
||||
"expo-haptics": "~14.1.4",
|
||||
"expo-image": "~2.3.2",
|
||||
"expo-linking": "~7.1.7",
|
||||
"expo-localization": "~16.1.6",
|
||||
"expo-navigation-bar": "~4.2.7",
|
||||
"expo-notifications": "~0.31.4",
|
||||
"expo-router": "~5.1.3",
|
||||
@ -37,7 +38,7 @@
|
||||
"react-dom": "19.0.0",
|
||||
"react-i18next": "^15.6.1",
|
||||
"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-gesture-handler": "~2.24.0",
|
||||
"react-native-input-select": "^2.1.7",
|
||||
@ -7533,6 +7534,19 @@
|
||||
"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": {
|
||||
"version": "2.1.14",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"expo-haptics": "~14.1.4",
|
||||
"expo-image": "~2.3.2",
|
||||
"expo-linking": "~7.1.7",
|
||||
"expo-localization": "~16.1.6",
|
||||
"expo-navigation-bar": "~4.2.7",
|
||||
"expo-notifications": "~0.31.4",
|
||||
"expo-router": "~5.1.3",
|
||||
@ -40,7 +41,7 @@
|
||||
"react-dom": "19.0.0",
|
||||
"react-i18next": "^15.6.1",
|
||||
"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-gesture-handler": "~2.24.0",
|
||||
"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",
|
||||
"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