Add zh-rCN translate

This commit is contained in:
世界 2024-12-14 20:00:39 +08:00
parent 1c494b56a3
commit f8a2783d32
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
15 changed files with 354 additions and 155 deletions

View file

@ -11,11 +11,13 @@ import android.net.wifi.WifiManager
import android.os.PowerManager
import androidx.core.content.getSystemService
import go.Seq
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.bg.AppChangeReceiver
import io.nekohasekai.sfa.bg.UpdateProfileWork
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.util.Locale
import io.nekohasekai.sfa.Application as BoxApplication
class Application : Application() {
@ -29,6 +31,7 @@ class Application : Application() {
super.onCreate()
Seq.setContext(this)
Libbox.setLocale(Locale.getDefault().toLanguageTag().replace("-", "_"))
@Suppress("OPT_IN_USAGE")
GlobalScope.launch(Dispatchers.IO) {

View file

@ -1,11 +1,30 @@
package io.nekohasekai.sfa.constant
import android.content.Context
import io.nekohasekai.sfa.R
enum class EnabledType(val boolValue: Boolean) {
Enabled(true), Disabled(false);
fun getString(context: Context): String {
return when (this) {
Enabled -> context.getString(R.string.enabled)
Disabled -> context.getString(R.string.disabled)
}
}
companion object {
fun from(value: Boolean): EnabledType {
return if (value) Enabled else Disabled
}
fun valueOf(context: Context, value: String): EnabledType {
return when (value) {
context.getString(R.string.enabled) -> Enabled
context.getString(R.string.disabled) -> Disabled
else -> Disabled
}
}
}
}

View file

@ -1,5 +1,7 @@
package io.nekohasekai.sfa.constant
import android.content.Context
import io.nekohasekai.sfa.R
import io.nekohasekai.sfa.database.Settings
enum class PerAppProxyUpdateType {
@ -11,6 +13,14 @@ enum class PerAppProxyUpdateType {
Deselect -> Settings.PER_APP_PROXY_EXCLUDE
}
fun getString(context: Context): String {
return when (this) {
Disabled -> context.getString(R.string.disabled)
Select -> context.getString(R.string.action_select)
Deselect -> context.getString(R.string.action_deselect)
}
}
companion object {
fun valueOf(value: Int): PerAppProxyUpdateType = when (value) {
Settings.PER_APP_PROXY_DISABLED -> Disabled
@ -18,5 +28,14 @@ enum class PerAppProxyUpdateType {
Settings.PER_APP_PROXY_EXCLUDE -> Deselect
else -> throw IllegalArgumentException()
}
fun valueOf(context: Context, value: String): PerAppProxyUpdateType {
return when (value) {
context.getString(R.string.disabled) -> Disabled
context.getString(R.string.action_select) -> Select
context.getString(R.string.action_deselect) -> Deselect
else -> Disabled
}
}
}
}

View file

@ -1,8 +1,11 @@
package io.nekohasekai.sfa.database
import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import androidx.room.TypeConverter
import io.nekohasekai.sfa.R
import io.nekohasekai.sfa.database.TypedProfile.Type.values
import io.nekohasekai.sfa.ktx.marshall
import io.nekohasekai.sfa.ktx.unmarshall
import java.util.Date
@ -12,6 +15,13 @@ class TypedProfile() : Parcelable {
enum class Type {
Local, Remote;
fun getString(context: Context): String {
return when (this) {
Local -> context.getString(R.string.profile_type_local)
Remote -> context.getString(R.string.profile_type_remote)
}
}
companion object {
fun valueOf(value: Int): Type {
for (it in values()) {

View file

@ -69,7 +69,7 @@ class SettingsFragment : Fragment() {
}
binding.checkUpdateEnabled.addTextChangedListener {
lifecycleScope.launch(Dispatchers.IO) {
val newValue = EnabledType.valueOf(it).boolValue
val newValue = EnabledType.valueOf(requireContext(), it).boolValue
Settings.checkUpdateEnabled = newValue
}
}
@ -81,13 +81,13 @@ class SettingsFragment : Fragment() {
}
binding.disableMemoryLimit.addTextChangedListener {
lifecycleScope.launch(Dispatchers.IO) {
val newValue = EnabledType.valueOf(it).boolValue
val newValue = EnabledType.valueOf(requireContext(), it).boolValue
Settings.disableMemoryLimit = !newValue
}
}
binding.dynamicNotificationEnabled.addTextChangedListener {
lifecycleScope.launch(Dispatchers.IO) {
val newValue = EnabledType.valueOf(it).boolValue
val newValue = EnabledType.valueOf(requireContext(), it).boolValue
Settings.dynamicNotification = newValue
}
}
@ -136,12 +136,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 =
EnabledType.from(checkUpdateEnabled).getString(requireContext())
binding.checkUpdateEnabled.setSimpleItems(R.array.enabled)
binding.disableMemoryLimit.text = EnabledType.from(!Settings.disableMemoryLimit).name
binding.disableMemoryLimit.text =
EnabledType.from(!Settings.disableMemoryLimit).getString(requireContext())
binding.disableMemoryLimit.setSimpleItems(R.array.enabled)
binding.backgroundPermissionCard.isGone = removeBackgroundPermissionPage
binding.dynamicNotificationEnabled.text = EnabledType.from(dynamicNotification).name
binding.dynamicNotificationEnabled.text =
EnabledType.from(dynamicNotification).getString(requireContext())
binding.dynamicNotificationEnabled.setSimpleItems(R.array.enabled)
}
}

View file

@ -67,7 +67,7 @@ class EditProfileActivity : AbstractActivity<ActivityEditProfileBinding>() {
}
}
}
binding.type.text = profile.typed.type.name
binding.type.text = profile.typed.type.getString(this@EditProfileActivity)
binding.editButton.setOnClickListener {
startActivity(
Intent(
@ -89,7 +89,8 @@ class EditProfileActivity : AbstractActivity<ActivityEditProfileBinding>() {
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 = EnabledType.from(profile.typed.autoUpdate)
.getString(this@EditProfileActivity)
binding.autoUpdate.setSimpleItems(R.array.enabled)
binding.autoUpdateInterval.isVisible = profile.typed.autoUpdate
binding.autoUpdateInterval.text = profile.typed.autoUpdateInterval.toString()
@ -111,7 +112,7 @@ class EditProfileActivity : AbstractActivity<ActivityEditProfileBinding>() {
}
private fun updateAutoUpdate(newValue: String) {
val boolValue = EnabledType.valueOf(newValue).boolValue
val boolValue = EnabledType.valueOf(this, newValue).boolValue
if (profile.typed.autoUpdate == boolValue) {
return
}

View file

@ -1,9 +1,11 @@
package io.nekohasekai.sfa.ui.profile
import android.content.Context
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 +31,13 @@ import java.io.InputStream
import java.util.Date
class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
enum class FileSource(val formatted: String) {
CreateNew("Create New"),
Import("Import");
enum class FileSource(@StringRes val formattedRes: Int) {
CreateNew(R.string.profile_source_create_new),
Import(R.string.profile_source_import);
fun formatted(context: Context): String {
return context.getString(formattedRes)
}
}
private val importFile =
@ -49,7 +55,7 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
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 = TypedProfile.Type.Remote.getString(this)
binding.remoteURL.editText?.setText(importURL)
binding.localFields.isVisible = false
binding.remoteFields.isVisible = true
@ -60,12 +66,12 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
binding.name.removeErrorIfNotEmpty()
binding.type.addTextChangedListener {
when (it) {
TypedProfile.Type.Local.name -> {
TypedProfile.Type.Local.getString(this) -> {
binding.localFields.isVisible = true
binding.remoteFields.isVisible = false
}
TypedProfile.Type.Remote.name -> {
TypedProfile.Type.Remote.getString(this) -> {
binding.localFields.isVisible = false
binding.remoteFields.isVisible = true
if (binding.autoUpdateInterval.text.toIntOrNull() == null) {
@ -76,12 +82,12 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
}
binding.fileSourceMenu.addTextChangedListener {
when (it) {
FileSource.CreateNew.formatted -> {
FileSource.CreateNew.formatted(this) -> {
binding.importFileButton.isVisible = false
binding.sourceURL.isVisible = false
}
FileSource.Import.formatted -> {
FileSource.Import.formatted(this) -> {
binding.importFileButton.isVisible = true
binding.sourceURL.isVisible = true
}
@ -99,9 +105,9 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
return
}
when (binding.type.text) {
TypedProfile.Type.Local.name -> {
TypedProfile.Type.Local.getString(this) -> {
when (binding.fileSourceMenu.text) {
FileSource.Import.formatted -> {
FileSource.Import.formatted(this) -> {
if (binding.sourceURL.showErrorIfEmpty()) {
return
}
@ -109,7 +115,7 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
}
}
TypedProfile.Type.Remote.name -> {
TypedProfile.Type.Remote.getString(this) -> {
if (binding.remoteURL.showErrorIfEmpty()) {
return
}
@ -138,15 +144,15 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
typedProfile.path = configFile.path
when (binding.type.text) {
TypedProfile.Type.Local.name -> {
TypedProfile.Type.Local.getString(this) -> {
typedProfile.type = TypedProfile.Type.Local
when (binding.fileSourceMenu.text) {
FileSource.CreateNew.formatted -> {
FileSource.CreateNew.formatted(this) -> {
configFile.writeText("{}")
}
FileSource.Import.formatted -> {
FileSource.Import.formatted(this) -> {
val sourceURL = binding.sourceURL.text
val content = if (sourceURL.startsWith("content://")) {
val inputStream =
@ -165,7 +171,7 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
}
}
TypedProfile.Type.Remote.name -> {
TypedProfile.Type.Remote.getString(this) -> {
typedProfile.type = TypedProfile.Type.Remote
val remoteURL = binding.remoteURL.text
val content = HTTPClient().use { it.getString(remoteURL) }
@ -173,7 +179,8 @@ class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
configFile.writeText(content)
typedProfile.remoteURL = remoteURL
typedProfile.lastUpdated = Date()
typedProfile.autoUpdate = EnabledType.valueOf(binding.autoUpdate.text).boolValue
typedProfile.autoUpdate =
EnabledType.valueOf(this, binding.autoUpdate.text).boolValue
binding.autoUpdateInterval.text.toIntOrNull()?.also {
typedProfile.autoUpdateInterval = it
}

View file

@ -304,7 +304,7 @@ class PerAppProxyActivity : AbstractActivity<ActivityPerAppProxyBinding>() {
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.per_app_menu0, menu)
menuInflater.inflate(R.menu.per_app_menu, menu)
if (menu != null) {
val searchView = menu.findItem(R.id.action_search).actionView as SearchView

View file

@ -33,7 +33,8 @@ class ProfileOverrideActivity :
binding.perAppProxyUpdateOnChange.addTextChangedListener {
lifecycleScope.launch(Dispatchers.IO) {
Settings.perAppProxyUpdateOnChange = PerAppProxyUpdateType.valueOf(it).value()
Settings.perAppProxyUpdateOnChange =
PerAppProxyUpdateType.valueOf(this@ProfileOverrideActivity, it).value()
}
}
@ -49,7 +50,8 @@ class ProfileOverrideActivity :
val perAppUpdateOnChange = Settings.perAppProxyUpdateOnChange
withContext(Dispatchers.Main) {
binding.perAppProxyUpdateOnChange.text =
PerAppProxyUpdateType.valueOf(perAppUpdateOnChange).name
PerAppProxyUpdateType.valueOf(perAppUpdateOnChange)
.getString(this@ProfileOverrideActivity)
binding.perAppProxyUpdateOnChange.setSimpleItems(R.array.per_app_proxy_update_on_change_value)
}
}

View file

@ -4,6 +4,7 @@ import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.BuildConfig
import io.nekohasekai.sfa.ktx.unwrap
import java.io.Closeable
import java.util.Locale
class HTTPClient : Closeable {
@ -15,6 +16,8 @@ class HTTPClient : Closeable {
userAgent += BuildConfig.VERSION_CODE
userAgent += "; sing-box "
userAgent += Libbox.version()
userAgent += "; language "
userAgent += Locale.getDefault().toLanguageTag().replace("-", "_")
userAgent += ")"
userAgent
}

View file

@ -32,18 +32,18 @@
android:id="@+id/statusContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
@ -61,6 +61,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/status_status"
android:textAppearance="?attr/textAppearanceTitleSmall">
@ -150,7 +151,9 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/status_connections"
android:textAppearance="?attr/textAppearanceTitleSmall">
@ -251,6 +254,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/status_traffic"
android:textAppearance="?attr/textAppearanceTitleSmall">
@ -341,6 +345,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/status_traffic_total"
android:textAppearance="?attr/textAppearanceTitleSmall">

View file

@ -10,20 +10,90 @@
app:iconTint="?colorControlNormal"
app:showAsAction="collapseActionView|always" />
<item
android:id="@+id/action_hide_system"
android:title="@string/menu_hide_system" />
<item android:title="@string/per_app_proxy_mode">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/action_mode_include"
android:title="@string/per_app_proxy_mode_include" />
<item
android:id="@+id/action_mode_exclude"
android:title="@string/per_app_proxy_mode_exclude" />
</group>
</menu>
</item>
<item
android:id="@+id/action_scan_china_apps"
android:title="@string/menu_scan_china_apps" />
<item android:title="@string/per_app_proxy_sort_mode">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/action_sort_by_name"
android:title="@string/per_app_proxy_sort_mode_name" />
<item
android:id="@+id/action_sort_by_package_name"
android:title="@string/per_app_proxy_sort_mode_package_name" />
<item
android:id="@+id/action_sort_by_uid"
android:title="@string/per_app_proxy_sort_mode_uid" />
<item
android:id="@+id/action_sort_by_install_time"
android:title="@string/per_app_proxy_sort_mode_install_time" />
<item
android:id="@+id/action_sort_by_update_time"
android:title="@string/per_app_proxy_sort_mode_update_time" />
</group>
<item
android:id="@+id/action_sort_reverse"
android:checkable="true"
android:title="@string/per_app_proxy_sort_mode_reverse" />
</menu>
</item>
<item
android:id="@+id/action_import"
android:title="@string/per_app_proxy_import" />
<item android:title="@string/per_app_proxy_filter">
<menu>
<item
android:id="@+id/action_hide_system_apps"
android:checkable="true"
android:title="@string/per_app_proxy_hide_system_apps" />
<item
android:id="@+id/action_hide_offline_apps"
android:checkable="true"
android:title="@string/per_app_proxy_hide_offline_apps" />
<item
android:id="@+id/action_hide_disabled_apps"
android:checkable="true"
android:title="@string/per_app_proxy_hide_disabled_apps" />
</menu>
</item>
<item
android:id="@+id/action_export"
android:title="@string/per_app_proxy_export" />
<item android:title="@string/action_select">
<menu>
<item
android:id="@+id/action_select_all"
android:title="@string/per_app_proxy_select_all" />
<item
android:id="@+id/action_deselect_all"
android:title="@string/per_app_proxy_select_none" />
</menu>
</item>
<item android:title="@string/per_app_proxy_backup">
<menu>
<item
android:id="@+id/action_import"
android:title="@string/per_app_proxy_import" />
<item
android:id="@+id/action_export"
android:title="@string/per_app_proxy_export" />
</menu>
</item>
<item android:title="@string/per_app_proxy_scan">
<menu>
<item
android:id="@+id/action_scan_china_apps"
android:title="@string/per_app_proxy_scan_china_apps" />
</menu>
</item>
</menu>

View file

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_find_in_page_24"
android:title="@string/search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:iconTint="?colorControlNormal"
app:showAsAction="collapseActionView|always" />
<item android:title="@string/per_app_proxy_mode">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/action_mode_include"
android:title="@string/per_app_proxy_mode_include" />
<item
android:id="@+id/action_mode_exclude"
android:title="@string/per_app_proxy_mode_exclude" />
</group>
</menu>
</item>
<item android:title="@string/per_app_proxy_sort_mode">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/action_sort_by_name"
android:title="@string/per_app_proxy_sort_mode_name" />
<item
android:id="@+id/action_sort_by_package_name"
android:title="@string/per_app_proxy_sort_mode_package_name" />
<item
android:id="@+id/action_sort_by_uid"
android:title="@string/per_app_proxy_sort_mode_uid" />
<item
android:id="@+id/action_sort_by_install_time"
android:title="@string/per_app_proxy_sort_mode_install_time" />
<item
android:id="@+id/action_sort_by_update_time"
android:title="@string/per_app_proxy_sort_mode_update_time" />
</group>
<item
android:id="@+id/action_sort_reverse"
android:checkable="true"
android:title="@string/per_app_proxy_sort_mode_reverse" />
</menu>
</item>
<item android:title="@string/per_app_proxy_filter">
<menu>
<item
android:id="@+id/action_hide_system_apps"
android:checkable="true"
android:title="@string/per_app_proxy_hide_system_apps" />
<item
android:id="@+id/action_hide_offline_apps"
android:checkable="true"
android:title="@string/per_app_proxy_hide_offline_apps" />
<item
android:id="@+id/action_hide_disabled_apps"
android:checkable="true"
android:title="@string/per_app_proxy_hide_disabled_apps" />
</menu>
</item>
<item android:title="@string/action_select">
<menu>
<item
android:id="@+id/action_select_all"
android:title="@string/per_app_proxy_select_all" />
<item
android:id="@+id/action_deselect_all"
android:title="@string/per_app_proxy_select_none" />
</menu>
</item>
<item android:title="@string/per_app_proxy_backup">
<menu>
<item
android:id="@+id/action_import"
android:title="@string/per_app_proxy_import" />
<item
android:id="@+id/action_export"
android:title="@string/per_app_proxy_export" />
</menu>
</item>
<item android:title="@string/per_app_proxy_scan">
<menu>
<item
android:id="@+id/action_scan_china_apps"
android:title="@string/per_app_proxy_scan_china_apps" />
</menu>
</item>
</menu>

View file

@ -1,3 +1,164 @@
<resources>
<string name="stop">停止</string>
<string name="ok"></string>
<string name="no_thanks">不,谢谢</string>
<string name="title_dashboard">仪表</string>
<string name="title_configuration">配置</string>
<string name="title_log">日志</string>
<string name="title_settings">设置</string>
<string name="title_new_profile">新建配置</string>
<string name="title_edit_profile">编辑配置</string>
<string name="title_edit_configuration">编辑配置</string>
<string name="title_overview">概述</string>
<string name="title_groups"></string>
<string name="title_debug">调试</string>
<string name="quick_toggle">切换</string>
<string name="profile_name">名称</string>
<string name="profile_type">类型</string>
<string name="profile_source"></string>
<string name="profile_import_file">导入文件</string>
<string name="profile_create">创建</string>
<string name="profile_edit_content">编辑内容</string>
<string name="profile_check">检查</string>
<string name="profile_share">分享</string>
<string name="profile_share_url">通过二维码分享 URL</string>
<string name="profile_input_required">必须</string>
<string name="profile_empty">无配置</string>
<string name="profile_item_last_updated">最后更新:%s</string>
<string name="profile_last_updated">最后更新</string>
<string name="profile_update">更新</string>
<string name="profile_auto_update">自动更新</string>
<string name="profile_auto_update_interval">自动更新间隔 (分)</string>
<string name="profile_auto_update_interval_minimum_hint">最低值为 15</string>
<string name="profile_type_local">本地</string>
<string name="profile_type_remote">远程</string>
<string name="profile_source_create_new">创建</string>
<string name="profile_source_import">导入</string>
<string name="profile_add_import_file">从文件导入</string>
<string name="profile_add_scan_qr_code">扫描二维码</string>
<string name="profile_add_scan_use_front_camera">前置摄像头</string>
<string name="profile_add_scan_use_vendor_analyzer">使用 MLKit 扫描</string>
<string name="profile_add_scan_enable_torch">灯光</string>
<string name="profile_add_create_manually">手动创建</string>
<string name="menu_undo">撤销</string>
<string name="menu_redo">重做</string>
<string name="menu_format">格式化</string>
<string name="menu_delete">删除</string>
<string name="menu_share">分享</string>
<string name="status_default">服务未启动</string>
<string name="status_starting">服务启动中...</string>
<string name="status_stopping">服务停止中...</string>
<string name="status_started">服务已启动</string>
<string name="enabled">启用</string>
<string name="disabled">禁用</string>
<string name="settings_clear_working_directory">清理工作目录</string>
<string name="error_title">错误</string>
<string name="file_manager_missing">您的设备缺少 Android 标准文件选择器,请安装一个,例如 Material Files。</string>
<string name="loading">加载中...</string>
<string name="service_error_missing_permission">缺少 VPN 权限</string>
<string name="service_error_missing_notification_permission">缺少通知权限</string>
<string name="service_error_empty_configuration">空配置</string>
<string name="service_error_title_start_command_server">启动命令服务器</string>
<string name="service_error_title_create_service">创建服务</string>
<string name="service_error_title_start_service">启动服务</string>
<string name="service_error_title_deprecated_warning">弃用警告</string>
<string name="service_error_deprecated_warning_documentation">文档</string>
<string name="status_status">状态</string>
<string name="status_memory">内存</string>
<string name="status_connections">连接</string>
<string name="status_connections_inbound">入站</string>
<string name="status_connections_outbound">出站</string>
<string name="status_uplink">上传</string>
<string name="status_downlink">下载</string>
<string name="status_traffic">速率</string>
<string name="status_traffic_total">流量</string>
<string name="group_selected_title">选中</string>
<string name="profile">配置</string>
<string name="core_version">版本</string>
<string name="core">核心</string>
<string name="dynamic_notification">在通知中显示实时速度</string>
<string name="core_data_size">数据大小</string>
<string name="check_update_automatic">自动检查更新</string>
<string name="check_update">检查更新</string>
<string name="privacy_policy">隐私政策</string>
<string name="title_app_settings">应用</string>
<string name="memory_limit">内存限制</string>
<string name="background_permission">后台权限</string>
<string name="background_permission_description">申请必要的权限以使 VPN 正常运行。 如果您使用的是中国公司生产的设备,则授予权限后该卡可能不会消失。</string>
<string name="read_more">阅读更多</string>
<string name="request_background_permission">忽略电池优化</string>
<string name="import_remote_profile">导入远程配置</string>
<string name="import_remote_profile_message">您确定要导入远程配置文件 %1$s 吗?您将连接到 %2$s 来下载配置。</string>
<string name="title_profile_override">配置覆盖</string>
<string name="profile_override_description">使用平台特定的值覆盖文件配置项。</string>
<string name="profile_override_configure">配置</string>
<string name="title_per_app_proxy">分应用代理</string>
<string name="per_app_proxy_description">覆盖配置中的 include_package 和 exclude_package。</string>
<string name="per_app_proxy_mode">代理模式</string>
<string name="per_app_proxy_mode_include">白名单</string>
<string name="per_app_proxy_mode_include_description">仅允许选定的应用程序通过 VPN</string>
<string name="per_app_proxy_mode_exclude">黑名单</string>
<string name="per_app_proxy_mode_exclude_description">选定的应用将从 VPN 中排除</string>
<string name="per_app_proxy_action_copy">复制</string>
<string name="per_app_proxy_action_copy_application_label">名称</string>
<string name="per_app_proxy_action_copy_package_name">包名</string>
<string name="per_app_proxy_action_copy_uid">UID</string>
<string name="per_app_proxy_sort_mode">排序</string>
<string name="per_app_proxy_sort_mode_name">名称</string>
<string name="per_app_proxy_sort_mode_package_name">包名</string>
<string name="per_app_proxy_sort_mode_uid">UID</string>
<string name="per_app_proxy_sort_mode_install_time">安装时间</string>
<string name="per_app_proxy_sort_mode_update_time">更新shijian</string>
<string name="per_app_proxy_sort_mode_reverse">反转</string>
<string name="per_app_proxy_filter">过滤</string>
<string name="per_app_proxy_hide_system_apps">隐藏系统应用</string>
<string name="per_app_proxy_hide_offline_apps">隐藏离线应用</string>
<string name="per_app_proxy_hide_disabled_apps">隐藏禁用应用</string>
<string name="per_app_proxy_select">选择</string>
<string name="per_app_proxy_select_all">全选</string>
<string name="per_app_proxy_select_none">全不选</string>
<string name="per_app_proxy_backup">备份</string>
<string name="per_app_proxy_import">从剪切板导入</string>
<string name="per_app_proxy_export">导出到剪切板</string>
<string name="per_app_proxy_scan">扫描</string>
<string name="per_app_proxy_scan_china_apps">中国应用</string>
<string name="content_description_app_icon">App 图标</string>
<string name="toast_clipboard_empty">剪切板为空</string>
<string name="toast_app_list_empty">应用列表为空</string>
<string name="toast_copied_to_clipboard">已导出到剪切板</string>
<string name="toast_imported_from_clipboard">已从剪贴板导入</string>
<string name="message_import_from_clipboard">从剪贴板导入应用列表将覆盖当前列表。您确定要继续吗?</string>
<string name="message_scanning">扫描中...</string>
<string name="message_scan_app_error">扫描应用程序时出错</string>
<string name="message_scan_app_no_apps_found">未找到匹配的应用</string>
<string name="message_scan_app_found">找到以下应用程序,请选择您想要的操作。</string>
<string name="title_scan_result">扫描结果</string>
<string name="action_select">选择</string>
<string name="action_deselect">取消选择</string>
<string name="per_app_proxy_update_on_change">新中国应用安装时更新</string>
<string name="import_profile">导入配置</string>
<string name="import_profile_message">您确定要导入配置文件 %s 吗?</string>
<string name="icloud_profile_unsupported">当前平台不支持 iCloud 配置文件</string>
<string name="search">搜索</string>
<string name="expand">展开</string>
<string name="http_proxy">HTTP 代理</string>
<string name="title_scan_vpn">扫描 VPN 应用</string>
<string name="message_scan_vpn">检查设备上安装的 VPN 及其内容</string>
<string name="action_scan_vpn">扫描</string>
<string name="message_debug_tools">一些调试工具</string>
<string name="action_open">打开</string>
<string name="vpn_app_type">应用类型</string>
<string name="vpn_core_type">核心类型</string>
<string name="vpn_core_path">发现路径</string>
<string name="vpn_golang_version">Go 版本</string>
<string name="vpn_app_type_other">其他</string>
<string name="vpn_core_type_unknown">未知</string>
<string name="no_updates_available">没有可用更新</string>
<string name="sponsor">赞助</string>
<string name="sponsor_message">支持我的工作</string>
<string name="action_start">启动</string>
<string name="location_permission_title">位置权限</string>
<string name="location_permission_description"><![CDATA[您的个人资料包含 <strong><tt>wifi_ssid</tt> 或 <tt>wifi_bssid</tt> 路由规则</strong>。为了使它们正常工作,sing-box 在<strong>后台</strong>使用 <strong>位置</strong> 权限来获取有关所连接 Wi-Fi 网络的信息。该信息将<strong>仅用于路由目的</strong>。]]></string>
<string name="location_permission_background_description"><![CDATA[在 Android 10 及更高版本中,需要<strong>后台位置</strong>权限。选择<strong>始终允许</strong>以授予权限。]]></string>
<string name="open_settings">打开设置</string>
</resources>

View file

@ -1,5 +1,5 @@
<resources>
<string name="app_name">sing-box</string>
<string name="app_name" translatable="false">sing-box</string>
<string name="stop">Stop</string>
<string name="ok">OK</string>
@ -20,7 +20,7 @@
<string name="profile_name">Name</string>
<string name="profile_type">Type</string>
<string name="profile_source">Source</string>
<string name="profile_url">URL</string>
<string name="profile_url" translatable="false">URL</string>
<string name="profile_import_file">Import File</string>
<string name="profile_create">Create</string>
<string name="profile_edit_content">Edit Content</string>
@ -69,8 +69,8 @@
<string name="file_manager_missing">Your device lacks an Android standard file selector, please install one, such as Material Files.</string>
<string name="loading">Loading…</string>
<string name="service_error_missing_permission">Failed to request VPN permission</string>
<string name="service_error_missing_notification_permission">Failed to request notification permission</string>
<string name="service_error_missing_permission">Missiong VPN permission</string>
<string name="service_error_missing_notification_permission">Missing notification permission</string>
<string name="service_error_empty_configuration">Empty configuration</string>
<string name="service_error_title_start_command_server">Start command server</string>
<string name="service_error_title_create_service">Create service</string>
@ -80,7 +80,7 @@
<string name="status_status">Status</string>
<string name="status_memory">Memory</string>
<string name="status_goroutines">Goroutines</string>
<string name="status_goroutines" translatable="false">Goroutines</string>
<string name="status_connections">Connections</string>
<string name="status_connections_inbound">Inbound</string>
<string name="status_connections_outbound">Outbound</string>
@ -124,7 +124,7 @@
<string name="per_app_proxy_action_copy_package_name">Package Name</string>
<string name="per_app_proxy_action_copy_uid">UID</string>
<string name="per_app_proxy_sort_mode">Sort Mode</string>
<string name="per_app_proxy_sort_mode">Sort By</string>
<string name="per_app_proxy_sort_mode_name">By name</string>
<string name="per_app_proxy_sort_mode_package_name">By package name</string>
<string name="per_app_proxy_sort_mode_uid">By UID</string>
@ -149,9 +149,6 @@
<string name="per_app_proxy_scan_china_apps">China apps</string>
<string name="content_description_app_icon">App icon</string>
<string name="menu_hide_system">Hide system apps</string>
<string name="menu_show_system">Show system apps</string>
<string name="menu_scan_china_apps">Scan China apps</string>
<string name="toast_clipboard_empty">Clipboard is empty</string>
<string name="toast_app_list_empty">App list is empty</string>
<string name="toast_copied_to_clipboard">Exported to clipboard</string>
@ -169,7 +166,7 @@
<string name="import_profile_message">Are you sure to import profile %s?</string>
<string name="icloud_profile_unsupported">iCloud profile is not support on current platform</string>
<string name="search">Search</string>
<string name="urltest">URLTest</string>
<string name="urltest" translatable="false">URLTest</string>
<string name="expand">Expand</string>
<string name="http_proxy">HTTP Proxy</string>
<string name="title_scan_vpn">Scan VPN apps</string>
@ -185,9 +182,7 @@
<string name="vpn_core_type_unknown">Unknown</string>
<string name="no_updates_available">No updates available</string>
<string name="sponsor">Sponsor</string>
<string name="sponsor_play">Sponsor via Play Store</string>
<string name="sponsor_message">If I\'ve defended your modern life, please consider sponsoring me.</string>
<string name="other_methods">Other methods</string>
<string name="action_start">Start</string>
<string name="location_permission_title">Location permission</string>
<string name="location_permission_description"><![CDATA[Your profile contains <strong><tt>wifi_ssid</tt> or <tt>wifi_bssid</tt> routing rules</strong>. To make them work, sing-box uses the <strong>location</strong> permission <strong>in the background</strong> to get information about the connected Wi-Fi network. The information will be used <strong>for routing purposes only</strong>.]]></string>