From 4049dd9b0a54eac50ef2e213e9cb6f147de8fd1a Mon Sep 17 00:00:00 2001 From: lgjint <78463565+lgjint@users.noreply.github.com> Date: Sat, 1 Jun 2024 16:15:31 +0800 Subject: [PATCH] Add zh-rCN (Chinese Simplified) --- .../nekohasekai/sfa/constant/EnabledType.kt | 15 +- .../sfa/constant/PerAppProxyUpdateType.kt | 18 +- .../nekohasekai/sfa/database/TypedProfile.kt | 7 +- .../sfa/ui/main/SettingsFragment.kt | 15 +- .../sfa/ui/profile/EditProfileActivity.kt | 7 +- .../sfa/ui/profile/NewProfileActivity.kt | 34 +-- .../sfa/ui/profile/QRScanActivity.kt | 2 +- .../ProfileOverrideActivity.kt | 5 +- app/src/main/res/values-zh-rCN/strings.xml | 196 +++++++++++++++++- 9 files changed, 263 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/io/nekohasekai/sfa/constant/EnabledType.kt b/app/src/main/java/io/nekohasekai/sfa/constant/EnabledType.kt index 750431e7..9f985844 100644 --- a/app/src/main/java/io/nekohasekai/sfa/constant/EnabledType.kt +++ b/app/src/main/java/io/nekohasekai/sfa/constant/EnabledType.kt @@ -1,11 +1,22 @@ package io.nekohasekai.sfa.constant -enum class EnabledType(val boolValue: Boolean) { - Enabled(true), Disabled(false); +import android.content.Context +import androidx.annotation.StringRes +import io.nekohasekai.sfa.R + +enum class EnabledType(val boolValue: Boolean, @StringRes var stringId: Int) { + Enabled(true, R.string.enabled), + Disabled(false, R.string.disabled); companion object { fun from(value: Boolean): EnabledType { return if (value) Enabled else Disabled } + + fun valueOf(value: String, context: Context): EnabledType = when (value) { + context.getString(Enabled.stringId) -> Enabled + context.getString(Disabled.stringId) -> Disabled + else -> throw IllegalArgumentException() + } } } \ No newline at end of file diff --git a/app/src/main/java/io/nekohasekai/sfa/constant/PerAppProxyUpdateType.kt b/app/src/main/java/io/nekohasekai/sfa/constant/PerAppProxyUpdateType.kt index aed1f19d..23afe1cf 100644 --- a/app/src/main/java/io/nekohasekai/sfa/constant/PerAppProxyUpdateType.kt +++ b/app/src/main/java/io/nekohasekai/sfa/constant/PerAppProxyUpdateType.kt @@ -1,9 +1,14 @@ package io.nekohasekai.sfa.constant +import android.content.Context +import androidx.annotation.StringRes +import io.nekohasekai.sfa.R import io.nekohasekai.sfa.database.Settings -enum class PerAppProxyUpdateType { - Disabled, Select, Deselect; +enum class PerAppProxyUpdateType(@StringRes var stringId: Int) { + Disabled(R.string.disabled), + Select(R.string.action_select), + Deselect(R.string.action_deselect); fun value() = when (this) { Disabled -> Settings.PER_APP_PROXY_DISABLED @@ -12,11 +17,18 @@ enum class PerAppProxyUpdateType { } companion object { - fun valueOf(value: Int): PerAppProxyUpdateType = when (value) { + fun from(value: Int): PerAppProxyUpdateType = when (value) { Settings.PER_APP_PROXY_DISABLED -> Disabled Settings.PER_APP_PROXY_INCLUDE -> Select Settings.PER_APP_PROXY_EXCLUDE -> Deselect else -> throw IllegalArgumentException() } + + fun valueOf(value: String, context: Context): PerAppProxyUpdateType = when (value) { + context.getString(Disabled.stringId) -> Disabled + context.getString(Select.stringId) -> Select + context.getString(Deselect.stringId) -> Deselect + else -> throw IllegalArgumentException() + } } } \ No newline at end of file diff --git a/app/src/main/java/io/nekohasekai/sfa/database/TypedProfile.kt b/app/src/main/java/io/nekohasekai/sfa/database/TypedProfile.kt index 84812384..b3ee7a16 100644 --- a/app/src/main/java/io/nekohasekai/sfa/database/TypedProfile.kt +++ b/app/src/main/java/io/nekohasekai/sfa/database/TypedProfile.kt @@ -2,15 +2,18 @@ package io.nekohasekai.sfa.database import android.os.Parcel import android.os.Parcelable +import androidx.annotation.StringRes import androidx.room.TypeConverter +import io.nekohasekai.sfa.R import io.nekohasekai.sfa.ktx.marshall import io.nekohasekai.sfa.ktx.unmarshall import java.util.Date class TypedProfile() : Parcelable { - enum class Type { - Local, Remote; + enum class Type(@StringRes var stringId: Int) { + Local(R.string.profile_type_local), + Remote(R.string.profile_type_remote); companion object { fun valueOf(value: Int): Type { diff --git a/app/src/main/java/io/nekohasekai/sfa/ui/main/SettingsFragment.kt b/app/src/main/java/io/nekohasekai/sfa/ui/main/SettingsFragment.kt index 564d69d0..8ffa21db 100644 --- a/app/src/main/java/io/nekohasekai/sfa/ui/main/SettingsFragment.kt +++ b/app/src/main/java/io/nekohasekai/sfa/ui/main/SettingsFragment.kt @@ -70,7 +70,7 @@ class SettingsFragment : Fragment() { } binding.checkUpdateEnabled.addTextChangedListener { lifecycleScope.launch(Dispatchers.IO) { - val newValue = EnabledType.valueOf(it).boolValue + val newValue = EnabledType.valueOf(it, requireContext()).boolValue Settings.checkUpdateEnabled = newValue } } @@ -82,13 +82,13 @@ class SettingsFragment : Fragment() { } binding.disableMemoryLimit.addTextChangedListener { lifecycleScope.launch(Dispatchers.IO) { - val newValue = EnabledType.valueOf(it).boolValue + val newValue = EnabledType.valueOf(it, requireContext()).boolValue Settings.disableMemoryLimit = !newValue } } binding.dynamicNotificationEnabled.addTextChangedListener { lifecycleScope.launch(Dispatchers.IO) { - val newValue = EnabledType.valueOf(it).boolValue + val newValue = EnabledType.valueOf(it, requireContext()).boolValue Settings.dynamicNotification = newValue } } @@ -137,12 +137,15 @@ class SettingsFragment : Fragment() { val dynamicNotification = Settings.dynamicNotification withContext(Dispatchers.Main) { binding.dataSizeText.text = dataSize - binding.checkUpdateEnabled.text = EnabledType.from(checkUpdateEnabled).name + binding.checkUpdateEnabled.text = + getString(EnabledType.from(checkUpdateEnabled).stringId) binding.checkUpdateEnabled.setSimpleItems(R.array.enabled) - binding.disableMemoryLimit.text = EnabledType.from(!Settings.disableMemoryLimit).name + binding.disableMemoryLimit.text = + getString(EnabledType.from(!Settings.disableMemoryLimit).stringId) binding.disableMemoryLimit.setSimpleItems(R.array.enabled) binding.backgroundPermissionCard.isGone = removeBackgroundPermissionPage - binding.dynamicNotificationEnabled.text = EnabledType.from(dynamicNotification).name + binding.dynamicNotificationEnabled.text = + getString(EnabledType.from(dynamicNotification).stringId) binding.dynamicNotificationEnabled.setSimpleItems(R.array.enabled) } } diff --git a/app/src/main/java/io/nekohasekai/sfa/ui/profile/EditProfileActivity.kt b/app/src/main/java/io/nekohasekai/sfa/ui/profile/EditProfileActivity.kt index 9935a1da..52f5968b 100644 --- a/app/src/main/java/io/nekohasekai/sfa/ui/profile/EditProfileActivity.kt +++ b/app/src/main/java/io/nekohasekai/sfa/ui/profile/EditProfileActivity.kt @@ -66,7 +66,7 @@ class EditProfileActivity : AbstractActivity() { } } } - binding.type.text = profile.typed.type.name + binding.type.text = getString(profile.typed.type.stringId) binding.editButton.setOnClickListener { startActivity( Intent( @@ -88,7 +88,8 @@ class EditProfileActivity : AbstractActivity() { binding.remoteURL.text = profile.typed.remoteURL binding.lastUpdated.text = DateFormat.getDateTimeInstance().format(profile.typed.lastUpdated) - binding.autoUpdate.text = EnabledType.from(profile.typed.autoUpdate).name + binding.autoUpdate.text = + getString(EnabledType.from(profile.typed.autoUpdate).stringId) binding.autoUpdate.setSimpleItems(R.array.enabled) binding.autoUpdateInterval.isVisible = profile.typed.autoUpdate binding.autoUpdateInterval.text = profile.typed.autoUpdateInterval.toString() @@ -110,7 +111,7 @@ class EditProfileActivity : AbstractActivity() { } private fun updateAutoUpdate(newValue: String) { - val boolValue = EnabledType.valueOf(newValue).boolValue + val boolValue = EnabledType.valueOf(newValue, this).boolValue if (profile.typed.autoUpdate == boolValue) { return } diff --git a/app/src/main/java/io/nekohasekai/sfa/ui/profile/NewProfileActivity.kt b/app/src/main/java/io/nekohasekai/sfa/ui/profile/NewProfileActivity.kt index 847924e3..5b129134 100644 --- a/app/src/main/java/io/nekohasekai/sfa/ui/profile/NewProfileActivity.kt +++ b/app/src/main/java/io/nekohasekai/sfa/ui/profile/NewProfileActivity.kt @@ -4,6 +4,7 @@ import android.net.Uri import android.os.Bundle import android.view.View import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.StringRes import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import io.nekohasekai.libbox.Libbox @@ -29,9 +30,9 @@ import java.io.InputStream import java.util.Date class NewProfileActivity : AbstractActivity() { - enum class FileSource(val formatted: String) { - CreateNew("Create New"), - Import("Import"); + enum class FileSource(@StringRes var stringId: Int) { + CreateNew(R.string.profile_source_create_new), + Import(R.string.profile_source_import); } private val importFile = @@ -49,7 +50,7 @@ class NewProfileActivity : AbstractActivity() { intent.getStringExtra("importName")?.also { importName -> intent.getStringExtra("importURL")?.also { importURL -> binding.name.editText?.setText(importName) - binding.type.text = TypedProfile.Type.Remote.name + binding.type.text = getString(TypedProfile.Type.Remote.stringId) binding.remoteURL.editText?.setText(importURL) binding.localFields.isVisible = false binding.remoteFields.isVisible = true @@ -60,12 +61,12 @@ class NewProfileActivity : AbstractActivity() { binding.name.removeErrorIfNotEmpty() binding.type.addTextChangedListener { when (it) { - TypedProfile.Type.Local.name -> { + getString(TypedProfile.Type.Local.stringId) -> { binding.localFields.isVisible = true binding.remoteFields.isVisible = false } - TypedProfile.Type.Remote.name -> { + getString(TypedProfile.Type.Remote.stringId) -> { binding.localFields.isVisible = false binding.remoteFields.isVisible = true if (binding.autoUpdateInterval.text.toIntOrNull() == null) { @@ -76,12 +77,12 @@ class NewProfileActivity : AbstractActivity() { } binding.fileSourceMenu.addTextChangedListener { when (it) { - FileSource.CreateNew.formatted -> { + getString(FileSource.CreateNew.stringId) -> { binding.importFileButton.isVisible = false binding.sourceURL.isVisible = false } - FileSource.Import.formatted -> { + getString(FileSource.Import.stringId) -> { binding.importFileButton.isVisible = true binding.sourceURL.isVisible = true } @@ -99,9 +100,9 @@ class NewProfileActivity : AbstractActivity() { return } when (binding.type.text) { - TypedProfile.Type.Local.name -> { + getString(TypedProfile.Type.Local.stringId) -> { when (binding.fileSourceMenu.text) { - FileSource.Import.formatted -> { + getString(FileSource.Import.stringId) -> { if (binding.sourceURL.showErrorIfEmpty()) { return } @@ -109,7 +110,7 @@ class NewProfileActivity : AbstractActivity() { } } - TypedProfile.Type.Remote.name -> { + getString(TypedProfile.Type.Remote.stringId) -> { if (binding.remoteURL.showErrorIfEmpty()) { return } @@ -138,15 +139,15 @@ class NewProfileActivity : AbstractActivity() { typedProfile.path = configFile.path when (binding.type.text) { - TypedProfile.Type.Local.name -> { + getString(TypedProfile.Type.Local.stringId) -> { typedProfile.type = TypedProfile.Type.Local when (binding.fileSourceMenu.text) { - FileSource.CreateNew.formatted -> { + getString(FileSource.CreateNew.stringId) -> { configFile.writeText("{}") } - FileSource.Import.formatted -> { + getString(FileSource.Import.stringId) -> { val sourceURL = binding.sourceURL.text val content = if (sourceURL.startsWith("content://")) { val inputStream = @@ -165,7 +166,7 @@ class NewProfileActivity : AbstractActivity() { } } - TypedProfile.Type.Remote.name -> { + getString(TypedProfile.Type.Remote.stringId) -> { typedProfile.type = TypedProfile.Type.Remote val remoteURL = binding.remoteURL.text val content = HTTPClient().use { it.getString(remoteURL) } @@ -173,7 +174,8 @@ class NewProfileActivity : AbstractActivity() { configFile.writeText(content) typedProfile.remoteURL = remoteURL typedProfile.lastUpdated = Date() - typedProfile.autoUpdate = EnabledType.valueOf(binding.autoUpdate.text).boolValue + typedProfile.autoUpdate = + EnabledType.valueOf(binding.autoUpdate.text, this).boolValue binding.autoUpdateInterval.text.toIntOrNull()?.also { typedProfile.autoUpdateInterval = it } diff --git a/app/src/main/java/io/nekohasekai/sfa/ui/profile/QRScanActivity.kt b/app/src/main/java/io/nekohasekai/sfa/ui/profile/QRScanActivity.kt index 7d047e0d..781f73fa 100644 --- a/app/src/main/java/io/nekohasekai/sfa/ui/profile/QRScanActivity.kt +++ b/app/src/main/java/io/nekohasekai/sfa/ui/profile/QRScanActivity.kt @@ -62,8 +62,8 @@ class QRScanActivity : AbstractActivity() { } else { setResult(RESULT_CANCELED) finish() + } } - } private lateinit var imageAnalysis: ImageAnalysis private lateinit var imageAnalyzer: ImageAnalysis.Analyzer diff --git a/app/src/main/java/io/nekohasekai/sfa/ui/profileoverride/ProfileOverrideActivity.kt b/app/src/main/java/io/nekohasekai/sfa/ui/profileoverride/ProfileOverrideActivity.kt index 8c8d1c45..cc614642 100644 --- a/app/src/main/java/io/nekohasekai/sfa/ui/profileoverride/ProfileOverrideActivity.kt +++ b/app/src/main/java/io/nekohasekai/sfa/ui/profileoverride/ProfileOverrideActivity.kt @@ -33,7 +33,8 @@ class ProfileOverrideActivity : binding.perAppProxyUpdateOnChange.addTextChangedListener { lifecycleScope.launch(Dispatchers.IO) { - Settings.perAppProxyUpdateOnChange = PerAppProxyUpdateType.valueOf(it).value() + Settings.perAppProxyUpdateOnChange = + PerAppProxyUpdateType.valueOf(it, this@ProfileOverrideActivity).value() } } @@ -49,7 +50,7 @@ class ProfileOverrideActivity : val perAppUpdateOnChange = Settings.perAppProxyUpdateOnChange withContext(Dispatchers.Main) { binding.perAppProxyUpdateOnChange.text = - PerAppProxyUpdateType.valueOf(perAppUpdateOnChange).name + getString(PerAppProxyUpdateType.from(perAppUpdateOnChange).stringId) binding.perAppProxyUpdateOnChange.setSimpleItems(R.array.per_app_proxy_update_on_change_value) } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1098170d..11b5ab23 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1,3 +1,197 @@ - + sing-box + + 停止 + 确定 + 不,谢谢 + + 仪表盘 + 配置文件 + 日志 + 设置 + 新的配置文件 + 编辑配置文件 + 编辑配置 + 概览 + 分组 + 调试 + + 切换 + 名称 + 类型 + 来源 + 订阅链接 + 导入文件 + 创建 + 编辑内容 + 检查 + 分享 + 将订阅链接分享为二维码 + 必填 + 空的配置文件 + 上次更新:%s + 上次更新 + 更新 + 自动更新 + 自动更新间隔(分钟) + 最小值为15 + + 本地 + 远程 + 创建新文件 + 导入 + + 从文件导入 + 扫描二维码 + 前置摄像头 + MLKit分析器 + 手电筒 + + 手动创建 + + 撤销 + 重做 + 格式化 + 删除 + 分享 + + 服务未启动 + 服务启动中… + 服务停止中… + 服务已启动 + + 启用 + 禁用 + + 清除工作目录 + + 错误 + 您的设备缺少Android标准文件选择器,请安装一个,如Material Files。 + 加载中… + + 请求VPN权限失败 + 请求通知权限失败 + 配置为空 + 启动命令服务器 + 创建服务 + 启动服务 + + 状态 + 内存 + 协程 + 连接 + 入站 + 出站 + 上传 + 下载 + 流量 + 总流量 + + 已选 + + 配置文件 + 版本 + 核心 + 在通知中显示实时速度 + 数据大小 + 自动检查更新 + 检查更新 + 隐私政策 + 应用 + 内存限制 + 后台权限 + 为了使VPN正常运行,申请必要的权限。\n\n如果您使用的是中国制造商的设备,在授予权限后,该卡片可能不会消失。 + 查看更多 + 忽略电池优化 + 导入远程配置文件 + 您确定要导入远程配置文件 %1$s 吗?您将连接到 %2$s 下载配置。 + + 配置文件覆盖 + 使用平台特定值覆盖配置文件中的配置项。 + 配置 + + 应用代理 + 覆盖配置中的 include_package 和 exclude_package 。 + 代理模式 + 包括 + 仅允许选择的应用通过VPN + 排除 + 选定的应用程序将被排除在VPN之外 + 复制 + 名称 + 包名 + UID + + 排序方式 + 按名称 + 按包名 + 按UID + 按安装时间 + 按更新时间 + 倒序 + + 过滤器 + 隐藏系统应用 + 隐藏离线应用 + 隐藏已禁用的应用 + + 选择 + 全选 + 取消全选 + + 备份 + 从剪贴板导入 + 导出到剪贴板 + + 扫描 + 中国应用 + + 应用图标 + 隐藏系统应用 + 显示系统应用 + 扫描中国应用 + 剪贴板为空 + 应用列表为空 + 已导出到剪贴板 + 已从剪贴板导入 + 从剪贴板导入应用列表将覆盖您当前的列表。确定要继续吗? + 扫描中… + 扫描应用时出错 + 未找到匹配的应用 + 找到以下应用程序,请选择您想要的操作。 + 扫描结果 + 选择 + 取消选择 + 有新的中国应用安装时更新 + 导入配置文件 + 确定要导入配置文件%s吗? + 当前平台不支持iCloud配置文件 + 搜索 + URL测试 + 展开 + HTTP代理 + 扫描VPN应用 + 检查设备上安装的VPN及其内容 + 扫描 + 一些调试工具 + 打开 + 应用类型 + 核心类型 + 检测到的路径 + Go版本 + 其他 + 未知 + 无可用更新 + 赞助 + 通过Play商店赞助 + 如果我为您的现代生活提供了帮助,请考虑赞助我。 + 其他方法 + 启动 + 位置权限 + wifi_ssidwifi_bssid 路由规则。为了使它们正常工作,sing-box使用后台位置权限来获取关于连接的Wi-Fi网络的信息。该信息仅用于路由目的。]]> + 后台位置权限。选择始终允许以授予该权限。]]> + 打开设置 + + 核心 + \ No newline at end of file