diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index 1e38112..04ef0cc 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -16,12 +16,12 @@ kotlin {
jvmTarget.set(JvmTarget.JVM_11)
}
}
-
+
jvm("desktop")
-
+
sourceSets {
val desktopMain by getting
-
+
androidMain.dependencies {
implementation(compose.preview)
implementation(libs.androidx.activity.compose)
@@ -29,13 +29,14 @@ kotlin {
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
- implementation(compose.material)
+ implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(projects.shared)
+ implementation(compose.materialIconsExtended)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
diff --git a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt
index 0be0fb1..c18ce86 100644
--- a/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt
+++ b/composeApp/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/App.kt
@@ -4,9 +4,7 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material.Button
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Text
+import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
diff --git a/composeApp/src/commonMain/resources/MR/base/strings.xml b/composeApp/src/commonMain/resources/MR/base/strings.xml
new file mode 100644
index 0000000..ba4f33b
--- /dev/null
+++ b/composeApp/src/commonMain/resources/MR/base/strings.xml
@@ -0,0 +1,32 @@
+
+
+ Activity Analyzer
+ Home
+ Analytics
+ Reports
+ Settings
+ About
+ Quick Actions
+ New Analysis
+ Import Data
+ Recent Activities
+ Analysis Report
+ Data Import
+ Analysis Tools
+ Time Analysis
+ Distribution Analysis
+ My Reports
+ Monthly Activity Report
+ Weekly Activity Report
+ Download
+ Data Storage Location
+ Default Location
+ Change
+ Auto Backup
+ Daily
+ Notifications
+ Enabled
+ Version
+ A powerful activity analysis tool to help you better understand and optimize your activity data.
+ Check Updates
+
\ No newline at end of file
diff --git a/composeApp/src/commonMain/resources/MR/zh/strings.xml b/composeApp/src/commonMain/resources/MR/zh/strings.xml
new file mode 100644
index 0000000..cd7cb67
--- /dev/null
+++ b/composeApp/src/commonMain/resources/MR/zh/strings.xml
@@ -0,0 +1,32 @@
+
+
+ 活动分析器
+ 首页
+ 数据分析
+ 报告
+ 设置
+ 关于
+ 快速操作
+ 新建分析
+ 导入数据
+ 最近活动
+ 数据分析报告
+ 活动数据导入
+ 分析工具
+ 时间分析
+ 分布分析
+ 我的报告
+ 月度活动报告
+ 周活动分析
+ 下载
+ 数据存储位置
+ 默认位置
+ 更改
+ 自动备份
+ 每天
+ 通知设置
+ 开启
+ 版本
+ 一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。
+ 检查更新
+
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt
new file mode 100644
index 0000000..f9656e5
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/DesktopApp.kt
@@ -0,0 +1,612 @@
+package com.grtsinry43.activityanalyzer
+
+import androidx.compose.foundation.*
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import org.jetbrains.compose.ui.tooling.preview.Preview
+import androidx.compose.material3.Switch
+import androidx.compose.material3.SwitchDefaults
+import androidx.compose.material3.TextButton
+
+@Composable
+@Preview
+fun DesktopApp() {
+ var selectedItem by remember { mutableStateOf(0) }
+ var isDarkTheme by remember { mutableStateOf(false) }
+
+ val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFF5F5F5)
+ val surfaceColor = if (isDarkTheme) Color(0xFF2D2D2D) else Color.White
+ val textColor = if (isDarkTheme) Color.White else Color.Black
+ val accentColor = Color(0xFF007AFF)
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(backgroundColor)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ // Sidebar
+ Box(
+ modifier = Modifier
+ .width(200.dp)
+ .fillMaxHeight()
+ .background(surfaceColor)
+ .padding(16.dp)
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Text(
+ "Activity Analyzer",
+ fontSize = 16.sp,
+ fontWeight = FontWeight.Bold,
+ color = textColor
+ )
+ Spacer(modifier = Modifier.height(32.dp))
+
+ // Navigation items
+ NavItem(
+ icon = Icons.Default.Home,
+ text = "Home",
+ isSelected = selectedItem == 0,
+ onClick = { selectedItem = 0 },
+ textColor = textColor,
+ accentColor = accentColor
+ )
+ NavItem(
+ icon = Icons.Default.BarChart,
+ text = "Analytics",
+ isSelected = selectedItem == 1,
+ onClick = { selectedItem = 1 },
+ textColor = textColor,
+ accentColor = accentColor
+ )
+ NavItem(
+ icon = Icons.Default.Dataset,
+ text = "Reports",
+ isSelected = selectedItem == 2,
+ onClick = { selectedItem = 2 },
+ textColor = textColor,
+ accentColor = accentColor
+ )
+ NavItem(
+ icon = Icons.Default.Settings,
+ text = "Settings",
+ isSelected = selectedItem == 3,
+ onClick = { selectedItem = 3 },
+ textColor = textColor,
+ accentColor = accentColor
+ )
+
+ // Spacer for alignment
+ Spacer(modifier = Modifier.weight(1f))
+
+ // Theme switch
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(8.dp))
+ .background(if (isDarkTheme) Color(0xFF3D3D3D) else Color(0xFFE8E8E8))
+ .padding(8.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ "Dark Theme",
+ color = textColor
+ )
+ Switch(
+ checked = isDarkTheme,
+ onCheckedChange = { isDarkTheme = it },
+ colors = SwitchDefaults.colors(
+ checkedThumbColor = accentColor,
+ checkedTrackColor = accentColor.copy(alpha = 0.5f)
+ )
+ )
+ }
+ }
+ }
+
+ // Main content
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(24.dp)
+ ) {
+ when (selectedItem) {
+ 0 -> HomeScreen(textColor = textColor, surfaceColor = surfaceColor)
+ 1 -> AnalyticsScreen(textColor = textColor, surfaceColor = surfaceColor)
+ 2 -> ReportsScreen(textColor = textColor, surfaceColor = surfaceColor)
+ 3 -> SettingsScreen(textColor = textColor, surfaceColor = surfaceColor)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun NavItem(
+ icon: ImageVector,
+ text: String,
+ isSelected: Boolean,
+ onClick: () -> Unit,
+ textColor: Color,
+ accentColor: Color
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(8.dp))
+ .background(if (isSelected) accentColor.copy(alpha = 0.1f) else Color.Transparent)
+ .padding(12.dp)
+ .clickable(
+ onClick = onClick,
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = if (isSelected) accentColor else textColor
+ )
+ Text(
+ text = text,
+ color = if (isSelected) accentColor else textColor,
+ fontSize = 16.sp
+ )
+ }
+ }
+}
+
+@Composable
+fun HomeScreen(textColor: Color, surfaceColor: Color) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(24.dp)
+ ) {
+ Text(
+ text = "Welcome Back!",
+ style = MaterialTheme.typography.headlineMedium,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+
+ // Quick actions
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp))
+ .height(150.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surface
+ )
+ ) {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Text(
+ text = "Quick Actions",
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface
+ )
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ ActionButton(
+ icon = Icons.Default.Add,
+ text = "New Activity",
+ onClick = { /* TODO */ }
+ )
+ ActionButton(
+ icon = Icons.Default.Upload,
+ text = "Import Data",
+ onClick = { /* TODO */ }
+ )
+ }
+ }
+ }
+
+ // Recent activities
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp)),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surface
+ )
+ ) {
+ Column(
+ modifier = Modifier.padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Text(
+ text = "Recent Activities",
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface
+ )
+ ActivityItem(
+ title = "Activity Report Generated",
+ time = "2024-04-12 14:30",
+ icon = Icons.Default.Description
+ )
+ ActivityItem(
+ title = "Data Imported",
+ time = "2024-04-12 10:15",
+ icon = Icons.Default.Upload
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun ActionButton(
+ icon: ImageVector,
+ text: String,
+ onClick: () -> Unit
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(12.dp))
+ .background(Color(0xFF007AFF).copy(alpha = 0.1f))
+ .clickable(onClick = onClick)
+ .padding(16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = Color(0xFF007AFF)
+ )
+ Text(
+ text = text,
+ color = Color(0xFF007AFF),
+ fontSize = 14.sp
+ )
+ }
+ }
+}
+
+@Composable
+fun ActivityItem(
+ title: String,
+ time: String,
+ icon: ImageVector
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(8.dp))
+ .background(Color(0xFFF5F5F5))
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = Color(0xFF007AFF)
+ )
+ Column {
+ Text(
+ text = title,
+ fontSize = 16.sp,
+ color = Color.Black
+ )
+ Text(
+ text = time,
+ fontSize = 14.sp,
+ color = Color.Gray
+ )
+ }
+ }
+}
+
+@Composable
+fun AnalyticsScreen(textColor: Color, surfaceColor: Color) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.spacedBy(24.dp)
+ ) {
+ Text(
+ "Analytics",
+ fontSize = 32.sp,
+ fontWeight = FontWeight.Bold,
+ color = textColor
+ )
+
+ // Analysis tools card
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp))
+ .background(surfaceColor)
+ .padding(24.dp)
+ ) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Text(
+ "Analysis Tools",
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ color = textColor
+ )
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ AnalysisToolButton(
+ icon = Icons.Default.Info,
+ text = "Time Analysis",
+ onClick = {/* TODO */ }
+ )
+ AnalysisToolButton(
+ icon = Icons.Default.Info,
+ text = "Distribution Analysis",
+ onClick = {/* TODO */ }
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun AnalysisToolButton(
+ icon: ImageVector,
+ text: String,
+ onClick: () -> Unit
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(12.dp))
+ .background(Color(0xFF007AFF).copy(alpha = 0.1f))
+ .clickable(onClick = onClick)
+ .padding(16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = Color(0xFF007AFF)
+ )
+ Text(
+ text = text,
+ color = Color(0xFF007AFF),
+ fontSize = 14.sp
+ )
+ }
+ }
+}
+
+@Composable
+fun ReportsScreen(textColor: Color, surfaceColor: Color) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.spacedBy(24.dp)
+ ) {
+ Text(
+ "Reports",
+ fontSize = 32.sp,
+ fontWeight = FontWeight.Bold,
+ color = textColor
+ )
+
+ // Report list
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp))
+ .background(surfaceColor)
+ .padding(24.dp)
+ ) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Text(
+ "My Reports",
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ color = textColor
+ )
+ ReportItem(
+ title = "Monthly Activity Report",
+ period = "March 2024",
+ icon = Icons.Default.Info
+ )
+ ReportItem(
+ title = "Weekly Activity Analysis",
+ period = "Week 12, 2024",
+ icon = Icons.Default.Info
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun ReportItem(
+ title: String,
+ period: String,
+ icon: ImageVector
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(8.dp))
+ .background(Color(0xFFF5F5F5))
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = Color(0xFF007AFF)
+ )
+ Column {
+ Text(
+ text = title,
+ fontSize = 16.sp,
+ color = Color.Black
+ )
+ Text(
+ text = period,
+ fontSize = 14.sp,
+ color = Color.Gray
+ )
+ }
+ }
+ IconButton(onClick = { /* TODO */ }) {
+ Icon(
+ imageVector = Icons.Default.Info,
+ contentDescription = "Download",
+ tint = Color(0xFF007AFF)
+ )
+ }
+ }
+}
+
+@Composable
+fun SettingsScreen(textColor: Color, surfaceColor: Color) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.spacedBy(24.dp)
+ ) {
+ Text(
+ "Settings",
+ fontSize = 32.sp,
+ fontWeight = FontWeight.Bold,
+ color = textColor
+ )
+
+ // Settings options
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp))
+ .background(surfaceColor)
+ .padding(24.dp)
+ ) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ SettingItem(
+ title = "Data Storage Location",
+ subtitle = "Default Location",
+ icon = Icons.Default.Info,
+ onClick = {/* TODO */ }
+ )
+ SettingItem(
+ title = "Auto Backup",
+ subtitle = "Daily",
+ icon = Icons.Default.Info,
+ showSwitch = true
+ )
+ SettingItem(
+ title = "Notification Settings",
+ subtitle = "Enabled",
+ icon = Icons.Default.Info,
+ showSwitch = true
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun SettingItem(
+ title: String,
+ subtitle: String,
+ icon: ImageVector,
+ onClick: (() -> Unit)? = null,
+ showSwitch: Boolean = false
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(8.dp))
+ .background(Color(0xFFF5F5F5))
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = Color(0xFF007AFF)
+ )
+ Column {
+ Text(
+ text = title,
+ fontSize = 16.sp,
+ color = Color.Black
+ )
+ Text(
+ text = subtitle,
+ fontSize = 14.sp,
+ color = Color.Gray
+ )
+ }
+ }
+ if (showSwitch) {
+ Switch(
+ checked = true,
+ onCheckedChange = {/* TODO */ },
+ colors = SwitchDefaults.colors(
+ checkedThumbColor = Color(0xFF007AFF),
+ checkedTrackColor = Color(0xFF007AFF).copy(alpha = 0.5f)
+ )
+ )
+ } else if (onClick != null) {
+ TextButton(onClick = onClick) {
+ Text(
+ "Change",
+ color = Color(0xFF007AFF)
+ )
+ }
+ }
+ }
+}
diff --git a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt
index 040d550..bc42181 100644
--- a/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt
+++ b/composeApp/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/main.kt
@@ -8,6 +8,6 @@ fun main() = application {
onCloseRequest = ::exitApplication,
title = "Activity Analyzer",
) {
- App()
+ DesktopApp()
}
}
\ No newline at end of file
diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
new file mode 100644
index 0000000..2ea8f58
--- /dev/null
+++ b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
@@ -0,0 +1,24 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+import android.content.Context
+import android.content.res.Resources
+
+actual class StringResource(private val context: Context) {
+ actual fun getString(key: String): String {
+ val resourceId = context.resources.getIdentifier(key, "string", context.packageName)
+ return if (resourceId != 0) {
+ context.getString(resourceId)
+ } else {
+ key
+ }
+ }
+
+ actual fun getString(key: String, vararg args: Any): String {
+ val resourceId = context.resources.getIdentifier(key, "string", context.packageName)
+ return if (resourceId != 0) {
+ context.getString(resourceId, *args)
+ } else {
+ key
+ }
+ }
+}
\ No newline at end of file
diff --git a/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
new file mode 100644
index 0000000..f54a5d8
--- /dev/null
+++ b/shared/src/androidMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
@@ -0,0 +1,15 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+import android.content.Context
+
+actual object StringResourceFactory {
+ private var context: Context? = null
+
+ fun initialize(context: Context) {
+ this.context = context.applicationContext
+ }
+
+ actual fun create(): StringResource {
+ return StringResource(context ?: throw IllegalStateException("Context not initialized"))
+ }
+}
\ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
new file mode 100644
index 0000000..9bd4d3e
--- /dev/null
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
@@ -0,0 +1,6 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+expect class StringResource {
+ fun getString(key: String): String
+ fun getString(key: String, vararg args: Any): String
+}
\ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
new file mode 100644
index 0000000..694a937
--- /dev/null
+++ b/shared/src/commonMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
@@ -0,0 +1,5 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+expect object StringResourceFactory {
+ fun create(): StringResource
+}
\ No newline at end of file
diff --git a/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
new file mode 100644
index 0000000..74eb1d4
--- /dev/null
+++ b/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
@@ -0,0 +1,42 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+import java.util.Properties
+import java.io.InputStream
+import java.text.MessageFormat
+
+actual class StringResource {
+ private val properties = Properties()
+ private var currentLocale: String = "en"
+
+ init {
+ loadProperties("en")
+ }
+
+ fun setLocale(locale: String) {
+ if (currentLocale != locale) {
+ currentLocale = locale
+ loadProperties(locale)
+ }
+ }
+
+ private fun loadProperties(locale: String) {
+ properties.clear()
+ val baseName = "strings"
+ val resourceName = if (locale == "en") "$baseName.properties" else "${baseName}_$locale.properties"
+
+ val inputStream = javaClass.classLoader.getResourceAsStream(resourceName)
+ if (inputStream != null) {
+ properties.load(inputStream)
+ inputStream.close()
+ }
+ }
+
+ actual fun getString(key: String): String {
+ return properties.getProperty(key, key)
+ }
+
+ actual fun getString(key: String, vararg args: Any): String {
+ val pattern = properties.getProperty(key, key)
+ return MessageFormat.format(pattern, *args)
+ }
+}
\ No newline at end of file
diff --git a/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
new file mode 100644
index 0000000..473f7d4
--- /dev/null
+++ b/shared/src/desktopMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
@@ -0,0 +1,13 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+actual object StringResourceFactory {
+ private val stringResource = StringResource()
+
+ actual fun create(): StringResource {
+ return stringResource
+ }
+
+ fun setLocale(locale: String) {
+ stringResource.setLocale(locale)
+ }
+}
\ No newline at end of file
diff --git a/shared/src/desktopMain/resources/strings.properties b/shared/src/desktopMain/resources/strings.properties
new file mode 100644
index 0000000..5b5542f
--- /dev/null
+++ b/shared/src/desktopMain/resources/strings.properties
@@ -0,0 +1,22 @@
+app.title=Activity Analyzer
+home.welcome=Welcome to Activity Analyzer
+home.quickActions=Quick Actions
+home.newAnalysis=New Analysis
+home.importData=Import Data
+home.recentActivities=Recent Activities
+analytics.title=Data Analysis
+analytics.tools=Analysis Tools
+analytics.timeAnalysis=Time Analysis
+analytics.distributionAnalysis=Distribution Analysis
+reports.title=Reports
+reports.myReports=My Reports
+reports.monthlyReport=Monthly Activity Report
+reports.weeklyReport=Weekly Activity Report
+settings.title=Settings
+settings.dataStorage=Data Storage Location
+settings.autoBackup=Auto Backup
+settings.notifications=Notifications
+about.title=About Activity Analyzer
+about.version=Version {0}
+about.description=A powerful activity analysis tool to help you better understand and optimize your activity data.
+about.checkUpdate=Check for Updates
\ No newline at end of file
diff --git a/shared/src/desktopMain/resources/strings_zh.properties b/shared/src/desktopMain/resources/strings_zh.properties
new file mode 100644
index 0000000..904da82
--- /dev/null
+++ b/shared/src/desktopMain/resources/strings_zh.properties
@@ -0,0 +1,22 @@
+app.title=活动分析器
+home.welcome=欢迎使用活动分析器
+home.quickActions=快速操作
+home.newAnalysis=新建分析
+home.importData=导入数据
+home.recentActivities=最近活动
+analytics.title=数据分析
+analytics.tools=分析工具
+analytics.timeAnalysis=时间分析
+analytics.distributionAnalysis=分布分析
+reports.title=报告
+reports.myReports=我的报告
+reports.monthlyReport=月度活动报告
+reports.weeklyReport=周活动分析
+settings.title=设置
+settings.dataStorage=数据存储位置
+settings.autoBackup=自动备份
+settings.notifications=通知设置
+about.title=关于活动分析器
+about.version=版本 {0}
+about.description=一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。
+about.checkUpdate=检查更新
\ No newline at end of file
diff --git a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
new file mode 100644
index 0000000..c40baf6
--- /dev/null
+++ b/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
@@ -0,0 +1,29 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+import platform.Foundation.NSBundle
+import platform.Foundation.NSString
+import platform.Foundation.stringWithFormat
+
+actual class StringResource {
+ private val bundle = NSBundle.mainBundle
+
+ actual fun getString(key: String): String {
+ return bundle.localizedStringForKey(key, "", null) ?: key
+ }
+
+ actual fun getString(key: String, vararg args: Any): String {
+ val format = bundle.localizedStringForKey(key, "", null) ?: key
+ return when (args.size) {
+ 0 -> format
+ 1 -> NSString.stringWithFormat(format, args[0])
+ 2 -> NSString.stringWithFormat(format, args[0], args[1])
+ 3 -> NSString.stringWithFormat(format, args[0], args[1], args[2])
+ 4 -> NSString.stringWithFormat(format, args[0], args[1], args[2], args[3])
+ else -> {
+ // 处理更多参数情况
+ // 简单方案:只使用前4个参数
+ NSString.stringWithFormat(format, args[0], args[1], args[2], args[3])
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
new file mode 100644
index 0000000..ff355e5
--- /dev/null
+++ b/shared/src/iosMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
@@ -0,0 +1,7 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+actual object StringResourceFactory {
+ actual fun create(): StringResource {
+ return StringResource()
+ }
+}
\ No newline at end of file
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
new file mode 100644
index 0000000..74eb1d4
--- /dev/null
+++ b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResource.kt
@@ -0,0 +1,42 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+import java.util.Properties
+import java.io.InputStream
+import java.text.MessageFormat
+
+actual class StringResource {
+ private val properties = Properties()
+ private var currentLocale: String = "en"
+
+ init {
+ loadProperties("en")
+ }
+
+ fun setLocale(locale: String) {
+ if (currentLocale != locale) {
+ currentLocale = locale
+ loadProperties(locale)
+ }
+ }
+
+ private fun loadProperties(locale: String) {
+ properties.clear()
+ val baseName = "strings"
+ val resourceName = if (locale == "en") "$baseName.properties" else "${baseName}_$locale.properties"
+
+ val inputStream = javaClass.classLoader.getResourceAsStream(resourceName)
+ if (inputStream != null) {
+ properties.load(inputStream)
+ inputStream.close()
+ }
+ }
+
+ actual fun getString(key: String): String {
+ return properties.getProperty(key, key)
+ }
+
+ actual fun getString(key: String, vararg args: Any): String {
+ val pattern = properties.getProperty(key, key)
+ return MessageFormat.format(pattern, *args)
+ }
+}
\ No newline at end of file
diff --git a/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
new file mode 100644
index 0000000..473f7d4
--- /dev/null
+++ b/shared/src/jvmMain/kotlin/com/grtsinry43/activityanalyzer/i18n/StringResourceFactory.kt
@@ -0,0 +1,13 @@
+package com.grtsinry43.activityanalyzer.i18n
+
+actual object StringResourceFactory {
+ private val stringResource = StringResource()
+
+ actual fun create(): StringResource {
+ return stringResource
+ }
+
+ fun setLocale(locale: String) {
+ stringResource.setLocale(locale)
+ }
+}
\ No newline at end of file
diff --git a/shared/src/jvmMain/resources/strings.properties b/shared/src/jvmMain/resources/strings.properties
new file mode 100644
index 0000000..5b5542f
--- /dev/null
+++ b/shared/src/jvmMain/resources/strings.properties
@@ -0,0 +1,22 @@
+app.title=Activity Analyzer
+home.welcome=Welcome to Activity Analyzer
+home.quickActions=Quick Actions
+home.newAnalysis=New Analysis
+home.importData=Import Data
+home.recentActivities=Recent Activities
+analytics.title=Data Analysis
+analytics.tools=Analysis Tools
+analytics.timeAnalysis=Time Analysis
+analytics.distributionAnalysis=Distribution Analysis
+reports.title=Reports
+reports.myReports=My Reports
+reports.monthlyReport=Monthly Activity Report
+reports.weeklyReport=Weekly Activity Report
+settings.title=Settings
+settings.dataStorage=Data Storage Location
+settings.autoBackup=Auto Backup
+settings.notifications=Notifications
+about.title=About Activity Analyzer
+about.version=Version {0}
+about.description=A powerful activity analysis tool to help you better understand and optimize your activity data.
+about.checkUpdate=Check for Updates
\ No newline at end of file
diff --git a/shared/src/jvmMain/resources/strings_zh.properties b/shared/src/jvmMain/resources/strings_zh.properties
new file mode 100644
index 0000000..904da82
--- /dev/null
+++ b/shared/src/jvmMain/resources/strings_zh.properties
@@ -0,0 +1,22 @@
+app.title=活动分析器
+home.welcome=欢迎使用活动分析器
+home.quickActions=快速操作
+home.newAnalysis=新建分析
+home.importData=导入数据
+home.recentActivities=最近活动
+analytics.title=数据分析
+analytics.tools=分析工具
+analytics.timeAnalysis=时间分析
+analytics.distributionAnalysis=分布分析
+reports.title=报告
+reports.myReports=我的报告
+reports.monthlyReport=月度活动报告
+reports.weeklyReport=周活动分析
+settings.title=设置
+settings.dataStorage=数据存储位置
+settings.autoBackup=自动备份
+settings.notifications=通知设置
+about.title=关于活动分析器
+about.version=版本 {0}
+about.description=一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。
+about.checkUpdate=检查更新
\ No newline at end of file