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