feat: Add multi-language support

- Create Chinese, English, Japanese, Korean string resources
- Add LanguageManager for language management
- Add language selection dropdown in Settings
- Update all layouts to use string resources
- Support app restart prompt for language change
This commit is contained in:
2026-05-24 13:29:16 +08:00
parent 48fbf1564d
commit f5789e0d7d
10 changed files with 426 additions and 35 deletions
@@ -9,5 +9,6 @@ data class AppSettings(
val gpsRate: Int = 5,
val sensorRate: Int = 100,
val defaultMapProvider: String = "Google",
val defaultMapType: MapType = MapType.GOOGLE_NORMAL
val defaultMapType: MapType = MapType.GOOGLE_NORMAL,
val language: String = "zh"
)
@@ -0,0 +1,48 @@
package com.senstools.presentation.ui.settings
import android.content.Context
import android.content.SharedPreferences
import java.util.Locale
class LanguageManager(private val context: Context) {
private val PREFS_NAME = "LanguagePrefs"
private val LANGUAGE_KEY = "selected_language"
fun getCurrentLanguage(): String {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
return prefs.getString(LANGUAGE_KEY, "zh") ?: "zh"
}
fun setLanguage(language: String) {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
prefs.edit().putString(LANGUAGE_KEY, language).apply()
}
fun applyLanguage(language: String) {
val locale = when (language) {
"en" -> Locale.ENGLISH
"ja" -> Locale.JAPANESE
"ko" -> Locale.KOREAN
else -> Locale.CHINESE
}
Locale.setDefault(locale)
val config = context.resources.configuration
config.setLocale(locale)
context.resources.updateConfiguration(config, context.resources.displayMetrics)
}
fun getLanguageDisplayName(language: String, context: Context): String {
return when (language) {
"zh" -> context.getString(R.string.language_chinese)
"en" -> context.getString(R.string.language_english)
"ja" -> context.getString(R.string.language_japanese)
"ko" -> context.getString(R.string.language_korean)
else -> language
}
}
companion object {
val SUPPORTED_LANGUAGES = listOf("zh", "en", "ja", "ko")
}
}
@@ -1,5 +1,6 @@
package com.senstools.presentation.ui.settings
import android.app.AlertDialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -17,9 +18,11 @@ class SettingsFragment : Fragment() {
private val binding get() = _binding!!
private val viewModel: SettingsViewModel by viewModels()
private lateinit var languageManager: LanguageManager
private val mapProviders = listOf("Google", "°Ù¶È", "¸ßµÂ")
private val allMapTypes = MapType.values().map { it.displayName }
private val languages = listOf("zh", "en", "ja", "ko")
override fun onCreateView(
inflater: LayoutInflater,
@@ -27,6 +30,7 @@ class SettingsFragment : Fragment() {
savedInstanceState: Bundle?
): View {
_binding = FragmentSettingsBinding.inflate(inflater, container, false)
languageManager = LanguageManager(requireContext())
return binding.root
}
@@ -51,6 +55,17 @@ class SettingsFragment : Fragment() {
allMapTypes
)
binding.tvDefaultMapType.setAdapter(typeAdapter)
val languageAdapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
getLanguageDisplayNames()
)
binding.tvLanguage.setAdapter(languageAdapter)
}
private fun getLanguageDisplayNames(): List<String> {
return languages.map { languageManager.getLanguageDisplayName(it, requireContext()) }
}
private fun setupObservers() {
@@ -63,6 +78,10 @@ class SettingsFragment : Fragment() {
sliderSensorRate.value = settings.sensorRate.toFloat()
tvDefaultMapProvider.setText(settings.defaultMapProvider, false)
tvDefaultMapType.setText(settings.defaultMapType.displayName, false)
tvLanguage.setText(
languageManager.getLanguageDisplayName(settings.language, requireContext()),
false
)
}
}
}
@@ -91,9 +110,25 @@ class SettingsFragment : Fragment() {
val selectedType = MapType.values().find { it.displayName == allMapTypes[position] }
selectedType?.let { viewModel.setDefaultMapType(it) }
}
tvLanguage.setOnItemClickListener { _, _, position, _ ->
val selectedLang = languages[position]
viewModel.setLanguage(selectedLang)
languageManager.setLanguage(selectedLang)
showLanguageRestartDialog()
}
}
}
private fun showLanguageRestartDialog() {
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.language))
.setMessage("Language change requires app restart to take effect.")
.setPositiveButton("OK") { dialog, _ ->
dialog.dismiss()
}
.show()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
@@ -46,4 +46,8 @@ class SettingsViewModel : ViewModel() {
fun setDefaultMapType(mapType: MapType) {
_settings.value = _settings.value?.copy(defaultMapType = mapType)
}
fun setLanguage(language: String) {
_settings.value = _settings.value?.copy(language = language)
}
}
@@ -28,7 +28,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Flight Dashboard"
android:text="@string/flight_dashboard"
android:textSize="18sp"
android:textStyle="bold" />
@@ -50,7 +50,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Pitch: 0.00¡ã" />
android:text="Pitch: 0.00" />
<TextView
android:id="@+id/tvRoll"
@@ -58,7 +58,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Roll: 0.00¡ã" />
android:text="Roll: 0.00" />
<TextView
android:id="@+id/tvYaw"
@@ -66,7 +66,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="Yaw: 0.00¡ã" />
android:text="Yaw: 0.00" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
@@ -88,7 +88,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GPS Data"
android:text="@string/gps_data"
android:textSize="18sp"
android:textStyle="bold" />
@@ -98,22 +98,22 @@
android:layout_marginTop="8dp"
android:columnCount="2">
<TextView android:text="Longitude:" />
<TextView android:text="@string/longitude" />
<TextView android:id="@+id/tvLongitude" android:text="0.00000000" />
<TextView android:text="Latitude:" />
<TextView android:text="@string/latitude" />
<TextView android:id="@+id/tvLatitude" android:text="0.00000000" />
<TextView android:text="Altitude:" />
<TextView android:text="@string/altitude" />
<TextView android:id="@+id/tvAltitude" android:text="0.00 m" />
<TextView android:text="Speed:" />
<TextView android:text="@string/speed" />
<TextView android:id="@+id/tvSpeed" android:text="0.00 m/s" />
<TextView android:text="Bearing:" />
<TextView android:id="@+id/tvBearing" android:text="0.00¡ã" />
<TextView android:text="@string/bearing" />
<TextView android:id="@+id/tvBearing" android:text="0.00" />
<TextView android:text="Satellites:" />
<TextView android:text="@string/satellites" />
<TextView android:id="@+id/tvSatellites" android:text="0" />
</GridLayout>
</LinearLayout>
@@ -136,7 +136,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sensor Data"
android:text="@string/sensor_data"
android:textSize="18sp"
android:textStyle="bold" />
@@ -144,7 +144,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Accelerometer (m/s?)" />
android:text="@string/acceleration" />
<LinearLayout
android:layout_width="match_parent"
@@ -177,7 +177,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Gyroscope (¡ã/s)" />
android:text="@string/gyroscope" />
<LinearLayout
android:layout_width="match_parent"
@@ -210,7 +210,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Magnetometer (¦ÌT)" />
android:text="@string/magnetometer" />
<LinearLayout
android:layout_width="match_parent"
@@ -243,7 +243,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Environment" />
android:text="@string/environment" />
<LinearLayout
android:layout_width="match_parent"
@@ -262,7 +262,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Temp: 0.00¡ãC" />
android:text="Temp: 0.00C" />
<TextView
android:id="@+id/tvHumidity"
@@ -286,7 +286,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:text="Start Recording" />
android:text="@string/start_recording" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnStopRecording"
@@ -295,7 +295,7 @@
android:layout_marginStart="8dp"
android:layout_weight="1"
android:enabled="false"
android:text="Stop Recording" />
android:text="@string/stop_recording" />
</LinearLayout>
</LinearLayout>
@@ -28,7 +28,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Data Collection"
android:text="@string/data_collection"
android:textSize="18sp"
android:textStyle="bold" />
@@ -43,7 +43,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="GPS Data" />
android:text="@string/gps_data_label" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchGps"
@@ -55,7 +55,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="GPS Sampling Rate (Hz)" />
android:text="@string/gps_sampling_rate" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderGpsRate"
@@ -76,7 +76,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Sensor Data" />
android:text="@string/sensor_data_label" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchSensor"
@@ -88,7 +88,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Sensor Sampling Rate (Hz)" />
android:text="@string/sensor_sampling_rate" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderSensorRate"
@@ -117,7 +117,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Display"
android:text="@string/display"
android:textSize="18sp"
android:textStyle="bold" />
@@ -132,7 +132,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Map Display" />
android:text="@string/map_display" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchMap"
@@ -163,7 +163,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Default Map Type" />
android:text="@string/default_map_type" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
@@ -175,7 +175,26 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:hint="Select Map Type" />
android:hint="@string/select_map_type" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/language" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp">
<com.google.android.material.textfield.AutoCompleteTextView
android:id="@+id/tvLanguage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:hint="@string/select_language" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
@@ -196,7 +215,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="About"
android:text="@string/about"
android:textSize="18sp"
android:textStyle="bold" />
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">sensTools</string>
<string name="nav_dashboard">Dashboard</string>
<string name="nav_data">Data</string>
<string name="nav_map">Map</string>
<string name="nav_settings">Settings</string>
<!-- Dashboard -->
<string name="flight_dashboard">Flight Dashboard</string>
<string name="gps_data">GPS Data</string>
<string name="sensor_data">Sensor Data</string>
<string name="acceleration">Accelerometer (m/s?)</string>
<string name="gyroscope">Gyroscope (¡ã/s)</string>
<string name="magnetometer">Magnetometer (¦ÌT)</string>
<string name="environment">Environment</string>
<string name="start_recording">Start Recording</string>
<string name="stop_recording">Stop Recording</string>
<!-- Labels -->
<string name="longitude">Longitude:</string>
<string name="latitude">Latitude:</string>
<string name="altitude">Altitude:</string>
<string name="speed">Speed:</string>
<string name="bearing">Bearing:</string>
<string name="satellites">Satellites:</string>
<string name="pitch">Pitch:</string>
<string name="roll">Roll:</string>
<string name="yaw">Yaw:</string>
<string name="pressure">Pressure:</string>
<string name="temperature">Temperature:</string>
<string name="humidity">Humidity:</string>
<!-- Settings -->
<string name="data_collection">Data Collection</string>
<string name="display">Display</string>
<string name="about">About</string>
<string name="gps_data_label">GPS Data</string>
<string name="sensor_data_label">Sensor Data</string>
<string name="map_display">Map Display</string>
<string name="gps_sampling_rate">GPS Sampling Rate (Hz)</string>
<string name="sensor_sampling_rate">Sensor Sampling Rate (Hz)</string>
<string name="default_map_provider">Default Map Provider</string>
<string name="default_map_type">Default Map Type</string>
<string name="language">Language</string>
<string name="select_provider">Select Provider</string>
<string name="select_map_type">Select Map Type</string>
<string name="select_language">Select Language</string>
<!-- Map Types -->
<string name="google_normal">Google Normal</string>
<string name="google_satellite">Google Satellite</string>
<string name="google_terrain">Google Terrain</string>
<string name="google_hybrid">Google Hybrid</string>
<string name="baidu_normal">Baidu Normal</string>
<string name="baidu_satellite">Baidu Satellite</string>
<string name="baidu_hybrid">Baidu Hybrid</string>
<string name="gaode_normal">Gaode Normal</string>
<string name="gaode_satellite">Gaode Satellite</string>
<!-- Languages -->
<string name="language_chinese">ÖÐÎÄ</string>
<string name="language_english">English</string>
<string name="language_japanese">ÈÕ±¾ÕZ</string>
<string name="language_korean">???</string>
<!-- Data Files -->
<string name="file_name">File Name</string>
<string name="file_size">File Size</string>
<string name="file_date">File Date</string>
<string name="export">Export</string>
<string name="delete">Delete</string>
</resources>
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">sensTools</string>
<string name="nav_dashboard">ダッシュボード</string>
<string name="nav_data">データ</string>
<string name="nav_map">地図</string>
<string name="nav_settings">設定</string>
<!-- Dashboard -->
<string name="flight_dashboard">フライトダッシュボード</string>
<string name="gps_data">GPSデータ</string>
<string name="sensor_data">センサーデータ</string>
<string name="acceleration">加速度計 (m/s?)</string>
<string name="gyroscope">ジャイロスコープ (°/s)</string>
<string name="magnetometer">磁力計 (μT)</string>
<string name="environment">環境</string>
<string name="start_recording">記録開始</string>
<string name="stop_recording">記録停止</string>
<!-- Labels -->
<string name="longitude">経度:</string>
<string name="latitude">緯度:</string>
<string name="altitude">高度:</string>
<string name="speed">速度:</string>
<string name="bearing">方位:</string>
<string name="satellites">衛星数:</string>
<string name="pitch">ピッチ:</string>
<string name="roll">ロール:</string>
<string name="yaw">ヨー:</string>
<string name="pressure">気圧:</string>
<string name="temperature">温度:</string>
<string name="humidity">湿度:</string>
<!-- Settings -->
<string name="data_collection">データ収集</string>
<string name="display">表示</string>
<string name="about">情報</string>
<string name="gps_data_label">GPSデータ</string>
<string name="sensor_data_label">センサーデータ</string>
<string name="map_display">地図表示</string>
<string name="gps_sampling_rate">GPSサンプリングレート (Hz)</string>
<string name="sensor_sampling_rate">センサーサンプリングレート (Hz)</string>
<string name="default_map_provider">デフォルト地図プロバイダー</string>
<string name="default_map_type">デフォルト地図タイプ</string>
<string name="language">言語</string>
<string name="select_provider">プロバイダーを選択</string>
<string name="select_map_type">地図タイプを選択</string>
<string name="select_language">言語を選択</string>
<!-- Map Types -->
<string name="google_normal">Google 標準</string>
<string name="google_satellite">Google 衛星</string>
<string name="google_terrain">Google 地形</string>
<string name="google_hybrid">Google ハイブリッド</string>
<string name="baidu_normal">百度 標準</string>
<string name="baidu_satellite">百度 衛星</string>
<string name="baidu_hybrid">百度 ハイブリッド</string>
<string name="gaode_normal">高德 標準</string>
<string name="gaode_satellite">高德 衛星</string>
<!-- Languages -->
<string name="language_chinese">中国語</string>
<string name="language_english">英語</string>
<string name="language_japanese">日本語</string>
<string name="language_korean">韓国語</string>
<!-- Data Files -->
<string name="file_name">ファイル名</string>
<string name="file_size">ファイルサイズ</string>
<string name="file_date">ファイル日付</string>
<string name="export">エクスポート</string>
<string name="delete">削除</string>
</resources>
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">sensTools</string>
<string name="nav_dashboard">????</string>
<string name="nav_data">???</string>
<string name="nav_map">??</string>
<string name="nav_settings">??</string>
<!-- Dashboard -->
<string name="flight_dashboard">?? ????</string>
<string name="gps_data">GPS ???</string>
<string name="sensor_data">?? ???</string>
<string name="acceleration">???? (m/s?)</string>
<string name="gyroscope">?????? (¡ã/s)</string>
<string name="magnetometer">??? (¦ÌT)</string>
<string name="environment">??</string>
<string name="start_recording">?? ??</string>
<string name="stop_recording">?? ??</string>
<!-- Labels -->
<string name="longitude">??:</string>
<string name="latitude">??:</string>
<string name="altitude">??:</string>
<string name="speed">??:</string>
<string name="bearing">??:</string>
<string name="satellites">?? ?:</string>
<string name="pitch">??:</string>
<string name="roll">?:</string>
<string name="yaw">?:</string>
<string name="pressure">??:</string>
<string name="temperature">??:</string>
<string name="humidity">??:</string>
<!-- Settings -->
<string name="data_collection">??? ??</string>
<string name="display">?????</string>
<string name="about">??</string>
<string name="gps_data_label">GPS ???</string>
<string name="sensor_data_label">?? ???</string>
<string name="map_display">?? ??</string>
<string name="gps_sampling_rate">GPS ??? ?? (Hz)</string>
<string name="sensor_sampling_rate">?? ??? ?? (Hz)</string>
<string name="default_map_provider">?? ?? ???</string>
<string name="default_map_type">?? ?? ??</string>
<string name="language">??</string>
<string name="select_provider">??? ??</string>
<string name="select_map_type">?? ?? ??</string>
<string name="select_language">?? ??</string>
<!-- Map Types -->
<string name="google_normal">Google ??</string>
<string name="google_satellite">Google ??</string>
<string name="google_terrain">Google ??</string>
<string name="google_hybrid">Google ??</string>
<string name="baidu_normal">??? ??</string>
<string name="baidu_satellite">??? ??</string>
<string name="baidu_hybrid">??? ??</string>
<string name="gaode_normal">??? ??</string>
<string name="gaode_satellite">??? ??</string>
<!-- Languages -->
<string name="language_chinese">???</string>
<string name="language_english">??</string>
<string name="language_japanese">???</string>
<string name="language_korean">???</string>
<!-- Data Files -->
<string name="file_name">?? ??</string>
<string name="file_size">?? ??</string>
<string name="file_date">?? ??</string>
<string name="export">????</string>
<string name="delete">??</string>
</resources>
+69 -4
View File
@@ -1,8 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">sensTools</string>
<string name="nav_dashboard">Dashboard</string>
<string name="nav_data">Data</string>
<string name="nav_map">Map</string>
<string name="nav_settings">Settings</string>
<string name="nav_dashboard">仪表盘</string>
<string name="nav_data">数据</string>
<string name="nav_map">地图</string>
<string name="nav_settings">设置</string>
<!-- Dashboard -->
<string name="flight_dashboard">飞行仪表盘</string>
<string name="gps_data">GPS数据</string>
<string name="sensor_data">传感器数据</string>
<string name="acceleration">加速度计 (m/s?)</string>
<string name="gyroscope">陀螺仪 (°/s)</string>
<string name="magnetometer">磁力计 (μT)</string>
<string name="environment">环境</string>
<string name="start_recording">开始记录</string>
<string name="stop_recording">停止记录</string>
<!-- Labels -->
<string name="longitude">经度:</string>
<string name="latitude">纬度:</string>
<string name="altitude">高度:</string>
<string name="speed">速度:</string>
<string name="bearing">方向:</string>
<string name="satellites">卫星数:</string>
<string name="pitch">俯仰:</string>
<string name="roll">横滚:</string>
<string name="yaw">偏航:</string>
<string name="pressure">气压:</string>
<string name="temperature">温度:</string>
<string name="humidity">湿度:</string>
<!-- Settings -->
<string name="data_collection">数据采集</string>
<string name="display">显示</string>
<string name="about">关于</string>
<string name="gps_data_label">GPS数据</string>
<string name="sensor_data_label">传感器数据</string>
<string name="map_display">地图显示</string>
<string name="gps_sampling_rate">GPS采样率 (Hz)</string>
<string name="sensor_sampling_rate">传感器采样率 (Hz)</string>
<string name="default_map_provider">默认地图提供商</string>
<string name="default_map_type">默认地图类型</string>
<string name="language">语言</string>
<string name="select_provider">选择提供商</string>
<string name="select_map_type">选择地图类型</string>
<string name="select_language">选择语言</string>
<!-- Map Types -->
<string name="google_normal">Google 标准</string>
<string name="google_satellite">Google 卫星</string>
<string name="google_terrain">Google 地形</string>
<string name="google_hybrid">Google 混合</string>
<string name="baidu_normal">百度 标准</string>
<string name="baidu_satellite">百度 卫星</string>
<string name="baidu_hybrid">百度 混合</string>
<string name="gaode_normal">高德 标准</string>
<string name="gaode_satellite">高德 卫星</string>
<!-- Languages -->
<string name="language_chinese">中文</string>
<string name="language_english">English</string>
<string name="language_japanese">日本語</string>
<string name="language_korean">???</string>
<!-- Data Files -->
<string name="file_name">文件名</string>
<string name="file_size">文件大小</string>
<string name="file_date">文件日期</string>
<string name="export">导出</string>
<string name="delete">删除</string>
</resources>