diff --git a/app/(tabs)/categories.tsx b/app/(tabs)/categories.tsx index f25cee3..2f497dd 100644 --- a/app/(tabs)/categories.tsx +++ b/app/(tabs)/categories.tsx @@ -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(TaskQuery)); export default function CategoryScreen() { + const { t } = useTranslation(); const [categories, setCategories] = useState([]); @@ -42,14 +44,18 @@ export default function CategoryScreen() { Taskeep! + }}> + {t('app_name')} + { navigate('../categoryForm', {}) }}> - Your Categories + + {t('your_categories')} + {categories.map((cat, index) => ( { diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 362bfa6..913e6fa 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -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 ( - Taskeep! + + {t('app_name')} + { - navigate('./taskForm', {}) + navigate('./taskForm', {}); }}> - Your Tasks + + {t('your_tasks')} + {tasks.map((task, index) => ( { @@ -136,10 +135,10 @@ export default function HomeScreen() { ))} {tasks.length === 0 && (<> - No tasks available. + {t('no_tasks_available')} - Add a task to get started! + {t('add_task_to_get_started')} )} diff --git a/app/+not-found.tsx b/app/+not-found.tsx index cadd03f..17db939 100644 --- a/app/+not-found.tsx +++ b/app/+not-found.tsx @@ -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 ( <> - + - This screen does not exist. + {t('screen_does_not_exist')} - Go to home screen! + {t('go_to_home_screen')} diff --git a/app/_layout.tsx b/app/_layout.tsx index 44b1361..ead2657 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -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'; diff --git a/app/categoryForm/index.tsx b/app/categoryForm/index.tsx index be57729..f630b59 100644 --- a/app/categoryForm/index.tsx +++ b/app/categoryForm/index.tsx @@ -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(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() { Taskeep! + }}> {t('app_name')} < Pressable onPress={() => { router.back(); } @@ -39,16 +41,16 @@ export default function CategoryForm() { < ScrollView style={styles.scrollView} > - Add a new category + {t('add_new_category')} setNewCategory({ ...newCategory, icon: emoji })} > {newCategory.icon != "" ? {newCategory.icon} : } - {newCategory.icon != "" ? "Change" : "Select"} icon * + {newCategory.icon != "" ? t('change_icon') : t('select_icon')} * { - 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')); } }}> - {category?.id ? "Update" : "Add"} Category + {category?.id ? t('update_category') : t('add_category')} < View style={{ height: 50 }} /> diff --git a/app/taskForm/index.tsx b/app/taskForm/index.tsx index 226ceae..6c703b9 100644 --- a/app/taskForm/index.tsx +++ b/app/taskForm/index.tsx @@ -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() { Taskeep! + }}> + {t('app_name')} + < Pressable onPress={() => { router.back(); } @@ -108,10 +112,10 @@ export default function TasksForm() { < ScrollView style={styles.scrollView} > - Add a new task + {t('add_new_task')} 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() { /> ({ label: `${category.icon} ${category.title}`, @@ -219,7 +223,7 @@ export default function TasksForm() { } }}> - {task?.id ? "Update" : "Add"} Task + {task?.id ? t('update_task') : t('add_task')} < View style={{ height: 50 }} /> diff --git a/app/widgets/hello.tsx b/app/widgets/hello.tsx index 7cf05c7..0eb7e87 100644 --- a/app/widgets/hello.tsx +++ b/app/widgets/hello.tsx @@ -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', }}> 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) && - } diff --git a/components/categoryItem.tsx b/components/categoryItem.tsx index 99b29c1..3df5fe6 100644 --- a/components/categoryItem.tsx +++ b/components/categoryItem.tsx @@ -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(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" } ]); } diff --git a/components/taskItem.tsx b/components/taskItem.tsx index f618157..dfbf8d9 100644 --- a/components/taskItem.tsx +++ b/components/taskItem.tsx @@ -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, .. { 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); }); }}> diff --git a/i18n.js b/i18n.js index 88f53b6..0612ca7 100644 --- a/i18n.js +++ b/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; \ No newline at end of file diff --git a/index.tsx b/index.tsx index f1539ef..0de2073 100644 --- a/index.tsx +++ b/index.tsx @@ -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); diff --git a/package-lock.json b/package-lock.json index 6997087..103cb05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index c964bec..b111502 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/translations/ar.json b/translations/ar.json new file mode 100644 index 0000000..37b7d1c --- /dev/null +++ b/translations/ar.json @@ -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": "متأخر" +} \ No newline at end of file diff --git a/translations/de.json b/translations/de.json new file mode 100644 index 0000000..8d9da9d --- /dev/null +++ b/translations/de.json @@ -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" +} \ No newline at end of file diff --git a/translations/en.json b/translations/en.json index f8b6f2c..25d20c1 100644 --- a/translations/en.json +++ b/translations/en.json @@ -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" } \ No newline at end of file diff --git a/translations/es.json b/translations/es.json new file mode 100644 index 0000000..c68620b --- /dev/null +++ b/translations/es.json @@ -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" +} \ No newline at end of file diff --git a/translations/fr.json b/translations/fr.json new file mode 100644 index 0000000..db77c93 --- /dev/null +++ b/translations/fr.json @@ -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" +} \ No newline at end of file diff --git a/translations/index.ts b/translations/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/translations/it.json b/translations/it.json new file mode 100644 index 0000000..5ef1370 --- /dev/null +++ b/translations/it.json @@ -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" +} \ No newline at end of file diff --git a/translations/ja.json b/translations/ja.json new file mode 100644 index 0000000..831f1fd --- /dev/null +++ b/translations/ja.json @@ -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": "期限切れ" +} \ No newline at end of file diff --git a/translations/pt.json b/translations/pt.json new file mode 100644 index 0000000..f0862da --- /dev/null +++ b/translations/pt.json @@ -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" +} \ No newline at end of file diff --git a/translations/ru.json b/translations/ru.json new file mode 100644 index 0000000..01429fc --- /dev/null +++ b/translations/ru.json @@ -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": "Просрочено" +} \ No newline at end of file diff --git a/translations/zh.json b/translations/zh.json new file mode 100644 index 0000000..f10913a --- /dev/null +++ b/translations/zh.json @@ -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": "逾期" +} \ No newline at end of file