nordiccalendar

A simple Material 3 native calendar app fitting my personal requirements
git clone git://archive.git.mtrnord.blog/MTRNord/nordiccalendar.git
Log | Files | Refs | README | LICENSE

commit 178b003ba0e0edabeb73548c4cacd98e9c7715bf
parent 367f9f8f612b67244f83979d1af998a5d1b4cf2c
Author: MTRNord <MTRNord@users.noreply.github.com>
Date:   Mon, 28 Jul 2025 10:30:00 +0200

Add translation support

Diffstat:
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt | 26++++++++++++++++++--------
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/components/AppScaffold.kt | 13+++++++++----
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/components/SidebarDrawer.kt | 13+++++++++----
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/onboarding/Onboarding.kt | 23++++++++++++-----------
Mapp/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt | 20++++++++++++--------
Aapp/src/main/res/values-en/strings.xml | 30++++++++++++++++++++++++++++++
Mapp/src/main/res/values/strings.xml | 32+++++++++++++++++++++++++++++---
7 files changed, 119 insertions(+), 38 deletions(-)

diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/MainActivity.kt @@ -39,6 +39,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -116,7 +117,7 @@ class MainActivity : ComponentActivity() { didSetTab.value = true } AppScaffold( - title = "NordicCalendar", + title = stringResource(R.string.app_name), calendars = calendars, selectedCalendars = selectedCalendars, selectedDestination = "calendar", @@ -126,7 +127,10 @@ class MainActivity : ComponentActivity() { onCalendarClick = { navController.navigate(Destinations.Calendar.route + "&tab=$selectedTab") }, floatingActionButton = { FloatingActionButton(onClick = { /* Event hinzufügen (später) */ }) { - Icon(Icons.Default.Add, contentDescription = "Event hinzufügen") + Icon( + Icons.Default.Add, + contentDescription = stringResource(R.string.event_add) + ) } } ) { innerPadding -> @@ -153,7 +157,7 @@ class MainActivity : ComponentActivity() { composable(Destinations.EventDetails.route) { backStackEntry -> val eventId = backStackEntry.arguments?.getString("eventId") AppScaffold( - title = "Event Details", + title = stringResource(R.string.event_details_title), calendars = emptyList(), selectedCalendars = emptyList(), selectedDestination = "eventDetails", @@ -174,7 +178,7 @@ class MainActivity : ComponentActivity() { verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - Text("Details für Event ID: $eventId") + Text(stringResource(R.string.event_details, eventId ?: "")) Spacer(modifier = Modifier.height(24.dp)) // Weitere Event-Details hier } @@ -185,7 +189,7 @@ class MainActivity : ComponentActivity() { val calendars by calendarViewModel.calendars.collectAsState() val selectedCalendars by calendarViewModel.selectedCalendars.collectAsState() AppScaffold( - title = "Einstellungen", + title = stringResource(R.string.settings), calendars = calendars, selectedCalendars = selectedCalendars, selectedDestination = "settings", @@ -198,7 +202,10 @@ class MainActivity : ComponentActivity() { modifier = innerPadding .fillMaxSize() ) { - Text("Einstellungen", modifier = Modifier.padding(16.dp)) + Text( + stringResource(R.string.settings), + modifier = Modifier.padding(16.dp) + ) // Weitere Einstellungen hier } } @@ -208,7 +215,7 @@ class MainActivity : ComponentActivity() { val calendars by calendarViewModel.calendars.collectAsState() val selectedCalendars by calendarViewModel.selectedCalendars.collectAsState() AppScaffold( - title = "Über NordicCalendar", + title = stringResource(R.string.about) + " Nordic Calendar", calendars = calendars, selectedCalendars = selectedCalendars, selectedDestination = "about", @@ -221,7 +228,10 @@ class MainActivity : ComponentActivity() { modifier = innerPadding .fillMaxSize() ) { - Text("Über NordicCalendar", modifier = Modifier.padding(16.dp)) + Text( + stringResource(R.string.about) + " Nordic Calendar", + modifier = Modifier.padding(16.dp) + ) // Weitere Infos hier } } diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/components/AppScaffold.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/components/AppScaffold.kt @@ -16,13 +16,15 @@ import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import kotlinx.coroutines.launch +import space.midnightthoughts.nordiccalendar.R import space.midnightthoughts.nordiccalendar.util.Calendar @OptIn(ExperimentalMaterial3Api::class) @Composable fun AppScaffold( - title: String, + title: String? = null, calendars: List<Calendar>, selectedCalendars: List<Calendar>, selectedDestination: String, @@ -62,18 +64,21 @@ fun AppScaffold( Scaffold( topBar = { TopAppBar( - title = { Text(title) }, + title = { title ?: Text(stringResource(R.string.app_name)) }, navigationIcon = { if (onBackClick != null) { IconButton(onClick = onBackClick) { Icon( Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Zurück" + contentDescription = stringResource(R.string.back) ) } } else { IconButton(onClick = { scope.launch { drawerState.open() } }) { - Icon(Icons.Default.Menu, contentDescription = "Menü") + Icon( + Icons.Default.Menu, + contentDescription = stringResource(R.string.menu) + ) } } } diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/components/SidebarDrawer.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/components/SidebarDrawer.kt @@ -17,7 +17,9 @@ import androidx.compose.material3.NavigationDrawerItemDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import space.midnightthoughts.nordiccalendar.R import space.midnightthoughts.nordiccalendar.util.Calendar @Composable @@ -37,21 +39,21 @@ fun SidebarDrawer( .padding(top = 32.dp, start = 8.dp, end = 8.dp, bottom = 8.dp) ) { NavigationDrawerItem( - label = { Text("Kalender") }, + label = { Text(stringResource(R.string.calendar)) }, selected = selectedDestination == "calendar", onClick = onCalendarClick, modifier = Modifier.fillMaxWidth(), colors = NavigationDrawerItemDefaults.colors() ) NavigationDrawerItem( - label = { Text("Einstellungen") }, + label = { Text(stringResource(R.string.settings)) }, selected = selectedDestination == "settings", onClick = onSettingsClick, modifier = Modifier.fillMaxWidth(), colors = NavigationDrawerItemDefaults.colors() ) NavigationDrawerItem( - label = { Text("Über") }, + label = { Text(stringResource(R.string.about)) }, selected = selectedDestination == "about", onClick = onAboutClick, modifier = Modifier.fillMaxWidth(), @@ -60,7 +62,10 @@ fun SidebarDrawer( Spacer(Modifier.height(16.dp)) HorizontalDivider(Modifier, DividerDefaults.Thickness, DividerDefaults.color) Spacer(Modifier.height(8.dp)) - Text("Kalenderauswahl", style = MaterialTheme.typography.titleMedium) + Text( + stringResource(R.string.calendar_selection), + style = MaterialTheme.typography.titleMedium + ) Spacer(Modifier.height(8.dp)) calendars.forEach { calendar -> Row(verticalAlignment = androidx.compose.ui.Alignment.CenterVertically) { diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/onboarding/Onboarding.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/onboarding/Onboarding.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.MutableState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -23,11 +24,12 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState +import space.midnightthoughts.nordiccalendar.R data class OnBoardModel( val imageRes: Int? = null, - val title: String, - val description: String, + val titleRes: Int, + val descriptionRes: Int, val permissionRequest: List<String> = emptyList(), val showPermissionRequest: Boolean = false ) @@ -35,14 +37,13 @@ data class OnBoardModel( val onBoardingData = listOf( // Explain the purpose of the app OnBoardModel( - title = "Welcome to Nordic Calendar", - description = "A simple open source calendar app for Android that helps you keep track of Nordic holidays and events." + titleRes = R.string.onboarding_intro_title, + descriptionRes = R.string.onboarding_intro_text ), // Ask for permissions OnBoardModel( - title = "Permissions Required", - description = "To provide you with the best experience, we need access to your calendar permissions. " + - "This allows us to add Nordic holidays and events directly to your calendar.", + titleRes = R.string.onboarding_permissions_title, + descriptionRes = R.string.onboarding_permissions_text, permissionRequest = listOf( android.Manifest.permission.READ_CALENDAR, android.Manifest.permission.WRITE_CALENDAR @@ -80,7 +81,7 @@ fun OnBoardItem(page: OnBoardModel, hasPermissions: MutableState<Boolean>) { ) } Text( - text = page.title, style = TextStyle( + text = stringResource(page.titleRes), style = TextStyle( fontSize = 24.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onBackground, @@ -88,7 +89,7 @@ fun OnBoardItem(page: OnBoardModel, hasPermissions: MutableState<Boolean>) { ) ) Text( - text = page.description, + text = stringResource(page.descriptionRes), modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp), style = TextStyle( fontSize = 14.sp, @@ -101,7 +102,7 @@ fun OnBoardItem(page: OnBoardModel, hasPermissions: MutableState<Boolean>) { // Show permission request if needed if (!permissionsState.allPermissionsGranted && page.showPermissionRequest) { Text( - text = "Please grant the required permissions to continue.", + text = stringResource(R.string.onboarding_permissions_request), style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight.Normal, @@ -114,7 +115,7 @@ fun OnBoardItem(page: OnBoardModel, hasPermissions: MutableState<Boolean>) { permissionsState.launchMultiplePermissionRequest() }) { Text( - text = "Request Permissions", + text = stringResource(R.string.onboarding_permissions_button), style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight.Normal, diff --git a/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt b/app/src/main/java/space/midnightthoughts/nordiccalendar/screens/CalendarScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset @@ -54,6 +55,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import space.midnightthoughts.nordiccalendar.R import space.midnightthoughts.nordiccalendar.util.Event import space.midnightthoughts.nordiccalendar.viewmodels.CalendarViewModel import java.text.SimpleDateFormat @@ -128,7 +130,7 @@ fun CalendarScreen( ) Spacer(Modifier.height(8.dp)) if (events.isEmpty()) { - Text("Keine Events gefunden.", modifier = Modifier.padding(16.dp)) + Text(stringResource(R.string.no_events_found), modifier = Modifier.padding(16.dp)) } else if (selectedTab == 2) { val hourHeightDp = 64.dp val timeColumnWidth = 64.dp @@ -248,7 +250,7 @@ fun CalendarScreen( verticalAlignment = Alignment.CenterVertically ) { Text( - "Jetzt", + stringResource(R.string.now), color = lineColor, style = MaterialTheme.typography.labelSmall, modifier = Modifier.padding(start = 16.dp, end = 24.dp) @@ -285,7 +287,11 @@ fun CalendarScreen( @Composable fun CalendarTabBar(selectedTab: Int, onTabSelected: (Int) -> Unit) { - val tabTitles = listOf("Monat", "Woche", "Tag") + val tabTitles = listOf( + stringResource(R.string.tab_month), + stringResource(R.string.tab_week), + stringResource(R.string.tab_day) + ) TabRow(selectedTabIndex = selectedTab) { tabTitles.forEachIndexed { index, title -> Tab( @@ -337,9 +343,7 @@ fun DateRangeHeader( 2 -> { val cal = Calendar.getInstance() - val today = cal.timeInMillis val startDay = Calendar.getInstance().apply { timeInMillis = startMillis } - val endDay = Calendar.getInstance().apply { timeInMillis = endMillis } cal.get(Calendar.YEAR) == startDay.get(Calendar.YEAR) && cal.get(Calendar.DAY_OF_YEAR) == startDay.get(Calendar.DAY_OF_YEAR) } @@ -356,7 +360,7 @@ fun DateRangeHeader( IconButton(onClick = onPrev) { Icon( Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Vorheriger Zeitraum" + contentDescription = stringResource(R.string.previous_period) ) } Row(verticalAlignment = Alignment.CenterVertically) { @@ -364,14 +368,14 @@ fun DateRangeHeader( if (!isToday) { Spacer(Modifier.width(8.dp)) TextButton(onClick = onToday) { - Text("Heute") + Text(stringResource(R.string.today)) } } } IconButton(onClick = onNext) { Icon( Icons.AutoMirrored.Filled.ArrowForward, - contentDescription = "Nächster Zeitraum" + contentDescription = stringResource(R.string.next_period) ) } } diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">NordicCalendar</string> + <string name="calendar">Calendar</string> + <string name="settings">Settings</string> + <string name="about">About</string> + <string name="calendar_selection">Calendar selection</string> + <string name="no_events_found">No events found.</string> + <string name="event_add">Add event</string> + <string name="event_details">Details for event ID: %1$s</string> + <string name="event_details_title">Event Details</string> + <string name="today">Today</string> + <string name="previous_period">Previous period</string> + <string name="next_period">Next period</string> + <string name="menu">Menu</string> + <string name="back">Back</string> + <string name="onboarding_finish">Finish</string> + <string name="onboarding_next">Next</string> + <string name="onboarding_intro_title">Welcome to NordicCalendar</string> + <string name="onboarding_intro_text">Your calendar for the north.</string> + <string name="onboarding_permissions_title">Permissions required</string> + <string name="onboarding_permissions_text">To use all features, the app needs access to your calendar.</string> + <string name="onboarding_permissions_request">Please allow access to the calendar.</string> + <string name="onboarding_permissions_button">Request permission</string> + <string name="tab_month">Month</string> + <string name="tab_week">Week</string> + <string name="tab_day">Day</string> + <string name="now">Now</string> +</resources> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml @@ -1,3 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name">Nordic Calendar</string> -</resources> -\ No newline at end of file + <string name="app_name">NordicCalendar</string> + <string name="calendar">Kalender</string> + <string name="settings">Einstellungen</string> + <string name="about">Über</string> + <string name="calendar_selection">Kalenderauswahl</string> + <string name="no_events_found">Keine Events gefunden.</string> + <string name="event_add">Event hinzufügen</string> + <string name="event_details">Details für Event ID: %1$s</string> + <string name="event_details_title">Event Details</string> + <string name="today">Heute</string> + <string name="previous_period">Vorheriger Zeitraum</string> + <string name="next_period">Nächster Zeitraum</string> + <string name="menu">Menü</string> + <string name="back">Zurück</string> + <string name="onboarding_finish">Fertig</string> + <string name="onboarding_next">Weiter</string> + <string name="onboarding_intro_title">Willkommen bei NordicCalendar</string> + <string name="onboarding_intro_text">Dein Kalender für den Norden.</string> + <string name="onboarding_permissions_title">Berechtigungen benötigt</string> + <string name="onboarding_permissions_text">Um alle Funktionen zu nutzen, benötigt die App Zugriff auf deinen Kalender.</string> + <string name="onboarding_permissions_request">Bitte erlaube den Zugriff auf den Kalender.</string> + <string name="onboarding_permissions_button">Berechtigung anfragen</string> + <string name="tab_month">Monat</string> + <string name="tab_week">Woche</string> + <string name="tab_day">Tag</string> + <string name="now">Jetzt</string> +</resources> +