From 34240a21632c939c6e03b5307b73bd1a2e7d0be9 Mon Sep 17 00:00:00 2001 From: Hadrian Burkhardt Date: Thu, 9 Jul 2026 18:01:50 +0200 Subject: [PATCH] ar, es, fr, it, ja, ko, pt-BR, pt-PT, zh-CN, zh-TW language added --- LOCALIZATION.md | 21 ++ app/build.gradle.kts | 138 +++++++++++++ .../privateqrscanner/MainActivity.kt | 45 ++++- .../privateqrscanner/i18n/AppLocaleCatalog.kt | 138 +++++++++++++ .../settings/SettingsRepository.kt | 10 + .../privateqrscanner/ui/AppViewModel.kt | 20 +- .../ui/CleanScannerAppRoot.kt | 11 +- .../ui/screens/SettingsScreen.kt | 163 ++++++++++++++- app/src/main/res/raw-ar/privacy_policy.txt | 57 ++++++ .../main/res/raw-b+pt+BR/privacy_policy.txt | 57 ++++++ .../main/res/raw-b+pt+PT/privacy_policy.txt | 57 ++++++ .../main/res/raw-b+zh+Hans/privacy_policy.txt | 57 ++++++ .../main/res/raw-b+zh+Hant/privacy_policy.txt | 57 ++++++ app/src/main/res/raw-es/privacy_policy.txt | 57 ++++++ app/src/main/res/raw-fr/privacy_policy.txt | 57 ++++++ app/src/main/res/raw-it/privacy_policy.txt | 57 ++++++ app/src/main/res/raw-ja/privacy_policy.txt | 57 ++++++ app/src/main/res/raw-ko/privacy_policy.txt | 57 ++++++ app/src/main/res/raw/supported_locales.tsv | 55 +++++ app/src/main/res/values-ar/strings.xml | 103 ++++++++++ app/src/main/res/values-b+pt+BR/strings.xml | 103 ++++++++++ app/src/main/res/values-b+pt+PT/strings.xml | 103 ++++++++++ app/src/main/res/values-b+zh+Hans/strings.xml | 103 ++++++++++ app/src/main/res/values-b+zh+Hant/strings.xml | 103 ++++++++++ app/src/main/res/values-de/strings.xml | 4 + app/src/main/res/values-es/strings.xml | 103 ++++++++++ app/src/main/res/values-fr/strings.xml | 103 ++++++++++ app/src/main/res/values-it/strings.xml | 103 ++++++++++ app/src/main/res/values-ja/strings.xml | 103 ++++++++++ app/src/main/res/values-ko/strings.xml | 103 ++++++++++ app/src/main/res/values/strings.xml | 4 + app/src/main/res/xml/locales_config.xml | 5 - .../i18n/AppLocaleCatalogTest.kt | 47 +++++ target_countries.txt | 188 ++++++++++++++++++ 34 files changed, 2433 insertions(+), 16 deletions(-) create mode 100644 LOCALIZATION.md create mode 100644 app/src/main/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalog.kt create mode 100644 app/src/main/res/raw-ar/privacy_policy.txt create mode 100644 app/src/main/res/raw-b+pt+BR/privacy_policy.txt create mode 100644 app/src/main/res/raw-b+pt+PT/privacy_policy.txt create mode 100644 app/src/main/res/raw-b+zh+Hans/privacy_policy.txt create mode 100644 app/src/main/res/raw-b+zh+Hant/privacy_policy.txt create mode 100644 app/src/main/res/raw-es/privacy_policy.txt create mode 100644 app/src/main/res/raw-fr/privacy_policy.txt create mode 100644 app/src/main/res/raw-it/privacy_policy.txt create mode 100644 app/src/main/res/raw-ja/privacy_policy.txt create mode 100644 app/src/main/res/raw-ko/privacy_policy.txt create mode 100644 app/src/main/res/raw/supported_locales.tsv create mode 100644 app/src/main/res/values-ar/strings.xml create mode 100644 app/src/main/res/values-b+pt+BR/strings.xml create mode 100644 app/src/main/res/values-b+pt+PT/strings.xml create mode 100644 app/src/main/res/values-b+zh+Hans/strings.xml create mode 100644 app/src/main/res/values-b+zh+Hant/strings.xml create mode 100644 app/src/main/res/values-es/strings.xml create mode 100644 app/src/main/res/values-fr/strings.xml create mode 100644 app/src/main/res/values-it/strings.xml create mode 100644 app/src/main/res/values-ja/strings.xml create mode 100644 app/src/main/res/values-ko/strings.xml delete mode 100644 app/src/main/res/xml/locales_config.xml create mode 100644 app/src/test/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalogTest.kt create mode 100644 target_countries.txt diff --git a/LOCALIZATION.md b/LOCALIZATION.md new file mode 100644 index 0000000..6230625 --- /dev/null +++ b/LOCALIZATION.md @@ -0,0 +1,21 @@ +# Localization + +The app uses `app/src/main/res/raw/supported_locales.tsv` as the locale source of truth. The catalog is seeded from `target_countries.txt`. + +Locale status controls rollout: + +- `complete`: included in the in-app language picker and generated Android `localeConfig`. +- `partial`: included in both, and shown as partial in the picker. +- `planned`: tracked for target-country coverage, but not exposed to users. + +To add a translated language: + +1. Create the matching `app/src/main/res//strings.xml`. +2. Translate every key from `app/src/main/res/values/strings.xml`. +3. If marking the locale `complete`, also translate `app/src/main/res/raw/privacy_policy.txt` into the matching raw resource folder, such as `raw-es/privacy_policy.txt`. +4. Change the locale row status in `supported_locales.tsv` from `planned` to `complete` or `partial`. +5. Run `bash gradlew verifyLocalization`. + +Mark a locale `complete` when all required UI strings and localized privacy policy copy are present and accepted for release. Native or legal review is recommended for sensitive markets, but the build only enforces resource completeness. + +`generateLocaleConfig` builds `@xml/locales_config` from active rows, so do not hand-edit a static `locales_config.xml`. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ae22540..895ac7e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,4 @@ +import java.io.File import java.util.Properties plugins { @@ -30,6 +31,129 @@ val hasReleaseSigning = listOf( releaseKeyPassword ).all { !it.isNullOrBlank() } +data class SupportedLocaleRow( + val tag: String, + val resourceQualifier: String, + val status: String +) + +val activeLocaleStatuses = setOf("complete", "partial") +val supportedLocalesCatalogFile = layout.projectDirectory.file("src/main/res/raw/supported_locales.tsv") +val defaultStringsFile = layout.projectDirectory.file("src/main/res/values/strings.xml") +val generatedLocaleResDir = layout.buildDirectory.dir("generated/res/locales") + +fun parseSupportedLocaleRows(file: File): List { + return file.readLines(Charsets.UTF_8) + .asSequence() + .map { it.trim() } + .filter { it.isNotEmpty() && !it.startsWith("#") } + .mapNotNull { line -> + val columns = line.split('\t') + if (columns.size < 3 || columns[0] == "tag") { + null + } else { + SupportedLocaleRow( + tag = columns[0].trim(), + resourceQualifier = columns[1].trim(), + status = columns[2].trim().lowercase() + ) + } + } + .toList() +} + +fun parseStringResourceNames(file: File): Set { + val stringNamePattern = Regex(""" + val localizedStringsFile = layout.projectDirectory + .file("src/main/res/${row.resourceQualifier}/strings.xml") + .asFile + if (!localizedStringsFile.exists()) { + throw GradleException( + "${row.tag} is marked '${row.status}' in supported_locales.tsv, " + + "but ${row.resourceQualifier}/strings.xml does not exist." + ) + } + + val localizedStrings = parseStringResourceNames(localizedStringsFile) + val missing = defaultStrings - localizedStrings + if (missing.isNotEmpty()) { + throw GradleException( + "${row.resourceQualifier}/strings.xml is missing localized strings: " + + missing.sorted().joinToString() + ) + } + } + + activeRows + .filter { it.status == "complete" } + .forEach { row -> + val privacyPolicyFile = layout.projectDirectory + .file("src/main/res/${rawResourceQualifierFor(row)}/privacy_policy.txt") + .asFile + if (!privacyPolicyFile.exists()) { + throw GradleException( + "${row.tag} is marked 'complete' in supported_locales.tsv, " + + "but ${rawResourceQualifierFor(row)}/privacy_policy.txt does not exist." + ) + } + } + } +} + +val generateLocaleConfig = tasks.register("generateLocaleConfig") { + inputs.file(supportedLocalesCatalogFile) + outputs.file(generatedLocaleResDir.map { it.file("xml/locales_config.xml") }) + + doLast { + val activeRows = parseSupportedLocaleRows(supportedLocalesCatalogFile.asFile) + .filter { it.status in activeLocaleStatuses && it.tag != "system" } + + val outputFile = generatedLocaleResDir.get().file("xml/locales_config.xml").asFile + outputFile.parentFile.mkdirs() + outputFile.writeText( + buildString { + appendLine("""""") + appendLine("""""") + activeRows.forEach { row -> + appendLine(""" """) + } + appendLine("") + }, + Charsets.UTF_8 + ) + } +} + gradle.taskGraph.whenReady { val releaseBundleRequested = allTasks.any { it.path == ":app:bundleRelease" || it.name == "bundleRelease" @@ -112,6 +236,12 @@ android { } } +androidComponents { + onVariants { variant -> + variant.sources.res?.addStaticSourceDirectory("build/generated/res/locales") + } +} + kotlin { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) @@ -163,3 +293,11 @@ dependencies { debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest") } + +tasks.named("preBuild") { + dependsOn(generateLocaleConfig, verifyLocalization) +} + +tasks.named("check") { + dependsOn(verifyLocalization) +} diff --git a/app/src/main/java/de/softwareapp_hb/privateqrscanner/MainActivity.kt b/app/src/main/java/de/softwareapp_hb/privateqrscanner/MainActivity.kt index a04620f..4dbb5f1 100644 --- a/app/src/main/java/de/softwareapp_hb/privateqrscanner/MainActivity.kt +++ b/app/src/main/java/de/softwareapp_hb/privateqrscanner/MainActivity.kt @@ -5,6 +5,16 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleApplier +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleCatalog import de.softwareapp_hb.privateqrscanner.ui.CleanScannerAppRoot import de.softwareapp_hb.privateqrscanner.ui.theme.CleanScannerTheme @@ -13,10 +23,37 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) val container = (application as CleanScannerApp).appContainer setContent { - CleanScannerTheme { - Surface(color = MaterialTheme.colorScheme.background) { - CleanScannerAppRoot(container) - } + PrivateQrLocalizedContent(container) + } + } +} + +@Composable +private fun PrivateQrLocalizedContent(container: AppContainer) { + val baseContext = LocalContext.current + val localeOptions = remember(baseContext.applicationContext) { + AppLocaleCatalog.load(baseContext.applicationContext) + } + val selectedLanguageTag by container.settingsRepository.appLanguageTag.collectAsStateWithLifecycle( + initialValue = AppLocaleCatalog.SYSTEM_STORAGE_TAG + ) + val resolvedLanguageTag = AppLocaleCatalog.resolveLanguageTag(localeOptions, selectedLanguageTag) + + LaunchedEffect(baseContext, resolvedLanguageTag) { + AppLocaleApplier.applyFrameworkAppLocale(baseContext.applicationContext, resolvedLanguageTag) + } + + val localizedContext = remember(baseContext, resolvedLanguageTag) { + AppLocaleApplier.wrapContext(baseContext, resolvedLanguageTag) + } + + CompositionLocalProvider( + LocalContext provides localizedContext, + LocalConfiguration provides localizedContext.resources.configuration + ) { + CleanScannerTheme { + Surface(color = MaterialTheme.colorScheme.background) { + CleanScannerAppRoot(container, localeOptions) } } } diff --git a/app/src/main/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalog.kt b/app/src/main/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalog.kt new file mode 100644 index 0000000..0d5effe --- /dev/null +++ b/app/src/main/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalog.kt @@ -0,0 +1,138 @@ +package de.softwareapp_hb.privateqrscanner.i18n + +import android.app.LocaleManager +import android.content.Context +import android.content.res.Configuration +import android.os.Build +import android.os.LocaleList +import de.softwareapp_hb.privateqrscanner.R +import java.util.Locale + +data class AppLocaleOption( + val storageTag: String, + val languageTag: String?, + val status: AppLocaleStatus, + val displayName: String +) { + val isSystemDefault: Boolean = languageTag == null +} + +enum class AppLocaleStatus(val storageValue: String) { + System("system"), + Complete("complete"), + Partial("partial"), + Planned("planned"); + + val isSelectable: Boolean + get() = this == System || this == Complete || this == Partial + + companion object { + fun fromStorageValue(value: String): AppLocaleStatus { + return entries.firstOrNull { it.storageValue == value.trim().lowercase(Locale.US) } ?: Planned + } + } +} + +object AppLocaleCatalog { + const val SYSTEM_STORAGE_TAG = "system" + + fun load(context: Context): List { + val parsed = context.resources.openRawResource(R.raw.supported_locales) + .bufferedReader(Charsets.UTF_8) + .use { parse(it.readText()) } + .filter { it.status.isSelectable } + + val withSystemDefault = if (parsed.any { it.isSystemDefault }) { + parsed + } else { + listOf(systemDefaultOption()) + parsed + } + + return withSystemDefault.ifEmpty { + listOf(systemDefaultOption(), AppLocaleOption("en", "en", AppLocaleStatus.Complete, "English")) + } + } + + fun parse(rawCatalog: String): List { + return rawCatalog + .lineSequence() + .map { it.trim() } + .filter { it.isNotEmpty() && !it.startsWith("#") } + .mapNotNull(::parseLine) + .toList() + } + + fun resolveOption(options: List, storageTag: String?): AppLocaleOption { + return options.firstOrNull { it.storageTag == storageTag } ?: options.firstOrNull { it.isSystemDefault } + ?: systemDefaultOption() + } + + fun resolveLanguageTag(options: List, storageTag: String?): String? { + return resolveOption(options, storageTag).languageTag + } + + private fun parseLine(line: String): AppLocaleOption? { + val columns = line.split('\t') + if (columns.size < 4 || columns[0] == "tag") return null + + val tag = columns[0].trim() + val status = AppLocaleStatus.fromStorageValue(columns[2]) + val displayName = columns[3].trim().ifBlank { tag } + val languageTag = tag.takeUnless { it == SYSTEM_STORAGE_TAG || status == AppLocaleStatus.System } + + return AppLocaleOption( + storageTag = tag, + languageTag = languageTag, + status = status, + displayName = displayName + ) + } + + private fun systemDefaultOption(): AppLocaleOption { + return AppLocaleOption( + storageTag = SYSTEM_STORAGE_TAG, + languageTag = null, + status = AppLocaleStatus.System, + displayName = "System default" + ) + } +} + +object AppLocaleApplier { + fun applyFrameworkAppLocale(context: Context, languageTag: String?) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return + + val localeManager = context.getSystemService(LocaleManager::class.java) + val targetLocales = if (languageTag.isNullOrBlank()) { + LocaleList.getEmptyLocaleList() + } else { + LocaleList.forLanguageTags(languageTag) + } + + if (localeManager.applicationLocales.toLanguageTags() != targetLocales.toLanguageTags()) { + localeManager.applicationLocales = targetLocales + } + } + + fun wrapContext(baseContext: Context, languageTag: String?): Context { + val baseConfiguration = baseContext.resources.configuration + val locale = if (languageTag.isNullOrBlank()) { + baseConfiguration.locales.get(0) ?: Locale.getDefault() + } else { + Locale.forLanguageTag(languageTag) + } + + Locale.setDefault(locale) + LocaleList.setDefault(LocaleList(locale)) + + if (languageTag.isNullOrBlank()) return baseContext + + val localizedConfiguration = Configuration(baseConfiguration).apply { + val locales = LocaleList(locale) + setLocales(locales) + setLayoutDirection(locale) + } + + return baseContext.createConfigurationContext(localizedConfiguration) + } +} diff --git a/app/src/main/java/de/softwareapp_hb/privateqrscanner/settings/SettingsRepository.kt b/app/src/main/java/de/softwareapp_hb/privateqrscanner/settings/SettingsRepository.kt index f31be05..ee89991 100644 --- a/app/src/main/java/de/softwareapp_hb/privateqrscanner/settings/SettingsRepository.kt +++ b/app/src/main/java/de/softwareapp_hb/privateqrscanner/settings/SettingsRepository.kt @@ -5,6 +5,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.preferencesDataStore +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleCatalog import de.softwareapp_hb.privateqrscanner.ui.UseCaseView import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -17,6 +18,7 @@ class SettingsRepository(private val context: Context) { val warningsEnabled = booleanPreferencesKey("warnings_enabled") val scanFeedbackEnabled = booleanPreferencesKey("scan_feedback_enabled") val useCaseView = stringPreferencesKey("use_case_view") + val appLanguageTag = stringPreferencesKey("app_language_tag") } val historyEnabled: Flow = context.dataStore.data.map { prefs -> @@ -35,6 +37,10 @@ class SettingsRepository(private val context: Context) { UseCaseView.fromStorageKey(prefs[Keys.useCaseView]) } + val appLanguageTag: Flow = context.dataStore.data.map { prefs -> + prefs[Keys.appLanguageTag] ?: AppLocaleCatalog.SYSTEM_STORAGE_TAG + } + suspend fun setHistoryEnabled(enabled: Boolean) { context.dataStore.edit { it[Keys.historyEnabled] = enabled } } @@ -50,4 +56,8 @@ class SettingsRepository(private val context: Context) { suspend fun setUseCaseView(useCaseView: UseCaseView) { context.dataStore.edit { it[Keys.useCaseView] = useCaseView.storageKey } } + + suspend fun setAppLanguageTag(languageTag: String) { + context.dataStore.edit { it[Keys.appLanguageTag] = languageTag } + } } diff --git a/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/AppViewModel.kt b/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/AppViewModel.kt index a57ce19..930e847 100644 --- a/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/AppViewModel.kt +++ b/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/AppViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import de.softwareapp_hb.privateqrscanner.AppContainer import de.softwareapp_hb.privateqrscanner.domain.ScanRecord +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleCatalog import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -17,6 +18,7 @@ data class AppUiState( val warningsEnabled: Boolean = true, val scanFeedbackEnabled: Boolean = true, val useCaseView: UseCaseView = UseCaseView.default, + val appLanguageTag: String = AppLocaleCatalog.SYSTEM_STORAGE_TAG, val history: List = emptyList(), val searchQuery: String = "" ) @@ -29,7 +31,8 @@ class AppViewModel( val historyEnabled: Boolean, val warningsEnabled: Boolean, val scanFeedbackEnabled: Boolean, - val useCaseView: UseCaseView + val useCaseView: UseCaseView, + val appLanguageTag: String ) private val query = MutableStateFlow("") @@ -38,13 +41,15 @@ class AppViewModel( container.settingsRepository.historyEnabled, container.settingsRepository.warningsEnabled, container.settingsRepository.scanFeedbackEnabled, - container.settingsRepository.useCaseView - ) { historyEnabled, warningsEnabled, scanFeedbackEnabled, useCaseView -> + container.settingsRepository.useCaseView, + container.settingsRepository.appLanguageTag + ) { historyEnabled, warningsEnabled, scanFeedbackEnabled, useCaseView, appLanguageTag -> SettingsState( historyEnabled = historyEnabled, warningsEnabled = warningsEnabled, scanFeedbackEnabled = scanFeedbackEnabled, - useCaseView = useCaseView + useCaseView = useCaseView, + appLanguageTag = appLanguageTag ) } @@ -58,6 +63,7 @@ class AppViewModel( warningsEnabled = settings.warningsEnabled, scanFeedbackEnabled = settings.scanFeedbackEnabled, useCaseView = settings.useCaseView, + appLanguageTag = settings.appLanguageTag, history = if (q.isBlank()) history else history.filter { it.content.contains(q, ignoreCase = true) || it.type.contains(q, ignoreCase = true) }, @@ -96,6 +102,12 @@ class AppViewModel( } } + fun setAppLanguageTag(languageTag: String) { + viewModelScope.launch { + container.settingsRepository.setAppLanguageTag(languageTag) + } + } + fun deleteHistoryItem(id: Long) { viewModelScope.launch { container.scanRepository.deleteById(id) diff --git a/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/CleanScannerAppRoot.kt b/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/CleanScannerAppRoot.kt index bdf3ad1..b4c50e7 100644 --- a/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/CleanScannerAppRoot.kt +++ b/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/CleanScannerAppRoot.kt @@ -23,6 +23,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import de.softwareapp_hb.privateqrscanner.AppContainer import de.softwareapp_hb.privateqrscanner.R +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleOption import de.softwareapp_hb.privateqrscanner.ui.screens.HistoryScreen import de.softwareapp_hb.privateqrscanner.ui.screens.ScannerScreen import de.softwareapp_hb.privateqrscanner.ui.screens.SettingsScreen @@ -31,7 +32,10 @@ import de.softwareapp_hb.privateqrscanner.ui.theme.PrivateQrColors private enum class RootTab { Scanner, History, Settings } @Composable -fun CleanScannerAppRoot(container: AppContainer) { +fun CleanScannerAppRoot( + container: AppContainer, + localeOptions: List +) { val appViewModel: AppViewModel = viewModel(factory = AppViewModel.Factory(container)) val scannerViewModel: ScannerViewModel = viewModel(factory = ScannerViewModel.Factory(container)) val appState by appViewModel.uiState.collectAsStateWithLifecycle() @@ -125,10 +129,13 @@ fun CleanScannerAppRoot(container: AppContainer) { warningsEnabled = appState.warningsEnabled, scanFeedbackEnabled = appState.scanFeedbackEnabled, selectedUseCaseView = appState.useCaseView, + appLocaleOptions = localeOptions, + selectedAppLanguageTag = appState.appLanguageTag, onHistoryToggle = appViewModel::setHistoryEnabled, onWarningsToggle = appViewModel::setWarningsEnabled, onScanFeedbackToggle = appViewModel::setScanFeedbackEnabled, - onUseCaseViewSelected = appViewModel::setUseCaseView + onUseCaseViewSelected = appViewModel::setUseCaseView, + onAppLanguageSelected = appViewModel::setAppLanguageTag ) } } diff --git a/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/screens/SettingsScreen.kt b/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/screens/SettingsScreen.kt index 5c41caa..ccf1f72 100644 --- a/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/de/softwareapp_hb/privateqrscanner/ui/screens/SettingsScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState @@ -24,6 +25,8 @@ import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.RadioButtonDefaults import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text @@ -41,6 +44,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import de.softwareapp_hb.privateqrscanner.R +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleCatalog +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleOption +import de.softwareapp_hb.privateqrscanner.i18n.AppLocaleStatus import de.softwareapp_hb.privateqrscanner.ui.UseCaseView import de.softwareapp_hb.privateqrscanner.ui.theme.PrivateQrColors import de.softwareapp_hb.privateqrscanner.util.InAppReviewRequester @@ -51,10 +57,13 @@ fun SettingsScreen( warningsEnabled: Boolean, scanFeedbackEnabled: Boolean, selectedUseCaseView: UseCaseView, + appLocaleOptions: List, + selectedAppLanguageTag: String, onHistoryToggle: (Boolean, Boolean) -> Unit, onWarningsToggle: (Boolean) -> Unit, onScanFeedbackToggle: (Boolean) -> Unit, - onUseCaseViewSelected: (UseCaseView) -> Unit + onUseCaseViewSelected: (UseCaseView) -> Unit, + onAppLanguageSelected: (String) -> Unit ) { val context = LocalContext.current val showDeleteConfirm = remember { mutableStateOf(false) } @@ -119,6 +128,11 @@ fun SettingsScreen( onScanFeedbackToggle = onScanFeedbackToggle, onRequestDisableHistory = { showDeleteConfirm.value = true } ) + LanguageSelectionCard( + appLocaleOptions = appLocaleOptions, + selectedAppLanguageTag = selectedAppLanguageTag, + onAppLanguageSelected = onAppLanguageSelected + ) AboutCard( onPrivacyClick = { showPrivacyPolicy.value = true }, onReviewClick = { InAppReviewRequester.requestReview(context) } @@ -140,6 +154,11 @@ fun SettingsScreen( onScanFeedbackToggle = onScanFeedbackToggle, onRequestDisableHistory = { showDeleteConfirm.value = true } ) + LanguageSelectionCard( + appLocaleOptions = appLocaleOptions, + selectedAppLanguageTag = selectedAppLanguageTag, + onAppLanguageSelected = onAppLanguageSelected + ) UseCaseSelectionCard( selectedUseCaseView = selectedUseCaseView, onUseCaseViewSelected = onUseCaseViewSelected @@ -155,6 +174,148 @@ fun SettingsScreen( } } +@Composable +private fun LanguageSelectionCard( + appLocaleOptions: List, + selectedAppLanguageTag: String, + onAppLanguageSelected: (String) -> Unit +) { + val showPicker = remember { mutableStateOf(false) } + val selectedOption = AppLocaleCatalog.resolveOption(appLocaleOptions, selectedAppLanguageTag) + + if (showPicker.value) { + LanguagePickerDialog( + appLocaleOptions = appLocaleOptions, + selectedAppLanguageTag = selectedOption.storageTag, + onSelect = { option -> + onAppLanguageSelected(option.storageTag) + showPicker.value = false + }, + onDismiss = { showPicker.value = false } + ) + } + + Card( + modifier = Modifier + .fillMaxWidth() + .clickable { showPicker.value = true }, + colors = CardDefaults.cardColors(containerColor = PrivateQrColors.Surface), + shape = RoundedCornerShape(28.dp), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) { + Text( + text = stringResource(R.string.app_language), + color = PrivateQrColors.TextPrimary, + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.ExtraBold + ) + Text( + text = selectedOption.displayLabel(), + color = PrivateQrColors.TextSecondary, + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.SemiBold + ) + } + TextButton( + onClick = { showPicker.value = true }, + colors = ButtonDefaults.textButtonColors(contentColor = PrivateQrColors.Teal700) + ) { + Text(text = stringResource(R.string.change), fontWeight = FontWeight.Bold) + } + } + } +} + +@Composable +private fun LanguagePickerDialog( + appLocaleOptions: List, + selectedAppLanguageTag: String, + onSelect: (AppLocaleOption) -> Unit, + onDismiss: () -> Unit +) { + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(stringResource(R.string.app_language)) }, + text = { + Column( + modifier = Modifier + .fillMaxWidth() + .heightIn(max = 420.dp) + .verticalScroll(rememberScrollState()) + ) { + appLocaleOptions.forEach { option -> + LanguagePickerRow( + option = option, + selected = option.storageTag == selectedAppLanguageTag, + onClick = { onSelect(option) } + ) + } + } + }, + confirmButton = { + TextButton(onClick = onDismiss) { + Text(stringResource(R.string.close)) + } + } + ) +} + +@Composable +private fun LanguagePickerRow( + option: AppLocaleOption, + selected: Boolean, + onClick: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 56.dp) + .clickable(onClick = onClick) + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = selected, + onClick = onClick, + colors = RadioButtonDefaults.colors(selectedColor = PrivateQrColors.Teal700) + ) + Column(modifier = Modifier.weight(1f)) { + Text( + text = option.displayLabel(), + color = PrivateQrColors.TextPrimary, + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Bold + ) + if (option.status == AppLocaleStatus.Partial) { + Text( + text = stringResource(R.string.app_language_partial_translation), + color = PrivateQrColors.TextSecondary, + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.SemiBold + ) + } + } + } +} + +@Composable +private fun AppLocaleOption.displayLabel(): String { + return if (isSystemDefault) { + stringResource(R.string.app_language_system_default) + } else { + displayName + } +} + @Composable private fun SettingsToggleCard( historyEnabled: Boolean, diff --git a/app/src/main/res/raw-ar/privacy_policy.txt b/app/src/main/res/raw-ar/privacy_policy.txt new file mode 100644 index 0000000..8487a01 --- /dev/null +++ b/app/src/main/res/raw-ar/privacy_policy.txt @@ -0,0 +1,57 @@ +سياسة الخصوصية +آخر تحديث: 2026-05-10 + +يتم توفير Private QR Scanner من قبل SoftwareApp-HB. +التواصل: softwareapp.hb@gmail.com + +توضح هذه السياسة كيفية تعامل Private QR Scanner مع البيانات على جهازك. + +ما الذي يصل إليه التطبيق + +الكاميرا: يستخدم التطبيق الكاميرا فقط لمسح رموز QR والباركود. تتم معالجة إطارات الكاميرا على جهازك ولا يحفظها التطبيق. + +الصور المحددة: إذا اخترت المسح من صورة، يقرأ التطبيق تلك الصورة المحددة فقط لاكتشاف رموز QR أو الباركود. لا يحتفظ التطبيق بنسخة من الصورة. + +المحتوى الممسوح: يعرض التطبيق المحتوى الممسوح على جهازك. إذا فعّلت حفظ السجل (محلي)، فسيتم تخزين المحتوى الممسوح ونوع المحتوى ووقت المسح محليًا على جهازك. يكون السجل متوقفًا بشكل افتراضي. + +الإعدادات: يخزن التطبيق تفضيلاتك المحلية، مثل ما إذا كان السجل وتحذيرات الأمان وتنبيه المسح وعرض حالة الاستخدام المحدد مفعّلة. + +بيانات الفعاليات والتذاكر: تتم معالجة معرّفات القائمة البيضاء المستوردة ونتائج المسح الدُفعي محليًا. تبقى معرّفات القائمة البيضاء في الذاكرة خلال جلسة التطبيق الحالية ولا يرفعها التطبيق. + +كيفية استخدام البيانات + +تُستخدم البيانات لمسح الرموز، وعرض نتائج المسح، وتقديم تحذيرات أمان محلية، ودعم السجل المحلي الاختياري، ودعم سير عمل الفعاليات والتذاكر، وتنفيذ الإجراءات التي تختارها. + +جمع البيانات ومشاركتها + +لا ينشئ Private QR Scanner حسابات، ولا يعرض إعلانات، ولا يستخدم التحليلات، ولا يستخدم حزم SDK للتتبع، ولا يستخدم حزم SDK للإبلاغ عن الأعطال، ولا يرسل بيانات التطبيق إلى خادم خلفي للمطور. لا يطلب التطبيق إذن الإنترنت في Android. + +لا يتلقى المطور ولا يجمع ولا يبيع ولا يشارك المحتوى الممسوح أو الصور المحددة أو السجل أو الإعدادات أو بيانات القائمة البيضاء الخاصة بك. + +يمكن أن تغادر البيانات جهازك فقط عندما تختار إجراءً من Android أو من جهة خارجية، مثل فتح، مشاركة، اتصال، SMS، بريد إلكتروني، إضافة جهة اتصال، إضافة حدث إلى التقويم، إعدادات Wi-Fi، أو مراجعة Google Play. تتم معالجة هذه الإجراءات بواسطة Android أو Google Play أو التطبيق أو الخدمة التي تختارها، وتنطبق ممارسات الخصوصية الخاصة بها. + +التخزين والاحتفاظ والحذف + +يتم تخزين سجل المسح الاختياري والإعدادات في مساحة التخزين الخاصة بالتطبيق على جهازك. النسخ الاحتياطي لتطبيق Android معطّل لهذا التطبيق. + +يبقى سجل المسح على جهازك إلى أن تحذف عناصر فردية، أو تحذف السجل بالكامل، أو توقف السجل وتختار حذف السجل الحالي، أو تمسح بيانات التطبيق، أو تزيل تثبيت التطبيق. + +تبقى الإعدادات على جهازك إلى أن تغيّرها، أو تمسح بيانات التطبيق، أو تزيل تثبيت التطبيق. + +لا يحتفظ التطبيق بإطارات الكاميرا أو الصور المحددة أو معرّفات القائمة البيضاء المستوردة أو نتائج المسح الدُفعي بعد انتهاء الاستخدام النشط أو جلسة التطبيق، إلا إذا تم حفظ المحتوى الممسوح الناتج في السجل المحلي لأنك فعّلت هذا الإعداد. + +الأمان + +يعتمد التطبيق على مساحة التخزين الخاصة بتطبيقات Android وعلى عناصر التحكم في أذونات Android. أبقِ قفل شاشة جهازك وتحديثات أمان نظام التشغيل مفعّلة لحماية البيانات المحلية. + +الأطفال + +التطبيق غير موجه للأطفال ولا يجمع عن علم بيانات شخصية من أي شخص. + +التغييرات + +إذا تغيرت هذه السياسة، فسيتم تضمين السياسة المحدثة في التطبيق وفي النسخة العامة من سياسة التطبيق. + +التواصل + +لأسئلة الخصوصية أو طلبات الحذف المتعلقة ببيانات ربما تم إرسالها من خلال إجراء خارجي اخترته، تواصل مع الخدمة الخارجية المعنية. للأسئلة المتعلقة بهذا التطبيق، تواصل مع SoftwareApp-HB على softwareapp.hb@gmail.com. diff --git a/app/src/main/res/raw-b+pt+BR/privacy_policy.txt b/app/src/main/res/raw-b+pt+BR/privacy_policy.txt new file mode 100644 index 0000000..7fc3be9 --- /dev/null +++ b/app/src/main/res/raw-b+pt+BR/privacy_policy.txt @@ -0,0 +1,57 @@ +Política de Privacidade +Última atualização: 2026-05-10 + +Private QR Scanner é fornecido por SoftwareApp-HB. +Contato: softwareapp.hb@gmail.com + +Esta política explica como Private QR Scanner lida com dados no seu dispositivo. + +O que o app acessa + +Câmera: O app usa a câmera somente para escanear códigos QR e códigos de barras. Os quadros da câmera são processados no seu dispositivo e não são salvos pelo app. + +Imagens selecionadas: Se você optar por escanear a partir de uma imagem, o app lê apenas essa imagem selecionada para detectar códigos QR ou códigos de barras. O app não mantém uma cópia da imagem. + +Conteúdo escaneado: O app exibe o conteúdo escaneado no seu dispositivo. Se você ativar Salvar histórico (local), o conteúdo escaneado, o tipo de conteúdo e o horário da leitura serão armazenados localmente no seu dispositivo. O histórico fica desativado por padrão. + +Configurações: O app armazena suas preferências locais, como se histórico, avisos de segurança, feedback da leitura e a visualização de caso de uso selecionada estão ativados. + +Dados de eventos e ingressos: IDs de lista permitida importados e resultados de leituras em lote são processados localmente. IDs de lista permitida ficam na memória durante a sessão atual do app e não são enviados pelo app. + +Como os dados são usados + +Os dados são usados para escanear códigos, mostrar resultados, fornecer avisos de segurança locais, oferecer suporte ao histórico local opcional, oferecer suporte a fluxos de eventos e ingressos e executar ações escolhidas por você. + +Coleta e compartilhamento de dados + +Private QR Scanner não cria contas, não mostra anúncios, não usa análise, não usa SDKs de rastreamento, não usa SDKs de relatório de falhas e não envia dados do app para um backend do desenvolvedor. O app não solicita a permissão de Internet do Android. + +O desenvolvedor não recebe, coleta, vende nem compartilha seu conteúdo escaneado, imagens selecionadas, histórico, configurações ou dados de lista permitida. + +Os dados só podem sair do seu dispositivo quando você escolhe uma ação do Android ou de terceiros, como Abrir, Compartilhar, Ligar, SMS, E-mail, Adicionar contato, Adicionar evento ao calendário, configurações de Wi-Fi ou avaliação no Google Play. Essas ações são tratadas pelo Android, Google Play ou pelo app ou serviço que você escolher, e as práticas de privacidade deles se aplicam. + +Armazenamento, retenção e exclusão + +Histórico opcional de leituras e configurações são armazenados no armazenamento privado do app no seu dispositivo. O backup do app pelo Android está desativado para este app. + +O histórico de leituras permanece no seu dispositivo até você excluir entradas individuais, excluir todo o histórico, desativar o histórico e escolher excluir o histórico existente, limpar os dados do app ou desinstalar o app. + +As configurações permanecem no seu dispositivo até você alterá-las, limpar os dados do app ou desinstalar o app. + +Quadros da câmera, imagens selecionadas, IDs de lista permitida importados e resultados de leituras em lote não são retidos pelo app após o uso ativo ou o fim da sessão do app, a menos que o conteúdo escaneado resultante seja salvo no histórico local porque você ativou essa configuração. + +Segurança + +O app depende do armazenamento privado de apps do Android e dos controles de permissão do Android. Mantenha o bloqueio de tela do dispositivo e as atualizações de segurança do sistema operacional ativados para proteger dados locais. + +Crianças + +O app não é direcionado a crianças e não coleta intencionalmente dados pessoais de ninguém. + +Alterações + +Se esta política mudar, a política atualizada será incluída no app e na cópia pública da política do app. + +Contato + +Para dúvidas de privacidade ou solicitações de exclusão sobre dados que possam ter sido enviados por uma ação externa escolhida por você, entre em contato com o serviço externo relevante. Para dúvidas sobre este app, entre em contato com SoftwareApp-HB em softwareapp.hb@gmail.com. diff --git a/app/src/main/res/raw-b+pt+PT/privacy_policy.txt b/app/src/main/res/raw-b+pt+PT/privacy_policy.txt new file mode 100644 index 0000000..b30ec43 --- /dev/null +++ b/app/src/main/res/raw-b+pt+PT/privacy_policy.txt @@ -0,0 +1,57 @@ +Política de Privacidade +Última atualização: 2026-05-10 + +Private QR Scanner é fornecido pela SoftwareApp-HB. +Contacto: softwareapp.hb@gmail.com + +Esta política explica como o Private QR Scanner trata os dados no seu dispositivo. + +Ao que a app acede + +Câmara: A app usa a câmara apenas para digitalizar códigos QR e códigos de barras. As imagens da câmara são processadas no seu dispositivo e não são guardadas pela app. + +Imagens selecionadas: Se escolher digitalizar a partir de uma imagem, a app lê apenas essa imagem selecionada para detetar códigos QR ou códigos de barras. A app não mantém uma cópia da imagem. + +Conteúdo digitalizado: A app apresenta o conteúdo digitalizado no seu dispositivo. Se ativar Guardar histórico (local), o conteúdo digitalizado, o tipo de conteúdo e a hora da digitalização são armazenados localmente no seu dispositivo. O histórico está desativado por predefinição. + +Definições: A app guarda as suas preferências locais, como se o histórico, os avisos de segurança, o feedback da digitalização e a vista de caso de uso selecionada estão ativados. + +Dados de eventos e bilhética: IDs de lista permitida importados e resultados de digitalizações em lote são processados localmente. Os IDs de lista permitida ficam em memória durante a sessão atual da app e não são carregados pela app. + +Como os dados são usados + +Os dados são usados para digitalizar códigos, mostrar resultados, fornecer avisos de segurança locais, suportar o histórico local opcional, suportar fluxos de eventos e bilhética e executar ações que escolher. + +Recolha e partilha de dados + +Private QR Scanner não cria contas, não mostra anúncios, não usa análises, não usa SDKs de rastreio, não usa SDKs de relatórios de falhas e não envia dados da app para um backend do programador. A app não solicita a permissão Android de Internet. + +O programador não recebe, recolhe, vende nem partilha o seu conteúdo digitalizado, imagens selecionadas, histórico, definições ou dados de lista permitida. + +Os dados só podem sair do seu dispositivo quando escolhe uma ação Android ou de terceiros, como Abrir, Partilhar, Chamar, SMS, E-mail, Adicionar contacto, Adicionar evento ao calendário, definições de Wi-Fi ou avaliação no Google Play. Essas ações são tratadas pelo Android, Google Play ou pela app ou serviço que escolher, e aplicam-se as respetivas práticas de privacidade. + +Armazenamento, retenção e eliminação + +O histórico opcional de digitalizações e as definições são armazenados no armazenamento privado da app no seu dispositivo. A cópia de segurança Android da app está desativada para esta app. + +O histórico de digitalizações permanece no seu dispositivo até eliminar entradas individuais, eliminar todo o histórico, desativar o histórico e escolher eliminar o histórico existente, limpar os dados da app ou desinstalar a app. + +As definições permanecem no seu dispositivo até as alterar, limpar os dados da app ou desinstalar a app. + +Imagens da câmara, imagens selecionadas, IDs de lista permitida importados e resultados de digitalizações em lote não são retidos pela app após a utilização ativa ou o fim da sessão da app, exceto se o conteúdo digitalizado resultante for guardado no histórico local porque ativou essa definição. + +Segurança + +A app depende do armazenamento privado de apps Android e dos controlos de permissões Android. Mantenha o bloqueio de ecrã do dispositivo e as atualizações de segurança do sistema operativo ativados para proteger os dados locais. + +Crianças + +A app não se destina a crianças e não recolhe conscientemente dados pessoais de ninguém. + +Alterações + +Se esta política mudar, a política atualizada será incluída na app e na cópia pública da política da app. + +Contacto + +Para questões de privacidade ou pedidos de eliminação sobre dados que possam ter sido enviados através de uma ação externa que escolheu, contacte o serviço externo relevante. Para questões sobre esta app, contacte a SoftwareApp-HB em softwareapp.hb@gmail.com. diff --git a/app/src/main/res/raw-b+zh+Hans/privacy_policy.txt b/app/src/main/res/raw-b+zh+Hans/privacy_policy.txt new file mode 100644 index 0000000..58fe30c --- /dev/null +++ b/app/src/main/res/raw-b+zh+Hans/privacy_policy.txt @@ -0,0 +1,57 @@ +隐私政策 +最后更新:2026-05-10 + +Private QR Scanner 由 SoftwareApp-HB 提供。 +联系:softwareapp.hb@gmail.com + +本政策说明 Private QR Scanner 如何处理你设备上的数据。 + +应用访问的内容 + +相机:应用仅使用相机扫描二维码和条形码。相机画面会在你的设备上处理,应用不会保存这些画面。 + +所选图片:如果你选择从图片扫描,应用只会读取该所选图片以检测二维码或条形码。应用不会保留该图片的副本。 + +扫描内容:应用会在你的设备上显示扫描内容。如果你启用保存历史记录(本地),扫描内容、内容类型和扫描时间会在你的设备上本地存储。历史记录默认关闭。 + +设置:应用会存储你的本地偏好,例如历史记录、安全警告、扫描反馈以及所选使用场景视图是否已启用。 + +活动和票务数据:导入的白名单 ID 和批量扫描结果会在本地处理。白名单 ID 在当前应用会话期间保存在内存中,应用不会上传这些 ID。 + +数据的使用方式 + +数据用于扫描代码、显示扫描结果、提供本地安全警告、支持可选的本地历史记录、支持活动和票务流程,以及执行你选择的操作。 + +数据收集和共享 + +Private QR Scanner 不创建账号、不显示广告、不使用分析、不使用跟踪 SDK、不使用崩溃报告 SDK,也不会将应用数据发送到开发者后端。应用不会请求 Android 互联网权限。 + +开发者不会接收、收集、出售或共享你的扫描内容、所选图片、历史记录、设置或白名单数据。 + +只有当你选择 Android 或第三方操作时,数据才可能离开你的设备,例如打开、分享、通话、短信、电子邮件、添加联系人、添加日历事件、Wi-Fi 设置或 Google Play 评价。这些操作由 Android、Google Play 或你选择的应用或服务处理,并适用其隐私做法。 + +存储、保留和删除 + +可选的扫描历史记录和设置会存储在你设备上的应用专用存储中。此应用已禁用 Android 应用备份。 + +扫描历史记录会保留在你的设备上,直到你删除单个条目、删除全部历史记录、关闭历史记录并选择删除现有历史记录、清除应用数据或卸载应用。 + +设置会保留在你的设备上,直到你更改设置、清除应用数据或卸载应用。 + +相机画面、所选图片、导入的白名单 ID 和批量扫描结果在主动使用结束或应用会话结束后不会被应用保留,除非你启用了相应设置,使产生的扫描内容保存到本地历史记录中。 + +安全 + +应用依赖 Android 应用专用存储和 Android 权限控制。请保持设备屏幕锁定和操作系统安全更新处于启用状态,以保护本地数据。 + +儿童 + +应用不面向儿童,也不会有意收集任何人的个人数据。 + +变更 + +如果本政策发生变化,更新后的政策将包含在应用中,并包含在应用的公开政策副本中。 + +联系 + +对于可能通过你选择的外部操作发送的数据,如果有隐私问题或删除请求,请联系相关外部服务。有关本应用的问题,请通过 softwareapp.hb@gmail.com 联系 SoftwareApp-HB。 diff --git a/app/src/main/res/raw-b+zh+Hant/privacy_policy.txt b/app/src/main/res/raw-b+zh+Hant/privacy_policy.txt new file mode 100644 index 0000000..148de97 --- /dev/null +++ b/app/src/main/res/raw-b+zh+Hant/privacy_policy.txt @@ -0,0 +1,57 @@ +私隱政策 +最後更新:2026-05-10 + +Private QR Scanner 由 SoftwareApp-HB 提供。 +聯絡:softwareapp.hb@gmail.com + +本政策說明 Private QR Scanner 如何處理你裝置上的資料。 + +App 存取的內容 + +相機:App 只會使用相機掃描 QR code 和條碼。相機畫面會在你的裝置上處理,App 不會儲存這些畫面。 + +所選圖片:如果你選擇從圖片掃描,App 只會讀取該所選圖片以偵測 QR code 或條碼。App 不會保留該圖片的副本。 + +掃描內容:App 會在你的裝置上顯示掃描內容。如果你啟用儲存記錄(本機),掃描內容、內容類型和掃描時間會在你的裝置上本機儲存。記錄預設為關閉。 + +設定:App 會儲存你的本機偏好,例如記錄、安全警告、掃描回饋以及所選使用情境視圖是否已啟用。 + +活動和票務資料:匯入的白名單 ID 和批次掃描結果會在本機處理。白名單 ID 會在目前 App 工作階段期間保存在記憶體中,App 不會上載這些 ID。 + +資料的使用方式 + +資料用於掃描代碼、顯示掃描結果、提供本機安全警告、支援可選的本機記錄、支援活動和票務流程,以及執行你選擇的操作。 + +資料收集和分享 + +Private QR Scanner 不建立帳戶、不顯示廣告、不使用分析、不使用追蹤 SDK、不使用崩潰報告 SDK,也不會將 App 資料傳送到開發者後端。App 不會要求 Android 互聯網權限。 + +開發者不會接收、收集、出售或分享你的掃描內容、所選圖片、記錄、設定或白名單資料。 + +只有當你選擇 Android 或第三方操作時,資料才可能離開你的裝置,例如開啟、分享、通話、SMS、電郵、新增聯絡人、新增日曆事件、Wi-Fi 設定或 Google Play 評價。這些操作由 Android、Google Play 或你選擇的 App 或服務處理,並適用其私隱做法。 + +儲存、保留和刪除 + +可選掃描記錄和設定會儲存在你裝置上的 App 私人儲存空間中。此 App 已停用 Android App 備份。 + +掃描記錄會保留在你的裝置上,直到你刪除個別項目、刪除全部記錄、關閉記錄並選擇刪除現有記錄、清除 App 資料或解除安裝 App。 + +設定會保留在你的裝置上,直到你變更設定、清除 App 資料或解除安裝 App。 + +相機畫面、所選圖片、匯入的白名單 ID 和批次掃描結果在主動使用結束或 App 工作階段結束後不會被 App 保留,除非你啟用了該設定,使產生的掃描內容儲存到本機記錄中。 + +安全 + +App 依賴 Android App 私人儲存空間和 Android 權限控制。請保持裝置螢幕鎖定和作業系統安全更新處於啟用狀態,以保護本機資料。 + +兒童 + +App 並非面向兒童,也不會有意收集任何人的個人資料。 + +變更 + +如果本政策發生變更,更新後的政策將包含在 App 中,並包含在 App 的公開政策副本中。 + +聯絡 + +對於可能透過你選擇的外部操作傳送的資料,如有私隱問題或刪除請求,請聯絡相關外部服務。有關此 App 的問題,請透過 softwareapp.hb@gmail.com 聯絡 SoftwareApp-HB。 diff --git a/app/src/main/res/raw-es/privacy_policy.txt b/app/src/main/res/raw-es/privacy_policy.txt new file mode 100644 index 0000000..8203249 --- /dev/null +++ b/app/src/main/res/raw-es/privacy_policy.txt @@ -0,0 +1,57 @@ +Política de privacidad +Última actualización: 2026-05-10 + +Private QR Scanner es proporcionado por SoftwareApp-HB. +Contacto: softwareapp.hb@gmail.com + +Esta política explica cómo Private QR Scanner gestiona los datos en tu dispositivo. + +A qué accede la app + +Cámara: La app usa la cámara solo para escanear códigos QR y códigos de barras. Los fotogramas de la cámara se procesan en tu dispositivo y la app no los guarda. + +Imágenes seleccionadas: Si eliges escanear desde una imagen, la app lee solo esa imagen seleccionada para detectar códigos QR o códigos de barras. La app no conserva una copia de la imagen. + +Contenido escaneado: La app muestra el contenido escaneado en tu dispositivo. Si activas Guardar historial (local), el contenido escaneado, el tipo de contenido y la hora del escaneo se almacenan localmente en tu dispositivo. El historial está desactivado por defecto. + +Ajustes: La app almacena tus preferencias locales, como si están activados el historial, las advertencias de seguridad, el feedback de escaneo y la vista de caso de uso seleccionada. + +Datos de eventos y tickets: Los ID de lista blanca importados y los resultados de escaneo por lotes se procesan localmente. Los ID de lista blanca se mantienen en memoria durante la sesión actual de la app y la app no los sube. + +Cómo se usan los datos + +Los datos se usan para escanear códigos, mostrar resultados de escaneo, proporcionar advertencias de seguridad locales, admitir el historial local opcional, admitir flujos de eventos y ticketing, y ejecutar las acciones que elijas. + +Recopilación y uso compartido de datos + +Private QR Scanner no crea cuentas, no muestra anuncios, no usa analíticas, no usa SDK de seguimiento, no usa SDK de informes de fallos y no envía datos de la app a un backend del desarrollador. La app no solicita el permiso de Internet de Android. + +El desarrollador no recibe, recopila, vende ni comparte tu contenido escaneado, imágenes seleccionadas, historial, ajustes ni datos de lista blanca. + +Los datos solo pueden salir de tu dispositivo cuando eliges una acción de Android o de terceros, como Abrir, Compartir, Llamar, SMS, Correo, Añadir contacto, Añadir evento al calendario, ajustes de Wi-Fi o reseña en Google Play. Esas acciones las gestionan Android, Google Play o la app o servicio que elijas, y se aplican sus prácticas de privacidad. + +Almacenamiento, conservación y eliminación + +El historial opcional de escaneos y los ajustes se almacenan en el almacenamiento privado de la app en tu dispositivo. La copia de seguridad de Android está desactivada para esta app. + +El historial de escaneos permanece en tu dispositivo hasta que elimines entradas individuales, elimines todo el historial, desactives el historial y elijas eliminar el historial existente, borres los datos de la app o desinstales la app. + +Los ajustes permanecen en tu dispositivo hasta que los cambies, borres los datos de la app o desinstales la app. + +Los fotogramas de la cámara, las imágenes seleccionadas, los ID de lista blanca importados y los resultados de escaneo por lotes no se conservan después del uso activo o del final de la sesión de la app, salvo que el contenido escaneado resultante se guarde en el historial local porque activaste esa opción. + +Seguridad + +La app se basa en el almacenamiento privado de Android para apps y en los controles de permisos de Android. Mantén activados el bloqueo de pantalla del dispositivo y las actualizaciones de seguridad del sistema operativo para proteger los datos locales. + +Menores + +La app no está dirigida a menores y no recopila conscientemente datos personales de nadie. + +Cambios + +Si esta política cambia, la política actualizada se incluirá en la app y en la copia pública de la política de la app. + +Contacto + +Para preguntas de privacidad o solicitudes de eliminación sobre datos que puedan haberse enviado mediante una acción externa que elegiste, contacta con el servicio externo correspondiente. Para preguntas sobre esta app, contacta con SoftwareApp-HB en softwareapp.hb@gmail.com. diff --git a/app/src/main/res/raw-fr/privacy_policy.txt b/app/src/main/res/raw-fr/privacy_policy.txt new file mode 100644 index 0000000..0edb1d2 --- /dev/null +++ b/app/src/main/res/raw-fr/privacy_policy.txt @@ -0,0 +1,57 @@ +Politique de confidentialité +Dernière mise à jour : 2026-05-10 + +Private QR Scanner est fourni par SoftwareApp-HB. +Contact : softwareapp.hb@gmail.com + +Cette politique explique comment Private QR Scanner traite les données sur votre appareil. + +Ce à quoi l’app accède + +Caméra : L’app utilise la caméra uniquement pour scanner les QR codes et les codes-barres. Les images de la caméra sont traitées sur votre appareil et ne sont pas enregistrées par l’app. + +Images sélectionnées : Si vous choisissez de scanner depuis une image, l’app lit uniquement cette image sélectionnée afin de détecter des QR codes ou des codes-barres. L’app ne conserve aucune copie de l’image. + +Contenu scanné : L’app affiche le contenu scanné sur votre appareil. Si vous activez Enregistrer l’historique (local), le contenu scanné, le type de contenu et l’heure du scan sont stockés localement sur votre appareil. L’historique est désactivé par défaut. + +Paramètres : L’app stocke vos préférences locales, par exemple si l’historique, les avertissements de sécurité, le retour de scan et la vue de cas d’utilisation sélectionnée sont activés. + +Données d’événements et de billetterie : Les ID de liste blanche importés et les résultats de scan en lot sont traités localement. Les ID de liste blanche sont conservés en mémoire pour la session actuelle de l’app et ne sont pas téléversés par l’app. + +Utilisation des données + +Les données sont utilisées pour scanner des codes, afficher les résultats, fournir des avertissements de sécurité locaux, prendre en charge l’historique local facultatif, prendre en charge les flux d’événements et de billetterie, et exécuter les actions que vous choisissez. + +Collecte et partage des données + +Private QR Scanner ne crée pas de comptes, n’affiche pas de publicités, n’utilise pas d’analytique, n’utilise pas de SDK de suivi, n’utilise pas de SDK de rapport de plantage et n’envoie pas de données de l’app vers un backend du développeur. L’app ne demande pas l’autorisation Android d’accès à Internet. + +Le développeur ne reçoit, ne collecte, ne vend ni ne partage votre contenu scanné, vos images sélectionnées, votre historique, vos paramètres ou vos données de liste blanche. + +Les données ne peuvent quitter votre appareil que lorsque vous choisissez une action Android ou tierce, comme Ouvrir, Partager, Appeler, SMS, E-mail, Ajouter un contact, Ajouter un événement au calendrier, paramètres Wi-Fi ou avis Google Play. Ces actions sont traitées par Android, Google Play ou l’app ou le service que vous choisissez, et leurs pratiques de confidentialité s’appliquent. + +Stockage, conservation et suppression + +L’historique facultatif des scans et les paramètres sont stockés dans l’espace privé de l’app sur votre appareil. La sauvegarde Android de l’app est désactivée pour cette app. + +L’historique des scans reste sur votre appareil jusqu’à ce que vous supprimiez des entrées individuelles, supprimiez tout l’historique, désactiviez l’historique et choisissiez de supprimer l’historique existant, effaciez les données de l’app ou désinstalliez l’app. + +Les paramètres restent sur votre appareil jusqu’à ce que vous les modifiiez, effaciez les données de l’app ou désinstalliez l’app. + +Les images de la caméra, les images sélectionnées, les ID de liste blanche importés et les résultats de scan en lot ne sont pas conservés par l’app après l’utilisation active ou la fin de la session, sauf si le contenu scanné résultant est enregistré dans l’historique local parce que vous avez activé ce paramètre. + +Sécurité + +L’app repose sur le stockage privé des apps Android et les contrôles d’autorisations Android. Gardez le verrouillage de l’écran de votre appareil et les mises à jour de sécurité du système d’exploitation activés pour protéger les données locales. + +Enfants + +L’app ne s’adresse pas aux enfants et ne collecte pas sciemment de données personnelles auprès de quiconque. + +Modifications + +Si cette politique change, la politique mise à jour sera incluse dans l’app et dans la copie publique de la politique de l’app. + +Contact + +Pour toute question de confidentialité ou demande de suppression concernant des données qui auraient pu être envoyées par une action externe que vous avez choisie, contactez le service externe concerné. Pour toute question sur cette app, contactez SoftwareApp-HB à softwareapp.hb@gmail.com. diff --git a/app/src/main/res/raw-it/privacy_policy.txt b/app/src/main/res/raw-it/privacy_policy.txt new file mode 100644 index 0000000..f9b4065 --- /dev/null +++ b/app/src/main/res/raw-it/privacy_policy.txt @@ -0,0 +1,57 @@ +Informativa sulla privacy +Ultimo aggiornamento: 2026-05-10 + +Private QR Scanner è fornita da SoftwareApp-HB. +Contatto: softwareapp.hb@gmail.com + +Questa informativa spiega come Private QR Scanner gestisce i dati sul tuo dispositivo. + +A cosa accede l’app + +Fotocamera: L’app usa la fotocamera solo per scansionare codici QR e codici a barre. I fotogrammi della fotocamera vengono elaborati sul dispositivo e non vengono salvati dall’app. + +Immagini selezionate: Se scegli di scansionare da un’immagine, l’app legge solo quell’immagine selezionata per rilevare codici QR o codici a barre. L’app non conserva una copia dell’immagine. + +Contenuto scansionato: L’app mostra il contenuto scansionato sul tuo dispositivo. Se abiliti Salva cronologia (locale), il contenuto scansionato, il tipo di contenuto e l’ora della scansione vengono memorizzati localmente sul dispositivo. La cronologia è disattivata per impostazione predefinita. + +Impostazioni: L’app memorizza le tue preferenze locali, ad esempio se sono abilitati cronologia, avvisi di sicurezza, feedback di scansione e la vista caso d’uso selezionata. + +Dati di eventi e ticketing: Gli ID whitelist importati e i risultati delle scansioni batch vengono elaborati localmente. Gli ID whitelist restano in memoria per la sessione corrente dell’app e non vengono caricati dall’app. + +Come vengono usati i dati + +I dati vengono usati per scansionare codici, mostrare i risultati, fornire avvisi di sicurezza locali, supportare la cronologia locale opzionale, supportare flussi di eventi e ticketing ed eseguire le azioni che scegli. + +Raccolta e condivisione dei dati + +Private QR Scanner non crea account, non mostra annunci, non usa analisi, non usa SDK di tracciamento, non usa SDK di crash reporting e non invia dati dell’app a un backend dello sviluppatore. L’app non richiede il permesso Android per Internet. + +Lo sviluppatore non riceve, raccoglie, vende o condivide il tuo contenuto scansionato, le immagini selezionate, la cronologia, le impostazioni o i dati whitelist. + +I dati possono lasciare il tuo dispositivo solo quando scegli un’azione Android o di terze parti, come Apri, Condividi, Chiama, SMS, E-mail, Aggiungi contatto, Aggiungi evento al calendario, impostazioni Wi-Fi o recensione su Google Play. Queste azioni sono gestite da Android, Google Play o dall’app o servizio che scegli, e si applicano le rispettive pratiche sulla privacy. + +Archiviazione, conservazione ed eliminazione + +La cronologia opzionale delle scansioni e le impostazioni vengono memorizzate nello spazio privato dell’app sul tuo dispositivo. Il backup Android dell’app è disabilitato per questa app. + +La cronologia delle scansioni resta sul dispositivo finché non elimini singole voci, elimini tutta la cronologia, disattivi la cronologia e scegli di eliminare la cronologia esistente, cancelli i dati dell’app o disinstalli l’app. + +Le impostazioni restano sul dispositivo finché non le modifichi, cancelli i dati dell’app o disinstalli l’app. + +I fotogrammi della fotocamera, le immagini selezionate, gli ID whitelist importati e i risultati delle scansioni batch non vengono conservati dall’app dopo l’uso attivo o il termine della sessione dell’app, a meno che il contenuto scansionato risultante venga salvato nella cronologia locale perché hai abilitato tale impostazione. + +Sicurezza + +L’app si basa sullo spazio privato delle app Android e sui controlli dei permessi Android. Mantieni attivi il blocco schermo del dispositivo e gli aggiornamenti di sicurezza del sistema operativo per proteggere i dati locali. + +Bambini + +L’app non è rivolta ai bambini e non raccoglie consapevolmente dati personali da nessuno. + +Modifiche + +Se questa informativa cambia, l’informativa aggiornata sarà inclusa nell’app e nella copia pubblica dell’informativa dell’app. + +Contatto + +Per domande sulla privacy o richieste di eliminazione relative a dati che potrebbero essere stati inviati tramite un’azione esterna scelta da te, contatta il servizio esterno pertinente. Per domande su questa app, contatta SoftwareApp-HB all’indirizzo softwareapp.hb@gmail.com. diff --git a/app/src/main/res/raw-ja/privacy_policy.txt b/app/src/main/res/raw-ja/privacy_policy.txt new file mode 100644 index 0000000..53cd8b3 --- /dev/null +++ b/app/src/main/res/raw-ja/privacy_policy.txt @@ -0,0 +1,57 @@ +プライバシーポリシー +最終更新日: 2026-05-10 + +Private QR Scanner は SoftwareApp-HB により提供されています。 +連絡先: softwareapp.hb@gmail.com + +このポリシーでは、Private QR Scanner が端末上のデータをどのように扱うかを説明します。 + +アプリがアクセスするもの + +カメラ: アプリは QR コードとバーコードをスキャンするためにのみカメラを使用します。カメラのフレームは端末上で処理され、アプリによって保存されません。 + +選択した画像: 画像からスキャンすることを選択した場合、アプリは QR コードまたはバーコードを検出するために、その選択した画像のみを読み取ります。アプリは画像のコピーを保持しません。 + +スキャンした内容: アプリはスキャンした内容を端末上に表示します。履歴を保存(ローカル)を有効にすると、スキャンした内容、内容の種類、スキャン時刻が端末上にローカル保存されます。履歴は既定ではオフです。 + +設定: アプリは、履歴、セキュリティ警告、スキャン通知、選択した用途ビューが有効かどうかなど、ローカルの設定を保存します。 + +イベントおよびチケットデータ: インポートしたホワイトリスト ID とバッチスキャン結果はローカルで処理されます。ホワイトリスト ID は現在のアプリセッション中にメモリ内に保持され、アプリによってアップロードされません。 + +データの使用方法 + +データは、コードのスキャン、スキャン結果の表示、ローカルのセキュリティ警告の提供、任意のローカル履歴のサポート、イベントおよびチケット業務のサポート、ユーザーが選択した操作の実行に使用されます。 + +データの収集と共有 + +Private QR Scanner は、アカウントを作成せず、広告を表示せず、分析機能を使用せず、追跡 SDK を使用せず、クラッシュレポート SDK を使用せず、アプリデータを開発者のバックエンドへ送信しません。アプリは Android のインターネット権限を要求しません。 + +開発者は、スキャンした内容、選択した画像、履歴、設定、ホワイトリストデータを受信、収集、販売、共有しません。 + +データが端末外に出るのは、ユーザーが Android または第三者の操作を選択した場合のみです。例として、開く、共有、通話、SMS、メール、連絡先の追加、カレンダー予定の追加、Wi-Fi 設定、Google Play レビューなどがあります。これらの操作は Android、Google Play、またはユーザーが選択したアプリやサービスによって処理され、それぞれのプライバシー慣行が適用されます。 + +保存、保持、削除 + +任意のスキャン履歴と設定は、端末上のアプリ専用ストレージに保存されます。このアプリでは Android のアプリバックアップは無効です。 + +スキャン履歴は、個別の項目を削除する、すべての履歴を削除する、履歴をオフにして既存の履歴の削除を選択する、アプリデータを消去する、またはアプリをアンインストールするまで端末上に残ります。 + +設定は、変更する、アプリデータを消去する、またはアプリをアンインストールするまで端末上に残ります。 + +カメラのフレーム、選択した画像、インポートしたホワイトリスト ID、バッチスキャン結果は、利用中またはアプリセッションの終了後にアプリによって保持されません。ただし、その設定を有効にしたために結果のスキャン内容がローカル履歴に保存される場合を除きます。 + +セキュリティ + +アプリは Android のアプリ専用ストレージと Android の権限制御に依存しています。ローカルデータを保護するため、端末の画面ロックとオペレーティングシステムのセキュリティ更新を有効にしておいてください。 + +子ども + +アプリは子どもを対象としておらず、誰からも個人データを意図的に収集しません。 + +変更 + +このポリシーが変更された場合、更新後のポリシーはアプリおよびアプリの公開ポリシー文書に含まれます。 + +連絡先 + +ユーザーが選択した外部操作を通じて送信された可能性のあるデータに関するプライバシーの質問または削除依頼については、該当する外部サービスにお問い合わせください。このアプリに関する質問は、SoftwareApp-HB(softwareapp.hb@gmail.com)までお問い合わせください。 diff --git a/app/src/main/res/raw-ko/privacy_policy.txt b/app/src/main/res/raw-ko/privacy_policy.txt new file mode 100644 index 0000000..4430edd --- /dev/null +++ b/app/src/main/res/raw-ko/privacy_policy.txt @@ -0,0 +1,57 @@ +개인정보 처리방침 +최종 업데이트: 2026-05-10 + +Private QR Scanner는 SoftwareApp-HB에서 제공합니다. +문의: softwareapp.hb@gmail.com + +이 방침은 Private QR Scanner가 사용자의 기기에서 데이터를 처리하는 방식을 설명합니다. + +앱이 접근하는 항목 + +카메라: 앱은 QR 코드와 바코드를 스캔하는 용도로만 카메라를 사용합니다. 카메라 프레임은 사용자의 기기에서 처리되며 앱에 저장되지 않습니다. + +선택한 이미지: 이미지에서 스캔하기를 선택하면 앱은 QR 코드 또는 바코드를 감지하기 위해 선택한 해당 이미지만 읽습니다. 앱은 이미지 사본을 보관하지 않습니다. + +스캔한 콘텐츠: 앱은 스캔한 콘텐츠를 사용자의 기기에 표시합니다. 기록 저장(로컬)을 사용 설정하면 스캔한 콘텐츠, 콘텐츠 유형, 스캔 시간이 사용자의 기기에 로컬로 저장됩니다. 기록은 기본적으로 꺼져 있습니다. + +설정: 앱은 기록, 보안 경고, 스캔 피드백, 선택한 사용 사례 보기가 사용 설정되었는지와 같은 로컬 환경설정을 저장합니다. + +이벤트 및 티켓 데이터: 가져온 화이트리스트 ID와 일괄 스캔 결과는 로컬에서 처리됩니다. 화이트리스트 ID는 현재 앱 세션 동안 메모리에 보관되며 앱에 의해 업로드되지 않습니다. + +데이터 사용 방식 + +데이터는 코드 스캔, 스캔 결과 표시, 로컬 보안 경고 제공, 선택적 로컬 기록 지원, 이벤트 및 티켓 워크플로 지원, 사용자가 선택한 작업 실행에 사용됩니다. + +데이터 수집 및 공유 + +Private QR Scanner는 계정을 만들지 않고, 광고를 표시하지 않으며, 분석 기능을 사용하지 않고, 추적 SDK를 사용하지 않으며, 충돌 보고 SDK를 사용하지 않고, 앱 데이터를 개발자 백엔드로 보내지 않습니다. 앱은 Android 인터넷 권한을 요청하지 않습니다. + +개발자는 사용자의 스캔 콘텐츠, 선택한 이미지, 기록, 설정 또는 화이트리스트 데이터를 받거나, 수집하거나, 판매하거나, 공유하지 않습니다. + +사용자가 열기, 공유, 전화, SMS, 이메일, 연락처 추가, 캘린더 일정 추가, Wi-Fi 설정 또는 Google Play 리뷰와 같은 Android 또는 타사 작업을 선택한 경우에만 데이터가 기기를 떠날 수 있습니다. 이러한 작업은 Android, Google Play 또는 사용자가 선택한 앱이나 서비스에서 처리되며, 해당 개인정보 처리 관행이 적용됩니다. + +저장, 보관 및 삭제 + +선택적 스캔 기록과 설정은 사용자의 기기 내 앱 전용 저장소에 저장됩니다. 이 앱에서는 Android 앱 백업이 비활성화되어 있습니다. + +스캔 기록은 개별 항목을 삭제하거나, 모든 기록을 삭제하거나, 기록을 끄고 기존 기록 삭제를 선택하거나, 앱 데이터를 지우거나, 앱을 제거할 때까지 사용자의 기기에 남아 있습니다. + +설정은 사용자가 변경하거나, 앱 데이터를 지우거나, 앱을 제거할 때까지 기기에 남아 있습니다. + +카메라 프레임, 선택한 이미지, 가져온 화이트리스트 ID, 일괄 스캔 결과는 활성 사용 또는 앱 세션이 끝난 후 앱에 의해 보관되지 않습니다. 다만 해당 설정을 사용 설정했기 때문에 결과 스캔 콘텐츠가 로컬 기록에 저장되는 경우는 예외입니다. + +보안 + +앱은 Android 앱 전용 저장소와 Android 권한 제어에 의존합니다. 로컬 데이터를 보호하려면 기기 화면 잠금과 운영체제 보안 업데이트를 활성 상태로 유지하세요. + +아동 + +앱은 아동을 대상으로 하지 않으며 누구의 개인정보도 고의로 수집하지 않습니다. + +변경 사항 + +이 방침이 변경되면 업데이트된 방침이 앱과 앱의 공개 정책 사본에 포함됩니다. + +문의 + +사용자가 선택한 외부 작업을 통해 전송되었을 수 있는 데이터에 대한 개인정보 관련 질문이나 삭제 요청은 해당 외부 서비스에 문의하세요. 이 앱에 대한 질문은 SoftwareApp-HB softwareapp.hb@gmail.com으로 문의하세요. diff --git a/app/src/main/res/raw/supported_locales.tsv b/app/src/main/res/raw/supported_locales.tsv new file mode 100644 index 0000000..8cae565 --- /dev/null +++ b/app/src/main/res/raw/supported_locales.tsv @@ -0,0 +1,55 @@ +# Planned locale inventory derived from target_countries.txt. +# Columns: tag, Android resource qualifier, status, native display name, release coverage note. +# Status values: +# - complete: selectable in the app and exported to Android localeConfig. +# - partial: selectable in the app and exported to Android localeConfig, but shown as partial. +# - planned: tracked for target-country coverage, not selectable until translated resources exist. +system system system System default Device language +en values complete English Default fallback and broad international release +de values-de complete Deutsch Germany, Austria, Switzerland, Liechtenstein, Luxembourg +ar values-ar complete العربية Algeria, Bahrain, Egypt, Iraq, Jordan, Kuwait, Lebanon, Libya, Morocco, Oman, Qatar, Saudi Arabia, Tunisia, United Arab Emirates, Yemen +az values-az planned Azərbaycanca Azerbaijan +bg values-bg planned Български Bulgaria +bn values-bn planned বাংলা Bangladesh +cs values-cs planned Čeština Czechia +da values-da planned Dansk Denmark +el values-el planned Ελληνικά Cyprus, Greece +es values-es complete Español Argentina, Bolivia, Chile, Colombia, Costa Rica, Dominican Republic, Ecuador, El Salvador, Guatemala, Honduras, Mexico, Nicaragua, Panama, Paraguay, Peru, Spain, Uruguay, Venezuela +et values-et planned Eesti Estonia +fa values-fa planned فارسی Iran +fi values-fi planned Suomi Finland +fr values-fr complete Français Belgium, Benin, Burkina Faso, Cameroon, Canada, Chad, Comoros, Congo, Côte d'Ivoire, Djibouti, France, Gabon, Guinea, Luxembourg, Mali, Monaco, Niger, Rwanda, Senegal, Seychelles, Switzerland, Togo +he values-b+he planned עברית Israel +hi values-hi planned हिन्दी India +hr values-hr planned Hrvatski Bosnia & Herzegovina, Croatia +hu values-hu planned Magyar Hungary +hy values-hy planned Հայերեն Armenia +id values-b+id planned Bahasa Indonesia Indonesia +it values-it complete Italiano Italy, San Marino, Switzerland, Vatican City +ja values-ja complete 日本語 Japan +ka values-ka planned ქართული Georgia +kk values-kk planned Қазақша Kazakhstan +ko values-ko complete 한국어 South Korea +lt values-lt planned Lietuvių Lithuania +lv values-lv planned Latviešu Latvia +ms values-ms planned Bahasa Melayu Malaysia +nb values-nb planned Norsk bokmål Norway +nl values-nl planned Nederlands Aruba, Belgium, Netherlands, Suriname +pl values-pl planned Polski Poland +pt-BR values-b+pt+BR complete Português (Brasil) Brazil +pt-PT values-b+pt+PT complete Português (Portugal) Angola, Cape Verde, Mozambique, Portugal +ro values-ro planned Română Moldova, Romania +ru values-ru planned Русский Belarus, Kazakhstan, Kyrgyzstan, Russia +sk values-sk planned Slovenčina Slovakia +sl values-sl planned Slovenščina Slovenia +sq values-sq planned Shqip Albania +sr values-sr planned Српски Serbia, Bosnia & Herzegovina +sv values-sv planned Svenska Sweden +sw values-sw planned Kiswahili Kenya, Tanzania, Uganda +th values-th planned ไทย Thailand +tr values-tr planned Türkçe Türkiye +uk values-uk planned Українська Ukraine +ur values-ur planned اردو Pakistan +vi values-vi planned Tiếng Việt Vietnam +zh-CN values-b+zh+Hans complete 简体中文 China, Singapore +zh-TW values-b+zh+Hant complete 繁體中文 Hong Kong, Macao, Taiwan diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000..ac02315 --- /dev/null +++ b/app/src/main/res/values-ar/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + مسح + المسح مرة أخرى + السجل + الإعدادات + لغة التطبيق + افتراضي النظام + ترجمة جزئية + تغيير + حفظ السجل (محلي) + الخصوصية + سياسة الخصوصية + معالجة محلية، بدون إعلانات وبدون تتبع. راجع سياسة الخصوصية للتفاصيل. + تحذيرات الأمان + تنبيه المسح (صوت + اهتزاز) + حول + نسخ + مشاركة + فتح + إلغاء + فتح على أي حال + يبدو عنوان URL هذا غير معتاد + تحقق من الرابط قبل فتحه. يتم حساب التحذير على جهازك دون إرسال نتيجة المسح إلى أي مكان. + حذف الكل + هل تريد حذف كل عناصر السجل؟ + تأكيد + إغلاق + بحث + المصباح + مطلوب الوصول إلى الكاميرا + إذن الكاميرا مطلوب لمسح رموز QR والباركود. + فتح الإعدادات + لا يوجد إذن للكاميرا + هل تريد حذف السجل الحالي عند التعطيل؟ + الإصدار 1.0.0 + تراخيص مفتوحة المصدر + التواصل: softwareapp.hb@gmail.com + النوع + المحتوى + يتم العرض بصيغة Base64 لأن البيانات الممسوحة لا يمكن عرضها كنص. + مشفر بصيغة Base64: %1$s + السماح بالكاميرا + قرّب بإصبعيك لتكبير الرموز الصغيرة + وجّه الرمز داخل الإطار الأوسط. + تم رصد رمز. ثبّت الجهاز أو اقترب. + تم اكتشاف رمز قابل للقراءة. + مشاركة السجل + TXT + CSV + JSON + يبقى السجل الاختياري على جهازك + راجع عمليات المسح السابقة محليًا + ابحث أو صدّر أو احذف عمليات المسح المحفوظة متى شئت. + بدون إعلانات + بدون تتبع + بدون حساب + عناصر تحكم محلية + لا توجد عمليات مسح محفوظة بعد + فعّل السجل المحلي في الإعدادات للاحتفاظ بسجل خاص على هذا الجهاز. + مسح من صورة + استيراد القائمة البيضاء + المعرّفات المسجلة المحمّلة: %1$d + تم استيراد %1$d معرّفات. + تعذر استيراد المعرّفات من هذا الملف. + وضع الدُفعات + عمليات مسح الدُفعة: %1$d + مسح الدُفعة + مشاركة الدُفعة + لم يتم العثور على رمز QR أو باركود في الصورة المحددة. + تم العثور على %1$d رموز في الصورة + اختر النتيجة التي تريد استخدامها. تتم عملية الكشف والتحليل محليًا. + استخدام المحدد + تعذرت قراءة هذه الصورة. جرّب صورة أخرى. + تم مسحه مسبقًا + تم اكتشاف تذكرة مكررة + تم مسح هذه التذكرة/هذا الرمز من قبل. تحقق من الدخول فورًا لمنع الاحتيال. + الرمز: %1$s + تم اكتشاف تذكرة غير مسجلة + هذه التذكرة/هذا الرمز غير موجود في القائمة البيضاء المستوردة. تحقق من تسجيل الحاضر. + الرمز: %1$s + عرض السجل + الاتصال بالرقم + إرسال SMS + إرسال بريد إلكتروني + فتح إعدادات Wi-Fi + إضافة جهة اتصال + إضافة حدث إلى التقويم + تقييم على Google Play + عرض حالة الاستخدام النشط + اختيار عرض حالة الاستخدام + استخدام شخصي يومي + الفعاليات والتذاكر + ماسح شخصي كامل مع سجل محلي وإجراءات نتائج شائعة. + مسح دُفعي، واكتشاف التكرارات، واستيراد القائمة البيضاء، ومشاركة الدُفعات. + إعدادات تمنحك التحكم + إعدادات الخصوصية لك + اختر السجل المحلي، وتحذيرات الأمان، والتنبيهات، وعرض الماسح المناسب لسير عملك. + فحص محلي + تم الفحص على الجهاز قبل الفتح + بيانات مشفرة + تبقى تنبيهات التذاكر المكررة وغير المسجلة على الجهاز. + diff --git a/app/src/main/res/values-b+pt+BR/strings.xml b/app/src/main/res/values-b+pt+BR/strings.xml new file mode 100644 index 0000000..e150338 --- /dev/null +++ b/app/src/main/res/values-b+pt+BR/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + Escanear + Escanear novamente + Histórico + Configurações + Idioma do app + Padrão do sistema + Tradução parcial + Alterar + Salvar histórico (local) + Privacidade + Política de Privacidade + Processamento local, sem anúncios e sem rastreamento. Consulte a política de privacidade para mais detalhes. + Avisos de segurança + Feedback da leitura (bipe + vibração) + Sobre + Copiar + Compartilhar + Abrir + Cancelar + Abrir mesmo assim + Esta URL parece incomum + Confira o link antes de abrir. O aviso é calculado no seu dispositivo, sem enviar a leitura para nenhum lugar. + Excluir tudo + Excluir todas as entradas do histórico? + Confirmar + Fechar + Pesquisar + Lanterna + Acesso à câmera necessário + A permissão da câmera é necessária para escanear códigos QR e de barras. + Abrir configurações + Sem permissão da câmera + Excluir o histórico existente ao desativar? + Versão 1.0.0 + Licenças de código aberto + Contato: softwareapp.hb@gmail.com + Tipo + Conteúdo + Exibido como Base64 porque os dados escaneados não podem ser mostrados como texto. + Codificado em Base64: %1$s + Permitir câmera + Faça pinça para ampliar códigos pequenos + Posicione o código dentro do quadro central. + Código encontrado. Mantenha firme ou aproxime-se. + Código legível detectado. + Compartilhar histórico + TXT + CSV + JSON + O histórico opcional fica no seu dispositivo + Revise leituras anteriores localmente + Pesquise, exporte ou exclua leituras salvas quando quiser. + Sem anúncios + Sem rastreamento + Sem conta + Controles locais + Nenhuma leitura salva ainda + Ative o histórico local nas configurações para manter um registro privado neste dispositivo. + Escanear de imagem + Importar lista permitida + IDs registrados carregados: %1$d + %1$d IDs importados. + Não foi possível importar IDs deste arquivo. + Modo em lote + Leituras em lote: %1$d + Limpar lote + Compartilhar lote + Nenhum QR code ou código de barras encontrado na imagem selecionada. + %1$d códigos encontrados na imagem + Escolha o resultado que deseja usar. A detecção e a análise acontecem localmente. + Usar selecionado + Não foi possível ler esta imagem. Tente outra. + Já escaneado + Ingresso duplicado detectado + Este ingresso/código já foi escaneado. Verifique a entrada imediatamente para evitar fraude. + Código: %1$s + Ingresso não registrado detectado + Este ingresso/código não está na lista permitida importada. Verifique o registro do participante. + Código: %1$s + Ver histórico + Ligar para número + Enviar SMS + Enviar e-mail + Abrir configurações de Wi-Fi + Adicionar contato + Adicionar evento ao calendário + Avaliar no Google Play + Visualização de caso de uso ativa + Selecionar visualização de caso de uso + Uso pessoal diário + Eventos e ingressos + Scanner pessoal completo com histórico local e ações comuns de resultado. + Leitura em lote, detecção de duplicados, importação de lista permitida e compartilhamento em lote. + Configurações que deixam você no controle + As configurações de privacidade são suas + Escolha histórico local, avisos de segurança, feedback e a visualização do scanner que se adapta ao seu fluxo de trabalho. + Verificação local + Verificado no dispositivo antes de abrir + Dados codificados + Alertas de ingressos duplicados e não registrados ficam no dispositivo. + diff --git a/app/src/main/res/values-b+pt+PT/strings.xml b/app/src/main/res/values-b+pt+PT/strings.xml new file mode 100644 index 0000000..1da2f06 --- /dev/null +++ b/app/src/main/res/values-b+pt+PT/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + Digitalizar + Digitalizar novamente + Histórico + Definições + Idioma da app + Predefinição do sistema + Tradução parcial + Alterar + Guardar histórico (local) + Privacidade + Política de Privacidade + Processamento local, sem anúncios e sem rastreio. Consulte a política de privacidade para mais detalhes. + Avisos de segurança + Feedback da digitalização (bip + vibração) + Sobre + Copiar + Partilhar + Abrir + Cancelar + Abrir na mesma + Este URL parece invulgar + Verifique a ligação antes de a abrir. O aviso é calculado no seu dispositivo, sem enviar a digitalização para qualquer lado. + Eliminar tudo + Eliminar todas as entradas do histórico? + Confirmar + Fechar + Pesquisar + Lanterna + Acesso à câmara necessário + A permissão da câmara é necessária para digitalizar códigos QR e códigos de barras. + Abrir definições + Sem permissão da câmara + Eliminar o histórico existente ao desativar? + Versão 1.0.0 + Licenças open source + Contacto: softwareapp.hb@gmail.com + Tipo + Conteúdo + Apresentado como Base64 porque os dados digitalizados não podem ser mostrados como texto. + Codificado em Base64: %1$s + Permitir câmara + Aproxime os dedos para ampliar códigos pequenos + Aponte o código para dentro da moldura central. + Código detetado. Mantenha estável ou aproxime-se. + Código legível detetado. + Partilhar histórico + TXT + CSV + JSON + O histórico opcional fica no seu dispositivo + Reveja digitalizações anteriores localmente + Pesquise, exporte ou elimine digitalizações guardadas quando quiser. + Sem anúncios + Sem rastreio + Sem conta + Controlos locais + Ainda não há digitalizações guardadas + Ative o histórico local nas definições para manter um registo privado neste dispositivo. + Digitalizar de imagem + Importar lista permitida + IDs registados carregados: %1$d + %1$d IDs importados. + Não foi possível importar IDs deste ficheiro. + Modo em lote + Digitalizações em lote: %1$d + Limpar lote + Partilhar lote + Nenhum código QR ou código de barras encontrado na imagem selecionada. + %1$d códigos encontrados na imagem + Escolha o resultado que pretende usar. A deteção e a análise acontecem localmente. + Usar selecionado + Não foi possível ler esta imagem. Tente outra. + Já digitalizado + Bilhete duplicado detetado + Este bilhete/código já foi digitalizado. Verifique a entrada imediatamente para evitar fraude. + Código: %1$s + Bilhete não registado detetado + Este bilhete/código não está na lista permitida importada. Verifique o registo do participante. + Código: %1$s + Ver histórico + Ligar para número + Enviar SMS + Enviar e-mail + Abrir definições de Wi-Fi + Adicionar contacto + Adicionar evento ao calendário + Avaliar no Google Play + Vista de caso de uso ativa + Selecionar vista de caso de uso + Uso pessoal diário + Eventos e bilhética + Scanner pessoal completo com histórico local e ações comuns de resultados. + Digitalização em lote, deteção de duplicados, importação de lista permitida e partilha em lote. + Definições que lhe dão controlo + As definições de privacidade são suas + Escolha histórico local, avisos de segurança, feedback e a vista do scanner adequada ao seu fluxo de trabalho. + Verificação local + Verificado no dispositivo antes de abrir + Dados codificados + Os alertas de bilhetes duplicados e não registados ficam no dispositivo. + diff --git a/app/src/main/res/values-b+zh+Hans/strings.xml b/app/src/main/res/values-b+zh+Hans/strings.xml new file mode 100644 index 0000000..7e7f53b --- /dev/null +++ b/app/src/main/res/values-b+zh+Hans/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + 扫描 + 再次扫描 + 历史记录 + 设置 + 应用语言 + 系统默认 + 部分翻译 + 更改 + 保存历史记录(本地) + 隐私 + 隐私政策 + 本地处理,无广告,无跟踪。详情请参阅隐私政策。 + 安全警告 + 扫描反馈(提示音 + 震动) + 关于 + 复制 + 分享 + 打开 + 取消 + 仍然打开 + 此网址看起来不寻常 + 打开前请检查链接。警告在你的设备上计算,不会将扫描内容发送到任何地方。 + 全部删除 + 删除所有历史记录条目? + 确认 + 关闭 + 搜索 + 手电筒 + 需要相机访问权限 + 需要相机权限才能扫描二维码和条形码。 + 打开设置 + 没有相机权限 + 停用时删除现有历史记录? + 版本 1.0.0 + 开源许可证 + 联系:softwareapp.hb@gmail.com + 类型 + 内容 + 由于扫描数据无法显示为文本,因此以 Base64 显示。 + Base64 编码:%1$s + 允许相机 + 捏合缩放以扫描小码 + 将代码对准中央框内。 + 发现代码。请保持稳定或靠近一些。 + 检测到可读取的代码。 + 分享历史记录 + TXT + CSV + JSON + 可选历史记录保留在你的设备上 + 在本地查看过去的扫描 + 随时搜索、导出或删除已保存的扫描。 + 无广告 + 无跟踪 + 无需账号 + 本地控制 + 还没有保存的扫描 + 在设置中启用本地历史记录,以在此设备上保留私密记录。 + 从图片扫描 + 导入白名单 + 已加载注册 ID:%1$d + 已导入 %1$d 个 ID。 + 无法从此文件导入 ID。 + 批量模式 + 批量扫描:%1$d + 清空批次 + 分享批次 + 在所选图片中未找到二维码或条形码。 + 在图片中找到 %1$d 个代码 + 选择要使用的结果。检测和解析均在本地完成。 + 使用所选项 + 无法读取此图片。请尝试另一张。 + 已扫描 + 检测到重复票券 + 此票券/代码以前已扫描。请立即核验入场以防止欺诈。 + 代码:%1$s + 检测到未注册票券 + 此票券/代码不在导入的白名单中。请核验参加者注册信息。 + 代码:%1$s + 查看历史记录 + 拨打号码 + 发送短信 + 发送电子邮件 + 打开 Wi-Fi 设置 + 添加联系人 + 添加日历事件 + 在 Google Play 上评价 + 当前使用场景视图 + 选择使用场景视图 + 日常个人使用 + 活动与票务 + 完整的个人扫描器,提供本地历史记录和常用结果操作。 + 批量扫描、重复检测、白名单导入和批量分享。 + 让你掌控的设置 + 隐私设置由你决定 + 选择本地历史记录、安全警告、反馈,以及适合你工作流程的扫描器视图。 + 本地检查 + 打开前已在设备上检查 + 编码数据 + 重复和未注册票券提醒会保留在设备上。 + diff --git a/app/src/main/res/values-b+zh+Hant/strings.xml b/app/src/main/res/values-b+zh+Hant/strings.xml new file mode 100644 index 0000000..f0c3f87 --- /dev/null +++ b/app/src/main/res/values-b+zh+Hant/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + 掃描 + 再次掃描 + 記錄 + 設定 + App 語言 + 系統預設 + 部分翻譯 + 變更 + 儲存記錄(本機) + 私隱 + 私隱政策 + 本機處理,無廣告,無追蹤。詳情請參閱私隱政策。 + 安全警告 + 掃描回饋(提示音 + 震動) + 關於 + 複製 + 分享 + 開啟 + 取消 + 仍要開啟 + 此網址看起來不尋常 + 開啟前請檢查連結。警告會在你的裝置上計算,不會將掃描內容傳送到任何地方。 + 全部刪除 + 刪除所有記錄項目? + 確認 + 關閉 + 搜尋 + 手電筒 + 需要相機存取權限 + 需要相機權限才能掃描 QR code 和條碼。 + 開啟設定 + 沒有相機權限 + 停用時刪除現有記錄? + 版本 1.0.0 + 開源授權 + 聯絡:softwareapp.hb@gmail.com + 類型 + 內容 + 由於掃描資料無法顯示為文字,因此以 Base64 顯示。 + Base64 編碼:%1$s + 允許相機 + 捏合縮放以掃描較小的代碼 + 將代碼對準中央框內。 + 發現代碼。請保持穩定或靠近一些。 + 偵測到可讀取的代碼。 + 分享記錄 + TXT + CSV + JSON + 可選記錄會保留在你的裝置上 + 在本機查看過去的掃描 + 隨時搜尋、匯出或刪除已儲存的掃描。 + 無廣告 + 無追蹤 + 無需帳戶 + 本機控制 + 尚未有已儲存的掃描 + 在設定中啟用本機記錄,以在此裝置上保留私人記錄。 + 從圖片掃描 + 匯入白名單 + 已載入註冊 ID:%1$d + 已匯入 %1$d 個 ID。 + 無法從此檔案匯入 ID。 + 批次模式 + 批次掃描:%1$d + 清除批次 + 分享批次 + 在所選圖片中找不到 QR code 或條碼。 + 在圖片中找到 %1$d 個代碼 + 選擇要使用的結果。偵測和解析都會在本機完成。 + 使用所選項目 + 無法讀取此圖片。請嘗試另一張。 + 已掃描 + 偵測到重複票券 + 此票券/代碼之前已掃描。請立即核驗入場以防止欺詐。 + 代碼:%1$s + 偵測到未註冊票券 + 此票券/代碼不在匯入的白名單中。請核驗參加者註冊資料。 + 代碼:%1$s + 查看記錄 + 撥打號碼 + 傳送 SMS + 傳送電郵 + 開啟 Wi-Fi 設定 + 新增聯絡人 + 新增日曆事件 + 在 Google Play 評價 + 目前使用情境視圖 + 選擇使用情境視圖 + 日常個人使用 + 活動與票務 + 完整個人掃描器,提供本機記錄和常用結果操作。 + 批次掃描、重複偵測、白名單匯入和批次分享。 + 讓你掌控的設定 + 私隱設定由你決定 + 選擇本機記錄、安全警告、回饋,以及適合你工作流程的掃描器視圖。 + 本機檢查 + 開啟前已在裝置上檢查 + 編碼資料 + 重複和未註冊票券提示會保留在裝置上。 + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 96118d0..876ec3e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -4,6 +4,10 @@ Nochmal scannen Historie Einstellungen + App-Sprache + Systemstandard + Teilweise übersetzt + Ändern Historie speichern (lokal) Datenschutz Datenschutzerklärung diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..545d35c --- /dev/null +++ b/app/src/main/res/values-es/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + Escanear + Escanear de nuevo + Historial + Ajustes + Idioma de la app + Predeterminado del sistema + Traducción parcial + Cambiar + Guardar historial (local) + Privacidad + Política de privacidad + Procesamiento local, sin anuncios ni seguimiento. Consulta la política de privacidad para más detalles. + Advertencias de seguridad + Feedback del escaneo (sonido + vibración) + Acerca de + Copiar + Compartir + Abrir + Cancelar + Abrir de todos modos + Esta URL parece inusual + Revisa el enlace antes de abrirlo. La advertencia se calcula en tu dispositivo, sin enviar el escaneo a ningún lugar. + Eliminar todo + ¿Eliminar todas las entradas del historial? + Confirmar + Cerrar + Buscar + Linterna + Se necesita acceso a la cámara + Se necesita permiso de cámara para escanear códigos QR y de barras. + Abrir ajustes + Sin permiso de cámara + ¿Eliminar el historial existente al desactivarlo? + Versión 1.0.0 + Licencias de código abierto + Contacto: softwareapp.hb@gmail.com + Tipo + Contenido + Se muestra como Base64 porque los datos escaneados no se pueden mostrar como texto. + Codificado en Base64: %1$s + Permitir cámara + Pellizca para ampliar códigos pequeños + Apunta el código dentro del marco central. + Código detectado. Mantén el dispositivo estable o acércate. + Código legible detectado. + Compartir historial + TXT + CSV + JSON + El historial opcional permanece en tu dispositivo + Revisa escaneos anteriores localmente + Busca, exporta o elimina escaneos guardados cuando quieras. + Sin anuncios + Sin seguimiento + Sin cuenta + Controles locales + Aún no hay escaneos guardados + Activa el historial local en los ajustes para conservar un registro privado en este dispositivo. + Escanear desde imagen + Importar lista blanca + ID registrados cargados: %1$d + Se importaron %1$d ID. + No se pudieron importar ID de este archivo. + Modo por lotes + Capturas por lotes: %1$d + Borrar lote + Compartir lote + No se encontró ningún QR ni código de barras en la imagen seleccionada. + Se encontraron %1$d códigos en la imagen + Elige el resultado que quieres usar. La detección y el análisis se realizan localmente. + Usar seleccionado + No se pudo leer esta imagen. Prueba con otra. + Ya escaneado + Ticket duplicado detectado + Este ticket/código ya fue escaneado. Verifica la entrada de inmediato para evitar fraude. + Código: %1$s + Ticket no registrado detectado + Este ticket/código no está en la lista blanca importada. Verifica el registro del asistente. + Código: %1$s + Ver historial + Llamar al número + Enviar SMS + Enviar correo + Abrir ajustes de Wi-Fi + Añadir contacto + Añadir evento al calendario + Valorar en Google Play + Vista de caso de uso activa + Seleccionar vista de caso de uso + Uso personal diario + Eventos y ticketing + Escáner personal completo con historial local y acciones comunes de resultados. + Escaneo por lotes, detección de duplicados, importación de lista blanca y uso compartido por lotes. + Ajustes que te mantienen en control + La privacidad está en tus manos + Elige historial local, advertencias de seguridad, feedback y la vista del escáner que encaja con tu flujo de trabajo. + Comprobación local + Comprobado en el dispositivo antes de abrir + Datos codificados + Las alertas de tickets duplicados y no registrados permanecen en el dispositivo. + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000..9a235c6 --- /dev/null +++ b/app/src/main/res/values-fr/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + Scanner + Scanner à nouveau + Historique + Paramètres + Langue de l’application + Langue du système + Traduction partielle + Modifier + Enregistrer l’historique (local) + Confidentialité + Politique de confidentialité + Traitement local, sans publicité ni suivi. Consultez la politique de confidentialité pour plus de détails. + Avertissements de sécurité + Retour de scan (bip + vibration) + À propos + Copier + Partager + Ouvrir + Annuler + Ouvrir quand même + Cette URL semble inhabituelle + Vérifiez le lien avant de l’ouvrir. L’avertissement est calculé sur votre appareil, sans envoyer le scan ailleurs. + Tout supprimer + Supprimer toutes les entrées de l’historique ? + Confirmer + Fermer + Rechercher + Lampe torche + Accès à la caméra requis + L’autorisation de la caméra est nécessaire pour scanner les QR codes et codes-barres. + Ouvrir les paramètres + Aucune autorisation de caméra + Supprimer l’historique existant lors de la désactivation ? + Version 1.0.0 + Licences open source + Contact : softwareapp.hb@gmail.com + Type + Contenu + Affiché en Base64, car les données scannées ne peuvent pas être affichées sous forme de texte. + Encodé en Base64 : %1$s + Autoriser la caméra + Pincez pour zoomer sur les petits codes + Placez le code dans le cadre central. + Code repéré. Restez stable ou rapprochez-vous. + Code lisible détecté. + Partager l’historique + TXT + CSV + JSON + L’historique facultatif reste sur votre appareil + Consulter les scans passés localement + Recherchez, exportez ou supprimez les scans enregistrés quand vous le souhaitez. + Sans publicité + Sans suivi + Sans compte + Contrôles locaux + Aucun scan enregistré pour le moment + Activez l’historique local dans les paramètres pour conserver un registre privé sur cet appareil. + Scanner depuis une image + Importer une liste blanche + ID enregistrés chargés : %1$d + %1$d ID importés. + Impossible d’importer des ID depuis ce fichier. + Mode lot + Scans en lot : %1$d + Effacer le lot + Partager le lot + Aucun QR code ni code-barres trouvé dans l’image sélectionnée. + %1$d codes trouvés dans l’image + Choisissez le résultat à utiliser. La détection et l’analyse se font localement. + Utiliser la sélection + Impossible de lire cette image. Essayez-en une autre. + Déjà scanné + Ticket en double détecté + Ce ticket/code a déjà été scanné. Vérifiez immédiatement l’entrée pour éviter la fraude. + Code : %1$s + Ticket non enregistré détecté + Ce ticket/code ne figure pas dans la liste blanche importée. Vérifiez l’inscription du participant. + Code : %1$s + Voir l’historique + Appeler le numéro + Envoyer un SMS + Envoyer un e-mail + Ouvrir les paramètres Wi-Fi + Ajouter un contact + Ajouter un événement au calendrier + Noter sur Google Play + Vue de cas d’utilisation active + Sélectionner une vue de cas d’utilisation + Usage personnel quotidien + Événements et billetterie + Scanner personnel complet avec historique local et actions de résultat courantes. + Scan en lot, détection des doublons, importation de liste blanche et partage de lots. + Des paramètres qui vous gardent aux commandes + Vos paramètres de confidentialité + Choisissez l’historique local, les avertissements de sécurité, le retour de scan et la vue adaptée à votre flux de travail. + Vérification locale + Vérifié sur l’appareil avant ouverture + Données encodées + Les alertes de tickets en double et non enregistrés restent sur l’appareil. + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml new file mode 100644 index 0000000..33d0cac --- /dev/null +++ b/app/src/main/res/values-it/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + Scansiona + Scansiona di nuovo + Cronologia + Impostazioni + Lingua dell’app + Predefinita di sistema + Traduzione parziale + Cambia + Salva cronologia (locale) + Privacy + Informativa sulla privacy + Elaborazione locale, senza annunci né tracciamento. Consulta l’informativa sulla privacy per i dettagli. + Avvisi di sicurezza + Feedback scansione (bip + vibrazione) + Informazioni + Copia + Condividi + Apri + Annulla + Apri comunque + Questo URL sembra insolito + Controlla il link prima di aprirlo. L’avviso viene calcolato sul dispositivo, senza inviare la scansione altrove. + Elimina tutto + Eliminare tutte le voci della cronologia? + Conferma + Chiudi + Cerca + Torcia + Accesso alla fotocamera richiesto + Il permesso della fotocamera è necessario per scansionare codici QR e codici a barre. + Apri impostazioni + Nessun permesso fotocamera + Eliminare la cronologia esistente alla disattivazione? + Versione 1.0.0 + Licenze open source + Contatto: softwareapp.hb@gmail.com + Tipo + Contenuto + Mostrato come Base64 perché i dati scansionati non possono essere visualizzati come testo. + Codificato in Base64: %1$s + Consenti fotocamera + Pizzica per ingrandire i codici piccoli + Allinea il codice dentro il riquadro centrale. + Codice individuato. Tieni fermo o avvicinati. + Codice leggibile rilevato. + Condividi cronologia + TXT + CSV + JSON + La cronologia opzionale resta sul tuo dispositivo + Rivedi localmente le scansioni passate + Cerca, esporta o elimina le scansioni salvate quando vuoi. + Nessun annuncio + Nessun tracciamento + Nessun account + Controlli locali + Nessuna scansione salvata + Attiva la cronologia locale nelle impostazioni per conservare un registro privato su questo dispositivo. + Scansiona da immagine + Importa whitelist + ID registrati caricati: %1$d + Importati %1$d ID. + Impossibile importare ID da questo file. + Modalità batch + Scansioni batch: %1$d + Svuota batch + Condividi batch + Nessun codice QR o codice a barre trovato nell’immagine selezionata. + Trovati %1$d codici nell’immagine + Scegli il risultato da usare. Rilevamento e analisi avvengono localmente. + Usa selezionato + Impossibile leggere questa immagine. Provane un’altra. + Già scansionato + Ticket duplicato rilevato + Questo ticket/codice è già stato scansionato. Verifica subito l’ingresso per prevenire frodi. + Codice: %1$s + Ticket non registrato rilevato + Questo ticket/codice non è nella whitelist importata. Verifica la registrazione del partecipante. + Codice: %1$s + Vedi cronologia + Chiama numero + Invia SMS + Invia e-mail + Apri impostazioni Wi-Fi + Aggiungi contatto + Aggiungi evento al calendario + Recensisci su Google Play + Vista caso d’uso attiva + Seleziona vista caso d’uso + Uso personale quotidiano + Eventi e ticketing + Scanner personale completo con cronologia locale e azioni comuni sui risultati. + Scansione batch, rilevamento duplicati, importazione whitelist e condivisione batch. + Impostazioni che ti lasciano il controllo + Le impostazioni privacy sono tue + Scegli cronologia locale, avvisi di sicurezza, feedback e la vista scanner adatta al tuo flusso di lavoro. + Controllo locale + Controllato sul dispositivo prima dell’apertura + Dati codificati + Gli avvisi su ticket duplicati e non registrati restano sul dispositivo. + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000..3179f9a --- /dev/null +++ b/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + スキャン + もう一度スキャン + 履歴 + 設定 + アプリの言語 + システムの既定 + 部分翻訳 + 変更 + 履歴を保存(ローカル) + プライバシー + プライバシーポリシー + ローカル処理、広告なし、追跡なし。詳細はプライバシーポリシーをご確認ください。 + セキュリティ警告 + スキャン通知(ビープ音 + 振動) + このアプリについて + コピー + 共有 + 開く + キャンセル + それでも開く + このURLは通常と異なるようです + 開く前にリンクを確認してください。警告は端末上で計算され、スキャン内容はどこにも送信されません。 + すべて削除 + すべての履歴項目を削除しますか? + 確認 + 閉じる + 検索 + ライト + カメラへのアクセスが必要です + QRコードとバーコードをスキャンするにはカメラ権限が必要です。 + 設定を開く + カメラ権限がありません + 無効にするときに既存の履歴を削除しますか? + バージョン 1.0.0 + オープンソースライセンス + 連絡先: softwareapp.hb@gmail.com + 種類 + 内容 + スキャンしたデータをテキストとして表示できないため、Base64として表示しています。 + Base64エンコード: %1$s + カメラを許可 + 小さなコードはピンチでズーム + コードを中央の枠内に合わせてください。 + コードを検出しました。端末を安定させるか、近づけてください。 + 読み取り可能なコードを検出しました。 + 履歴を共有 + TXT + CSV + JSON + 任意の履歴は端末内に残ります + 過去のスキャンをローカルで確認 + 保存したスキャンをいつでも検索、エクスポート、削除できます。 + 広告なし + 追跡なし + アカウント不要 + ローカル管理 + 保存されたスキャンはまだありません + 設定でローカル履歴を有効にすると、この端末に非公開の記録を保存できます。 + 画像からスキャン + ホワイトリストをインポート + 読み込まれた登録済みID: %1$d + %1$d件のIDをインポートしました。 + このファイルからIDをインポートできませんでした。 + バッチモード + バッチスキャン: %1$d + バッチをクリア + バッチを共有 + 選択した画像にQRコードまたはバーコードが見つかりませんでした。 + 画像内に%1$d個のコードが見つかりました + 使用する結果を選択してください。検出と解析はローカルで行われます。 + 選択したものを使用 + この画像を読み取れませんでした。別の画像を試してください。 + スキャン済み + 重複チケットを検出しました + このチケット/コードは以前にスキャンされています。不正防止のため、すぐに入場を確認してください。 + コード: %1$s + 未登録チケットを検出しました + このチケット/コードはインポートしたホワイトリストにありません。参加者登録を確認してください。 + コード: %1$s + 履歴を表示 + 番号に発信 + SMSを送信 + メールを送信 + Wi-Fi設定を開く + 連絡先を追加 + カレンダー予定を追加 + Google Playでレビュー + 有効な用途ビュー + 用途ビューを選択 + 日常の個人利用 + イベントとチケット + ローカル履歴と一般的な結果アクションを備えた個人用スキャナー。 + バッチスキャン、重複検出、ホワイトリストインポート、バッチ共有。 + 自分で管理できる設定 + プライバシー設定はあなたのものです + ローカル履歴、セキュリティ警告、通知、作業に合ったスキャナービューを選択できます。 + ローカル確認 + 開く前に端末上で確認済み + エンコード済みデータ + 重複チケットと未登録チケットの警告は端末内に残ります。 + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000..559eea9 --- /dev/null +++ b/app/src/main/res/values-ko/strings.xml @@ -0,0 +1,103 @@ + + Private QR Scanner + 스캔 + 다시 스캔 + 기록 + 설정 + 앱 언어 + 시스템 기본값 + 부분 번역 + 변경 + 기록 저장(로컬) + 개인정보 + 개인정보 처리방침 + 로컬 처리, 광고 없음, 추적 없음. 자세한 내용은 개인정보 처리방침을 확인하세요. + 보안 경고 + 스캔 피드백(비프음 + 진동) + 정보 + 복사 + 공유 + 열기 + 취소 + 그래도 열기 + 이 URL이 평소와 달라 보입니다 + 열기 전에 링크를 확인하세요. 경고는 스캔 내용을 어디에도 보내지 않고 기기에서 계산됩니다. + 모두 삭제 + 모든 기록 항목을 삭제할까요? + 확인 + 닫기 + 검색 + 손전등 + 카메라 접근 필요 + QR 코드와 바코드를 스캔하려면 카메라 권한이 필요합니다. + 설정 열기 + 카메라 권한 없음 + 비활성화할 때 기존 기록을 삭제할까요? + 버전 1.0.0 + 오픈 소스 라이선스 + 문의: softwareapp.hb@gmail.com + 유형 + 내용 + 스캔한 데이터를 텍스트로 표시할 수 없어 Base64로 표시됩니다. + Base64 인코딩: %1$s + 카메라 허용 + 작은 코드는 손가락으로 확대하세요 + 코드를 중앙 프레임 안에 맞추세요. + 코드를 찾았습니다. 기기를 고정하거나 더 가까이 이동하세요. + 읽을 수 있는 코드가 감지되었습니다. + 기록 공유 + TXT + CSV + JSON + 선택적 기록은 기기에 남습니다 + 지난 스캔을 로컬에서 확인 + 저장된 스캔을 언제든 검색, 내보내기 또는 삭제할 수 있습니다. + 광고 없음 + 추적 없음 + 계정 없음 + 로컬 제어 + 아직 저장된 스캔 없음 + 설정에서 로컬 기록을 켜면 이 기기에 개인 기록을 보관할 수 있습니다. + 이미지에서 스캔 + 화이트리스트 가져오기 + 불러온 등록 ID: %1$d + %1$d개 ID를 가져왔습니다. + 이 파일에서 ID를 가져올 수 없습니다. + 일괄 모드 + 일괄 스캔: %1$d + 일괄 항목 지우기 + 일괄 공유 + 선택한 이미지에서 QR 코드나 바코드를 찾지 못했습니다. + 이미지에서 %1$d개의 코드 발견 + 사용할 결과를 선택하세요. 감지와 분석은 로컬에서 처리됩니다. + 선택 항목 사용 + 이 이미지를 읽을 수 없습니다. 다른 이미지를 시도하세요. + 이미 스캔됨 + 중복 티켓 감지됨 + 이 티켓/코드는 이전에 스캔되었습니다. 부정 사용을 방지하려면 즉시 입장을 확인하세요. + 코드: %1$s + 등록되지 않은 티켓 감지됨 + 이 티켓/코드는 가져온 화이트리스트에 없습니다. 참석자 등록을 확인하세요. + 코드: %1$s + 기록 보기 + 번호로 전화 + SMS 보내기 + 이메일 보내기 + Wi-Fi 설정 열기 + 연락처 추가 + 캘린더 일정 추가 + Google Play에서 리뷰 + 활성 사용 사례 보기 + 사용 사례 보기 선택 + 일상 개인 사용 + 이벤트 및 티켓 + 로컬 기록과 일반 결과 작업을 갖춘 전체 개인 스캐너입니다. + 일괄 스캔, 중복 감지, 화이트리스트 가져오기, 일괄 공유. + 사용자가 제어하는 설정 + 개인정보 설정은 사용자의 선택입니다 + 로컬 기록, 보안 경고, 피드백, 작업 흐름에 맞는 스캐너 보기를 선택하세요. + 로컬 확인 + 열기 전에 기기에서 확인됨 + 인코딩된 데이터 + 중복 및 미등록 티켓 알림은 기기에 남습니다. + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf572bf..fd118f6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,6 +4,10 @@ Scan again History Settings + App language + System default + Partial translation + Change Save history (local) Privacy Privacy Policy diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml deleted file mode 100644 index ee4a0a8..0000000 --- a/app/src/main/res/xml/locales_config.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/test/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalogTest.kt b/app/src/test/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalogTest.kt new file mode 100644 index 0000000..b62c761 --- /dev/null +++ b/app/src/test/java/de/softwareapp_hb/privateqrscanner/i18n/AppLocaleCatalogTest.kt @@ -0,0 +1,47 @@ +package de.softwareapp_hb.privateqrscanner.i18n + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +class AppLocaleCatalogTest { + @Test + fun parseReadsSelectableAndPlannedLocaleRows() { + val options = AppLocaleCatalog.parse( + """ + # comment + tag resourceQualifier status nativeName coverage + system system system System default Device language + en values complete English Default + de values-de complete Deutsch Germany + es values-es planned Español Spain + """.trimIndent() + ) + + assertEquals(4, options.size) + assertNull(options[0].languageTag) + assertEquals(AppLocaleStatus.System, options[0].status) + assertEquals("de", options[2].languageTag) + assertEquals(AppLocaleStatus.Planned, options[3].status) + } + + @Test + fun resolveOptionFallsBackToSystemDefaultForUnknownTag() { + val systemDefault = AppLocaleOption( + storageTag = AppLocaleCatalog.SYSTEM_STORAGE_TAG, + languageTag = null, + status = AppLocaleStatus.System, + displayName = "System default" + ) + val english = AppLocaleOption( + storageTag = "en", + languageTag = "en", + status = AppLocaleStatus.Complete, + displayName = "English" + ) + + val resolved = AppLocaleCatalog.resolveOption(listOf(systemDefault, english), "fr") + + assertEquals(AppLocaleCatalog.SYSTEM_STORAGE_TAG, resolved.storageTag) + } +} diff --git a/target_countries.txt b/target_countries.txt new file mode 100644 index 0000000..5d28750 --- /dev/null +++ b/target_countries.txt @@ -0,0 +1,188 @@ +Albania +Algeria +Angola +Antigua & Barbuda +Argentina +Armenia +Aruba +Australia +Austria +Azerbaijan +Bahamas +Bahrain +Bangladesh +Belarus +Belgium +Belize +Benin +Bermuda +Bolivia +Bosnia & Herzegovina +Botswana +Brazil +British Virgin Islands +Bulgaria +Burkina Faso +Cambodia +Cameroon +Canada +Cape Verde +Cayman Islands +Chad +Chile +China +Cannot target for paid app +Colombia +Comoros +Congo - Brazzaville +Congo - Kinshasa +Costa Rica +Croatia +Cuba +Cannot target for paid app +Cyprus +Czechia +Côte d’Ivoire +Denmark +Includes 2 locations +Djibouti +Dominica +Dominican Republic +Ecuador +Egypt +El Salvador +Eritrea +Estonia +Fiji +Finland +Includes 1 location +France +Includes 11 locations +Gabon +Gambia +Georgia +Germany +Targeted +Ghana +Gibraltar +Greece +Grenada +Guatemala +Guinea +Guinea-Bissau +Haiti +Honduras +Hong Kong +Hungary +Iceland +India +Indonesia +Iran +Cannot target for paid app +Iraq +Ireland +Israel +Italy +Jamaica +Japan +Jordan +Kazakhstan +Kenya +Kuwait +Kyrgyzstan +Laos +Latvia +Lebanon +Liberia +Libya +Liechtenstein +Lithuania +Luxembourg +Macao +Malaysia +Maldives +Mali +Malta +Mauritius +Mexico +Micronesia +Moldova +Monaco +Mongolia +Morocco +Mozambique +Myanmar (Burma) +Namibia +Nepal +Netherlands +New Zealand +Nicaragua +Niger +Nigeria +North Macedonia +Norway +Includes 1 location +Oman +Pakistan +Panama +Papua New Guinea +Paraguay +Peru +Philippines +Poland +Portugal +Qatar +Romania +Russia +Rwanda +Samoa +San Marino +Saudi Arabia +Senegal +Serbia +Seychelles +Sierra Leone +Singapore +Slovakia +Slovenia +Solomon Islands +Somalia +South Africa +South Korea +Spain +Sri Lanka +St. Kitts & Nevis +St. Lucia +Sudan +Cannot target for paid app +Suriname +Sweden +Switzerland +Taiwan +Tajikistan +Tanzania +Thailand +Togo +Tonga +Trinidad & Tobago +Tunisia +Turkmenistan +Turks & Caicos Islands +Türkiye +Uganda +Ukraine +United Arab Emirates +United Kingdom +Targeted +United States +Includes 7 locations +Targeted +Uruguay +Uzbekistan +Vanuatu +Vatican City +Venezuela +Vietnam +Yemen +Zambia +Zimbabwe