feat: Experimenting with desktop UI and manual implementation of i18n

Plan to migrate to an i18n library
Plan to finalize UI design before continuing development
Next step: Implement data retrieval for all platforms
This commit is contained in:
grtsinry43 2025-04-12 20:12:06 +08:00
parent fdd413d7ff
commit 1c61774c04
Signed by: grtsinry43
GPG Key ID: F3305FB3A978C934
21 changed files with 974 additions and 8 deletions

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Activity Analyzer</string>
<string name="home">Home</string>
<string name="analytics">Analytics</string>
<string name="reports">Reports</string>
<string name="settings">Settings</string>
<string name="about">About</string>
<string name="quick_actions">Quick Actions</string>
<string name="new_analysis">New Analysis</string>
<string name="import_data">Import Data</string>
<string name="recent_activities">Recent Activities</string>
<string name="analysis_report">Analysis Report</string>
<string name="data_import">Data Import</string>
<string name="analysis_tools">Analysis Tools</string>
<string name="time_analysis">Time Analysis</string>
<string name="distribution_analysis">Distribution Analysis</string>
<string name="my_reports">My Reports</string>
<string name="monthly_report">Monthly Activity Report</string>
<string name="weekly_report">Weekly Activity Report</string>
<string name="download">Download</string>
<string name="data_storage">Data Storage Location</string>
<string name="default_location">Default Location</string>
<string name="change">Change</string>
<string name="auto_backup">Auto Backup</string>
<string name="daily">Daily</string>
<string name="notifications">Notifications</string>
<string name="enabled">Enabled</string>
<string name="version">Version</string>
<string name="app_description">A powerful activity analysis tool to help you better understand and optimize your activity data.</string>
<string name="check_updates">Check Updates</string>
</resources>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">活动分析器</string>
<string name="home">首页</string>
<string name="analytics">数据分析</string>
<string name="reports">报告</string>
<string name="settings">设置</string>
<string name="about">关于</string>
<string name="quick_actions">快速操作</string>
<string name="new_analysis">新建分析</string>
<string name="import_data">导入数据</string>
<string name="recent_activities">最近活动</string>
<string name="analysis_report">数据分析报告</string>
<string name="data_import">活动数据导入</string>
<string name="analysis_tools">分析工具</string>
<string name="time_analysis">时间分析</string>
<string name="distribution_analysis">分布分析</string>
<string name="my_reports">我的报告</string>
<string name="monthly_report">月度活动报告</string>
<string name="weekly_report">周活动分析</string>
<string name="download">下载</string>
<string name="data_storage">数据存储位置</string>
<string name="default_location">默认位置</string>
<string name="change">更改</string>
<string name="auto_backup">自动备份</string>
<string name="daily">每天</string>
<string name="notifications">通知设置</string>
<string name="enabled">开启</string>
<string name="version">版本</string>
<string name="app_description">一个强大的活动分析工具,帮助您更好地理解和优化您的活动数据。</string>
<string name="check_updates">检查更新</string>
</resources>

View File

@ -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)
)
}
}
}
}

View File

@ -8,6 +8,6 @@ fun main() = application {
onCloseRequest = ::exitApplication,
title = "Activity Analyzer",
) {
App()
DesktopApp()
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -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
}
}
}

View File

@ -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"))
}
}

View File

@ -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
}

View File

@ -0,0 +1,5 @@
package com.grtsinry43.activityanalyzer.i18n
expect object StringResourceFactory {
fun create(): StringResource
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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=检查更新

View File

@ -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])
}
}
}
}

View File

@ -0,0 +1,7 @@
package com.grtsinry43.activityanalyzer.i18n
actual object StringResourceFactory {
actual fun create(): StringResource {
return StringResource()
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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=检查更新