From 8e2503399ab21a6d6c6dd1a5579884535e06e6e8 Mon Sep 17 00:00:00 2001 From: sanjaysargam Date: Wed, 16 Sep 2026 17:01:13 +0530 Subject: [PATCH 01/43] feat(note-editor): add preview sidebar toggle Adds a checkable "Show preview" action to the note editor overflow menu. --- .../java/com/ichi2/anki/NoteEditorActivity.kt | 75 +++++++++++++++---- .../java/com/ichi2/anki/NoteEditorFragment.kt | 27 ++++++- AnkiDroid/src/main/res/menu/note_editor.xml | 7 +- AnkiDroid/src/main/res/values/02-strings.xml | 2 + .../java/com/ichi2/anki/CardBrowserTest.kt | 2 + 5 files changed, 94 insertions(+), 19 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt index c0e2b9c808f5..cf6006a05a4c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt @@ -8,6 +8,7 @@ import android.os.Build import android.os.Bundle import androidx.activity.SystemBarStyle import androidx.activity.enableEdgeToEdge +import androidx.core.content.edit import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat.Type.displayCutout import androidx.core.view.WindowInsetsCompat.Type.ime @@ -70,14 +71,30 @@ class NoteEditorActivity : */ private var previewerFrame: FragmentContainerView? = null + /** + * The user's persisted previewer visibility preference. + * Applied to the views by [applyPreviewerVisibility], restored in [onCreate]. + */ + private var previewerVisibilityPref: Boolean = true + /** * Job for managing delayed previewer refresh operations. * Automatically cancelled when the lifecycle scope is destroyed, preventing memory leaks. */ private var refreshPreviewerJob: Job? = null - val fragmented: Boolean - get() = previewerFrame?.isVisible == true + /** + * Whether the previewer pane is currently visible. + * + * `false` in single-pane layouts, and in split layouts where the user has + * hidden the pane via the "Show preview sidebar" menu action. + */ + val isPreviewerVisible: Boolean + get() = binding.previewerFrameLayout?.isVisible == true + + /** Whether this layout contains a previewer pane (split layouts only); never changes at runtime. */ + val hasPreviewerPane: Boolean + get() = previewerFrame != null private lateinit var binding: ActivityNoteEditorBinding @@ -123,7 +140,9 @@ class NoteEditorActivity : setupEdgeToEdge() previewerFrame = binding.previewerFrame - Timber.i("Note Editor is in %s mode", if (fragmented) "split" else "single-pane") + previewerVisibilityPref = loadPreviewerVisibilityPreference() + applyPreviewerVisibility() + Timber.i("Note Editor is in %s mode", if (isPreviewerVisible) "split" else "single-pane") // TODO: specify how non-null but invalid extras are handled val fragmentArgs = intent.extras ?: NoteEditorFragment.addNoteArgs() @@ -158,11 +177,7 @@ class NoteEditorActivity : onBackPressedDispatcher.onBackPressed() } - if (fragmented) { - // Defer previewer loading to avoid blocking onCreate - binding.previewerFrame!!.post { - loadNoteEditorPreviewer(true) - } + if (hasPreviewerPane) { val parentLayout = binding.noteEditorXlView!! val divider = binding.noteEditorResizingDivider!! val noteEditorPane = binding.noteEditorFragmentFrame @@ -176,11 +191,40 @@ class NoteEditorActivity : leftPaneWeightKey = PREF_NOTE_EDITOR_PANE_WEIGHT, rightPaneWeightKey = PREF_PREVIEWER_PANE_WEIGHT, ) + if (isPreviewerVisible) { + // Defer previewer loading to avoid blocking onCreate + binding.previewerFrame!!.post { + loadNoteEditorPreviewer(true) + } + } } startLoadingCollection() } + fun setPreviewerVisible(visible: Boolean) { + if (!hasPreviewerPane) return + + previewerVisibilityPref = visible + savePreviewerVisibilityPreference(visible) + applyPreviewerVisibility() + if (visible) { + loadNoteEditorPreviewer(true) + } + } + + private fun loadPreviewerVisibilityPreference(): Boolean = Prefs.getUiConfig(this).getBoolean(PREF_SHOW_PREVIEWER, true) + + private fun savePreviewerVisibilityPreference(visible: Boolean) { + Prefs.getUiConfig(this).edit { putBoolean(PREF_SHOW_PREVIEWER, visible) } + } + + private fun applyPreviewerVisibility() { + val visible = hasPreviewerPane && previewerVisibilityPref + binding.previewerFrameLayout?.isVisible = visible + binding.noteEditorResizingDivider?.isVisible = visible + } + /** * Loads and configures the note editor previewer. * @@ -203,7 +247,7 @@ class NoteEditorActivity : * - Cloze notes: Fragment recreation with preserved tab selection to handle dynamic cloze changes */ fun loadNoteEditorPreviewer(forceReplace: Boolean) { - if (!fragmented) { + if (!isPreviewerVisible) { return } @@ -363,7 +407,7 @@ class NoteEditorActivity : override fun onResume() { super.onResume() // Refresh the previewer when activity resumes, if needed - if (fragmented) { + if (isPreviewerVisible) { loadNoteEditorPreviewer(false) } } @@ -371,14 +415,14 @@ class NoteEditorActivity : //region NoteEditorFragmentDelegate Protocol Methods override fun onNoteEditorReady() { - // Load the if fragmented, else does nothing - if (!fragmented) return + // Load the previewer if visible, else do nothing + if (!isPreviewerVisible) return loadNoteEditorPreviewer(false) } override fun onNoteTextChanged() { - if (!fragmented) return + if (!isPreviewerVisible) return refreshPreviewerJob?.cancel() refreshPreviewerJob = @@ -389,13 +433,13 @@ class NoteEditorActivity : } override fun onNoteSaved() { - if (!fragmented) return + if (!isPreviewerVisible) return loadNoteEditorPreviewer(true) } override fun onNoteTypeChanged() { - if (!fragmented) return + if (!isPreviewerVisible) return loadNoteEditorPreviewer(true) } @@ -407,6 +451,7 @@ class NoteEditorActivity : // Keys for saving pane weights in SharedPreferences private const val PREF_NOTE_EDITOR_PANE_WEIGHT = "noteEditorPaneWeight" private const val PREF_PREVIEWER_PANE_WEIGHT = "previewerPaneWeight" + private const val PREF_SHOW_PREVIEWER = "noteEditorShowPreviewer" private val REFRESH_NOTE_EDITOR_PREVIEW_DELAY = 100.milliseconds } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt index 950a0301dfa5..1063dae15dba 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt @@ -1432,6 +1432,16 @@ class NoteEditorFragment : // because this item is already present in CardBrowser menu.findItem(R.id.action_add_note_from_note_editor).isVisible = !inCardBrowserActivity } + menu.findItem(R.id.action_toggle_previewer).apply { + isVisible = inNoteEditorActivity && noteEditorActivity?.hasPreviewerPane == true + isChecked = noteEditorActivity?.isPreviewerVisible == true + title = + if (isChecked) { + getString(R.string.note_editor_hide_previewer) + } else { + getString(R.string.note_editor_show_previewer) + } + } if (editFields != null) { for (i in editFields!!.indices) { val fieldText = editFields!![i].text @@ -1476,7 +1486,7 @@ class NoteEditorFragment : // TODO: Move this logic to a [NoteEditorActivity.kt] private fun allowPreviewAction(): Boolean = when { - inNoteEditorActivity && noteEditorActivity?.fragmented == true -> false + inNoteEditorActivity && noteEditorActivity?.isPreviewerVisible == true -> false addNote && currentNotetypeIsImageOcclusion() -> false else -> true } @@ -1491,6 +1501,17 @@ class NoteEditorFragment : } return true } + R.id.action_toggle_previewer -> { + Timber.i("NoteEditor:: Toggle previewer pressed") + val editorActivity = noteEditorActivity + if (editorActivity == null) { + Timber.w("NoteEditor:: Toggle previewer pressed outside of NoteEditorActivity") + return true + } + editorActivity.setPreviewerVisible(!editorActivity.isPreviewerVisible) + editorActivity.invalidateOptionsMenu() + return true + } R.id.action_save -> { Timber.i("NoteEditor:: Save note button pressed") if (allowSaveAction()) { @@ -2236,7 +2257,7 @@ class NoteEditorFragment : val bars = getInsets(systemBars() or displayCutout() or ime()) return insetsOf( left = if (inCardBrowserActivity) 0 else bars.left, - right = if (noteEditorActivity?.fragmented == true) 0 else bars.right, + right = if (noteEditorActivity?.isPreviewerVisible == true) 0 else bars.right, bottom = bars.bottom, ) } @@ -2250,7 +2271,7 @@ class NoteEditorFragment : val corners = insets.bottomCornerSideClearance(bars.bottom) toolbar.setSideClearance( left = if (inCardBrowserActivity) 0 else (corners.left - bars.left).coerceAtLeast(0), - right = if (noteEditorActivity?.fragmented == true) 0 else (corners.right - bars.right).coerceAtLeast(0), + right = if (noteEditorActivity?.isPreviewerVisible == true) 0 else (corners.right - bars.right).coerceAtLeast(0), ) applyBottomInset() insets diff --git a/AnkiDroid/src/main/res/menu/note_editor.xml b/AnkiDroid/src/main/res/menu/note_editor.xml index 289f79ac5103..d8ad9ed56d34 100644 --- a/AnkiDroid/src/main/res/menu/note_editor.xml +++ b/AnkiDroid/src/main/res/menu/note_editor.xml @@ -44,4 +44,9 @@ android:checkable="true" android:checked="true" ankidroid:showAsAction="never"/> - \ No newline at end of file + + diff --git a/AnkiDroid/src/main/res/values/02-strings.xml b/AnkiDroid/src/main/res/values/02-strings.xml index 90f9c52bbcfc..271234bce2d1 100644 --- a/AnkiDroid/src/main/res/values/02-strings.xml +++ b/AnkiDroid/src/main/res/values/02-strings.xml @@ -241,6 +241,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt index 74654b969e72..6d430cfa4bf8 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt @@ -1762,6 +1762,7 @@ class CardBrowserTest : RobolectricTest() { R.id.action_capitalize to true, R.id.action_show_toolbar to true, R.id.action_scroll_toolbar to true, + R.id.action_toggle_previewer to false, ) assertMenusEqual(expectedMenuItems, menu) @@ -1805,6 +1806,7 @@ class CardBrowserTest : RobolectricTest() { R.id.action_capitalize to true, R.id.action_show_toolbar to true, R.id.action_scroll_toolbar to true, + R.id.action_toggle_previewer to false, ) assertMenusEqual(expectedMenuItems, menu) From bbda6a537fd8d34be7c83b6703aa5532551584e9 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Tue, 22 Sep 2026 20:25:12 +0100 Subject: [PATCH 02/43] fix(import): handle requests to close the error page Prep for Anki 26.09 Part of 21926 Assisted-by: GPT-6 --- .../ichi2/anki/pages/PostRequestHandler.kt | 6 ++++ .../com/ichi2/anki/pages/CsvImporterTest.kt | 28 +++++++++++++++++++ .../anki/pages/PostRequestHandlerTest.kt | 7 ++--- 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/pages/CsvImporterTest.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/PostRequestHandler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/PostRequestHandler.kt index cd16905ad9be..1b07de5f91f0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/PostRequestHandler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/PostRequestHandler.kt @@ -147,6 +147,12 @@ val uiMethods = }, "importCsv" to { bytes -> lifecycleScope.async { importCsvRaw(bytes) } }, "importAnkiPackage" to { bytes -> lifecycleScope.async { importAnkiPackageUndoable(bytes) } }, + "importDialogRequireClose" to { _ -> + lifecycleScope.async { + finish() + byteArrayOf() + } + }, "addImageOcclusionNote" to { bytes -> lifecycleScope.async { withCol { addImageOcclusionNoteRaw(bytes) } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/pages/CsvImporterTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/pages/CsvImporterTest.kt new file mode 100644 index 000000000000..02019d11e7dc --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/pages/CsvImporterTest.kt @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.pages + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.RobolectricTest +import com.ichi2.anki.SingleFragmentActivity +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@RunWith(AndroidJUnit4::class) +class CsvImporterTest : RobolectricTest() { + @Test + fun `acknowledging an import error closes the importer`() = + runTest { + val activity = + startActivityNormallyOpenCollectionWithIntent( + SingleFragmentActivity::class.java, + CsvImporter.getIntent(targetContext, "/missing.csv"), + ) + val response = activity.handleUiPostRequest("importDialogRequireClose", byteArrayOf()) + + assertEquals(UiPostRequestResponse.Handled(byteArrayOf()), response) + assertTrue(activity.isFinishing) + } +} diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/pages/PostRequestHandlerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/pages/PostRequestHandlerTest.kt index 524865bf08fd..04daa5b2530a 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/pages/PostRequestHandlerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/pages/PostRequestHandlerTest.kt @@ -5,7 +5,6 @@ package com.ichi2.anki.pages import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.RobolectricTest -import com.ichi2.testutils.HamcrestUtils.containsInAnyOrder import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.empty import org.hamcrest.Matchers.not @@ -18,11 +17,11 @@ import kotlin.test.assertNotNull class PostRequestHandlerTest : RobolectricTest() { @Test fun `All backend typescript functions should be handled`() { + // TODO: Reset this after Anki 26.09 is merged assertThat( "Mapping exists for every TS backend function call", - typescriptFunctionsUsedByBackend, - // this matcher asserts equality in everything but order, no extras, nothing missing - containsInAnyOrder((collectionMethods + uiMethods).keys), + typescriptFunctionsUsedByBackend - (collectionMethods + uiMethods).keys, + empty(), ) } From c06fa7b7dfef40c177ac8b4add9d503fe45450c3 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 21:27:47 +0100 Subject: [PATCH 03/43] fix(tests): ActivityLaunchLoggerTest - stop app launch logging ActivityLaunchLoggerTest was broken due to async logs from AnkiDroidApp. Fix by using `EmptyApplication` Fixes 22132 Assisted-by: GPT-6 --- .../com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt index 33604e9aceb9..a653eb4058c2 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt @@ -10,20 +10,27 @@ import android.os.Bundle import androidx.core.net.toUri import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress +import com.ichi2.anki.EmptyApplicationCategory import com.ichi2.anki.NavigationDrawerActivity.Companion.EXTRA_STARTED_WITH_SHORTCUT import com.ichi2.anki.SingleFragmentActivity.Companion.EXTRA_FRAGMENT_ARGS import com.ichi2.anki.SingleFragmentActivity.Companion.EXTRA_FRAGMENT_NAME +import com.ichi2.testutils.EmptyApplication import io.mockk.every import io.mockk.mockk import org.junit.After import org.junit.Before import org.junit.Test +import org.junit.experimental.categories.Category import org.junit.runner.RunWith +import org.robolectric.annotation.Config import timber.log.Timber import kotlin.test.assertFalse import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) +// Application startup logs can race with the launch diagnostics captured by these tests. +@Config(application = EmptyApplication::class) +@Category(EmptyApplicationCategory::class) class ActivityLaunchLoggerTest { private val messages = mutableListOf() private val tree = From c5b0c047d0a34756688f1d78f190df59c9846a62 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 21:42:10 +0100 Subject: [PATCH 04/43] test: report captured activity launch logs on failure Part of 22132 Assisted-by: GPT-6 --- .../java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt index a653eb4058c2..ac17eaf01254 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/logging/ActivityLaunchLoggerTest.kt @@ -25,6 +25,7 @@ import org.junit.runner.RunWith import org.robolectric.annotation.Config import timber.log.Timber import kotlin.test.assertFalse +import kotlin.test.assertNotNull import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) @@ -76,7 +77,7 @@ class ActivityLaunchLoggerTest { activity.logActivityCreation(Bundle().apply { putString(privateText, privateText) }) - val message = messages.single() + val message = assertNotNull(messages.singleOrNull(), "Expected one log message; captured: $messages") for (privateValue in listOf(privateText, "private-referrer", "private.caller.package", "1234567")) { assertFalse(message.contains(privateValue), "Launch diagnostics leaked $privateValue") } @@ -96,7 +97,7 @@ class ActivityLaunchLoggerTest { mockActivity(intent).logActivityCreation(null) - val message = messages.single() + val message = assertNotNull(messages.singleOrNull(), "Expected one log message; captured: $messages") assertTrue(message.contains(Intent.ACTION_VIEW)) assertTrue(message.contains(Intent.CATEGORY_LAUNCHER)) assertTrue(message.contains(EXTRA_FRAGMENT_NAME)) From 7df34204e26f383ce581f0146f855e4dcc9c9464 Mon Sep 17 00:00:00 2001 From: AnkiDroid Translations Date: Sun, 27 Sep 2026 19:29:41 +0000 Subject: [PATCH 05/43] Updated strings from Crowdin --- .../src/main/res/values-af/02-strings.xml | 4 +- .../src/main/res/values-af/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-am/02-strings.xml | 4 +- .../src/main/res/values-am/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ar/02-strings.xml | 4 +- .../src/main/res/values-ar/10-preferences.xml | 84 ++++++++++++++- .../src/main/res/values-az/02-strings.xml | 4 +- .../src/main/res/values-az/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-be/02-strings.xml | 4 +- .../src/main/res/values-be/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-bg/02-strings.xml | 4 +- .../src/main/res/values-bg/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-bn/02-strings.xml | 4 +- .../src/main/res/values-bn/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ca/02-strings.xml | 4 +- .../src/main/res/values-ca/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ckb/02-strings.xml | 4 +- .../main/res/values-ckb/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-cs/02-strings.xml | 4 +- .../src/main/res/values-cs/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-da/02-strings.xml | 4 +- .../src/main/res/values-da/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-de/02-strings.xml | 4 +- .../src/main/res/values-de/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-el/02-strings.xml | 4 +- .../src/main/res/values-el/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-eo/02-strings.xml | 4 +- .../src/main/res/values-eo/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-es-rAR/02-strings.xml | 4 +- .../main/res/values-es-rAR/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-es-rES/02-strings.xml | 4 +- .../main/res/values-es-rES/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-et/02-strings.xml | 4 +- .../src/main/res/values-et/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-eu/02-strings.xml | 4 +- .../src/main/res/values-eu/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-fa/02-strings.xml | 4 +- .../src/main/res/values-fa/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-fi/02-strings.xml | 4 +- .../src/main/res/values-fi/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-fil/02-strings.xml | 4 +- .../main/res/values-fil/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-fr/02-strings.xml | 4 +- .../src/main/res/values-fr/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-fy/02-strings.xml | 4 +- .../src/main/res/values-fy/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ga/02-strings.xml | 4 +- .../src/main/res/values-ga/10-preferences.xml | 83 +++++++++++++- AnkiDroid/src/main/res/values-gl/01-core.xml | 4 +- .../src/main/res/values-gl/02-strings.xml | 4 +- .../src/main/res/values-gl/10-preferences.xml | 82 ++++++++++++-- .../src/main/res/values-got/02-strings.xml | 4 +- .../main/res/values-got/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-gu/02-strings.xml | 4 +- .../src/main/res/values-gu/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-heb/02-strings.xml | 4 +- .../main/res/values-heb/10-preferences.xml | 81 +++++++++++++- .../src/main/res/values-hi/02-strings.xml | 4 +- .../src/main/res/values-hi/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-hr/02-strings.xml | 4 +- .../src/main/res/values-hr/10-preferences.xml | 81 +++++++++++++- .../src/main/res/values-hu/02-strings.xml | 4 +- .../src/main/res/values-hu/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-hy/02-strings.xml | 4 +- .../src/main/res/values-hy/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ind/02-strings.xml | 4 +- .../main/res/values-ind/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-it/02-strings.xml | 4 +- .../src/main/res/values-it/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-iw/02-strings.xml | 4 +- .../src/main/res/values-iw/10-preferences.xml | 81 +++++++++++++- AnkiDroid/src/main/res/values-ja/01-core.xml | 4 +- .../src/main/res/values-ja/02-strings.xml | 8 +- .../src/main/res/values-ja/07-cardbrowser.xml | 14 +-- .../src/main/res/values-ja/10-preferences.xml | 91 ++++++++++++++-- .../src/main/res/values-ka/02-strings.xml | 4 +- .../src/main/res/values-ka/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-kk/02-strings.xml | 4 +- .../src/main/res/values-kk/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-km/02-strings.xml | 4 +- .../src/main/res/values-km/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-kn/02-strings.xml | 4 +- .../src/main/res/values-kn/10-preferences.xml | 80 +++++++++++++- AnkiDroid/src/main/res/values-ko/01-core.xml | 4 +- .../src/main/res/values-ko/02-strings.xml | 10 +- .../src/main/res/values-ko/03-dialogs.xml | 2 +- .../src/main/res/values-ko/10-preferences.xml | 101 +++++++++++++++--- .../src/main/res/values-ku/02-strings.xml | 4 +- .../src/main/res/values-ku/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ky/02-strings.xml | 4 +- .../src/main/res/values-ky/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-lt/02-strings.xml | 4 +- .../src/main/res/values-lt/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-lv/02-strings.xml | 4 +- .../src/main/res/values-lv/10-preferences.xml | 81 +++++++++++++- .../src/main/res/values-mk/02-strings.xml | 4 +- .../src/main/res/values-mk/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ml/02-strings.xml | 4 +- .../src/main/res/values-ml/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-mn/02-strings.xml | 4 +- .../src/main/res/values-mn/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-mr/02-strings.xml | 4 +- .../src/main/res/values-mr/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ms/02-strings.xml | 4 +- .../src/main/res/values-ms/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-my/02-strings.xml | 4 +- .../src/main/res/values-my/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-nl/02-strings.xml | 4 +- .../src/main/res/values-nl/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-nn/02-strings.xml | 4 +- .../src/main/res/values-nn/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-no/02-strings.xml | 4 +- .../src/main/res/values-no/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-or/02-strings.xml | 4 +- .../src/main/res/values-or/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-pa/02-strings.xml | 4 +- .../src/main/res/values-pa/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-pl/02-strings.xml | 4 +- .../src/main/res/values-pl/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-pt-rBR/02-strings.xml | 4 +- .../main/res/values-pt-rBR/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-pt-rPT/02-strings.xml | 4 +- .../main/res/values-pt-rPT/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ro/02-strings.xml | 4 +- .../src/main/res/values-ro/10-preferences.xml | 81 +++++++++++++- AnkiDroid/src/main/res/values-ru/01-core.xml | 2 +- .../src/main/res/values-ru/02-strings.xml | 4 +- .../src/main/res/values-ru/07-cardbrowser.xml | 2 +- .../src/main/res/values-ru/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-sat/02-strings.xml | 4 +- .../main/res/values-sat/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-sc/02-strings.xml | 4 +- .../src/main/res/values-sc/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-sk/02-strings.xml | 4 +- .../src/main/res/values-sk/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-sl/02-strings.xml | 4 +- .../src/main/res/values-sl/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-sq/02-strings.xml | 4 +- .../src/main/res/values-sq/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-sr/02-strings.xml | 4 +- .../src/main/res/values-sr/10-preferences.xml | 81 +++++++++++++- .../src/main/res/values-sv/02-strings.xml | 4 +- .../src/main/res/values-sv/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-ta/02-strings.xml | 4 +- .../src/main/res/values-ta/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-te/02-strings.xml | 4 +- .../src/main/res/values-te/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-tgl/02-strings.xml | 4 +- .../main/res/values-tgl/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-th/02-strings.xml | 4 +- .../src/main/res/values-th/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-ti/02-strings.xml | 4 +- .../src/main/res/values-ti/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-tr/02-strings.xml | 4 +- .../src/main/res/values-tr/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-tt/02-strings.xml | 4 +- .../src/main/res/values-tt/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-ug/02-strings.xml | 4 +- .../src/main/res/values-ug/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-uk/02-strings.xml | 4 +- .../src/main/res/values-uk/10-preferences.xml | 82 +++++++++++++- .../src/main/res/values-ur/02-strings.xml | 4 +- .../src/main/res/values-ur/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-uz/02-strings.xml | 4 +- .../src/main/res/values-uz/10-preferences.xml | 80 +++++++++++++- .../src/main/res/values-vi/02-strings.xml | 4 +- .../src/main/res/values-vi/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-zh-rCN/02-strings.xml | 4 +- .../main/res/values-zh-rCN/10-preferences.xml | 79 +++++++++++++- .../src/main/res/values-zh-rTW/02-strings.xml | 4 +- .../main/res/values-zh-rTW/10-preferences.xml | 79 +++++++++++++- 171 files changed, 6453 insertions(+), 531 deletions(-) diff --git a/AnkiDroid/src/main/res/values-af/02-strings.xml b/AnkiDroid/src/main/res/values-af/02-strings.xml index a4621044650a..a44df437282d 100644 --- a/AnkiDroid/src/main/res/values-af/02-strings.xml +++ b/AnkiDroid/src/main/res/values-af/02-strings.xml @@ -29,7 +29,7 @@ Note saved Kaartpak: - Pak: + Pak: Tipe: Etikette: %1$s Kaarte: %1$s @@ -220,6 +220,8 @@ Lettergrootte Wys nutsbalk + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-af/10-preferences.xml b/AnkiDroid/src/main/res/values-af/10-preferences.xml index af4c66c9b022..82e3845e88da 100644 --- a/AnkiDroid/src/main/res/values-af/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-af/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Af + Af Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-am/02-strings.xml b/AnkiDroid/src/main/res/values-am/02-strings.xml index 49d3076932e4..7cd1529bac88 100644 --- a/AnkiDroid/src/main/res/values-am/02-strings.xml +++ b/AnkiDroid/src/main/res/values-am/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Type: Tags: %1$s Cards: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-am/10-preferences.xml b/AnkiDroid/src/main/res/values-am/10-preferences.xml index fca2e8cb68c3..53cc0aec1dd3 100644 --- a/AnkiDroid/src/main/res/values-am/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-am/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media የተለያዪ - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-ar/02-strings.xml b/AnkiDroid/src/main/res/values-ar/02-strings.xml index 9463bdf574f0..bf13435d2bed 100644 --- a/AnkiDroid/src/main/res/values-ar/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ar/02-strings.xml @@ -29,7 +29,7 @@ تم حفظ الملحوظة رزمة البطاقة: - الرزمة: + الرزمة: النوع: الوسوم: %1$s البطاقات: %1$s @@ -232,6 +232,8 @@ حجم الخط إظهار شريط الأدوات + Show preview + Hide preview تعريض إمالة إضافة خطٍ سفلي diff --git a/AnkiDroid/src/main/res/values-ar/10-preferences.xml b/AnkiDroid/src/main/res/values-ar/10-preferences.xml index b09452200de1..28b2c5fb4069 100644 --- a/AnkiDroid/src/main/res/values-ar/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ar/10-preferences.xml @@ -24,7 +24,8 @@ --> - مُعَطَّل + Enabled + مُعَطَّل %d دقيقة %d دقيقة @@ -76,7 +77,7 @@ عرض أسماء الملفات الصوتية في حقول السؤال والجواب في متصفح البطاقات مجلد آنكي درويد وضع ملء الشاشة - غير مفعل + غير مفعل إخفاء أشرطة النظام إخفاء أشرطة النظام وأزرار الإجابة موضع أزرار الإجابة @@ -226,11 +227,84 @@ التنقل الوسائط منوع - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders إضافة تنبيه - إضافة تنبيه للمراجعة - تعديل تنبيه المراجعة ليس لديك تنبيهات للمراجعة + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + إضافة تنبيه للمراجعة + تعديل تنبيه المراجعة + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + %d card due + %d cards due + %d cards due + %d cards due + %d cards due + اختيار وسوم diff --git a/AnkiDroid/src/main/res/values-az/02-strings.xml b/AnkiDroid/src/main/res/values-az/02-strings.xml index 458c76be51c5..1db0a8126b84 100644 --- a/AnkiDroid/src/main/res/values-az/02-strings.xml +++ b/AnkiDroid/src/main/res/values-az/02-strings.xml @@ -29,7 +29,7 @@ Qeyd yadda saxlanıldı Kart dəsti: - Dəst: + Dəst: Növ: Etiketlər: %1$s Kartlar: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-az/10-preferences.xml b/AnkiDroid/src/main/res/values-az/10-preferences.xml index 47fba78a508a..180f05ff908d 100644 --- a/AnkiDroid/src/main/res/values-az/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-az/10-preferences.xml @@ -24,7 +24,8 @@ --> - Ləğv edildi + Enabled + Ləğv edildi %d dəqiqə %d dəqiqə @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Tam ekran rejimi - Bağlı + Bağlı Sistem sətirlərini gizlət Sistem sətirlərini və cavab düymələrini gizlət Cavab düymələrinin mövqeyi @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-be/02-strings.xml b/AnkiDroid/src/main/res/values-be/02-strings.xml index 39fd6fd8da6e..8c0904fe2553 100644 --- a/AnkiDroid/src/main/res/values-be/02-strings.xml +++ b/AnkiDroid/src/main/res/values-be/02-strings.xml @@ -29,7 +29,7 @@ Нататка захавана Калода картак: - Калода: + Калода: Тып: Меткі: %1$s Карткі: %1$s @@ -224,6 +224,8 @@ Памер шрыфту Паказаць панэль інструментаў + Show preview + Hide preview Фарматаваць як тлусты Фарматаваць як курсіў Фарматаваць як падкрэслены diff --git a/AnkiDroid/src/main/res/values-be/10-preferences.xml b/AnkiDroid/src/main/res/values-be/10-preferences.xml index 8b319441cfe2..69a5061b5b06 100644 --- a/AnkiDroid/src/main/res/values-be/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-be/10-preferences.xml @@ -24,7 +24,8 @@ --> - Адключана + Enabled + Адключана %d хв %d хв @@ -74,7 +75,7 @@ Адлюстроўваць назвы аўдыяфайлаў у аглядальніку картак у палях пытанні/адказы Каталог AnkiDroid Поўнаэкранны рэжым - Выключаны + Выключаны Схаваць сістэмную панэль Схаваць сістэмную панэль і панэль кнопак адказу Становішча кнопак адказу @@ -223,11 +224,82 @@ Навігацыя Медыя Рознае - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Дадаць напамін - Дадаць нагадванне паўтарэння - Рэдагаваць нагадванне паўтарэння У вас адсутнічаюць нагадванні паўтарэння + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Дадаць нагадванне паўтарэння + Рэдагаваць нагадванне паўтарэння + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Выберыце меткі diff --git a/AnkiDroid/src/main/res/values-bg/02-strings.xml b/AnkiDroid/src/main/res/values-bg/02-strings.xml index 2b67e0c9e9eb..b37a2379eb2c 100644 --- a/AnkiDroid/src/main/res/values-bg/02-strings.xml +++ b/AnkiDroid/src/main/res/values-bg/02-strings.xml @@ -29,7 +29,7 @@ Запазена бележка Тесте от карти: - Тесте: + Тесте: Тип: Етикети:%1$s Карти: %1$s @@ -220,6 +220,8 @@ Големина на шрифт Показване на лентата с инструменти + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-bg/10-preferences.xml b/AnkiDroid/src/main/res/values-bg/10-preferences.xml index d899e2dcdb15..470d469b13d0 100644 --- a/AnkiDroid/src/main/res/values-bg/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-bg/10-preferences.xml @@ -24,7 +24,8 @@ --> - Изключен + Enabled + Изключен %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields Директория на AnkiDroid Режим на цял екран - Изкл. + Изкл. Скриване на системните ленти Скриване на системните ленти и бутоните за отговори Място на бутона за отговор @@ -218,11 +219,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Изберете етикети diff --git a/AnkiDroid/src/main/res/values-bn/02-strings.xml b/AnkiDroid/src/main/res/values-bn/02-strings.xml index f474b7ac8b25..f565bc9d97bc 100644 --- a/AnkiDroid/src/main/res/values-bn/02-strings.xml +++ b/AnkiDroid/src/main/res/values-bn/02-strings.xml @@ -29,7 +29,7 @@ Note saved কার্ড ডেক: - ডেক: + ডেক: ধরন: ট্যাগসমূহ: %1$s কার্ড: %1$s @@ -220,6 +220,8 @@ ফন্টের আকার টুলবার দেখান + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-bn/10-preferences.xml b/AnkiDroid/src/main/res/values-bn/10-preferences.xml index b61f44d2c244..010d17af4c0f 100644 --- a/AnkiDroid/src/main/res/values-bn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-bn/10-preferences.xml @@ -24,7 +24,8 @@ --> - নিষ্ক্রিয় করুন + Enabled + নিষ্ক্রিয় করুন %d মিনিট %d মিনিট @@ -73,7 +74,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid ডিরেক্টরি ফুলস্ক্রিন মোড - বন্ধ + বন্ধ সিস্টেম বার আড়াল করুন সিস্টেম বার এবং উত্তর বোতাম লুকান উত্তর বোতামের অবস্থান @@ -219,11 +220,80 @@ নেভিগেশন মিডিয়া বিবিধ - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + ট্যাগ নির্বাচন করুন diff --git a/AnkiDroid/src/main/res/values-ca/02-strings.xml b/AnkiDroid/src/main/res/values-ca/02-strings.xml index 859c30891ca9..165d72ef7e86 100644 --- a/AnkiDroid/src/main/res/values-ca/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ca/02-strings.xml @@ -29,7 +29,7 @@ Note saved Paquet de cartes: - Paquet: + Paquet: Tipus: Etiquetes: %1$s Cartes: %1$s @@ -220,6 +220,8 @@ Mida de la lletra Mostra la barra d\'eines + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ca/10-preferences.xml b/AnkiDroid/src/main/res/values-ca/10-preferences.xml index a1c0df2afa6f..7adbb185024d 100644 --- a/AnkiDroid/src/main/res/values-ca/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ca/10-preferences.xml @@ -24,7 +24,8 @@ --> - Deshabilitat + Enabled + Deshabilitat %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields Directori de l\'AnkiDroid Mode de pantalla completa - Desactivat + Desactivat Amagar les barres del sistema Amagar les barres del sistema i els botons de resposta Posició dels botons de resposta @@ -218,11 +219,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Trieu les etiquetes diff --git a/AnkiDroid/src/main/res/values-ckb/02-strings.xml b/AnkiDroid/src/main/res/values-ckb/02-strings.xml index a2f2b8dd4164..33f5940d12b4 100644 --- a/AnkiDroid/src/main/res/values-ckb/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ckb/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - دەستە: + دەستە: جۆر: Nîşan: %1$s کارتەکان: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ckb/10-preferences.xml b/AnkiDroid/src/main/res/values-ckb/10-preferences.xml index 3df74e2d5e26..08332b7be302 100644 --- a/AnkiDroid/src/main/res/values-ckb/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ckb/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-cs/02-strings.xml b/AnkiDroid/src/main/res/values-cs/02-strings.xml index 7f613f4dd1d0..5b8e11d5cfce 100644 --- a/AnkiDroid/src/main/res/values-cs/02-strings.xml +++ b/AnkiDroid/src/main/res/values-cs/02-strings.xml @@ -29,7 +29,7 @@ Poznámka uložena Balíček karet: - Balíček: + Balíček: Typ poznámky: Štítky: %1$s Karty: %1$s @@ -226,6 +226,8 @@ Velikost písma Zobrazit nástrojovou lištu + Show preview + Hide preview Formátovat jako tučné Formát jako kurzíva Formát jako podtržené diff --git a/AnkiDroid/src/main/res/values-cs/10-preferences.xml b/AnkiDroid/src/main/res/values-cs/10-preferences.xml index 729a7dfffd63..2e7ff235e386 100644 --- a/AnkiDroid/src/main/res/values-cs/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-cs/10-preferences.xml @@ -24,7 +24,8 @@ --> - Vypnuto + Enabled + Vypnuto %d min %d min @@ -74,7 +75,7 @@ Zobrazit názvy audio souborů v polích otázka/odpověď v prohlížeči karet Adresář AnkiDroid Celoobrazovkový režim - Vypnout + Vypnout Skrýt systémové lišty Skrýt systémové lišty a tlačítka odpovědí Pozice tlačítek odpovědí @@ -222,11 +223,82 @@ Navigace Multimédia Různé - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Přidat upomínku - Přidat upomínku opakování - Upravit upomínku opakování Nemáte žádné upomínky opakování + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Přidat upomínku opakování + Upravit upomínku opakování + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Výběr štítků diff --git a/AnkiDroid/src/main/res/values-da/02-strings.xml b/AnkiDroid/src/main/res/values-da/02-strings.xml index 053b86624b55..936a6155699c 100644 --- a/AnkiDroid/src/main/res/values-da/02-strings.xml +++ b/AnkiDroid/src/main/res/values-da/02-strings.xml @@ -29,7 +29,7 @@ Note saved Kortstak: - Stak: + Stak: Type: Tags: %1$s Kort: %1$s @@ -220,6 +220,8 @@ Skriftstørrelse Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-da/10-preferences.xml b/AnkiDroid/src/main/res/values-da/10-preferences.xml index f4a65c9a0c03..d6bb327a26b8 100644 --- a/AnkiDroid/src/main/res/values-da/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-da/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d minutter @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-de/02-strings.xml b/AnkiDroid/src/main/res/values-de/02-strings.xml index 16fdfa7dcae0..811b0f258d0b 100644 --- a/AnkiDroid/src/main/res/values-de/02-strings.xml +++ b/AnkiDroid/src/main/res/values-de/02-strings.xml @@ -29,7 +29,7 @@ Notiz gespeichert Kartenstapel: - Stapel: + Stapel: Typ: Schlagwörter: %1$s Karten: %1$s @@ -220,6 +220,8 @@ Schriftgröße Werkzeug zeigen + Show preview + Hide preview Als fett formatieren Kursiv formatieren Als Unterstrichen formatieren diff --git a/AnkiDroid/src/main/res/values-de/10-preferences.xml b/AnkiDroid/src/main/res/values-de/10-preferences.xml index ff1cd179461c..42fb2c4112af 100644 --- a/AnkiDroid/src/main/res/values-de/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-de/10-preferences.xml @@ -24,7 +24,8 @@ --> - Deaktiviert + Enabled + Deaktiviert %d min %d Minuten @@ -72,7 +73,7 @@ Audiodateinamen anzeigen in den Frage-/Antwortfeldern der Kartenübersicht AnkiDroid-Verzeichnis Vollbildmodus - Aus + Aus Systemleisten ausblenden Systemleisten und Antwortschaltflächen ausblenden Position der Antwortschaltflächen @@ -218,11 +219,80 @@ Navigation Medien Sonstiges - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Erinnerung hinzufügen - Bewertungserinnerung machen - Eine Bewertungserinnerung editieren Sie haben keine Bewertungserinnerungen + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Bewertungserinnerung machen + Eine Bewertungserinnerung editieren + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Schlagwörter auswählen diff --git a/AnkiDroid/src/main/res/values-el/02-strings.xml b/AnkiDroid/src/main/res/values-el/02-strings.xml index 02c858b8a580..6553ae9f7e50 100644 --- a/AnkiDroid/src/main/res/values-el/02-strings.xml +++ b/AnkiDroid/src/main/res/values-el/02-strings.xml @@ -29,7 +29,7 @@ Note saved Τράπουλα κάρτας: - Τράπουλα: + Τράπουλα: Τύπος: Ετικέτες:%1$s Κάρτες %1$s @@ -220,6 +220,8 @@ Μέγεθος γραμματοσειράς Εμφάνιση γραμμής εργαλείων + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-el/10-preferences.xml b/AnkiDroid/src/main/res/values-el/10-preferences.xml index dadd645555a7..0a2c79e827e1 100644 --- a/AnkiDroid/src/main/res/values-el/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-el/10-preferences.xml @@ -24,7 +24,8 @@ --> - Ανενεργό + Enabled + Ανενεργό %d λεπτό %d λεπτά @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields Κατάλογος AnkiDroid Λειτουργία πλήρους οθόνης - Ανενεργό + Ανενεργό Απόκρυψη γραμμών συστήματος Hide the system bars and answer buttons Θέση κουμπιών απάντησης @@ -219,11 +220,80 @@ Πλοήγηση Πολυμέσα Διάφορα - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Επιλογή Ετικετών diff --git a/AnkiDroid/src/main/res/values-eo/02-strings.xml b/AnkiDroid/src/main/res/values-eo/02-strings.xml index c9e8a4a8d20c..e869fb40e474 100644 --- a/AnkiDroid/src/main/res/values-eo/02-strings.xml +++ b/AnkiDroid/src/main/res/values-eo/02-strings.xml @@ -29,7 +29,7 @@ Noto konservita Kartaro: - Kartaro: + Kartaro: Tipo: Etikedoj: %1$s Kartoj: %1$s @@ -220,6 +220,8 @@ Grando de tiparo Montri ilarstrion + Show preview + Hide preview Aranĝi kiel grasan Aranĝi kiel kursivan Aranĝi kiel substrekitan diff --git a/AnkiDroid/src/main/res/values-eo/10-preferences.xml b/AnkiDroid/src/main/res/values-eo/10-preferences.xml index 200705c1249a..e6ecc9e048bf 100644 --- a/AnkiDroid/src/main/res/values-eo/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-eo/10-preferences.xml @@ -24,7 +24,8 @@ --> - Malaktiva + Enabled + Malaktiva %d min %d min @@ -72,7 +73,7 @@ Vidigi dosiernomojn de sonoj en la kampoj “demando”/“respondo” de la kart‑foliumilo Dosierujo de AnkiDroid Plenekrana reĝimo - Malaktiva + Malaktiva Kaŝi striojn de operaciumo Kaŝi striojn de operaciumo kaj respondobutonojn Pozicio de respondobutonoj @@ -213,11 +214,80 @@ Navigi Aŭdvidaĵo Aliaj - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Aldoni memorigon - Aldoni memorigon pri ripetado - Redakti memorigon pri ripetado Vi havas neniun memorigon pri ripetado + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Aldoni memorigon pri ripetado + Redakti memorigon pri ripetado + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Elekti etikedojn diff --git a/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml b/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml index 287fbbe1a030..11f86b3de489 100644 --- a/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml +++ b/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml @@ -29,7 +29,7 @@ Nota guardada Mazo de tarjetas: - Mazo: + Mazo: Tipo: Etiquetas: %1$s Tarjetas: %1$s @@ -220,6 +220,8 @@ Tamaño de Letra Mostrar barra de herramientas + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml b/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml index 77438f9151cd..5d603a10e6f3 100644 --- a/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml @@ -24,7 +24,8 @@ --> - Deshabilitado + Enabled + Deshabilitado %d minuto %d minutos @@ -72,7 +73,7 @@ Mostrar nombres de archivos de audio en los campos de pregunta/respuesta del navegador de tarjetas Directorio de AnkiDroid Pantalla completa - Apagado + Apagado Ocultar las barras del sistema Ocultar las barras del sistema y los botones de respuesta Posición de los botones de respuesta @@ -218,11 +219,80 @@ Navegación Multimedia Varios - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Añadir recordatorio - Añadir recordatorio de revisión - Editar recordatorio de revisión No tienes recordatorios de revisión + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Añadir recordatorio de revisión + Editar recordatorio de revisión + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Seleccionar etiquetas diff --git a/AnkiDroid/src/main/res/values-es-rES/02-strings.xml b/AnkiDroid/src/main/res/values-es-rES/02-strings.xml index ba6b0efda18c..627a810f8c43 100644 --- a/AnkiDroid/src/main/res/values-es-rES/02-strings.xml +++ b/AnkiDroid/src/main/res/values-es-rES/02-strings.xml @@ -29,7 +29,7 @@ Nota guardada Mazo de tarjetas: - Mazo: + Mazo: Tipo: Etiquetas: %1$s Tarjetas: %1$s @@ -220,6 +220,8 @@ Tamaño de Letra Mostrar barra de herramientas + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml b/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml index cfa7a99ca035..4c0b0deeb1f3 100644 --- a/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml @@ -24,7 +24,8 @@ --> - Deshabilitado + Enabled + Deshabilitado %d min %d mins @@ -72,7 +73,7 @@ Mostrar nombres de archivos de audio en los campos de pregunta/respuesta del navegador de tarjetas Carpeta de AnkiDroid Pantalla completa - Desactivado + Desactivado Ocultar las barras de sistema Ocultar las barras de sistema y los botones de respuesta Posición de botones de respuesta @@ -218,11 +219,80 @@ Navegación Multimedia Varios - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Añadir recordatorio - Añadir recordatorio de revisión - Editar recordatorio de revisión No tienes recordatorios de revisión + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Añadir recordatorio de revisión + Editar recordatorio de revisión + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Seleccionar etiquetas diff --git a/AnkiDroid/src/main/res/values-et/02-strings.xml b/AnkiDroid/src/main/res/values-et/02-strings.xml index e548ea83f83f..889cd3fcacee 100644 --- a/AnkiDroid/src/main/res/values-et/02-strings.xml +++ b/AnkiDroid/src/main/res/values-et/02-strings.xml @@ -29,7 +29,7 @@ Märkus salvestatud Kaardipakk: - Kaardipakk: + Kaardipakk: Tüüp: Sildid: %1$s Kaardid: %1$s @@ -220,6 +220,8 @@ Teksti suurus Näita tööriistariba + Show preview + Hide preview Rasvane kiri Kaldkiri Allajoonitud diff --git a/AnkiDroid/src/main/res/values-et/10-preferences.xml b/AnkiDroid/src/main/res/values-et/10-preferences.xml index 793369e71e6d..63aac86a2dd7 100644 --- a/AnkiDroid/src/main/res/values-et/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-et/10-preferences.xml @@ -24,7 +24,8 @@ --> - Keelatud + Enabled + Keelatud %d minut %d minutit @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroidi kaust Täisekraani režiim - Väljas + Väljas Hide the system bars Hide the system bars and answer buttons Vastuse nuppude asukoht @@ -219,11 +220,80 @@ Navigatsioon Meedia Varia - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Lisa meeldetuletus - Lisa kordamise meeldetuletus - Muuda kordamise meeldetuletust Sul pole ühtegi kordamise meeldetuletust + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Lisa kordamise meeldetuletus + Muuda kordamise meeldetuletust + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Vali sildid diff --git a/AnkiDroid/src/main/res/values-eu/02-strings.xml b/AnkiDroid/src/main/res/values-eu/02-strings.xml index 9fd8d87eb46d..bc4e7ee9a0ef 100644 --- a/AnkiDroid/src/main/res/values-eu/02-strings.xml +++ b/AnkiDroid/src/main/res/values-eu/02-strings.xml @@ -29,7 +29,7 @@ Note saved Txartel-sorta: - Sorta: + Sorta: Mota: Etiketak: %1$s Cards: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-eu/10-preferences.xml b/AnkiDroid/src/main/res/values-eu/10-preferences.xml index 68358e8124a6..c0186b28b4a4 100644 --- a/AnkiDroid/src/main/res/values-eu/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-eu/10-preferences.xml @@ -24,7 +24,8 @@ --> - Desgaituta + Enabled + Desgaituta %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-fa/02-strings.xml b/AnkiDroid/src/main/res/values-fa/02-strings.xml index 0a768dfa0241..4e84ddf873e1 100644 --- a/AnkiDroid/src/main/res/values-fa/02-strings.xml +++ b/AnkiDroid/src/main/res/values-fa/02-strings.xml @@ -29,7 +29,7 @@ یادداشت ذخیره شد دسته کارت: - دسته: + دسته: نوع: برچسب ها: %1$s کارت‌ها: %1$s @@ -222,6 +222,8 @@ اندازه فونت نمایش نوار ابزار + Show preview + Hide preview قالب‌حروف به‌صورت پررنگ قالب حروف با استایل مورب قاب‌بندی خط‌کشی زیر متن diff --git a/AnkiDroid/src/main/res/values-fa/10-preferences.xml b/AnkiDroid/src/main/res/values-fa/10-preferences.xml index d67407b5be79..639b8dfa7c24 100644 --- a/AnkiDroid/src/main/res/values-fa/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fa/10-preferences.xml @@ -24,7 +24,8 @@ --> - غیر فعال شده + Enabled + غیر فعال شده %d دقیقه %d دقیقه @@ -72,7 +73,7 @@ نمایش نام فایل‌های صوتی در کادر‌های پرسش/پاسخ مرورگر کارت مسیر قرارگیری AnkiDroid حالت تمام‌صفحه - خاموش + خاموش پنهان کردن نوار سیستم پنهان کردن نوارهای سیستم و دکمه های پاسخ موقعیت دکمه های پاسخ @@ -217,11 +218,80 @@ هدایت رسانه سایر - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders افزودن یادآوری - ایجاد یادآوری برای مرور - ویرایش یادآور مرور شما هیچ یادآوری برای مرور ندارید + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + ایجاد یادآوری برای مرور + ویرایش یادآور مرور + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + انتخاب برچسبها diff --git a/AnkiDroid/src/main/res/values-fi/02-strings.xml b/AnkiDroid/src/main/res/values-fi/02-strings.xml index aeca6d16a84e..3772ce4a602e 100644 --- a/AnkiDroid/src/main/res/values-fi/02-strings.xml +++ b/AnkiDroid/src/main/res/values-fi/02-strings.xml @@ -29,7 +29,7 @@ Merkintä tallennettu Korttipakka: - Pakka: + Pakka: Tyyppi: Tunnisteet: %1$s Kortit: %1$s @@ -220,6 +220,8 @@ Fonttikoko Näytä työkalupalkki + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-fi/10-preferences.xml b/AnkiDroid/src/main/res/values-fi/10-preferences.xml index 515cb5a0e930..2f6765143d59 100644 --- a/AnkiDroid/src/main/res/values-fi/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fi/10-preferences.xml @@ -24,7 +24,8 @@ --> - Ei käytössä + Enabled + Ei käytössä %d min %d min @@ -72,7 +73,7 @@ Näytä äänten tiedostonimet korttiselaimen kysymys-/vastaus-kentissä AnkiDroid-hakemisto Koko näytön tila - Pois päältä + Pois päältä Piilota järjestelmäpalkit Piilota järjestelmäpalkit ja vastauspainikkeet Vastauspainikkeiden sijainti @@ -217,11 +218,80 @@ Liikkuminen Media Sekalaiset - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Lisää muistutus - Lisää läpikäyntimuistutus - Muokkaa läpikäyntimuistutusta Sinulla ei ole läpikäyntimuistutuksia + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Lisää läpikäyntimuistutus + Muokkaa läpikäyntimuistutusta + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Valitse tunnisteet diff --git a/AnkiDroid/src/main/res/values-fil/02-strings.xml b/AnkiDroid/src/main/res/values-fil/02-strings.xml index 09d1a883d9b9..ef347bf4a06a 100644 --- a/AnkiDroid/src/main/res/values-fil/02-strings.xml +++ b/AnkiDroid/src/main/res/values-fil/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Uri: Mga Tag:%1$s Mga Card: %1$s @@ -220,6 +220,8 @@ Laki ng font Ipakita ang toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-fil/10-preferences.xml b/AnkiDroid/src/main/res/values-fil/10-preferences.xml index b82755a7daf4..4dfc8bdd621d 100644 --- a/AnkiDroid/src/main/res/values-fil/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fil/10-preferences.xml @@ -24,7 +24,8 @@ --> - Hindi pinapagana + Enabled + Hindi pinapagana %d min %d min @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Itago ang mga bar ng system Itago ang mga system bar at sagutin ang mga pindutan Sagot na mga pindutan ng mga pindutan @@ -218,11 +219,80 @@ Nabigasyon Media Iba pa - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Piliin ang mga tag diff --git a/AnkiDroid/src/main/res/values-fr/02-strings.xml b/AnkiDroid/src/main/res/values-fr/02-strings.xml index 11322ceefcd8..01efc1fe3024 100644 --- a/AnkiDroid/src/main/res/values-fr/02-strings.xml +++ b/AnkiDroid/src/main/res/values-fr/02-strings.xml @@ -29,7 +29,7 @@ Note sauvegardée Paquet de cartes : - Paquet : + Paquet : Type : Étiquettes : %1$s Cartes : %1$s @@ -220,6 +220,8 @@ Taille de la police Afficher la barre d\'outils + Show preview + Hide preview Formater en gras Formater en italique Souligner diff --git a/AnkiDroid/src/main/res/values-fr/10-preferences.xml b/AnkiDroid/src/main/res/values-fr/10-preferences.xml index 4bff6ebc03e8..3ed893a20cec 100644 --- a/AnkiDroid/src/main/res/values-fr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fr/10-preferences.xml @@ -24,7 +24,8 @@ --> - Désactivé + Enabled + Désactivé %d min %d mins @@ -72,7 +73,7 @@ Afficher le nom des fichiers audio dans les champs questions/ réponses du navigateur de cartes. Répertoire de AnkiDroid Mode plein écran - Inactif + Inactif Masquer les barres système Masquer les barres système et les boutons de réponse Position des boutons de réponse @@ -218,11 +219,80 @@ Navigation Média Divers - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Ajouter un rappel - Ajouter un rappel de révision - Modifier le rappel de révision Vous n\'avez aucun rappel de révision + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Ajouter un rappel de révision + Modifier le rappel de révision + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Sélectionner les étiquettes diff --git a/AnkiDroid/src/main/res/values-fy/02-strings.xml b/AnkiDroid/src/main/res/values-fy/02-strings.xml index c5c548502f24..890ff67b9ce0 100644 --- a/AnkiDroid/src/main/res/values-fy/02-strings.xml +++ b/AnkiDroid/src/main/res/values-fy/02-strings.xml @@ -29,7 +29,7 @@ Note saved Kaartenset: - Set: + Set: Type: Skaaimerken: %1$s Cards: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-fy/10-preferences.xml b/AnkiDroid/src/main/res/values-fy/10-preferences.xml index 22ad3f8f77db..000649529766 100644 --- a/AnkiDroid/src/main/res/values-fy/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fy/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-ga/02-strings.xml b/AnkiDroid/src/main/res/values-ga/02-strings.xml index 406971bfbbf0..29de2baca9ba 100644 --- a/AnkiDroid/src/main/res/values-ga/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ga/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Type: Tags: %1$s Cards: %1$s @@ -229,6 +229,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ga/10-preferences.xml b/AnkiDroid/src/main/res/values-ga/10-preferences.xml index ffeb4b5caf2f..ae6a8142aedc 100644 --- a/AnkiDroid/src/main/res/values-ga/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ga/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -75,7 +76,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -225,11 +226,83 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-gl/01-core.xml b/AnkiDroid/src/main/res/values-gl/01-core.xml index e94581a2ea05..623467b556df 100644 --- a/AnkiDroid/src/main/res/values-gl/01-core.xml +++ b/AnkiDroid/src/main/res/values-gl/01-core.xml @@ -127,8 +127,8 @@ Ler e escribir na base de datos do AnkiDroid acceder ás notas, tarxetas, tipos de notas e barallas existentes, así como crear novas - Card browser - Anki card + Explorador de tarxetas + Tarxeta Anki Non se poden combinar as coleccións.\nQue colección queres manter? AnkiDroid diff --git a/AnkiDroid/src/main/res/values-gl/02-strings.xml b/AnkiDroid/src/main/res/values-gl/02-strings.xml index 68d8ffb5ed0b..988a5441f4f0 100644 --- a/AnkiDroid/src/main/res/values-gl/02-strings.xml +++ b/AnkiDroid/src/main/res/values-gl/02-strings.xml @@ -29,7 +29,7 @@ Nota gardada Baralla de tarxetas: - Baralla: + Baralla: Tipo: Etiquetas: %1$s Tarxetas: %1$s @@ -220,6 +220,8 @@ Tamaño da letra Amosar barra de ferramentas + Show preview + Hide preview Aplicar letra grosa Aplicar cursiva Aplicar subliñado diff --git a/AnkiDroid/src/main/res/values-gl/10-preferences.xml b/AnkiDroid/src/main/res/values-gl/10-preferences.xml index 625d43d8a8e5..da9e379274f2 100644 --- a/AnkiDroid/src/main/res/values-gl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-gl/10-preferences.xml @@ -24,7 +24,8 @@ --> - Desactivado + Enabled + Desactivado %d min %d mins @@ -72,7 +73,7 @@ Amosar os nomes dos ficheiros de son nos campos de pregunta/resposta no explorador de tarxetas Directorio do AnkiDroid Modo pantalla completa - Desactivado + Desactivado Ocultar as barras do sistema Ocultar as barras do sistema e os botóns de resposta Posición dos botóns de resposta @@ -107,7 +108,7 @@ Toque con tres dedos Toque con catro dedos Axitar o dispositivo - ‘%s’ menu + Menú «%s» Activar globalmente o menú contextual ‘%s’ Desprazamento dobre Duplicar a distancia de desprazamento co eReader @@ -219,11 +220,80 @@ Navegación Multimedia Outros - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Engadir recordatorio - Engadir recordatorio de repaso - Editar recordatorio de repaso Non tes recordatorios de repaso + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Engadir recordatorio de repaso + Editar recordatorio de repaso + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Seleccionar etiquetas diff --git a/AnkiDroid/src/main/res/values-got/02-strings.xml b/AnkiDroid/src/main/res/values-got/02-strings.xml index cbd5c6eb5d8a..a971340f88ea 100644 --- a/AnkiDroid/src/main/res/values-got/02-strings.xml +++ b/AnkiDroid/src/main/res/values-got/02-strings.xml @@ -29,7 +29,7 @@ 𐌼𐌴𐌻𐌴𐌹𐌽𐍃 𐌲𐌰𐍆𐌰𐍃𐍄𐌰𐌽𐌰 𐌺𐌰𐍂𐍄𐌰𐌷𐌿𐌶𐌳: - 𐌲𐌰𐌻𐌹𐍃𐌰 𐌺𐌰𐍂𐍄𐍉 + 𐌲𐌰𐌻𐌹𐍃𐌰 𐌺𐌰𐍂𐍄𐍉 𐌺𐌹𐌽𐌸𐍃: 𐍃𐍉𐌺𐌽𐌹𐍅𐌰𐌿𐍂𐌳𐌰: %1$s 𐌺𐌰𐍂𐍄𐍉𐍃: %1$s @@ -220,6 +220,8 @@ 𐌼𐌹𐌺𐌹𐌻𐌴𐌹 𐍆𐌰𐌿𐌽𐍄𐌹𐍃 𐍄𐍉𐌻𐌰-𐌰𐌽𐍃 𐌱𐌰𐌽𐌳𐍅𐌾𐌰𐌽 + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-got/10-preferences.xml b/AnkiDroid/src/main/res/values-got/10-preferences.xml index 48962545c894..8a6c2ff032e3 100644 --- a/AnkiDroid/src/main/res/values-got/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-got/10-preferences.xml @@ -24,7 +24,8 @@ --> - 𐌿𐌽𐌲𐌰𐍄𐌰𐍅𐌹𐌸 + Enabled + 𐌿𐌽𐌲𐌰𐍄𐌰𐍅𐌹𐌸 %d 𐌼𐌹𐌽 %d 𐌼𐌹𐌽𐍃 @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields 𐌰𐌽𐌺𐌴𐌹𐌳𐍂𐌰𐌿𐌳 𐌳𐌹𐍂𐌰𐌹𐌺𐍄𐌰𐌿𐍂𐌹 𐍆𐌿𐌻𐌻𐌰𐍃𐌺𐌰𐌹𐍂𐌼𐌹𐍃 𐌼𐍉𐌳𐌿𐍃 - 𐌿𐍄 + 𐌿𐍄 Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ 𐌽𐌰𐌱𐌹𐌲𐌰𐍄𐍃𐌾𐍉 𐌼𐌰𐌹𐌳𐌾𐌰 𐌰𐌽𐌸𐌰𐍂 - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders 𐌲𐌰𐌼𐌿𐌽𐌳 𐌱𐌹𐌰𐌿𐌺𐌰𐌽 - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + 𐍃𐍉𐌺𐌽𐌹𐍅𐌰𐌿𐍂𐌳𐌰 𐍅𐌰𐌻𐌾𐌰𐌽 diff --git a/AnkiDroid/src/main/res/values-gu/02-strings.xml b/AnkiDroid/src/main/res/values-gu/02-strings.xml index 82e91dec792d..c9fa7e32a29f 100644 --- a/AnkiDroid/src/main/res/values-gu/02-strings.xml +++ b/AnkiDroid/src/main/res/values-gu/02-strings.xml @@ -29,7 +29,7 @@ Note saved કાર્ડ ડેક: - તૂતક: + તૂતક: પ્રકાર: ટૅગ્સ: %1$s કાર્ડ્સ: %1$s @@ -220,6 +220,8 @@ અક્ષર ની જાડાઈ ટૂલબાર બતાવો + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-gu/10-preferences.xml b/AnkiDroid/src/main/res/values-gu/10-preferences.xml index 6d936eedf4ac..cf203ecbaa84 100644 --- a/AnkiDroid/src/main/res/values-gu/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-gu/10-preferences.xml @@ -24,7 +24,8 @@ --> - અક્ષમ + Enabled + અક્ષમ %d મિનિટ %d મિનિટ @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid ડિરેક્ટરી પૂર્ણસ્ક્રીન મોડ - બંધ + બંધ સિસ્ટમ બાર છુપાવો સિસ્ટમ બાર અને જવાબ બટનો છુપાવો Answer buttons position @@ -218,11 +219,80 @@ સંશોધક મીડિયા વિવિધ - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + ટૅગ્સ પસંદ કરો diff --git a/AnkiDroid/src/main/res/values-heb/02-strings.xml b/AnkiDroid/src/main/res/values-heb/02-strings.xml index 7f9a9ff2df01..eb9130b791b0 100644 --- a/AnkiDroid/src/main/res/values-heb/02-strings.xml +++ b/AnkiDroid/src/main/res/values-heb/02-strings.xml @@ -29,7 +29,7 @@ הרשומה נשמרה חפיסת כרטיסים:‏ - חפיסה:‏ + חפיסה:‏ סוג: תגיות: %1$s כרטיסים: %1$s @@ -223,6 +223,8 @@ גודל גופן הצגת סרגל הכלים + Show preview + Hide preview עצב כמודגש עצב כנטוי עצב עם קו תחתון diff --git a/AnkiDroid/src/main/res/values-heb/10-preferences.xml b/AnkiDroid/src/main/res/values-heb/10-preferences.xml index 67664fd18432..7ea96d606933 100644 --- a/AnkiDroid/src/main/res/values-heb/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-heb/10-preferences.xml @@ -24,7 +24,8 @@ --> - מבוטל + Enabled + מבוטל דקה אחת %d דקות @@ -73,7 +74,7 @@ הצגת שמות קבצי שמע בשדות השאלות/תשובות של דפדפן הכרטיסים ספריית AnkiDroid מצב מסך מלא - כבוי + כבוי הסתרת שורות המערכת הסתרת שורות המערכת וכפתורי התשובה מיקום כפתור המענה @@ -219,11 +220,81 @@ ניווט מדיה אחר - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders הוספת תזכורת - הוסף תזכורת לסקירה - תזכורת עריכת סקירה You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + הוסף תזכורת לסקירה + תזכורת עריכת סקירה + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + בחר תגיות diff --git a/AnkiDroid/src/main/res/values-hi/02-strings.xml b/AnkiDroid/src/main/res/values-hi/02-strings.xml index 03e5140832d0..a974dbadff0f 100644 --- a/AnkiDroid/src/main/res/values-hi/02-strings.xml +++ b/AnkiDroid/src/main/res/values-hi/02-strings.xml @@ -29,7 +29,7 @@ टिप्पणी सुरक्षित पत्ता डेक: - डेक: + डेक: प्रकार: टैग: %1$s पत्ते: %1$s @@ -220,6 +220,8 @@ फ़ॉन्ट आकार टूलबार दिखाएं + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-hi/10-preferences.xml b/AnkiDroid/src/main/res/values-hi/10-preferences.xml index 09e6d4d88b2a..5cc51a065e89 100644 --- a/AnkiDroid/src/main/res/values-hi/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hi/10-preferences.xml @@ -24,7 +24,8 @@ --> - अक्षम + Enabled + अक्षम %d मिनट %d मिनट @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid निर्देशिका फुल स्क्रीन मोड - ऑफ + ऑफ सिस्टम पट्टियां छुपाएं सिस्टम पट्टियां और उत्तर बटन छुपाएं उत्तर बटन स्थिति @@ -214,11 +215,80 @@ नेविगेशन मिडिया विविध - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + टैग का चयन करें diff --git a/AnkiDroid/src/main/res/values-hr/02-strings.xml b/AnkiDroid/src/main/res/values-hr/02-strings.xml index b907f1c56d27..5386f5e9dd6d 100644 --- a/AnkiDroid/src/main/res/values-hr/02-strings.xml +++ b/AnkiDroid/src/main/res/values-hr/02-strings.xml @@ -29,7 +29,7 @@ Bilješka spremljena Špil karata: - Špil: + Špil: Tip: Oznake: %1$s Karte: %1$s @@ -223,6 +223,8 @@ Veličina fonta Pokaži alatnu traku + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-hr/10-preferences.xml b/AnkiDroid/src/main/res/values-hr/10-preferences.xml index 3eccf418ebda..35057498d8af 100644 --- a/AnkiDroid/src/main/res/values-hr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hr/10-preferences.xml @@ -24,7 +24,8 @@ --> - Onemogućeno + Enabled + Onemogućeno %d min %d min @@ -73,7 +74,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid direktorij Prikaz na cijelom zaslonu - Ugašeno + Ugašeno Sakrij trake sustava Sakrij trake sustava i gumbe za odgovore Pozicije gumba za odgovore @@ -220,11 +221,81 @@ Navigacija Mediji Razno - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Dodaj podsjetnik - Dodaj podsjetnik za ponavljanje - Uredi podsjetnik za ponavljanje Nemate podsjetnika za ponavljanje + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Dodaj podsjetnik za ponavljanje + Uredi podsjetnik za ponavljanje + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + Odaberi oznake diff --git a/AnkiDroid/src/main/res/values-hu/02-strings.xml b/AnkiDroid/src/main/res/values-hu/02-strings.xml index 7f1168b0afa6..774f21451d64 100644 --- a/AnkiDroid/src/main/res/values-hu/02-strings.xml +++ b/AnkiDroid/src/main/res/values-hu/02-strings.xml @@ -29,7 +29,7 @@ Jegyzet elmentve Kártyapakli: - Pakli: + Pakli: Típus: Címkék: %1$s Címkék: %1$s @@ -220,6 +220,8 @@ Betűméret Eszköztár megjelenítése + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-hu/10-preferences.xml b/AnkiDroid/src/main/res/values-hu/10-preferences.xml index 4794a8f3dd8b..3b269140a6e9 100644 --- a/AnkiDroid/src/main/res/values-hu/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hu/10-preferences.xml @@ -24,7 +24,8 @@ --> - Kikapcsolva + Enabled + Kikapcsolva %d perc %d perc @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid könyvtár Teljes képernyős mód - Kikapcsol + Kikapcsol Rendszer sávok elrejtése Rendszer sávok és válasz gombok elrejtése A válasz gombok elhelyezkedése @@ -217,11 +218,80 @@ Navigáció Média Egyéb - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Válassz címkét diff --git a/AnkiDroid/src/main/res/values-hy/02-strings.xml b/AnkiDroid/src/main/res/values-hy/02-strings.xml index 5870717c1a12..98bda81b1161 100644 --- a/AnkiDroid/src/main/res/values-hy/02-strings.xml +++ b/AnkiDroid/src/main/res/values-hy/02-strings.xml @@ -29,7 +29,7 @@ Պահպանված չէ Քարտերի կապուկ՝ - Կապուկ՝ + Կապուկ՝ Տեսակ՝ Պիտակներ՝ %1$s Քարտեր՝ %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-hy/10-preferences.xml b/AnkiDroid/src/main/res/values-hy/10-preferences.xml index 3d40685b0f1b..6ac61daeecc8 100644 --- a/AnkiDroid/src/main/res/values-hy/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hy/10-preferences.xml @@ -24,7 +24,8 @@ --> - Անջատված + Enabled + Անջատված %d րոպե %d րոպե @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid թղթապանակ Ամբողջական էկրանի ռեժիմ - Անջատված + Անջատված Hide the system bars Hide the system bars and answer buttons Պատասխանի կոճակների տեղադրություն @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-ind/02-strings.xml b/AnkiDroid/src/main/res/values-ind/02-strings.xml index fb0f4795c0f8..9b807cbe8f90 100644 --- a/AnkiDroid/src/main/res/values-ind/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ind/02-strings.xml @@ -29,7 +29,7 @@ Catatan Disimpan Dek kartu: - Dek: + Dek: Tipe: Tag: %1$s Kartu: %1$s @@ -217,6 +217,8 @@ Ukuran font Tampilkan toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ind/10-preferences.xml b/AnkiDroid/src/main/res/values-ind/10-preferences.xml index fb004e2dc205..de7d203aca0f 100644 --- a/AnkiDroid/src/main/res/values-ind/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ind/10-preferences.xml @@ -24,7 +24,8 @@ --> - Nonaktif + Enabled + Nonaktif %d menit @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields Direktori AnkiDroid Mode layar penuh - Mati + Mati Sembunyikan bar sistem Sembunyikan bar sistem dan tombol jawab Posisi tombol jawab @@ -216,11 +217,79 @@ Navigasi Media Lainnya - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + Pilih tag diff --git a/AnkiDroid/src/main/res/values-it/02-strings.xml b/AnkiDroid/src/main/res/values-it/02-strings.xml index 23cc5bfd9a02..273546ac4ee7 100644 --- a/AnkiDroid/src/main/res/values-it/02-strings.xml +++ b/AnkiDroid/src/main/res/values-it/02-strings.xml @@ -29,7 +29,7 @@ Nota salvata Mazzo: - Mazzo: + Mazzo: Tipo: Etichette: %1$s Carte: %1$s @@ -220,6 +220,8 @@ Dimensione Carattere Mostra barra strumenti + Show preview + Hide preview Formatta in grassetto Formata in corsivo Formatta come sottolineato diff --git a/AnkiDroid/src/main/res/values-it/10-preferences.xml b/AnkiDroid/src/main/res/values-it/10-preferences.xml index b48d44193f56..378a9c4c6587 100644 --- a/AnkiDroid/src/main/res/values-it/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-it/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabilitato + Enabled + Disabilitato %d min %d min @@ -72,7 +73,7 @@ Mostra i nomi dei file multimediali nei campi domanda/risposta del browser delle carte Cartella AnkiDroid Modalità a schermo intero - Disabilitato + Disabilitato Nascondi le barre di sistema Nascondi le barre di sistema e i pulsanti di risposta Posizione dei pulsanti di risposta @@ -218,11 +219,80 @@ Navigazione Media Miscellanea - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Aggiungi promemoria - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Seleziona etichette diff --git a/AnkiDroid/src/main/res/values-iw/02-strings.xml b/AnkiDroid/src/main/res/values-iw/02-strings.xml index 7f9a9ff2df01..eb9130b791b0 100644 --- a/AnkiDroid/src/main/res/values-iw/02-strings.xml +++ b/AnkiDroid/src/main/res/values-iw/02-strings.xml @@ -29,7 +29,7 @@ הרשומה נשמרה חפיסת כרטיסים:‏ - חפיסה:‏ + חפיסה:‏ סוג: תגיות: %1$s כרטיסים: %1$s @@ -223,6 +223,8 @@ גודל גופן הצגת סרגל הכלים + Show preview + Hide preview עצב כמודגש עצב כנטוי עצב עם קו תחתון diff --git a/AnkiDroid/src/main/res/values-iw/10-preferences.xml b/AnkiDroid/src/main/res/values-iw/10-preferences.xml index 67664fd18432..7ea96d606933 100644 --- a/AnkiDroid/src/main/res/values-iw/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-iw/10-preferences.xml @@ -24,7 +24,8 @@ --> - מבוטל + Enabled + מבוטל דקה אחת %d דקות @@ -73,7 +74,7 @@ הצגת שמות קבצי שמע בשדות השאלות/תשובות של דפדפן הכרטיסים ספריית AnkiDroid מצב מסך מלא - כבוי + כבוי הסתרת שורות המערכת הסתרת שורות המערכת וכפתורי התשובה מיקום כפתור המענה @@ -219,11 +220,81 @@ ניווט מדיה אחר - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders הוספת תזכורת - הוסף תזכורת לסקירה - תזכורת עריכת סקירה You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + הוסף תזכורת לסקירה + תזכורת עריכת סקירה + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + בחר תגיות diff --git a/AnkiDroid/src/main/res/values-ja/01-core.xml b/AnkiDroid/src/main/res/values-ja/01-core.xml index 04962518b3ab..5513eb7a7693 100644 --- a/AnkiDroid/src/main/res/values-ja/01-core.xml +++ b/AnkiDroid/src/main/res/values-ja/01-core.xml @@ -122,8 +122,8 @@ AnkiDroidデータベースの読み取りと書き込み ノート、カード、ノートタイプ、デッキへのアクセスや、それらの新規作成を行います。 - Card browser - Anki card + AnkiDroid検索 + Ankiカード コレクションを統合できません。\nどちらのコレクションを保持しますか? AnkiDroid diff --git a/AnkiDroid/src/main/res/values-ja/02-strings.xml b/AnkiDroid/src/main/res/values-ja/02-strings.xml index 02790e213ad7..303ce999cf75 100644 --- a/AnkiDroid/src/main/res/values-ja/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ja/02-strings.xml @@ -29,7 +29,7 @@ ノートを保存しました カードデッキ: - デッキ: + デッキ: ノートタイプ: タグ: %1$s カード: %1$s @@ -169,7 +169,7 @@ 既存のタグ「%1$s」を選択しました この名前のタグがすでに存在します この名前のデッキがすでに存在します - Consider backing up your collection from DeckPicker before using this option! + この機能を使用する前にバックアップを作成しておくことを強くおすすめします(メイン画面右上の「⋮」→「バックアップを作成」)。 Ankiファイル(*.apkg)の共有中にエラーが発生しました @@ -211,12 +211,14 @@ カードコンテンツエラー: ’%s’ の読み込みに失敗しました デッキ名を検索 - Fatal error + 致命的なエラー AnkiDroidはAndroidシステムのWebViewに依存していますが、このWebViewが現在使用できません。この状況は、システムがアップデートプログラムをインストールしている場合に発生する可能性があります。数分後に再試行してください。\n\n%s Androidのエラーのため、AnkiDroidはストレージにアクセスできません。\n\n%s フォントサイズ ツールバーを表示 + Show preview + Hide preview 太字 斜体 下線 diff --git a/AnkiDroid/src/main/res/values-ja/07-cardbrowser.xml b/AnkiDroid/src/main/res/values-ja/07-cardbrowser.xml index 2f2072c001dc..32777f4f277d 100644 --- a/AnkiDroid/src/main/res/values-ja/07-cardbrowser.xml +++ b/AnkiDroid/src/main/res/values-ja/07-cardbrowser.xml @@ -89,13 +89,13 @@ ソート(並び替え)しない 並べ替え処理をしない分、表示されるまでの時間が短くなります - Sort by %1$s - A–Z - Z–A - Low to high - High to low - Oldest first - Newest first + ソート: %1$s + 昇順 + 降順 + 低い順 + 高い順 + 古い順 + 新しい順 A diff --git a/AnkiDroid/src/main/res/values-ja/10-preferences.xml b/AnkiDroid/src/main/res/values-ja/10-preferences.xml index 7028584cf355..d18fd05a1f97 100644 --- a/AnkiDroid/src/main/res/values-ja/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ja/10-preferences.xml @@ -24,7 +24,8 @@ --> - 無効 + Enabled + 無効 %d分 @@ -48,13 +49,13 @@ 編集 セキュリティ - Globally allow dangerous JavaScript APIs - Allows any card or template to read and modify other cards, or your Anki collection data.\nLeave off unless you trust all cards and templates in your collection. + JavaScript API を包括的に許可 + すべてのカードやテンプレートにおいて、他のカードやコレクションデータの読み取りや変更を可能にします。\n他者が作成した悪意や不具合のあるカードなどによる被害を防ぐため、そのような機能を与えるほどには信用していないカードやテンプレートがコレクション内にある場合は、このオプションはオンにしないでください。 Security: Unsafe JavaScript call blocked.\nIf you trust all cards and templates in your collection, this can be unblocked in the advanced settings - Allow cards to launch other apps + カード内で別のアプリの起動を許可 Cards can open other apps on your device, triggering potentially unwanted actions.\nLeave off unless you trust all cards and templates in your collection. - Security: Card blocked from launching another app.\nIf you trust all cards and templates in your collection, this can be unblocked in the advanced settings + 安全を確保するため、別のアプリの起動をブロックしました。\nコレクション内のすべてのカードとテンプレートを信頼している場合、ブロックを解除することができます(「設定」→「高度な設定」→「カード内で別のアプリの起動を許可」) 線の太さ 答えを入力する欄をカード内に表示 @@ -71,7 +72,7 @@ カードブラウザのリスト画面の質問欄と解答欄に、メディア(画像・音声・動画)ファイル名も表示します AnkiDroidディレクトリ フルスクリーンモード - オフ + オフ ステータスバー(通知バー)を隠す ステータスバー(通知バー)と解答表示ボタン・回答ボタンを隠す 解答表示ボタン・回答ボタンの位置 @@ -216,11 +217,79 @@ 画面移動 音声・動画 その他 - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders リマインダーを追加 - 学習リマインダーを追加 - 学習リマインダーを編集 学習のリマインダーがありません + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + 学習リマインダーを追加 + 学習リマインダーを編集 + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + タグを選択 @@ -261,8 +330,8 @@ ユーザーアクション 8 ユーザーアクション 9 カードの自動送りをオン / オフ - Enable auto advance - Disable auto advance + カードの自動送りをオン + カードの自動送りをオフ 発音チェック機能をオン 発音チェック機能をオフ diff --git a/AnkiDroid/src/main/res/values-ka/02-strings.xml b/AnkiDroid/src/main/res/values-ka/02-strings.xml index 65e2b8f2e3b3..93d4595cf4dd 100644 --- a/AnkiDroid/src/main/res/values-ka/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ka/02-strings.xml @@ -29,7 +29,7 @@ Note saved ბარათის დასტა: - დასტა: + დასტა: ტიპი: იარლიყები: %1$s ბარათები: %1$s @@ -220,6 +220,8 @@ ტექსტის ზომა ინსტრუმენტების პანელის ჩვენება + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ka/10-preferences.xml b/AnkiDroid/src/main/res/values-ka/10-preferences.xml index 79aa176bdcfa..21a3131a4f2b 100644 --- a/AnkiDroid/src/main/res/values-ka/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ka/10-preferences.xml @@ -24,7 +24,8 @@ --> - გამორთულია + Enabled + გამორთულია %d წთ %d წთ @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid-ის საქაღალდე სრულეკრანიანი რეჟიმი - გამორთულია + გამორთულია სისტემის ზოლების დამალვა სისტემის ზოლების და პასუხის ღილაკების დამალვა პასუხის ღილაკის ადგილმდებარეობა @@ -218,11 +219,80 @@ ნავიგაცია მედიაფაილები სხვადასხვა - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + იარლიყების მონიშვნა diff --git a/AnkiDroid/src/main/res/values-kk/02-strings.xml b/AnkiDroid/src/main/res/values-kk/02-strings.xml index 49d3076932e4..7cd1529bac88 100644 --- a/AnkiDroid/src/main/res/values-kk/02-strings.xml +++ b/AnkiDroid/src/main/res/values-kk/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Type: Tags: %1$s Cards: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-kk/10-preferences.xml b/AnkiDroid/src/main/res/values-kk/10-preferences.xml index 5fba3e8c30c5..96c821077da1 100644 --- a/AnkiDroid/src/main/res/values-kk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-kk/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-km/02-strings.xml b/AnkiDroid/src/main/res/values-km/02-strings.xml index 55f8c484809d..4b89245b4767 100644 --- a/AnkiDroid/src/main/res/values-km/02-strings.xml +++ b/AnkiDroid/src/main/res/values-km/02-strings.xml @@ -29,7 +29,7 @@ Note saved ជាន់របស់កាត - ជាន់ + ជាន់ ប្រភេទ Tags: %1$s Cards: %1$s @@ -217,6 +217,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-km/10-preferences.xml b/AnkiDroid/src/main/res/values-km/10-preferences.xml index 4e7c67606cce..53cb18860319 100644 --- a/AnkiDroid/src/main/res/values-km/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-km/10-preferences.xml @@ -24,7 +24,8 @@ --> - បានបិទ + Enabled + បានបិទ %d mins @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - បិទ + បិទ Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -217,11 +218,79 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + ជ្រើសរើសស្លាក diff --git a/AnkiDroid/src/main/res/values-kn/02-strings.xml b/AnkiDroid/src/main/res/values-kn/02-strings.xml index 475e4b336c78..21ffb6088742 100644 --- a/AnkiDroid/src/main/res/values-kn/02-strings.xml +++ b/AnkiDroid/src/main/res/values-kn/02-strings.xml @@ -29,7 +29,7 @@ Note saved ಕಾರ್ಡ್ ಡೆಕ್: - ಡೆಕ್: + ಡೆಕ್: ಮಾದರಿ: ಟ್ಯಾಗ್ಗಳು:%1$s ಕಾರ್ಡ್‌ಗಳು: %1$s @@ -220,6 +220,8 @@ ಫಾಂಟ್ ಗಾತ್ರ ಉಪಕರಣಪಟ್ಟಿ ಪ್ರದರ್ಶಿಸಿ + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-kn/10-preferences.xml b/AnkiDroid/src/main/res/values-kn/10-preferences.xml index 32ffd50ee1e9..e9b57f073035 100644 --- a/AnkiDroid/src/main/res/values-kn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-kn/10-preferences.xml @@ -24,7 +24,8 @@ --> - ಅಂಗವಿಕಲ + Enabled + ಅಂಗವಿಕಲ %d ನಿಮಿಷ %d ನಿಮಿಷಗಳು @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields ಅಂಕಿಡ್ರಾಯ್ಡ್ ಡೈರೆಕ್ಟರಿ ಪೂರ್ಣಪರದೆ ಮೋಡ್ - ಆಫ್ + ಆಫ್ ಸಿಸ್ಟಮ್ ಬಾರ್ಗಳನ್ನು ಮರೆಮಾಡಿ ಸಿಸ್ಟಮ್ ಬಾರ್‌ಗಳು ಮತ್ತು ಉತ್ತರ ಬಟನ್‌ಗಳನ್ನು ಮರೆಮಾಡಿ ಉತ್ತರ ಬಟನ್‌ಗಳ ಸ್ಥಾನ @@ -216,11 +217,80 @@ ನ್ಯಾವಿಗೇಷನ್ ಮಾಧ್ಯಮ ಇತರರು - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + ಟ್ಯಾಗ್‌ಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ diff --git a/AnkiDroid/src/main/res/values-ko/01-core.xml b/AnkiDroid/src/main/res/values-ko/01-core.xml index fb42f2076d4d..6aa4ab49f35c 100644 --- a/AnkiDroid/src/main/res/values-ko/01-core.xml +++ b/AnkiDroid/src/main/res/values-ko/01-core.xml @@ -122,8 +122,8 @@ AnkiDroid 데이터베이스 읽고 쓰기 기존의 노트, 카드, 노트 유형, 덱에 접근하여 새로운 항목 생성하기 - Card browser - Anki card + Anki에서 찾기 + Anki 카드 추가 컬렉션을 합칠 수 없습니다.\n어떤 컬렉션을 유지하시겠습니까? AnkiDroid diff --git a/AnkiDroid/src/main/res/values-ko/02-strings.xml b/AnkiDroid/src/main/res/values-ko/02-strings.xml index 95d53a5a5b7f..3afa228db020 100644 --- a/AnkiDroid/src/main/res/values-ko/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ko/02-strings.xml @@ -29,7 +29,7 @@ 노트가 저장되었습니다. 카드 덱: - 덱 : + 덱 : 유형: 태그: %1$s 카드: %1$s @@ -110,9 +110,9 @@ 수평으로 뒤집기 수직으로 뒤집기 - 시간 상자에 도달했습니다 + 목표 학습 시간에 도달했습니다. - %1$d 카드가 %2$s에 학습됨 + %2$s분 동안 카드 %1$d개를 복습했습니다 @@ -218,6 +218,8 @@ 글꼴 크기 도구 모음 표시 + Show preview + Hide preview 굵게 기울임꼴 밑줄 @@ -367,5 +369,5 @@ Switch profile 프로필 편집 - Delete profile + 프로필 삭제 diff --git a/AnkiDroid/src/main/res/values-ko/03-dialogs.xml b/AnkiDroid/src/main/res/values-ko/03-dialogs.xml index 3ac47a4a9dae..2acf76ccd9ea 100644 --- a/AnkiDroid/src/main/res/values-ko/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ko/03-dialogs.xml @@ -134,7 +134,7 @@ Too many reminders scheduled. Some will not appear 언어 설정 - Some multilingual keyboards, such as GBoard support changing language when editing text\n\nPlease request the “setImeHintLocales” feature from your keyboard manufacturer if your keyboard does not change language. + 일부 다국어 키보드(예: Gboard)는 텍스트를 편집할 때 언어를 자동으로 변경할 수 있습니다.\n\n사용 중인 키보드에서 언어가 변경되지 않는다면, 키보드 제조사에 \"setImeHintLocales\" 기능 지원을 요청해 주세요. AnkiDroid 사용법 AnkiDroid 매뉴얼 diff --git a/AnkiDroid/src/main/res/values-ko/10-preferences.xml b/AnkiDroid/src/main/res/values-ko/10-preferences.xml index e23b9b132b70..a5f99026667e 100644 --- a/AnkiDroid/src/main/res/values-ko/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ko/10-preferences.xml @@ -24,7 +24,8 @@ --> - 비활성화됨 + Enabled + 비활성화됨 %d분 @@ -34,7 +35,7 @@ 일반 학습 - 시스템 전역 설정 + 시스템 전역 복습 동기화 스케줄링 @@ -71,14 +72,14 @@ 카드 탐색기의 질문/답변 필드에 미디어 파일 이름을 표시합니다. AnkiDroid 디렉토리 전체 화면 모드 - 끔 + 끔 시스템 바 숨기기 시스템 바와 답변 버튼 숨기기 답변 버튼 위치 상단 하단 - 없음 + 숨기기 제스처 사용하기 응답 및 카드 편집 등의 작업에 제스쳐 할당 @@ -106,7 +107,7 @@ 세 손가락으로 탭 네 손가락으로 탭 기기 흔들기 - ‘%s’ menu + \'%s\' 메뉴 시스템 전역에서 글자를 길게 눌렀을 때 \'%s\' 컨텍스트 메뉴를 보입니다. 이중 스크롤 eReader 사용시 스크롤 간격 두배로 늘리기 @@ -146,7 +147,7 @@ Allows \'Cloze deletion\' context menu when in landscape mode. 가운데 정렬 - 카드의 내용을 세로로 중앙에 배치 + 카드 내용 새로 중앙 정렬 더블 탭 인식 간격 이 간격보다 짧은 간격의 더블 탭은 무시됩니다. (실수 방지) 정답 보기 버튼 홀드 시간 @@ -165,7 +166,7 @@ 남은 시간 표시 새 카드의 카드 묶음 미리 학습하기 시간 설정 - 타임박스 시간 설정 + 목표 학습 시간(도달 시 알림) 다음 날의 시작 %d시 @@ -198,7 +199,7 @@ 안드로이드에서 미디어 파일을 인식하지 못할 경우 자체 동기화 서버 - 사용되지 않음 + 사용 안 함 탐색 미디어 기타 - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders 리마인더 추가 - 복습 리마인더 추가 - 복습 리마인더 편집 복습 리마인더가 없습니다 + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + 복습 리마인더 추가 + 복습 리마인더 편집 + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + 태그 선택하기 @@ -227,10 +296,10 @@ 검색 AnkiDroid를 개선하는데 도움을 주세요! - 기능 사용 공유하기 + 사용 정보 공유 개발팀이 사람들이 어떤 기능을 사용하는지 볼 수 있도록 도와주어 AnkiDroid에 기여할 수 있습니다. 줄바꿈을 HTML로 변환 - In the note editor, convert any instances of <br> to newlines when editing a card. + 노트 편집기에서 카드를 편집할 때 <br>을 줄바꿈으로 변환합니다. \'정답 입력\' 포커스 \'정답 입력\' 필드에 자동으로 포커스합니다. @@ -278,8 +347,8 @@ 실험적 AnkiDroid에 더 향상된 TTS(음성 합성) 기능이 도입되었습니다! 이제 다른 Anki 클라이언트와도 호환되며, 더 다양한 음성과 언어 재생 품질이 개선되었습니다.\n\n이 설정은 곧 제거될 예정이니, 가능한 빨리 앱을 업그레이드해 주세요. 새로운 TTS 포맷을 위해 업데이트 해주세요. - Allow templates to record audio - This template is trying to record audio + 카드 템플릿이 오디오를 녹음하도록 허용 + 이 템플릿은 오디오를 녹음합니다 학습 화면 로컬 스토리지 학습 화면에서 고정된 서버 포트를 사용하여 자바스크립트 로컬 스토리지를 활성화합니다. @@ -315,7 +384,7 @@ 사용되지 않는 미디어 파일 삭제하기 - Delete images and other media that are not prefixed with _ and are not referenced by any notes + 어떤 노트에도 사용되지 않거나, _로 시작하지 않는 이미지나 미디어를 삭제합니다. 사용되지 않는 미디어 파일 삭제하기 백업 삭제하기 diff --git a/AnkiDroid/src/main/res/values-ku/02-strings.xml b/AnkiDroid/src/main/res/values-ku/02-strings.xml index 1d3992800dbb..12c2a8eabfe9 100644 --- a/AnkiDroid/src/main/res/values-ku/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ku/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Cure: Nîşan: %1$s Kart: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ku/10-preferences.xml b/AnkiDroid/src/main/res/values-ku/10-preferences.xml index 3df74e2d5e26..08332b7be302 100644 --- a/AnkiDroid/src/main/res/values-ku/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ku/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-ky/02-strings.xml b/AnkiDroid/src/main/res/values-ky/02-strings.xml index 49d3076932e4..7cd1529bac88 100644 --- a/AnkiDroid/src/main/res/values-ky/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ky/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Type: Tags: %1$s Cards: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ky/10-preferences.xml b/AnkiDroid/src/main/res/values-ky/10-preferences.xml index 5fba3e8c30c5..96c821077da1 100644 --- a/AnkiDroid/src/main/res/values-ky/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ky/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-lt/02-strings.xml b/AnkiDroid/src/main/res/values-lt/02-strings.xml index 6da114303785..aed5c19b0ea6 100644 --- a/AnkiDroid/src/main/res/values-lt/02-strings.xml +++ b/AnkiDroid/src/main/res/values-lt/02-strings.xml @@ -29,7 +29,7 @@ Note saved Kortelių rinkinys: - Rinkinys: + Rinkinys: Tipas: Žymės: %1$s Kortelės: %1$s @@ -226,6 +226,8 @@ Šrifto dydis Rodyti įrankių juostą + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-lt/10-preferences.xml b/AnkiDroid/src/main/res/values-lt/10-preferences.xml index 8b5aa9a20c6f..a08ff360a4c8 100644 --- a/AnkiDroid/src/main/res/values-lt/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-lt/10-preferences.xml @@ -24,7 +24,8 @@ --> - Išjungta + Enabled + Išjungta %d min %d mins @@ -74,7 +75,7 @@ Display audio filenames in the card browser question/answer fields „AnkiDroid“ katalogas Viso ekrano režimas - Išjungta + Išjungta Slėpti sistemines juostas Slėpti sistemos juostas ir atsakymų mygtukus Atsakymų mygtukų vieta @@ -223,11 +224,82 @@ Navigacija Medija Įvairūs - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Pasirinkite žymes diff --git a/AnkiDroid/src/main/res/values-lv/02-strings.xml b/AnkiDroid/src/main/res/values-lv/02-strings.xml index 48399d23797b..02408933b3e0 100644 --- a/AnkiDroid/src/main/res/values-lv/02-strings.xml +++ b/AnkiDroid/src/main/res/values-lv/02-strings.xml @@ -29,7 +29,7 @@ Note saved Kartītes kava: - Kava: + Kava: Veids: Birkas: %1$s Kartītes: %1$s @@ -223,6 +223,8 @@ Burtu lielums Rādīt rīkjoslu + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-lv/10-preferences.xml b/AnkiDroid/src/main/res/values-lv/10-preferences.xml index 04bfb183953f..4bfa31611db2 100644 --- a/AnkiDroid/src/main/res/values-lv/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-lv/10-preferences.xml @@ -24,7 +24,8 @@ --> - Atspējots/a + Enabled + Atspējots/a %d minūšu %d minūte @@ -73,7 +74,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid mape Pilnekrāna skats - Izslēgts + Izslēgts Paslēpt sistēmas joslas Paslēpt sistēmas joslas un atbilžu pogas Atbilžu pogu novietojums @@ -220,11 +221,81 @@ Pārvietošanās Informācijas nesēji Dažādi - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + %d card due + %d cards due + Atlasīt birkas diff --git a/AnkiDroid/src/main/res/values-mk/02-strings.xml b/AnkiDroid/src/main/res/values-mk/02-strings.xml index f9178617b20c..1a4017fd3ea6 100644 --- a/AnkiDroid/src/main/res/values-mk/02-strings.xml +++ b/AnkiDroid/src/main/res/values-mk/02-strings.xml @@ -29,7 +29,7 @@ Note saved Картички коло: - Коло: + Коло: Тип: Тагови: %1$s Картички: %1$s @@ -221,6 +221,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-mk/10-preferences.xml b/AnkiDroid/src/main/res/values-mk/10-preferences.xml index c5be5cd26dbb..4ec43182d091 100644 --- a/AnkiDroid/src/main/res/values-mk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-mk/10-preferences.xml @@ -24,7 +24,8 @@ --> - Оневозможи + Enabled + Оневозможи %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Режим на цел екран - Off + Off Hide the system bars Hide the system bars and answer buttons Место на копчиња за одговарање @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-ml/02-strings.xml b/AnkiDroid/src/main/res/values-ml/02-strings.xml index abf035bd8e17..e002ffe7a571 100644 --- a/AnkiDroid/src/main/res/values-ml/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ml/02-strings.xml @@ -29,7 +29,7 @@ Note saved കാർഡ് ഡെക്ക്: - ഡെക്ക്: + ഡെക്ക്: തരം: ടാഗുകൾ‌:%1$s കാർഡുകൾ:%1$s @@ -220,6 +220,8 @@ അക്ഷര വലിപ്പം ടൂൾബാർ കാണിക്കുക + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ml/10-preferences.xml b/AnkiDroid/src/main/res/values-ml/10-preferences.xml index 05b7dc05beda..b909751fe35b 100644 --- a/AnkiDroid/src/main/res/values-ml/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ml/10-preferences.xml @@ -24,7 +24,8 @@ --> - പ്രവര്‍ത്തന രഹിതമാക്കിയത്‌ + Enabled + പ്രവര്‍ത്തന രഹിതമാക്കിയത്‌ %d മിനിറ്റ് %d മിനിറ്റ് @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid ഡയറക്ടറി പൂർണ്ണസ്ക്രീൻ മോഡ് - ഓഫ് + ഓഫ് സിസ്റ്റം ബാറുകൾ മറയ്‌ക്കുക സിസ്റ്റം ബാറുകളും ഉത്തര ബട്ടണുകളും മറയ്‌ക്കുക ഉത്തരം ബട്ടണുകളുടെ സ്ഥാനം @@ -218,11 +219,80 @@ നാവിഗേഷൻ മാധ്യമങ്ങൾ മറ്റുള്ളവ - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + ടാഗ് തിരഞ്ഞെടുക്കുക diff --git a/AnkiDroid/src/main/res/values-mn/02-strings.xml b/AnkiDroid/src/main/res/values-mn/02-strings.xml index 2d0b51543dd6..09f15e4e5b40 100644 --- a/AnkiDroid/src/main/res/values-mn/02-strings.xml +++ b/AnkiDroid/src/main/res/values-mn/02-strings.xml @@ -29,7 +29,7 @@ Note saved Картны Багц - Багц: + Багц: Төрөл: Шошгонууд:%1$s Картнууд: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-mn/10-preferences.xml b/AnkiDroid/src/main/res/values-mn/10-preferences.xml index 1733669553e7..fb5e3302d2ed 100644 --- a/AnkiDroid/src/main/res/values-mn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-mn/10-preferences.xml @@ -24,7 +24,8 @@ --> - Идэвхгүй + Enabled + Идэвхгүй %d минут %d минут @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-mr/02-strings.xml b/AnkiDroid/src/main/res/values-mr/02-strings.xml index 60427b630399..1090a2965493 100644 --- a/AnkiDroid/src/main/res/values-mr/02-strings.xml +++ b/AnkiDroid/src/main/res/values-mr/02-strings.xml @@ -29,7 +29,7 @@ Note saved Kārḍa ḍēka: - Ḍēka: + Ḍēka: Prakāra: Ṭĕgja:%1$s Kārḍa:%1$s @@ -220,6 +220,8 @@ अक्षराचा आकार टूलबार दर्शवा + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-mr/10-preferences.xml b/AnkiDroid/src/main/res/values-mr/10-preferences.xml index 44e429a52e37..24e0f1607966 100644 --- a/AnkiDroid/src/main/res/values-mr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-mr/10-preferences.xml @@ -24,7 +24,8 @@ --> - डिसेबल केले + Enabled + डिसेबल केले %d मि %d मिनिटे @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid निर्देशिका पूर्णस्क्रीन मोड - बंद + बंद सिस्टम बार लपवा सिस्टम बार आणि उत्तर बटणे लपवा उत्तर बटणे स्थिती @@ -216,11 +217,80 @@ नेव्हिगेशन मीडिया Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + टॅग निवडा diff --git a/AnkiDroid/src/main/res/values-ms/02-strings.xml b/AnkiDroid/src/main/res/values-ms/02-strings.xml index 50280c46b7ef..4706a2b4a32b 100644 --- a/AnkiDroid/src/main/res/values-ms/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ms/02-strings.xml @@ -29,7 +29,7 @@ Note saved Dek kad: - Dek: + Dek: Jenis: Tag: %1$s Kad: %1$s @@ -217,6 +217,8 @@ Saiz tulisan Lihat Bar Alat + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ms/10-preferences.xml b/AnkiDroid/src/main/res/values-ms/10-preferences.xml index 1d6cd7e1677d..bdda9569e744 100644 --- a/AnkiDroid/src/main/res/values-ms/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ms/10-preferences.xml @@ -24,7 +24,8 @@ --> - Dilumpuhkan + Enabled + Dilumpuhkan %d mins @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields Direktori AnkiDroid Mod skrin penuh - Matikan + Matikan Sembunyi bar sistem Sembunyi bar sistem dan butang jawapan Kedudukan butang jawapan @@ -217,11 +218,79 @@ Navigasi Media Lain-lain - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + Pilih tag diff --git a/AnkiDroid/src/main/res/values-my/02-strings.xml b/AnkiDroid/src/main/res/values-my/02-strings.xml index 9346ea150078..18d3e307047a 100644 --- a/AnkiDroid/src/main/res/values-my/02-strings.xml +++ b/AnkiDroid/src/main/res/values-my/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Type: Tags: %1$s Cards: %1$s @@ -217,6 +217,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-my/10-preferences.xml b/AnkiDroid/src/main/res/values-my/10-preferences.xml index b42c976391cb..521fb773b270 100644 --- a/AnkiDroid/src/main/res/values-my/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-my/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d mins @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -217,11 +218,79 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-nl/02-strings.xml b/AnkiDroid/src/main/res/values-nl/02-strings.xml index 89495a50ff1b..33edd2304344 100644 --- a/AnkiDroid/src/main/res/values-nl/02-strings.xml +++ b/AnkiDroid/src/main/res/values-nl/02-strings.xml @@ -29,7 +29,7 @@ Notitie opgeslagen Kaartenset: - Set: + Set: Type: Kenmerken: %1$s Kaarten: %1$s @@ -220,6 +220,8 @@ Lettergrootte Werkbalk weergeven + Show preview + Hide preview Maak dikgedrukt Maak schuingedrukt Maak onderstreept diff --git a/AnkiDroid/src/main/res/values-nl/10-preferences.xml b/AnkiDroid/src/main/res/values-nl/10-preferences.xml index a2308f3d7466..7056dd5ab5c7 100644 --- a/AnkiDroid/src/main/res/values-nl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-nl/10-preferences.xml @@ -24,7 +24,8 @@ --> - Uitgeschakeld + Enabled + Uitgeschakeld %d min %d min @@ -72,7 +73,7 @@ Namen van audiobestanden in de vraag/antwoord velden van de kaartenbrowser weergeven AnkiDroid directory Volledig schermmodus - Uit + Uit De systeembalken verbergen De systeembalken en antwoordknoppen verbergen Antwoord knoppen positie @@ -217,11 +218,80 @@ Navigatie Media Diversen - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Herinnering toevoegen - Overhoringsherinnering toevoegen - Overhoringsherinnering bewerken U heeft geen overhoringsherinneringen + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Overhoringsherinnering toevoegen + Overhoringsherinnering bewerken + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Selecteer Tags diff --git a/AnkiDroid/src/main/res/values-nn/02-strings.xml b/AnkiDroid/src/main/res/values-nn/02-strings.xml index 5a967ae715a3..c2318ca8d5f2 100644 --- a/AnkiDroid/src/main/res/values-nn/02-strings.xml +++ b/AnkiDroid/src/main/res/values-nn/02-strings.xml @@ -29,7 +29,7 @@ Notat lagret Kortleik: - Kortleik: + Kortleik: Slag: Etiketter: %1$s Kort: %1$s @@ -220,6 +220,8 @@ Skriftstørrelse Vis verktøylinje + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-nn/10-preferences.xml b/AnkiDroid/src/main/res/values-nn/10-preferences.xml index 60ceccba6f61..b3aec9ab614c 100644 --- a/AnkiDroid/src/main/res/values-nn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-nn/10-preferences.xml @@ -24,7 +24,8 @@ --> - Deaktivert + Enabled + Deaktivert %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullskjermmodus - Av + Av Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Velg emneord diff --git a/AnkiDroid/src/main/res/values-no/02-strings.xml b/AnkiDroid/src/main/res/values-no/02-strings.xml index a8791e55b8c5..7a46131dc929 100644 --- a/AnkiDroid/src/main/res/values-no/02-strings.xml +++ b/AnkiDroid/src/main/res/values-no/02-strings.xml @@ -29,7 +29,7 @@ Notat lagret Kortstokk: - Kortstokk: + Kortstokk: Type: Etiketter: %1$s Kort: %1$s @@ -220,6 +220,8 @@ Skriftstørrelse Vis verktøylinje + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-no/10-preferences.xml b/AnkiDroid/src/main/res/values-no/10-preferences.xml index 93d2da0e6e95..374d7808806b 100644 --- a/AnkiDroid/src/main/res/values-no/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-no/10-preferences.xml @@ -24,7 +24,8 @@ --> - Deaktivert + Enabled + Deaktivert %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullskjermvisning - Av + Av Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Velg emneord diff --git a/AnkiDroid/src/main/res/values-or/02-strings.xml b/AnkiDroid/src/main/res/values-or/02-strings.xml index 1b799f2df48f..0616e0566827 100644 --- a/AnkiDroid/src/main/res/values-or/02-strings.xml +++ b/AnkiDroid/src/main/res/values-or/02-strings.xml @@ -29,7 +29,7 @@ Note saved ପତ୍ର କେରୀ: - କେରୀ: + କେରୀ: ପ୍ରକାର: ଟ୍ୟାଗ୍: %1$s ପତ୍ର: %1$s @@ -220,6 +220,8 @@ ଫଣ୍ଟ ଆକାର ଉପକରଣ-ଦଣ୍ଡିକା ଦେଖାଇବା + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-or/10-preferences.xml b/AnkiDroid/src/main/res/values-or/10-preferences.xml index 319cdbb002f0..c7f01bc776f5 100644 --- a/AnkiDroid/src/main/res/values-or/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-or/10-preferences.xml @@ -24,7 +24,8 @@ --> - ଅକ୍ଷମ + Enabled + ଅକ୍ଷମ %d ମି %d ମି @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory ପୂର୍ଣ୍ଣସ୍କ୍ରୀନ୍ ମୋଡ୍ - ବନ୍ଦ + ବନ୍ଦ ସିଷ୍ଟମ୍ ଦଣ୍ଡିକାଗୁଡ଼ିକ ଲୁଚାଇବା ସିଷ୍ଟମ୍ ଦଣ୍ଡିକା ଏଵଂ ଉତ୍ତର ବଟନ୍ ଲୁଚାଇବା ଉତ୍ତର ବଟନ୍ ଅଵସ୍ଥାନ @@ -218,11 +219,80 @@ ଦିଗଚଳନ ମିଡ଼ିଆ ଵିଵିଧ - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders ସ୍ମାରକ ଯୋଡ଼ିବା - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + ଟ୍ୟାଗ୍ ଚୟନ କରନ୍ତୁ diff --git a/AnkiDroid/src/main/res/values-pa/02-strings.xml b/AnkiDroid/src/main/res/values-pa/02-strings.xml index 929078d6a676..b72bd15de596 100644 --- a/AnkiDroid/src/main/res/values-pa/02-strings.xml +++ b/AnkiDroid/src/main/res/values-pa/02-strings.xml @@ -29,7 +29,7 @@ Note saved ਕਾਰਡ ਡੈੱਕ: - ਡੈੱਕ: + ਡੈੱਕ: ਕਿਸਮ: ਟੈਗਸ: %1$s ਕਾਰਡ: %1$s @@ -220,6 +220,8 @@ ਫੌਂਟ ਦਾ ਆਕਾਰ ਟੂਲਬਾਰ ਦਿਖਾਓ + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-pa/10-preferences.xml b/AnkiDroid/src/main/res/values-pa/10-preferences.xml index a437cdafa0dd..fd2b4e42cf03 100644 --- a/AnkiDroid/src/main/res/values-pa/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pa/10-preferences.xml @@ -24,7 +24,8 @@ --> - ਅਯੋਗ + Enabled + ਅਯੋਗ %d ਮਿੰਟ %d ਮਿੰਟ @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid ਡਾਇਰੈਕਟਰੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ - بند + بند ਸਿਸਟਮ ਬਾਰ ਲੁਕਾਓ ਸਿਸਟਮ ਬਾਰ ਅਤੇ ਜਵਾਬ ਬਟਨਾਂ ਨੂੰ ਓਹਲੇ ਕਰੋ ਉੱਤਰ ਬਟਨਾਂ ਦੀ ਸਥਿਤੀ @@ -217,11 +218,80 @@ ਨੈਵੀਗੇਸ਼ਨ ਮੀਡੀਆ ਫੁਟਕਲ - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + ਟੈਗ ਚੁਣੋ diff --git a/AnkiDroid/src/main/res/values-pl/02-strings.xml b/AnkiDroid/src/main/res/values-pl/02-strings.xml index c77e3aae2331..3021cfceb443 100644 --- a/AnkiDroid/src/main/res/values-pl/02-strings.xml +++ b/AnkiDroid/src/main/res/values-pl/02-strings.xml @@ -29,7 +29,7 @@ Zapisano notatkę Talia kart: - Talia: + Talia: Typ: Tagi: %1$s Karty: %1$s @@ -226,6 +226,8 @@ Rozmiar czcionki Pokaż pasek narzędzi + Show preview + Hide preview Pogrubienie Pochylenie Podkreślenie diff --git a/AnkiDroid/src/main/res/values-pl/10-preferences.xml b/AnkiDroid/src/main/res/values-pl/10-preferences.xml index 93e8eafbc31b..0d9358d47087 100644 --- a/AnkiDroid/src/main/res/values-pl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pl/10-preferences.xml @@ -24,7 +24,8 @@ --> - Wyłączone + Enabled + Wyłączone %d min. %d min. @@ -74,7 +75,7 @@ Wyświetlaj nazwy plików dźwiękowych w polach pytanie/odpowiedź w przeglądarce kart Katalog AnkiDroid Tryb pełnoekranowy - Wyłączony + Wyłączony Ukryj paski systemowe Ukryj paski systemowe i przyciski odpowiedzi Położenie przycisków odpowiedzi @@ -222,11 +223,82 @@ Nawigacja Multimedia Pozostałe - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Dodaj przypomnienie - Dodaj przypomnienie o powtórce - Edytuj przypomnienie o powtórce Nie masz przypomnień o powtórce + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Dodaj przypomnienie o powtórce + Edytuj przypomnienie o powtórce + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Wybierz tagi diff --git a/AnkiDroid/src/main/res/values-pt-rBR/02-strings.xml b/AnkiDroid/src/main/res/values-pt-rBR/02-strings.xml index 3921e2870112..f885a929159a 100644 --- a/AnkiDroid/src/main/res/values-pt-rBR/02-strings.xml +++ b/AnkiDroid/src/main/res/values-pt-rBR/02-strings.xml @@ -29,7 +29,7 @@ Nota salva Baralho de cartões: - Baralho: + Baralho: Tipo: Tags: %1$s Cartões: %1$s @@ -220,6 +220,8 @@ Tamanho da fonte Mostrar barra de ferramentas + Show preview + Hide preview Formatar como Negrito Formatar como Itálico Formatar como Sublinhado diff --git a/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml b/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml index 001c6b4c6332..c27eb862aa63 100644 --- a/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml @@ -24,7 +24,8 @@ --> - Desativado + Enabled + Desativado %d minuto %d minutos @@ -72,7 +73,7 @@ Exibir os nomes dos arquivos de áudio nos campos de pergunta/resposta do navegador do cartão Diretório do AnkiDroid Modo tela cheia - Desligar + Desligar Ocultar barras de sistema Ocultar barras de sistema e botões de resposta Posição dos botões de resposta @@ -218,11 +219,80 @@ Navegação Multimídia Diversos - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Adicionar lembrete - Adicionar lembrete de revisão - Editar lembrete de revisão Você não tem lembretes de revisão + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Adicionar lembrete de revisão + Editar lembrete de revisão + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Selecione Tags diff --git a/AnkiDroid/src/main/res/values-pt-rPT/02-strings.xml b/AnkiDroid/src/main/res/values-pt-rPT/02-strings.xml index b521cff48afb..83bc465127f8 100644 --- a/AnkiDroid/src/main/res/values-pt-rPT/02-strings.xml +++ b/AnkiDroid/src/main/res/values-pt-rPT/02-strings.xml @@ -29,7 +29,7 @@ Note saved Baralho de fichas: - Baralho: + Baralho: Tipo: Etiquetas: %1$s Fichas: %1$s @@ -220,6 +220,8 @@ Tamanho da letra Mostrar Barra de Ferramentas + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml b/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml index af95d2feb056..ecec07334c40 100644 --- a/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml @@ -24,7 +24,8 @@ --> - Desativado + Enabled + Desativado %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields Diretoria do AnkiDroid Modo de ecrã completo - Desligado + Desligado Ocultar as barras do sistema Ocultar as barras do sistema e botões de resposta Posição dos botões de resposta @@ -219,11 +220,80 @@ Navegação Multimédia Outros - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Selecione as etiquetas diff --git a/AnkiDroid/src/main/res/values-ro/02-strings.xml b/AnkiDroid/src/main/res/values-ro/02-strings.xml index d222f68c2708..f18e08142d12 100644 --- a/AnkiDroid/src/main/res/values-ro/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ro/02-strings.xml @@ -29,7 +29,7 @@ Notiță salvată Pachet de cartonașe: - Pachet: + Pachet: Tip: Etichete: %1$s Cartonașe: %1$s @@ -223,6 +223,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ro/10-preferences.xml b/AnkiDroid/src/main/res/values-ro/10-preferences.xml index a0cca83cbf9f..6ce9ccad30eb 100644 --- a/AnkiDroid/src/main/res/values-ro/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ro/10-preferences.xml @@ -24,7 +24,8 @@ --> - Dezactivat + Enabled + Dezactivat %d min %d mins @@ -73,7 +74,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -221,11 +222,81 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + Selectaţi Tag-uri diff --git a/AnkiDroid/src/main/res/values-ru/01-core.xml b/AnkiDroid/src/main/res/values-ru/01-core.xml index f98e5fe8eca4..84ad8acc7958 100644 --- a/AnkiDroid/src/main/res/values-ru/01-core.xml +++ b/AnkiDroid/src/main/res/values-ru/01-core.xml @@ -137,7 +137,7 @@ Чтение и запись в базу данных AnkiDroid доступ к колодам, карточкам, записям, их типам, а также создание новых - Card browser + Обозреватель карточек Anki card Коллекции не могут быть объединены.\nКакую коллекцию вы хотите оставить? diff --git a/AnkiDroid/src/main/res/values-ru/02-strings.xml b/AnkiDroid/src/main/res/values-ru/02-strings.xml index 70d983a87854..895e268a3adf 100644 --- a/AnkiDroid/src/main/res/values-ru/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ru/02-strings.xml @@ -29,7 +29,7 @@ Запись сохранена Колода: - Колода: + Колода: Тип: Метки: %1$s Карточки: %1$s @@ -226,6 +226,8 @@ Размер шрифта Показать панель инструментов + Show preview + Hide preview Жирный Курсив Подчёркнутый diff --git a/AnkiDroid/src/main/res/values-ru/07-cardbrowser.xml b/AnkiDroid/src/main/res/values-ru/07-cardbrowser.xml index 96f28f5abd2f..e3df3ef96126 100644 --- a/AnkiDroid/src/main/res/values-ru/07-cardbrowser.xml +++ b/AnkiDroid/src/main/res/values-ru/07-cardbrowser.xml @@ -127,5 +127,5 @@ Selected Unavailable Card content can\'t be sorted. Use Sort Field - Only available in cards mode + Доступно только в режиме карточек diff --git a/AnkiDroid/src/main/res/values-ru/10-preferences.xml b/AnkiDroid/src/main/res/values-ru/10-preferences.xml index 6e7af8956f89..d047d5e2377a 100644 --- a/AnkiDroid/src/main/res/values-ru/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ru/10-preferences.xml @@ -24,7 +24,8 @@ --> - Выкл. + Enabled + Выкл. %d мин. %d мин. @@ -74,7 +75,7 @@ Отображать имена аудио файлов в полях вопроса или ответа в списке карточек Папка AnkiDroid Полноэкранный режим - Выкл. + Выкл. Скрыть системную панель Скрыть системную панель и кнопки ответа Положение кнопок ответа @@ -221,11 +222,82 @@ Навигация Медиа Разное - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Добавить напоминание - Добавить напоминание о повторении - Редактировать напоминания о повторении У Вас нет напоминаний о повторении + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Добавить напоминание о повторении + Редактировать напоминания о повторении + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Выберите метки diff --git a/AnkiDroid/src/main/res/values-sat/02-strings.xml b/AnkiDroid/src/main/res/values-sat/02-strings.xml index 021157fb966e..709e5e7e3f11 100644 --- a/AnkiDroid/src/main/res/values-sat/02-strings.xml +++ b/AnkiDroid/src/main/res/values-sat/02-strings.xml @@ -29,7 +29,7 @@ Note saved ᱠᱟᱰ ᱰᱮᱠᱺ - ᱰᱮᱠᱺ + ᱰᱮᱠᱺ ᱯᱨᱚᱠᱟᱨᱺ ᱴᱮᱜᱥᱺ%1$s ᱠᱟᱰᱺ%1$s @@ -220,6 +220,8 @@ ᱪᱤᱠᱤ ᱢᱟᱯ ᱴᱩᱞᱵᱟᱨ ᱫᱮᱠᱷᱟᱣᱢᱮ + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-sat/10-preferences.xml b/AnkiDroid/src/main/res/values-sat/10-preferences.xml index 1b28a1c24217..7cde9a3a68be 100644 --- a/AnkiDroid/src/main/res/values-sat/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sat/10-preferences.xml @@ -24,7 +24,8 @@ --> - ᱵᱚᱸᱫ + Enabled + ᱵᱚᱸᱫ %d ᱢᱤᱱᱤᱴ %d ᱢᱤᱱᱤᱴ @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid ᱩᱫᱩᱜ ᱯᱩᱛᱷᱤ ᱯᱩᱨᱟᱹᱥᱠᱨᱤᱱ ᱢᱚᱰ - ᱵᱚᱸᱫ + ᱵᱚᱸᱫ ᱥᱭᱥᱴᱚᱢ ᱵᱟᱨ ᱩᱠᱩᱭ ᱢᱮ ᱥᱭᱥᱴᱚᱢ ᱵᱟᱨ ᱟᱨ ᱛᱮᱞᱟ ᱵᱚᱴᱚᱱ ᱩᱠᱩᱭᱢᱮ ᱛᱮᱞᱟ ᱵᱚᱴᱚᱱ ᱡᱟᱭᱜᱟ @@ -218,11 +219,80 @@ ᱱᱮᱵᱤᱜᱮᱥᱚᱱ ᱢᱤᱰᱤᱭᱟ ᱢᱤᱥᱠ - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-sc/02-strings.xml b/AnkiDroid/src/main/res/values-sc/02-strings.xml index 7ac6e4839226..4810d605b88b 100644 --- a/AnkiDroid/src/main/res/values-sc/02-strings.xml +++ b/AnkiDroid/src/main/res/values-sc/02-strings.xml @@ -29,7 +29,7 @@ Note saved Matzu de cartas: - Matzu: + Matzu: Casta: Etichetas: %1$s Cartas: %1$s @@ -222,6 +222,8 @@ Mannària de su caràtere Ammustra sa barra de sas ainas + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-sc/10-preferences.xml b/AnkiDroid/src/main/res/values-sc/10-preferences.xml index 88f42d75e941..31f6b4121ce9 100644 --- a/AnkiDroid/src/main/res/values-sc/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sc/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabilitadu + Enabled + Disabilitadu %d min %d minutos @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields Cartella de AnkiDroid Modalidade a ischermu intreu - Istudadu + Istudadu Cua sas barras de sistema Cua sas barras de sistema e sos butones de risposta Positzione de su butone de sas rispostas @@ -218,11 +219,80 @@ Navigatzione Mèdios Vàrias - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Seletziona sas etichetas diff --git a/AnkiDroid/src/main/res/values-sk/02-strings.xml b/AnkiDroid/src/main/res/values-sk/02-strings.xml index 6dc5a1a5f74d..faabb2d0419b 100644 --- a/AnkiDroid/src/main/res/values-sk/02-strings.xml +++ b/AnkiDroid/src/main/res/values-sk/02-strings.xml @@ -29,7 +29,7 @@ Note saved Balíček kartičky: - Balíček: + Balíček: Typ: Štítky: %1$s Kartičky: %1$s @@ -226,6 +226,8 @@ Veľkosť fontu Zobraziť lištu nástrojov + Show preview + Hide preview Formátovať ako tučné Formátovať ako kurzíva Formátovať ako podčiarknutie diff --git a/AnkiDroid/src/main/res/values-sk/10-preferences.xml b/AnkiDroid/src/main/res/values-sk/10-preferences.xml index 91c8e5159415..bb46bd4137c0 100644 --- a/AnkiDroid/src/main/res/values-sk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sk/10-preferences.xml @@ -24,7 +24,8 @@ --> - Vypnuté + Enabled + Vypnuté %d minúta %d minúty @@ -74,7 +75,7 @@ Zobraziť názvy audio súborov v poliach otázka/odpoveď prehliadača kartičiek Adresár AnkiDroid Celoobrazovkový režim - Vypnúť + Vypnúť Skryť systémové panely Skryť systémové panely a tlačidlá s odpoveďami Pozícia tlačidla pre odpoveď @@ -223,11 +224,82 @@ Navigácia Médiá Rôzne - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Zvoliť štítky diff --git a/AnkiDroid/src/main/res/values-sl/02-strings.xml b/AnkiDroid/src/main/res/values-sl/02-strings.xml index b326182ef0fa..04f6442b2bff 100644 --- a/AnkiDroid/src/main/res/values-sl/02-strings.xml +++ b/AnkiDroid/src/main/res/values-sl/02-strings.xml @@ -29,7 +29,7 @@ Note saved Komplet kartic: - Komplet: + Komplet: Vrsta: Oznake: %1$s Kartice: %1$s @@ -226,6 +226,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-sl/10-preferences.xml b/AnkiDroid/src/main/res/values-sl/10-preferences.xml index d6c3256bf9d0..dd53b9297b0c 100644 --- a/AnkiDroid/src/main/res/values-sl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sl/10-preferences.xml @@ -24,7 +24,8 @@ --> - Onemogočeno + Enabled + Onemogočeno %d min %d mins @@ -74,7 +75,7 @@ Display audio filenames in the card browser question/answer fields Mapa AnkiDroid Celozaslonski način - Izklopljen + Izklopljen Skrij sistemske vrstice Skrij sistemske vrstice in gumbe za odgovor Položaj gumbov za odgovarjanje @@ -223,11 +224,82 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Izberite oznake diff --git a/AnkiDroid/src/main/res/values-sq/02-strings.xml b/AnkiDroid/src/main/res/values-sq/02-strings.xml index 9f9f2b39ec1e..f11af2156c59 100644 --- a/AnkiDroid/src/main/res/values-sq/02-strings.xml +++ b/AnkiDroid/src/main/res/values-sq/02-strings.xml @@ -29,7 +29,7 @@ Note saved Kuverta e kartave: - Kuvertë: + Kuvertë: Lloji: Etiketa: %1$s Kartat: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-sq/10-preferences.xml b/AnkiDroid/src/main/res/values-sq/10-preferences.xml index 835edd57643c..1dc44c50f470 100644 --- a/AnkiDroid/src/main/res/values-sq/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sq/10-preferences.xml @@ -24,7 +24,8 @@ --> - E çaktivizuar + Enabled + E çaktivizuar %d min %d minuta @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-sr/02-strings.xml b/AnkiDroid/src/main/res/values-sr/02-strings.xml index 148f686a2bac..10ab206c1e13 100644 --- a/AnkiDroid/src/main/res/values-sr/02-strings.xml +++ b/AnkiDroid/src/main/res/values-sr/02-strings.xml @@ -29,7 +29,7 @@ Note saved Шпил карата: - Шпил: + Шпил: Тип: Ознаке: %1$s Карте: %1$s @@ -223,6 +223,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-sr/10-preferences.xml b/AnkiDroid/src/main/res/values-sr/10-preferences.xml index 4af3b5744a39..a85df7d90c9d 100644 --- a/AnkiDroid/src/main/res/values-sr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sr/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -73,7 +74,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Пуноекрански режим - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -221,11 +222,81 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + Изаберите ознаке diff --git a/AnkiDroid/src/main/res/values-sv/02-strings.xml b/AnkiDroid/src/main/res/values-sv/02-strings.xml index 908cafdc73c7..322ce619c611 100644 --- a/AnkiDroid/src/main/res/values-sv/02-strings.xml +++ b/AnkiDroid/src/main/res/values-sv/02-strings.xml @@ -29,7 +29,7 @@ Anteckning sparad! Kortlek: - Kortlek: + Kortlek: Typ: Etiketter: %1$s Kort: %1$s @@ -220,6 +220,8 @@ Teckenstorlek Visa verktygsfält + Show preview + Hide preview Formatera som fet Formatera som kursiv Formatera som understruken diff --git a/AnkiDroid/src/main/res/values-sv/10-preferences.xml b/AnkiDroid/src/main/res/values-sv/10-preferences.xml index 08fa1397d470..67bb9c6b932b 100644 --- a/AnkiDroid/src/main/res/values-sv/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sv/10-preferences.xml @@ -24,7 +24,8 @@ --> - Inaktiverad + Enabled + Inaktiverad %d min %d min @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid sökväg Fullskärmsläge - Av + Av Dölj systemets statusfält Dölj systemets statusfält och svarsknapparna Svarsknapparnas placering @@ -218,11 +219,80 @@ Navigering Media Övrigt - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Lägg till påminnelse - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Välj etiketter diff --git a/AnkiDroid/src/main/res/values-ta/02-strings.xml b/AnkiDroid/src/main/res/values-ta/02-strings.xml index f26f74f8ab6b..0e13bf8e9bb9 100644 --- a/AnkiDroid/src/main/res/values-ta/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ta/02-strings.xml @@ -29,7 +29,7 @@ Note saved அட்டை தளம்: - தளம்: + தளம்: வகை: குறிச்சொற்கள்: %1$s கார்டுகள்: %1$s @@ -220,6 +220,8 @@ எழுத்துரு அளவு கருவிப்பட்டியைக் காட்டு + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ta/10-preferences.xml b/AnkiDroid/src/main/res/values-ta/10-preferences.xml index 28a8562420a7..296efc5bd8dc 100644 --- a/AnkiDroid/src/main/res/values-ta/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ta/10-preferences.xml @@ -24,7 +24,8 @@ --> - முடக்கப்பட்டது + Enabled + முடக்கப்பட்டது %d நிமிடங்கள் %d நிமிடங்கள் @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid அடைவு முழு திரை பயன்முறை - ஆஃப் + ஆஃப் சிஸ்டம் பார்களை மறை கணினி பார்கள் மற்றும் பதில் பொத்தான்களை மறைக்கவும் பதில் பொத்தான்களின் நிலை @@ -218,11 +219,80 @@ வழிசெலுத்தல் ஊடகம் மற்றவை - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + குறிச்சொற்களைத் தேர்ந்தெடுக்கவும் diff --git a/AnkiDroid/src/main/res/values-te/02-strings.xml b/AnkiDroid/src/main/res/values-te/02-strings.xml index 1f47a2a3f4ef..ba57be640244 100644 --- a/AnkiDroid/src/main/res/values-te/02-strings.xml +++ b/AnkiDroid/src/main/res/values-te/02-strings.xml @@ -29,7 +29,7 @@ Note saved కార్డ్ డెక్: - డెక్: + డెక్: రకం: టాగ్లు:%1$s కార్డులు:%1$s @@ -220,6 +220,8 @@ ఫాంట్ పరిమాణం టూల్ బార్ చూపించు + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-te/10-preferences.xml b/AnkiDroid/src/main/res/values-te/10-preferences.xml index 79709b9da69f..d5152d0e0c57 100644 --- a/AnkiDroid/src/main/res/values-te/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-te/10-preferences.xml @@ -24,7 +24,8 @@ --> - నిలిపివేయబడింది + Enabled + నిలిపివేయబడింది %d నిమి %d నిమిషాలు @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid డైరెక్టరీ పూర్తి స్క్రీన్ మోడ్ - ఆఫ్ + ఆఫ్ సిస్టమ్ బార్లను దాచు సిస్టమ్ బార్లను దాచిపెట్టి, సమాధానం బటన్లను దాచిపెట్టు సమాధానం బటన్లు స్థానం @@ -216,11 +217,80 @@ నావిగేషన్ Media ఇతర - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + ట్యాగ్లను ఎంచుకోండి diff --git a/AnkiDroid/src/main/res/values-tgl/02-strings.xml b/AnkiDroid/src/main/res/values-tgl/02-strings.xml index bc3f5b989ba7..7728d7f1ccb7 100644 --- a/AnkiDroid/src/main/res/values-tgl/02-strings.xml +++ b/AnkiDroid/src/main/res/values-tgl/02-strings.xml @@ -29,7 +29,7 @@ Note saved Deck ng baraha: - Deck: + Deck: Uri: Tags: %1$s Mga baraha: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-tgl/10-preferences.xml b/AnkiDroid/src/main/res/values-tgl/10-preferences.xml index 6b55d1dc43ca..58c122943b05 100644 --- a/AnkiDroid/src/main/res/values-tgl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-tgl/10-preferences.xml @@ -24,7 +24,8 @@ --> - I-disable + Enabled + I-disable %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields Direktoryo ng AnkiDroid Fullscreen mode - Off + Off Itago ang mga bar ng system Itago ang mga bar ng system at sagutin ang mga pindutan Posisyon ng sagot sa mga pindutan @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Piliin ang mga tag diff --git a/AnkiDroid/src/main/res/values-th/02-strings.xml b/AnkiDroid/src/main/res/values-th/02-strings.xml index 720421e56d43..2a564772ac6e 100644 --- a/AnkiDroid/src/main/res/values-th/02-strings.xml +++ b/AnkiDroid/src/main/res/values-th/02-strings.xml @@ -29,7 +29,7 @@ บันทึกโน้ตเรียบร้อยแล้ว สำรับบัตรคำ: - สำรับ: + สำรับ: ชนิด: ป้ายกำกับ: %1$s การ์ด: %1$s @@ -217,6 +217,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-th/10-preferences.xml b/AnkiDroid/src/main/res/values-th/10-preferences.xml index f9681201b59a..49c1d12e8176 100644 --- a/AnkiDroid/src/main/res/values-th/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-th/10-preferences.xml @@ -24,7 +24,8 @@ --> - ปิดใช้งาน + Enabled + ปิดใช้งาน %d นาที @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - ปิด + ปิด Hide the system bars Hide the system bars and answer buttons ตำแหน่งปุ่มคำตอบ @@ -217,11 +218,79 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-ti/02-strings.xml b/AnkiDroid/src/main/res/values-ti/02-strings.xml index 49d3076932e4..7cd1529bac88 100644 --- a/AnkiDroid/src/main/res/values-ti/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ti/02-strings.xml @@ -29,7 +29,7 @@ Note saved Card deck: - Deck: + Deck: Type: Tags: %1$s Cards: %1$s @@ -220,6 +220,8 @@ Font size Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ti/10-preferences.xml b/AnkiDroid/src/main/res/values-ti/10-preferences.xml index 5fba3e8c30c5..96c821077da1 100644 --- a/AnkiDroid/src/main/res/values-ti/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ti/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d min %d mins @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Off + Off Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-tr/02-strings.xml b/AnkiDroid/src/main/res/values-tr/02-strings.xml index c53d46b86b44..ce5a8fd3b101 100644 --- a/AnkiDroid/src/main/res/values-tr/02-strings.xml +++ b/AnkiDroid/src/main/res/values-tr/02-strings.xml @@ -29,7 +29,7 @@ Not kaydedildi Kart destesi: - Deste: + Deste: Tür: Etiketler: %1$s Kartlar: %1$s @@ -220,6 +220,8 @@ Yazı tipi boyutu Araç çubuğunu göster + Show preview + Hide preview Kalın olarak şekillendir İtalik olarak şekillendir Altı çizili olarak şekillendir diff --git a/AnkiDroid/src/main/res/values-tr/10-preferences.xml b/AnkiDroid/src/main/res/values-tr/10-preferences.xml index c06f2a442f63..ff277d7e40f8 100644 --- a/AnkiDroid/src/main/res/values-tr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-tr/10-preferences.xml @@ -24,7 +24,8 @@ --> - Devredışı + Enabled + Devredışı %d dk %d dk @@ -72,7 +73,7 @@ Kart gözatıcısı soru/cevap alanlarında ses dosyası isimlerini göster AnkiDroid dizini Tam ekran modu - Kapalı + Kapalı Sistem çubuklarını gizle Sistem çubuklarını ve cevap butonlarını gizle Cevap düğmelerinin yeri @@ -217,11 +218,80 @@ Navigasyon Medya Diğer - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Hatırlatıcı ekle - Çalışma hatırlatıcısı ekle - Çalışma hatırlatıcısını değiştir Çalışma hatırlatıcınız yok + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Çalışma hatırlatıcısı ekle + Çalışma hatırlatıcısını değiştir + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Etiketleri Seçin diff --git a/AnkiDroid/src/main/res/values-tt/02-strings.xml b/AnkiDroid/src/main/res/values-tt/02-strings.xml index c945d3858dd5..96876eb71813 100644 --- a/AnkiDroid/src/main/res/values-tt/02-strings.xml +++ b/AnkiDroid/src/main/res/values-tt/02-strings.xml @@ -29,7 +29,7 @@ Note saved Кәрт колодасы: - Колода: + Колода: Төр: Тамгалар: %1$s Кәртләр: %1$s @@ -218,6 +218,8 @@ Шрифт үлчәме Show toolbar + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-tt/10-preferences.xml b/AnkiDroid/src/main/res/values-tt/10-preferences.xml index 0fa15a5f122f..1a1619f27da8 100644 --- a/AnkiDroid/src/main/res/values-tt/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-tt/10-preferences.xml @@ -24,7 +24,8 @@ --> - Disabled + Enabled + Disabled %d mins @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid directory Fullscreen mode - Сүнгән + Сүнгән Hide the system bars Hide the system bars and answer buttons Answer buttons position @@ -217,11 +218,79 @@ Навигация Медиа Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-ug/02-strings.xml b/AnkiDroid/src/main/res/values-ug/02-strings.xml index 1273edf9b435..3c3dfc822322 100644 --- a/AnkiDroid/src/main/res/values-ug/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ug/02-strings.xml @@ -29,7 +29,7 @@ خاتىرە ساقلاندى كارتا دەستىسى: - دەستە: + دەستە: تۈرى: بەلگە: %1$s كارتا: %1$s @@ -220,6 +220,8 @@ خەت چوڭلۇقى قورال بالداقنى كۆرسەت + Show preview + Hide preview توم پىچىم يانتۇ پىچىم ئاستى سىزىق پىچىم diff --git a/AnkiDroid/src/main/res/values-ug/10-preferences.xml b/AnkiDroid/src/main/res/values-ug/10-preferences.xml index 203da91ef4de..5ece6e72d399 100644 --- a/AnkiDroid/src/main/res/values-ug/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ug/10-preferences.xml @@ -24,7 +24,8 @@ --> - چەكلەنگەن + Enabled + چەكلەنگەن %d مىنۇت %d مىنۇت @@ -72,7 +73,7 @@ كارتا كۆرگۈچتىكى سوئال/جاۋاب بۆلىكىدە ئاۋاز ھۆججەت ئاتىنى كۆرسىتىدۇ AnkiDroid مۇندەرىجىسى پۈتۈن ئېكران ھالىتى - تاقاق + تاقاق سىستېما بالداقنى يوشۇر سىستېما بالداق ۋە جاۋاب توپچەنى يوشۇرىدۇ جاۋاب توپچەنىڭ ئورنى @@ -219,11 +220,80 @@ يولباشچى ۋاسىتە ئارىلاش - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders ئەسكەرتىش قوش - تەكرارلاش ئەسكەرتىشى قوش - تەكرارلاش ئەسكەرتىشى تەھرىر تەكرارلاش ئەسكەرتىشىڭىز يوق + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + تەكرارلاش ئەسكەرتىشى قوش + تەكرارلاش ئەسكەرتىشى تەھرىر + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + بەلگە تاللىنىدۇ diff --git a/AnkiDroid/src/main/res/values-uk/02-strings.xml b/AnkiDroid/src/main/res/values-uk/02-strings.xml index ac7809bf5edc..35034b83cd25 100644 --- a/AnkiDroid/src/main/res/values-uk/02-strings.xml +++ b/AnkiDroid/src/main/res/values-uk/02-strings.xml @@ -29,7 +29,7 @@ Запис збережено Колода карток: - Колода: + Колода: Тип: Теги: %1$s Картки: %1$s @@ -226,6 +226,8 @@ Розмір шрифту Показувати панель інструментів + Show preview + Hide preview Форматувати як жирний Форматувати як курсив Форматувати як підкреслений diff --git a/AnkiDroid/src/main/res/values-uk/10-preferences.xml b/AnkiDroid/src/main/res/values-uk/10-preferences.xml index 9324663d56e6..1ec86aec3fed 100644 --- a/AnkiDroid/src/main/res/values-uk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-uk/10-preferences.xml @@ -24,7 +24,8 @@ --> - Вимкнено + Enabled + Вимкнено %d хв. %d хв. @@ -74,7 +75,7 @@ Показувати назви аудіофайлів в переглядачі карток в полях запитання/відповіді Шлях до папки AnkiDroid Повноекранний режим - Вимкнено + Вимкнено Приховати системну панель Приховати системну панель і панель кнопок відповіді Положення кнопок відповіді @@ -222,11 +223,82 @@ Навігація Медіафайли Різне - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Додати нагадування - Додати нагадування про навчання - Редагувати нагадування про навчання У вас немає нагадувань про навчання + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Додати нагадування про навчання + Редагувати нагадування про навчання + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + %d cards due + %d cards due + Оберіть теги diff --git a/AnkiDroid/src/main/res/values-ur/02-strings.xml b/AnkiDroid/src/main/res/values-ur/02-strings.xml index 6f24fc696048..9d053df4b36a 100644 --- a/AnkiDroid/src/main/res/values-ur/02-strings.xml +++ b/AnkiDroid/src/main/res/values-ur/02-strings.xml @@ -29,7 +29,7 @@ Note saved کارڈ ڈیک: - ڈیک: + ڈیک: قسم: ٹیگز: %1$s کارڈ: %1$s @@ -221,6 +221,8 @@ حرف کا سائز ٹول بار دکھائیں + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-ur/10-preferences.xml b/AnkiDroid/src/main/res/values-ur/10-preferences.xml index c14ae771dc94..caabb16a475e 100644 --- a/AnkiDroid/src/main/res/values-ur/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ur/10-preferences.xml @@ -24,7 +24,8 @@ --> - غیر فعال + Enabled + غیر فعال %d منٹ %d منٹ @@ -72,7 +73,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid ڈائریکٹری فل سکرین موڈ - بند + بند سسٹم کی سلاخوں کو چھپائیں سسٹم بارز اور جوابی بٹن چھپائیں جواب بٹن کی پوزیشن @@ -219,11 +220,80 @@ Navigation Media Misc - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Select tags diff --git a/AnkiDroid/src/main/res/values-uz/02-strings.xml b/AnkiDroid/src/main/res/values-uz/02-strings.xml index 11f164e1e348..5b779bc38ada 100644 --- a/AnkiDroid/src/main/res/values-uz/02-strings.xml +++ b/AnkiDroid/src/main/res/values-uz/02-strings.xml @@ -29,7 +29,7 @@ Qayt saqlandi Karta dastai: - Dasta: + Dasta: Turi: Teglar: %1$s Kartalar: %1$s @@ -220,6 +220,8 @@ Shrift oʻlchami Asboblar panelini koʻrsatish + Show preview + Hide preview Qalin qilib formatlash Kursiv sifatida formatlash Tagiga chiziq tortib formatlash diff --git a/AnkiDroid/src/main/res/values-uz/10-preferences.xml b/AnkiDroid/src/main/res/values-uz/10-preferences.xml index e0c9473ae575..434e620864e7 100644 --- a/AnkiDroid/src/main/res/values-uz/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-uz/10-preferences.xml @@ -24,7 +24,8 @@ --> - Oʻchirilgan + Enabled + Oʻchirilgan %d daqiqa %d daqiqa @@ -72,7 +73,7 @@ Audio fayl nomlarini karta brauzeri savol/javob maydonlarida ko\'rsatish AnkiDroid katalogi Butun ekran rejimi - Oʻchiq + Oʻchiq Tizim panellarini yashirish Tizim panellarini va javob tugmalarini yashirish Javob tugmalari joylashuvi @@ -217,11 +218,80 @@ Navigatsiya Media fayllar Boshqa - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Bildirishnoma qoʻshish - Takrorlash bildirishnomasini qoʻshish - Takrorlash bildirishnomasini tahrirlash Siza hech qanday takrorlash bildirishnomalari yoʻq + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Takrorlash bildirishnomasini qoʻshish + Takrorlash bildirishnomasini tahrirlash + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d card due + %d cards due + Teglarni tanlash diff --git a/AnkiDroid/src/main/res/values-vi/02-strings.xml b/AnkiDroid/src/main/res/values-vi/02-strings.xml index 7fca57c6a95a..cc3ed11f69e8 100644 --- a/AnkiDroid/src/main/res/values-vi/02-strings.xml +++ b/AnkiDroid/src/main/res/values-vi/02-strings.xml @@ -29,7 +29,7 @@ Ghi chú đã được lưu Bộ thẻ: - Bộ: + Bộ: Loại: Các đuôi: %1$s Các thẻ: %1$s @@ -217,6 +217,8 @@ Cỡ chữ Hiển thị thanh công cụ + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-vi/10-preferences.xml b/AnkiDroid/src/main/res/values-vi/10-preferences.xml index 3d9951c0872a..0bad8ec77dbf 100644 --- a/AnkiDroid/src/main/res/values-vi/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-vi/10-preferences.xml @@ -24,7 +24,8 @@ --> - Đã tắt + Enabled + Đã tắt %d phút @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields Thư mục AnkiDroid Chế độ toàn màn hình - Tắt + Tắt Ẩn các thanh hệ thống Ẩn các thanh hệ thống và các nút trả lời Vị trí các nút trả lời @@ -214,11 +215,79 @@ Điều hướng Đa phương tiện Hỗn hợp - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Thêm lời nhắc - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + Chọn thẻ diff --git a/AnkiDroid/src/main/res/values-zh-rCN/02-strings.xml b/AnkiDroid/src/main/res/values-zh-rCN/02-strings.xml index 87492a198afa..6501ecb6deae 100644 --- a/AnkiDroid/src/main/res/values-zh-rCN/02-strings.xml +++ b/AnkiDroid/src/main/res/values-zh-rCN/02-strings.xml @@ -29,7 +29,7 @@ 已保存附注 牌组: - 牌组: + 牌组: 笔记模板: 标签:%1$s 卡片:%1$s @@ -217,6 +217,8 @@ 字体大小 显示工具栏 + Show preview + Hide preview 粗体格式 斜体格式 下划线格式 diff --git a/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml b/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml index 7d487a11c9ab..4300d465afe8 100644 --- a/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml @@ -24,7 +24,8 @@ --> - 禁用 + Enabled + 禁用 %d 分钟 @@ -71,7 +72,7 @@ 在卡牌浏览器问题/回答字段中显示音频文件名 AnkiDroid 路径 全屏显示 - 关闭 + 关闭 隐藏系统状态栏 隐藏系统状态栏和回答按钮 显示答案按钮位置 @@ -216,11 +217,79 @@ 导航 多媒体 杂项 - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders 添加提醒 - 添加复习提醒 - 编辑复习提醒 您没有复习提醒 + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + 添加复习提醒 + 编辑复习提醒 + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + 选择标签 diff --git a/AnkiDroid/src/main/res/values-zh-rTW/02-strings.xml b/AnkiDroid/src/main/res/values-zh-rTW/02-strings.xml index fd9ebb048ee4..923f7ada4718 100644 --- a/AnkiDroid/src/main/res/values-zh-rTW/02-strings.xml +++ b/AnkiDroid/src/main/res/values-zh-rTW/02-strings.xml @@ -29,7 +29,7 @@ 筆記已儲存 卡片牌組: - 牌組: + 牌組: 種類: 標籤: %1$s 卡片:%1$s @@ -217,6 +217,8 @@ 字型大小 顯示工具列 + Show preview + Hide preview Format as bold Format as italic Format as underline diff --git a/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml b/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml index c77ee9a51afe..fe8925f17b01 100644 --- a/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml @@ -24,7 +24,8 @@ --> - 已停用 + Enabled + 已停用 %d分鐘 @@ -71,7 +72,7 @@ Display audio filenames in the card browser question/answer fields AnkiDroid 路徑 全螢幕模式 - 關閉 + 關閉 隱藏系統狀態欄 隱藏系統狀態欄和回答按鈕 顯示答案按鈕位置 @@ -216,11 +217,79 @@ 瀏覽 媒體 雜項 - + + + Review reminders + App-wide + Deck-specific + An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + + Schedule reminders Add reminder - Add review reminder - Edit review reminder You have no review reminders + Troubleshoot + Notification settings + List of scheduled review reminders + Deck not found + Reminders are unavailable + Fix + Successfully added new review reminder + Successfully deleted review reminder + Successfully edited review reminder + + Add review reminder + Edit review reminder + Time: + Deck: + Advanced settings + Card threshold: + Do not send me notifications if there are less than this many cards due + Only notify me if no reviews have been done today + Please enter a whole number of cards + The threshold must be at least 0 + Delete this reminder? + This action cannot be undone. + + Reminders are unavailable. + Reminders may not work correctly. + Your reminders should work as expected. + Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + + Notification permission + Notification channel + Do not disturb + Battery optimization + Power saving mode + Alarms & reminders permission + + Granted + Denied + On + Off + Unrestricted + Optimized + Restricted + + The review reminder notification channel must be enabled + Do Not Disturb may mute reminder notifications + Battery optimization may delay reminders + Background usage is disabled. Reminders may not be delivered + Power saving mode may prevent timely delivery of reminders + Required to schedule reminders at exact times + + Grant permission + Enable notification channel + Disable battery optimization + Open battery settings + + It\'s time to study your cards + It\'s time to study \"%s\" + Snooze %s + + %d cards due + 選擇標籤 From 544446e9ac3dd04c821174a4d0fafd546a3ba460 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sat, 26 Sep 2026 23:42:21 +0100 Subject: [PATCH 06/43] fix(ci): reconnect log capture if ADB disconnects Previously, a disconnect had stopped `logcat` before tests began we use this log for `@Ignore` tests, so although the tests passed it broke reporting. To fix: add an event reporter and keep logcat running. Preserve the exit status, and terminate the capture when gradle exits. Fixes 22115 Assisted-by: GPT-6 --- .github/with_emulator_log.sh | 41 ++++++++++++++++++++++++++++ .github/workflows/tests_emulator.yml | 3 +- 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .github/with_emulator_log.sh diff --git a/.github/with_emulator_log.sh b/.github/with_emulator_log.sh new file mode 100644 index 000000000000..6605f1a151f0 --- /dev/null +++ b/.github/with_emulator_log.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-3.0-or-later + +set -euo pipefail + +log_file=$1 +shift + +# Fail before starting the test command if we cannot capture its logs. +: >> "$log_file" + +stop_process() { + kill "$1" 2>/dev/null || true + wait "$1" 2>/dev/null || true +} + +capture_logcat() { + local child_pid= + trap 'exit 0' INT TERM + trap 'stop_process "$child_pid"' EXIT + + # ADB can disconnect after boot, ending logcat before the tests start (Issue 22115). + # Reconnect even on a clean exit. Replaying the buffer also recovers events emitted + # while disconnected; the test reporter deduplicates ignored/failed events. + while true; do + # Background both commands so TERM interrupts wait, including during the delay. + adb logcat '*:D' >> "$log_file" & + child_pid=$! + wait "$child_pid" || true + echo 'Emulator logcat exited; reconnecting in 1 second.' >&2 + sleep 1 & + child_pid=$! + wait "$child_pid" || true + done +} + +capture_logcat & +capture_pid=$! +# Stop both the retry loop and its child before returning the test command's status. +trap 'stop_process "$capture_pid"' EXIT +"$@" diff --git a/.github/workflows/tests_emulator.yml b/.github/workflows/tests_emulator.yml index 7efb3ac6c300..2f62d5f8de6e 100644 --- a/.github/workflows/tests_emulator.yml +++ b/.github/workflows/tests_emulator.yml @@ -222,13 +222,12 @@ jobs: # update workflows/README.md when modifying this script script: | touch adb-log.txt - $ANDROID_HOME/platform-tools/adb logcat '*:D' >> adb-log.txt & bash .github/disable_art_metrics.sh # Fix "Speech Recognition and Synthesis from Google keeps stopping" causing RootViewWithoutFocusException bash .github/adb_retry.sh shell settings put global hide_error_dialogs 1 bash .github/adb_retry.sh emu screenrecord start --time-limit 1800 video.webm sleep 5 - ./gradlew uninstallAll jacocoAndroidTestReport --daemon + bash .github/with_emulator_log.sh adb-log.txt ./gradlew uninstallAll jacocoAndroidTestReport --daemon - name: Report Emulator Test Results if: always() && hashFiles('AnkiDroid/build/outputs/androidTest-results/connected/**/TEST-*.xml') != '' From 69374b15fb19c82f590e0be1e427b8483f5c65ee Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 00:22:00 +0100 Subject: [PATCH 07/43] build: enable buildSrc tests in CI Just a sample 1+1 for now as a smoke test. Assisted-by: GPT-6 --- .github/workflows/README.md | 1 + .github/workflows/lint.yml | 3 +++ buildSrc/build.gradle.kts | 6 ++++++ .../kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt | 13 +++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 36a6bbc036f1..3789efc75203 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -15,6 +15,7 @@ Alternately, you may run the actions on your fork of `Anki-Android`. |------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------| | [Lint (Kotlin)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew lintAll ktLintCheck lint-rules:test --daemon` | Android lint rules, formatting and tests for lint rules | | [Lint (JavaScript)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | See script | Prettier, lint & code formatting | +| [Build Logic](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew -p buildSrc test` | Tests for build logic | | [Unit Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_unit.yml) | `./gradlew jacocoUnitTestReport --daemon` | Unit tests for the Android Project | | [Emulator Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_emulator.yml) | `TEST_RELEASE_BUILD=true ./gradlew jacocoAndroidTestReport --daemon` | Emulator tests for the Android Project.
CI runs `bash .github/disable_art_metrics.sh` and `adb shell settings put global hide_error_dialogs 1` first | | [CodeQL](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/codeql.yml) | N/A | GitHub-only check.
[Docs](https://codeql.github.com/) | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1f6c1854e51c..a6e45ea6736f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -47,6 +47,9 @@ jobs: # Comment this and the with: above out for performance testing on a branch cache-read-only: ${{ github.ref != 'refs/heads/main' }} + - name: Test build logic + run: ./gradlew -p buildSrc test --daemon + - name: Warm Gradle Cache # This makes sure we fetch gradle network resources with a retry uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c5b20ad42642..6d1e48f82be4 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -19,6 +19,12 @@ dependencies { implementation("com.android.tools.build:gradle:${libs.versions.androidGradlePlugin.get()}") // Force the catalog version of KGP, otherwise it's overridden by AGP. implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${libs.versions.kotlin.get()}") + testImplementation(libs.junit.jupiter) + testRuntimeOnly(libs.junit.platform.launcher) +} + +tasks.test { + useJUnitPlatform() } configure { diff --git a/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt b/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt new file mode 100644 index 000000000000..f1f076b759ee --- /dev/null +++ b/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.gradle + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class BuildSrcTest { + @Test + fun `runs buildSrc tests`() { + assertEquals(2, 1 + 1) + } +} From 07542653d4153129d74084fee696ff0f606ed128 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sat, 26 Sep 2026 22:08:44 +0100 Subject: [PATCH 08/43] build: add `versionCode` bump validation Read the version code and ensure it never increases by more than 100,000 (and never decreases). Assisted-by: GPT-6 --- .../com/ichi2/anki/gradle/VersionCode.kt | 44 ++++++++++ .../com/ichi2/anki/gradle/VersionCodeTasks.kt | 58 +++++++++++++ .../com/ichi2/anki/gradle/BuildSrcTest.kt | 13 --- .../com/ichi2/anki/gradle/VersionCodeTest.kt | 81 +++++++++++++++++++ 4 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCode.kt create mode 100644 buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCodeTasks.kt delete mode 100644 buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt create mode 100644 buildSrc/src/test/kotlin/com/ichi2/anki/gradle/VersionCodeTest.kt diff --git a/buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCode.kt b/buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCode.kt new file mode 100644 index 000000000000..a3e238bb1ff7 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCode.kt @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.gradle + +object VersionCode { + private const val MAX_BUMP = 100_000 + + // Consume whole comments atomically so dots inside them cannot look like chained calls. + private const val WHITESPACE_OR_COMMENTS = """(?>\s|//[^\r\n]*|/\*[\s\S]*?\*/)*""" + + // Include chained calls across whitespace/comments so a numeric prefix cannot pass as a literal. + private val assignment = Regex("""^\h*versionCode\b\h*=?\s*([^\r\n]+(?:\R$WHITESPACE_OR_COMMENTS\.[^\r\n]*)*)""", RegexOption.MULTILINE) + private val literal = Regex("""([1-9][0-9]*)\h*;?\h*(?://.*)?""") + + /** + * Accepted formats, using digits only with no leading zeros: + * - Groovy: `versionCode 22500202` + * - Kotlin: `versionCode = 22500202` + * - Whitespace: ` versionCode=22500202` + * - Comment: `versionCode = 22500202 // beta` + */ + fun read(buildScript: String): Int { + val declaration = assignment.findAll(buildScript).singleOrNull() + val number = declaration?.let { literal.matchEntire(it.groupValues[1].trim()) } + return requireNotNull(number?.groupValues?.get(1)?.toIntOrNull()) { + "Expected exactly one versionCode declaration with a positive integer literal (digits only, no leading zeros), e.g. versionCode = 22500202." + } + } + + /** Checks the increase in the base code, before the ABI prefix is added. */ + fun validate( + previous: Int, + code: Int?, + ) { + require(previous > 0 && code != null && code > 0) { + "Previous and current versionCode must be positive: $previous -> $code" + } + val increase = code.toLong() - previous.toLong() + require(increase in 0L..MAX_BUMP.toLong()) { + "versionCode increase must be between 0 and $MAX_BUMP: " + + "$previous -> $code (increase $increase)." + } + } +} diff --git a/buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCodeTasks.kt b/buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCodeTasks.kt new file mode 100644 index 000000000000..9c01cebdc8c6 --- /dev/null +++ b/buildSrc/src/main/kotlin/com/ichi2/anki/gradle/VersionCodeTasks.kt @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.gradle + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.TaskProvider +import java.io.File + +object VersionCodeTasks { + @JvmStatic + fun registerValidationTask( + project: Project, + versionCode: Int?, + ): TaskProvider = + with(project) { + val explicitBase = providers.gradleProperty("versionCodeBase") + val baseCode = atRevision(providers, rootDir, explicitBase.getOrElse("HEAD^")) + val headCode = atRevision(providers, rootDir, "HEAD") + val currentScript = providers.fileContents(layout.projectDirectory.file(buildFile.name)).asText + tasks.register("validateVersionCode") { + group = "verification" + description = "Validates the versionCode bump." + doLast { + // Require a literal in the current script as well as the comparison revision. + VersionCode.read(currentScript.get()) + // Use CI's base, or compare local changes to HEAD (HEAD^ once committed). + val previousCode = if (explicitBase.isPresent || headCode.get() == versionCode) baseCode.get() else headCode.get() + VersionCode.validate(previousCode, versionCode) + } + } + } + + /** Read whichever script exists at the comparison revision, including across a Kotlin DSL migration. */ + private fun atRevision( + providers: ProviderFactory, + rootDir: File, + revision: String, + ): Provider { + val scripts = + listOf("AnkiDroid/build.gradle", "AnkiDroid/build.gradle.kts").map { path -> + providers.exec { + workingDir(rootDir) + commandLine("git", "show", "$revision:$path") + isIgnoreExitValue = true + } + } + return providers.provider { + val script = + requireNotNull(scripts.singleOrNull { it.result.get().exitValue == 0 }) { + "Expected exactly one AnkiDroid build.gradle or build.gradle.kts at '$revision'." + } + VersionCode.read(script.standardOutput.asText.get()) + } + } +} diff --git a/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt b/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt deleted file mode 100644 index f1f076b759ee..000000000000 --- a/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/BuildSrcTest.kt +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package com.ichi2.anki.gradle - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class BuildSrcTest { - @Test - fun `runs buildSrc tests`() { - assertEquals(2, 1 + 1) - } -} diff --git a/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/VersionCodeTest.kt b/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/VersionCodeTest.kt new file mode 100644 index 000000000000..920fe07c81d8 --- /dev/null +++ b/buildSrc/src/test/kotlin/com/ichi2/anki/gradle/VersionCodeTest.kt @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.gradle + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class VersionCodeTest { + @Test + fun `accepts Groovy Kotlin whitespace and comment formats`() { + for (script in listOf( + "versionCode 22500202", // Groovy + "versionCode = 22500202", // Kotlin + " versionCode=\n 22500202;\n", // Whitespace + "versionCode = 22500202 // beta", // Comment + )) { + assertEquals(22500202, VersionCode.read(script)) + } + } + + @Test + fun `rejects missing ambiguous and unsupported versionCode declarations`() { + for (script in listOf( + "", + "versionCode = appVersionCode", + "versionCode = 22_500_202", + "versionCode = 22500202 + 1", + "versionCode = 22500202.toInt()", + "versionCode = 22500202\n .toInt()", + "versionCode=22500202\nversionCode=22500203", + "versionCode=22500202\nversionCode=appVersionCode", + "versionCode=2147483648", + )) { + val error = assertThrows(IllegalArgumentException::class.java) { VersionCode.read(script) } + assertTrue(error.message!!.contains("literal")) + } + } + + @Test + fun `rejects zero and leading-zero literals`() { + // can be parsed as octal + for (literal in listOf("0100000", "022500202", "001", "0")) { + assertThrows(IllegalArgumentException::class.java) { VersionCode.read("versionCode = $literal") } + } + } + + @Test + fun `rejects chained calls after whitespace or comments`() { + for (gap in listOf("\n\n", "\n// adjustment\n", "\n/* adjustment\n * continues here */\n")) { + assertThrows(IllegalArgumentException::class.java) { VersionCode.read("versionCode = 22500202$gap .plus(1)") } + } + } + + @Test + fun `ignores dots in comments following a literal`() { + for (comment in listOf("// See example.com", "/* See example.com */")) { + assertEquals(22500202, VersionCode.read("versionCode = 22500202\n$comment\nversionName = \"2.25.0beta2\"")) + } + } + + @Test + fun `accepts unchanged codes one increment and the maximum bump`() { + VersionCode.validate(previous = 22500202, code = 22500202) + VersionCode.validate(previous = 22500202, code = 22500203) + VersionCode.validate(previous = 22500300, code = 22600300) + } + + @Test + fun `rejects oversized and decreasing codes`() { + for (code in listOf(22600203, 22500201, Int.MAX_VALUE)) { + assertThrows(IllegalArgumentException::class.java) { VersionCode.validate(22500202, code) } + } + } + + @Test + fun `rejects a missing code`() { + assertThrows(IllegalArgumentException::class.java) { VersionCode.validate(22500202, null) } + } +} From 2531237657ba55ac66ef5ca631b7bed7436544f6 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sat, 26 Sep 2026 22:09:23 +0100 Subject: [PATCH 09/43] build(release): validate `versionCode` before publishing Specifically added to `release.sh` so non-CI publishes are tested. Fetch 2 commits so HEAD and its parent are available. Assisted-by: GPT-6 --- .github/workflows/publish.yml | 2 ++ AnkiDroid/build.gradle | 11 +++++++++++ tools/release.sh | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 05a0ab5573bd..2adaf6bd1313 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -28,6 +28,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 2 - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 name: Gradle Cache diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle index 8ac6af4f8f68..63061a5ccf44 100644 --- a/AnkiDroid/build.gradle +++ b/AnkiDroid/build.gradle @@ -1,3 +1,5 @@ +import com.ichi2.anki.gradle.VersionCodeTasks + plugins { // Gradle plugin portal alias(libs.plugins.tripletPlay) @@ -375,6 +377,15 @@ android { } } +// register `validateVersionCode` with our (potentially new versionCode). +androidComponents.finalizeDsl { androidDsl -> + def validation = VersionCodeTasks.registerValidationTask(project, androidDsl.defaultConfig.versionCode) + // handle all 'publish' calls, even if run directly + tasks.matching { it.name.startsWith("publish") }.configureEach { + dependsOn(validation) + } +} + play { serviceAccountCredentials.set(file("${homePath}/src/AnkiDroid-GCP-Publish-Credentials.json")) track.set('alpha') diff --git a/tools/release.sh b/tools/release.sh index 74761c80d304..a3954f66dbb1 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -96,6 +96,12 @@ if [ "$PUBLIC" != "public" ]; then sed -i -e s/versionCode="$PREVIOUS_CODE"/versionCode="$GUESSED_CODE"/g $GRADLEFILE fi +# Validate the resulting versionCode before committing or tagging the release. +if ! ./gradlew :AnkiDroid:validateVersionCode --no-configuration-cache +then + exit 1 +fi + # If any changes go in during the release process, pushing fails, so push immediately. # Worst case this burns a version number despite a failure later, and we have a version/tag # that never launched. That's better than having to manually patch up build.gradle.kts and push a tag From 462c27833376c74e98df264c63b4d115ee623ce4 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sat, 26 Sep 2026 22:09:47 +0100 Subject: [PATCH 10/43] ci: enforce `versionCode` bump limits Ensure it doesn't jump by over 100k from the base commit Assisted-by: GPT-6 --- .github/workflows/README.md | 14 +++++++------- .github/workflows/lint.yml | 8 +++++++- tools/validate-version-code.sh | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) create mode 100755 tools/validate-version-code.sh diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 3789efc75203..a6051a7cbbdf 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -11,14 +11,14 @@ following scripts before submitting a PR. Alternately, you may run the actions on your fork of `Anki-Android`. -| **Job** | **Command** | **Comments** | -|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------| -| [Lint (Kotlin)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew lintAll ktLintCheck lint-rules:test --daemon` | Android lint rules, formatting and tests for lint rules | -| [Lint (JavaScript)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | See script | Prettier, lint & code formatting | -| [Build Logic](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew -p buildSrc test` | Tests for build logic | -| [Unit Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_unit.yml) | `./gradlew jacocoUnitTestReport --daemon` | Unit tests for the Android Project | +| **Job** | **Command** | **Comments** | +|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Lint (Kotlin)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew lintAll ktLintCheck lint-rules:test --daemon` | Android lint rules, formatting and tests for lint rules | +| [Lint (JavaScript)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | See script | Prettier, lint & code formatting | +| [Build Logic](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew -p buildSrc test` and `tools/validate-version-code.sh` | Version-code bump checks | +| [Unit Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_unit.yml) | `./gradlew jacocoUnitTestReport --daemon` | Unit tests for the Android Project | | [Emulator Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_emulator.yml) | `TEST_RELEASE_BUILD=true ./gradlew jacocoAndroidTestReport --daemon` | Emulator tests for the Android Project.
CI runs `bash .github/disable_art_metrics.sh` and `adb shell settings put global hide_error_dialogs 1` first | -| [CodeQL](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/codeql.yml) | N/A | GitHub-only check.
[Docs](https://codeql.github.com/) | +| [CodeQL](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/codeql.yml) | N/A | GitHub-only check.
[Docs](https://codeql.github.com/) | ## Other Workflows diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a6e45ea6736f..b95542719c11 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - fetch-depth: 1 + fetch-depth: 2 - name: Configure JDK uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0 @@ -50,6 +50,12 @@ jobs: - name: Test build logic run: ./gradlew -p buildSrc test --daemon + # Reject versionCode decreases and increases over 100,000. + - name: Validate versionCode + env: + VERSION_CODE_BASE: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }} + run: tools/validate-version-code.sh "$VERSION_CODE_BASE" + - name: Warm Gradle Cache # This makes sure we fetch gradle network resources with a retry uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 diff --git a/tools/validate-version-code.sh b/tools/validate-version-code.sh new file mode 100755 index 000000000000..6acfd877c218 --- /dev/null +++ b/tools/validate-version-code.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +# Usage from the repository root: tools/validate-version-code.sh [base-commit] + +set -euo pipefail + +base=${1:-HEAD^} +if [[ "$base" =~ ^0+$ ]]; then + base=HEAD^ +fi + +# Fetch the comparison commit if it is outside the shallow checkout. +if ! git cat-file -e "${base}^{commit}" 2>/dev/null; then + git fetch --no-tags --depth=1 origin "$base" +fi + +./gradlew :AnkiDroid:validateVersionCode -PversionCodeBase="$base" --daemon From de4a16b450da34039e14a0e9f5e3de25efbd9ea6 Mon Sep 17 00:00:00 2001 From: Mike Hardy Date: Sun, 27 Sep 2026 17:10:22 -0500 Subject: [PATCH 11/43] ci: target merged PRs at 2.26 milestone --- .github/workflows/milestone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml index c79ac6a97bcd..d09ae34b1f89 100644 --- a/.github/workflows/milestone.yml +++ b/.github/workflows/milestone.yml @@ -20,7 +20,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ github.event.pull_request.number }}, - milestone: 77 + milestone: 79 }); // graphql query to get referenced issues to merged pull request @@ -51,7 +51,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, - milestone: 77 + milestone: 79 }); } From 332104e3d119e5c09f6b445ebd00d51e0d50dd8f Mon Sep 17 00:00:00 2001 From: Mike Hardy Date: Sun, 27 Sep 2026 18:49:44 -0500 Subject: [PATCH 12/43] release: prepare for 2.26.0 alpha series --- AnkiDroid/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle index 63061a5ccf44..ca6cdea8e422 100644 --- a/AnkiDroid/build.gradle +++ b/AnkiDroid/build.gradle @@ -116,9 +116,9 @@ android { // // This ensures the correct ordering between the various types of releases (dev < alpha < beta < release) which is // needed for upgrades to be offered correctly. - versionCode=22500202 + versionCode=22600100 // If you change this to a new version, you probably also want to update .gradle/workflows/milestone.yml for the new version... - versionName="2.25.0beta2" + versionName="2.26.0alpha0" minSdk = libs.versions.minSdk.get().toInteger() // After #13695: change .tests_emulator.yml From 979fb1ae08f819ff15789db4b9db35481090d7c8 Mon Sep 17 00:00:00 2001 From: chaos10001 Date: Tue, 22 Sep 2026 23:03:04 +0100 Subject: [PATCH 13/43] fix: Commit issues --- .../dialogs/customstudy/CustomStudyDialog.kt | 36 +++++++++++++++---- .../main/res/layout/fragment_custom_study.xml | 2 +- AnkiDroid/src/main/res/values/03-dialogs.xml | 4 +-- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt index fa5d6b7be798..064144b3b2d7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt @@ -33,11 +33,13 @@ import anki.scheduler.CustomStudyRequest.Cram.CramKind import anki.scheduler.copy import anki.scheduler.customStudyRequest import anki.search.SearchNode +import anki.search.searchNode import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.asyncIO +import com.ichi2.anki.browser.search.CardState import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.common.preferences.sharedPrefs import com.ichi2.anki.common.utils.annotation.KotlinCleanup @@ -286,7 +288,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { binding.detailsText1.text = text1 // 'review ahead' has a dialog title, so the empty label would only add a blank line - binding.detailsText1.isVisible = contextMenuOption != STUDY_AHEAD + binding.detailsText1.isVisible = contextMenuOption != STUDY_AHEAD && contextMenuOption != STUDY_PREVIEW binding.detailsText2.text = text2 binding.cardsStateSelectorLayout.isVisible = contextMenuOption == STUDY_TAGS @@ -326,7 +328,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { if (contextMenuOption == EXTEND_NEW || contextMenuOption == EXTEND_REV) { inputType = EditorInfo.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_FLAG_SIGNED } - if (contextMenuOption == STUDY_AHEAD) { + if (contextMenuOption == STUDY_AHEAD || contextMenuOption == STUDY_PREVIEW) { inputType = EditorInfo.TYPE_CLASS_NUMBER filters += arrayOf(InputFilter.LengthFilter(5), NonLeadingZeroInputFilter) setSuffixText(defaultValue.toInt()) @@ -335,7 +337,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { val positiveBtnLabel = if (contextMenuOption == STUDY_TAGS) { TR.sentenceCase.chooseTags - } else if (contextMenuOption == STUDY_AHEAD) { + } else if (contextMenuOption == STUDY_AHEAD || contextMenuOption == STUDY_PREVIEW) { getString(R.string.dialog_positive_create) } else { getString(R.string.dialog_ok) @@ -349,7 +351,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { AlertDialog .Builder(requireActivity()) .apply { - if (contextMenuOption == STUDY_AHEAD) { + if (contextMenuOption == STUDY_AHEAD || contextMenuOption == STUDY_PREVIEW) { title(text = contextMenuOption.getTitle(resources)) } }.customView( @@ -421,7 +423,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { } launchCustomStudy(contextMenuOption, n) } - if (contextMenuOption == STUDY_AHEAD) { + if (contextMenuOption == STUDY_AHEAD || contextMenuOption == STUDY_PREVIEW) { // the stored default may match no cards searchJob = launchCatchingTask { updateCreateButtonState(dialog, userInputValue) } } @@ -429,7 +431,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { binding.detailsEditText2.doAfterTextChanged { val value = userInputValue - if (contextMenuOption != STUDY_AHEAD) { + if (contextMenuOption != STUDY_AHEAD && contextMenuOption != STUDY_PREVIEW) { dialog.positiveButton.isEnabled = value != null && value != 0 return@doAfterTextChanged } @@ -448,6 +450,22 @@ class CustomStudyDialog : AnalyticsDialogFragment() { binding.detailsEditText2Layout.suffixText = resources.getQuantityString(R.plurals.set_due_date_label_suffix, days) } + /** + * Whether the deck has new cards added in the last [days] days. + * + * Upstream: https://github.com/ankitects/anki/blob/main/qt/aqt/customstudy.py + */ + private suspend fun hasPreviewCards(days: Int): Boolean = + withCol { + val search = + listOf( + searchNode { deck = decks.name(viewModel.deckId) }, + CardState.New.toSearchNode(), + searchNode { addedInDays = days }, + ) + findCards(buildSearchString(search)).isNotEmpty() + } + /** Enables 'Create' only if some cards would be reviewed ahead by [days] */ private suspend fun updateCreateButtonState( dialog: AlertDialog, @@ -458,7 +476,11 @@ class CustomStudyDialog : AnalyticsDialogFragment() { dialog.positiveButton.isEnabled = false return } - val hasCards = hasCardsDueWithin(days) + val hasCards = + when (selectedSubDialog) { + STUDY_PREVIEW -> hasPreviewCards(days) + else -> hasCardsDueWithin(days) + } binding.detailsEditText2Layout.error = if (hasCards) null else TR.customStudyNoCardsMatchedTheCriteriaYou() dialog.positiveButton.isEnabled = hasCards } diff --git a/AnkiDroid/src/main/res/layout/fragment_custom_study.xml b/AnkiDroid/src/main/res/layout/fragment_custom_study.xml index 1d9ac2f63810..f6a647f921cb 100644 --- a/AnkiDroid/src/main/res/layout/fragment_custom_study.xml +++ b/AnkiDroid/src/main/res/layout/fragment_custom_study.xml @@ -31,7 +31,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start" - android:layout_marginBottom="16dip" + android:layout_marginBottom="4dip" android:layout_marginStart="4dip" android:layout_weight="4" android:gravity="start|center" diff --git a/AnkiDroid/src/main/res/values/03-dialogs.xml b/AnkiDroid/src/main/res/values/03-dialogs.xml index 732873170dd7..9313f6d387ff 100644 --- a/AnkiDroid/src/main/res/values/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values/03-dialogs.xml @@ -37,7 +37,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full @@ -79,7 +79,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result From 9982f6fefaba7ecfca08ccadbdfaac6737ca41ac Mon Sep 17 00:00:00 2001 From: chaos10001 Date: Fri, 25 Sep 2026 09:00:41 +0100 Subject: [PATCH 14/43] docs: update upstream reference to GitHub permalink --- .../com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt index 064144b3b2d7..b4c71a270a94 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt @@ -453,7 +453,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { /** * Whether the deck has new cards added in the last [days] days. * - * Upstream: https://github.com/ankitects/anki/blob/main/qt/aqt/customstudy.py + * Upstream: https://github.com/ankitects/anki/blob/acaeee91fa853e4a7a78dcddbb832d009ec3529a/qt/aqt/customstudy.py */ private suspend fun hasPreviewCards(days: Int): Boolean = withCol { From 02a804b33e6c1ec7022436296a594d8be84b2578 Mon Sep 17 00:00:00 2001 From: chaos10001 Date: Fri, 25 Sep 2026 15:02:05 +0100 Subject: [PATCH 15/43] docs: update upstream reference to HEAD permalink --- .../com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt index b4c71a270a94..9ce37bdda7a3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt @@ -453,7 +453,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { /** * Whether the deck has new cards added in the last [days] days. * - * Upstream: https://github.com/ankitects/anki/blob/acaeee91fa853e4a7a78dcddbb832d009ec3529a/qt/aqt/customstudy.py + * Upstream: https://github.com/ankitects/anki/blob/618c1787c83ac9baace0527a2f572631b86b7aee/qt/aqt/customstudy.py */ private suspend fun hasPreviewCards(days: Int): Boolean = withCol { From 745d9679e872e53a5252d0dfa9d0f2ae0c4bf2fc Mon Sep 17 00:00:00 2001 From: Sanjeev narang Date: Thu, 20 Aug 2026 23:47:54 +0530 Subject: [PATCH 16/43] fix(controls): improve post-assignment dialog Fixes 20759 --- .../preferences/ControlsSettingsFragment.kt | 33 ++++--- .../src/main/res/values/10-preferences.xml | 12 +-- .../ControlsSettingsFragmentTest.kt | 89 +++++++++++++++++++ 3 files changed, 116 insertions(+), 18 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt index 3965651b85f1..6b2f093feb54 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt @@ -26,6 +26,8 @@ import com.ichi2.anki.ui.internationalization.sentenceCase import com.ichi2.anki.utils.ext.sharedPrefs import com.ichi2.preferences.ControlPreference import com.ichi2.preferences.ReviewerControlPreference +import com.ichi2.utils.negativeButton +import com.ichi2.utils.positiveButton import com.ichi2.utils.show import kotlinx.coroutines.launch import timber.log.Timber @@ -238,23 +240,28 @@ class ControlsSettingsFragment : ) for (key in answerCommandKeys) { (findPreference(key) as? ReviewerControlPreference)?.let { answerPref -> - val items = - arrayOf( - getString(R.string.only_answer), - getString(R.string.flip_and_answer), - ) answerPref.setOnBindingSelectedListener { binding -> + val isAlreadyAssigned = + showAnswerPref?.getMappableBindings()?.any { + it.binding == binding && + (it.side == CardSide.QUESTION || it.side == CardSide.BOTH) + } == true + if (isAlreadyAssigned) { + answerPref.addBinding(binding, CardSide.ANSWER) + return@setOnBindingSelectedListener true + } + AlertDialog.Builder(requireContext()).show { setTitle(answerPref.title) setIcon(answerPref.icon) - setItems(items) { _, index -> - when (index) { - 0 -> answerPref.addBinding(binding, CardSide.ANSWER) - 1 -> { - answerPref.addBinding(binding, CardSide.ANSWER) - showAnswerPref?.addBinding(binding, CardSide.QUESTION) - } - } + setMessage(getString(R.string.also_assign_binding_to_show_answer, getString(R.string.show_answer))) + + positiveButton(text = getString(R.string.dialog_assign)) { + answerPref.addBinding(binding, CardSide.ANSWER) + showAnswerPref?.addBinding(binding, CardSide.QUESTION) + } + negativeButton(text = getString(R.string.dialog_skip)) { + answerPref.addBinding(binding, CardSide.ANSWER) } } true diff --git a/AnkiDroid/src/main/res/values/10-preferences.xml b/AnkiDroid/src/main/res/values/10-preferences.xml index 4a052bc521d8..0245a5590b0b 100644 --- a/AnkiDroid/src/main/res/values/10-preferences.xml +++ b/AnkiDroid/src/main/res/values/10-preferences.xml @@ -311,7 +311,7 @@ Replace newlines with HTML In the note editor, convert any instances of <br> to newlines when editing a card. - + Focus ‘type in answer’ Automatically focus the ‘type in answer’ field in the study screen @@ -539,10 +539,12 @@ this formatter is used if the bind only applies to the answer">A: %s
>feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/ControlsSettingsFragmentTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/ControlsSettingsFragmentTest.kt index a31e56670403..a04d84c92bfa 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/ControlsSettingsFragmentTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/ControlsSettingsFragmentTest.kt @@ -2,12 +2,26 @@ package com.ichi2.anki.preferences +import android.content.DialogInterface +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.matcher.ViewMatchers.assertThat import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.R import com.ichi2.anki.RobolectricTest +import com.ichi2.anki.cardviewer.Gesture +import com.ichi2.anki.common.destinations.DeferredNavigation +import com.ichi2.anki.reviewer.Binding +import com.ichi2.anki.reviewer.CardSide +import com.ichi2.preferences.ReviewerControlPreference import com.ichi2.testutils.HamcrestUtils +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasItem +import org.hamcrest.Matchers.not import org.junit.Test import org.junit.runner.RunWith +import org.robolectric.shadows.ShadowDialog +import kotlin.test.assertNull @RunWith(AndroidJUnit4::class) class ControlsSettingsFragmentTest : RobolectricTest() { @@ -28,4 +42,79 @@ class ControlsSettingsFragmentTest : RobolectricTest() { assertThat(xmlKeys, HamcrestUtils.containsInAnyOrder(enumKeys)) } } + + /** + * Tests for alert dialog behavior in [ControlsSettingsFragment] using 'swipe up' gesture as the input binding. + */ + @Test + fun `skip button binding only to answer`() { + withControlsSettings { + val binding = Binding.GestureInput(Gesture.SWIPE_UP) + answerPref.setBinding(binding) + clickAlertDialogButton(DialogInterface.BUTTON_NEGATIVE, checkDismissed = true) + + val showAnswerBindings = showAnswerPref.getMappableBindings().map { Pair(it.binding, it.side) } + val answerBindings = answerPref.getMappableBindings().map { Pair(it.binding, it.side) } + assertThat("binding is assigned to Show answer", showAnswerBindings, not(hasItem(equalTo(Pair(binding, CardSide.QUESTION))))) + assertThat("binding not assigned to answer", answerBindings, hasItem(equalTo(Pair(binding, CardSide.ANSWER)))) + } + } + + @Test + fun `assign buttons binding to both answer and show answer`() { + withControlsSettings { + val binding = Binding.GestureInput(Gesture.SWIPE_UP) + answerPref.setBinding(binding) + clickAlertDialogButton(DialogInterface.BUTTON_POSITIVE, checkDismissed = true) + + val showAnswerBindings = showAnswerPref.getMappableBindings().map { Pair(it.binding, it.side) } + val answerBindings = answerPref.getMappableBindings().map { Pair(it.binding, it.side) } + assertThat("binding not assigned to Show answer", showAnswerBindings, hasItem(equalTo(Pair(binding, CardSide.QUESTION)))) + assertThat("binding not assigned to answer", answerBindings, hasItem(equalTo(Pair(binding, CardSide.ANSWER)))) + } + } + + @Test + fun `no dialog if binding is already assigned to show answer`() { + withControlsSettings { + val binding = Binding.GestureInput(Gesture.SWIPE_UP) + showAnswerPref.addBinding(binding, CardSide.QUESTION) + + answerPref.setBinding(binding) + + assertNull(ShadowDialog.getLatestDialog(), "no dialog should be shown") + val showAnswerBindings = showAnswerPref.getMappableBindings().map { Pair(it.binding, it.side) } + val answerBindings = answerPref.getMappableBindings().map { Pair(it.binding, it.side) } + assertThat("binding not kept on Show answer", showAnswerBindings, hasItem(Pair(binding, CardSide.QUESTION))) + assertThat("binding not assigned to answer", answerBindings, hasItem(Pair(binding, CardSide.ANSWER))) + } + } +} + +private val ControlsSettingsFragment.showAnswerPref + get() = requirePreference(getString(R.string.show_answer_command_key)) + +private val ControlsSettingsFragment.answerPref + get() = requirePreference(getString(R.string.answer_good_command_key)) + +/** + * launches the [ControlsSettingsFragment] and runs [block] on it. + */ +context(_: DeferredNavigation) +private fun withControlsSettings(block: ControlsSettingsFragment.() -> Unit) { + val intent = + PreferencesActivity.getIntent( + ApplicationProvider.getApplicationContext(), + ControlsSettingsFragment::class, + ) + ActivityScenario.launch(intent).use { scenario -> + scenario.onActivity { activity -> + val preferencesFragment = activity.fragment as PreferencesFragment + val controlsSettingsFragment = + preferencesFragment.childFragmentManager.findFragmentByTag( + ControlsSettingsFragment::class.java.name, + ) as ControlsSettingsFragment + controlsSettingsFragment.block() + } + } } From 17177c53e8335fc71de967d2a3fd0bc9c11b61ba Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:07:18 +0530 Subject: [PATCH 17/43] feat(common): add runCatchingWithLog Like runCatchingWithReport, but only logs the exception instead of also sending a crash report. The upcoming directory picker scans storage, where failures are routine and not worth reporting. Co-authored-by: ShaanNarendran --- .../crashreporting/CrashReportService.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/common/android/src/main/java/com/ichi2/anki/common/crashreporting/CrashReportService.kt b/common/android/src/main/java/com/ichi2/anki/common/crashreporting/CrashReportService.kt index 23c1167b6ada..e2fd68739177 100644 --- a/common/android/src/main/java/com/ichi2/anki/common/crashreporting/CrashReportService.kt +++ b/common/android/src/main/java/com/ichi2/anki/common/crashreporting/CrashReportService.kt @@ -262,3 +262,29 @@ fun runCatchingWithReport( if (e is Error) throw e Result.failure(e) } + +/** + * Runs the provided block, catching [Exception] and logging it. + * + * Unlike [runCatchingWithReport], this does **not** send a crash report. + * + * **Note**: This differs from [runCatching] - `Error` is thrown + * + * @param origin Data logged to Timber + * @param block Code to execute + * + * @throws Error If raised, this will be rethrown + * + * @return A Result containing either the successful result of [block] or the [Exception] thrown + */ +fun runCatchingWithLog( + origin: String? = null, + block: () -> T, +): Result = + try { + Result.success(block()) + } catch (e: Throwable) { + Timber.w(e, origin) + if (e is Error) throw e + Result.failure(e) + } From 8e0e45ca7786c5825da28d01bda30dbd41792456 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:07:20 +0530 Subject: [PATCH 18/43] feat(utils): add File.containsFile Checks whether a directory contains a named file, resolving the canonical path first to guard against path traversal. The upcoming directory picker uses it to spot existing collections. Comes with a JVM test covering the edge cases. Co-authored-by: ShaanNarendran Assisted-by: Claude Opus 5 --- .../java/com/ichi2/anki/utils/ext/File.kt | 24 ++++++++ .../ichi2/anki/utils/ext/ContainsFileTest.kt | 58 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/File.kt create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/utils/ext/ContainsFileTest.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/File.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/File.kt new file mode 100644 index 000000000000..5c0f078f309b --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/File.kt @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2026 Shaan Narendran +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.utils.ext + +import java.io.File + +/** + * @param name Relative file name or path inside this directory. + * @return `true` if the named file exists within this directory + * + * @throws SecurityException If [SecurityManager.checkRead] is used and access is denied. + */ +fun File.containsFile(name: String): Boolean { + if (!isDirectory) return false + + val maybeFile = File(this, name).canonicalFile + + // guard against path traversal + if (!maybeFile.path.startsWith(this.canonicalPath + File.separator)) return false + + // ensure the file exists, and is not a directory + return maybeFile.isFile +} diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/utils/ext/ContainsFileTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/utils/ext/ContainsFileTest.kt new file mode 100644 index 000000000000..bfafd00ce183 --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/utils/ext/ContainsFileTest.kt @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: 2026 Ashish Yadav +// SPDX-License-Identifier: GPL-3.0-or-later +package com.ichi2.anki.utils.ext + +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** Tests for [File.containsFile] */ +class ContainsFileTest { + @get:Rule + val temp = TemporaryFolder() + + @Test + fun `finds an existing file`() { + temp.newFile("collection.anki2") + assertTrue(temp.root.containsFile("collection.anki2")) + } + + @Test + fun `false when the file does not exist`() { + assertFalse(temp.root.containsFile("collection.anki2")) + } + + @Test + fun `false when the name points to a directory`() { + temp.newFolder("collection.anki2") + assertFalse(temp.root.containsFile("collection.anki2")) + } + + @Test + fun `finds a file in a subdirectory`() { + temp.newFolder("sub") + temp.newFile("sub/collection.anki2") + assertTrue(temp.root.containsFile("sub/collection.anki2")) + } + + @Test + fun `false for a path escaping the directory`() { + temp.newFile("secret.txt") + val sub = temp.newFolder("sub") + assertFalse(sub.containsFile("../secret.txt")) + } + + @Test + fun `false for an empty name`() { + assertFalse(temp.root.containsFile("")) + } + + @Test + fun `false when the receiver is not a directory`() { + val file = temp.newFile("plain.txt") + assertFalse(file.containsFile("anything")) + } +} From 9b567680da6e68e5912551387bd5f660f9715b6b Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:07:22 +0530 Subject: [PATCH 19/43] feat(preferences): add a directory picker Scans the storage roots for AnkiDroid directories and lists them for one tap selection, so an SD card can be chosen without knowing its path. A neutral button still accepts a custom path, which is normalized, created and checked for writability before being applied. Co-authored-by: ShaanNarendran --- .../ExternalDirectorySelectionPreference.kt | 192 ++++++++++++++++++ .../src/main/res/values/10-preferences.xml | 6 + 2 files changed, 198 insertions(+) create mode 100644 AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt b/AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt new file mode 100644 index 000000000000..fb742c1ad785 --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt @@ -0,0 +1,192 @@ +// SPDX-FileCopyrightText: 2024 David Allison +// SPDX-FileCopyrightText: 2026 Shaan Narendran +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.preferences + +import android.content.Context +import android.graphics.Color +import android.text.Spannable +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import android.util.AttributeSet +import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.preference.ListPreference +import androidx.preference.ListPreferenceDialogFragmentCompat +import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.runCatchingWithLog +import com.ichi2.anki.common.utils.android.showThemedToast +import com.ichi2.anki.startup.getDefaultAnkiDroidDirectory +import com.ichi2.anki.utils.ext.containsFile +import com.ichi2.utils.input +import com.ichi2.utils.negativeButton +import com.ichi2.utils.positiveButton +import com.ichi2.utils.show +import timber.log.Timber +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths + +/** + * Lets the user pick the AnkiDroid directory from a list of detected storage + * locations, so an SD card can be chosen without knowing its path. + * A custom path can still be typed in via the dialog. + */ +class ExternalDirectorySelectionPreference( + context: Context, + attrs: AttributeSet?, +) : ListPreference(context, attrs), + DialogFragmentProvider { + init { + summaryProvider = + SummaryProvider { pref -> + pref.value.takeUnless { it.isNullOrEmpty() } ?: context.getString(R.string.pref_directory_not_set) + } + } + + /** The default AnkiDroid directory, or null if storage is unavailable. */ + private val defaultAnkiDir: File? + get() = + try { + getDefaultAnkiDroidDirectory(context) + } catch (e: Exception) { + Timber.w(e, "Could not access default AnkiDroid directory") + null + } + + /** + * Builds the candidate paths to show: the current value, the default + * directory, and anything found by scanning storage. + */ + private fun loadDirectories(): List = + buildList { + if (value?.isNotEmpty() == true) { + add(File(value)) + } + defaultAnkiDir?.let { add(it) } + addAll(getScannedDirectories()) + }.map { it.absolutePath } + .distinct() + + private fun isValidAnkiDir(dir: File): Boolean { + try { + if (!dir.isDirectory || dir.name.startsWith(".")) return false + if (IGNORED_DIRECTORIES.contains(dir.name.lowercase())) return false + if (dir.name.startsWith("AnkiDroid", ignoreCase = true)) return true + return dir.containsFile("collection.anki2") + } catch (e: Exception) { + Timber.w(e, "Could not access directory") + return false + } + } + + /** + * Scans the external storage roots for AnkiDroid directories. + * A root with none, or which cannot be listed, is offered as a new AnkiDroid location instead. + */ + private fun getScannedDirectories(): List { + val storageRoots = + runCatchingWithLog("get storage roots") { + context.getExternalFilesDirs(null) + }.getOrNull() + .orEmpty() + .filterNotNull() + + fun File.getValidAnkiSubfolders(): List? { + val subFolders = listFiles() ?: return null + return subFolders.filter { isValidAnkiDir(it) }.ifEmpty { null } + } + + return storageRoots.flatMap { root -> + when (val ankiFolders = root.getValidAnkiSubfolders()) { + // If no anki directories are found, we can list this as it is likely an SD card + null -> listOf(File(root, "AnkiDroid")) + else -> ankiFolders + } + } + } + + // TODO: Possibly move loadDirectories() to a background thread if ANR occurs + override fun makeDialogFragment(): DialogFragment { + val paths = loadDirectories() + entries = paths.map(::toDisplayLabel).toTypedArray() + entryValues = paths.toTypedArray() + return FullWidthListPreferenceDialogFragment() + } + + /** Builds the display label for a path, graying out its storage root. */ + private fun toDisplayLabel(path: String): CharSequence { + // Split at "/Android/" so the root is gray and the rest keeps the normal color + // Eg: "/storage/emulated/0" is gray, "/Android/data/com.ichi2.anki" is not + val androidIndex = path.indexOf("/Android/") + if (androidIndex == -1) return path + val displayString = "${path.take(androidIndex)}\n${path.substring(androidIndex)}" + return SpannableString(displayString).apply { + setSpan(ForegroundColorSpan(Color.GRAY), 0, androidIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + + companion object { + private val IGNORED_DIRECTORIES = setOf("collection.media", "backups", "cache", "code_cache") + } +} + +/** List dialog stretched to full width, with a neutral button to enter a custom path. */ +class FullWidthListPreferenceDialogFragment : ListPreferenceDialogFragmentCompat() { + override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) { + super.onPrepareDialogBuilder(builder) + builder.setNeutralButton(R.string.pref_custom_path) { _, _ -> showCustomPathInput() } + } + + private fun showCustomPathInput() { + val context = requireContext() + val pref = (preference as? ExternalDirectorySelectionPreference) ?: return + AlertDialog + .Builder(context) + .show { + setTitle(R.string.pref_enter_custom_path) + setView(R.layout.dialog_generic_text_input) + positiveButton(android.R.string.ok) + negativeButton(android.R.string.cancel) + }.input( + prefill = pref.value ?: "", + allowEmpty = false, + ) { dialog, text -> + try { + // normalize before saving so "a//b/" and "a/b" become one entry + val pathObj = Paths.get(text.toString().trim()).toAbsolutePath().normalize() + val newPath = pathObj.toString() + Files.createDirectories(pathObj) + if (!Files.isWritable(pathObj)) { + showThemedToast( + context, + context.getString(R.string.pref_directory_not_writable), + true, + ) + return@input + } + dialog.dismiss() + if (pref.callChangeListener(newPath)) { + pref.value = newPath + } + } catch (e: Exception) { + Timber.w(e, "Failed to set custom path") + AlertDialog.Builder(context).show { + setTitle(context.getString(R.string.could_not_create_dir, text.toString())) + setMessage(e.stackTraceToString()) + positiveButton(android.R.string.ok) + } + } + } + } + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + ) + } +} diff --git a/AnkiDroid/src/main/res/values/10-preferences.xml b/AnkiDroid/src/main/res/values/10-preferences.xml index 0245a5590b0b..0be7067c3945 100644 --- a/AnkiDroid/src/main/res/values/10-preferences.xml +++ b/AnkiDroid/src/main/res/values/10-preferences.xml @@ -549,4 +549,10 @@ this formatter is used if the bind only applies to the answer">A: %s Open settings Related settings + + + Not set + Custom path + Enter custom path + Directory is not writable From d2d0a1d2a4a9d702e6342fa14a0c3f1e5b8b2dd3 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Sun, 16 Aug 2026 22:07:24 +0530 Subject: [PATCH 20/43] feat(settings): use the new directory picker Replaces the free text AnkiDroid directory field in Advanced settings with the picker. Fixes #3114 Co-authored-by: ShaanNarendran --- .../com/ichi2/anki/preferences/AdvancedSettingsFragment.kt | 6 +++--- AnkiDroid/src/main/res/xml/preferences_advanced.xml | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt index c1bc2b7ff23f..bba80e798126 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt @@ -9,7 +9,6 @@ import android.content.pm.PackageManager import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat -import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.ichi2.anki.CollectionManager @@ -25,6 +24,7 @@ import com.ichi2.anki.settings.Prefs import com.ichi2.anki.snackbar.showSnackbar import com.ichi2.anki.startup.getDefaultAnkiDroidDirectory import com.ichi2.anki.utils.openUrl +import com.ichi2.preferences.ExternalDirectorySelectionPreference import com.ichi2.utils.Permissions import com.ichi2.utils.Permissions.openAppSettingsScreen import com.ichi2.utils.show @@ -62,7 +62,7 @@ class AdvancedSettingsFragment : SettingsFragment() { removeUnnecessaryAdvancedPrefs() // Check that input is valid before committing change in the collection path - requirePreference(CollectionHelper.PREF_COLLECTION_PATH).apply { + requirePreference(CollectionHelper.PREF_COLLECTION_PATH).apply { setOnPreferenceChangeListener { _, newValue: Any? -> val newPath = newValue as String try { @@ -81,7 +81,7 @@ class AdvancedSettingsFragment : SettingsFragment() { setTitle(R.string.dialog_collection_path_not_dir) setPositiveButton(R.string.dialog_ok) { _, _ -> } setNegativeButton(R.string.reset_custom_buttons) { _, _ -> - text = getDefaultAnkiDroidDirectory(requireContext()).absolutePath + value = getDefaultAnkiDroidDirectory(requireContext()).absolutePath } } false diff --git a/AnkiDroid/src/main/res/xml/preferences_advanced.xml b/AnkiDroid/src/main/res/xml/preferences_advanced.xml index e27c5c88fbc0..99172960d890 100644 --- a/AnkiDroid/src/main/res/xml/preferences_advanced.xml +++ b/AnkiDroid/src/main/res/xml/preferences_advanced.xml @@ -11,14 +11,12 @@ - + android:title="@string/col_path"/> Date: Sun, 16 Aug 2026 22:07:26 +0530 Subject: [PATCH 21/43] test(preferences): cover the directory picker list Scaffolding asked for in review: opens the dialog and checks the default and current directories are offered, and that every entry has a label. Written as a Robolectric test rather than androidTest: the instrumented suite runs against the release variant, where the host activity used by launchFragmentInContainer is missing, as it ships in a debug only manifest. Co-authored-by: ShaanNarendran --- ...xternalDirectorySelectionPreferenceTest.kt | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 AnkiDroid/src/test/java/com/ichi2/preferences/ExternalDirectorySelectionPreferenceTest.kt diff --git a/AnkiDroid/src/test/java/com/ichi2/preferences/ExternalDirectorySelectionPreferenceTest.kt b/AnkiDroid/src/test/java/com/ichi2/preferences/ExternalDirectorySelectionPreferenceTest.kt new file mode 100644 index 000000000000..efecc5bfbdcb --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/preferences/ExternalDirectorySelectionPreferenceTest.kt @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2026 Shaan Narendran + +package com.ichi2.preferences + +import androidx.core.content.edit +import androidx.preference.PreferenceManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.RobolectricTest +import com.ichi2.anki.common.preferences.sharedPrefs +import com.ichi2.anki.common.storage.CollectionHelper +import com.ichi2.anki.startup.getDefaultAnkiDroidDirectory +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasItem +import org.hamcrest.Matchers.instanceOf +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File + +/** + * Tests that [ExternalDirectorySelectionPreference] offers the directories a user + * would expect to pick from. + */ +@RunWith(AndroidJUnit4::class) +class ExternalDirectorySelectionPreferenceTest : RobolectricTest() { + @Test + fun `the default directory is offered`() { + val pref = buildPreference() + + assertThat(pref.entryPaths(), hasItem(getDefaultAnkiDroidDirectory(targetContext).absolutePath)) + } + + @Test + fun `the current directory is offered`() { + val current = File(targetContext.filesDir, "AnkiDroidTest").absolutePath + targetContext.sharedPrefs().edit(commit = true) { + putString(CollectionHelper.PREF_COLLECTION_PATH, current) + } + + val pref = buildPreference() + + assertThat(pref.entryPaths(), hasItem(current)) + } + + @Test + fun `each entry has a label`() { + val pref = buildPreference() + pref.makeDialogFragment() + + assertThat(pref.entries.size, equalTo(pref.entryValues.size)) + } + + @Test + fun `the dialog is the full width list`() { + val pref = buildPreference() + + assertThat(pref.makeDialogFragment(), instanceOf(FullWidthListPreferenceDialogFragment::class.java)) + } + + /** Builds the preference attached to a screen, as the settings screen does */ + private fun buildPreference(): ExternalDirectorySelectionPreference { + val pref = ExternalDirectorySelectionPreference(targetContext, null) + pref.key = CollectionHelper.PREF_COLLECTION_PATH + val preferenceManager = PreferenceManager(targetContext) + val screen = preferenceManager.createPreferenceScreen(targetContext) + preferenceManager.setPreferences(screen) + screen.addPreference(pref) + return pref + } + + /** The paths offered by the dialog */ + private fun ExternalDirectorySelectionPreference.entryPaths(): List { + makeDialogFragment() + return entryValues.map { it.toString() } + } +} From 9394fb09813da74af7abd7bc547ef5ba4d63e2bc Mon Sep 17 00:00:00 2001 From: sanjaysargam Date: Mon, 21 Sep 2026 02:24:03 +0530 Subject: [PATCH 22/43] feat(deckpicker): improve clock error message --- .../src/main/java/com/ichi2/anki/Sync.kt | 12 ++ .../com/ichi2/anki/dialogs/SyncErrorDialog.kt | 37 ++++ AnkiDroid/src/main/res/values/03-dialogs.xml | 1 + .../anki/SyncClockErrorScreenshotTest.kt | 26 +++ .../src/test/java/com/ichi2/anki/SyncTest.kt | 159 ++++++++++++++++++ 5 files changed, 235 insertions(+) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/SyncClockErrorScreenshotTest.kt create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt index 1348196e5eb5..d4b17d20a546 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt @@ -104,6 +104,10 @@ fun DeckPicker.handleNewSync( // auth failed; log out updateLogin("", "") throw exc + } catch (exc: BackendSyncException) { + if (!exc.isClockOffException()) throw exc + showSyncErrorDialog(SyncErrorDialog.Type.DIALOG_SYNC_CLOCK_OFF, exc.localizedMessage) + return@launchCatchingTask } withCol { notetypes.clearCache() } notifySubscribersAllValuesChanged(deckPicker) @@ -116,6 +120,14 @@ fun DeckPicker.handleNewSync( } } +/** The backend currently exposes ClockIncorrect as a localized sync error message. */ +private fun BackendSyncException.isClockOffException(): Boolean = + try { + message == TR.syncClockOff() + } catch (_: Throwable) { + false + } + fun updateLogin( username: String, hkey: String, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt index dd03a819a4d0..a639da0e4afb 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt @@ -4,17 +4,22 @@ package com.ichi2.anki.dialogs import android.app.Dialog +import android.content.ActivityNotFoundException +import android.content.Intent import android.os.Bundle import android.os.Message +import android.provider.Settings import androidx.annotation.CheckResult import androidx.appcompat.app.AlertDialog import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.ConflictResolution import com.ichi2.anki.R +import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_CONNECTION_ERROR import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_MEDIA_SYNC_ERROR import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_SYNC_BASIC_CHECK_ERROR +import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_SYNC_CLOCK_OFF import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_LOCAL import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_REMOTE import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_SYNC_CONFLICT_RESOLUTION @@ -26,7 +31,11 @@ import com.ichi2.anki.dialogs.SyncErrorDialog.Type.DIALOG_USER_NOT_LOGGED_IN_SYN import com.ichi2.anki.ui.internationalization.sentenceCase import com.ichi2.anki.utils.ext.dismissAllDialogFragments import com.ichi2.anki.utils.openUrl +import com.ichi2.utils.positiveButton +import com.ichi2.utils.setupEnterKeyHandler import com.ichi2.utils.titleWithHelpIcon +import timber.log.Timber +import java.text.DateFormat class SyncErrorDialog : AsyncDialogFragment() { interface SyncErrorDialogListener { @@ -162,6 +171,26 @@ class SyncErrorDialog : AsyncDialogFragment() { }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } .create() } + DIALOG_SYNC_CLOCK_OFF -> { + dialog + .setPositiveButton(R.string.open_settings, null) + .setNegativeButton(R.string.dialog_cancel, null) + .create() + .apply { + // Override the default handler so the dialog remains open if no date-settings activity exists. + setOnShowListener { + positiveButton.setOnClickListener { + try { + startActivity(Intent(Settings.ACTION_DATE_SETTINGS)) + this@SyncErrorDialog.dismiss() + } catch (e: ActivityNotFoundException) { + Timber.w(e, "No app can show date and time settings") + } + } + } + setupEnterKeyHandler() + } + } } } @@ -182,6 +211,7 @@ class SyncErrorDialog : AsyncDialogFragment() { DIALOG_SYNC_CORRUPT_COLLECTION, DIALOG_SYNC_BASIC_CHECK_ERROR, -> res().getString(R.string.sync_error) + DIALOG_SYNC_CLOCK_OFF -> res().getString(R.string.vague_error) } /** @@ -218,6 +248,12 @@ class SyncErrorDialog : AsyncDialogFragment() { val dialogMessage = res().getString(R.string.sync_corrupt_database, repairUrl) joinSyncMessages(dialogMessage, syncMessage) } + DIALOG_SYNC_CLOCK_OFF -> + res().getString( + R.string.sync_clock_off_with_current_time, + requireArguments().getString(DIALOG_MESSAGE_KEY).orEmpty(), + DateFormat.getDateTimeInstance().format(TimeManager.time.currentDate), + ) else -> requireArguments().getString(DIALOG_MESSAGE_KEY) } @@ -281,6 +317,7 @@ class SyncErrorDialog : AsyncDialogFragment() { DIALOG_MEDIA_SYNC_ERROR(8), DIALOG_SYNC_CORRUPT_COLLECTION(9), DIALOG_SYNC_BASIC_CHECK_ERROR(10), + DIALOG_SYNC_CLOCK_OFF(11), ; companion object { diff --git a/AnkiDroid/src/main/res/values/03-dialogs.xml b/AnkiDroid/src/main/res/values/03-dialogs.xml index 9313f6d387ff..d028635b97ae 100644 --- a/AnkiDroid/src/main/res/values/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values/03-dialogs.xml @@ -69,6 +69,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/SyncClockErrorScreenshotTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/SyncClockErrorScreenshotTest.kt new file mode 100644 index 000000000000..306732ee1077 --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/SyncClockErrorScreenshotTest.kt @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki + +import com.ichi2.anki.common.time.MockTime +import com.ichi2.anki.common.time.TimeManager +import com.ichi2.anki.dialogs.SyncErrorDialog +import org.junit.Test + +/** + * `./gradlew :AnkiDroid:verifyRoborazziPlayDebug -Pscreenshot --tests "com.ichi2.anki.SyncClockErrorScreenshotTest"` + */ +class SyncClockErrorScreenshotTest : ScreenshotTest() { + @Test + fun `clock error dialog`() = + withDeckPicker(deckCount = 0) { deckPicker -> + TimeManager.resetWith(MockTime(1_600_000_000_000)) + deckPicker.showSyncErrorDialog( + SyncErrorDialog.Type.DIALOG_SYNC_CLOCK_OFF, + CollectionManager.TR.syncClockOff(), + ) + advanceRobolectricLooper() + + captureScreen("clock_error_dialog") + } +} diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt new file mode 100644 index 000000000000..978cd4f8854d --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki + +import android.content.DialogInterface +import android.provider.Settings +import android.view.View +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.test.ext.junit.runners.AndroidJUnit4 +import anki.backend.backendError +import anki.sync.SyncAuth +import anki.sync.SyncCollectionResponse +import com.ichi2.anki.common.time.MockTime +import com.ichi2.anki.common.time.TimeManager +import com.ichi2.anki.dialogs.SyncErrorDialog +import com.ichi2.anki.settings.Prefs +import kotlinx.coroutines.runBlocking +import net.ankiweb.rsdroid.Backend +import net.ankiweb.rsdroid.BackendFactory +import net.ankiweb.rsdroid.exceptions.BackendSyncException +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Shadows.shadowOf +import org.robolectric.shadows.ShadowDialog +import java.text.DateFormat +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +@RunWith(AndroidJUnit4::class) +class SyncTest : RobolectricTest() { + private lateinit var syncFailure: BackendSyncException + + @Before + fun setUpFailingSync() { + runBlocking { CollectionManager.discardBackend() } + BackendFactory.setOverride { + object : Backend() { + override fun syncCollection( + auth: SyncAuth, + syncMedia: Boolean, + ): SyncCollectionResponse = throw syncFailure + } + } + } + + @After + fun resetBackendFactory() { + BackendFactory.setOverride(null) + } + + @Test + fun `clock errors use sync recovery dialog and open date settings`() = + deckPicker { + TimeManager.resetWith(MockTime(1_600_000_000_000)) + val currentTime = DateFormat.getDateTimeInstance().format(TimeManager.time.currentDate) + val exception = BackendSyncException(backendError { message = CollectionManager.TR.syncClockOff() }) + + val dialog = failSync(exception) + + assertTrue(supportFragmentManager.fragments.any { it is SyncErrorDialog }) + assertEquals( + getString(R.string.sync_clock_off_with_current_time, exception.localizedMessage, currentTime), + dialog.findViewById(android.R.id.message)?.text?.toString(), + ) + assertEquals(getString(R.string.dialog_cancel), dialog.getButton(DialogInterface.BUTTON_NEGATIVE).text) + assertEquals(getString(R.string.open_settings), dialog.getButton(DialogInterface.BUTTON_POSITIVE).text) + assertEquals(View.GONE, dialog.getButton(DialogInterface.BUTTON_NEUTRAL).visibility) + assertEquals("test", Prefs.hkey) + + dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick() + advanceRobolectricLooper() + + val intent = assertNotNull(shadowOf(this).nextStartedActivity) + assertEquals(Settings.ACTION_DATE_SETTINGS, intent.action) + assertFalse(dialog.isShowing) + } + + @Test + fun `cancelling a clock error does not open settings`() = + deckPicker { + val dialog = failSync(BackendSyncException(backendError { message = CollectionManager.TR.syncClockOff() })) + + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick() + advanceRobolectricLooper() + + assertFalse(dialog.isShowing) + assertNull(shadowOf(this).nextStartedActivity) + } + + @Test + fun `clock error remains visible when date settings are unavailable`() = + deckPicker { + val dialog = failSync(BackendSyncException(backendError { message = CollectionManager.TR.syncClockOff() })) + val applicationShadow = shadowOf(application) + applicationShadow.checkActivities(true) + try { + dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick() + advanceRobolectricLooper() + + assertTrue(dialog.isShowing) + assertNull(shadowOf(this).nextStartedActivity) + + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick() + advanceRobolectricLooper() + assertFalse(dialog.isShowing) + } finally { + applicationShadow.checkActivities(false) + } + } + + @Test + fun `other sync errors keep their message and OK button`() = + deckPicker { + val message = "other sync error" + val dialog = failSync(BackendSyncException(backendError { this.message = message })) + + assertEquals(message, dialog.findViewById(android.R.id.message)?.text?.toString()) + assertEquals(getString(R.string.dialog_ok), dialog.getButton(DialogInterface.BUTTON_POSITIVE).text) + assertEquals(View.GONE, dialog.getButton(DialogInterface.BUTTON_NEGATIVE).visibility) + assertEquals(View.GONE, dialog.getButton(DialogInterface.BUTTON_NEUTRAL).visibility) + assertEquals("test", Prefs.hkey) + + dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick() + advanceRobolectricLooper() + + assertFalse(dialog.isShowing) + assertNull(shadowOf(this).nextStartedActivity) + } + + @Test + fun `authentication errors still log out`() = + deckPicker { + Prefs.username = "test user" + val message = "authentication failed" + val dialog = failSync(BackendSyncException.BackendSyncAuthFailedException(backendError { this.message = message })) + + assertEquals("", Prefs.hkey) + assertEquals("", Prefs.username) + assertEquals(message, dialog.findViewById(android.R.id.message)?.text?.toString()) + } + + private fun DeckPicker.failSync(exception: BackendSyncException): AlertDialog { + syncFailure = exception + Prefs.hkey = "test" + Prefs.lastSyncTime = 0 + throwOnShowError = false + + handleNewSync(conflict = null, syncMedia = false) + advanceRobolectricLooperUntil { Prefs.lastSyncTime != 0L } + + return assertNotNull(ShadowDialog.getLatestDialog() as? AlertDialog) + } +} From 999fcd6db9dfda646dd1eb65402019621ce48239 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 20:03:12 +0100 Subject: [PATCH 23/43] test(reviewer): check each shake opens a dialog Related to 22126 Assisted-by: GPT-6 --- .../ichi2/anki/ui/windows/reviewer/ReviewerShakeTest.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerShakeTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerShakeTest.kt index 3ea33518cfc2..73c588e455ab 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerShakeTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerShakeTest.kt @@ -66,9 +66,13 @@ class ReviewerShakeTest : RobolectricTest() { } scenario.recreate() - advanceRobolectricLooper() + scenario.withSetDueDate { + assertEquals(listOf(cardId), cardIds) + // The next shake must open a new dialog, not find the restored one. + dismissNow() + } + assertEquals(1, sensors.listeners.size) } - assertEquals(1, sensors.listeners.size) } assertEquals(0, sensors.listeners.size) } From c010ecfc6316a5a952512e6df2d9d9cabb674fe2 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 23:07:50 +0100 Subject: [PATCH 24/43] fix(image-occlusion): send IDs as strings NoteIds and NoteTypeIds need to be strings, because JS needs them as BigInteger. This was previously handled by the code in 26.05, now we need to handle this for 26.09. Backwards compatible as `BigInt` accepts a `BigInt` https://github.com/ankitects/anki/blame/643187a050abc36525dedd6f714c6a1260ef54a7/ts/routes/image-occlusion/lib.ts#L17 Part of 21926 Assisted-by: GPT-6 --- .../java/com/ichi2/anki/pages/ImageOcclusion.kt | 14 +++++++++++++- .../pages/viewmodel/ImageOcclusionViewModel.kt | 5 +++-- .../pages/viewmodel/ImageOcclusionViewModelTest.kt | 12 ++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt index 1eed8665ac43..f4d2a7b204ea 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt @@ -96,7 +96,19 @@ class ImageOcclusion : PageFragment(R.layout.page_image_occlusion) { ) { super.onPageFinished(view, url) viewModel.args.toImageOcclusionMode().let { options -> - view?.evaluateJavascript("globalThis.anki.imageOcclusion.mode = $options") { + view?.evaluateJavascript( + """ + (() => { + const mode = $options; + if (mode.kind === "add") { + mode.notetypeId = BigInt(mode.notetypeId); + } else { + mode.noteId = BigInt(mode.noteId); + } + globalThis.anki.imageOcclusion.mode = mode; + })(); + """.trimIndent(), + ) { super.onPageFinished(view, url) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModel.kt index 0974d95d615e..0219fa167b7a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModel.kt @@ -48,6 +48,7 @@ sealed class ImageOcclusionArgs : Parcelable { /** * A [JSONObject] containing options for loading the [image occlusion page][ImageOcclusion]. * This includes the type of operation ("add" or "edit"), and relevant IDs and paths. + * IDs are decimal strings so JavaScript can convert them to bigint without losing precision. * * See 'IOMode' in https://github.com/ankitects/anki/blob/main/ts/routes/image-occlusion/lib.ts */ @@ -57,12 +58,12 @@ sealed class ImageOcclusionArgs : Parcelable { JSONObject().also { it.put("kind", "add") it.put("imagePath", this.imagePath) - it.put("notetypeId", this.noteTypeId) + it.put("notetypeId", this.noteTypeId.toString()) } is Edit -> JSONObject().also { it.put("kind", "edit") - it.put("noteId", this.noteId) + it.put("noteId", this.noteId.toString()) } } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModelTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModelTest.kt index 26dbda87dc47..c4a7960974a3 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModelTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/pages/viewmodel/ImageOcclusionViewModelTest.kt @@ -28,6 +28,7 @@ import org.junit.jupiter.api.assertNull import org.junit.runner.RunWith import org.robolectric.annotation.Config import kotlin.properties.Delegates.notNull +import kotlin.test.assertEquals import kotlin.test.assertNotNull /** @@ -40,6 +41,13 @@ import kotlin.test.assertNotNull class ImageOcclusionViewModelTest : JvmTest() { private var deckIdToSwitchTo by notNull() + @Test + fun `IDs beyond JavaScript number precision are serialized as decimal strings`() { + val id = 9007199254740993L + assertEquals(id.toString(), ImageOcclusionArgs.Edit(id).toImageOcclusionMode().get("noteId")) + assertEquals(id.toString(), ImageOcclusionArgs.Add("/image.png", id, 1).toImageOcclusionMode().get("notetypeId")) + } + @Before override fun setUp() { super.setUp() @@ -140,7 +148,7 @@ class ImageOcclusionViewModelTest : JvmTest() { { "kind": "add", "imagePath": "/", - "notetypeId": 12 + "notetypeId": "12" } """ @@ -154,7 +162,7 @@ class ImageOcclusionViewModelTest : JvmTest() { val expected = """ { "kind": "edit", - "noteId": 12 + "noteId": "12" } """ From 62cf58242c6922a88301e27604c493633d3e8b97 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 23:08:35 +0100 Subject: [PATCH 25/43] fix(image-occlusion): two calls to super.onPageFinished() Part of 21926 Assisted-by: GPT-6 --- .../src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt index f4d2a7b204ea..19febacae9d0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt @@ -108,9 +108,8 @@ class ImageOcclusion : PageFragment(R.layout.page_image_occlusion) { globalThis.anki.imageOcclusion.mode = mode; })(); """.trimIndent(), - ) { - super.onPageFinished(view, url) - } + null, + ) } } } From c7e4518ca676eb299e69d820c5dfc0616b52ecc1 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 20 Sep 2026 20:29:06 +0100 Subject: [PATCH 26/43] test(note-editor): test share & deck defaults Prep for 20497 Assisted-by: GPT-6 --- .../ichi2/anki/NoteEditorDeckSelectionTest.kt | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt new file mode 100644 index 000000000000..a94e7feb0e5f --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki + +import android.content.Intent +import android.os.Bundle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.common.destinations.NoteEditorDestination +import com.ichi2.anki.common.destinations.toIntent +import com.ichi2.anki.common.utils.ext.AddingDefaultsMode +import com.ichi2.anki.common.utils.ext.addingDefaultsMode +import com.ichi2.anki.libanki.DeckId +import com.ichi2.anki.model.SelectableDeck +import com.ichi2.anki.noteeditor.getNoteEditorFragment +import com.ichi2.anki.noteeditor.openNoteEditorWithArgs +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@RunWith(AndroidJUnit4::class) +class NoteEditorDeckSelectionTest : RobolectricTest() { + private fun openAdd(deckId: DeckId? = null) = + startRegularActivity(NoteEditorDestination.AddNote(deckId).toIntent()).getNoteEditorFragment() + + private fun useAddingDefaults(mode: AddingDefaultsMode) { + ensureCollectionLoadIsSynchronous() + col.config.addingDefaultsMode = mode + } + + private fun share(text: String) = + openNoteEditorWithArgs( + Bundle().apply { putString(Intent.EXTRA_TEXT, text) }, + Intent.ACTION_SEND, + ) + + private fun NoteEditorFragment.select(did: DeckId) { + onDeckSelected(SelectableDeck.Deck(did, col.decks.name(did))) + } + + @Test + fun `successive shares use the study deck in current-deck mode`() = + runTest { + useAddingDefaults(AddingDefaultsMode.USE_CURRENT_DECK) + val a = addDeck("Study A", setAsSelected = true) + val b = addDeck("Add B") + val first = share("first share") + val activity = first.requireActivity() + first.select(b) + first.saveNote() + advanceRobolectricLooper() + assertTrue(activity.isFinishing) + assertEquals(listOf(b), col.findCards("").map { col.getCard(it).did }) + assertEquals(a, col.decks.selected()) + assertEquals(a, share("second share").deckId) + } + + @Test + fun `successive shares remember the destination in note-type mode`() = + runTest { + useAddingDefaults(AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) + val a = addDeck("Study A", setAsSelected = true) + val b = addDeck("Add B") + val first = share("first share") + val ntid = first.editorNote!!.noteTypeId + val activity = first.requireActivity() + first.select(b) + first.saveNote() + advanceRobolectricLooper() + assertTrue(activity.isFinishing) + assertEquals(listOf(b), col.findCards("").map { col.getCard(it).did }) + assertEquals(a, col.decks.selected()) + val second = share("second share") + assertEquals(ntid, second.editorNote!!.noteTypeId) + assertEquals(b, second.deckId) + } + + @Test + fun `explicit Default deck takes precedence over the remembered deck`() = + runTest { + useAddingDefaults(AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) + val b = addDeck("Remembered B", setAsSelected = true) + col.addNote(col.newNote(col.notetypes.basic).apply { fields[0] = "saved" }, b) + assertEquals(b, col.defaultsForAdding().deckId) + assertEquals(1L, openAdd(1L).deckId) + } + + // Preserve Android's existing launch validation. Anki's legacy desktop chooser instead uses + // Default for an invalid explicit destination; this is not an upstream parity assertion. + @Test + fun `invalid explicit destination falls back to the remembered deck`() = + runTest { + useAddingDefaults(AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) + addDeck("Study", setAsSelected = true) + val rememberedDeck = addDeck("Remembered") + col.addNote(col.newNote(col.notetypes.basic).apply { fields[0] = "existing" }, rememberedDeck) + val filteredDeck = addDynamicDeck("Filtered") + val missingDeck = addDeck("Deleted") + col.decks.remove(listOf(missingDeck)) + + for (invalidDeck in listOf(filteredDeck, missingDeck)) { + val editor = openAdd(invalidDeck) + assertEquals(rememberedDeck, editor.deckId) + editor.requireActivity().finish() + } + } +} From da35f252f725f67e8bb521bce9ae88d114788348 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 20 Sep 2026 20:30:43 +0100 Subject: [PATCH 27/43] fix(note-editor): retain deck if note type has associated deck matching upstream functionality: https://github.com/ankitects/anki/blob/754ce3a25f608010c0249e074e5d7fe95bda035f/qt/aqt/addcards_legacy.py#L165-L171 Part of 20497 Assisted-by: GPT-6 --- .../java/com/ichi2/anki/NoteEditorFragment.kt | 6 +- .../ichi2/anki/NoteEditorDeckSelectionTest.kt | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt index 1063dae15dba..b37b38c8f4d0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt @@ -2609,10 +2609,8 @@ class NoteEditorFragment : currentDeck.put("mid", newId) getColUnsafe.decks.save(currentDeck) - // Update deck - if (getColUnsafe.config.addingDefaultsMode == AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) { - deckId = getColUnsafe.defaultsForAdding().deckId - } + // Preserves the editor's deck when the new note type has no remembered destination. + getColUnsafe.defaultDeckForNoteType(newId)?.let { deckId = it } refreshNoteData(FieldChangeType.changeFieldCount(shouldReplaceNewlines())) setDuplicateFieldStyles() diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt index a94e7feb0e5f..dc45b4c46ddb 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorDeckSelectionTest.kt @@ -16,6 +16,7 @@ import com.ichi2.anki.noteeditor.openNoteEditorWithArgs import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertEquals +import kotlin.test.assertNull import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) @@ -104,4 +105,88 @@ class NoteEditorDeckSelectionTest : RobolectricTest() { editor.requireActivity().finish() } } + + @Test + fun `switching to a note type without history preserves the chosen deck`() = + runTest { + useAddingDefaults(AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) + addDeck("Study A", setAsSelected = true) + val b = addDeck("Add B") + val reversed = col.notetypes.basicAndReversed + assertNull(col.defaultDeckForNoteType(reversed.id)) + val editor = openAdd() + editor.select(b) + editor.setCurrentlySelectedNoteType(reversed.id) + advanceRobolectricLooper() + assertEquals(reversed.id, editor.editorNote!!.noteTypeId) + assertEquals(b, editor.deckId) + } + + @Test + fun `deleted remembered deck does not replace the editor destination on a note-type change`() = + runTest { + useAddingDefaults(AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) + addDeck("Study", setAsSelected = true) + val destination = addDeck("Destination") + val deletedDeck = addDeck("Deleted") + val reversed = col.notetypes.basicAndReversed + col.addNote(col.newNote(reversed).apply { fields[0] = "existing" }, deletedDeck) + col.decks.remove(listOf(deletedDeck)) + col.notetypes.setCurrent(col.notetypes.basic) + assertNull(col.defaultDeckForNoteType(reversed.id)) + + val editor = openAdd() + editor.select(destination) + editor.setCurrentlySelectedNoteType(reversed.id) + advanceRobolectricLooper() + + assertEquals(reversed.id, editor.editorNote!!.noteTypeId) + assertEquals(destination, editor.deckId) + } + + @Test + fun `filtered remembered deck does not replace the editor destination on a note-type change`() = + runTest { + useAddingDefaults(AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) + val filteredDeck = addDynamicDeck("Filtered") + val studyDeck = addDeck("Study", setAsSelected = true) + val destination = addDeck("Destination") + val reversed = col.notetypes.basicAndReversed + // addNote records the requested destination even when card generation uses a fallback. + col.addNote(col.newNote(reversed).apply { fields[0] = "existing" }, filteredDeck) + col.notetypes.setCurrent(col.notetypes.basic) + assertNull(col.defaultDeckForNoteType(reversed.id)) + + val editor = openAdd() + editor.select(destination) + editor.setCurrentlySelectedNoteType(reversed.id) + advanceRobolectricLooper() + + assertEquals(reversed.id, editor.editorNote!!.noteTypeId) + assertEquals(destination, editor.deckId) + assertEquals(studyDeck, col.decks.selected()) + } + + @Test + fun `switching note types in current-deck mode preserves the chosen deck despite remembered history`() = + runTest { + useAddingDefaults(AddingDefaultsMode.DECIDE_BY_NOTE_TYPE) + val studyDeck = addDeck("Study", setAsSelected = true) + val destination = addDeck("Destination") + val rememberedDeck = addDeck("Remembered") + val reversed = col.notetypes.basicAndReversed + col.addNote(col.newNote(reversed).apply { fields[0] = "existing" }, rememberedDeck) + assertEquals(rememberedDeck, col.defaultDeckForNoteType(reversed.id)) + col.config.addingDefaultsMode = AddingDefaultsMode.USE_CURRENT_DECK + col.notetypes.setCurrent(col.notetypes.basic) + + val editor = openAdd() + editor.select(destination) + editor.setCurrentlySelectedNoteType(reversed.id) + advanceRobolectricLooper() + + assertEquals(reversed.id, editor.editorNote!!.noteTypeId) + assertEquals(destination, editor.deckId) + assertEquals(studyDeck, col.decks.selected()) + } } From 92449399742c5b14c2fc24aeb71c0b80fec69417 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Sat, 26 Sep 2026 11:56:37 +0530 Subject: [PATCH 28/43] fix(change-notetype): edge to edge Assisted-by: Opus 5.5 --- .../anki/dialogs/ChangeNoteTypeDialog.kt | 19 +++++ .../dialogs/ChangeNoteTypeDialogInsetsTest.kt | 74 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogInsetsTest.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt index 798ce00bd53e..a6feca6881d1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt @@ -6,6 +6,7 @@ package com.ichi2.anki.dialogs import android.content.Context import android.content.res.ColorStateList import android.graphics.Color +import android.os.Build import android.os.Bundle import android.text.SpannableStringBuilder import android.view.LayoutInflater @@ -21,6 +22,10 @@ import androidx.annotation.CheckResult import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting import androidx.core.text.BidiFormatter +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat.Type.displayCutout +import androidx.core.view.WindowInsetsCompat.Type.systemBars import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment @@ -59,6 +64,7 @@ import com.ichi2.anki.ui.internationalization.sentenceCase import com.ichi2.anki.utils.InitStatus import com.ichi2.anki.utils.ext.launchCollectionInLifecycleScope import com.ichi2.anki.withProgress +import com.ichi2.themes.Themes import com.ichi2.utils.LanguageUtil import com.ichi2.utils.boldList import kotlinx.coroutines.flow.filterNotNull @@ -99,6 +105,7 @@ class ChangeNoteTypeDialog : AnalyticsDialogFragment(R.layout.dialog_change_note ) { super.onViewCreated(view, savedInstanceState) val binding = DialogChangeNoteTypeBinding.bind(view) + setupEdgeToEdge(binding) binding.toolbar.title = TR.sentenceCase.changeNoteType binding.toolbar.setNavigationOnClickListener { dismiss() } @@ -131,6 +138,18 @@ class ChangeNoteTypeDialog : AnalyticsDialogFragment(R.layout.dialog_change_note } } + private fun setupEdgeToEdge(binding: DialogChangeNoteTypeBinding) { + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets -> + val bars = insets.getInsets(systemBars() or displayCutout()) + view.setPadding(bars.left, bars.top, bars.right, bars.bottom) + insets + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + val window = dialog?.window ?: return + WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = !Themes.isNightTheme + } + } + private fun setupFlows() { launchCatchingTask { viewModel.closeDialogFlow.filterNotNull().collect { diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogInsetsTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogInsetsTest.kt new file mode 100644 index 000000000000..56b23328cff0 --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogInsetsTest.kt @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.dialogs + +import android.os.Bundle +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.fragment.app.testing.launchFragment +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.RobolectricTest +import com.ichi2.anki.dialogs.ChangeNoteTypeDialog.Companion.ARG_NOTE_IDS +import com.ichi2.anki.settings.enums.DayTheme +import com.ichi2.anki.settings.enums.NightTheme +import com.ichi2.anki.settings.enums.Theme +import com.ichi2.testutils.windowInsetsOf +import com.ichi2.themes.Themes +import com.ichi2.utils.dp +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ChangeNoteTypeDialogInsetsTest : RobolectricTest() { + @Test + fun `dialog clears system bars and cutouts`() = + withDialog { dialog -> + val insets = + with(targetContext) { + windowInsetsOf(navBarBottom = 48.dp, navBarRight = 48.dp, cutoutLeft = 32.dp) + } + ViewCompat.dispatchApplyWindowInsets(dialog.requireDialog().window!!.decorView, insets) + + val root = dialog.requireView() + assertThat(root.paddingLeft, equalTo(32.dp.toPx(targetContext))) + assertThat(root.paddingTop, equalTo(24.dp.toPx(targetContext))) + assertThat(root.paddingRight, equalTo(48.dp.toPx(targetContext))) + assertThat(root.paddingBottom, equalTo(48.dp.toPx(targetContext))) + } + + @Test + fun `status bar icons are dark over a light theme`() = + withDialog(DayTheme.LIGHT) { dialog -> + assertThat(dialog.hasLightStatusBars, equalTo(true)) + } + + @Test + fun `status bar icons stay light over a dark theme`() = + withDialog(NightTheme.DARK) { dialog -> + assertThat(dialog.hasLightStatusBars, equalTo(false)) + } + + private val ChangeNoteTypeDialog.hasLightStatusBars: Boolean + get() { + val window = requireDialog().window!! + return WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars + } + + private fun withDialog( + theme: Theme = DayTheme.LIGHT, + block: (ChangeNoteTypeDialog) -> Unit, + ) { + val previousTheme = Themes.currentTheme + Themes.currentTheme = theme + try { + launchFragment( + fragmentArgs = Bundle().apply { putLongArray(ARG_NOTE_IDS, longArrayOf(addBasicNote().id)) }, + themeResId = theme.styleResId, + ).use { scenario -> scenario.onFragment(block) } + } finally { + Themes.currentTheme = previousTheme + } + } +} From 138ddbf2f2fde0c533a4cb6efe8b61e4158d9bba Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Mon, 28 Sep 2026 01:46:08 +0530 Subject: [PATCH 29/43] test(change-notetype): screenshot tests Assisted-by: Opus 5.5 --- .../ChangeNoteTypeDialogScreenshotTest.kt | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogScreenshotTest.kt diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogScreenshotTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogScreenshotTest.kt new file mode 100644 index 000000000000..04188c335e58 --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialogScreenshotTest.kt @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.dialogs + +import androidx.core.view.WindowInsetsCompat +import com.ichi2.anki.ScreenshotTest +import com.ichi2.anki.withCardBrowser +import com.ichi2.testutils.simulateSystemBars +import com.ichi2.testutils.windowInsetsOf +import com.ichi2.utils.dp +import org.junit.Test +import org.robolectric.RuntimeEnvironment + +class ChangeNoteTypeDialogScreenshotTest : ScreenshotTest() { + @Test + fun portrait() = + captureChangeNoteType( + "portrait", + with(targetContext) { windowInsetsOf(navBarBottom = 48.dp) }, + ) + + @Test + fun gestureNavigation() = + captureChangeNoteType( + "gesture_navigation", + with(targetContext) { windowInsetsOf(navBarBottom = 24.dp) }, + ) + + @Test + fun portraitCutout() = + captureChangeNoteType( + "portrait_cutout", + with(targetContext) { windowInsetsOf(navBarBottom = 48.dp, cutoutTop = 40.dp) }, + ) + + @Test + fun landscape() { + RuntimeEnvironment.setQualifiers("+land") + captureChangeNoteType( + "landscape", + with(targetContext) { windowInsetsOf(navBarRight = 48.dp, cutoutLeft = 32.dp) }, + ) + } + + private fun captureChangeNoteType( + name: String, + insets: WindowInsetsCompat, + ) = withCardBrowser(noteCount = 1) { browser -> + val dialog = ChangeNoteTypeDialog.newInstance(col.findNotes("")) + dialog.showNow(browser.supportFragmentManager, "change_note_type") + advanceRobolectricLooper() + try { + dialog.requireDialog().window!!.simulateSystemBars(insets) + captureScreen(name) + } finally { + dialog.dismissNow() + } + } +} From 6b6e1f5953fff02370069b4a98181b642e9e1bcf Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Mon, 28 Sep 2026 01:46:43 +0530 Subject: [PATCH 30/43] fix(insert-field): edge to edge Fixes 22095 Assisted-by: Opus 5.5 --- .../ichi2/anki/dialogs/InsertFieldDialog.kt | 19 ++++ .../dialogs/InsertFieldDialogInsetsTest.kt | 89 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogInsetsTest.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt index 8f4ecb06c6d4..3b520af9e2e7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt @@ -4,6 +4,7 @@ package com.ichi2.anki.dialogs import android.content.Context +import android.os.Build import android.os.Bundle import android.text.Spanned import android.view.LayoutInflater @@ -14,6 +15,10 @@ import androidx.annotation.CheckResult import androidx.annotation.StringRes import androidx.core.text.HtmlCompat import androidx.core.text.parseAsHtml +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat.Type.displayCutout +import androidx.core.view.WindowInsetsCompat.Type.systemBars import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -37,6 +42,7 @@ import com.ichi2.anki.launchCatchingTask import com.ichi2.anki.model.SpecialField import com.ichi2.anki.model.SpecialFields import com.ichi2.anki.utils.ext.requireString +import com.ichi2.themes.Themes import dev.androidbroadcast.vbpd.viewBinding import org.jetbrains.annotations.VisibleForTesting @@ -63,6 +69,7 @@ class InsertFieldDialog : DialogFragment(R.layout.dialog_insert_field) { ) { super.onViewCreated(view, savedInstanceState) val binding = DialogInsertFieldBinding.bind(view) + setupEdgeToEdge(binding) binding.toolbar.title = getString(R.string.card_template_editor_select_field) binding.toolbar.setNavigationOnClickListener { dismiss() } @@ -106,6 +113,18 @@ class InsertFieldDialog : DialogFragment(R.layout.dialog_insert_field) { } } + private fun setupEdgeToEdge(binding: DialogInsertFieldBinding) { + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets -> + val bars = insets.getInsets(systemBars() or displayCutout()) + view.setPadding(bars.left, bars.top, bars.right, bars.bottom) + insets + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + val window = dialog?.window ?: return + WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = !Themes.isNightTheme + } + } + companion object { /** * A key in the extras of the Fragment Result diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogInsetsTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogInsetsTest.kt new file mode 100644 index 000000000000..3837f2959d5e --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogInsetsTest.kt @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.dialogs + +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.fragment.app.testing.launchFragment +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.EmptyApplicationCategory +import com.ichi2.anki.RobolectricTest +import com.ichi2.anki.cardviewer.SingleCardSide +import com.ichi2.anki.settings.enums.DayTheme +import com.ichi2.anki.settings.enums.NightTheme +import com.ichi2.anki.settings.enums.Theme +import com.ichi2.testutils.EmptyApplication +import com.ichi2.testutils.windowInsetsOf +import com.ichi2.themes.Themes +import com.ichi2.utils.dp +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Test +import org.junit.experimental.categories.Category +import org.junit.runner.RunWith +import org.robolectric.annotation.Config + +@RunWith(AndroidJUnit4::class) +@Config(application = EmptyApplication::class) +@Category(EmptyApplicationCategory::class) +class InsertFieldDialogInsetsTest : RobolectricTest() { + @Test + fun `dialog clears system bars and cutouts`() = + withDialog { dialog -> + val insets = + with(targetContext) { + windowInsetsOf(navBarBottom = 48.dp, navBarRight = 48.dp, cutoutLeft = 32.dp) + } + ViewCompat.dispatchApplyWindowInsets(dialog.requireDialog().window!!.decorView, insets) + + val root = dialog.requireView() + assertThat(root.paddingLeft, equalTo(32.dp.toPx(targetContext))) + assertThat(root.paddingTop, equalTo(24.dp.toPx(targetContext))) + assertThat(root.paddingRight, equalTo(48.dp.toPx(targetContext))) + assertThat(root.paddingBottom, equalTo(48.dp.toPx(targetContext))) + } + + @Test + fun `status bar icons are dark over a light theme`() = + withDialog(DayTheme.LIGHT) { dialog -> + assertThat(dialog.hasLightStatusBars, equalTo(true)) + } + + @Test + fun `status bar icons stay light over a dark theme`() = + withDialog(NightTheme.DARK) { dialog -> + assertThat(dialog.hasLightStatusBars, equalTo(false)) + } + + private val InsertFieldDialog.hasLightStatusBars: Boolean + get() { + val window = requireDialog().window!! + return WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars + } + + private fun withDialog( + theme: Theme = DayTheme.LIGHT, + block: (InsertFieldDialog) -> Unit, + ) { + val metadata = + InsertFieldMetadata( + side = SingleCardSide.FRONT, + cardTemplateName = "Card 1", + noteTypeName = "Basic", + tags = null, + flag = null, + cardId = null, + deck = null, + ) + val previousTheme = Themes.currentTheme + Themes.currentTheme = theme + try { + launchFragment( + fragmentArgs = InsertFieldDialog.newInstance(listOf("Front", "Back"), metadata, "insert_field").requireArguments(), + themeResId = theme.styleResId, + ).use { scenario -> scenario.onFragment(block) } + } finally { + Themes.currentTheme = previousTheme + } + } +} From 79d05d0618d863f6fb701a6c1e65ee4dc497ed57 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Mon, 28 Sep 2026 01:46:45 +0530 Subject: [PATCH 31/43] test(insert-field): screenshot tests Assisted-by: Opus 5.5 --- .../InsertFieldDialogScreenshotTest.kt | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogScreenshotTest.kt diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogScreenshotTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogScreenshotTest.kt new file mode 100644 index 000000000000..2dec48df1f02 --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/InsertFieldDialogScreenshotTest.kt @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki.dialogs + +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.viewpager2.widget.ViewPager2 +import com.ichi2.anki.R +import com.ichi2.anki.ScreenshotTest +import com.ichi2.anki.dialogs.InsertFieldDialog.SelectSpecialFieldFragment +import com.ichi2.anki.dialogs.InsertFieldDialogViewModel.Tab +import com.ichi2.anki.withCardTemplateEditor +import com.ichi2.testutils.scrollToEnd +import com.ichi2.testutils.simulateSystemBars +import com.ichi2.testutils.windowInsetsOf +import com.ichi2.utils.dp +import org.junit.Test +import org.robolectric.RuntimeEnvironment + +class InsertFieldDialogScreenshotTest : ScreenshotTest() { + @Test + fun portrait() = + captureInsertField( + "portrait", + with(targetContext) { windowInsetsOf(navBarBottom = 48.dp) }, + ) + + @Test + fun gestureNavigation() = + captureInsertField( + "gesture_navigation", + with(targetContext) { windowInsetsOf(navBarBottom = 24.dp) }, + ) + + @Test + fun portraitCutout() = + captureInsertField( + "portrait_cutout", + with(targetContext) { windowInsetsOf(navBarBottom = 48.dp, cutoutTop = 40.dp) }, + ) + + @Test + fun landscape() { + RuntimeEnvironment.setQualifiers("+land") + captureInsertField( + "landscape", + with(targetContext) { windowInsetsOf(navBarRight = 48.dp, cutoutLeft = 32.dp) }, + ) + } + + private fun captureInsertField( + name: String, + insets: WindowInsetsCompat, + ) = withCardTemplateEditor { + currentFragment!!.showInsertFieldDialog() + advanceRobolectricLooper() + val dialog = supportFragmentManager.fragments.filterIsInstance().single() + try { + val window = dialog.requireDialog().window!! + window.simulateSystemBars(insets) + captureScreen(name) + + dialog.requireView().findViewById(R.id.view_pager).setCurrentItem(Tab.SPECIAL.position, false) + advanceRobolectricLooper() + ViewCompat.dispatchApplyWindowInsets(window.decorView, insets) + dialog.childFragmentManager.fragments + .filterIsInstance() + .single() + .binding.root + .scrollToEnd() + captureScreen("${name}_special_scrolled_to_bottom") + } finally { + dialog.dismissNow() + } + } +} From 6b8035e5f31ec0368490d7fd22af8b04b9be89fe Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Tue, 22 Sep 2026 20:32:04 +0100 Subject: [PATCH 32/43] test(reviewer): MathJax asset URLs Forward slashes are used to the tests pass on Windows. Part of 21926 Assisted-by: GPT-6 --- .../java/com/ichi2/anki/ViewerResourceHandler.kt | 12 +++--------- .../com/ichi2/anki/ViewerResourceHandlerTest.kt | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt index d26379e76ed7..4a4efabb4e22 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt @@ -13,11 +13,9 @@ import timber.log.Timber import java.io.ByteArrayInputStream import java.io.File import java.io.FileInputStream -import java.nio.file.Paths -import kotlin.io.path.pathString private const val RANGE_HEADER = "Range" -private const val MATHJAX_PATH_PREFIX = "/_anki/js/vendor/mathjax" +private const val MATHJAX_PATH_PREFIX = "/_anki/js/vendor/mathjax/" class ViewerResourceHandler( context: Context, @@ -38,12 +36,8 @@ class ViewerResourceHandler( try { if (path.startsWith(MATHJAX_PATH_PREFIX)) { - val mathjaxAssetPath = - Paths - .get( - "backend/js/vendor/mathjax", - path.removePrefix(MATHJAX_PATH_PREFIX), - ).pathString + // Asset names use forward slashes, including when tests run on Windows. + val mathjaxAssetPath = "backend/js/vendor/mathjax/${path.removePrefix(MATHJAX_PATH_PREFIX)}" val inputStream = assetManager.open(mathjaxAssetPath) return WebResourceResponse(guessMimeType(path), null, inputStream) } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt index 39be328952fe..830be945966f 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt @@ -38,6 +38,20 @@ class ViewerResourceHandlerTest : RobolectricTest() { assertNull(response, "a request escaping the media directory must not be served") } + @Test + fun `MathJax implementation is available at the backend URL`() { + val response = ViewerResourceHandler(targetContext).shouldInterceptRequest(request("/_anki/js/vendor/mathjax/tex-chtml-full.js")) + + val expected = readAssetText("backend/js/vendor/mathjax/tex-chtml-full.js") + assertEquals(expected, assertNotNull(response).data.bufferedReader().use { it.readText() }) + } + + private fun readAssetText(path: String): String = + targetContext.assets + .open(path) + .bufferedReader() + .use { it.readText() } + private fun request(path: String): WebResourceRequest = mock { on { url } doReturn "http://127.0.0.1$path".toUri() From 0db09ba48e09782466f77c014191c787217190f3 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Tue, 22 Sep 2026 20:32:04 +0100 Subject: [PATCH 33/43] fix(reviewer): handle Anki 26.09 '/_anki/js/mathjax.js' Anki 26.09 requests this file, previously: * Vendored MathJax (/_anki/js/vendor/mathjax) was handled * mathjax.js sat outside this directory Part of 21926 Assisted-by: GPT-6 --- .../src/main/java/com/ichi2/anki/ViewerResourceHandler.kt | 3 +++ .../test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt index 4a4efabb4e22..6fccdad86ecd 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ViewerResourceHandler.kt @@ -35,6 +35,9 @@ class ViewerResourceHandler( } try { + if (path == "/_anki/js/mathjax.js") { + return WebResourceResponse(guessMimeType(path), null, assetManager.open("backend/js/mathjax.js")) + } if (path.startsWith(MATHJAX_PATH_PREFIX)) { // Asset names use forward slashes, including when tests run on Windows. val mathjaxAssetPath = "backend/js/vendor/mathjax/${path.removePrefix(MATHJAX_PATH_PREFIX)}" diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt index 830be945966f..2f1d8a232b65 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ViewerResourceHandlerTest.kt @@ -38,6 +38,14 @@ class ViewerResourceHandlerTest : RobolectricTest() { assertNull(response, "a request escaping the media directory must not be served") } + @Test + fun `MathJax configuration is available at the backend URL`() { + val response = ViewerResourceHandler(targetContext).shouldInterceptRequest(request("/_anki/js/mathjax.js")) + + val expected = readAssetText("backend/js/mathjax.js") + assertEquals(expected, assertNotNull(response).data.bufferedReader().use { it.readText() }) + } + @Test fun `MathJax implementation is available at the backend URL`() { val response = ViewerResourceHandler(targetContext).shouldInterceptRequest(request("/_anki/js/vendor/mathjax/tex-chtml-full.js")) From 0bf29a54ea996e0a79aba6f8079e8002b2391ac7 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 21:48:08 +0100 Subject: [PATCH 34/43] refactor: add `Prefs.useSoftwareRendering` Part of 22136 Assisted-by: GPT-6 --- AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt | 1 + AnkiDroid/src/main/java/com/ichi2/utils/ViewGroupUtils.kt | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt b/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt index e8c089a9012e..069388c1cf33 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt @@ -391,6 +391,7 @@ open class PrefsRepository( // **************************************** Advanced **************************************** // + var useSoftwareRendering by booleanPref(R.string.disable_hardware_render_key, defaultValue = false) val isHtmlTypeAnswerEnabled by booleanPref(R.string.use_input_tag_key, defaultValue = false) var useFixedPortInReviewer by booleanPref(R.string.use_fixed_port_pref_key, false) var allowTemplatesToRecordAudio by booleanPref(R.string.pref_allow_template_audio_recording, false) diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/ViewGroupUtils.kt b/AnkiDroid/src/main/java/com/ichi2/utils/ViewGroupUtils.kt index 043f542ba46f..89d16c55379a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/ViewGroupUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/ViewGroupUtils.kt @@ -6,7 +6,7 @@ import android.app.Activity import android.view.View import android.view.ViewGroup import androidx.core.view.children -import com.ichi2.anki.common.preferences.sharedPrefs +import com.ichi2.anki.settings.Prefs import timber.log.Timber import java.util.ArrayList @@ -33,7 +33,7 @@ object ViewGroupUtils { } fun setRenderWorkaround(activity: Activity) { - if (activity.sharedPrefs().getBoolean("softwareRender", false)) { + if (Prefs.useSoftwareRendering) { Timber.i("ViewGroupUtils::setRenderWorkaround - software render requested, altering Views...") setContentViewLayerTypeSoftware(activity) } else { From 380582d9a0cf6cd92a951338f7c5fb0a6aa11fbe Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 27 Sep 2026 21:53:02 +0100 Subject: [PATCH 35/43] feat(study-screen): handle 'Disable card hardware render' Fixes 22136 Assisted-by: GPT-6 --- .../ui/windows/reviewer/ReviewerFragment.kt | 8 +++ .../windows/reviewer/ReviewerFragmentTest.kt | 70 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt index fe88b4f60cd5..1379f5e747e1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt @@ -81,6 +81,7 @@ import com.ichi2.anki.utils.ext.showDialogFragment import com.ichi2.anki.utils.ext.window import com.ichi2.anki.workarounds.SafeWebViewLayout import com.ichi2.themes.Themes +import com.ichi2.utils.ViewGroupUtils.setRenderWorkaround import com.ichi2.utils.dp import com.ichi2.utils.show import com.squareup.seismic.ShakeDetector @@ -246,6 +247,13 @@ class ReviewerFragment : if (Prefs.keepScreenOn) { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } + + setRenderWorkaround(requireActivity()) + } + + override fun onWebViewRecreated(webView: WebView) { + super.onWebViewRecreated(webView) + setRenderWorkaround(requireActivity()) } private fun setupTypeAnswer() { diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragmentTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragmentTest.kt index 77061480769d..425cc76235ad 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragmentTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragmentTest.kt @@ -2,9 +2,12 @@ package com.ichi2.anki.ui.windows.reviewer +import android.view.View +import android.webkit.WebView import androidx.core.content.edit import androidx.fragment.app.DialogFragment import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.R import com.ichi2.anki.RobolectricTest import com.ichi2.anki.cardviewer.Gesture import com.ichi2.anki.common.preferences.sharedPrefs @@ -15,9 +18,11 @@ import com.ichi2.anki.reviewer.MappableBinding.Companion.toPreferenceString import com.ichi2.anki.reviewer.ReviewerBinding import com.ichi2.anki.scheduling.SetDueDateDialog import com.ichi2.anki.scheduling.singleDayText +import com.ichi2.anki.settings.Prefs import com.ichi2.anki.utils.ext.DIALOG_FRAGMENT_TAG import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle +import org.junit.After import org.junit.Test import org.junit.runner.RunWith import org.robolectric.Robolectric @@ -35,6 +40,64 @@ import kotlin.test.assertTrue class ReviewerFragmentTest : RobolectricTest() { override fun getCollectionStorageMode() = CollectionStorageMode.IN_MEMORY_WITH_MEDIA + @After + fun resetSoftwareRendering() { + Prefs.sharedPrefs.edit { + remove(Prefs.key(R.string.disable_hardware_render_key)) + } + } + + @Test + fun `software rendering is applied to the study screen when enabled`() = + runTest { + addBasicNote() + Prefs.useSoftwareRendering = true + + withReviewer { + assertRenderingLayerType(View.LAYER_TYPE_SOFTWARE) + } + } + + @Test + fun `software rendering is reapplied after the WebView is recreated`() = + runTest { + addBasicNote() + Prefs.useSoftwareRendering = true + + withReviewer { + val webViewLayout = binding.webViewLayout + val originalWebView = assertIs(webViewLayout.getChildAt(0)) + webViewLayout.onRenderProcessGone(originalWebView) + + assertNotSame(originalWebView, webViewLayout.getChildAt(0)) + assertRenderingLayerType(View.LAYER_TYPE_SOFTWARE) + } + } + + @Test + fun `default rendering is preserved when software rendering is disabled`() = + runTest { + addBasicNote() + Prefs.useSoftwareRendering = false + + withReviewer { + assertRenderingLayerType(View.LAYER_TYPE_NONE) + } + } + + @Test + fun `default rendering is preserved when the rendering preference is unset`() = + runTest { + addBasicNote() + Prefs.sharedPrefs.edit { + remove(Prefs.key(R.string.disable_hardware_render_key)) + } + + withReviewer { + assertRenderingLayerType(View.LAYER_TYPE_NONE) + } + } + @Test fun `shaking does not stack set due date dialogs`() = assertShakeDoesNotStackDialogs(ViewerAction.RESCHEDULE_NOTE) @@ -113,6 +176,13 @@ class ReviewerFragmentTest : RobolectricTest() { } } + private fun ReviewerFragment.assertRenderingLayerType(expected: Int) { + assertEquals(expected, requireView().layerType, "study screen root") + assertEquals(expected, binding.backButton.layerType, "toolbar button") + assertEquals(expected, binding.answerArea.layerType, "answer buttons") + assertEquals(expected, assertIs(binding.webViewLayout.getChildAt(0)).layerType, "card WebView") + } + private suspend fun TestScope.openEditTags(reviewer: ReviewerFragment): TagsDialog { reviewer.viewModel.executeAction(ViewerAction.TAG) advanceUntilIdle() From f1b7173556c204f1ce9b781813451844307e6676 Mon Sep 17 00:00:00 2001 From: AnkiDroid Translations Date: Mon, 28 Sep 2026 09:32:07 +0000 Subject: [PATCH 36/43] Updated strings from Crowdin --- .../src/main/res/values-af/03-dialogs.xml | 5 +- .../src/main/res/values-af/10-preferences.xml | 10 +- .../src/main/res/values-am/03-dialogs.xml | 5 +- .../src/main/res/values-am/10-preferences.xml | 10 +- .../src/main/res/values-ar/03-dialogs.xml | 5 +- .../src/main/res/values-ar/10-preferences.xml | 10 +- .../src/main/res/values-az/03-dialogs.xml | 5 +- .../src/main/res/values-az/10-preferences.xml | 10 +- .../src/main/res/values-be/03-dialogs.xml | 5 +- .../src/main/res/values-be/10-preferences.xml | 10 +- .../src/main/res/values-bg/03-dialogs.xml | 5 +- .../src/main/res/values-bg/10-preferences.xml | 10 +- .../src/main/res/values-bn/03-dialogs.xml | 5 +- .../src/main/res/values-bn/10-preferences.xml | 10 +- .../src/main/res/values-ca/03-dialogs.xml | 5 +- .../src/main/res/values-ca/10-preferences.xml | 10 +- .../src/main/res/values-ckb/03-dialogs.xml | 5 +- .../main/res/values-ckb/10-preferences.xml | 10 +- .../src/main/res/values-cs/03-dialogs.xml | 5 +- .../src/main/res/values-cs/10-preferences.xml | 10 +- .../src/main/res/values-da/03-dialogs.xml | 5 +- .../src/main/res/values-da/10-preferences.xml | 10 +- .../src/main/res/values-de/03-dialogs.xml | 5 +- .../src/main/res/values-de/10-preferences.xml | 10 +- .../src/main/res/values-el/03-dialogs.xml | 5 +- .../src/main/res/values-el/10-preferences.xml | 10 +- .../src/main/res/values-eo/03-dialogs.xml | 5 +- .../src/main/res/values-eo/10-preferences.xml | 10 +- .../src/main/res/values-es-rAR/03-dialogs.xml | 5 +- .../main/res/values-es-rAR/10-preferences.xml | 10 +- .../src/main/res/values-es-rES/03-dialogs.xml | 5 +- .../main/res/values-es-rES/10-preferences.xml | 10 +- .../src/main/res/values-et/03-dialogs.xml | 5 +- .../src/main/res/values-et/10-preferences.xml | 10 +- .../src/main/res/values-eu/03-dialogs.xml | 5 +- .../src/main/res/values-eu/10-preferences.xml | 10 +- .../src/main/res/values-fa/03-dialogs.xml | 5 +- .../src/main/res/values-fa/10-preferences.xml | 10 +- .../src/main/res/values-fi/03-dialogs.xml | 5 +- .../src/main/res/values-fi/10-preferences.xml | 10 +- .../src/main/res/values-fil/03-dialogs.xml | 5 +- .../main/res/values-fil/10-preferences.xml | 10 +- .../src/main/res/values-fr/03-dialogs.xml | 5 +- .../src/main/res/values-fr/10-preferences.xml | 10 +- .../src/main/res/values-fy/03-dialogs.xml | 5 +- .../src/main/res/values-fy/10-preferences.xml | 10 +- .../src/main/res/values-ga/03-dialogs.xml | 5 +- .../src/main/res/values-ga/10-preferences.xml | 10 +- .../src/main/res/values-gl/02-strings.xml | 4 +- .../src/main/res/values-gl/03-dialogs.xml | 5 +- .../src/main/res/values-gl/10-preferences.xml | 124 +++++++++--------- .../src/main/res/values-got/03-dialogs.xml | 5 +- .../main/res/values-got/10-preferences.xml | 10 +- .../src/main/res/values-gu/03-dialogs.xml | 5 +- .../src/main/res/values-gu/10-preferences.xml | 10 +- .../src/main/res/values-heb/03-dialogs.xml | 5 +- .../main/res/values-heb/10-preferences.xml | 10 +- .../src/main/res/values-hi/03-dialogs.xml | 5 +- .../src/main/res/values-hi/10-preferences.xml | 10 +- .../src/main/res/values-hr/03-dialogs.xml | 5 +- .../src/main/res/values-hr/10-preferences.xml | 10 +- .../src/main/res/values-hu/03-dialogs.xml | 5 +- .../src/main/res/values-hu/10-preferences.xml | 10 +- .../src/main/res/values-hy/03-dialogs.xml | 5 +- .../src/main/res/values-hy/10-preferences.xml | 10 +- .../src/main/res/values-ind/03-dialogs.xml | 5 +- .../main/res/values-ind/10-preferences.xml | 10 +- .../src/main/res/values-it/03-dialogs.xml | 5 +- .../src/main/res/values-it/10-preferences.xml | 10 +- .../src/main/res/values-iw/03-dialogs.xml | 5 +- .../src/main/res/values-iw/10-preferences.xml | 10 +- .../src/main/res/values-ja/03-dialogs.xml | 5 +- .../src/main/res/values-ja/10-preferences.xml | 24 ++-- .../src/main/res/values-ka/03-dialogs.xml | 5 +- .../src/main/res/values-ka/10-preferences.xml | 10 +- .../src/main/res/values-kk/03-dialogs.xml | 5 +- .../src/main/res/values-kk/10-preferences.xml | 10 +- .../src/main/res/values-km/03-dialogs.xml | 5 +- .../src/main/res/values-km/10-preferences.xml | 10 +- .../src/main/res/values-kn/03-dialogs.xml | 5 +- .../src/main/res/values-kn/10-preferences.xml | 10 +- .../src/main/res/values-ko/03-dialogs.xml | 5 +- .../src/main/res/values-ko/10-preferences.xml | 10 +- .../src/main/res/values-ku/03-dialogs.xml | 5 +- .../src/main/res/values-ku/10-preferences.xml | 10 +- .../src/main/res/values-ky/03-dialogs.xml | 5 +- .../src/main/res/values-ky/10-preferences.xml | 10 +- .../src/main/res/values-lt/03-dialogs.xml | 5 +- .../src/main/res/values-lt/10-preferences.xml | 10 +- .../src/main/res/values-lv/03-dialogs.xml | 5 +- .../src/main/res/values-lv/10-preferences.xml | 10 +- .../src/main/res/values-mk/03-dialogs.xml | 5 +- .../src/main/res/values-mk/10-preferences.xml | 10 +- .../src/main/res/values-ml/03-dialogs.xml | 5 +- .../src/main/res/values-ml/10-preferences.xml | 10 +- .../src/main/res/values-mn/03-dialogs.xml | 5 +- .../src/main/res/values-mn/10-preferences.xml | 10 +- .../src/main/res/values-mr/03-dialogs.xml | 5 +- .../src/main/res/values-mr/10-preferences.xml | 10 +- .../src/main/res/values-ms/03-dialogs.xml | 5 +- .../src/main/res/values-ms/10-preferences.xml | 10 +- .../src/main/res/values-my/03-dialogs.xml | 5 +- .../src/main/res/values-my/10-preferences.xml | 10 +- .../src/main/res/values-nl/03-dialogs.xml | 5 +- .../src/main/res/values-nl/10-preferences.xml | 10 +- .../src/main/res/values-nn/03-dialogs.xml | 5 +- .../src/main/res/values-nn/10-preferences.xml | 10 +- .../src/main/res/values-no/03-dialogs.xml | 5 +- .../src/main/res/values-no/10-preferences.xml | 10 +- .../src/main/res/values-or/03-dialogs.xml | 5 +- .../src/main/res/values-or/10-preferences.xml | 10 +- .../src/main/res/values-pa/03-dialogs.xml | 5 +- .../src/main/res/values-pa/10-preferences.xml | 10 +- .../src/main/res/values-pl/03-dialogs.xml | 5 +- .../src/main/res/values-pl/10-preferences.xml | 10 +- .../src/main/res/values-pt-rBR/03-dialogs.xml | 5 +- .../main/res/values-pt-rBR/10-preferences.xml | 10 +- .../src/main/res/values-pt-rPT/03-dialogs.xml | 5 +- .../main/res/values-pt-rPT/10-preferences.xml | 10 +- .../src/main/res/values-ro/03-dialogs.xml | 5 +- .../src/main/res/values-ro/10-preferences.xml | 10 +- .../src/main/res/values-ru/03-dialogs.xml | 5 +- .../src/main/res/values-ru/10-preferences.xml | 10 +- .../src/main/res/values-sat/03-dialogs.xml | 5 +- .../main/res/values-sat/10-preferences.xml | 10 +- .../src/main/res/values-sc/03-dialogs.xml | 5 +- .../src/main/res/values-sc/10-preferences.xml | 10 +- .../src/main/res/values-sk/03-dialogs.xml | 5 +- .../src/main/res/values-sk/10-preferences.xml | 10 +- .../src/main/res/values-sl/03-dialogs.xml | 5 +- .../src/main/res/values-sl/10-preferences.xml | 10 +- .../src/main/res/values-sq/03-dialogs.xml | 5 +- .../src/main/res/values-sq/10-preferences.xml | 10 +- .../src/main/res/values-sr/03-dialogs.xml | 5 +- .../src/main/res/values-sr/10-preferences.xml | 10 +- .../src/main/res/values-sv/03-dialogs.xml | 5 +- .../src/main/res/values-sv/10-preferences.xml | 10 +- .../src/main/res/values-ta/03-dialogs.xml | 5 +- .../src/main/res/values-ta/10-preferences.xml | 10 +- .../src/main/res/values-te/03-dialogs.xml | 5 +- .../src/main/res/values-te/10-preferences.xml | 10 +- .../src/main/res/values-tgl/03-dialogs.xml | 5 +- .../main/res/values-tgl/10-preferences.xml | 10 +- .../src/main/res/values-th/03-dialogs.xml | 5 +- .../src/main/res/values-th/10-preferences.xml | 10 +- .../src/main/res/values-ti/03-dialogs.xml | 5 +- .../src/main/res/values-ti/10-preferences.xml | 10 +- .../src/main/res/values-tr/03-dialogs.xml | 5 +- .../src/main/res/values-tr/10-preferences.xml | 10 +- .../src/main/res/values-tt/03-dialogs.xml | 5 +- .../src/main/res/values-tt/10-preferences.xml | 10 +- .../src/main/res/values-ug/03-dialogs.xml | 5 +- .../src/main/res/values-ug/10-preferences.xml | 10 +- .../src/main/res/values-uk/03-dialogs.xml | 5 +- .../src/main/res/values-uk/10-preferences.xml | 10 +- .../src/main/res/values-ur/03-dialogs.xml | 5 +- .../src/main/res/values-ur/10-preferences.xml | 10 +- .../src/main/res/values-uz/03-dialogs.xml | 5 +- .../src/main/res/values-uz/10-preferences.xml | 10 +- .../src/main/res/values-vi/03-dialogs.xml | 5 +- .../src/main/res/values-vi/10-preferences.xml | 10 +- .../src/main/res/values-zh-rCN/03-dialogs.xml | 5 +- .../main/res/values-zh-rCN/10-preferences.xml | 10 +- .../src/main/res/values-zh-rTW/03-dialogs.xml | 5 +- .../main/res/values-zh-rTW/10-preferences.xml | 10 +- 165 files changed, 968 insertions(+), 394 deletions(-) diff --git a/AnkiDroid/src/main/res/values-af/03-dialogs.xml b/AnkiDroid/src/main/res/values-af/03-dialogs.xml index aafa5d5e99bb..a09cbaee9716 100644 --- a/AnkiDroid/src/main/res/values-af/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-af/03-dialogs.xml @@ -49,7 +49,7 @@ Verhoog/verlaag (“-3”) vandag se nuwe hersieningslimiet met Hersien kaarte wat in die laaste x dae vergeet is Review cards due in the next: - Voorskou nuwe kaarte wat in die laaste x dae bygevoeg is: + Preview new cards added in the last: Select x cards from the deck: Stoor is vol Stoor is amper vol @@ -78,6 +78,7 @@ Oorskryf Herstel Vervang + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Die gespesifiseerde pad was nie \'n geldige gids nie. Die verskafde teks los nie op na \'n geldige PEM-gekodeerde X509 sertifikaat nie. Fout met die ontleding van sertifikaat @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Media word nagegaan… Adding tags… - Tagging results + Tags added Media word uitgevee… Verwyderingsresultaat diff --git a/AnkiDroid/src/main/res/values-af/10-preferences.xml b/AnkiDroid/src/main/res/values-af/10-preferences.xml index 82e3845e88da..51a5078f78ae 100644 --- a/AnkiDroid/src/main/res/values-af/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-af/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-am/03-dialogs.xml b/AnkiDroid/src/main/res/values-am/03-dialogs.xml index 58d9cf1414e9..202ed1b91ba6 100644 --- a/AnkiDroid/src/main/res/values-am/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-am/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-am/10-preferences.xml b/AnkiDroid/src/main/res/values-am/10-preferences.xml index 53cc0aec1dd3..88ffb716551e 100644 --- a/AnkiDroid/src/main/res/values-am/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-am/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ar/03-dialogs.xml b/AnkiDroid/src/main/res/values-ar/03-dialogs.xml index 0d90deb713e8..98127d3260cd 100644 --- a/AnkiDroid/src/main/res/values-ar/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ar/03-dialogs.xml @@ -57,7 +57,7 @@ إضافة/طرح (مثلًا «-٣») إلى حد المراجعات لهذا اليوم راجع البطاقات المنسية في آخر س يوم: Review cards due in the next: - عاين البطاقات الجديدة المضافة في آخر س يوم: + Preview new cards added in the last: إختر س بطاقة من الرزمة: ذاكرة التخزين ممتلئة ذاكرة التخزين ممتلئة تقريبًا @@ -86,6 +86,7 @@ استبدال إصلاح استبدال + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s المسار المحدد ليس مجلدًا صالحًا لا يُحال النص المُدخل إلى شهادة «X509» مشفرة ب«PEM» صالحة. حدث خطأ في تحليل الشهادة @@ -94,7 +95,7 @@ قد يستغرق هذا وقتًا طويلًا مع مجموعات الوسائط الكبيرة. أتود الاستمرار؟ يتم فحص الوسائط… تجري إضافة الوسوم… - Tagging results + Tags added جار حذف الوسائط… نتيجة الحذف diff --git a/AnkiDroid/src/main/res/values-ar/10-preferences.xml b/AnkiDroid/src/main/res/values-ar/10-preferences.xml index 28b2c5fb4069..d9f172739ca1 100644 --- a/AnkiDroid/src/main/res/values-ar/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ar/10-preferences.xml @@ -457,9 +457,15 @@ برأيك أو عن أي خلل تجده.]]> إظهار توكيد الإجابة شاشة - إجابة فقط - قلب وإجابة + Also assign to ‘%1$s’? + Skip + Assign فتح الإعدادات الإعدادات المرتبطة + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-az/03-dialogs.xml b/AnkiDroid/src/main/res/values-az/03-dialogs.xml index c2a9ed0314a6..0ed372b2fe0c 100644 --- a/AnkiDroid/src/main/res/values-az/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-az/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Son x günlərdə unudulmuş kartları nəzərdən keçirin: Review cards due in the next: - Son x günlərdə əlavə edilmiş yeni kartların Ön görünüşü: + Preview new cards added in the last: Select x cards from the deck: Yaddaş doludur Yaddaş dolmaq üzrədir @@ -78,6 +78,7 @@ Overwrite Repair Əvəz et + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Media silinir… Deletion result diff --git a/AnkiDroid/src/main/res/values-az/10-preferences.xml b/AnkiDroid/src/main/res/values-az/10-preferences.xml index 180f05ff908d..061eec00420d 100644 --- a/AnkiDroid/src/main/res/values-az/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-az/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-be/03-dialogs.xml b/AnkiDroid/src/main/res/values-be/03-dialogs.xml index fe4ba51d968e..2239c8945de6 100644 --- a/AnkiDroid/src/main/res/values-be/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-be/03-dialogs.xml @@ -53,7 +53,7 @@ Павялічыць/паменшыць (“-3”) абмежаванні паўтарэння новых картак на сёння Паўтарыць забытыя карткі за апошнія некалькі дзён: Review cards due in the next: - Прагледзець новыя карткі, якія дадзены за апошнія некалькі дзён: + Preview new cards added in the last: Выберыце х картак з калоды: Сховішча запоўнена Сховішча амаль запоўнена @@ -82,6 +82,7 @@ Перазапісаць Выправіць Замяніць + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Азначаны шлях не з\'яўляецца дапушчальным каталогам Прыведзены тэкст не пераўтвараецца ў дапушчальны сертыфікат X509 з кадзіраваннем PEM Памылка разборкі сертыфіката @@ -90,7 +91,7 @@ Гэта можа заняць шмат часу ў тым выпадку, калі калекцыя медыяфайлаў занадта вялікая. Працягнуць? Праверка медыяфайлаў… Дадаванне тэгаў… - Вынікі тэгавання + Tags added Выдаленне медыяфайлаў… Вынік выдалення diff --git a/AnkiDroid/src/main/res/values-be/10-preferences.xml b/AnkiDroid/src/main/res/values-be/10-preferences.xml index 69a5061b5b06..1238b9e006c6 100644 --- a/AnkiDroid/src/main/res/values-be/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-be/10-preferences.xml @@ -452,9 +452,15 @@ водгукі або памылкі.]]> Паказваць зваротную сувязь адказу Экран - Толькі адказ - Перавярнуць і адказаць + Also assign to ‘%1$s’? + Skip + Assign Адкрыць налады Звязаныя налады + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-bg/03-dialogs.xml b/AnkiDroid/src/main/res/values-bg/03-dialogs.xml index 6344af322996..357dfef7e79c 100644 --- a/AnkiDroid/src/main/res/values-bg/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-bg/03-dialogs.xml @@ -49,7 +49,7 @@ Увеличаване/намаляване (\"-3\") днешен лимитът на повтарящи се карти Повтаряне на забравени карти през последните x дни: Review cards due in the next: - Преглед на нови карти, добавени през последните x дни: + Preview new cards added in the last: Select x cards from the deck: Пълно хранилище Почти пълно хранилище @@ -78,6 +78,7 @@ Презапиши Поправи Замени + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Указаният път не е валидна директория The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Проверка на медия… Adding tags… - Tagging results + Tags added Изтриване на медийни файлове… Резултат за изтриване diff --git a/AnkiDroid/src/main/res/values-bg/10-preferences.xml b/AnkiDroid/src/main/res/values-bg/10-preferences.xml index 470d469b13d0..8d42ad08ecb5 100644 --- a/AnkiDroid/src/main/res/values-bg/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-bg/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-bn/03-dialogs.xml b/AnkiDroid/src/main/res/values-bn/03-dialogs.xml index 8d9d920f852a..5f84559395c2 100644 --- a/AnkiDroid/src/main/res/values-bn/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-bn/03-dialogs.xml @@ -49,7 +49,7 @@ আজকের পর্যালোচনা সীমা বাড়ান/কমান (“-3”) গত x দিনে ভুলে যাওয়া কার্ডগুলি পর্যালোচনা করুন: Review cards due in the next: - গত x দিনে যোগ করা নতুন কার্ডের পূর্বরূপ দেখুন: + Preview new cards added in the last: Select x cards from the deck: স্টোরেজ পূর্ণ স্টোরেজ প্রায় পূর্ণ @@ -78,6 +78,7 @@ ওভাররাইট করুন মেরামত প্রতিস্থাপন + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s নির্দিষ্ট পথ একটি বৈধ ডিরেক্টরি ছিল না The provided text does not resolve to a valid PEM-encoded X509 certificate শংসাপত্র পার্সিং ত্রুটি৷ @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? মিডিয়া পরীক্ষা করা হচ্ছে… Adding tags… - Tagging results + Tags added মিডিয়া মুছে ফেলা হচ্ছে… মুছে ফেলার ফলাফল diff --git a/AnkiDroid/src/main/res/values-bn/10-preferences.xml b/AnkiDroid/src/main/res/values-bn/10-preferences.xml index 010d17af4c0f..64a295369ca6 100644 --- a/AnkiDroid/src/main/res/values-bn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-bn/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign সেটিংস খুলুন Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ca/03-dialogs.xml b/AnkiDroid/src/main/res/values-ca/03-dialogs.xml index dd0986101c76..df7a0839e935 100644 --- a/AnkiDroid/src/main/res/values-ca/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ca/03-dialogs.xml @@ -49,7 +49,7 @@ Augmenta/disminueix (\"-3\") el límit d\'avui de revisions a Revisa les fitxes oblidades en els darrers x dies: Review cards due in the next: - Mostra les fitxes noves afegides en els últims x dies: + Preview new cards added in the last: Select x cards from the deck: No queda espai al disc El disc està gairebé ple @@ -78,6 +78,7 @@ Sobreescriure Repara Reemplaça + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s El camí especificat no era un directori vàlid The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Comprovant multimèdia… Adding tags… - Tagging results + Tags added Suprimint medis… Resultat de la supressió diff --git a/AnkiDroid/src/main/res/values-ca/10-preferences.xml b/AnkiDroid/src/main/res/values-ca/10-preferences.xml index 7adbb185024d..8e197c080c83 100644 --- a/AnkiDroid/src/main/res/values-ca/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ca/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ckb/03-dialogs.xml b/AnkiDroid/src/main/res/values-ckb/03-dialogs.xml index e6f1feb769cd..a391163131d6 100644 --- a/AnkiDroid/src/main/res/values-ckb/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ckb/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Tamîr bike Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Rêya ku hat rawekirin rêzdarekî rast nîne The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? …Medyayê kontrol dike Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-ckb/10-preferences.xml b/AnkiDroid/src/main/res/values-ckb/10-preferences.xml index 08332b7be302..fe965ce1afde 100644 --- a/AnkiDroid/src/main/res/values-ckb/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ckb/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-cs/03-dialogs.xml b/AnkiDroid/src/main/res/values-cs/03-dialogs.xml index d73fc472aff5..fbe668d1243a 100644 --- a/AnkiDroid/src/main/res/values-cs/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-cs/03-dialogs.xml @@ -53,7 +53,7 @@ Zvýšit/snížit („-3“) dnešní limit opakování o Opakovat zapomenuté karty z posledních x dnů: Review cards due in the next: - Zkoušení nových karet přidaných v posledních x dnech: + Preview new cards added in the last: Vybrat x karet z balíčku: Úložiště je plné Úložiště téměř plné @@ -82,6 +82,7 @@ Přepsat Opravit Nahradit + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Zadaná cesta není platný adresář Zadaný text neposkytuje platný certifikát X509 v kódování PEM. Chyba zpracování certifikátu @@ -90,7 +91,7 @@ Toto může trvat dlouhou dobu u velké sbírky multimédií? Pokračovat? Kontrolují se multimédia… Přidávání štítků… - Tagging results + Tags added Odstraňují se multimédia… Výsledek odstraňování diff --git a/AnkiDroid/src/main/res/values-cs/10-preferences.xml b/AnkiDroid/src/main/res/values-cs/10-preferences.xml index 2e7ff235e386..47145a42a25f 100644 --- a/AnkiDroid/src/main/res/values-cs/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-cs/10-preferences.xml @@ -448,9 +448,15 @@ zpětnou vazbu nebo chybu.]]> Zobrazit zpětnou vazbu odpovědi Obrazovka - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Otevřít nastavení Související nastavení + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-da/03-dialogs.xml b/AnkiDroid/src/main/res/values-da/03-dialogs.xml index 6a75e6a2fa53..f73fdabf8e17 100644 --- a/AnkiDroid/src/main/res/values-da/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-da/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Lagerplads opbrugt Lagerplads næsten fuld @@ -78,6 +78,7 @@ Overskriv Reparér Erstat + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-da/10-preferences.xml b/AnkiDroid/src/main/res/values-da/10-preferences.xml index d6bb327a26b8..e3828c0c7e80 100644 --- a/AnkiDroid/src/main/res/values-da/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-da/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-de/03-dialogs.xml b/AnkiDroid/src/main/res/values-de/03-dialogs.xml index 21cf535420a8..d9709066e4cb 100644 --- a/AnkiDroid/src/main/res/values-de/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-de/03-dialogs.xml @@ -49,7 +49,7 @@ Heutigen Tageshöchstwerts für Wiederholungskarten erhöhen („3“) oder verringern („-3“) um Karten wiederholen, die in den letzten x Tagen vergessen wurden: Review cards due in the next: - Vorschau auf neue Karten, die in den letzten x Tagen hinzugefügt wurden: + Preview new cards added in the last: Wählen Sie x Karten aus dem Stapel aus: Speicher voll Speicher fast voll @@ -78,6 +78,7 @@ Überschreiben Reparieren Ersetzen + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Der angegebene Pfad ist kein gültiges Verzeichnis Der angegebene text ergibt kein gültiges PEM-codiertes X509-zertifikat Fehler beim parsen des Zertifikats @@ -86,7 +87,7 @@ Das kann mit großen Mediensammlungen lange dauern. Fortfahren? Medien werden überprüft … Schlagworte werden hinzugefügt… - Tagging results + Tags added Medien werden gelöscht … Löschergebnis diff --git a/AnkiDroid/src/main/res/values-de/10-preferences.xml b/AnkiDroid/src/main/res/values-de/10-preferences.xml index 42fb2c4112af..117249b4678e 100644 --- a/AnkiDroid/src/main/res/values-de/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-de/10-preferences.xml @@ -442,9 +442,15 @@ Feedback oder Fehler.]]> Antwort-Feedback anzeigen Bildschirm - Nur Antwort - Umdrehen und Antworten + Also assign to ‘%1$s’? + Skip + Assign Einstellungen öffnen Verwandte Einstellungen + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-el/03-dialogs.xml b/AnkiDroid/src/main/res/values-el/03-dialogs.xml index 2477711ce780..c3e1fc4b23a2 100644 --- a/AnkiDroid/src/main/res/values-el/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-el/03-dialogs.xml @@ -49,7 +49,7 @@ Αύξηση / μείωση (“-3”) σημερινού ορίου επαναλήψεων κατά Επανάληψη ξεχασμένων καρτών στις τελευταίες x μέρες: Review cards due in the next: - Προεπισκόπηση νέων καρτών του προστέθηκαν στις τελευταίες x μέρες: + Preview new cards added in the last: Επιλέξτε x κάρτες από την τράπουλα: Γεμάτος χώρος αποθήκευσης Χώρος αποθήκευσης σχεδόν πλήρης @@ -78,6 +78,7 @@ Αντικατάσταση Επισκευή Αντικατάσταση + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Η καθορισμένη διαδρομή δεν ήταν ένας έγκυρος κατάλογος Το κείμενο που εισέρθηκε δεν αντιπροσωπεί σε έγκυρο πιστοποιητικό Χ509 του κωδικού PEM. Σφάλμα ανάλυσης πιστοποιητικού @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Έλεγχος πολυμέσων… Προσθήκη ετικετών… - Tagging results + Tags added Διαγραφή πολυμέσων… Αποτέλεσμα διαγραφής diff --git a/AnkiDroid/src/main/res/values-el/10-preferences.xml b/AnkiDroid/src/main/res/values-el/10-preferences.xml index 0a2c79e827e1..dafb01db0920 100644 --- a/AnkiDroid/src/main/res/values-el/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-el/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Άνοιγμα ρυθμίσεων Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-eo/03-dialogs.xml b/AnkiDroid/src/main/res/values-eo/03-dialogs.xml index d2b632bf8f3e..3b2a825c30f8 100644 --- a/AnkiDroid/src/main/res/values-eo/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-eo/03-dialogs.xml @@ -49,7 +49,7 @@ (Mal)pliigi (“-3”) hodiaŭan limigon de kartoj por ripeti je Ripeti kartojn forgesitajn dum tiom da antaŭaj tagoj: Review cards due in the next: - Antaŭrigardi novajn kartojn aldonitajn dum tiom da antaŭaj tagoj: + Preview new cards added in the last: Nombro da kartoj por elekti el la kartaro: Konservejo plena Konservejo preskaŭ plena @@ -78,6 +78,7 @@ Anstataŭigi Ripari Anstataŭigi + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s La difinita dosier-indiko estas malĝusta La eneigita teksto ne estas ĝusta atestilo X509 en la PEM-formo Eraro dum pritrakti atestilon @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Kontrolado de aŭdvidaĵoj… Aldonado de etikedoj… - Tagging results + Tags added Forigado de aŭdvidaĵoj… Rezulto de forigo diff --git a/AnkiDroid/src/main/res/values-eo/10-preferences.xml b/AnkiDroid/src/main/res/values-eo/10-preferences.xml index e6ecc9e048bf..2f5bce5dd633 100644 --- a/AnkiDroid/src/main/res/values-eo/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-eo/10-preferences.xml @@ -432,9 +432,15 @@ Vi povas restarigi el sekurkopio aŭ elporti vian kolekton per la menuo de listo rimarkojn aŭ raporti erarojn.]]> Montri movbildon de respondo Ekrano - Nur respondi - Inversigi kaj respondi + Also assign to ‘%1$s’? + Skip + Assign Malfermi agordojn Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-es-rAR/03-dialogs.xml b/AnkiDroid/src/main/res/values-es-rAR/03-dialogs.xml index a8a3e5c42c0d..b7c54cc15b9e 100644 --- a/AnkiDroid/src/main/res/values-es-rAR/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-es-rAR/03-dialogs.xml @@ -49,7 +49,7 @@ Aumentar/disminuir (“-3”) el límite actual de revisión por Repasar tarjetas olvidadas en los últimos x días: Review cards due in the next: - Previsualizar nuevas tarjetas añadidas en los últimos x días: + Preview new cards added in the last: Selecciona x tarjetas del mazo: Almacenamiento lleno Almacenamiento casi lleno @@ -78,6 +78,7 @@ Sobreescribir Reparar Reemplazar + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s La ruta especificada no es un directorio válido El texto proporcionado no corresponde a un certificado X509 con codificación PEM válido. Error al procesar el certificado @@ -86,7 +87,7 @@ Esto puede llevar mucho tiempo con las colecciones de grandes archivos multimedia. ¿Continuar? Comprobando archivos multimedia… Añadiendo etiquetas… - Tagging results + Tags added Eliminando multimedia… Resultado de la eliminación diff --git a/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml b/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml index 5d603a10e6f3..e83617bbe8f5 100644 --- a/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-es-rAR/10-preferences.xml @@ -440,9 +440,15 @@ Puedes restaurar una colección desde una copia de seguridad en el menú de la l sugerencia o fallo.]]> Show answer feedback Pantalla - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Abrir la configuración Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-es-rES/03-dialogs.xml b/AnkiDroid/src/main/res/values-es-rES/03-dialogs.xml index 774bc7639d16..7b6c559485cd 100644 --- a/AnkiDroid/src/main/res/values-es-rES/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-es-rES/03-dialogs.xml @@ -49,7 +49,7 @@ Aumentar/disminuir (“-3”) el límite actual de revisión por Revisar tarjetas olvidadas en los últimos x días: Review cards due in the next: - Previsualizar nuevas tarjetas añadidas en los últimos x días: + Preview new cards added in the last: Selecciona x tarjetas del mazo: Almacenamiento lleno Almacenamiento casi lleno @@ -78,6 +78,7 @@ Sobreescribir Reparar Reemplazar + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s La ruta especificada no era un directorio válido El texto proporcionado no corresponde a un certificado X509 con codificación PEM válido. Error al procesar el certificado @@ -86,7 +87,7 @@ Esto puede llevar mucho tiempo con las colecciones de grandes archivos multimedia. ¿Continuar? Comprobando archivos multimedia… Añadiendo etiquetas… - Tagging results + Tags added Eliminando multimedia… Resultado de la eliminación diff --git a/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml b/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml index 4c0b0deeb1f3..1e6bbbbae5c1 100644 --- a/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-es-rES/10-preferences.xml @@ -440,9 +440,15 @@ Puedes restaurar una colección desde una copia de seguridad en el menú de la l sugerencia o fallo.]]> Show answer feedback Pantalla - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Abrir la configuración Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-et/03-dialogs.xml b/AnkiDroid/src/main/res/values-et/03-dialogs.xml index 874b5d4560e8..f0ff89501454 100644 --- a/AnkiDroid/src/main/res/values-et/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-et/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Kirjuta üle Paranda Asenda + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Meedia kontrollimine… Siltide lisamine… - Tagging results + Tags added Meedia kustutamine… Tulemuse kustutamine diff --git a/AnkiDroid/src/main/res/values-et/10-preferences.xml b/AnkiDroid/src/main/res/values-et/10-preferences.xml index 63aac86a2dd7..a99985a413e5 100644 --- a/AnkiDroid/src/main/res/values-et/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-et/10-preferences.xml @@ -443,9 +443,15 @@ tagasisidet või teata vigadest.]]> Näita vastuse tagasisidet Ekraan - Ainult vastus - Pööra ja vasta + Also assign to ‘%1$s’? + Skip + Assign Ava seaded Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-eu/03-dialogs.xml b/AnkiDroid/src/main/res/values-eu/03-dialogs.xml index 27c30547b585..ebe92f5007d6 100644 --- a/AnkiDroid/src/main/res/values-eu/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-eu/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Azkenengo x egunetan ahaztu diren kartak errepasatu: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Gainidatzi Konpondu Ordeztu + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-eu/10-preferences.xml b/AnkiDroid/src/main/res/values-eu/10-preferences.xml index c0186b28b4a4..2b0dcd3e2348 100644 --- a/AnkiDroid/src/main/res/values-eu/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-eu/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-fa/03-dialogs.xml b/AnkiDroid/src/main/res/values-fa/03-dialogs.xml index 4fcab2f68acf..cfa7533c3d7c 100644 --- a/AnkiDroid/src/main/res/values-fa/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-fa/03-dialogs.xml @@ -49,7 +49,7 @@ محدودیت تعداد مرور های امروز را کم/زیاد (\"-۳\") کن به این اندازه مرور کارت های فراموش شده در x روز گذشته: Review cards due in the next: - پیش نمایش کارت های جدید اضافه شده در x روز گذشته: + Preview new cards added in the last: انتخاب x کارت از دسته کارت ها: حافظه اوکی @@ -78,6 +78,7 @@ رونويسي کردن اصلاح جایگزین کردن + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s مسیر مشخص شده یک مسیر معتبر نیست متن ارائه شده به یک گواهی X509 کدگذاری شده با PEM معتبر تبدیل نمی‌شود خطا در تجزیه گواهی @@ -86,7 +87,7 @@ با مجموعه‌های رسانه‌ای حجیم، این کار ممکن است بسیار زمان‌بر باشد. آیا ادامه می‌دهید؟ در حال بررسی رسانه… در حال اضافه کردن تگ ها… - Tagging results + Tags added حذف رسانه… نتیجه حذف diff --git a/AnkiDroid/src/main/res/values-fa/10-preferences.xml b/AnkiDroid/src/main/res/values-fa/10-preferences.xml index 639b8dfa7c24..65bd881d64ec 100644 --- a/AnkiDroid/src/main/res/values-fa/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fa/10-preferences.xml @@ -439,9 +439,15 @@ بازخورد یا مشکل فنی را گزارش دهید.]]> نشان دادن پاسخ برای بازخورد صفحه‌نمایش - منحصراً جواب - برگرداندن و جواب + Also assign to ‘%1$s’? + Skip + Assign تنظیمات را باز کنید تنظیمات مرتبط + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-fi/03-dialogs.xml b/AnkiDroid/src/main/res/values-fi/03-dialogs.xml index 10d405b33e9f..bc3d1a1d4b82 100644 --- a/AnkiDroid/src/main/res/values-fi/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-fi/03-dialogs.xml @@ -49,7 +49,7 @@ Korota/alenna (\"-3\") tämän päivän kerrattavien kortien rajaa arvolla Kortteja unohdettu edellisenä x päivänä: Review cards due in the next: - Esikatsele viimeisinä x päivänä lisättyjä kortteja: + Preview new cards added in the last: Valitse x korttia satunnaisesti pakasta: Tallennustila on täynnä Tallennustila on melkein täynnä @@ -78,6 +78,7 @@ Korvaa Korjaa Korvaa + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Annettu hakemistopolku ei ole pätevä Annettu teksti ei vastaa kelvollista PEM-koodattua X509-varmentetta. Varmenteen jäsentäminen epäonnistui @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Tarkistetaan mediaa… Lisätään tunnisteita… - Tagging results + Tags added Poistetaan mediaa… Poiston tulos diff --git a/AnkiDroid/src/main/res/values-fi/10-preferences.xml b/AnkiDroid/src/main/res/values-fi/10-preferences.xml index 2f6765143d59..f102bb8aa88a 100644 --- a/AnkiDroid/src/main/res/values-fi/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fi/10-preferences.xml @@ -441,9 +441,15 @@ palautetta tai ilmoita virheistä.]]> Näytä vastauksen palaute Näyttö - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Avaa asetukset Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-fil/03-dialogs.xml b/AnkiDroid/src/main/res/values-fil/03-dialogs.xml index 3eedfe498b75..5887e452c0a3 100644 --- a/AnkiDroid/src/main/res/values-fil/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-fil/03-dialogs.xml @@ -49,7 +49,7 @@ Dagdagan/bawasan (\"-3\") ang hangganan ng pagrepaso ngayong araw ng Ang mga review kard na nakalimutan sa huling x araw: Review cards due in the next: - I-preview ang mga bagong kjard na naidagdag sa mga huling x araw: + Preview new cards added in the last: Select x cards from the deck: Puno na ang storage Malapit nang mapuno ang storage @@ -78,6 +78,7 @@ I-overwrite Isaayos Palitan + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Ang path na tinukoy ay hindi isang wastong direktoryo The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Sinusuri ang media… Adding tags… - Tagging results + Tags added Binubura ang media… Resulta ng pagbura diff --git a/AnkiDroid/src/main/res/values-fil/10-preferences.xml b/AnkiDroid/src/main/res/values-fil/10-preferences.xml index 4dfc8bdd621d..f94d3d31f21b 100644 --- a/AnkiDroid/src/main/res/values-fil/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fil/10-preferences.xml @@ -441,9 +441,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-fr/03-dialogs.xml b/AnkiDroid/src/main/res/values-fr/03-dialogs.xml index 57d0bba31c4b..ad3cfcf44a95 100644 --- a/AnkiDroid/src/main/res/values-fr/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-fr/03-dialogs.xml @@ -49,7 +49,7 @@ Augmentation/diminution (« -3 ») de la limite de révision d\'aujourd\'hui de Revoir les cartes oubliées dans les x derniers jours : Revoir les cartes dues dans le prochain : - Afficher les nouvelles cartes ajoutées dans les x derniers jours : + Preview new cards added in the last: Sélectionnez x cartes du paquet : Stockage plein Il n\'y a presque plus d\'espace libre @@ -78,6 +78,7 @@ Écraser Réparer Remplacer + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Le chemin indiqué ne mène pas à un répertoire valide Le texte fourni ne correspond pas à un certificat X509 encodé en PEM valide Erreur lors de l’analyse du certificat @@ -86,7 +87,7 @@ Cela peut prendre beaucoup de temps avec les grandes collections de médias. Continuer? Vérification des médias… Ajout d\'étiquettes… - Tagging results + Tags added Suppression du média… Résultat de la suppression diff --git a/AnkiDroid/src/main/res/values-fr/10-preferences.xml b/AnkiDroid/src/main/res/values-fr/10-preferences.xml index 3ed893a20cec..43d5fa8d51bb 100644 --- a/AnkiDroid/src/main/res/values-fr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fr/10-preferences.xml @@ -443,9 +443,15 @@ Elles ne sont pas destinées à la plupart des utilisateurs et restent en anglai feedback ou bugs.]]> Montrer la réaction de réponse Écran - Répondre uniquement - Retourner et répondre + Also assign to ‘%1$s’? + Skip + Assign Aller aux paramètres Paramètres associés + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-fy/03-dialogs.xml b/AnkiDroid/src/main/res/values-fy/03-dialogs.xml index 0add619121a9..1b0ee6d99d6f 100644 --- a/AnkiDroid/src/main/res/values-fy/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-fy/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-fy/10-preferences.xml b/AnkiDroid/src/main/res/values-fy/10-preferences.xml index 000649529766..8a8d9d27a880 100644 --- a/AnkiDroid/src/main/res/values-fy/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-fy/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ga/03-dialogs.xml b/AnkiDroid/src/main/res/values-ga/03-dialogs.xml index 05b518f1d903..20cad664aced 100644 --- a/AnkiDroid/src/main/res/values-ga/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ga/03-dialogs.xml @@ -55,7 +55,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -84,6 +84,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -92,7 +93,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-ga/10-preferences.xml b/AnkiDroid/src/main/res/values-ga/10-preferences.xml index ae6a8142aedc..1b3e23bab601 100644 --- a/AnkiDroid/src/main/res/values-ga/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ga/10-preferences.xml @@ -455,9 +455,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-gl/02-strings.xml b/AnkiDroid/src/main/res/values-gl/02-strings.xml index 988a5441f4f0..5d09b14d71f7 100644 --- a/AnkiDroid/src/main/res/values-gl/02-strings.xml +++ b/AnkiDroid/src/main/res/values-gl/02-strings.xml @@ -220,8 +220,8 @@ Tamaño da letra Amosar barra de ferramentas - Show preview - Hide preview + Amosar vista previa + Ocultar vista previa Aplicar letra grosa Aplicar cursiva Aplicar subliñado diff --git a/AnkiDroid/src/main/res/values-gl/03-dialogs.xml b/AnkiDroid/src/main/res/values-gl/03-dialogs.xml index e0384f894e5f..a092794e11dc 100644 --- a/AnkiDroid/src/main/res/values-gl/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-gl/03-dialogs.xml @@ -49,7 +49,7 @@ Aumentar/diminuír (\"-3\") o límite de repasos actual en Repasar as tarxetas esquecidas nos últimos x días: Repasar as tarxetas pendentes nos vindeiros: - Amosar as tarxetas novas engadidas nos últimos x días: + Preview new cards added in the last: Seleccionar x tarxetas da baralla: Almacenamento cheo Almacenamento case cheo @@ -78,6 +78,7 @@ Substituír Reparar Substituír + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s A ruta especificada non é un cartafol válido O texto proporcionado non corresponde a un certificado X509 codificado nun formato PEM válido Erro na análise do certificado @@ -86,7 +87,7 @@ Isto pode levar moito tempo con coleccións multimedia grandes. Queres continuar? Verificando multimedia… Engadindo etiquetas… - Resultados das etiquetas + Tags added Eliminando multimedia… Resultado da eliminación diff --git a/AnkiDroid/src/main/res/values-gl/10-preferences.xml b/AnkiDroid/src/main/res/values-gl/10-preferences.xml index da9e379274f2..f2e587a8560d 100644 --- a/AnkiDroid/src/main/res/values-gl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-gl/10-preferences.xml @@ -24,7 +24,7 @@ --> - Enabled + Activado Desactivado %d min @@ -224,75 +224,75 @@ ################################## Review reminders ################################### ####################################################################################### --> - Review reminders - App-wide - Deck-specific - An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: + Recordatorios de estudo + Xeral da aplicación + Por baralla + Produciuse un erro ao cargar os teus recordatorios de repaso. Elimináronse os recordatorios danados. Detalles: - Schedule reminders + Programar recordatorios Engadir recordatorio Non tes recordatorios de repaso - Troubleshoot - Notification settings - List of scheduled review reminders - Deck not found - Reminders are unavailable - Fix - Successfully added new review reminder - Successfully deleted review reminder - Successfully edited review reminder + Solucionar problemas + Axustes de notificación + Recordatorios de repaso programados + Baralla non atopada + Os recordatorios non están dispoñíbeis + Arranxar + Engadiuse o novo recordatorio de repaso correctamente + Eliminouse o recordatorio de repaso correctamente + Editouse o recordatorio de repaso correctamente Engadir recordatorio de repaso Editar recordatorio de repaso - Time: - Deck: - Advanced settings - Card threshold: - Do not send me notifications if there are less than this many cards due - Only notify me if no reviews have been done today - Please enter a whole number of cards - The threshold must be at least 0 - Delete this reminder? - This action cannot be undone. + Hora: + Baralla: + Axustes avanzados + Límite de tarxetas: + Non enviar notificacións se hai menos deste número de tarxetas pendentes + Notificar se aínda non se fixeron os repasos diarios + Insire un número entero de tarxetas + O límite debe ser polo menos 0 + Eliminar este recordatorio? + Esta acción non se pode desfacer. - Reminders are unavailable. - Reminders may not work correctly. - Your reminders should work as expected. - Different devices have different notification restriction settings. Check your system settings or search online if issues persist. + Os recordatorios non están dispoñíbeis. + Os recordatorios poden funcionar incorrectamente. + Os teus recordatorios deberían funcionar correctamente. + Os axustes de notificacións varían segundo o dispositivo. Comproba a configuración do sistema ou busca en liña se o problema continúa. - Notification permission - Notification channel - Do not disturb - Battery optimization - Power saving mode - Alarms & reminders permission + Permiso de notificacións + Canle de notificacións + Non molestar + Optimización de batería + Modo aforro de batería + Permiso de alarmas & recordatorios - Granted - Denied - On - Off - Unrestricted - Optimized - Restricted + Concedido + Denegado + Si + Non + Sen restricións + Optimizada + Restritiva - The review reminder notification channel must be enabled - Do Not Disturb may mute reminder notifications - Battery optimization may delay reminders - Background usage is disabled. Reminders may not be delivered - Power saving mode may prevent timely delivery of reminders - Required to schedule reminders at exact times + A canle de notificacións dos recordatorios de repaso debe estar activada + O modo Non molestar pode silenciar as notificacións dos recordatorios + A optimización da batería pode atrasar os recordatorios + O uso en segundo plano está desactivado. É posible que os recordatorios non se envíen + O modo de aforro de batería pode impedir que os recordatorios cheguen a tempo + Necesario para programar recordatorios a horas exactas - Grant permission - Enable notification channel - Disable battery optimization - Open battery settings + Conceder permisos + Activar a canlel de notificacións + Desactivar optimización da batería + Abrir axustes de batería - It\'s time to study your cards - It\'s time to study \"%s\" - Snooze %s + É momento de estudar as túas tarxetas + É momento de estudar «%s» + Adiar %s - %d card due - %d cards due + %d tarxeta pendente + %d tarxetas pendentes Seleccionar etiquetas @@ -442,9 +442,15 @@ suxestión ou erro]]> Amosar explicación da resposta Pantalla - Só responder - Voltear e responder + Also assign to ‘%1$s’? + Skip + Assign Abrir configuración Configuracións relacionadas + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-got/03-dialogs.xml b/AnkiDroid/src/main/res/values-got/03-dialogs.xml index 1cfeec5c6afe..ad4ec4f0a7c2 100644 --- a/AnkiDroid/src/main/res/values-got/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-got/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - 𐌽𐌹𐌿𐌾𐍉𐍃 𐌺𐌰𐍂𐍄𐍉𐍃 𐍆𐌰𐌿𐍂𐌰𐍃𐌰𐌹𐍈𐌰𐌽 𐌹𐌽 𐌰𐍆𐍄𐌿𐌼𐌹𐍃𐍄𐌰𐌹𐌼 𐍇 𐌳𐌰𐌲𐌰𐌼: + Preview new cards added in the last: 𐍅𐌰𐌻𐌴𐌹 x 𐌺𐌰𐍂𐍄𐍉𐍃 𐌺𐌰𐍂𐍄𐌰𐌷𐌿𐌶𐌳𐌹𐍃: 𐍂𐌿𐌼 𐌹𐍃𐍄 𐍆𐌿𐌻𐌻 𐌻𐌹𐍃𐌰 𐌽𐌴𐍈𐌰 𐍆𐌿𐌻𐌻𐌰 @@ -78,6 +78,7 @@ 𐌿𐍆𐌰𐍂𐌼𐌴𐌻𐌾𐌰𐌽 𐌲𐌰𐌱𐍉𐍄𐌾𐌰𐌽 𐌼𐌹𐌸𐍃𐌰𐍄𐌾𐌰𐌽 + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s 𐌻𐌰𐌹𐌳𐌰 𐍃𐌴𐌹 𐌲𐌰𐍂𐌰𐌹𐌷𐍄𐌹𐌳𐌰 𐌽𐌹 𐍅𐌰𐍃 𐌳𐌹𐍂𐌰𐌹𐌺𐍄𐌰𐌿𐍂𐌹 𐍂𐌰𐌹𐌷𐍄 The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? 𐌿𐍃𐍃𐍉𐌺𐌾𐌰𐌽𐌳𐌰 𐌼𐌰𐌹𐌳𐌾𐌰… 𐌱𐌹𐌰𐌿𐌺𐌰𐌽𐌳𐌰 𐍃𐍉𐌺𐌽𐌹𐍅𐌰𐌿𐍂𐌳𐌰… - Tagging results + Tags added 𐌿𐍃𐌵𐌹𐍃𐍄𐌾𐌰𐌽𐌳𐌰 𐌼𐌰𐌹𐌳𐌾𐌰𐌼… 𐍄𐌰𐌿𐌹 𐌿𐍃𐌵𐌹𐍃𐍄𐌰𐌹𐍃 diff --git a/AnkiDroid/src/main/res/values-got/10-preferences.xml b/AnkiDroid/src/main/res/values-got/10-preferences.xml index 8a6c2ff032e3..d7eabfde978e 100644 --- a/AnkiDroid/src/main/res/values-got/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-got/10-preferences.xml @@ -444,9 +444,15 @@ feedback or bugs.]]> 𐌰𐌽𐌳𐌰𐌷𐌰𐍆𐍄𐌰𐌹𐍃 𐌷𐌹𐌻𐍀𐌰 𐌱𐌰𐌽𐌳𐍅𐌾𐌰𐌽 𐍃𐌺𐌰𐌹𐍂𐌼𐍃 - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign 𐌻𐌰𐌲𐌴𐌹𐌽𐌹𐌽𐍃 𐌿𐍃𐌻𐌿𐌺𐌰𐌽 Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-gu/03-dialogs.xml b/AnkiDroid/src/main/res/values-gu/03-dialogs.xml index ebea83e92caf..01a5720cc294 100644 --- a/AnkiDroid/src/main/res/values-gu/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-gu/03-dialogs.xml @@ -49,7 +49,7 @@ આજની સમીક્ષા મર્યાદામાં વધારો/ઘટાડો (“-3”). છેલ્લા x દિવસમાં ભૂલી ગયેલા કાર્ડની સમીક્ષા કરો: Review cards due in the next: - છેલ્લા x દિવસમાં ઉમેરાયેલા નવા કાર્ડ્સનું પૂર્વાવલોકન કરો: + Preview new cards added in the last: Select x cards from the deck: સ્ટોરેજ ભરાઈ ગયું સ્ટોરેજ લગભગ ભરાઈ ગયું છે @@ -78,6 +78,7 @@ ઓવરરાઇટ કરો સમારકામ બદલો + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s ઉલ્લેખિત પાથ માન્ય ડિરેક્ટરી ન હતી પ્રદાન કરેલ ટેક્સ્ટ માન્ય PEM-એનકોડેડ X509 પ્રમાણપત્રને ઉકેલતું નથી પ્રમાણપત્રનું પદચ્છેદન કરવામાં ભૂલ @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? મીડિયા તપાસી રહ્યું છે… Adding tags… - Tagging results + Tags added મીડિયા ડિલીટ કરી રહ્યાં છીએ… કાઢી નાખવાનું પરિણામ diff --git a/AnkiDroid/src/main/res/values-gu/10-preferences.xml b/AnkiDroid/src/main/res/values-gu/10-preferences.xml index cf203ecbaa84..200a0fe5574a 100644 --- a/AnkiDroid/src/main/res/values-gu/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-gu/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-heb/03-dialogs.xml b/AnkiDroid/src/main/res/values-heb/03-dialogs.xml index 13de7ac93cc6..688ee38d9593 100644 --- a/AnkiDroid/src/main/res/values-heb/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-heb/03-dialogs.xml @@ -52,7 +52,7 @@ הוספה/הורדה(“-3”) הגבלת חזרות להיום ב חזרה על כרטיסים נשכחים בx ימים האחרונים Review cards due in the next: - צפייה ראשונית בכרטיסים חדשים שנוספו בx ימים האחרונים: + Preview new cards added in the last: בחר x קלפים מהחפיסה: האיחסון מלא שטח האחסון כמעט מלא @@ -81,6 +81,7 @@ שכתוב תיקון החלפה + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s הנתיב שצוין אינו תיקייה תקנית הטקסט שסופק אינו מתאים לאישור X509 תקין מקודד ב-PEM שגיאה בניתוח האישור @@ -89,7 +90,7 @@ This may take a long time with large media collections. Proceed? קבצי המדיה בבדיקה… הוספת תגיות… - Tagging results + Tags added המדיה נמחקת… תוצאת מחיקה diff --git a/AnkiDroid/src/main/res/values-heb/10-preferences.xml b/AnkiDroid/src/main/res/values-heb/10-preferences.xml index 7ea96d606933..d6dae079d1e8 100644 --- a/AnkiDroid/src/main/res/values-heb/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-heb/10-preferences.xml @@ -445,9 +445,15 @@ משוב או באג.]]> הצג משוב על התשובה מסך - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign פתח הגדרות הגדרות קשורים + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-hi/03-dialogs.xml b/AnkiDroid/src/main/res/values-hi/03-dialogs.xml index 42dcbdb17ddd..e4d7d39a3752 100644 --- a/AnkiDroid/src/main/res/values-hi/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-hi/03-dialogs.xml @@ -49,7 +49,7 @@ आज की समीक्षा सीमा बढ़ाएँ / घटाएँ (\"-3\") पिछले x दिनों में भूल गए कार्ड की समीक्षा करें: Review cards due in the next: - पिछले x दिनों में जोड़े गए नए कार्ड का पूर्वावलोकन करें: + Preview new cards added in the last: Select x cards from the deck: स्टोरेज भर गया स्टोरेज लगभग पूर्ण @@ -78,6 +78,7 @@ उपरिलेखित करें सुधारें की जगह + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s निर्दिष्ट पथ कोई मांय निर्देशिका नहीं है प्रदान किया गया पाठ वैध PEM-एन्कोडेड X509 प्रमाणपत्र का समाधान नहीं करता है प्रमाणपत्र पार्स करने में त्रुटि @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? मीडिया जांच कर रहा है.. । Adding tags… - Tagging results + Tags added मीडिया हटा रहा है… विलोपन का परिणाम diff --git a/AnkiDroid/src/main/res/values-hi/10-preferences.xml b/AnkiDroid/src/main/res/values-hi/10-preferences.xml index 5cc51a065e89..56728e551b10 100644 --- a/AnkiDroid/src/main/res/values-hi/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hi/10-preferences.xml @@ -432,9 +432,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-hr/03-dialogs.xml b/AnkiDroid/src/main/res/values-hr/03-dialogs.xml index 9e500371926c..6aeb066edf66 100644 --- a/AnkiDroid/src/main/res/values-hr/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-hr/03-dialogs.xml @@ -51,7 +51,7 @@ Povećaj/umanji (“-3”) današnji limit kartica ponavljanja za Kartice ponavljanja zaboravljene u zadnjih x dana: Review cards due in the next: - Pregledaj nove kartice dodane u zadnjih x dana: + Preview new cards added in the last: Odaberi x kartica iz špila: Pohrana je puna Pohrana je skoro puna @@ -80,6 +80,7 @@ Overwrite Popravi Zamijeni + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Zadani put nije bio valjana mapa Navedeni tekst se na razrješava u valjani PEM-kodirani X509 certifikat Greška u parsiranju certifikata @@ -88,7 +89,7 @@ This may take a long time with large media collections. Proceed? Provjera medija… Dodavanje oznaka… - Tagging results + Tags added Brisanje medija… Rezultat brisanja diff --git a/AnkiDroid/src/main/res/values-hr/10-preferences.xml b/AnkiDroid/src/main/res/values-hr/10-preferences.xml index 35057498d8af..f50e8cc955a6 100644 --- a/AnkiDroid/src/main/res/values-hr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hr/10-preferences.xml @@ -446,9 +446,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-hu/03-dialogs.xml b/AnkiDroid/src/main/res/values-hu/03-dialogs.xml index 31ef273d9cfe..e8be4d4e90c7 100644 --- a/AnkiDroid/src/main/res/values-hu/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-hu/03-dialogs.xml @@ -49,7 +49,7 @@ Növeld/csökkentsd (\"-3\") a mai ismétlődő kártyák korlátját Az elmúlt x napban elfelejtett kártyák ismétlése: Review cards due in the next: - Az elmúlt x nap új kártyáinak előre átnézése: + Preview new cards added in the last: Select x cards from the deck: Tárhely megtelt Tárhely majdnem megtelt @@ -78,6 +78,7 @@ Felülír Javít Kicserél + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s A megadott elérési út nem érvényes mappa The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Média ellenőrzése… Adding tags… - Tagging results + Tags added Média törlése… Törlés eredménye diff --git a/AnkiDroid/src/main/res/values-hu/10-preferences.xml b/AnkiDroid/src/main/res/values-hu/10-preferences.xml index 3b269140a6e9..eecf9875eae1 100644 --- a/AnkiDroid/src/main/res/values-hu/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hu/10-preferences.xml @@ -441,9 +441,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Beállítások megnyitása Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-hy/03-dialogs.xml b/AnkiDroid/src/main/res/values-hy/03-dialogs.xml index edcd4d9965e6..dcea7f7fed65 100644 --- a/AnkiDroid/src/main/res/values-hy/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-hy/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Վերանայել վերջին X օրում մոռացված քարտերը` Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Վերագրանցել Վերականգնել Փոխարինել + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Նշված ուղին վավեր թղթապանակ չէ The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-hy/10-preferences.xml b/AnkiDroid/src/main/res/values-hy/10-preferences.xml index 6ac61daeecc8..9f3b545373f7 100644 --- a/AnkiDroid/src/main/res/values-hy/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-hy/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ind/03-dialogs.xml b/AnkiDroid/src/main/res/values-ind/03-dialogs.xml index 117f6994f719..8e97c1770884 100644 --- a/AnkiDroid/src/main/res/values-ind/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ind/03-dialogs.xml @@ -47,7 +47,7 @@ Tambahkan/kurangi (“-3”) batas kartu ulasan hari ini sampai Ulas kartu yang terlupakan dalam x hari terakhir: Review cards due in the next: - Pratinjau kartu yang baru ditambahkan dalam x hari terakhir: + Preview new cards added in the last: Pilih x kartu dari dek: Penyimpanan sudah penuh Penyimpanan hampir penuh @@ -76,6 +76,7 @@ Timpa Perbaiki Ganti + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Jalur yang dipilih bukan direktori yang valid Teks yang diberikan tidak menghasilkan sertifikat PEM-encoded X509 yang valid Error penguraian sertifikat @@ -84,7 +85,7 @@ This may take a long time with large media collections. Proceed? Memeriksa media… Menambah tag… - Tagging results + Tags added Menghapus media… Hasil penghapusan diff --git a/AnkiDroid/src/main/res/values-ind/10-preferences.xml b/AnkiDroid/src/main/res/values-ind/10-preferences.xml index de7d203aca0f..5686dfd2553a 100644 --- a/AnkiDroid/src/main/res/values-ind/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ind/10-preferences.xml @@ -437,9 +437,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-it/03-dialogs.xml b/AnkiDroid/src/main/res/values-it/03-dialogs.xml index 43052aa2f6c1..cc2b3c4d5cb5 100644 --- a/AnkiDroid/src/main/res/values-it/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-it/03-dialogs.xml @@ -49,7 +49,7 @@ Aumentare/diminuire (“-3”) il limite odierno di revisioni di Ripeti carte dimenticate negli ultimi x giorni: Review cards due in the next: - Anteprima carte nuove aggiunte negli ultimi x giorni: + Preview new cards added in the last: Seleziona x carte casualmente dal mazzo: Spazio di archiviazione pieno Spazio di archiviazione quasi pieno @@ -78,6 +78,7 @@ Sovrascrivi Ripara Sostituisci + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Il percorso specificato non era una cartella valida Il testo fornito non si risolve con un certificato X509 codificato in PEM valido Errore durante l\'analisi del certificato @@ -86,7 +87,7 @@ Potrebbe richiedere molto tempo per collezioni multimediali di grandi dimensioni. Procedere? Controllo i media… Aggiungi etichetta - Tagging results + Tags added Eliminando media… Eliminazione risultato diff --git a/AnkiDroid/src/main/res/values-it/10-preferences.xml b/AnkiDroid/src/main/res/values-it/10-preferences.xml index 378a9c4c6587..efb83a4f9902 100644 --- a/AnkiDroid/src/main/res/values-it/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-it/10-preferences.xml @@ -442,9 +442,15 @@ feedback o bugs.]]> Mostra feedback risposta Screen - Solo risposta - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Apri impostazioni Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-iw/03-dialogs.xml b/AnkiDroid/src/main/res/values-iw/03-dialogs.xml index 13de7ac93cc6..688ee38d9593 100644 --- a/AnkiDroid/src/main/res/values-iw/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-iw/03-dialogs.xml @@ -52,7 +52,7 @@ הוספה/הורדה(“-3”) הגבלת חזרות להיום ב חזרה על כרטיסים נשכחים בx ימים האחרונים Review cards due in the next: - צפייה ראשונית בכרטיסים חדשים שנוספו בx ימים האחרונים: + Preview new cards added in the last: בחר x קלפים מהחפיסה: האיחסון מלא שטח האחסון כמעט מלא @@ -81,6 +81,7 @@ שכתוב תיקון החלפה + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s הנתיב שצוין אינו תיקייה תקנית הטקסט שסופק אינו מתאים לאישור X509 תקין מקודד ב-PEM שגיאה בניתוח האישור @@ -89,7 +90,7 @@ This may take a long time with large media collections. Proceed? קבצי המדיה בבדיקה… הוספת תגיות… - Tagging results + Tags added המדיה נמחקת… תוצאת מחיקה diff --git a/AnkiDroid/src/main/res/values-iw/10-preferences.xml b/AnkiDroid/src/main/res/values-iw/10-preferences.xml index 7ea96d606933..d6dae079d1e8 100644 --- a/AnkiDroid/src/main/res/values-iw/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-iw/10-preferences.xml @@ -445,9 +445,15 @@ משוב או באג.]]> הצג משוב על התשובה מסך - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign פתח הגדרות הגדרות קשורים + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ja/03-dialogs.xml b/AnkiDroid/src/main/res/values-ja/03-dialogs.xml index b8e69dd2dce8..b6c6c3e856c4 100644 --- a/AnkiDroid/src/main/res/values-ja/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ja/03-dialogs.xml @@ -47,7 +47,7 @@ 上限枚数をいくつ増やしますか?(減らす場合は負の数) 直近 x 日間で「やり直し」を選んだカードを再学習 Review cards due in the next: - 直近 x 日間で追加した習得未開始の新規カードを予習 + Preview new cards added in the last: デッキから抽出する枚数: ストレージの空き容量がありません ストレージの空き容量が少なくなっています @@ -76,6 +76,7 @@ 上書きする 修復 上書き + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s 指定されたパスは有効なディレクトリではありませんでした 提供されたテキストをPEMエンコードしても有効な X.509証明書になりません 証明書の解析エラー @@ -84,7 +85,7 @@ メディア(画像・音声・動画)ファイルが多いと、チェックに長い時間がかかる場合があります。続行しますか? メディアをチェックしています… タグを追加しています… - Tagging results + Tags added メディアを削除中… 削除結果 diff --git a/AnkiDroid/src/main/res/values-ja/10-preferences.xml b/AnkiDroid/src/main/res/values-ja/10-preferences.xml index d18fd05a1f97..a97ab9518218 100644 --- a/AnkiDroid/src/main/res/values-ja/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ja/10-preferences.xml @@ -221,9 +221,9 @@ ################################## Review reminders ################################### ####################################################################################### --> - Review reminders - App-wide - Deck-specific + 学習リマインダー + コレクション全体 + 特定のデッキ An error occurred while loading your review reminders, corrupted reminders have been deleted. Details: Schedule reminders @@ -284,11 +284,11 @@ Disable battery optimization Open battery settings - It\'s time to study your cards - It\'s time to study \"%s\" - Snooze %s + 学習する時間です + %s を学習する時間です + %s スヌーズ - %d cards due + %d枚の課題 タグを選択 @@ -438,9 +438,15 @@ ご意見・ご感想をお寄せください。不具合がある場合もお知らせいただければ幸いです。]]> 回答選択時にフィードバック(目印)を表示 画面枠 - 回答のみ - 表面では解答を表示 + Also assign to ‘%1$s’? + Skip + Assign 設定画面を開く 関連設定 + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ka/03-dialogs.xml b/AnkiDroid/src/main/res/values-ka/03-dialogs.xml index fa3a3d7d881a..08a89a5f4294 100644 --- a/AnkiDroid/src/main/res/values-ka/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ka/03-dialogs.xml @@ -49,7 +49,7 @@ დღევანდელი გადახედვების ლიმიტის გაზრდა/შემცირება (\"-3\") ამდენით: გასულ x დღეში დავიწყებული ბარათების გადახედვა: Review cards due in the next: - ბოლო x დღეში დამატებული ბარათების წინასწარ ნახვა: + Preview new cards added in the last: Select x cards from the deck: მეხსიერება სავსეა მეხსიერება თითქმის გავსებულია @@ -78,6 +78,7 @@ ჩანაცვლება შეკეთება ჩანაცვლება + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate შეცდომა სერტიფიკატის პარსინგისას @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? მედიაფაილების შემოწმება… იარლიყების დამატება… - Tagging results + Tags added მიმდინარეობს მედიაფაილების წაშლა… წაშლის შედეგი diff --git a/AnkiDroid/src/main/res/values-ka/10-preferences.xml b/AnkiDroid/src/main/res/values-ka/10-preferences.xml index 21a3131a4f2b..8fc83b991684 100644 --- a/AnkiDroid/src/main/res/values-ka/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ka/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign პარამეტრების გახსნა Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-kk/03-dialogs.xml b/AnkiDroid/src/main/res/values-kk/03-dialogs.xml index 9c2b92940dd5..a9e280184cec 100644 --- a/AnkiDroid/src/main/res/values-kk/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-kk/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-kk/10-preferences.xml b/AnkiDroid/src/main/res/values-kk/10-preferences.xml index 96c821077da1..e4052f7f9b26 100644 --- a/AnkiDroid/src/main/res/values-kk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-kk/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-km/03-dialogs.xml b/AnkiDroid/src/main/res/values-km/03-dialogs.xml index c7f0ca5922c0..c3b49cad2704 100644 --- a/AnkiDroid/src/main/res/values-km/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-km/03-dialogs.xml @@ -47,7 +47,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -76,6 +76,7 @@ Overwrite ជួសជុល ជំនួស + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -84,7 +85,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-km/10-preferences.xml b/AnkiDroid/src/main/res/values-km/10-preferences.xml index 53cb18860319..20070b189ec5 100644 --- a/AnkiDroid/src/main/res/values-km/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-km/10-preferences.xml @@ -439,9 +439,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign បើកការកំណត់ Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-kn/03-dialogs.xml b/AnkiDroid/src/main/res/values-kn/03-dialogs.xml index 9b8dd412ca11..cf1574a31842 100644 --- a/AnkiDroid/src/main/res/values-kn/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-kn/03-dialogs.xml @@ -49,7 +49,7 @@ ಇಂದಿನ ಚೆಕ್ ಮಿತಿಯನ್ನು ಹೆಚ್ಚಿಸಿ/ಕಡಿಮೆ ಮಾಡಿ (\"-3\"). ಕಳೆದ x ದಿನಗಳಲ್ಲಿ ಮರೆತಿರುವ ರಿವ್ಯೂ ಕಾರ್ಡ್‌ಗಳು: Review cards due in the next: - ಕಳೆದ x ದಿನಗಳಲ್ಲಿ ಸೇರಿಸಲಾದ ಹೊಸ ಕಾರ್ಡ್‌ಗಳ ಪೂರ್ವವೀಕ್ಷಣೆ: + Preview new cards added in the last: Select x cards from the deck: ಸಂಗ್ರಹಣೆ ತುಂಬಿದೆ ಸಂಗ್ರಹಣೆಯು ಬಹುತೇಕ ಪೂರ್ಣವಾಗಿದೆ @@ -78,6 +78,7 @@ ತಿದ್ದಿ ಬರೆಯಿರಿ ದುರಸ್ತಿ ಬದಲಾವಣೆ + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s ನಿರ್ದಿಷ್ಟಪಡಿಸಿದ ಮಾರ್ಗವು ಮಾನ್ಯವಾದ ಡೈರೆಕ್ಟರಿಯಾಗಿರಲಿಲ್ಲ ಒದಗಿಸಿದ ಪಠ್ಯವು ಮಾನ್ಯವಾದ PEM-ಎನ್‌ಕೋಡ್ ಮಾಡಿದ X509 ಪ್ರಮಾಣಪತ್ರವನ್ನು ಪರಿಹರಿಸುವುದಿಲ್ಲ ಪ್ರಮಾಣಪತ್ರವನ್ನು ಪಾರ್ಸಿಂಗ್ ಮಾಡುವಲ್ಲಿ ದೋಷ @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? ಮಾಧ್ಯಮವನ್ನು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ… Adding tags… - Tagging results + Tags added ಮಾಧ್ಯಮವನ್ನು ಅಳಿಸಲಾಗುತ್ತಿದೆ… ಅಳಿಸುವಿಕೆ ಫಲಿತಾಂಶ diff --git a/AnkiDroid/src/main/res/values-kn/10-preferences.xml b/AnkiDroid/src/main/res/values-kn/10-preferences.xml index e9b57f073035..249f6af98a7b 100644 --- a/AnkiDroid/src/main/res/values-kn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-kn/10-preferences.xml @@ -441,9 +441,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ko/03-dialogs.xml b/AnkiDroid/src/main/res/values-ko/03-dialogs.xml index 2acf76ccd9ea..6725a8eec274 100644 --- a/AnkiDroid/src/main/res/values-ko/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ko/03-dialogs.xml @@ -47,7 +47,7 @@ 오늘의 복습 제한을 (“-3”) 만큼 증가/감소시키기 최근 x일 동안 잊혀진 복습 카드: Review cards due in the next: - 최근 x일 내에 추가된 카드 미리 학습하기: + Preview new cards added in the last: 카드 묶음으로부터 선택된 카드 수: 저장 공간이 가득 찼습니다. 저장공간이 거의 가득 찼습니다. @@ -76,6 +76,7 @@ 덮어쓰기 복구 대체 + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s 지정된 경로가 올바르지 않습니다. 포함된 텍스트가 올바른 PEM 인코딩된 X509 인증서를 가지고 있지 않습니다 인증서 인식 오류 @@ -84,7 +85,7 @@ 미디어가 클 경우 시간이 오래 걸릴 수 있습니다. 계속할까요? 미디어 점검 중… 태그 추가 중… - Tagging results + Tags added 미디어 삭제 중… 삭제 결과 diff --git a/AnkiDroid/src/main/res/values-ko/10-preferences.xml b/AnkiDroid/src/main/res/values-ko/10-preferences.xml index a5f99026667e..8295db083ae6 100644 --- a/AnkiDroid/src/main/res/values-ko/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ko/10-preferences.xml @@ -436,9 +436,15 @@ 피드백 혹은 버그를 제보해주세요.]]> 정답 피드백 보이기 화면 - 답만 - 뒤집기와 답 + Also assign to ‘%1$s’? + Skip + Assign 설정 열기 관련 설정 + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ku/03-dialogs.xml b/AnkiDroid/src/main/res/values-ku/03-dialogs.xml index e6f1feb769cd..a391163131d6 100644 --- a/AnkiDroid/src/main/res/values-ku/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ku/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Tamîr bike Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Rêya ku hat rawekirin rêzdarekî rast nîne The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? …Medyayê kontrol dike Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-ku/10-preferences.xml b/AnkiDroid/src/main/res/values-ku/10-preferences.xml index 08332b7be302..fe965ce1afde 100644 --- a/AnkiDroid/src/main/res/values-ku/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ku/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ky/03-dialogs.xml b/AnkiDroid/src/main/res/values-ky/03-dialogs.xml index df1b0be99047..e37c55819c38 100644 --- a/AnkiDroid/src/main/res/values-ky/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ky/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-ky/10-preferences.xml b/AnkiDroid/src/main/res/values-ky/10-preferences.xml index 96c821077da1..e4052f7f9b26 100644 --- a/AnkiDroid/src/main/res/values-ky/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ky/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-lt/03-dialogs.xml b/AnkiDroid/src/main/res/values-lt/03-dialogs.xml index 8cde49722dbe..1912659f1954 100644 --- a/AnkiDroid/src/main/res/values-lt/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-lt/03-dialogs.xml @@ -53,7 +53,7 @@ Padidinti / sumažinti (\"-3\") šiandienos peržiūros ribą Peržiūrėti korteles, kurios buvo pamirštos per paskutines x dienų: Review cards due in the next: - Peržiūrėti naujas korteles, pridėtas per paskutines x dienų: + Preview new cards added in the last: Iš kortelių rinkinio pasirinkite x kortų: Saugykla pilna Saugykla beveik užpildyta @@ -82,6 +82,7 @@ Perrašyti Taisyti Pakeisti + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Nurodytas adresas negalioja Pateiktas tekstas neatitinka galiojančio PEM koduoto X509 sertifikato Klaida analizuojant sertifikatą @@ -90,7 +91,7 @@ This may take a long time with large media collections. Proceed? Tikrinamos laikmenos… Žymių pridėjimas… - Tagging results + Tags added Medijos pašalinimas… Ištrynimo rezultatas diff --git a/AnkiDroid/src/main/res/values-lt/10-preferences.xml b/AnkiDroid/src/main/res/values-lt/10-preferences.xml index a08ff360a4c8..0cd44fdac642 100644 --- a/AnkiDroid/src/main/res/values-lt/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-lt/10-preferences.xml @@ -449,9 +449,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Atidaryti nustatymus Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-lv/03-dialogs.xml b/AnkiDroid/src/main/res/values-lv/03-dialogs.xml index 237f4967211b..a2bea7b83aff 100644 --- a/AnkiDroid/src/main/res/values-lv/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-lv/03-dialogs.xml @@ -51,7 +51,7 @@ Palielināt/samazināt (\"-3\") šodienas izskatīšanas ierobežojumu par Parskatīt kartītes, kas ir aizmirstas pēdējās x dienās: Review cards due in the next: - Priekškatīt jaunās kartītes, kas pievienotas pēdējās x dienās: + Preview new cards added in the last: Atlasīt x kavas kartītes: Krātuve pilna Krātuve gandrīz pilna @@ -80,6 +80,7 @@ Aizvietot Labot Aizstāt + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Norādītais ceļš nenorāda uz derīgu mapi Norādītais tekstst nesatur derīgu ar PEM šifrētu X509 sertifikātu Kļūda sertifikāta apstrādāšanā @@ -88,7 +89,7 @@ Tas var aizņemt ilgu laiku, ja ir lieli informācijas nesēju krājumi. Turpināt? Pārbauda informācijas nesējus… Pievieno birkas… - Tagging results + Tags added Izdzēš informācijas nesējus… Izdzēšanas iznākums diff --git a/AnkiDroid/src/main/res/values-lv/10-preferences.xml b/AnkiDroid/src/main/res/values-lv/10-preferences.xml index 4bfa31611db2..1e86558d900f 100644 --- a/AnkiDroid/src/main/res/values-lv/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-lv/10-preferences.xml @@ -445,9 +445,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Atvērt iestatījumus Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-mk/03-dialogs.xml b/AnkiDroid/src/main/res/values-mk/03-dialogs.xml index ec61e51d23e1..491e7637edb3 100644 --- a/AnkiDroid/src/main/res/values-mk/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-mk/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Преглед на картички заборавени во последните x денови: Review cards due in the next: - Преглед на нови картички додадени во последните x дена: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Пребриши Поправка Замени + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Наведената патека не е валиден директориум The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Проверка на медиумит… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-mk/10-preferences.xml b/AnkiDroid/src/main/res/values-mk/10-preferences.xml index 4ec43182d091..68bb71fbafd3 100644 --- a/AnkiDroid/src/main/res/values-mk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-mk/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ml/03-dialogs.xml b/AnkiDroid/src/main/res/values-ml/03-dialogs.xml index 6312b4517fb4..4eec6da4bb91 100644 --- a/AnkiDroid/src/main/res/values-ml/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ml/03-dialogs.xml @@ -49,7 +49,7 @@ ഇന്നത്തെ അവലോകന പരിധി വർദ്ധിപ്പിക്കുക / കുറയ്ക്കുക (“-3”) കഴിഞ്ഞ x ദിവസങ്ങളിൽ മറന്ന അവലോകന കാർഡുകൾ: Review cards due in the next: - അവസാന x ദിവസങ്ങളിൽ ചേർത്ത പുതിയ കാർഡുകൾ പ്രിവ്യൂ ചെയ്യുക: + Preview new cards added in the last: Select x cards from the deck: സംഭരണം നിറഞ്ഞു സംഭരണം ഏതാണ്ട് നിറഞ്ഞു @@ -78,6 +78,7 @@ തിരുത്തിയെഴുതുക റിപ്പെയർ ചെയ്യുക മാറ്റി മറ്റൊന്നുവയ്ക്കുക + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s വ്യക്തമാക്കിയ പാത്ത് സാധുവായ ഒരു ഡയറക്‌ടറിയായിരുന്നില്ല The provided text does not resolve to a valid PEM-encoded X509 certificate സർട്ടിഫിക്കറ്റ് പാഴ്‌സ് ചെയ്യുന്നതിൽ പിശക് @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? മീഡിയ പരിശോധിക്കുന്നു… Adding tags… - Tagging results + Tags added മീഡിയ ഇല്ലാതാക്കുന്നു… ഇല്ലാതാക്കൽ ഫലം diff --git a/AnkiDroid/src/main/res/values-ml/10-preferences.xml b/AnkiDroid/src/main/res/values-ml/10-preferences.xml index b909751fe35b..352ab300d7fa 100644 --- a/AnkiDroid/src/main/res/values-ml/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ml/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign ക്രമീകരണങ്ങൾ തുറക്കുക Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-mn/03-dialogs.xml b/AnkiDroid/src/main/res/values-mn/03-dialogs.xml index 4c4f63b3c894..09d2f5c37d8f 100644 --- a/AnkiDroid/src/main/res/values-mn/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-mn/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-mn/10-preferences.xml b/AnkiDroid/src/main/res/values-mn/10-preferences.xml index fb5e3302d2ed..857ec034a0ef 100644 --- a/AnkiDroid/src/main/res/values-mn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-mn/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-mr/03-dialogs.xml b/AnkiDroid/src/main/res/values-mr/03-dialogs.xml index 5b4780c553ce..2774a2700ea4 100644 --- a/AnkiDroid/src/main/res/values-mr/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-mr/03-dialogs.xml @@ -49,7 +49,7 @@ आजची पुनरावलोकन मर्यादा वाढवा / कमी करा (“-3”) गेल्या x दिवसात विसरलेल्या कार्डांचे पुनरावलोकन करा: Review cards due in the next: - शेवटच्या x दिवसात जोडलेल्या नवीन कार्डांचे पूर्वावलोकन करा: + Preview new cards added in the last: Select x cards from the deck: स्टोरेज भरले आहे स्टोरेज जवळजवळ भरले आहे @@ -78,6 +78,7 @@ अधिलिखित दुरुस्ती पुनर्स्थित करा + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s निर्दिष्ट पथ वैध निर्देशिका नाही प्रदान केलेला मजकूर वैध PEM-एनकोड केलेल्या X509 प्रमाणपत्राचे निराकरण करत नाही प्रमाणपत्र पार्स करताना त्रुटी @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? मीडिया तपासत आहे… Adding tags… - Tagging results + Tags added मीडिया हटवित आहे… हटविण्याचा निकाल diff --git a/AnkiDroid/src/main/res/values-mr/10-preferences.xml b/AnkiDroid/src/main/res/values-mr/10-preferences.xml index 24e0f1607966..c9a0ca29c13e 100644 --- a/AnkiDroid/src/main/res/values-mr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-mr/10-preferences.xml @@ -439,9 +439,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ms/03-dialogs.xml b/AnkiDroid/src/main/res/values-ms/03-dialogs.xml index bd05ba077554..235e054d23a9 100644 --- a/AnkiDroid/src/main/res/values-ms/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ms/03-dialogs.xml @@ -47,7 +47,7 @@ Tambahkan/Kurangkan (“-3”) had semakan hari ini sebanyak Semak kad terlupa dalam x hari: Review cards due in the next: - Pratonton kad baru ditambah dalam x hari lepas: + Preview new cards added in the last: Select x cards from the deck: Storan penuh Storan hampir penuh @@ -76,6 +76,7 @@ Tulis ganti Baiki Ganti + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Laluan yang dinyatakan bukan direktori yang sah The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -84,7 +85,7 @@ This may take a long time with large media collections. Proceed? Memeriksa media… Adding tags… - Tagging results + Tags added Memadam media… Keputusan pemadaman diff --git a/AnkiDroid/src/main/res/values-ms/10-preferences.xml b/AnkiDroid/src/main/res/values-ms/10-preferences.xml index bdda9569e744..c788f92981ba 100644 --- a/AnkiDroid/src/main/res/values-ms/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ms/10-preferences.xml @@ -439,9 +439,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-my/03-dialogs.xml b/AnkiDroid/src/main/res/values-my/03-dialogs.xml index 40d9735fcb9f..4c8ff155e87c 100644 --- a/AnkiDroid/src/main/res/values-my/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-my/03-dialogs.xml @@ -47,7 +47,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -76,6 +76,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -84,7 +85,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-my/10-preferences.xml b/AnkiDroid/src/main/res/values-my/10-preferences.xml index 521fb773b270..047d011700d0 100644 --- a/AnkiDroid/src/main/res/values-my/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-my/10-preferences.xml @@ -439,9 +439,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-nl/03-dialogs.xml b/AnkiDroid/src/main/res/values-nl/03-dialogs.xml index 249bc9b33737..9bc2dacb5d87 100644 --- a/AnkiDroid/src/main/res/values-nl/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-nl/03-dialogs.xml @@ -49,7 +49,7 @@ De nieuwe overhoringslimiet van vandaag verhogen/verlagen (\"-3\") met Herhaal kaarten vergeten in de laatste x dagen: Review cards due in the next: - Voorbeschouw nieuwe kaarten toegevoegd in de laatste x dagen: + Preview new cards added in the last: Selecteer x aantal kaarten van de deck Opslag vol Opslag is bijna vol @@ -78,6 +78,7 @@ Overschrijven Repareer Vervangen + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Het opgegeven pad was geen geldige map De opgegeven tekst wordt niet omgezet in een geldig PEM-gecodeerd X509-certificaat Fout bij het parseren van certificaat @@ -86,7 +87,7 @@ Dit kan lang duren bij grote media-collecties. Doorgaan? Media controleren… Tags aan het toevoegen… - Tagging results + Tags added Verwijdering van media… Resultaat van verwijdering diff --git a/AnkiDroid/src/main/res/values-nl/10-preferences.xml b/AnkiDroid/src/main/res/values-nl/10-preferences.xml index 7056dd5ab5c7..c9183f74dd7b 100644 --- a/AnkiDroid/src/main/res/values-nl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-nl/10-preferences.xml @@ -440,9 +440,15 @@ feedback or bugs.]]> Show answer feedback Scherm - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Instellingen openen Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-nn/03-dialogs.xml b/AnkiDroid/src/main/res/values-nn/03-dialogs.xml index 8bf69d155d60..e4b18c40241e 100644 --- a/AnkiDroid/src/main/res/values-nn/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-nn/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Lagring fullt Lagring nesten full @@ -78,6 +78,7 @@ Overwrite Reparer Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Sletter media… Deletion result diff --git a/AnkiDroid/src/main/res/values-nn/10-preferences.xml b/AnkiDroid/src/main/res/values-nn/10-preferences.xml index b3aec9ab614c..f7ef199c2573 100644 --- a/AnkiDroid/src/main/res/values-nn/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-nn/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-no/03-dialogs.xml b/AnkiDroid/src/main/res/values-no/03-dialogs.xml index 0480457e7569..75f508953dd7 100644 --- a/AnkiDroid/src/main/res/values-no/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-no/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Lagring fullt Lagring nesten full @@ -78,6 +78,7 @@ Overwrite Reparer Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Sletter media… Deletion result diff --git a/AnkiDroid/src/main/res/values-no/10-preferences.xml b/AnkiDroid/src/main/res/values-no/10-preferences.xml index 374d7808806b..35cd2ef3b2ab 100644 --- a/AnkiDroid/src/main/res/values-no/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-no/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-or/03-dialogs.xml b/AnkiDroid/src/main/res/values-or/03-dialogs.xml index aaad1da1a51a..a593ae697828 100644 --- a/AnkiDroid/src/main/res/values-or/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-or/03-dialogs.xml @@ -49,7 +49,7 @@ ଆଜିର ସମୀକ୍ଷା ସୀମା ରେ ବୃଦ୍ଧି/ହ୍ରାସ (“-3”) କରନ୍ତୁ ଗତ x ଦିନରେ ଭୁଲିଯାଇଥିବା ପତ୍ରର ସମୀକ୍ଷା କରନ୍ତୁ: Review cards due in the next: - ଗତ x ଦିନରେ ଯୋଡ଼ାଯାଇଥିବା ନୂଆ ପତ୍ରଗୁଡ଼ିକର ପୂର୍ଵାଵଲୋକନ କରିବା: + Preview new cards added in the last: କେରୀରୁ x ଟି ପତ୍ର ଚୟନ କରିବା: ଷ୍ଟୋରେଜ୍ ଭର୍ତ୍ତି ହେଲାଣି ଷ୍ଟୋରେଜ୍ ପ୍ରାୟ ଭର୍ତ୍ତି ହେବାକୁ ବସିଲାଣି @@ -78,6 +78,7 @@ ନବଲିଖନ କରନ୍ତୁ ସଜାଡନ୍ତୁ ପ୍ରତିସ୍ଥାପନ + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s ନିର୍ଦ୍ଦିଷ୍ଟ ପଥ ଏକ ବୈଧ ନିର୍ଦେଶିକା ନୁହେଁ The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? ମିଡ଼ିଆ ଯାଞ୍ଚ ଚାଲିଛି… ଟ୍ୟାଗ୍‌ ଯୋଡ଼ାଯାଉଛି… - Tagging results + Tags added ମିଡ଼ିଆ ଵିଲୋପ ହେଉଛି… ବିଲୋପ ଫଳାଫଳ diff --git a/AnkiDroid/src/main/res/values-or/10-preferences.xml b/AnkiDroid/src/main/res/values-or/10-preferences.xml index c7f01bc776f5..9ce15e4f072b 100644 --- a/AnkiDroid/src/main/res/values-or/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-or/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback ପରଦା - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-pa/03-dialogs.xml b/AnkiDroid/src/main/res/values-pa/03-dialogs.xml index c4040e88c520..e77ce3eb51ea 100644 --- a/AnkiDroid/src/main/res/values-pa/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-pa/03-dialogs.xml @@ -49,7 +49,7 @@ ਅੱਜ ਦੀ ਸਮੀਖਿਆ ਸੀਮਾ ਵਧਾਓ/ਘਟਾਓ (“-3”) ਅੱਜ ਦੀ ਸਮੀਖਿਆ ਸੀਮਾ ਵਧਾਓ/ਘਟਾਓ (“-3”) Review cards due in the next: - ਪਿਛਲੇ x ਦਿਨਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤੇ ਗਏ ਨਵੇਂ ਕਾਰਡਾਂ ਦੀ ਸੰਖੇਪ ਜਾਣਕਾਰੀ: + Preview new cards added in the last: Select x cards from the deck: ਸਟੋਰੇਜ ਭਰੀ ਹੋਈ ਹੈ ਸਟੋਰੇਜ ਲਗਭਗ ਭਰੀ ਹੋਈ ਹੈ @@ -78,6 +78,7 @@ ਓਵਰਰਾਈਟ ਕਰੋ ਮੁਰੰਮਤ ਤਬਦੀਲੀ + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s ਨਿਰਦਿਸ਼ਟ ਮਾਰਗ ਇੱਕ ਵੈਧ ਡਾਇਰੈਕਟਰੀ ਨਹੀਂ ਸੀ ਨਿਰਦਿਸ਼ਟ ਮਾਰਗ ਇੱਕ ਵੈਧ ਡਾਇਰੈਕਟਰੀ ਨਹੀਂ ਸੀ The provided text does not resolve to a valid PEM-encoded X509 certificate ਸਰਟੀਫਿਕੇਟ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਤਰੁੱਟੀ @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added ਮੀਡੀਆ ਨੂੰ ਮਿਟਾਇਆ ਜਾ ਰਿਹਾ ਹੈ… ਮਿਟਾਉਣ ਦਾ ਨਤੀਜਾ diff --git a/AnkiDroid/src/main/res/values-pa/10-preferences.xml b/AnkiDroid/src/main/res/values-pa/10-preferences.xml index fd2b4e42cf03..c8153ae3fd64 100644 --- a/AnkiDroid/src/main/res/values-pa/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pa/10-preferences.xml @@ -439,9 +439,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-pl/03-dialogs.xml b/AnkiDroid/src/main/res/values-pl/03-dialogs.xml index f6059739b332..32059bf5fbe0 100644 --- a/AnkiDroid/src/main/res/values-pl/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-pl/03-dialogs.xml @@ -53,7 +53,7 @@ Zwiększ/zmniejsz (“-3”) dzisiejszy limit powtórek Przejrzyj zapomniane karty z ostatnich x dni: Review cards due in the next: - Podgląd nowych kart dodanych w ostatnich x dniach: + Preview new cards added in the last: Wybierz x kart z talii: Pamięć jest pełna Pamięć jest prawie pełna @@ -82,6 +82,7 @@ Nadpisz Napraw Zastąp + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Wybrana ścieżka nie wskazuje prawidłowo katalogu Podany tekst nie rozwiązuje poprawnego certyfikatu X509 zakodowanego PEM Błąd podczas parsowania certyfikatu @@ -90,7 +91,7 @@ To może zająć dużo czasu w przypadku dużych kolekcji multimedialnych. Kontynuować? Sprawdzanie mediów… Dodawanie tagów… - Tagging results + Tags added Usuwanie mediów… Wynik usuwania diff --git a/AnkiDroid/src/main/res/values-pl/10-preferences.xml b/AnkiDroid/src/main/res/values-pl/10-preferences.xml index 0d9358d47087..05155fac2429 100644 --- a/AnkiDroid/src/main/res/values-pl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pl/10-preferences.xml @@ -450,9 +450,15 @@ opinii lub błędów.]]> Pokaż reakcję odpowiedzi Ekran - Tylko odpowiedź - Odwróć i odpowiedz + Also assign to ‘%1$s’? + Skip + Assign Otwórz ustawienia Powiązane ustawienia + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-pt-rBR/03-dialogs.xml b/AnkiDroid/src/main/res/values-pt-rBR/03-dialogs.xml index 3998cda20c05..b2a6795f0c8c 100644 --- a/AnkiDroid/src/main/res/values-pt-rBR/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-pt-rBR/03-dialogs.xml @@ -49,7 +49,7 @@ Aumentar/diminuir (\"-3\") o limite de revisões de hoje. Revisar cards esquecidos nos últimos x dias: Review cards due in the next: - Visualizar novos cards adicionados nos últimos x dias: + Preview new cards added in the last: Selecione x cards do baralho: Armazenamento cheio Armazenamento quase cheio @@ -78,6 +78,7 @@ Sobrescrever Reparar Substituir + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s O caminho especificado não era um diretório válido O texto fornecido não corresponde a um certificado X509 codificado em PEM Erro na análise do certificado. @@ -86,7 +87,7 @@ Coleções de mídias grandes podem levar muito tempo. Prosseguir? Verificando mídia… Adicionando tags… - Tagging results + Tags added Excluindo mídia… Resultado da exclusão diff --git a/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml b/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml index c27eb862aa63..8ac597d53598 100644 --- a/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pt-rBR/10-preferences.xml @@ -442,9 +442,15 @@ sugestão ou relato de erro.]]> Mostre feedback da resposta Tela - Somente responder - Virar e responder + Also assign to ‘%1$s’? + Skip + Assign Abrir configurações Configurações relacionadas + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-pt-rPT/03-dialogs.xml b/AnkiDroid/src/main/res/values-pt-rPT/03-dialogs.xml index aa9179b1ac67..0ed89c9f6e03 100644 --- a/AnkiDroid/src/main/res/values-pt-rPT/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-pt-rPT/03-dialogs.xml @@ -49,7 +49,7 @@ Aumentar/diminuir (\"-3\") o limite de fichas a rever, de hoje, em Rever fichas esquecidas nos últimos x dias: Review cards due in the next: - Pré-visualizar novas fichas adicionadas nos últimos x dias: + Preview new cards added in the last: Seleccione x fichas do baralho: Armazenamento cheio Armazenamento quase cheio @@ -78,6 +78,7 @@ Substituir Reparar Substituir + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s O caminho especificado não era uma diretoria válida O texto introduzido não é um certificado X509 no formato PEM. Erro ao analisar certificado @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? A verificar multimédia… A adicionar etiquetas… - Tagging results + Tags added A apagar ficheiros de multimédia… Resultado da remoção diff --git a/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml b/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml index ecec07334c40..e5612916a553 100644 --- a/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-pt-rPT/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Abrir as definições Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ro/03-dialogs.xml b/AnkiDroid/src/main/res/values-ro/03-dialogs.xml index bd3d942ded89..fcc707949414 100644 --- a/AnkiDroid/src/main/res/values-ro/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ro/03-dialogs.xml @@ -51,7 +51,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -80,6 +80,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -88,7 +89,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-ro/10-preferences.xml b/AnkiDroid/src/main/res/values-ro/10-preferences.xml index 6ce9ccad30eb..e4822f76030b 100644 --- a/AnkiDroid/src/main/res/values-ro/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ro/10-preferences.xml @@ -447,9 +447,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ru/03-dialogs.xml b/AnkiDroid/src/main/res/values-ru/03-dialogs.xml index c5ec8b15af36..c5b99c9451c5 100644 --- a/AnkiDroid/src/main/res/values-ru/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ru/03-dialogs.xml @@ -53,7 +53,7 @@ Увеличить или уменьшить («-3») лимит повторяемых на сегодня Забытые за сколько дней повторить: Review cards due in the next: - Посмотреть новые за сколько дней: + Preview new cards added in the last: Выберите количество карточек: Хранилище заполнено Хранилище почти заполнено @@ -82,6 +82,7 @@ Перезаписать Исправить Заменить + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Указанный путь не является допустимой папкой Предоставленный текст не преобразуется в допустимый X509 сертификат PEM Ошибка при обработке сертификата @@ -90,7 +91,7 @@ Это может занять много времени с большими коллекциями медиа. Продолжить? Медиафайлы проверяются… Добавление меток… - Tagging results + Tags added Медиафайлы удаляются… Результат удаления diff --git a/AnkiDroid/src/main/res/values-ru/10-preferences.xml b/AnkiDroid/src/main/res/values-ru/10-preferences.xml index d047d5e2377a..1a1e275573f2 100644 --- a/AnkiDroid/src/main/res/values-ru/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ru/10-preferences.xml @@ -448,9 +448,15 @@ отзывах или ошибках.]]> Показать обратную связь по ответу Экран - Только ответ - Перевернуть и ответить + Also assign to ‘%1$s’? + Skip + Assign Открыть настройки Связанные настройки + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-sat/03-dialogs.xml b/AnkiDroid/src/main/res/values-sat/03-dialogs.xml index 416a884bd00f..02cf111f4799 100644 --- a/AnkiDroid/src/main/res/values-sat/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-sat/03-dialogs.xml @@ -49,7 +49,7 @@ ᱵᱚᱰᱷᱟᱣ/ᱠᱚᱢᱢᱮ (“-3”) ᱛᱮᱦᱚᱸᱧᱟᱜᱽ ᱨᱤᱵᱷᱤᱣ ᱞᱤᱢᱤᱴ ᱨᱤᱵᱷᱭᱩ ᱠᱟᱰ x ᱫᱤᱱ ᱨᱮ ᱦᱤᱲᱤᱧᱟᱜ ᱠᱚᱺ Review cards due in the next: - ᱢᱟᱲᱟᱝ xᱫᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫ ᱠᱟᱱ ᱠᱟᱰ ᱠᱚ preview ᱢᱮ + Preview new cards added in the last: Select x cards from the deck: ᱥᱴᱳᱨᱮᱡ ᱯᱩᱨᱤ ᱥᱴᱳᱨᱮᱡ ᱜᱮ ᱯᱩᱨᱤ @@ -78,6 +78,7 @@ ᱚᱞ ᱪᱮᱛᱟᱱ ᱚᱞ ᱡᱩᱛᱨᱟᱹᱣ ᱥᱟᱦᱟ + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s ᱦᱚᱨ ᱨᱮ ᱢᱮᱱᱟᱜ ᱩᱫᱩᱫᱩᱜ ᱵᱟᱭ ᱴᱷᱤᱠ ᱟᱟ ᱮᱢ ᱟᱠᱟᱱ ᱴᱮᱠᱥᱴ ᱫᱚ PEM-encoded X509 ᱥᱟᱨᱴᱤᱯᱷᱤᱠᱮᱴ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱭ ᱦᱟᱹᱴᱤᱧ ᱟᱠᱟᱱᱟ Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? ᱢᱮᱰᱤᱭᱟ ᱧᱮᱞ ᱵᱤᱰᱟᱣᱜ ᱠᱟᱱᱟ… Adding tags… - Tagging results + Tags added ᱢᱤᱰᱤᱭᱟ ᱜᱮᱫ ᱜᱤᱰᱤᱜ ᱠᱟᱱᱟ… ᱜᱮᱫ ᱜᱤᱰᱤ‌ ᱛᱮᱞᱟ diff --git a/AnkiDroid/src/main/res/values-sat/10-preferences.xml b/AnkiDroid/src/main/res/values-sat/10-preferences.xml index 7cde9a3a68be..aebf068a3691 100644 --- a/AnkiDroid/src/main/res/values-sat/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sat/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign ᱚᱯᱮᱱ ᱥᱮᱴᱤᱝ Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-sc/03-dialogs.xml b/AnkiDroid/src/main/res/values-sc/03-dialogs.xml index 377626b4cd13..36962b87940a 100644 --- a/AnkiDroid/src/main/res/values-sc/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-sc/03-dialogs.xml @@ -54,7 +54,7 @@ Ismànnia/mìnima (“-3”) su lìmite de revisiones de oe de Revisiona sas cartas ismentigadas in sas ùrtimas x dies: Review cards due in the next: - Ammustra s\'antiprima de sas cartas noas annànghidas in sas ùrtimas x dies: + Preview new cards added in the last: Select x cards from the deck: Memòria prena Memòria belle prena @@ -83,6 +83,7 @@ Subraiscrie Acontza Remplasa + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s S\'àndala dislindada non fiat una cartella vàlida Su testu frunidu non currispondet a unu tzertificadu X509 codificadu in PEM vàlidu Faddina analizende su tzertificadu @@ -91,7 +92,7 @@ This may take a long time with large media collections. Proceed? Vetifichende sos mèdios… Adding tags… - Tagging results + Tags added Iscantzellende mèdios… Resurtadu de s\'iscantzelladura diff --git a/AnkiDroid/src/main/res/values-sc/10-preferences.xml b/AnkiDroid/src/main/res/values-sc/10-preferences.xml index 31f6b4121ce9..7955989b2cdf 100644 --- a/AnkiDroid/src/main/res/values-sc/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sc/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Aberi sas impostatziones Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-sk/03-dialogs.xml b/AnkiDroid/src/main/res/values-sk/03-dialogs.xml index 1cc5ed864431..ebffb7dde89f 100644 --- a/AnkiDroid/src/main/res/values-sk/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-sk/03-dialogs.xml @@ -53,7 +53,7 @@ Zvýšiť/znížiť („-3”) dnešný limit kartičiek na zopakovanie o Zopakovať si kartičky zabudnuté za posledných x dní: Zopakovať kartičky na skúšanie za: - Ukážka nových kartičiek pridaných za posledných x dní: + Preview new cards added in the last: Vybrať x kartičiek z balíčka: Úložisko je plné Úložisko je takmer plné @@ -82,6 +82,7 @@ Prepísať Opraviť Nahradiť + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Špecifikovaná cesta nebola platným adresárom Zadaný text neodpovedá požadovanému certifikátu X509 kódovanom vo formáte PEM. Chyba pri dekódovaní certifikátu @@ -90,7 +91,7 @@ This may take a long time with large media collections. Proceed? Kontrola multimédií… Adding tags… - Tagging results + Tags added Médiá sa vymazávajú… Výsledok vymazania diff --git a/AnkiDroid/src/main/res/values-sk/10-preferences.xml b/AnkiDroid/src/main/res/values-sk/10-preferences.xml index bb46bd4137c0..7ec9a48b9025 100644 --- a/AnkiDroid/src/main/res/values-sk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sk/10-preferences.xml @@ -451,9 +451,15 @@ spätnú väzbu alebo chyby.]]> Ukázať odozvu odpovede Obrazovka - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Súvisiace nastavenia + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-sl/03-dialogs.xml b/AnkiDroid/src/main/res/values-sl/03-dialogs.xml index a11dd9936706..d39d9c0f91c0 100644 --- a/AnkiDroid/src/main/res/values-sl/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-sl/03-dialogs.xml @@ -53,7 +53,7 @@ Increase/decrease (“-3”) today’s review limit by Preglej kartice, ki so bile v zadnjih x-ih dneh pozabljene: Review cards due in the next: - Preglej nove kartice dodane v zadnjih x-ih dneh: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -82,6 +82,7 @@ Prepiši Popravi Nadomesti + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Navedena pot ni veljavna mapa The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -90,7 +91,7 @@ This may take a long time with large media collections. Proceed? Preverjanje predstavnosti … Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-sl/10-preferences.xml b/AnkiDroid/src/main/res/values-sl/10-preferences.xml index dd53b9297b0c..9be3094b4a61 100644 --- a/AnkiDroid/src/main/res/values-sl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sl/10-preferences.xml @@ -451,9 +451,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-sq/03-dialogs.xml b/AnkiDroid/src/main/res/values-sq/03-dialogs.xml index 496adbae4a06..aea0f6d16223 100644 --- a/AnkiDroid/src/main/res/values-sq/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-sq/03-dialogs.xml @@ -49,7 +49,7 @@ Rrit/zvogëlo (“-3”) kufirin e sotëm të rishikimit me Rishikoni kartat e harruara në x ditët e fundit: Review cards due in the next: - Shikoni paraprakisht kartat e reja të shtuara në x ditët e fundit: + Preview new cards added in the last: Select x cards from the deck: Magazinimi plot Magazinimi pothuajse i mbushur @@ -78,6 +78,7 @@ Mbishkruaj Riparim Zëvendëso + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Rruga e specifikuar nuk ishte një drejtori e vlefshme The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Po kontrollon median… Adding tags… - Tagging results + Tags added Po fshin median… Rezultati i fshirjes diff --git a/AnkiDroid/src/main/res/values-sq/10-preferences.xml b/AnkiDroid/src/main/res/values-sq/10-preferences.xml index 1dc44c50f470..3afa08a1ced7 100644 --- a/AnkiDroid/src/main/res/values-sq/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sq/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-sr/03-dialogs.xml b/AnkiDroid/src/main/res/values-sr/03-dialogs.xml index e61eef533b60..75ad76c00ad7 100644 --- a/AnkiDroid/src/main/res/values-sr/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-sr/03-dialogs.xml @@ -51,7 +51,7 @@ Increase/decrease (“-3”) today’s review limit by Понови заборављено за последњих х дана: Review cards due in the next: - Нове картице, додате за последњих х дана: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -80,6 +80,7 @@ Пресними Обнављање Замени + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Путању коју сте унели није правилна фасцикла The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -88,7 +89,7 @@ This may take a long time with large media collections. Proceed? Проверавање медијске датотеке… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-sr/10-preferences.xml b/AnkiDroid/src/main/res/values-sr/10-preferences.xml index a85df7d90c9d..5d739e5340ea 100644 --- a/AnkiDroid/src/main/res/values-sr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sr/10-preferences.xml @@ -447,9 +447,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-sv/03-dialogs.xml b/AnkiDroid/src/main/res/values-sv/03-dialogs.xml index 735abf1b3b13..43540b076141 100644 --- a/AnkiDroid/src/main/res/values-sv/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-sv/03-dialogs.xml @@ -49,7 +49,7 @@ Öka/minska (“-3”) dagens repetitionsgräns med Repetera kort som är glömda under de senaste x dagarna: Review cards due in the next: - Förhandsgranska nya kort som lagts till de senaste x dagarna: + Preview new cards added in the last: Välj x kort från kortleken: Lagring full Lagring nästan full @@ -78,6 +78,7 @@ Skriv över Reparera Ersätt + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Den valda sökvägen är ogiltig The provided text does not resolve to a valid PEM-encoded X509 certificate Kunde inte tolka certifikatet @@ -86,7 +87,7 @@ Detta kan ta lång tid med stora mediasamlingar. Fortsätt? Kontrollerar media… Lägger till taggar… - Tagging results + Tags added Tar bort media… Raderingsresultat diff --git a/AnkiDroid/src/main/res/values-sv/10-preferences.xml b/AnkiDroid/src/main/res/values-sv/10-preferences.xml index 67bb9c6b932b..f8c360fac321 100644 --- a/AnkiDroid/src/main/res/values-sv/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-sv/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Öppna inställningar Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ta/03-dialogs.xml b/AnkiDroid/src/main/res/values-ta/03-dialogs.xml index e9fb0f6cbdcd..02e2f50297ea 100644 --- a/AnkiDroid/src/main/res/values-ta/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ta/03-dialogs.xml @@ -49,7 +49,7 @@ இன்றைய மதிப்பாய்வு வரம்பை அதிகரிக்கவும்/குறைக்கவும் (\"-3\"). கடந்த x நாட்களில் மறக்கப்பட்ட மதிப்பாய்வு அட்டைகள்: Review cards due in the next: - கடந்த x நாட்களில் சேர்க்கப்பட்ட புதிய கார்டுகளின் மாதிரிக்காட்சி: + Preview new cards added in the last: Select x cards from the deck: சேமிப்பு நிரம்பியது சேமிப்பு கிட்டத்தட்ட நிரம்பிவிட்டது @@ -78,6 +78,7 @@ மேலெழுதவும் பழுது மாற்றம் + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s குறிப்பிடப்பட்ட பாதை சரியான கோப்பகமாக இல்லை வழங்கப்பட்ட உரை சரியான PEM-குறியீடு செய்யப்பட்ட X509 சான்றிதழில் தீர்க்கப்படாது சான்றிதழைப் பாகுபடுத்துவதில் பிழை @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? மீடியாவைச் சரிபார்க்கிறது… Adding tags… - Tagging results + Tags added மீடியாவை நீக்குகிறது… நீக்குதல் முடிவு diff --git a/AnkiDroid/src/main/res/values-ta/10-preferences.xml b/AnkiDroid/src/main/res/values-ta/10-preferences.xml index 296efc5bd8dc..87518c1632d4 100644 --- a/AnkiDroid/src/main/res/values-ta/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ta/10-preferences.xml @@ -442,9 +442,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign அமைப்புகளைத் திறக்கவும் Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-te/03-dialogs.xml b/AnkiDroid/src/main/res/values-te/03-dialogs.xml index 6f53f2635692..12d75bff7b95 100644 --- a/AnkiDroid/src/main/res/values-te/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-te/03-dialogs.xml @@ -49,7 +49,7 @@ నేటి సమీక్ష పరిమితిని పెంచడం/తగ్గించడం (\"-3\"). గత x రోజుల్లో రిజిస్టర్డ్ కార్డులను మర్చిపోయారు: Review cards due in the next: - గత x రోజులలో జోడించిన కొత్త కార్డులను ప్రివ్యూ చేయండి: + Preview new cards added in the last: Select x cards from the deck: నిల్వ నిండింది నిల్వ దాదాపు నిండింది @@ -78,6 +78,7 @@ తిరిగి వ్రాయండి బాగు చేయు పునఃస్థాపించుము + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s పేర్కొన్న మార్గం చెల్లుబాటు అయ్యే డైరెక్టరీ కాదు అందించిన వచనం చెల్లుబాటు అయ్యే PEM-ఎన్‌కోడ్ చేసిన X509 ప్రమాణపత్రానికి పరిష్కారం కాదు సర్టిఫికెట్‌ని అన్వయించడంలో లోపం @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? మీడియాను తనిఖీ చేస్తోంది… Adding tags… - Tagging results + Tags added మీడియాను తీసివేస్తోంది… తొలగింపు ఫలితం diff --git a/AnkiDroid/src/main/res/values-te/10-preferences.xml b/AnkiDroid/src/main/res/values-te/10-preferences.xml index d5152d0e0c57..b0d08e52baf9 100644 --- a/AnkiDroid/src/main/res/values-te/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-te/10-preferences.xml @@ -441,9 +441,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign సెట్టింగ్‌లను తెరవండి Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-tgl/03-dialogs.xml b/AnkiDroid/src/main/res/values-tgl/03-dialogs.xml index 38bcc66cbbb2..ea1411450e68 100644 --- a/AnkiDroid/src/main/res/values-tgl/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-tgl/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Ang mga baraha na sinusuri ay nakalimutan sa huling x na mga araw: Review cards due in the next: - I-prebiyu ang mga bagong baraha na idinagdag sa huling x na mga araw: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ I-overwrite Ayusin Palitan + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Ang path na tinukoy ay hindi isang wastong direktoryo The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Sinusuri ang media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-tgl/10-preferences.xml b/AnkiDroid/src/main/res/values-tgl/10-preferences.xml index 58c122943b05..4bf367512f81 100644 --- a/AnkiDroid/src/main/res/values-tgl/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-tgl/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-th/03-dialogs.xml b/AnkiDroid/src/main/res/values-th/03-dialogs.xml index 10004113e9ea..7a5ec3b6bd2a 100644 --- a/AnkiDroid/src/main/res/values-th/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-th/03-dialogs.xml @@ -47,7 +47,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -76,6 +76,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -84,7 +85,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-th/10-preferences.xml b/AnkiDroid/src/main/res/values-th/10-preferences.xml index 49c1d12e8176..d8bbe0dec24b 100644 --- a/AnkiDroid/src/main/res/values-th/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-th/10-preferences.xml @@ -439,9 +439,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ti/03-dialogs.xml b/AnkiDroid/src/main/res/values-ti/03-dialogs.xml index 4c4f63b3c894..09d2f5c37d8f 100644 --- a/AnkiDroid/src/main/res/values-ti/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ti/03-dialogs.xml @@ -49,7 +49,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -78,6 +78,7 @@ Overwrite Repair Replace + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-ti/10-preferences.xml b/AnkiDroid/src/main/res/values-ti/10-preferences.xml index 96c821077da1..e4052f7f9b26 100644 --- a/AnkiDroid/src/main/res/values-ti/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ti/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-tr/03-dialogs.xml b/AnkiDroid/src/main/res/values-tr/03-dialogs.xml index 32d789138b6c..f0f510a169f8 100644 --- a/AnkiDroid/src/main/res/values-tr/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-tr/03-dialogs.xml @@ -49,7 +49,7 @@ Bugünün gözden geçirme sınırını şu kadar arttırın/azaltın (“-3”) Son x gündeki unutulan kartları izle: Şu kadar zaman içinde sırası gelecek kartları çalış: - Son x günlerdeki eklenen yeni kartları önizle: + Preview new cards added in the last: Desteden x kart seç: Depolama alanı dolu Depolama alanı nerdeyse dolu @@ -78,6 +78,7 @@ Üzerine Yaz Onar Değiştir + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Belirtilen konum geçerli bir dizin değildi. Verilen metin geçerli bir PEM-kodlanmış X509 sertifikasına çözümlenmiyor Sertifika çözümlemesinde hata @@ -86,7 +87,7 @@ Bu, büyük medya koleksiyonlarıyla uzun zaman alabilir. Devam edilsin mi? Medya kontrol ediliyor… Etiketler ekleniyor… - Etiketleme sonuçları + Tags added Medya siliniyor… Silme sonuçları diff --git a/AnkiDroid/src/main/res/values-tr/10-preferences.xml b/AnkiDroid/src/main/res/values-tr/10-preferences.xml index ff277d7e40f8..c3205ec3c8fb 100644 --- a/AnkiDroid/src/main/res/values-tr/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-tr/10-preferences.xml @@ -440,9 +440,15 @@ geri bildiriminizi veya bulduğunuz hataları bildirin.]]> Cevap geribildirimini göster Ekran - Sadece cevap ver - Çevir ve cevap ver + Also assign to ‘%1$s’? + Skip + Assign Ayarları aç İlgili ayarlar + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-tt/03-dialogs.xml b/AnkiDroid/src/main/res/values-tt/03-dialogs.xml index db1a3cbdd300..e6bcc66b3096 100644 --- a/AnkiDroid/src/main/res/values-tt/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-tt/03-dialogs.xml @@ -47,7 +47,7 @@ Increase/decrease (“-3”) today’s review limit by Review cards forgotten in last x days: Review cards due in the next: - Preview new cards added in the last x days: + Preview new cards added in the last: Select x cards from the deck: Storage full Storage almost full @@ -76,6 +76,7 @@ Яңадан язу Төзәтү Алмаштыру + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate Error parsing certificate @@ -84,7 +85,7 @@ This may take a long time with large media collections. Proceed? Checking media… Adding tags… - Tagging results + Tags added Deleting media… Deletion result diff --git a/AnkiDroid/src/main/res/values-tt/10-preferences.xml b/AnkiDroid/src/main/res/values-tt/10-preferences.xml index 1a1619f27da8..302838d3284c 100644 --- a/AnkiDroid/src/main/res/values-tt/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-tt/10-preferences.xml @@ -439,9 +439,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ug/03-dialogs.xml b/AnkiDroid/src/main/res/values-ug/03-dialogs.xml index a295733b7fb0..601708e3a98d 100644 --- a/AnkiDroid/src/main/res/values-ug/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ug/03-dialogs.xml @@ -49,7 +49,7 @@ بۈگۈنكى تەكرارلايدىغان كارتا چېكىنى («3-»)ئاشۇرىدۇ/كېمەيتىدۇ ئۆتكەن x كۈندە ئۇنتۇلغان كارتىنى تەكرارلايدۇ: كېيىنكى كارتىنى تەكرارلاش مۆھلىتى: - يېقىنقى x كۈندە قوشقان يېڭى كارتىنى ئالدىن كۆزىتىدۇ: + Preview new cards added in the last: دەستىدىن x كارتا تاللىنىدۇ: ساقلىغۇچ لىق ساقلىغۇچ ئاساسەن لىق @@ -78,6 +78,7 @@ قاپلا تۈزەت ئالماشتۇر + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s بەلگىلەنگەن يول ئىناۋەتلىك مۇندەرىجە ئەمەس تەمىنلەنگەن تېكىستنى ئىناۋەتلىك PEM كودىنىڭ X509 گۇۋاھنامىسىنى بىر تەرەپ قىلالمىدى گۇۋاھنامە يېشىش خاتالىقى @@ -86,7 +87,7 @@ چوڭ ۋاسىتە توپلىمىغا ئۇزۇن ۋاقىت كېتىشى مۇمكىن. داۋاملاشتۇرامدۇ؟ ۋاسىتە تەكشۈرۈۋاتىدۇ… بەلگە قوش… - بەلگە قويۇش نەتىجىسى + Tags added ۋاسىتە ئۆچۈرۈۋاتىدۇ… ئۆچۈرۈش نەتىجىسى diff --git a/AnkiDroid/src/main/res/values-ug/10-preferences.xml b/AnkiDroid/src/main/res/values-ug/10-preferences.xml index 5ece6e72d399..8c590afe6a10 100644 --- a/AnkiDroid/src/main/res/values-ug/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ug/10-preferences.xml @@ -443,9 +443,15 @@ قايتۇرما ئىنكاس ياكى كەمتۈك دوكلاتى يوللاڭ.]]> جاۋاب قايتۇرما ئىنكاسىنى كۆرسىتىدۇ ئېكران - جاۋابلا - ئۆرۈ ۋە جاۋاب + Also assign to ‘%1$s’? + Skip + Assign تەڭشەكنى ئاچ مۇناسىۋەتلىك تەڭشەك + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-uk/03-dialogs.xml b/AnkiDroid/src/main/res/values-uk/03-dialogs.xml index 6ffdb626bf97..5b63e69f28bb 100644 --- a/AnkiDroid/src/main/res/values-uk/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-uk/03-dialogs.xml @@ -53,7 +53,7 @@ Збільшення/зменшення («-3») сьогоднішнього ліміту на повторення Повторити картки забуті за останні x днів: Review cards due in the next: - Переглянути нові картки додані за останні x днів: + Preview new cards added in the last: Вибрати x карток з колоди: Сховище заповнено Сховище майже заповнено @@ -82,6 +82,7 @@ Перезаписати Виправити Замінити + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Вказаний шлях не є дійсним Наданий текст не перетворюється на дійсний сертифікат X509 з PEM-кодуванням Помилка парсингу сертифікату @@ -90,7 +91,7 @@ У випадку великих медіа-колекцій це може забрати багато часу. Продовжити? Перевіряємо мультимедіа… Додавання тегів… - Tagging results + Tags added Видалення медіа… Результат видалення diff --git a/AnkiDroid/src/main/res/values-uk/10-preferences.xml b/AnkiDroid/src/main/res/values-uk/10-preferences.xml index 1ec86aec3fed..2da28a4fc07d 100644 --- a/AnkiDroid/src/main/res/values-uk/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-uk/10-preferences.xml @@ -450,9 +450,15 @@ відгуки або помилки.]]> Показувати фідбек відповіді Екран - Лише відповідь - Перевернути та відповісти + Also assign to ‘%1$s’? + Skip + Assign Відкрити налаштування Пов\'язані налаштування + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-ur/03-dialogs.xml b/AnkiDroid/src/main/res/values-ur/03-dialogs.xml index 4769c796da3b..161fa7a60e06 100644 --- a/AnkiDroid/src/main/res/values-ur/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-ur/03-dialogs.xml @@ -49,7 +49,7 @@ آج کی نظرثانی کی حد میں اضافہ/کمی (\"-3\") پچھلے دنوں میں بھولے گئے کارڈز کا جائزہ لیں: Review cards due in the next: - پچھلے x دنوں میں شامل کیے گئے نئے کارڈز کا جائزہ لیں: + Preview new cards added in the last: Select x cards from the deck: اسٹوریج بھرا ہوا ہے اسٹوریج تقریباً بھر گیا ہے @@ -78,6 +78,7 @@ اووررائٹ مرمت بدل دیں + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s بیان کردہ راستہ درست ڈائریکٹری نہیں تھی فراہم کردہ متن ایک درست PEM-انکوڈ شدہ X509 سرٹیفکیٹ کو حل نہیں کرتا ہے سرٹیفکیٹ کو پارس کرنے میں خرابی @@ -86,7 +87,7 @@ This may take a long time with large media collections. Proceed? میڈیا کو چیک کیا جا رہا ہے… Adding tags… - Tagging results + Tags added میڈیا کو حذف کیا جا رہا ہے… حذف کرنے کا نتیجہ diff --git a/AnkiDroid/src/main/res/values-ur/10-preferences.xml b/AnkiDroid/src/main/res/values-ur/10-preferences.xml index caabb16a475e..907231caca1b 100644 --- a/AnkiDroid/src/main/res/values-ur/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-ur/10-preferences.xml @@ -443,9 +443,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-uz/03-dialogs.xml b/AnkiDroid/src/main/res/values-uz/03-dialogs.xml index c7b17bcf3b62..f882da29787d 100644 --- a/AnkiDroid/src/main/res/values-uz/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-uz/03-dialogs.xml @@ -49,7 +49,7 @@ Bugungi takrorlanadigan kartalar limitini oshirish/kamaytirish (“-3”) Oʻtgan x kun ichida unutilgan kartalarni takrorlash: Review cards due in the next: - Oxirgi x kunda qoʻshilgan yangi kartalarni koʻrib chiqish: + Preview new cards added in the last: Dastadan x ta karta tanlash: Saqlash xotirasi toʻla Saqlash xotirasi deyarli toʻla @@ -78,6 +78,7 @@ Ustiga yozish Tuzatish Almashtirish + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Koʻrsatilgan yoʻl yaroqli katalog emas Taʼminlangan matn yaroqli PEM bilan kodlangan X509 sertifikatiga mos kelmaydi Sertifikatni tekshirishda xatolik @@ -86,7 +87,7 @@ Juda katta media kolleksiya uchun uzoq vaqt olishi mumkin. Davom etmoqchimisiz? Media fayllar tekshirilmoqda… Teglar qoʻshilmoqda - Tagging results + Tags added Media fayllar oʻchirilmoqda… Oʻchirish natijasi diff --git a/AnkiDroid/src/main/res/values-uz/10-preferences.xml b/AnkiDroid/src/main/res/values-uz/10-preferences.xml index 434e620864e7..50c6218ffd86 100644 --- a/AnkiDroid/src/main/res/values-uz/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-uz/10-preferences.xml @@ -439,9 +439,15 @@ fikr-mulohazalaringiz yoki baglar haqida xabar bering.]]> Javob firk-mulohazalarini koʻrsatish Ekran - Faqat javob - Oʻgirish va javob berish + Also assign to ‘%1$s’? + Skip + Assign Sozlamalarni ochish Aloqador sozlamalar + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-vi/03-dialogs.xml b/AnkiDroid/src/main/res/values-vi/03-dialogs.xml index c9dedf448d5a..f9a5fdbc93f6 100644 --- a/AnkiDroid/src/main/res/values-vi/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-vi/03-dialogs.xml @@ -47,7 +47,7 @@ Tăng / giảm (“-3”) giới hạn ôn tập của ngày hôm nay lên Ôn lại các thẻ đã quên trong x ngày trước: Review cards due in the next: - Xem trước thẻ mới được thêm vào x ngày trước: + Preview new cards added in the last: Chọn x thẻ từ bộ thẻ: Bộ nhớ đầy Bộ nhớ gần đầy @@ -76,6 +76,7 @@ Ghi đè Sửa chữa Thay thế + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s Đường dẫn đã chỉ định không phải thư mục hợp lệ Dòng văn bản được cung cấp không phải là chứng chỉ X509 được mã hóa PEM hợp lệ Lỗi phân tích chứng chỉ @@ -84,7 +85,7 @@ This may take a long time with large media collections. Proceed? Đang kiểm tra tệp đa phương tiện… Adding tags… - Tagging results + Tags added Đang xóa phương tiện… Kết quả xóa diff --git a/AnkiDroid/src/main/res/values-vi/10-preferences.xml b/AnkiDroid/src/main/res/values-vi/10-preferences.xml index 0bad8ec77dbf..febb090715c3 100644 --- a/AnkiDroid/src/main/res/values-vi/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-vi/10-preferences.xml @@ -435,9 +435,15 @@ phản hồi hoặc báo lỗi nếu có.]]> Show answer feedback Screen - Chỉ trả lời - Lật và trả lời + Also assign to ‘%1$s’? + Skip + Assign Mở cài đặt Related settings + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-zh-rCN/03-dialogs.xml b/AnkiDroid/src/main/res/values-zh-rCN/03-dialogs.xml index 13603d846049..9e7d4be1fbbb 100644 --- a/AnkiDroid/src/main/res/values-zh-rCN/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-zh-rCN/03-dialogs.xml @@ -47,7 +47,7 @@ 增加/减少(“-3”)今天的复习卡片限制 复习过去 x 天中忘记的卡片: 复习接下去多久到期的卡片: - 预览过去 x 天中新增的卡片: + Preview new cards added in the last: 从牌组中选择 x 张卡片: 存储空间已满 存储空间快满了 @@ -76,6 +76,7 @@ 覆盖 修复 替换 + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s 指定的路径不是一个有效的目录 提供的文本无法解析为有效的 PEM 编码的 X509 证书 解析证书时发生错误 @@ -84,7 +85,7 @@ 对于大型媒体集合,这可能需要很长时间。继续吗? 正在检查媒体文件… 正在添加标签… - 标记结果 + Tags added 正在删除媒体… 填空结果 diff --git a/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml b/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml index 4300d465afe8..a89e5a4598e9 100644 --- a/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml @@ -438,9 +438,15 @@ 反馈或故障。]]> 显示回答反馈 屏幕 - 仅回答 - 翻转和回答 + Also assign to ‘%1$s’? + Skip + Assign 打开设置 相关设置 + + Not set + Custom path + Enter custom path + Directory is not writable diff --git a/AnkiDroid/src/main/res/values-zh-rTW/03-dialogs.xml b/AnkiDroid/src/main/res/values-zh-rTW/03-dialogs.xml index d0a701db248e..45cc2408ed22 100644 --- a/AnkiDroid/src/main/res/values-zh-rTW/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values-zh-rTW/03-dialogs.xml @@ -47,7 +47,7 @@ 將今天的複習卡片上限新增/減少(“-3”) 過去 x 天內忘記的複習卡: Review cards due in the next: - 預覽過去 x 天新增的卡片: + Preview new cards added in the last: 在牌組中選擇 x 張卡 存儲已滿 存儲空間幾乎滿了 @@ -76,6 +76,7 @@ 覆蓋 修復 取代 + %1$s\n\nCorrect the date and time, or enable automatic date and time, then sync again.\n\nYour device’s current time is: %2$s 所選的路徑不是有效路徑 提供的文字無法解析為有效的 PEM 編碼 X509 憑證 證書解析錯誤 @@ -84,7 +85,7 @@ 由於較大的媒體檔案,這可能會花比較久的時間。確定繼續? 正在檢查媒體… 添加標籤… - Tagging results + Tags added 刪除媒體中… 克漏字結果 diff --git a/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml b/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml index fe8925f17b01..f76a4c218665 100644 --- a/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml +++ b/AnkiDroid/src/main/res/values-zh-rTW/10-preferences.xml @@ -438,9 +438,15 @@ feedback or bugs.]]> Show answer feedback Screen - Only answer - Flip and answer + Also assign to ‘%1$s’? + Skip + Assign Open settings Related settings + + Not set + Custom path + Enter custom path + Directory is not writable From f0d946df90c03f011ecdbd70d39dc8899d3184e0 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Mon, 28 Sep 2026 18:45:07 +0100 Subject: [PATCH 37/43] fix(ci): avoid duplicate scheduled test iterations '1' was already in the list, so it was appended twice Assisted-by: GPT-6 --- .github/workflows/tests_unit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests_unit.yml b/.github/workflows/tests_unit.yml index 989a1bc4d091..60d628eb8479 100644 --- a/.github/workflows/tests_unit.yml +++ b/.github/workflows/tests_unit.yml @@ -83,6 +83,7 @@ jobs: // Goal is to run enough iterations on all systems that we have confidence there are no flakes if (context.eventName === "schedule") { const iterationCount = 15 + iterationArray = [] for (let i = 1; i <= iterationCount; i++) { iterationArray.push(i); } From ea5b8bbe6145a346264c0a2789c4d5517349c0a1 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Mon, 28 Sep 2026 21:26:17 +0100 Subject: [PATCH 38/43] docs: convert licenses to SPDX format Completes `androidTest`: 13 files. One remains due to mixed licenses AnkiDroid is standardizing licenses to SPDX. I removed my copyright lines from the files as-per docs/contributing/copyright-headers.md Issue 20954 This change was not performed using LLMs and I have verified that it is correct --- .../java/com/ichi2/anki/DeckPickerTest.kt | 17 ++--------------- .../java/com/ichi2/anki/TestUtils.kt | 18 +++--------------- .../NoteTypeFieldEditorContextMenuTest.kt | 18 +++--------------- .../com/ichi2/anki/tests/CollectionTest.kt | 18 +++--------------- .../com/ichi2/anki/tests/InstrumentedTest.kt | 17 ++--------------- .../java/com/ichi2/anki/tests/Shared.kt | 18 +++--------------- .../com/ichi2/anki/tests/libanki/MediaTest.kt | 18 +++--------------- .../ichi2/anki/tests/libanki/NotetypeTest.kt | 18 ++---------------- .../com/ichi2/anki/tests/libanki/RetryRule.kt | 19 +++---------------- .../com/ichi2/anki/testutil/DeckPicker.kt | 18 +++--------------- .../ichi2/anki/testutil/IntroductionSlide.kt | 17 ++--------------- .../anki/testutil/StudyOptionsFragment.kt | 17 ++--------------- .../com/ichi2/anki/utils/AssetHelperTest.kt | 16 ++-------------- 13 files changed, 33 insertions(+), 196 deletions(-) diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/DeckPickerTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/DeckPickerTest.kt index 8934159a6657..9374f58c9d43 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/DeckPickerTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/DeckPickerTest.kt @@ -1,18 +1,5 @@ -/* - * Copyright (c) 2021 Shridhar Goel - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2021 Shridhar Goel package com.ichi2.anki diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/TestUtils.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/TestUtils.kt index 74aa8b3f9c80..8682de330239 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/TestUtils.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/TestUtils.kt @@ -1,18 +1,6 @@ -/* - * Copyright (c) 2021 Shridhar Goel - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2021 Shridhar Goel + package com.ichi2.anki import android.app.Activity diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenuTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenuTest.kt index d88d58435187..fc1b5a23b0a9 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenuTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenuTest.kt @@ -1,18 +1,6 @@ -/* - * Copyright (c) 2022 lukstbit - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2022 lukstbit + package com.ichi2.anki.dialogs import android.os.Bundle diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/CollectionTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/CollectionTest.kt index 14e040787cbc..24699ca5947c 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/CollectionTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/CollectionTest.kt @@ -1,18 +1,6 @@ -/* - * Copyright (c) 2015 Timothy Rae - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2015 Timothy Rae + package com.ichi2.anki.tests import androidx.test.ext.junit.runners.AndroidJUnit4 diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/InstrumentedTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/InstrumentedTest.kt index c2e0184ffd9c..ebf64add99c1 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/InstrumentedTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/InstrumentedTest.kt @@ -1,18 +1,5 @@ -/* - * Copyright (c) 2020 Mike Hardy - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2020 Mike Hardy package com.ichi2.anki.tests diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/Shared.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/Shared.kt index 47239169c66d..9f104ec2a93f 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/Shared.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/Shared.kt @@ -1,18 +1,6 @@ -/* - * Copyright (c) 2014 Houssam Salem - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2014 Houssam Salem + package com.ichi2.anki.tests import android.annotation.SuppressLint diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/MediaTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/MediaTest.kt index bd2041b2f4c4..6cb40e63f10d 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/MediaTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/MediaTest.kt @@ -1,18 +1,6 @@ -/* - * Copyright (c) 2014 Houssam Salem - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2014 Houssam Salem + package com.ichi2.anki.tests.libanki import androidx.test.ext.junit.runners.AndroidJUnit4 diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/NotetypeTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/NotetypeTest.kt index 19b2cb9389bc..8e065836efe9 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/NotetypeTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/NotetypeTest.kt @@ -1,19 +1,5 @@ -/* - * - * Copyright (c) 2020 Arthur Milchior - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2020 Arthur Milchior package com.ichi2.anki.tests.libanki diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/RetryRule.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/RetryRule.kt index d669fecbda9d..197dca614985 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/RetryRule.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/libanki/RetryRule.kt @@ -1,19 +1,6 @@ -/* - * - * Copyright (c) 2018 Mike Hardy - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2018 Mike Hardy + package com.ichi2.anki.tests.libanki import org.junit.rules.TestRule diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt index df5502c02f1f..4596ae2348f5 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt @@ -1,18 +1,6 @@ -/* - * Copyright (c) 2024 Arthur Milchior - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2024 Arthur Milchior + package com.ichi2.anki.testutil import android.annotation.SuppressLint diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/IntroductionSlide.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/IntroductionSlide.kt index 43c5d486f31e..be89daa4df15 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/IntroductionSlide.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/IntroductionSlide.kt @@ -1,18 +1,5 @@ -/* - * Copyright (c) 2024 Arthur Milchior - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2024 Arthur Milchior package com.ichi2.anki.testutil diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/StudyOptionsFragment.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/StudyOptionsFragment.kt index f836caf83d82..5cbed83e8946 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/StudyOptionsFragment.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/StudyOptionsFragment.kt @@ -1,18 +1,5 @@ -/* - * Copyright (c) 2024 Arthur Milchior - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2024 Arthur Milchior package com.ichi2.anki.testutil diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/utils/AssetHelperTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/utils/AssetHelperTest.kt index eee5afcd0381..6d168b22540b 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/utils/AssetHelperTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/utils/AssetHelperTest.kt @@ -1,18 +1,6 @@ -/* - Copyright (c) 2021 Piyush Goel +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2021 Piyush Goel - This program is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free Software - Foundation; either version 3 of the License, or (at your option) any later - version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - this program. If not, see . - */ package com.ichi2.anki.utils import androidx.test.ext.junit.runners.AndroidJUnit4 From 2865c46be5a9e1815fa4587904168f52c360f38a Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:56:52 +0100 Subject: [PATCH 39/43] refactor: add CommonString and CommonPlurals resource aliases Typealiases for R.string/R.plurals support references to CrowdIn resources that can move modules with their alias definitions. Plain imports can be inserted automatically by the IDE. Part of 21500 Assisted-by: Claude Fable 5 Assisted-by: GPT-6 - rename to Common[String/Plurals] --- .../main/java/com/ichi2/anki/StringResAliases.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 AnkiDroid/src/main/java/com/ichi2/anki/StringResAliases.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/StringResAliases.kt b/AnkiDroid/src/main/java/com/ichi2/anki/StringResAliases.kt new file mode 100644 index 000000000000..d054529dc01c --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/StringResAliases.kt @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package com.ichi2.anki + +/** + * String resources: `getString(CommonString.card_browser)`. + * + * A plain import (`import com.ichi2.anki.CommonString`), which the IDE auto-inserts, + * unlike `as` alias imports. Files in package `com.ichi2.anki` need no import. + */ +typealias CommonString = com.ichi2.anki.R.string + +/** Plural resources: `getQuantityString(CommonPlurals.widget_cards_due, n, n)`. */ +typealias CommonPlurals = com.ichi2.anki.R.plurals From ac2fd53b4cc5f2755b79ad399929dd9da0cb51e2 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:57:01 +0100 Subject: [PATCH 40/43] refactor: use CommonString and CommonPlurals for CrowdIn strings Convert CrowdIn string and plural references, including usages added since this branch was created. These resources are about to move to :common:android; other strings will remain in :AnkiDroid. Part of 21500 Assisted-by: Claude Fable 5 Assisted-by: GPT-6 - de-conflicted and added more references --- .../CollectionPathMismatchRecoveryTest.kt | 10 +- .../anki/dialogs/DatabaseCorruptDialogTest.kt | 5 +- .../preferences/PreferencesNavigationTest.kt | 5 +- .../java/com/ichi2/anki/tests/ACRATest.kt | 14 +- .../com/ichi2/anki/testutil/DeckPicker.kt | 5 +- .../com/ichi2/anki/AbstractFlashcardViewer.kt | 40 ++--- .../java/com/ichi2/anki/AcraCrashReporter.kt | 12 +- .../java/com/ichi2/anki/AndroidTtsPlayer.kt | 2 +- .../main/java/com/ichi2/anki/AnkiActivity.kt | 30 ++-- .../main/java/com/ichi2/anki/AnkiDroidApp.kt | 2 +- .../java/com/ichi2/anki/AnkiDroidJsAPI.kt | 16 +- .../com/ichi2/anki/BottomNavController.kt | 12 +- .../main/java/com/ichi2/anki/CardBrowser.kt | 14 +- .../CardTemplateBrowserAppearanceEditor.kt | 8 +- .../java/com/ichi2/anki/CardTemplateEditor.kt | 70 ++++---- .../anki/CollectionIntegrityStorageCheck.kt | 10 +- .../java/com/ichi2/anki/CoroutineHelpers.kt | 12 +- .../main/java/com/ichi2/anki/DeckPicker.kt | 92 +++++----- .../main/java/com/ichi2/anki/FieldEditText.kt | 2 +- .../src/main/java/com/ichi2/anki/Info.kt | 4 +- .../java/com/ichi2/anki/InitialActivity.kt | 4 +- .../main/java/com/ichi2/anki/IntentHandler.kt | 12 +- .../java/com/ichi2/anki/IntentHandler2.kt | 2 +- .../java/com/ichi2/anki/MediaRegistration.kt | 24 +-- .../main/java/com/ichi2/anki/MoreFragment.kt | 8 +- .../ichi2/anki/NavigationDrawerActivity.kt | 16 +- .../java/com/ichi2/anki/NoteEditorActivity.kt | 2 +- .../java/com/ichi2/anki/NoteEditorFragment.kt | 92 +++++----- .../com/ichi2/anki/NoteTypeFieldEditor.kt | 44 ++--- .../com/ichi2/anki/NotificationChannel.kt | 4 +- .../src/main/java/com/ichi2/anki/ReadText.kt | 20 +-- .../src/main/java/com/ichi2/anki/Reviewer.kt | 56 +++--- .../com/ichi2/anki/StudyOptionsFragment.kt | 12 +- .../src/main/java/com/ichi2/anki/Sync.kt | 14 +- .../com/ichi2/anki/account/LoginFragment.kt | 11 +- .../com/ichi2/anki/account/LoginViewModel.kt | 6 +- .../analytics/AnkiDroidCrashReportDialog.kt | 7 +- .../ExitViaDoubleTapBackBackPressCallback.kt | 3 +- .../browser/BrowserColumnSelectionAdapter.kt | 3 +- .../browser/BrowserColumnSelectionFragment.kt | 5 +- .../ichi2/anki/browser/CardBrowserColumn.kt | 14 +- .../ichi2/anki/browser/CardBrowserFragment.kt | 100 ++++++----- .../browser/ColumnSelectionDialogFragment.kt | 7 +- .../browser/FindAndReplaceDialogFragment.kt | 9 +- .../anki/browser/RepositionCardFragment.kt | 4 +- .../browser/search/AdvancedSearchFragment.kt | 3 +- .../search/SortOrderBottomSheetFragment.kt | 17 +- .../browser/search/StandardSearchFragment.kt | 5 +- .../ichi2/anki/browser/search/ui/SortPill.kt | 15 +- .../java/com/ichi2/anki/cardviewer/Gesture.kt | 38 ++-- .../cardviewer/OnRenderProcessGoneDelegate.kt | 18 +- .../com/ichi2/anki/cardviewer/TypeAnswer.kt | 6 +- .../ichi2/anki/deckpicker/BackgroundImage.kt | 8 +- .../ichi2/anki/dialogs/BackupPromptDialog.kt | 23 +-- .../anki/dialogs/BrowserOptionsDialog.kt | 5 +- .../anki/dialogs/CardSideSelectionDialog.kt | 16 +- .../anki/dialogs/ChangeNoteTypeDialog.kt | 7 +- .../ichi2/anki/dialogs/ConfirmationDialog.kt | 5 +- .../ichi2/anki/dialogs/CreateDeckDialog.kt | 19 +- .../ichi2/anki/dialogs/DatabaseErrorDialog.kt | 169 +++++++++--------- .../dialogs/DeckPickerAnalyticsOptInDialog.kt | 10 +- .../DeckPickerBackupNoSpaceLeftDialog.kt | 8 +- .../DeckPickerConfirmDeleteDeckDialog.kt | 12 +- .../anki/dialogs/DeckPickerContextMenu.kt | 16 +- .../dialogs/DeckPickerNoSpaceLeftDialog.kt | 8 +- .../ichi2/anki/dialogs/DeckSelectionDialog.kt | 17 +- .../com/ichi2/anki/dialogs/DialogHandler.kt | 4 +- .../anki/dialogs/DiscardChangesDialog.kt | 4 +- .../anki/dialogs/EditDeckDescriptionDialog.kt | 9 +- .../anki/dialogs/EmptyCardsDialogFragment.kt | 18 +- .../ichi2/anki/dialogs/ExportReadyDialog.kt | 8 +- .../ichi2/anki/dialogs/FatalErrorDialog.kt | 8 +- .../anki/dialogs/FieldSelectionDialog.kt | 4 +- .../ichi2/anki/dialogs/FlagRenameDialog.kt | 4 +- .../com/ichi2/anki/dialogs/GradeNowDialog.kt | 5 +- .../com/ichi2/anki/dialogs/ImportDialog.kt | 22 +-- .../dialogs/ImportFileSelectionFragment.kt | 10 +- .../ichi2/anki/dialogs/InsertFieldDialog.kt | 25 +-- .../com/ichi2/anki/dialogs/IntegerDialog.kt | 7 +- .../anki/dialogs/LoadingDialogFragment.kt | 7 +- .../dialogs/NoteTypeFieldEditorContextMenu.kt | 12 +- .../SaveBrowserSearchDialogFragment.kt | 13 +- .../SavedBrowserSearchesDialogFragment.kt | 10 +- .../anki/dialogs/SchedulerUpgradeDialog.kt | 7 +- .../ichi2/anki/dialogs/SimpleMessageDialog.kt | 3 +- .../com/ichi2/anki/dialogs/SyncErrorDialog.kt | 77 ++++---- .../anki/dialogs/TtsPlaybackErrorDialog.kt | 11 +- .../anki/dialogs/TtsVoicesDialogFragment.kt | 9 +- .../anki/dialogs/WhiteBoardWidthDialog.kt | 8 +- .../dialogs/customstudy/CustomStudyDialog.kt | 26 +-- .../dialogs/customstudy/TagLimitFragment.kt | 6 +- .../com/ichi2/anki/dialogs/help/HelpDialog.kt | 57 +++--- .../dialogs/help/HelpItemActionsDispatcher.kt | 8 +- .../anki/dialogs/tags/TagsArrayAdapter.kt | 4 +- .../com/ichi2/anki/dialogs/tags/TagsDialog.kt | 29 +-- .../ichi2/anki/export/ExportDialogFragment.kt | 3 +- .../filtered/FilteredDeckOptionsFragment.kt | 23 +-- .../anki/instantnoteeditor/EditorChips.kt | 6 +- .../InstantNoteEditorActivity.kt | 25 +-- .../ichi2/anki/jsaddons/TgzPackageExtract.kt | 22 +-- .../anki/mediacheck/MediaCheckFragment.kt | 24 +-- .../anki/multimedia/AudioRecordingFragment.kt | 7 +- .../anki/multimedia/AudioVideoFragment.kt | 9 +- .../anki/multimedia/MultimediaFragment.kt | 8 +- .../multimedia/MultimediaImageFragment.kt | 25 +-- .../audio/AudioRecordingController.kt | 19 +- .../com/ichi2/anki/noteeditor/FieldState.kt | 4 +- .../NoteEditorMultimediaController.kt | 6 +- .../java/com/ichi2/anki/noteeditor/Toolbar.kt | 7 +- .../ichi2/anki/notetype/AddNewNotesType.kt | 12 +- .../ichi2/anki/notetype/ManageNotetypes.kt | 32 ++-- .../ichi2/anki/notetype/NoteTypesAdapter.kt | 3 +- .../anki/notetype/RenameCardTypeDialog.kt | 9 +- .../notetype/RepositionCardTemplateDialog.kt | 5 +- .../java/com/ichi2/anki/pages/CongratsPage.kt | 11 +- .../java/com/ichi2/anki/pages/DeckOptions.kt | 3 +- .../com/ichi2/anki/pages/PageChromeClient.kt | 8 +- .../ichi2/anki/pages/RemoveAccountFragment.kt | 3 +- .../java/com/ichi2/anki/pages/Statistics.kt | 3 +- .../ichi2/anki/preferences/AboutFragment.kt | 19 +- .../preferences/AdvancedSettingsFragment.kt | 33 ++-- .../preferences/AppearanceSettingsFragment.kt | 23 +-- .../preferences/ControlsSettingsFragment.kt | 25 +-- .../CustomButtonsSettingsFragment.kt | 7 +- .../CustomSyncServerSettingsFragment.kt | 9 +- .../preferences/DeveloperOptionsFragment.kt | 15 +- .../preferences/GeneralSettingsFragment.kt | 18 +- .../ichi2/anki/preferences/HeaderFragment.kt | 51 +++--- .../anki/preferences/SyncSettingsFragment.kt | 15 +- .../preferences/profiles/AddProfileDialog.kt | 12 +- .../profiles/SwitchProfilesScreen.kt | 9 +- .../preferences/reviewer/MenuDisplayType.kt | 8 +- .../anki/preferences/reviewer/ViewerAction.kt | 41 ++--- .../anki/previewer/CardViewerFragment.kt | 27 +-- .../ichi2/anki/previewer/PreviewerFragment.kt | 9 +- .../previewer/TemplatePreviewerFragment.kt | 5 +- .../anki/previewer/TemplatePreviewerPage.kt | 3 +- .../anki/receiver/CopyToClipboardReceiver.kt | 4 +- .../ichi2/anki/reviewer/ReviewerBinding.kt | 6 +- .../reviewreminders/AddEditReminderDialog.kt | 21 +-- .../ReminderTroubleshootingFragment.kt | 61 +++---- .../ReviewRemindersDatabase.kt | 4 +- .../ScheduleRemindersAdapter.kt | 3 +- .../ScheduleRemindersFragment.kt | 15 +- .../anki/scheduling/ForgetCardsDialog.kt | 10 +- .../ichi2/anki/scheduling/SetDueDateDialog.kt | 34 ++-- .../anki/scheduling/SetDueDateViewModel.kt | 5 +- .../com/ichi2/anki/security/AppPermissions.kt | 5 +- .../anki/servicelayer/SchedulerService.kt | 4 +- .../com/ichi2/anki/services/BootService.kt | 5 +- .../anki/services/NotificationService.kt | 17 +- .../anki/shareddeck/SharedDecksActivity.kt | 7 +- .../shareddeck/SharedDecksDownloadFragment.kt | 21 +-- .../com/ichi2/anki/sync/MeteredSyncPolicy.kt | 10 +- .../java/com/ichi2/anki/sync/SchemaChanges.kt | 14 +- .../anki/ui/dialogs/tools/AsyncDialogs.kt | 4 +- .../screens/BackupLimitsPresenter.kt | 11 +- .../anki/ui/windows/managespace/FileUtils.kt | 4 +- .../managespace/ManageSpaceFragment.kt | 58 +++--- .../AllPermissionsExplanationFragment.kt | 3 +- ...roidPermanentlyRevokedPermissionsDialog.kt | 6 +- .../LegacyNotificationsPermissionFragment.kt | 3 +- .../permissions/PermissionsActivity.kt | 3 +- .../permissions/PermissionsFragment.kt | 6 +- .../permissions/PermissionsUntil29Fragment.kt | 3 +- .../ui/windows/reviewer/ReviewerFragment.kt | 7 +- .../anki/ui/windows/reviewer/ReviewerMenu.kt | 15 +- .../reviewer/audiorecord/AudioPlayView.kt | 3 +- .../audiorecord/CheckPronunciationFragment.kt | 9 +- .../reviewer/whiteboard/ColorPickerDialog.kt | 7 +- .../reviewer/whiteboard/WhiteboardFragment.kt | 11 +- .../java/com/ichi2/anki/utils/AndroidUtils.kt | 12 +- .../java/com/ichi2/anki/utils/Exceptions.kt | 4 +- .../main/java/com/ichi2/anki/utils/Time.kt | 8 +- .../com/ichi2/anki/widgets/DeckAdapter.kt | 5 +- .../AppLoadedFromBackupWorkaround.kt | 4 +- .../com/ichi2/anki/worker/SyncMediaWorker.kt | 5 +- .../java/com/ichi2/anki/worker/SyncWorker.kt | 7 +- .../customtabs/CustomTabActivityHelper.kt | 4 +- .../compat/customtabs/CustomTabsFallback.kt | 4 +- .../compose/ui/dialogs/TextInputDialog.kt | 4 +- .../com/ichi2/imagecropper/ImageCropper.kt | 5 +- .../ichi2/preferences/ControlPreference.kt | 23 +-- .../ExternalDirectorySelectionPreference.kt | 11 +- .../IncrementerNumberRangePreferenceCompat.kt | 8 +- .../java/com/ichi2/ui/AccessibleSearchView.kt | 4 +- .../main/java/com/ichi2/ui/GesturePicker.kt | 4 +- .../java/com/ichi2/utils/AlarmManagement.kt | 6 +- .../java/com/ichi2/utils/AlertDialogFacade.kt | 2 +- .../java/com/ichi2/utils/ClipboardUtil.kt | 6 +- .../java/com/ichi2/utils/ExceptionUtil.kt | 4 +- .../main/java/com/ichi2/utils/ImportUtils.kt | 25 +-- .../main/java/com/ichi2/utils/IntentUtil.kt | 6 +- .../main/java/com/ichi2/utils/LanguageUtil.kt | 4 +- .../main/java/com/ichi2/utils/Permissions.kt | 8 +- .../main/java/com/ichi2/utils/WebViewUtils.kt | 5 +- .../com/ichi2/widget/AnkiDroidWidgetSmall.kt | 5 +- .../widget/cardanalysis/CardAnalysisWidget.kt | 5 +- .../cardanalysis/CardAnalysisWidgetConfig.kt | 7 +- .../widget/deckpicker/DeckPickerWidget.kt | 5 +- .../deckpicker/DeckPickerWidgetConfig.kt | 16 +- .../com/ichi2/anki/CardTemplateEditorTest.kt | 16 +- .../java/com/ichi2/anki/DeckPickerTest.kt | 10 +- .../java/com/ichi2/anki/NoteEditorTest.kt | 6 +- .../src/test/java/com/ichi2/anki/SyncTest.kt | 8 +- .../anki/dialogs/CreateDeckDialogTest.kt | 20 +-- .../anki/dialogs/DeckPickerContextMenuTest.kt | 13 +- .../ImportFileSelectionFragmentTest.kt | 10 +- .../ichi2/anki/dialogs/help/HelpDialogTest.kt | 67 +++---- .../anki/progress/ProgressObserverTest.kt | 3 +- ...eminderTroubleshootingResolveActionTest.kt | 4 +- .../permissions/PermissionsActivityTest.kt | 3 +- .../reviewer/audiorecord/AudioPlayViewTest.kt | 7 +- .../audiorecord/AudioRecordViewTest.kt | 4 +- .../com/ichi2/anki/widget/DeckAdapterTest.kt | 4 +- .../CardAnalysisWidgetConfigTest.kt | 5 +- .../java/com/ichi2/utils/ClipboardUtilTest.kt | 8 +- .../java/com/ichi2/utils/ImportUtilsTest.kt | 4 +- 218 files changed, 1615 insertions(+), 1482 deletions(-) diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/CollectionPathMismatchRecoveryTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/CollectionPathMismatchRecoveryTest.kt index 9374bffe74f9..12a8f21ee4fa 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/CollectionPathMismatchRecoveryTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/CollectionPathMismatchRecoveryTest.kt @@ -13,8 +13,8 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker -import com.ichi2.anki.R import com.ichi2.anki.common.preferences.sharedPrefs import com.ichi2.anki.common.storage.CollectionHelper import com.ichi2.anki.startup.getDefaultAnkiDroidDirectory @@ -61,14 +61,14 @@ class CollectionPathMismatchRecoveryTest : InstrumentedTest() { @Test fun errorHandlingShowsUseDefaultFolderOption() { - onView(withText(R.string.open_collection_failed_title)) + onView(withText(CommonString.open_collection_failed_title)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.error_handling_options)).inRoot(isDialog()).perform(click()) - onView(withText(R.string.error_handling_title)) + onView(withText(CommonString.error_handling_options)).inRoot(isDialog()).perform(click()) + onView(withText(CommonString.error_handling_title)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.backup_use_default_folder)) + onView(withText(CommonString.backup_use_default_folder)) .inRoot(isDialog()) .check(matches(isDisplayed())) } diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/DatabaseCorruptDialogTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/DatabaseCorruptDialogTest.kt index dc93f9006267..668f7255784d 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/DatabaseCorruptDialogTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/dialogs/DatabaseCorruptDialogTest.kt @@ -11,6 +11,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.R import com.ichi2.anki.backend.DatabaseCorruption @@ -55,7 +56,7 @@ class DatabaseCorruptDialogTest : InstrumentedTest() { @Test fun testOpenCollectionFailedDialog() { - onView(withText(R.string.open_collection_failed_title)) + onView(withText(CommonString.open_collection_failed_title)) .inRoot(isDialog()) .check(matches(isDisplayed())) } @@ -64,7 +65,7 @@ class DatabaseCorruptDialogTest : InstrumentedTest() { fun testCorruptCollectionDialog() { val corruptMsg = testContext.getString( - R.string.corrupt_db_message, + CommonString.corrupt_db_message, testContext.getString(R.string.repair_deck), ) onView(withText(corruptMsg)) diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesNavigationTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesNavigationTest.kt index 296051cbd112..ae0ca7d6ba09 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesNavigationTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesNavigationTest.kt @@ -23,6 +23,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withResourceName import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.CommonString import com.ichi2.anki.IntentHandler import com.ichi2.anki.R import com.ichi2.anki.testutil.GrantStoragePermission @@ -82,9 +83,9 @@ class PreferencesNavigationTest { openSettings() onView(withId(com.bytehamster.lib.preferencesearch.R.id.search)).perform(click()) onView(allOf(withId(com.bytehamster.lib.preferencesearch.R.id.search), hasFocus())).perform(typeText("Card")) - onView(withText(R.string.card_zoom)).perform(click()) + onView(withText(CommonString.card_zoom)).perform(click()) onView(withId(R.id.settings_container)).check(matches(isDisplayed())) - onView(withText(R.string.notification_pref)).perform(click()) + onView(withText(CommonString.notification_pref)).perform(click()) pressBack() onView(withClassName(endsWith("PreferencesActivity"))).check(doesNotExist()) } diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt index b285a85a1671..a414f17acfbe 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt @@ -10,7 +10,7 @@ import androidx.core.content.edit import androidx.test.annotation.UiThreadTest import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.acraCoreConfigBuilder import com.ichi2.anki.analytics.AnalyticsExceptionHandler import com.ichi2.anki.analytics.AnkiDroidUsageAnalytics @@ -111,7 +111,7 @@ class ACRATest : InstrumentedTest() { setProductionACRAConfig(sharedPrefs) verifyACRANotDisabled() - assertToastMessage(R.string.feedback_for_manual_toast_text) + assertToastMessage(CommonString.feedback_for_manual_toast_text) assertToastIsEnabled() assertDialogEnabledStatus("Dialog should be enabled", true) } @@ -188,7 +188,7 @@ class ACRATest : InstrumentedTest() { setProductionACRAConfig(sharedPrefs) verifyACRANotDisabled() - assertToastMessage(R.string.feedback_auto_toast_text) + assertToastMessage(CommonString.feedback_auto_toast_text) assertToastIsEnabled() assertDialogEnabledStatus("Dialog should not be enabled", false) } @@ -204,12 +204,12 @@ class ACRATest : InstrumentedTest() { verifyACRANotDisabled() assertDialogEnabledStatus("dialog should be disabled when status is ALWAYS", false) - assertToastMessage(R.string.feedback_auto_toast_text) + assertToastMessage(CommonString.feedback_auto_toast_text) setAcraReportingMode(FEEDBACK_REPORT_ASK) assertDialogEnabledStatus("dialog should be re-enabled after changed to ASK", true) - assertToastMessage(R.string.feedback_for_manual_toast_text) + assertToastMessage(CommonString.feedback_for_manual_toast_text) } @Test @@ -222,11 +222,11 @@ class ACRATest : InstrumentedTest() { setProductionACRAConfig(sharedPrefs) verifyACRANotDisabled() - assertToastMessage(R.string.feedback_for_manual_toast_text) + assertToastMessage(CommonString.feedback_for_manual_toast_text) setAcraReportingMode(FEEDBACK_REPORT_ALWAYS) - assertToastMessage(R.string.feedback_auto_toast_text) + assertToastMessage(CommonString.feedback_auto_toast_text) } @Test diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt index 4596ae2348f5..5349b53595f0 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/testutil/DeckPicker.kt @@ -13,6 +13,7 @@ import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.TestUtils.clickChildViewWithId import com.ichi2.anki.tests.checkWithTimeout @@ -27,7 +28,7 @@ fun closeGetStartedScreenIfExists() { } fun closeBackupCollectionDialogIfExists() { - onView(withText(R.string.button_backup_later)) + onView(withText(CommonString.button_backup_later)) .withFailureHandler { _, _ -> } .perform(click()) } @@ -51,7 +52,7 @@ fun createDeckWithUniqueName(): String { onView(withId(R.id.fab_main)).perform(click()) onView(withId(R.id.add_deck_button)).perform(click()) onView(withId(R.id.dialog_text_input)).perform(typeText(deckName)) - onView(withText(R.string.dialog_ok)).perform(click()) + onView(withText(CommonString.dialog_ok)).perform(click()) return deckName } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt index 85b09a1f79ed..67c448947a0d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt @@ -821,16 +821,16 @@ abstract class AbstractFlashcardViewer : protected fun showDeleteNoteDialog() { Timber.i("Displaying 'delete note' dialog") AlertDialog.Builder(this).show { - title(R.string.delete_card_title) + title(CommonString.delete_card_title) setIcon(R.drawable.ic_warning) message( text = resources.getString( - R.string.delete_note_message, + CommonString.delete_note_message, stripHTMLAndSpecialFields(currentCard!!.question(getColUnsafe, true)).trim(), ), ) - positiveButton(R.string.dialog_positive_delete) { + positiveButton(CommonString.dialog_positive_delete) { Timber.i( "AbstractFlashcardViewer:: OK button pressed to delete note %d", currentCard!!.nid, @@ -838,7 +838,7 @@ abstract class AbstractFlashcardViewer : launchCatchingTask { stopCardMediaPlayer() } deleteNoteWithoutConfirmation() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } @@ -854,12 +854,12 @@ abstract class AbstractFlashcardViewer : } val deletedMessage = resources.getQuantityString( - R.plurals.card_browser_cards_deleted, + CommonPlurals.card_browser_cards_deleted, noteCount, noteCount, ) showSnackbar(deletedMessage, Snackbar.LENGTH_LONG) { - setAction(R.string.undo) { launchCatchingTask { undoAndShowSnackbar() } } + setAction(CommonString.undo) { launchCatchingTask { undoAndShowSnackbar() } } } } } @@ -1613,7 +1613,7 @@ abstract class AbstractFlashcardViewer : } } stopCardMediaPlayer() - showSnackbar(R.string.card_buried, Reviewer.ACTION_SNACKBAR_TIME) + showSnackbar(CommonString.card_buried, Reviewer.ACTION_SNACKBAR_TIME) } return true } @@ -1642,7 +1642,7 @@ abstract class AbstractFlashcardViewer : } } val count = changed.count - val noteSuspended = resources.getQuantityString(R.plurals.note_suspended, count, count) + val noteSuspended = resources.getQuantityString(CommonPlurals.note_suspended, count, count) stopCardMediaPlayer() showSnackbar(noteSuspended, Reviewer.ACTION_SNACKBAR_TIME) } @@ -1895,12 +1895,12 @@ abstract class AbstractFlashcardViewer : showSnackbar( getString( - R.string.show_answer_hint_long_press, - getString(R.string.pref_show_answer_long_press_time), + CommonString.show_answer_hint_long_press, + getString(CommonString.pref_show_answer_long_press_time), ), minimalClickSpeed + Reviewer.ACTION_SNACKBAR_TIME, ) { - setAction(R.string.settings) { + setAction(CommonString.settings) { navigate(PreferencesDestination.Accessibility) } } @@ -2471,13 +2471,13 @@ abstract class AbstractFlashcardViewer : } if (url.startsWith("missing-user-action:")) { val actionNumber = url.substringAfter(":") - val message = getString(R.string.missing_user_action_dialog_message, actionNumber) + val message = getString(CommonString.missing_user_action_dialog_message, actionNumber) Timber.i("showing 'missing user action' dialog") AlertDialog.Builder(this@AbstractFlashcardViewer).show { - setTitle(R.string.vague_error) + setTitle(CommonString.vague_error) setMessage(message) - setPositiveButton(R.string.dialog_ok) { _, _ -> } - setNeutralButton(R.string.help) { _, _ -> + setPositiveButton(CommonString.dialog_ok) { _, _ -> } + setNeutralButton(CommonString.help) { _, _ -> openUrl(R.string.link_user_actions_help) } } @@ -2638,7 +2638,7 @@ abstract class AbstractFlashcardViewer : startActivity(intent) } catch (_: ActivityNotFoundException) { Timber.w("No app found to handle open external url from AbstractFlashcardViewer") - showSnackbar(R.string.activity_start_failed) + showSnackbar(CommonString.activity_start_failed) } return true } @@ -2703,7 +2703,7 @@ abstract class AbstractFlashcardViewer : Timber.e(e, "Exception decoding: '%s'", url) showThemedToast( this@AbstractFlashcardViewer, - getString(R.string.card_viewer_url_decode_error), + getString(CommonString.card_viewer_url_decode_error), true, ) } @@ -2715,8 +2715,8 @@ abstract class AbstractFlashcardViewer : } internal fun displayCouldNotFindMediaSnackbar(filename: String?) { - showSnackbar(getString(R.string.card_viewer_could_not_find_image, filename)) { - setAction(R.string.help) { openUrl(R.string.link_faq_missing_media) } + showSnackbar(getString(CommonString.card_viewer_could_not_find_image, filename)) { + setAction(CommonString.help) { openUrl(R.string.link_faq_missing_media) } } } @@ -2864,7 +2864,7 @@ abstract class AbstractFlashcardViewer : is AndroidTtsError.MissingVoiceError -> TtsPlaybackErrorDialog.ttsPlaybackErrorDialog(activity, supportFragmentManager, error.tag) is AndroidTtsError.InvalidVoiceError -> - activity.showSnackbar(getString(R.string.voice_not_supported)) + activity.showSnackbar(getString(CommonString.voice_not_supported)) else -> activity.showSnackbar(error.localizedErrorMessage(activity)) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt index 1721ce5faefa..f09e090f34b6 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt @@ -70,8 +70,8 @@ private object AcraCrashReporter : CrashReporter { private enum class ToastType( @StringRes private val toastMessageRes: Int, ) { - AUTO_TOAST(R.string.feedback_auto_toast_text), - MANUAL_TOAST(R.string.feedback_for_manual_toast_text), + AUTO_TOAST(CommonString.feedback_auto_toast_text), + MANUAL_TOAST(CommonString.feedback_for_manual_toast_text), ; fun getToastMessage(context: Context) = context.getString(toastMessageRes) @@ -114,10 +114,10 @@ private object AcraCrashReporter : CrashReporter { .withPluginConfigurations( DialogConfigurationBuilder() .withReportDialogClass(AnkiDroidCrashReportDialog::class.java) - .withCommentPrompt(application.getString(R.string.empty_string)) - .withTitle(application.getString(R.string.feedback_title)) - .withText(application.getString(R.string.feedback_default_text)) - .withPositiveButtonText(application.getString(R.string.feedback_report)) + .withCommentPrompt(application.getString(CommonString.empty_string)) + .withTitle(application.getString(CommonString.feedback_title)) + .withText(application.getString(CommonString.feedback_default_text)) + .withPositiveButtonText(application.getString(CommonString.feedback_report)) .withResIcon(R.drawable.logo_star_144dp) .withEnabled(dialogEnabled) .build(), diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AndroidTtsPlayer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AndroidTtsPlayer.kt index a7f19d5df1fe..03a94c8c82fe 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AndroidTtsPlayer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AndroidTtsPlayer.kt @@ -299,7 +299,7 @@ fun TtsPlayer.TtsError.localizedErrorMessage(context: Context): String = // TODO: Do we want a human readable string here as well - snackbar has limited room // but developerString is currently not translated as it returns // developerString: ERROR_NETWORK_TIMEOUT, so "Audio error (ERROR_NETWORK_TIMEOUT)" - context.getString(R.string.tts_voices_playback_error_new, this.developerString) + context.getString(CommonString.tts_voices_playback_error_new, this.developerString) } else { this.toString() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt index 702ea1c6559b..8a2691a03cac 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt @@ -297,7 +297,7 @@ open class AnkiActivity( } protected fun onSdCardNotMounted() { - showThemedToast(this, resources.getString(R.string.sd_card_not_mounted), false) + showThemedToast(this, resources.getString(CommonString.sd_card_not_mounted), false) finish() } @@ -472,7 +472,7 @@ open class AnkiActivity( */ open fun openUrl(url: Uri) { if (!AdaptionUtil.hasWebBrowser(this)) { - showSnackbar(getString(R.string.no_browser_msg, url.toString())) + showSnackbar(getString(CommonString.no_browser_msg, url.toString())) return } val toolbarColor = MaterialColors.getColor(this, CommonR.attr.appBarColor, 0) @@ -691,9 +691,9 @@ open class AnkiActivity( val generalShortcutGroup = ShortcutGroup( listOf( - shortcut("Ctrl+Z", R.string.undo), + shortcut("Ctrl+Z", CommonString.undo), ), - R.string.pref_cat_general, + CommonString.pref_cat_general, ).toShortcutGroup(this) return listOfNotNull(shortcuts?.toShortcutGroup(this), generalShortcutGroup) @@ -718,7 +718,7 @@ open class AnkiActivity( val attachment = File(path) if (!attachment.exists()) { Timber.e("Specified apkg file %s does not exist", path) - showThemedToast(this, resources.getString(R.string.apk_share_error), false) + showThemedToast(this, resources.getString(CommonString.apk_share_error), false) return } val authority = "${this.packageName}.apkgfileprovider" @@ -729,7 +729,7 @@ open class AnkiActivity( FileProvider.getUriForFile(this, authority, attachment) } catch (e: IllegalArgumentException) { Timber.e(e, "Could not generate a valid URI for the apkg file") - showThemedToast(this, resources.getString(R.string.apk_share_error), false) + showThemedToast(this, resources.getString(CommonString.apk_share_error), false) return } val targetMimeType = if (asText) "text/plain" else "application/apkg" @@ -739,10 +739,10 @@ open class AnkiActivity( .IntentBuilder(this) .setType(targetMimeType) .setStream(uri) - .setSubject(getString(R.string.export_email_subject, attachment.name)) + .setSubject(getString(CommonString.export_email_subject, attachment.name)) .setHtmlText( getString( - R.string.export_email_text, + CommonString.export_email_text, getString(R.string.link_manual), getString(R.string.link_distributions), ), @@ -754,13 +754,13 @@ open class AnkiActivity( val shareFileIntent = Intent.createChooser( sendIntent, - getString(R.string.export_share_title), + getString(CommonString.export_share_title), ) if (shareFileIntent.resolveActivity(packageManager) != null) { startActivity(shareFileIntent) } else { // Try to save it? - showSnackbar(R.string.export_send_no_handlers) + showSnackbar(CommonString.export_send_no_handlers) saveExportFile(path) } } @@ -770,7 +770,7 @@ open class AnkiActivity( val attachment = File(exportPath) if (!attachment.exists()) { Timber.e("saveExportFile() Specified apkg file %s does not exist", exportPath) - showSnackbar(R.string.export_save_apkg_unsuccessful) + showSnackbar(CommonString.export_save_apkg_unsuccessful) return } @@ -790,22 +790,22 @@ open class AnkiActivity( saveFileLauncher.launch(saveIntent) } catch (ex: ActivityNotFoundException) { Timber.w(ex, "No activity found to handle saveExportFile request") - showSnackbar(R.string.activity_start_failed) + showSnackbar(CommonString.activity_start_failed) } } private fun saveFileCallback(result: ActivityResult) { launchCatchingTask { - withProgress(getString(R.string.export_saving_exported_collection)) { + withProgress(getString(CommonString.export_saving_exported_collection)) { val isSuccessful = withContext(Dispatchers.IO) { exportToProvider(result.data!!) } if (isSuccessful) { - showSnackbar(R.string.export_save_apkg_successful, Snackbar.LENGTH_SHORT) + showSnackbar(CommonString.export_save_apkg_successful, Snackbar.LENGTH_SHORT) } else { - showSnackbar(R.string.export_save_apkg_unsuccessful) + showSnackbar(CommonString.export_save_apkg_unsuccessful) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt index 897b1b3d7d58..8b408d851936 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt @@ -176,7 +176,7 @@ open class AnkiDroidApp : } launchCacheCleanup() if (AdaptionUtil.isUserATestClient) { - showThemedToast(this.applicationContext, getString(R.string.user_is_a_robot), false) + showThemedToast(this.applicationContext, getString(CommonString.user_is_a_robot), false) } setWebContentsDebuggingEnabled(Prefs.isWebDebugEnabled) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidJsAPI.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidJsAPI.kt index 74acdaa2f2c6..fd5f9224c038 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidJsAPI.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidJsAPI.kt @@ -115,7 +115,7 @@ open class AnkiDroidJsAPI( } catch (j: JSONException) { Timber.w(j) activity.runOnUiThread { - activity.showSnackbar(context.getString(R.string.invalid_json_data, j.localizedMessage)) + activity.showSnackbar(context.getString(CommonString.invalid_json_data, j.localizedMessage)) } } return null @@ -133,12 +133,12 @@ open class AnkiDroidJsAPI( errorCode: Int, apiDevContact: String, ) { - val errorMsg: String = context.getString(R.string.anki_js_error_code, errorCode) - val snackbarMsg: String = context.getString(R.string.api_version_developer_contact, apiDevContact, errorMsg) + val errorMsg: String = context.getString(CommonString.anki_js_error_code, errorCode) + val snackbarMsg: String = context.getString(CommonString.api_version_developer_contact, apiDevContact, errorMsg) activity.showSnackbar(snackbarMsg, Snackbar.LENGTH_INDEFINITE) { setMaxLines(3) - setAction(R.string.reviewer_invalid_api_version_visit_documentation) { + setAction(CommonString.reviewer_invalid_api_version_visit_documentation) { activity.openUrl("https://github.com/ankidroid/Anki-Android/wiki") } } @@ -154,7 +154,7 @@ open class AnkiDroidJsAPI( try { if (apiDevContact.isEmpty() || apiVer.isEmpty()) { activity.runOnUiThread { - activity.showSnackbar(context.getString(R.string.invalid_json_data, "")) + activity.showSnackbar(context.getString(CommonString.invalid_json_data, "")) } return false } @@ -172,13 +172,13 @@ open class AnkiDroidJsAPI( } versionSupplied.isLowerThan(versionCurrent) -> { activity.runOnUiThread { - activity.showSnackbar(context.getString(R.string.update_js_api_version, apiDevContact)) + activity.showSnackbar(context.getString(CommonString.update_js_api_version, apiDevContact)) } versionSupplied.isHigherThanOrEquivalentTo(Version.parse(AnkiDroidJsAPIConstants.MINIMUM_JS_API_VERSION)) } else -> { activity.runOnUiThread { - activity.showSnackbar(context.getString(R.string.valid_js_api_version, apiDevContact)) + activity.showSnackbar(context.getString(CommonString.valid_js_api_version, apiDevContact)) } false } @@ -568,7 +568,7 @@ open class AnkiDroidJsAPI( .map { withCol { getCard(it.cardOrNoteId) } } } catch (_: Exception) { activity.webView!!.evaluateJavascript( - "console.log('${context.getString(R.string.search_card_js_api_no_results)}')", + "console.log('${context.getString(CommonString.search_card_js_api_no_results)}')", null, ) showDeveloperContact(AnkiDroidJsAPIConstants.ANKI_JS_ERROR_CODE_SEARCH_CARD, apiContract.cardSuppliedDeveloperContact) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt b/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt index d37f250de099..e246c36d54eb 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt @@ -56,18 +56,18 @@ class BottomNavController( val tag: String, @StringRes val shortcutLabel: Int, ) { - HOME(R.id.nav_home, "home", R.string.deck_picker_group), - BROWSER(R.id.nav_browser, "browser", R.string.card_browser_context_menu), - STATS(R.id.nav_stats, "stats", R.string.open_statistics), - MORE(R.id.nav_more, "more", R.string.bottom_nav_more), + HOME(R.id.nav_home, "home", CommonString.deck_picker_group), + BROWSER(R.id.nav_browser, "browser", CommonString.card_browser_context_menu), + STATS(R.id.nav_stats, "stats", CommonString.open_statistics), + MORE(R.id.nav_more, "more", CommonString.bottom_nav_more), ; fun title(context: Context): String = when (this) { HOME -> TR.actionsDecks() - BROWSER -> context.getString(R.string.bottom_nav_browse) + BROWSER -> context.getString(CommonString.bottom_nav_browse) STATS -> TR.statisticsTitle() - MORE -> context.getString(R.string.bottom_nav_more) + MORE -> context.getString(CommonString.bottom_nav_more) } companion object { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt index 51d9aa03593f..f780e7176e0a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt @@ -151,14 +151,14 @@ open class CardBrowser : when (saveSearchResult) { SaveSearchResult.ALREADY_EXISTS -> showSnackbar( - R.string.card_browser_list_my_searches_new_search_error_dup, + CommonString.card_browser_list_my_searches_new_search_error_dup, Snackbar.LENGTH_SHORT, ) SaveSearchResult.SUCCESS -> { searchView!!.setQuery("", false) mySearchesItem!!.isVisible = true showSnackbar( - R.string.card_browser_list_my_searches_successful_save, + CommonString.card_browser_list_my_searches_successful_save, Snackbar.LENGTH_SHORT, ) } @@ -385,12 +385,12 @@ open class CardBrowser : private fun showSaveChangesDialog(destination: NoteEditorDestination) { DiscardChangesDialog.showDialog( context = this, - positiveButtonText = this.getString(R.string.save), - negativeButtonText = this.getString(R.string.discard), + positiveButtonText = this.getString(CommonString.save), + negativeButtonText = this.getString(CommonString.discard), // The neutral button allows the user to back out of the action, // e.g., if they accidentally triggered a navigation or card selection. - neutralButtonText = this.getString(R.string.dialog_cancel), - message = this.getString(R.string.save_changes_message), + neutralButtonText = this.getString(CommonString.dialog_cancel), + message = this.getString(CommonString.save_changes_message), positiveMethod = { launchCatchingTask { fragment?.saveNote() @@ -751,7 +751,7 @@ suspend fun searchForRows( /** Renders the row count for [this] event as a localized "X cards/notes" string. */ fun SearchState.Completed.formatCardCount(resources: android.content.res.Resources): String = resources.getQuantityString( - if (cardsOrNotes == CARDS) R.plurals.card_browser_subtitle else R.plurals.card_browser_subtitle_notes_mode, + if (cardsOrNotes == CARDS) CommonPlurals.card_browser_subtitle else CommonPlurals.card_browser_subtitle_notes_mode, rowCount, rowCount, ) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateBrowserAppearanceEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateBrowserAppearanceEditor.kt index 6a7cb68bde4a..3f3e446b9b55 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateBrowserAppearanceEditor.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateBrowserAppearanceEditor.kt @@ -71,7 +71,7 @@ class CardTemplateBrowserAppearanceEditor : AnkiActivity(R.layout.activity_card_ } val bundle = savedInstanceState ?: intent.extras if (bundle == null) { - showThemedToast(this, getString(R.string.something_wrong), true) + showThemedToast(this, getString(CommonString.something_wrong), true) finish() return } @@ -128,11 +128,11 @@ class CardTemplateBrowserAppearanceEditor : AnkiActivity(R.layout.activity_card_ private fun showRestoreDefaultDialog() { AlertDialog.Builder(this).show { setTitle(TR.sentenceCase.restoreToDefault) - positiveButton(R.string.restore) { + positiveButton(CommonString.restore) { restoreDefaultAndClose() } - negativeButton(R.string.dialog_cancel) - message(R.string.card_template_browser_appearance_restore_default_dialog) + negativeButton(CommonString.dialog_cancel) + message(CommonString.card_template_browser_appearance_restore_default_dialog) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt index c0a8a8b631d5..a9385bc6370b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt @@ -389,7 +389,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi // Set activity title supportActionBar?.let { - it.setTitle(R.string.title_activity_template_editor) + it.setTitle(CommonString.title_activity_template_editor) it.subtitle = tempNoteType!!.notetype.name } // Close collection opening dialog if needed @@ -444,7 +444,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi require(deck is SelectableDeck.Deck?) if (tempNoteType!!.notetype.isCloze) { Timber.w("Attempted to set deck for cloze note type") - showSnackbar(getString(R.string.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) + showSnackbar(getString(CommonString.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) return } @@ -453,7 +453,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi if (deck != null && getColUnsafe.decks.isFiltered(deck.deckId)) { Timber.w("Attempted to set default deck of %s to dynamic deck %s", templateName, deck.name) - showSnackbar(getString(R.string.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) + showSnackbar(getString(CommonString.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) return } @@ -461,11 +461,11 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi if (deck == null) { Timber.i("Removing default template from template '%s'", templateName) template.jsonObject.put("did", JSONObject.NULL) - getString(R.string.model_manager_deck_override_removed_message, templateName) + getString(CommonString.model_manager_deck_override_removed_message, templateName) } else { Timber.i("Setting template '%s' to '%s'", templateName, deck.name) template.jsonObject.put("did", deck.deckId) - getString(R.string.model_manager_deck_override_added_message, templateName, deck.name) + getString(CommonString.model_manager_deck_override_added_message, templateName, deck.name) } showSnackbar(message, Snackbar.LENGTH_SHORT) @@ -587,20 +587,20 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi get() = ShortcutGroup( listOf( - shortcut("Ctrl+P", R.string.card_editor_preview_card), - shortcut("Ctrl+1", R.string.edit_front_template), - shortcut("Ctrl+2", R.string.edit_back_template), - shortcut("Ctrl+3", R.string.edit_styling), - shortcut("Ctrl+S", R.string.save), - shortcut("Ctrl+I", R.string.card_template_editor_insert_field), + shortcut("Ctrl+P", CommonString.card_editor_preview_card), + shortcut("Ctrl+1", CommonString.edit_front_template), + shortcut("Ctrl+2", CommonString.edit_back_template), + shortcut("Ctrl+3", CommonString.edit_styling), + shortcut("Ctrl+S", CommonString.save), + shortcut("Ctrl+I", CommonString.card_template_editor_insert_field), shortcut("Ctrl+A", Translations::cardTemplatesAddCardType), shortcut("Ctrl+R", Translations::cardTemplatesRenameCardType), - shortcut("Ctrl+B", R.string.edit_browser_appearance), + shortcut("Ctrl+B", CommonString.edit_browser_appearance), shortcut("Ctrl+D", Translations::cardTemplatesRemoveCardType), shortcut("Ctrl+O", Translations::cardTemplatesDeckOverride), - shortcut("Ctrl+M", R.string.copy_the_template), + shortcut("Ctrl+M", CommonString.copy_the_template), ), - R.string.card_template_editor_group, + CommonString.card_template_editor_group, ) class CardTemplateFragment : Fragment(R.layout.fragment_card_template_editor_template) { @@ -844,7 +844,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi if (currentEditorViewId != R.id.styling_edit) { // 10644: Do not pass in a R.string as the final parameter as MIUI on Android 12 crashes. - menu.add(Menu.FIRST, insertFieldId, 0, getString(R.string.card_template_editor_insert_field)) + menu.add(Menu.FIRST, insertFieldId, 0, getString(CommonString.card_template_editor_insert_field)) } return initialSize != menu.size @@ -1030,7 +1030,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi val template = tempModel!!.getTemplate(ordinal) // Don't do anything if only one template if (tempModel.templateCount < 2) { - templateEditor.showSimpleMessageDialog(resources.getString(R.string.card_template_editor_cant_delete)) + templateEditor.showSimpleMessageDialog(resources.getString(CommonString.card_template_editor_cant_delete)) return@launch } @@ -1058,8 +1058,8 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi val builder = AlertDialog .Builder(requireContext()) - .setTitle(R.string.orphan_note_title) - .setMessage(R.string.orphan_note_message) + .setTitle(CommonString.orphan_note_title) + .setMessage(CommonString.orphan_note_message) .setPositiveButton(android.R.string.ok, null) builder.show() @@ -1098,9 +1098,9 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi } confirmButton.isEnabled = false } - launchCatchingTask(resources.getString(R.string.card_template_editor_save_error)) { + launchCatchingTask(resources.getString(CommonString.card_template_editor_save_error)) { try { - requireActivity().withProgress(resources.getString(R.string.saving_model)) { + requireActivity().withProgress(resources.getString(CommonString.saving_model)) { templateEditor.tempNoteType!!.saveToDatabase() } onModelSaved() @@ -1137,9 +1137,9 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi @StringRes val overrideStringRes = if (template != null && template.jsonObject.has("did") && !template.jsonObject.isNull("did")) { - R.string.card_template_editor_deck_override_on + CommonString.card_template_editor_deck_override_on } else { - R.string.card_template_editor_deck_override_off + CommonString.card_template_editor_deck_override_off } menu.findItem(R.id.action_add_deck_override).setTitle(overrideStringRes) } @@ -1233,12 +1233,12 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi AlertDialog.Builder(requireContext()).show { setTitle(TR.sentenceCase.restoreToDefault) setMessage(TR.cardTemplatesRestoreToDefaultConfirmation()) - setPositiveButton(R.string.restore) { _, _ -> + setPositiveButton(CommonString.restore) { _, _ -> launchCatchingTask { restoreNotetypeToStock(kind) } } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } } @@ -1258,7 +1258,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi } AlertDialog.Builder(requireContext()).show { setTitle(TR.sentenceCase.restoreToDefault) - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } listItems(stockNotetypesNames) { _: DialogInterface, index: Int -> val kind = stockNotetypeKinds[index] askUser(kind) @@ -1340,12 +1340,12 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi launchCatchingTask { val activity = requireAnkiActivity() if (tempModel.notetype.isCloze) { - showSnackbar(getString(R.string.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) + showSnackbar(getString(CommonString.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) return@launchCatchingTask } val name = getCurrentTemplateName(tempModel) - val title = getString(R.string.card_template_editor_deck_override) - val explanation = getString(R.string.deck_override_explanation, name) + val title = getString(CommonString.card_template_editor_deck_override) + val explanation = getString(CommonString.deck_override_explanation, name) // Anki Desktop allows Dynamic decks, have reported this as a bug: // https://forums.ankiweb.net/t/minor-bug-deck-override-to-filtered-deck/1493 startDeckSelection(title = title, templateEditorMessage = explanation, allowAll = false, allowFiltered = false) @@ -1459,16 +1459,16 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi val msg = String.format( resources.getQuantityString( - R.plurals.card_template_editor_confirm_delete, + CommonPlurals.card_template_editor_confirm_delete, numAffectedCards, ), numAffectedCards, tmpl.jsonObject.optString("name"), ) d.setArgs( - title = getString(R.string.delete_card_type), + title = getString(CommonString.delete_card_type), message = msg, - positiveButtonText = getString(R.string.dialog_positive_delete), + positiveButtonText = getString(CommonString.dialog_positive_delete), ) val deleteCard = Runnable { deleteTemplate(tmpl, notetype) } @@ -1490,15 +1490,15 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi val msg = String.format( resources.getQuantityString( - R.plurals.card_template_editor_confirm_add, + CommonPlurals.card_template_editor_confirm_add, numAffectedCards, ), numAffectedCards, ) d.setArgs( - title = getString(R.string.add_card_type), + title = getString(CommonString.add_card_type), message = msg, - positiveButtonText = getString(R.string.menu_add), + positiveButtonText = getString(CommonString.menu_add), ) val addCard = Runnable { addNewTemplate(notetype) } @@ -1528,7 +1528,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi } catch (e: ConfirmModSchemaException) { e.log() val d = ConfirmationDialog() - d.setArgs(resources.getString(R.string.full_sync_confirmation)) + d.setArgs(resources.getString(CommonString.full_sync_confirmation)) val confirm = Runnable { templateEditor.getColUnsafe.modSchema(check = false) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CollectionIntegrityStorageCheck.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CollectionIntegrityStorageCheck.kt index a061d9807319..67aa5a5630b0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CollectionIntegrityStorageCheck.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CollectionIntegrityStorageCheck.kt @@ -53,14 +53,14 @@ class CollectionIntegrityStorageCheck { Timber.e("CollectionIntegrityCheckStatus in an invalid state") val defaultRequiredFreeSpace = defaultRequiredFreeSpace(context) return context.resources.getString( - R.string.integrity_check_insufficient_space, + CommonString.integrity_check_insufficient_space, defaultRequiredFreeSpace, ) } val required = Formatter.formatShortFileSize(context, requiredSpace) val insufficientSpace = context.resources.getString( - R.string.integrity_check_insufficient_space, + CommonString.integrity_check_insufficient_space, required, ) @@ -68,7 +68,7 @@ class CollectionIntegrityStorageCheck { val currentFree = Formatter.formatShortFileSize(context, freeSpace) val insufficientSpaceCurrentFree = context.resources.getString( - R.string.integrity_check_insufficient_space_extra_content, + CommonString.integrity_check_insufficient_space_extra_content, currentFree, ) return insufficientSpace + insufficientSpaceCurrentFree @@ -90,7 +90,7 @@ class CollectionIntegrityStorageCheck { val requiredFreeSpace = defaultRequiredFreeSpace(context) return fromError( context.resources.getString( - R.string.integrity_check_insufficient_space, + CommonString.integrity_check_insufficient_space, requiredFreeSpace, ), ) @@ -108,7 +108,7 @@ class CollectionIntegrityStorageCheck { val readableFileSize = Formatter.formatFileSize(context, requiredSpaceInBytes) return fromError( context.resources.getString( - R.string.integrity_check_insufficient_space, + CommonString.integrity_check_insufficient_space, readableFileSize, ), ) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt index 83a33d1016c1..058b76403b1d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt @@ -253,9 +253,9 @@ fun Context.showError( AlertDialog .Builder(this) .create { - title(R.string.vague_error) + title(CommonString.vague_error) message(text = message) - positiveButton(R.string.dialog_ok) + positiveButton(CommonString.dialog_ok) helpAction?.let { neutralButton(text = it.buttonText(this@showError)) } if (crashReportData?.reportableException == true) { Timber.w("sending crash report on close") @@ -355,7 +355,7 @@ suspend fun FragmentActivity.withProgress( * flashes of a dialog. */ suspend fun Activity.withProgress( - message: String = resources.getString(R.string.dialog_processing), + message: String = resources.getString(CommonString.dialog_processing), op: suspend () -> T, ): T = withProgressDialog( @@ -369,7 +369,7 @@ suspend fun Activity.withProgress( /** @see withProgress(String, ...) */ suspend fun Fragment.withProgress( - message: String = getString(R.string.dialog_processing), + message: String = getString(CommonString.dialog_processing), block: suspend () -> T, ): T = requireActivity().withProgress(message, block) @@ -528,7 +528,7 @@ data class ProgressContext( // replace spaces with NBSP so newlines are handled better val curStr = Formatter.formatShortFileSize(context, current).replace(' ', '\u00A0') val maxStr = Formatter.formatShortFileSize(context, max).replace(' ', '\u00A0') - context.getString(R.string.progress_amount_bytes, curStr, maxStr) + context.getString(CommonString.progress_amount_bytes, curStr, maxStr) }, ) } @@ -657,7 +657,7 @@ data class CrashReportData( */ sealed class HelpAction { /** Label for the 'help' button on the error dialog. Defaults to "Help". */ - open fun buttonText(context: Context): CharSequence = context.getString(R.string.help) + open fun buttonText(context: Context): CharSequence = context.getString(CommonString.help) /** `false` hides the help button. */ open fun canExecute(context: Context): Boolean = true diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index 2b1cb03ec039..982271ef543b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -380,7 +380,7 @@ open class DeckPicker : DeckPickerActivityResultCallback { if (it.resultCode == RESULT_OK) { lifecycleScope.launch { - withProgress(message = getString(R.string.import_preparing_file)) { + withProgress(message = getString(CommonString.import_preparing_file)) { withContext(Dispatchers.IO) { onSelectedPackageToImport(it.data!!) } @@ -761,13 +761,13 @@ open class DeckPicker : fun onDeckDeleted(result: DeckDeletionResult) { floatingActionButtonBinding.fabMain.isVisible = true showSnackbar(result.toHumanReadableString(), Snackbar.LENGTH_SHORT) { - setAction(R.string.undo) { undo() } + setAction(CommonString.undo) { undo() } } } fun onCardsEmptied(result: EmptyCardsResult) { showSnackbar(result.toHumanReadableString(), Snackbar.LENGTH_SHORT) { - setAction(R.string.undo) { undo() } + setAction(CommonString.undo) { undo() } } } @@ -855,7 +855,7 @@ open class DeckPicker : } supportActionBar?.apply { - subtitle = if (dueCount == 0) null else resources.getQuantityString(R.plurals.widget_cards_due, dueCount, dueCount) + subtitle = if (dueCount == 0) null else resources.getQuantityString(CommonPlurals.widget_cards_due, dueCount, dueCount) val toolbar = findViewById(R.id.toolbar) TooltipCompat.setTooltipText(toolbar, toolbar.subtitle) } @@ -929,7 +929,7 @@ open class DeckPicker : fun onError(errorMessage: String) { AlertDialog .Builder(this) - .setTitle(R.string.vague_error) + .setTitle(CommonString.vague_error) .setMessage(errorMessage) .show() } @@ -968,7 +968,7 @@ open class DeckPicker : val clip = uriContent?.clip ?: return@OnReceiveContentListener remaining val uri = clip.getItemAt(0).uri if (!ImportUtils.FileImporter().isValidImportType(this, uri)) { - showSnackbar(R.string.import_log_no_apkg) + showSnackbar(CommonString.import_log_no_apkg) return@OnReceiveContentListener remaining } @@ -1137,19 +1137,19 @@ open class DeckPicker : linksClickable = true text = getString( - R.string.directory_inaccessible_info, + CommonString.directory_inaccessible_info, getString(R.string.link_full_storage_access), ) } AlertDialog.Builder(this).show { - title(R.string.directory_inaccessible) + title(CommonString.directory_inaccessible) customView( contentView, paddingTop = 16.dp.toPx(this@DeckPicker), paddingStart = 32.dp.toPx(this@DeckPicker), paddingEnd = 32.dp.toPx(this@DeckPicker), ) - positiveButton(R.string.open_settings) { + positiveButton(CommonString.open_settings) { requestPathUpdateLauncher.navigate(PreferencesDestination.Advanced) } } @@ -1173,7 +1173,7 @@ open class DeckPicker : val drawable = (result as? BackgroundImage.ResolveResult.Ready)?.drawable val applied = deckPickerBinding.background.setImageDrawableSafe(drawable) { - showThemedToast(this, getString(R.string.background_image_too_large), shortLength = false) + showThemedToast(this, getString(CommonString.background_image_too_large), shortLength = false) } // activityHasBackground calls notifyDataSetChanged, ensure only 1 call deckListAdapter.activityHasBackground = applied && drawable != null @@ -1273,7 +1273,7 @@ open class DeckPicker : ) (menuItem.actionView as AccessibleSearchView).run { - queryHint = getString(R.string.search_decks) + queryHint = getString(CommonString.search_decks) setOnQueryTextListener( object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { @@ -1341,9 +1341,9 @@ open class DeckPicker : ?: return val tooltipText = when (state.syncIcon) { - SyncIconState.Normal, SyncIconState.PendingChanges -> R.string.button_sync - SyncIconState.OneWay -> R.string.sync_menu_title_one_way_sync - SyncIconState.NotLoggedIn -> R.string.sync_menu_title_no_account + SyncIconState.Normal, SyncIconState.PendingChanges -> CommonString.button_sync + SyncIconState.OneWay -> CommonString.sync_menu_title_one_way_sync + SyncIconState.NotLoggedIn -> CommonString.sync_menu_title_no_account } provider.setTooltipText(getString(tooltipText)) when (state.syncIcon) { @@ -1433,7 +1433,7 @@ open class DeckPicker : } R.id.action_deck_delete -> { launchCatchingTask { - withProgress(resources.getString(R.string.delete_deck)) { + withProgress(resources.getString(CommonString.delete_deck)) { viewModel.deleteSelectedDeck().join() } } @@ -1466,12 +1466,12 @@ open class DeckPicker : Timber.i("showing media check dialog") AlertDialog.Builder(this).show { title(text = TR.sentenceCase.checkMediaTitle) - message(text = getString(R.string.check_media_warning)) - positiveButton(R.string.dialog_ok) { + message(text = getString(CommonString.check_media_warning)) + positiveButton(CommonString.dialog_ok) { Timber.i("Starting media check") startActivity(MediaCheckFragment.getIntent(this@DeckPicker)) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } @@ -1874,7 +1874,7 @@ open class DeckPicker : Timber.i("Dev Build - not showing 'new features'") // Don't show new features dialog for development builds InitialActivity.setUpgradedToLatestVersion(preferences) - val ver = resources.getString(R.string.updated_version, VersionUtils.pkgVersionName) + val ver = resources.getString(CommonString.updated_version, VersionUtils.pkgVersionName) postSnackbar(ver, Snackbar.LENGTH_SHORT) showStartupScreensAndDialogs(preferences, 2) } @@ -1934,10 +1934,10 @@ open class DeckPicker : fun repairCollection() { Timber.i("Repairing the Collection") // TODO: doesn't work on null collection-only on non-openable(is this still relevant with withCol?) - launchCatchingTask(resources.getString(R.string.deck_repair_error)) { + launchCatchingTask(resources.getString(CommonString.deck_repair_error)) { Timber.d("doInBackgroundRepairCollection") val result = - withProgress(resources.getString(R.string.backup_repair_deck_progress)) { + withProgress(resources.getString(CommonString.backup_repair_deck_progress)) { Timber.i("RepairCollection: Closing collection") CollectionManager.ensureClosed() val colFile = @@ -1945,7 +1945,7 @@ open class DeckPicker : BackupManager.repairCollection(colFile) } if (!result) { - showThemedToast(this@DeckPicker, resources.getString(R.string.deck_repair_error), true) + showThemedToast(this@DeckPicker, resources.getString(CommonString.deck_repair_error), true) showCollectionErrorDialog() } } @@ -1961,10 +1961,10 @@ open class DeckPicker : AlertDialog.Builder(this).show { title(text = TR.sentenceCase.checkDatabase) message(text = status.getWarningDetails(this@DeckPicker)) - positiveButton(R.string.integrity_check_continue_anyway) { + positiveButton(CommonString.integrity_check_continue_anyway) { performIntegrityCheck() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } else { performIntegrityCheck() @@ -2090,7 +2090,7 @@ open class DeckPicker : fun openAnkiWebSharedDecks() { if (!NetworkUtils.isOnline) { - showSnackbar(R.string.check_network) + showSnackbar(CommonString.check_network) Timber.d("DeckPicker:: No network, Shared deck download failed") return } @@ -2139,8 +2139,8 @@ open class DeckPicker : } fun showEmptyDeckSnackbar() = - showSnackbar(R.string.empty_deck) { - setAction(R.string.menu_add) { viewModel.addNote(did, true) } + showSnackbar(CommonString.empty_deck) { + setAction(CommonString.menu_add) { viewModel.addNote(did, true) } } /** Check if we need to update the fragment or update the deck list */ @@ -2204,27 +2204,27 @@ open class DeckPicker : // User report: "success" is true even if Vivo does not have permission if (AdaptionUtil.isVivo) { - showThemedToast(this, getString(R.string.create_shortcut_error_vivo), false) + showThemedToast(this, getString(CommonString.create_shortcut_error_vivo), false) } // #18601: MIUI gates shortcut creation behind Settings - Privacy Protection - // Other permissions - AnkiDroid - Home screen shortcuts [add shortcuts to Home screen] // TODO: Determine the result of 'success' if (AdaptionUtil.isMiui) { - showThemedToast(this, getString(R.string.create_shortcut_error_miui, getString(R.string.app_name)), false) + showThemedToast(this, getString(CommonString.create_shortcut_error_miui, getString(R.string.app_name)), false) } if (!success) { - showThemedToast(this, getString(R.string.create_shortcut_failed), false) + showThemedToast(this, getString(CommonString.create_shortcut_failed), false) } } catch (e: Exception) { Timber.w(e) - showThemedToast(this, getString(R.string.create_shortcut_error, e.localizedMessage), false) + showThemedToast(this, getString(CommonString.create_shortcut_error, e.localizedMessage), false) } } /** Disables the shortcut of the deck and the children belonging to it.*/ @NeedsTest("ensure collapsed decks are also deleted") private fun disableDeckAndChildrenShortcuts(deckTreeDids: List) { - val errorMessage: CharSequence = getString(R.string.deck_shortcut_doesnt_exist) + val errorMessage: CharSequence = getString(CommonString.deck_shortcut_doesnt_exist) ShortcutUtils.disableShortcuts(this, deckTreeDids, errorMessage) } @@ -2286,7 +2286,7 @@ open class DeckPicker : */ fun deleteDeck(did: DeckId) = launchCatchingTask { - withProgress(resources.getString(R.string.delete_deck)) { + withProgress(resources.getString(CommonString.delete_deck)) { viewModel.deleteDeck(did).join() } } @@ -2294,7 +2294,7 @@ open class DeckPicker : @NeedsTest("14285: regression test to ensure UI is updated after this call") fun rebuildFiltered(did: DeckId) { launchCatchingTask { - withProgress(resources.getString(R.string.rebuild_filtered_deck)) { + withProgress(resources.getString(CommonString.rebuild_filtered_deck)) { viewModel.rebuildFilteredDeck(did).join() } updateDeckList() @@ -2326,7 +2326,7 @@ open class DeckPicker : val createDeckDialog = CreateDeckDialog( context = this@DeckPicker, - title = getString(R.string.create_subdeck), + title = getString(CommonString.create_subdeck), deckDialogType = CreateDeckDialog.DeckDialogType.SUB_DECK, parentId = did, ) @@ -2372,29 +2372,29 @@ open class DeckPicker : return ShortcutGroup( listOfNotNull( - shortcut("A", R.string.menu_add_note), + shortcut("A", CommonString.menu_add_note), bottomNavShortcut("Alt+1", NavigationItem.HOME), bottomNavShortcut("Alt+2", NavigationItem.BROWSER), bottomNavShortcut("Alt+3", NavigationItem.STATS), bottomNavShortcut("Alt+4", NavigationItem.MORE), - shortcut("B", R.string.card_browser_context_menu), - shortcut("Y", R.string.pref_cat_sync), - shortcut("/", R.string.deck_conf_cram_search), + shortcut("B", CommonString.card_browser_context_menu), + shortcut("Y", CommonString.pref_cat_sync), + shortcut("/", CommonString.deck_conf_cram_search), shortcut("S", Translations::decksStudyDeck), - shortcut("T", R.string.open_statistics), + shortcut("T", CommonString.open_statistics), shortcut("C") { this.sentenceCase.checkDatabase }, shortcut("D") { sentenceCase.createDeck }, - shortcut("F", R.string.new_dynamic_deck), + shortcut("F", CommonString.new_dynamic_deck), if (fragmented) shortcut("DEL") { this.sentenceCase.deleteDeck } else null, - if (fragmented) shortcut("Shift+DEL", R.string.delete_deck_without_confirmation) else null, + if (fragmented) shortcut("Shift+DEL", CommonString.delete_deck_without_confirmation) else null, if (fragmented) shortcut("R") { this.sentenceCase.renameDeck } else null, - shortcut("P", R.string.open_settings), + shortcut("P", CommonString.open_settings), shortcut("M") { this.sentenceCase.checkMediaAction }, - shortcut("Ctrl+E", R.string.export_collection), + shortcut("Ctrl+E", CommonString.export_collection), shortcut("Ctrl+Shift+I", Translations::actionsImport), - shortcut("Ctrl+Shift+N", R.string.model_browser_label), + shortcut("Ctrl+Shift+N", CommonString.model_browser_label), ), - R.string.deck_picker_group, + CommonString.deck_picker_group, ) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/FieldEditText.kt b/AnkiDroid/src/main/java/com/ichi2/anki/FieldEditText.kt index 9689a4737498..725a779b58e4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/FieldEditText.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/FieldEditText.kt @@ -179,7 +179,7 @@ class FieldEditText : pasteListener!!.onPaste(this, mediaUri, description) } catch (e: Exception) { Timber.w(e, "Failed to paste media") - showSnackbar(context.getString(R.string.multimedia_editor_something_wrong)) + showSnackbar(context.getString(CommonString.multimedia_editor_something_wrong)) false } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt index 7e1f388aa962..d9d639fae1c6 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt @@ -83,7 +83,7 @@ class Info : } binding.leftButton.run { if (canOpenMarketUri()) { - setText(R.string.info_rate) + setText(CommonString.info_rate) setOnClickListener { tryOpenIntent( this@Info, @@ -113,7 +113,7 @@ class Info : binding.webView.settings.allowContentAccess = true setRenderWorkaround(this) binding.rightButton.run { - text = getString(R.string.dialog_continue) + text = getString(CommonString.dialog_continue) setOnClickListener { close() } } val background = backgroundColor.toRGBHex() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt index fd4e1d65d34f..73df41266d34 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt @@ -187,12 +187,12 @@ object InitialActivity { when (error) { is FatalInitializationError.WebViewError -> context.getString( - R.string.ankidroid_init_failed_webview, + CommonString.ankidroid_init_failed_webview, error.errorDetail, ) is FatalInitializationError.StorageError -> context.getString( - R.string.ankidroid_init_failed_storage, + CommonString.ankidroid_init_failed_storage, error.errorDetail, ) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler.kt index 5b33eec87769..0cf54fec60d7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler.kt @@ -121,7 +121,7 @@ class IntentHandler : AbstractIntentHandler() { } this.copyToClipboard( text = TruncatedString.from(clipboardData), - failureMessageId = R.string.about_ankidroid_error_copy_debug_info, + failureMessageId = CommonString.about_ankidroid_error_copy_debug_info, ) } @@ -219,7 +219,7 @@ class IntentHandler : AbstractIntentHandler() { Timber.i("Handling file import") if (!hasShownAppIntro()) { Timber.i("Trying to import a file when the app was not started at all") - showThemedToast(this, R.string.app_not_initialized_new, false) + showThemedToast(this, CommonString.app_not_initialized_new, false) finish() return } @@ -377,7 +377,7 @@ class IntentHandler : AbstractIntentHandler() { val granted = grantedStoragePermissions(context) if (!granted && showToast) { - showThemedToast(context, context.getString(R.string.intent_handler_failed_no_storage_permission), false) + showThemedToast(context, context.getString(CommonString.intent_handler_failed_no_storage_permission), false) } return granted @@ -456,7 +456,7 @@ class IntentHandler : AbstractIntentHandler() { if (!limited && !hkey.isNullOrEmpty() && NetworkUtils.isOnline) { deckPicker.sync() } else { - val err = res.getString(R.string.sync_error) + val err = res.getString(CommonString.sync_error) if (limited) { val remainingTimeInSeconds = max((INTENT_SYNC_MIN_INTERVAL - millisecondsSinceLastSync) / 1000, 1) @@ -464,7 +464,7 @@ class IntentHandler : AbstractIntentHandler() { val remaining = min(Int.MAX_VALUE.toLong(), remainingTimeInSeconds).toInt() val message = res.getQuantityString( - R.plurals.sync_automatic_sync_needs_more_time, + CommonPlurals.sync_automatic_sync_needs_more_time, remaining, remaining, ) @@ -472,7 +472,7 @@ class IntentHandler : AbstractIntentHandler() { } else { deckPicker.showSimpleNotification( err, - res.getString(R.string.youre_offline), + res.getString(CommonString.youre_offline), NotificationChannel.SYNC, ) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt b/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt index 37c4fe79ebad..75ab625e6ce6 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt @@ -28,7 +28,7 @@ class IntentHandler2 : AbstractIntentHandler() { val intentExtras = intent.extras if (intentExtras == null) { Timber.w("Intent unexpectedly has no extras. Notifying user, defaulting to add note.") - showThemedToast(this, getString(R.string.something_wrong), false) + showThemedToast(this, getString(CommonString.something_wrong), false) navigate(NoteEditorDestination.AddNote()) finish() } else { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt b/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt index 256da728b3a9..2037dec39c2f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt @@ -46,36 +46,36 @@ object MediaRegistration { * Represents different types of media errors. */ sealed class MediaError { - /** [Something wrong wrong][R.string.multimedia_editor_something_wrong] */ + /** [Something wrong wrong][CommonString.multimedia_editor_something_wrong] */ data object GenericError : MediaError() - /** [Something went wrong, please try again[R.string.something_wrong] */ + /** [Something went wrong, please try again[CommonString.something_wrong] */ data class GenericErrorTryAgain( val details: String?, ) : MediaError() - /** [Error converting clipboard image to png][R.string.multimedia_editor_png_paste_error] */ + /** [Error converting clipboard image to png][CommonString.multimedia_editor_png_paste_error] */ class ConversionError( val message: String, ) : MediaError() - /** [The image is too large, please insert the image manually][R.string.note_editor_image_too_large] */ + /** [The image is too large, please insert the image manually][CommonString.note_editor_image_too_large] */ data object ImageTooLarge : MediaError() - /** [The video file is too large, please insert the video manually][R.string.note_editor_video_too_large] */ + /** [The video file is too large, please insert the video manually][CommonString.note_editor_video_too_large] */ data object VideoTooLarge : MediaError() - /** [The audio file is too large, please insert the audio manually][R.string.note_editor_audio_too_large] */ + /** [The audio file is too large, please insert the audio manually][CommonString.note_editor_audio_too_large] */ data object AudioTooLarge : MediaError() fun toHumanReadableString(context: Context): String = when (this) { - is GenericError -> context.getString(R.string.multimedia_editor_something_wrong) - is GenericErrorTryAgain -> context.getString(R.string.something_wrong) + details?.let { "\n\n$it" }.orEmpty() - is ConversionError -> context.getString(R.string.multimedia_editor_png_paste_error, message) - is ImageTooLarge -> context.getString(R.string.note_editor_image_too_large) - is VideoTooLarge -> context.getString(R.string.note_editor_video_too_large) - is AudioTooLarge -> context.getString(R.string.note_editor_audio_too_large) + is GenericError -> context.getString(CommonString.multimedia_editor_something_wrong) + is GenericErrorTryAgain -> context.getString(CommonString.something_wrong) + details?.let { "\n\n$it" }.orEmpty() + is ConversionError -> context.getString(CommonString.multimedia_editor_png_paste_error, message) + is ImageTooLarge -> context.getString(CommonString.note_editor_image_too_large) + is VideoTooLarge -> context.getString(CommonString.note_editor_video_too_large) + is AudioTooLarge -> context.getString(CommonString.note_editor_audio_too_large) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/MoreFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/MoreFragment.kt index 67ed42781e0d..58c4e17fb673 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/MoreFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/MoreFragment.kt @@ -48,19 +48,19 @@ class MoreFragment : Fragment(R.layout.fragment_more) { // Help section: each opens HelpDialog with the relevant sub-items binding.moreHelpManual.setOnClickListener { Analytics.send(LinkClicked(LinkAction.OPENED_USING_ANKIDROID)) - openHelpSection(R.string.help_title_using_ankidroid) + openHelpSection(CommonString.help_title_using_ankidroid) } binding.moreHelpGetHelp.setOnClickListener { Analytics.send(LinkClicked(LinkAction.OPENED_GET_HELP)) - openHelpSection(R.string.help_title_get_help) + openHelpSection(CommonString.help_title_get_help) } binding.moreHelpCommunity.setOnClickListener { Analytics.send(LinkClicked(LinkAction.OPENED_COMMUNITY)) - openHelpSection(R.string.help_title_community) + openHelpSection(CommonString.help_title_community) } binding.moreHelpPrivacy.setOnClickListener { Analytics.send(LinkClicked(LinkAction.OPENED_PRIVACY)) - openHelpSection(R.string.help_title_privacy) + openHelpSection(CommonString.help_title_privacy) } // Support section: direct URL actions diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt index fcb0c5e38fa4..2d4ec5e17096 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NavigationDrawerActivity.kt @@ -191,8 +191,8 @@ abstract class NavigationDrawerActivity( object : ActionBarDrawerToggle( this, drawerLayout, - R.string.drawer_open, - R.string.drawer_close, + CommonString.drawer_open, + CommonString.drawer_close, ) { override fun onDrawerClosed(drawerView: View) { super.onDrawerClosed(drawerView) @@ -528,8 +528,8 @@ abstract class NavigationDrawerActivity( val reviewCardsShortcut = ShortcutInfoCompat .Builder(context, "reviewCardsShortcutId") - .setShortLabel(context.getString(R.string.studyoptions_start)) - .setLongLabel(context.getString(R.string.studyoptions_start)) + .setShortLabel(context.getString(CommonString.studyoptions_start)) + .setLongLabel(context.getString(CommonString.studyoptions_start)) .setIcon(IconCompat.createWithResource(context, R.drawable.review_shortcut)) .setIntents(arrayOf(deckPickerIntent, intentReviewCards)) .build() @@ -542,8 +542,8 @@ abstract class NavigationDrawerActivity( val noteEditorShortcut = ShortcutInfoCompat .Builder(context, "noteEditorShortcutId") - .setShortLabel(context.getString(R.string.menu_add)) - .setLongLabel(context.getString(R.string.menu_add)) + .setShortLabel(context.getString(CommonString.menu_add)) + .setLongLabel(context.getString(CommonString.menu_add)) .setIcon(IconCompat.createWithResource(context, R.drawable.add_shortcut)) .setIntent(intentAddNote) .build() @@ -555,8 +555,8 @@ abstract class NavigationDrawerActivity( val cardBrowserShortcut = ShortcutInfoCompat .Builder(context, "cardBrowserShortcutId") - .setShortLabel(context.getString(R.string.card_browser)) - .setLongLabel(context.getString(R.string.card_browser)) + .setShortLabel(context.getString(CommonString.card_browser)) + .setLongLabel(context.getString(CommonString.card_browser)) .setIcon(IconCompat.createWithResource(context, R.drawable.browse_shortcut)) .setIntent(intentCardBrowser) .build() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt index cf6006a05a4c..21f83cc02351 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt @@ -364,7 +364,7 @@ class NoteEditorActivity : for ((index, templateName) in templateNames.withIndex()) { val tabTitle = if (cardsWithEmptyFronts?.get(index) == true) { - getString(R.string.card_previewer_empty_front_indicator, templateName) + getString(CommonString.card_previewer_empty_front_indicator, templateName) } else { templateName } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt index b37b38c8f4d0..132aa3c4f565 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt @@ -373,7 +373,7 @@ class NoteEditorFragment : * type was changed without moving this * card to another type. */ Timber.d("onActivityResult() template edit return - current card is gone, close note editor") - showSnackbar(getString(R.string.template_for_current_card_deleted)) + showSnackbar(getString(CommonString.template_for_current_card_deleted)) closeNoteEditor() } else { Timber.d("onActivityResult() template edit return, in add mode, just re-display") @@ -497,7 +497,7 @@ class NoteEditorFragment : saveNoteWithProgress() } } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } @@ -512,8 +512,8 @@ class NoteEditorFragment : get() = when { addNoteErrorMessage != null -> addNoteErrorMessage!! - allFieldsHaveContent() -> resources.getString(R.string.note_editor_no_cards_created_all_fields) - else -> resources.getString(R.string.note_editor_no_cards_created) + allFieldsHaveContent() -> resources.getString(CommonString.note_editor_no_cards_created_all_fields) + else -> resources.getString(CommonString.note_editor_no_cards_created) } override val baseSnackbarBuilder: SnackbarBuilder = { @@ -713,7 +713,7 @@ class NoteEditorFragment : ioEditorLauncher.launch("image/*") } catch (_: ActivityNotFoundException) { Timber.w("No app found to handle onGalleryClicked request") - activity?.showSnackbar(R.string.activity_start_failed) + activity?.showSnackbar(CommonString.activity_start_failed) } } pasteOcclusionImageButton?.setOnClickListener { @@ -736,7 +736,7 @@ class NoteEditorFragment : } else { imageSelectionForOcclusionContainer?.visibility = View.GONE editOcclusionsButton?.visibility = View.VISIBLE - editOcclusionsButton?.text = resources.getString(R.string.edit_occlusions) + editOcclusionsButton?.text = resources.getString(CommonString.edit_occlusions) editOcclusionsButton?.setOnClickListener { setupImageOcclusionEditor() } @@ -750,7 +750,7 @@ class NoteEditorFragment : val deckTextView = requireView().findViewById(R.id.CardEditorDeckText) // If edit mode and more than one card template distinguish between "Deck" and "Card deck" if (!addNote && editorNote!!.notetype.templates.length() > 1) { - deckTextView.setText(R.string.CardEditorCardDeck) + deckTextView.setText(CommonString.CardEditorCardDeck) } deckId = requireArguments().getLong(EXTRA_DID, deckId) @@ -786,7 +786,7 @@ class NoteEditorFragment : setNote(editorNote, FieldChangeType.onActivityCreation(shouldReplaceNewlines())) if (addNote) { noteTypeSpinner!!.onItemSelectedListener = SetNoteTypeListener() - requireAnkiActivity().setToolbarText(titleRes = R.string.menu_add) + requireAnkiActivity().setToolbarText(titleRes = CommonString.menu_add) // set information transferred by intent var contents: String? = null val tags = requireArguments().getStringArray(EXTRA_TAGS) @@ -801,7 +801,7 @@ class NoteEditorFragment : changeNoteType(noteType.id) } } catch (e: NoSuchElementException) { - showSnackbar(R.string.missing_note_type) + showSnackbar(CommonString.missing_note_type) // setting the text to null & caller to CALLER_NO_CALLER would skip adding text/image to edit field sourceText = null caller = NoteEditorCaller.NO_CALLER @@ -843,7 +843,7 @@ class NoteEditorFragment : reloadRequired = true closeNoteEditorAfterSave() } - requireAnkiActivity().setTitle(R.string.cardeditor_title_edit_card) + requireAnkiActivity().setTitle(CommonString.cardeditor_title_edit_card) } requireView().findViewById(R.id.CardEditorTagButton).setOnClickListener { Timber.i("NoteEditor:: Tags button pressed... opening tags editor") @@ -960,7 +960,7 @@ class NoteEditorFragment : cameraLauncher.launch(photoURI) } catch (_: ActivityNotFoundException) { Timber.w("No app found to handle image capture") - activity?.showSnackbar(R.string.activity_start_failed) + activity?.showSnackbar(CommonString.activity_start_failed) } } } @@ -1044,7 +1044,7 @@ class NoteEditorFragment : insertCloze(if (event.isAltPressed) AddClozeType.SAME_NUMBER else AddClozeType.INCREMENT_NUMBER) // Anki Desktop warns, but still inserts the cloze if (!isClozeType) { - showSnackbar(R.string.note_editor_insert_cloze_no_cloze_note_type) + showSnackbar(CommonString.note_editor_insert_cloze_no_cloze_note_type) } return true } @@ -1150,12 +1150,12 @@ class NoteEditorFragment : return false } } - showSnackbar(resources.getString(R.string.intent_aedict_empty)) + showSnackbar(resources.getString(CommonString.intent_aedict_empty)) return true } } } - showSnackbar(resources.getString(R.string.intent_aedict_category)) + showSnackbar(resources.getString(CommonString.intent_aedict_category)) return true } @@ -1256,7 +1256,7 @@ class NoteEditorFragment : if (closeEditorAfterSave) { if (caller == NoteEditorCaller.NOTEEDITOR_INTENT_ADD || aedictIntent) { - showThemedToast(requireContext(), R.string.note_message, shortLength = true) + showThemedToast(requireContext(), CommonString.note_message, shortLength = true) } closeNoteEditor(closeIntent ?: Intent()) } else { @@ -1269,7 +1269,7 @@ class NoteEditorFragment : private suspend fun saveNoteWithProgress() { // adding current note to collection val changes = - requireActivity().withProgress(resources.getString(R.string.saving_facts)) { + requireActivity().withProgress(resources.getString(CommonString.saving_facts)) { undoableOp { addNote(editorNote!!, deckId) } @@ -1307,7 +1307,7 @@ class NoteEditorFragment : addNoteJob.launch { val noteFieldsCheck = checkNoteFieldsResponse(editorNote!!) if (noteFieldsCheck is NoteFieldsCheckResult.Failure) { - addNoteErrorMessage = noteFieldsCheck.localizedMessage ?: getString(R.string.something_wrong) + addNoteErrorMessage = noteFieldsCheck.localizedMessage ?: getString(CommonString.something_wrong) displayErrorSavingNote() return@launch } @@ -1437,9 +1437,9 @@ class NoteEditorFragment : isChecked = noteEditorActivity?.isPreviewerVisible == true title = if (isChecked) { - getString(R.string.note_editor_hide_previewer) + getString(CommonString.note_editor_hide_previewer) } else { - getString(R.string.note_editor_show_previewer) + getString(CommonString.note_editor_show_previewer) } } if (editFields != null) { @@ -1532,7 +1532,7 @@ class NoteEditorFragment : R.id.action_font_size -> { Timber.i("NoteEditor:: Font Size button pressed") val fontSizeDialog = IntegerDialog() - fontSizeDialog.setArgs(getString(R.string.menu_font_size), editTextFontSize, 2) + fontSizeDialog.setArgs(getString(CommonString.menu_font_size), editTextFontSize, 2) fontSizeDialog.setCallbackRunnable { fontSizeSp: Int? -> setFontSize(fontSizeSp) } showDialogFragment(fontSizeDialog) return true @@ -1922,9 +1922,9 @@ class NoteEditorFragment : previous.lastViewInTabOrder.nextFocusForwardId = R.id.CardEditorTagButton } mediaButton.contentDescription = - getString(R.string.multimedia_editor_attach_mm_content, editLineView.name) + getString(CommonString.multimedia_editor_attach_mm_content, editLineView.name) toggleStickyButton.contentDescription = - getString(R.string.note_editor_toggle_sticky, editLineView.name) + getString(CommonString.note_editor_toggle_sticky, editLineView.name) editLineView.isVisible = i !in indicesToHide fieldsLayoutContainer!!.addView(editLineView) @@ -1937,7 +1937,7 @@ class NoteEditorFragment : ): ActionMode.Callback = CustomActionModeCallback( isClozeType, - getString(R.string.multimedia_editor_popup_cloze), + getString(CommonString.multimedia_editor_popup_cloze), clozeMenuId, onActionItemSelected = { mode, item -> if (item.itemId == clozeMenuId) { @@ -1967,11 +1967,11 @@ class NoteEditorFragment : val limitStr = Backend.MAX_INDIVIDUAL_MEDIA_FILE_SIZE.toBytesShortString(context) MaterialAlertDialogBuilder(context).show { - title(R.string.media_file_size_warning_title) + title(CommonString.media_file_size_warning_title) iconAttr(R.drawable.ic_warning) - message(text = getString(R.string.media_file_size_warning_message, fileName, fileSizeStr, limitStr)) - positiveButton(R.string.dialog_cancel) - negativeButton(R.string.media_file_size_add_anyway) { + message(text = getString(CommonString.media_file_size_warning_message, fileName, fileSizeStr, limitStr)) + positiveButton(CommonString.dialog_cancel) + negativeButton(CommonString.media_file_size_add_anyway) { onForceAdd() } } @@ -2345,8 +2345,8 @@ class NoteEditorFragment : val drawable = ResourcesCompat.getDrawable(resources, R.drawable.ic_add_toolbar_icon, null) drawable!!.setTint(MaterialColors.getColor(requireContext(), R.attr.toolbarIconColor, 0)) val addButton = toolbar.insertItem(0, drawable) { displayAddToolbarDialog() } - addButton.contentDescription = resources.getString(R.string.add_toolbar_item) - addButton.setTooltipTextCompat(resources.getString(R.string.add_toolbar_item)) + addButton.contentDescription = resources.getString(CommonString.add_toolbar_item) + addButton.setTooltipTextCompat(resources.getString(CommonString.add_toolbar_item)) } private val toolbarButtons: ArrayList @@ -2402,12 +2402,12 @@ class NoteEditorFragment : editToolbarItemDialog: AlertDialog, ) { AlertDialog.Builder(requireContext()).show { - title(R.string.remove_toolbar_item) - positiveButton(R.string.dialog_positive_delete) { + title(CommonString.remove_toolbar_item) + positiveButton(CommonString.dialog_positive_delete) { editToolbarItemDialog.dismiss() removeButton(button) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } @@ -2422,16 +2422,16 @@ class NoteEditorFragment : get() = AlertDialog .Builder(requireContext()) - .neutralButton(R.string.help) { + .neutralButton(CommonString.help) { requireContext().openUrl(R.string.link_manual_note_format_toolbar) - }.negativeButton(R.string.dialog_cancel) + }.negativeButton(CommonString.dialog_cancel) private fun displayAddToolbarDialog() { val v = layoutInflater.inflate(R.layout.dialog_note_editor_toolbar_add_custom_item, null) toolbarDialog.show { - title(R.string.add_toolbar_item) + title(CommonString.add_toolbar_item) setView(v) - positiveButton(R.string.dialog_positive_create) { + positiveButton(CommonString.dialog_positive_create) { val etIcon = v.findViewById(R.id.note_editor_toolbar_item_icon) val et = v.findViewById(R.id.note_editor_toolbar_before) val et2 = v.findViewById(R.id.note_editor_toolbar_after) @@ -2452,7 +2452,7 @@ class NoteEditorFragment : val editToolbarDialog = toolbarDialog .setView(view) - .positiveButton(R.string.save) { + .positiveButton(CommonString.save) { editToolbarButton( etIcon.text.toString(), et.text.toString(), @@ -2480,15 +2480,15 @@ class NoteEditorFragment : get() = ShortcutGroup( listOf( - shortcut("Ctrl+ENTER") { getString(R.string.save) }, + shortcut("Ctrl+ENTER") { getString(CommonString.save) }, shortcut("Ctrl+D") { TR.sentenceCase.selectDeck }, - shortcut("Ctrl+L") { getString(R.string.card_template_editor_group) }, - shortcut("Ctrl+N") { getString(R.string.select_note_type) }, - shortcut("Ctrl+Shift+T") { getString(R.string.tag_editor) }, - shortcut("Ctrl+Shift+C") { getString(R.string.multimedia_editor_popup_cloze) }, - shortcut("Ctrl+P") { getString(R.string.card_editor_preview_card) }, + shortcut("Ctrl+L") { getString(CommonString.card_template_editor_group) }, + shortcut("Ctrl+N") { getString(CommonString.select_note_type) }, + shortcut("Ctrl+Shift+T") { getString(CommonString.tag_editor) }, + shortcut("Ctrl+Shift+C") { getString(CommonString.multimedia_editor_popup_cloze) }, + shortcut("Ctrl+P") { getString(CommonString.card_editor_preview_card) }, ), - R.string.note_editor_group, + CommonString.note_editor_group, ) private fun updateTags() { @@ -2497,7 +2497,7 @@ class NoteEditorFragment : } tagsButton!!.text = resources.getString( - R.string.CardEditorTags, + CommonString.CardEditorTags, getColUnsafe.tags .join(getColUnsafe.tags.canonify(selectedTags!!)) .trim() @@ -2535,7 +2535,7 @@ class NoteEditorFragment : } cardsButton!!.text = HtmlCompat.fromHtml( - resources.getString(R.string.CardEditorCards, cardsList.toString()), + resources.getString(CommonString.CardEditorCards, cardsList.toString()), HtmlCompat.FROM_HTML_MODE_LEGACY, ) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteTypeFieldEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteTypeFieldEditor.kt index 6cf6309ab92d..b24855f06423 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteTypeFieldEditor.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteTypeFieldEditor.kt @@ -153,7 +153,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito val noteTypeID = intent.getLongExtra(EXTRA_NOTETYPE_ID, 0) val collectionModel = getColUnsafe.notetypes.get(noteTypeID) if (collectionModel == null) { - showThemedToast(this, R.string.field_editor_model_not_available, true) + showThemedToast(this, CommonString.field_editor_model_not_available, true) finish() return } @@ -193,11 +193,11 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito } input = input.substring(offset).trim() if (input.isEmpty()) { - showThemedToast(this, resources.getString(R.string.toast_empty_name), true) + showThemedToast(this, resources.getString(CommonString.toast_empty_name), true) return null } if (fieldsLabels.any { input == it }) { - showThemedToast(this, resources.getString(R.string.toast_duplicate_field), true) + showThemedToast(this, resources.getString(CommonString.toast_duplicate_field), true) return null } return input @@ -216,7 +216,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito AlertDialog.Builder(this).show { customView(view = fieldNameInput, paddingStart = 64, paddingEnd = 64, paddingTop = 32) title(text = TR.sentenceCase.addField) - positiveButton(R.string.menu_add) { + positiveButton(CommonString.menu_add) { // Name is valid, now field is added val fieldName = uniqueName(fieldNameInput) try { @@ -226,7 +226,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito // Create dialogue to for schema change val c = ConfirmationDialog() - c.setArgs(resources.getString(R.string.full_sync_confirmation)) + c.setArgs(resources.getString(CommonString.full_sync_confirmation)) val confirm = Runnable { try { @@ -242,7 +242,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito getColUnsafe.notetypes.update(notetype) initialize() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } } @@ -290,7 +290,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito } if (fieldsLabels.size < 2) { - showThemedToast(this, resources.getString(R.string.toast_last_field), true) + showThemedToast(this, resources.getString(CommonString.toast_last_field), true) } else { try { getColUnsafe.modSchema(check = true) @@ -298,8 +298,8 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito ConfirmationDialog().let { it.setArgs( title = fieldName, - message = resources.getString(R.string.field_delete_warning), - positiveButtonText = getString(R.string.dialog_positive_delete), + message = resources.getString(CommonString.field_delete_warning), + positiveButtonText = getString(CommonString.dialog_positive_delete), ) it.setConfirm(confirm) showDialogFragment(it) @@ -308,7 +308,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito e.log() ConfirmationDialog().let { it.setConfirm(confirm) - it.setArgs(resources.getString(R.string.full_sync_confirmation)) + it.setArgs(resources.getString(CommonString.full_sync_confirmation)) showDialogFragment(it) } } @@ -318,7 +318,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito private fun deleteField() { launchCatchingTask { Timber.d("doInBackGroundDeleteField") - withProgress(message = getString(R.string.model_field_editor_changing)) { + withProgress(message = getString(CommonString.model_field_editor_changing)) { val result = withCol { try { @@ -350,8 +350,8 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito fieldNameInput.moveCursorToEnd() AlertDialog.Builder(this).show { customView(view = fieldNameInput, paddingStart = 64, paddingEnd = 64, paddingTop = 32) - title(R.string.model_field_editor_rename) - positiveButton(R.string.rename) { + title(CommonString.model_field_editor_rename) + positiveButton(CommonString.rename) { if (uniqueName(fieldNameInput) == null) { return@positiveButton } @@ -363,7 +363,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito // Handler mod schema confirmation val c = ConfirmationDialog() - c.setArgs(resources.getString(R.string.full_sync_confirmation)) + c.setArgs(resources.getString(CommonString.full_sync_confirmation)) val confirm = Runnable { getColUnsafe.modSchema(check = false) @@ -378,7 +378,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito this@NoteTypeFieldEditor.showDialogFragment(c) } } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } } @@ -400,12 +400,12 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito AlertDialog .Builder(this) .show { - title(R.string.model_field_editor_reposition_menu) + title(CommonString.model_field_editor_reposition_menu) positiveButton(text = TR.actionsReposition()) { val input = (it as AlertDialog).getInputField() result(input.text.toString().toInt()) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) setMessage(TR.fieldsNewPosition1(numberOfTemplates)) setView(R.layout.dialog_generic_text_input) }.input( @@ -432,7 +432,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito // Handle mod schema confirmation val c = ConfirmationDialog() - c.setArgs(resources.getString(R.string.full_sync_confirmation)) + c.setArgs(resources.getString(CommonString.full_sync_confirmation)) val confirm = Runnable { try { @@ -450,7 +450,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito private fun repositionField(index: Int) { launchCatchingTask { - withProgress(message = getString(R.string.model_field_editor_changing)) { + withProgress(message = getString(CommonString.model_field_editor_changing)) { val result = withCol { Timber.d("doInBackgroundRepositionField") @@ -497,7 +497,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito e.log() // Handler mMod schema confirmation val c = ConfirmationDialog() - c.setArgs(resources.getString(R.string.full_sync_confirmation)) + c.setArgs(resources.getString(CommonString.full_sync_confirmation)) val confirm = Runnable { getColUnsafe.modSchema(check = false) @@ -512,7 +512,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito notetype: NotetypeJson, idx: Int, ) { - withProgress(resources.getString(R.string.model_field_editor_changing)) { + withProgress(resources.getString(CommonString.model_field_editor_changing)) { withCol { Timber.d("doInBackgroundChangeSortField") notetypes.setSortIndex(notetype, idx) @@ -548,7 +548,7 @@ class NoteTypeFieldEditor : AnkiActivity(R.layout.activity_note_type_field_edito */ private fun addFieldLocaleHint(selectedLocale: Locale) { setLanguageHintForField(getColUnsafe.notetypes, notetype, currentPos, selectedLocale) - val format = getString(R.string.model_field_editor_language_hint_dialog_success_result, selectedLocale.displayName) + val format = getString(CommonString.model_field_editor_language_hint_dialog_success_result, selectedLocale.displayName) showSnackbar(format, Snackbar.LENGTH_SHORT) initialize() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NotificationChannel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NotificationChannel.kt index 30a05d7c52f1..7d14741477a5 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NotificationChannel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NotificationChannel.kt @@ -68,8 +68,8 @@ enum class NotificationChannel( val importance: Int, ) { GENERAL("General Notifications", R.string.app_name, NotificationManagerCompat.IMPORTANCE_DEFAULT), - SYNC("Synchronization", R.string.sync_title, NotificationManagerCompat.IMPORTANCE_LOW), - REVIEW_REMINDERS("Review Reminders", R.string.review_reminders_title, NotificationManagerCompat.IMPORTANCE_DEFAULT), + SYNC("Synchronization", CommonString.sync_title, NotificationManagerCompat.IMPORTANCE_LOW), + REVIEW_REMINDERS("Review Reminders", CommonString.review_reminders_title, NotificationManagerCompat.IMPORTANCE_DEFAULT), ; fun getName(res: Resources) = res.getString(nameId) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ReadText.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ReadText.kt index 9a258e9009de..adb77a528c74 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ReadText.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ReadText.kt @@ -69,7 +69,7 @@ object ReadText { if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { showThemedToast( flashCardViewer.get()!!, - flashCardViewer.get()!!.getString(R.string.no_tts_available_message) + + flashCardViewer.get()!!.getString(CommonString.no_tts_available_message) + " (" + loc + ")", false, ) @@ -115,13 +115,13 @@ object ReadText { if (availableLocales().isEmpty()) { Timber.w("ReadText.textToSpeech() no TTS languages available") dialog - .message(R.string.no_tts_available_message) + .message(CommonString.no_tts_available_message) .setIcon(R.drawable.ic_warning) - .positiveButton(R.string.dialog_ok) + .positiveButton(CommonString.dialog_ok) } else { val localeMappings: List> = mutableListOf>().apply { - add(Pair(NO_TTS, res.getString(R.string.tts_no_tts))) // add option: "no tts" + add(Pair(NO_TTS, res.getString(CommonString.tts_no_tts))) // add option: "no tts" val (validLocales, invalidLocales) = availableLocales() .sortedWith(compareBy { it.displayName }) @@ -137,7 +137,7 @@ object ReadText { } Timber.i("showing 'select language' dialog") dialog - .title(R.string.select_locale_title) + .title(CommonString.select_locale_title) .setItems(localeMappings.map { it.second }.toTypedArray()) { _, index -> val locale = localeMappings[index].first Timber.d("ReadText.selectTts() user chose locale '%s'", locale) @@ -267,7 +267,7 @@ object ReadText { // they originally requested) showThemedToast( flashCardViewer.get()!!, - flashCardViewer.get()!!.getString(R.string.no_tts_available_message) + + flashCardViewer.get()!!.getString(CommonString.no_tts_available_message) + " (" + originalLocaleCode + ")", false, ) @@ -301,7 +301,7 @@ object ReadText { Timber.d("TTS initialized and available languages found") (context as AbstractFlashcardViewer).ttsInitialized() } else { - ankiActivityContext?.showSnackbar(R.string.no_tts_available_message) + ankiActivityContext?.showSnackbar(CommonString.no_tts_available_message) Timber.w("TTS initialized but no available languages found") } textToSpeech!!.setOnUtteranceProgressListener( @@ -324,8 +324,8 @@ object ReadText { val ankiActivity = context as AnkiActivity ankiActivity.mayOpenUrl(helpUrl) // TODO: We can do better in this UI now we have a reason for failure - ankiActivity.showSnackbar(R.string.no_tts_available_message) { - setAction(R.string.help) { openTtsHelpUrl(helpUrl) } + ankiActivity.showSnackbar(CommonString.no_tts_available_message) { + setAction(CommonString.help) { openTtsHelpUrl(helpUrl) } } } @@ -342,7 +342,7 @@ object ReadText { }, ) } else { - showThemedToast(context, context.getString(R.string.no_tts_available_message), false) + showThemedToast(context, context.getString(CommonString.no_tts_available_message), false) Timber.w("TTS not successfully initialized") } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt index bd00120241c1..886924b0fed4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt @@ -836,10 +836,10 @@ open class Reviewer : if (whiteboard != null) { try { val savedWhiteboardFileName = whiteboard!!.saveWhiteboard(TimeManager.time).path - showSnackbar(getString(R.string.white_board_image_saved, savedWhiteboardFileName), Snackbar.LENGTH_SHORT) + showSnackbar(getString(CommonString.white_board_image_saved, savedWhiteboardFileName), Snackbar.LENGTH_SHORT) } catch (e: Exception) { Timber.w(e) - showSnackbar(getString(R.string.white_board_image_save_failed, e.localizedMessage), Snackbar.LENGTH_SHORT) + showSnackbar(getString(CommonString.white_board_image_save_failed, e.localizedMessage), Snackbar.LENGTH_SHORT) } } } @@ -938,10 +938,10 @@ open class Reviewer : if (isEraserMode) { startMonitoringEraserButtonRipple() - showSnackbar(getString(R.string.white_board_eraser_enabled), 1000) + showSnackbar(getString(CommonString.white_board_eraser_enabled), 1000) } else { stopMonitoringEraserButtonRipple() - showSnackbar(getString(R.string.white_board_eraser_disabled), 1000) + showSnackbar(getString(CommonString.white_board_eraser_disabled), 1000) } } } @@ -1097,7 +1097,7 @@ open class Reviewer : CrashReportService.sendExceptionReport(e, "Unable to create recorder tool bar") showThemedToast( this, - this.getText(R.string.multimedia_editor_audio_view_create_failed).toString(), + this.getText(CommonString.multimedia_editor_audio_view_create_failed).toString(), true, ) } @@ -1147,7 +1147,7 @@ open class Reviewer : @NeedsTest("Starting animation from swipe is inverse to the finishing one") protected fun openCardInfo(fromGesture: Gesture? = null) { if (currentCard == null) { - showSnackbar(getString(R.string.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) + showSnackbar(getString(CommonString.multimedia_editor_something_wrong), Snackbar.LENGTH_SHORT) return } Timber.i("opening card info") @@ -1182,7 +1182,7 @@ open class Reviewer : val alpha = Themes.ALPHA_ICON_ENABLED_LIGHT val markCardIcon = menu.findItem(R.id.action_mark_card) if (currentCard != null && isMarked(getColUnsafe, currentCard!!.note(getColUnsafe))) { - markCardIcon.setTitle(R.string.menu_unmark_note).setIcon(R.drawable.ic_star_white) + markCardIcon.setTitle(CommonString.menu_unmark_note).setIcon(R.drawable.ic_star_white) } else { markCardIcon.setTitle(TR.sentenceCase.markNote).setIcon(R.drawable.ic_star_border_white) } @@ -1242,7 +1242,7 @@ open class Reviewer : if (colIsOpenUnsafe()) { // Required mostly because there are tests where `col` is null if (whiteboardIsShownAndHasStrokes) { // Make Undo action title to whiteboard Undo specific one - undoIcon.title = resources.getString(R.string.undo_action_whiteboard_last_stroke) + undoIcon.title = resources.getString(CommonString.undo_action_whiteboard_last_stroke) // Show whiteboard Eraser action button if (!actionButtons.status.toggleEraserIsDisabled()) { @@ -1260,7 +1260,7 @@ open class Reviewer : // In this case, there is no object word for the verb, "Undo", // so in some languages such as Japanese, which have pre/post-positional particle with the object, // we need to use the string for just "Undo" instead of the string for "Undo %s". - undoIcon.title = resources.getString(R.string.undo) + undoIcon.title = resources.getString(CommonString.undo) undoIcon.iconAlpha = Themes.ALPHA_ICON_DISABLED_LIGHT } } @@ -1276,7 +1276,7 @@ open class Reviewer : iconAlpha = Themes.ALPHA_ICON_ENABLED_LIGHT isEnabled = true } else { - setTitle(R.string.redo) + setTitle(CommonString.redo) iconAlpha = Themes.ALPHA_ICON_DISABLED_LIGHT isEnabled = false } @@ -1296,7 +1296,7 @@ open class Reviewer : // White board button if (prefWhiteboard) { // Configure the whiteboard related items in the action bar - toggleWhiteboardIcon.setTitle(R.string.disable_whiteboard) + toggleWhiteboardIcon.setTitle(CommonString.disable_whiteboard) // Always allow "Disable Whiteboard", even if "Enable Whiteboard" is disabled toggleWhiteboardIcon.isVisible = true if (!actionButtons.status.toggleStylusIsDisabled()) { @@ -1322,29 +1322,29 @@ open class Reviewer : // "hide whiteboard" icon whiteboardIcon.alpha = Themes.ALPHA_ICON_ENABLED_LIGHT hideWhiteboardIcon.icon = whiteboardIcon - hideWhiteboardIcon.setTitle(R.string.hide_whiteboard) + hideWhiteboardIcon.setTitle(CommonString.hide_whiteboard) whiteboardColorPaletteIcon.alpha = Themes.ALPHA_ICON_ENABLED_LIGHT // eraser icon toggleEraserIcon.icon = eraserIcon if (isEraserMode) { - toggleEraserIcon.setTitle(R.string.disable_eraser) + toggleEraserIcon.setTitle(CommonString.disable_eraser) } else { // default - toggleEraserIcon.setTitle(R.string.enable_eraser) + toggleEraserIcon.setTitle(CommonString.enable_eraser) } // whiteboard editor icon changePenColorIcon.icon = whiteboardColorPaletteIcon if (toggleStylus) { - toggleStylusIcon.setTitle(R.string.disable_stylus) + toggleStylusIcon.setTitle(CommonString.disable_stylus) stylusIcon.alpha = Themes.ALPHA_ICON_ENABLED_LIGHT } else { - toggleStylusIcon.setTitle(R.string.enable_stylus) + toggleStylusIcon.setTitle(CommonString.enable_stylus) stylusIcon.alpha = Themes.ALPHA_ICON_DISABLED_LIGHT } toggleStylusIcon.icon = stylusIcon } else { whiteboardIcon.alpha = Themes.ALPHA_ICON_DISABLED_LIGHT hideWhiteboardIcon.icon = whiteboardIcon - hideWhiteboardIcon.setTitle(R.string.show_whiteboard) + hideWhiteboardIcon.setTitle(CommonString.show_whiteboard) whiteboardColorPaletteIcon.alpha = Themes.ALPHA_ICON_DISABLED_LIGHT stylusIcon.alpha = Themes.ALPHA_ICON_DISABLED_LIGHT toggleStylusIcon.isEnabled = false @@ -1354,7 +1354,7 @@ open class Reviewer : colorPalette.visibility = View.GONE } } else { - toggleWhiteboardIcon.setTitle(R.string.enable_whiteboard) + toggleWhiteboardIcon.setTitle(CommonString.enable_whiteboard) } if (colIsOpenUnsafe() && getColUnsafe.decks.isFiltered(parentDid)) { menu.findItem(R.id.action_open_deck_options).isVisible = false @@ -1373,11 +1373,11 @@ open class Reviewer : val voicePlaybackIcon = menu.findItem(R.id.action_toggle_mic_tool_bar) if (isMicToolBarVisible) { - voicePlaybackIcon.setTitle(R.string.menu_disable_voice_playback) + voicePlaybackIcon.setTitle(CommonString.menu_disable_voice_playback) // #18477: always show 'disable', even if 'enable' was invisible voicePlaybackIcon.isVisible = true } else { - voicePlaybackIcon.setTitle(R.string.menu_enable_voice_playback) + voicePlaybackIcon.setTitle(CommonString.menu_enable_voice_playback) } increaseHorizontalPaddingOfOverflowMenuIcons(menu) @@ -1446,10 +1446,10 @@ open class Reviewer : easeButton1!!.setColor(background[0]) easeButton4!!.setColor(background[3]) // Ease 2 is "hard" - easeButton2!!.setup(background[1], textColor[1], R.string.ease_button_hard) + easeButton2!!.setup(background[1], textColor[1], CommonString.ease_button_hard) easeButton2!!.requestFocus() // Ease 3 is good - easeButton3!!.setup(background[2], textColor[2], R.string.ease_button_good) + easeButton3!!.setup(background[2], textColor[2], CommonString.ease_button_good) easeButton4!!.setVisibility(View.VISIBLE) easeButton3!!.requestFocus() @@ -1591,9 +1591,9 @@ open class Reviewer : state.topCard.load(getColUnsafe) val leechMessage: String = if (state.topCard.queue.buriedOrSuspended()) { - resources.getString(R.string.leech_suspend_notification) + resources.getString(CommonString.leech_suspend_notification) } else { - resources.getString(R.string.leech_notification) + resources.getString(CommonString.leech_notification) } showSnackbar(leechMessage, Snackbar.LENGTH_SHORT) } @@ -1609,14 +1609,14 @@ open class Reviewer : private suspend fun dealWithTimeBox(timebox: Collection.TimeboxReached) { val nCards = timebox.reps val nMins = timebox.secs / 60 - val mins = resources.getQuantityString(R.plurals.in_minutes, nMins, nMins) - val timeboxMessage = resources.getQuantityString(R.plurals.timebox_reached, nCards, nCards, mins) + val mins = resources.getQuantityString(CommonPlurals.in_minutes, nMins, nMins) + val timeboxMessage = resources.getQuantityString(CommonPlurals.timebox_reached, nCards, nCards, mins) suspendCancellableCoroutine { coroutines -> Timber.i("Showing timebox reached dialog") AlertDialog.Builder(this).show { - title(R.string.timebox_reached_title) + title(CommonString.timebox_reached_title) message(text = timeboxMessage) - positiveButton(R.string.dialog_continue) { + positiveButton(CommonString.dialog_continue) { coroutines.resume(Unit) } negativeButton(text = CollectionManager.TR.studyingFinish()) { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt index 07d0bdd8616a..c4f18b8e9b03 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt @@ -243,7 +243,7 @@ class StudyOptionsFragment : R.id.action_rebuild -> { Timber.i("StudyOptionsFragment:: rebuild cram deck button pressed") launchCatchingTask { - withProgress(R.string.rebuild_filtered_deck) { + withProgress(CommonString.rebuild_filtered_deck) { viewModel.rebuildCram() } } @@ -252,7 +252,7 @@ class StudyOptionsFragment : R.id.action_empty -> { Timber.i("StudyOptionsFragment:: empty cram deck button pressed") launchCatchingTask { - withProgress(R.string.empty_filtered_deck) { + withProgress(CommonString.empty_filtered_deck) { viewModel.emptyCram() } } @@ -272,7 +272,7 @@ class StudyOptionsFragment : menu.findItem(R.id.action_rebuild)?.isVisible = true menu.findItem(R.id.action_empty)?.isVisible = true menu.findItem(R.id.action_custom_study)?.isVisible = false - menu.findItem(R.id.action_deck_or_study_options)?.setTitle(R.string.menu__study_options) + menu.findItem(R.id.action_deck_or_study_options)?.setTitle(CommonString.menu__study_options) } else { menu.findItem(R.id.action_rebuild)?.isVisible = false menu.findItem(R.id.action_empty)?.isVisible = false @@ -344,7 +344,7 @@ class StudyOptionsFragment : is StudyOptionsState.StudyOptions -> { deckInfoLayout.visibility = View.VISIBLE buttonStart.visibility = View.VISIBLE - buttonStart.setText(R.string.studyoptions_start) + buttonStart.setText(CommonString.studyoptions_start) } is StudyOptionsState.Loading -> return } @@ -365,7 +365,7 @@ class StudyOptionsFragment : @Language("HTML") val desc: String = if (isDynamic) { - resources.getString(R.string.dyn_deck_desc) + resources.getString(CommonString.dyn_deck_desc) } else { description ?: "" } @@ -400,7 +400,7 @@ class StudyOptionsFragment : when { count > 0 -> requireContext().resources.getQuantityString( - R.plurals.studyoptions_buried_count, + CommonPlurals.studyoptions_buried_count, count, count, ) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt index d4b17d20a546..54ea704fb7d4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt @@ -156,7 +156,7 @@ private suspend fun handleNormalSync( } }, onCancel = ::cancelSync, - manualCancelButton = R.string.dialog_cancel, + manualCancelButton = CommonString.dialog_cancel, ) { withColExclusive( operation = CollectionOperation.SYNC, @@ -188,7 +188,7 @@ private suspend fun handleNormalSync( SyncCollectionResponse.ChangesRequired.NO_CHANGES -> { // scheduler version may have changed withCol { _loadScheduler() } - val message = if (syncMedia) R.string.col_synced_media_in_background else R.string.sync_database_acknowledge + val message = if (syncMedia) CommonString.col_synced_media_in_background else CommonString.sync_database_acknowledge deckPicker.showSyncLogMessage(message, output.serverMessage) deckPicker.refreshState() if (syncMedia) { @@ -238,7 +238,7 @@ private suspend fun handleDownload( progressContext = ProgressContext.ofBytes(context = deckPicker).copy(separator = "\n"), extractProgress = fullDownloadProgress(TR.syncDownloadingFromAnkiweb()), onCancel = ::cancelSync, - manualCancelButton = R.string.dialog_cancel, + manualCancelButton = CommonString.dialog_cancel, ) { withColExclusive( operation = CollectionOperation.FULL_DOWNLOAD, @@ -263,7 +263,7 @@ private suspend fun handleDownload( } Timber.i("Full Download Completed") - deckPicker.showSyncLogMessage(R.string.backup_one_way_sync_from_server, "") + deckPicker.showSyncLogMessage(CommonString.backup_one_way_sync_from_server, "") } private suspend fun handleUpload( @@ -276,7 +276,7 @@ private suspend fun handleUpload( progressContext = ProgressContext.ofBytes(context = deckPicker).copy(separator = "\n"), extractProgress = fullDownloadProgress(TR.syncUploadingToAnkiweb()), onCancel = ::cancelSync, - manualCancelButton = R.string.dialog_cancel, + manualCancelButton = CommonString.dialog_cancel, ) { withColExclusive( operation = CollectionOperation.FULL_UPLOAD, @@ -295,7 +295,7 @@ private suspend fun handleUpload( } } Timber.i("Full Upload Completed") - deckPicker.showSyncLogMessage(R.string.sync_log_uploading_message, "") + deckPicker.showSyncLogMessage(CommonString.sync_log_uploading_message, "") } fun cancelMediaSync(backend: Backend) { @@ -326,7 +326,7 @@ suspend fun monitorMediaSync(deckPicker: DeckPicker) { .Builder(deckPicker) .setTitle(with(deckPicker) { TR.sentenceCase.mediaSyncLog }) .setMessage("") - .setPositiveButton(R.string.dialog_continue) { _, _ -> + .setPositiveButton(CommonString.dialog_continue) { _, _ -> scope.cancel() }.setNegativeButton(TR.syncAbortButton()) { _, _ -> isAborted = true diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginFragment.kt index e335438adef3..1a6dd2b8711a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginFragment.kt @@ -29,6 +29,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.R import com.ichi2.anki.account.AccountActivity.Companion.START_FROM_DECKPICKER @@ -287,11 +288,11 @@ class LoginFragment : Fragment(R.layout.fragment_my_account) { MaterialAlertDialogBuilder(requireContext()).show { Timber.i("Showing dialog: 'Sync now?'") - setTitle(R.string.login_successful) + setTitle(CommonString.login_successful) setIcon(R.drawable.ic_sync) - setMessage(R.string.sync_now) - positiveButton(R.string.button_sync) { openDeckPickerAndSync() } - negativeButton(R.string.dialog_continue) { showLoggedInView() } + setMessage(CommonString.sync_now) + positiveButton(CommonString.button_sync) { openDeckPickerAndSync() } + negativeButton(CommonString.dialog_continue) { showLoggedInView() } setOnCancelListener { showLoggedInView() } } } @@ -316,7 +317,7 @@ class LoginFragment : Fragment(R.layout.fragment_my_account) { lifecycleScope.launch { requireActivity().withProgress( extractProgress = { - text = getString(R.string.sign_in) + text = getString(CommonString.sign_in) }, onCancel = { backend -> backend.setWantsAbort() }, ) { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginViewModel.kt index 84fff1b55249..7db3b11b4ded 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/account/LoginViewModel.kt @@ -23,7 +23,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import anki.sync.SyncAuth import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.settings.Prefs import kotlinx.coroutines.flow.MutableStateFlow @@ -128,8 +128,8 @@ class LoginViewModel : ViewModel() { enum class LoginError( @StringRes private val messageResId: Int, ) { - EMPTY_USERNAME(R.string.invalid_email), - EMPTY_PASSWORD(R.string.password_empty), + EMPTY_USERNAME(CommonString.invalid_email), + EMPTY_PASSWORD(CommonString.password_empty), ; fun toHumanReadableString(context: Context): String = context.getString(this.messageResId) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt index 9b1a21f9d05d..cd1729bcd8d0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt @@ -10,6 +10,7 @@ import android.content.DialogInterface import android.os.Bundle import android.view.View import androidx.core.content.edit +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.crashreporting.CrashReporter @@ -38,9 +39,9 @@ class AnkiDroidCrashReportDialog : super.onCreate(savedInstanceState) val dialogBuilder = AlertDialog.Builder(this) dialogBuilder.setIcon(R.drawable.logo_star_144dp) - dialogBuilder.setTitle(R.string.feedback_title) - dialogBuilder.setPositiveButton(getString(R.string.feedback_report), this@AnkiDroidCrashReportDialog) - dialogBuilder.setNegativeButton(R.string.dialog_cancel, this@AnkiDroidCrashReportDialog) + dialogBuilder.setTitle(CommonString.feedback_title) + dialogBuilder.setPositiveButton(getString(CommonString.feedback_report), this@AnkiDroidCrashReportDialog) + dialogBuilder.setNegativeButton(CommonString.dialog_cancel, this@AnkiDroidCrashReportDialog) helper = CrashReportDialogHelper(this, intent) dialogBuilder.setView(buildCustomView(savedInstanceState)) val dialog = dialogBuilder.create() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/android/back/ExitViaDoubleTapBackBackPressCallback.kt b/AnkiDroid/src/main/java/com/ichi2/anki/android/back/ExitViaDoubleTapBackBackPressCallback.kt index 8771ce53bf67..c79ceb306046 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/android/back/ExitViaDoubleTapBackBackPressCallback.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/android/back/ExitViaDoubleTapBackBackPressCallback.kt @@ -7,6 +7,7 @@ import androidx.activity.OnBackPressedCallback import androidx.preference.PreferenceManager import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.android.appContext import com.ichi2.anki.common.utils.android.HandlerUtils @@ -57,7 +58,7 @@ fun AnkiActivity.exitViaDoubleTapBackCallback(): OnBackPressedCallback = lateinit var strongListenerReference: OnSharedPreferenceChangeListener override fun handleOnBackPressed() { - showSnackbar(R.string.back_pressed_once, Snackbar.LENGTH_SHORT) + showSnackbar(CommonString.back_pressed_once, Snackbar.LENGTH_SHORT) this.isEnabled = false HandlerUtils.executeFunctionWithDelay(2.seconds.inWholeMilliseconds) { this.isEnabled = true diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionAdapter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionAdapter.kt index 6dc3676e2dd3..75d9e53cf745 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionAdapter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionAdapter.kt @@ -8,6 +8,7 @@ import android.view.MotionEvent import android.view.ViewGroup import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.browser.BrowserColumnSelectionRecyclerItem.ColumnItem import com.ichi2.anki.browser.BrowserColumnSelectionRecyclerItem.UsageItem @@ -125,7 +126,7 @@ class BrowserColumnSelectionAdapter( val isExclude = absoluteAdapterPosition < positionOfAvailableHeading setImageResource(if (isExclude) R.drawable.ic_remove else R.drawable.ic_add) - val label = context.getString(if (isExclude) R.string.exclude_column else R.string.include_column) + val label = context.getString(if (isExclude) CommonString.exclude_column else CommonString.include_column) contentDescription = label if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { tooltipText = label diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionFragment.kt index a71b197dac0e..6ac488863e6a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserColumnSelectionFragment.kt @@ -21,6 +21,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.ichi2.anki.CardBrowser import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.browser.BrowserColumnSelectionRecyclerItem.ColumnItem import com.ichi2.anki.browser.BrowserColumnSelectionRecyclerItem.UsageItem @@ -251,8 +252,8 @@ enum class ColumnUsage( @StringRes val titleRes: Int, ) { /** A column displayed in Browse */ - ACTIVE(R.string.user_active_columns), + ACTIVE(CommonString.user_active_columns), /** A column which is not displayed in Browse */ - AVAILABLE(R.string.user_potential_columns), + AVAILABLE(CommonString.user_potential_columns), } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserColumn.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserColumn.kt index 3451423b9826..c98e4526259d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserColumn.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserColumn.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.browser import androidx.annotation.StringRes import anki.search.BrowserColumns import com.ichi2.anki.CardBrowser -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.model.CardsOrNotes import com.ichi2.anki.model.CardsOrNotes.CARDS @@ -196,16 +196,16 @@ fun BrowserColumns.Column.getTooltip(cardsOrNotes: CardsOrNotes): String? = fun ColumnType.humanReadableExplanation(descending: Boolean): Int? = if (descending) { when (this) { - ColumnType.TEXT -> R.string.card_browser_order_subtitle_text_descending - ColumnType.NUMERIC -> R.string.card_browser_order_subtitle_numeric_descending - ColumnType.DATE -> R.string.card_browser_order_subtitle_date_descending + ColumnType.TEXT -> CommonString.card_browser_order_subtitle_text_descending + ColumnType.NUMERIC -> CommonString.card_browser_order_subtitle_numeric_descending + ColumnType.DATE -> CommonString.card_browser_order_subtitle_date_descending ColumnType.UNSPECIFIED -> null } } else { when (this) { - ColumnType.TEXT -> R.string.card_browser_order_subtitle_text_ascending - ColumnType.NUMERIC -> R.string.card_browser_order_subtitle_numeric_ascending - ColumnType.DATE -> R.string.card_browser_order_subtitle_date_ascending + ColumnType.TEXT -> CommonString.card_browser_order_subtitle_text_ascending + ColumnType.NUMERIC -> CommonString.card_browser_order_subtitle_numeric_ascending + ColumnType.DATE -> CommonString.card_browser_order_subtitle_date_ascending ColumnType.UNSPECIFIED -> null } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserFragment.kt index 9c82be4302db..664915369676 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserFragment.kt @@ -66,6 +66,8 @@ import com.ichi2.anki.CardBrowser import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.getColUnsafe import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.Flag import com.ichi2.anki.R import com.ichi2.anki.android.input.ShortcutGroup @@ -580,7 +582,7 @@ class CardBrowserFragment : ) legacySearchView = (searchItem!!.actionView as CardBrowserSearchView).apply { - queryHint = resources.getString(R.string.card_browser_search_hint) + queryHint = resources.getString(CommonString.card_browser_search_hint) setMaxWidth(Integer.MAX_VALUE) setOnQueryTextListener( object : @@ -622,7 +624,7 @@ class CardBrowserFragment : if (vm.isInMultiSelectMode) return // qtMiscCreateFilteredDeck() contains QT Accelerators ('&') in Belarusian - menu.findItem(R.id.action_create_filtered_deck).title = getString(R.string.new_dynamic_deck) + menu.findItem(R.id.action_create_filtered_deck).title = getString(CommonString.new_dynamic_deck) saveSearchItem?.isVisible = legacySearchView?.query?.isNotEmpty() != false @@ -775,12 +777,12 @@ class CardBrowserFragment : this.title = if (vm.cardsOrNotes == CARDS) { resources.getQuantityString( - R.plurals.card_browser_export_cards, + CommonPlurals.card_browser_export_cards, vm.selectedRowCount(), ) } else { resources.getQuantityString( - R.plurals.card_browser_export_notes, + CommonPlurals.card_browser_export_notes, vm.selectedRowCount(), ) } @@ -799,7 +801,7 @@ class CardBrowserFragment : deleteNoteItem.apply { this.title = resources.getQuantityString( - R.plurals.card_browser_delete_notes, + CommonPlurals.card_browser_delete_notes, vm.selectedNoteCount(), ) } @@ -1008,7 +1010,7 @@ class CardBrowserFragment : showSnackbar(message, Snackbar.LENGTH_SHORT) { if (!searchAllDecks) return@showSnackbar - setAction(R.string.card_browser_search_all_decks) { + setAction(CommonString.card_browser_search_all_decks) { activityViewModel.setSelectedDeck(SelectableDeck.AllDecks) } } @@ -1024,7 +1026,7 @@ class CardBrowserFragment : ) SearchResultMessage.NoCardsInSelectedDeck -> showSnackbar( - getString(R.string.card_browser_no_cards_in_deck, selectedDeckNameForUi), + getString(CommonString.card_browser_no_cards_in_deck, selectedDeckNameForUi), searchAllDecks = true, ) } @@ -1100,14 +1102,14 @@ class CardBrowserFragment : toggleRowSelections.contentDescription = getString( when (selectionState) { - SELECT_ALL -> R.string.card_browser_select_all - SELECT_NONE -> R.string.card_browser_select_none + SELECT_ALL -> CommonString.card_browser_select_all + SELECT_NONE -> CommonString.card_browser_select_none }, ) } fun onSearchForDecks(unit: Unit) { - startDeckSelection(title = getString(R.string.search_deck), asChild = true, skipEmptyDefault = true) + startDeckSelection(title = getString(CommonString.search_deck), asChild = true, skipEmptyDefault = true) } fun advancedSearchChanged(inAdvancedSearch: Boolean) { @@ -1151,22 +1153,22 @@ class CardBrowserFragment : when (message) { UserMessage.SEARCH_SAVED -> showSnackbar( - R.string.card_browser_list_my_searches_successful_save, + CommonString.card_browser_list_my_searches_successful_save, Snackbar.LENGTH_SHORT, ) UserMessage.SAVED_SEARCH_DUPLICATE_ADDED -> showSnackbar( - R.string.card_browser_list_my_searches_new_search_error_dup, + CommonString.card_browser_list_my_searches_new_search_error_dup, Snackbar.LENGTH_SHORT, ) UserMessage.SAVED_SEARCH_DELETED -> showSnackbar( - R.string.card_browser_list_my_searches_successful_deleted, + CommonString.card_browser_list_my_searches_successful_deleted, Snackbar.LENGTH_SHORT, ) UserMessage.SAVED_SEARCH_NAME_DOES_NOT_EXIST -> showSnackbar( - R.string.something_wrong, + CommonString.something_wrong, Snackbar.LENGTH_SHORT, ) } @@ -1227,7 +1229,7 @@ class CardBrowserFragment : fun onChangeNoteType(result: ChangeNoteTypeResponse) = when (result) { ChangeNoteTypeResponse.NoSelection -> Timber.w("change note type: no selection") - ChangeNoteTypeResponse.MixedSelection -> showSnackbar(R.string.different_note_types_selected) + ChangeNoteTypeResponse.MixedSelection -> showSnackbar(CommonString.different_note_types_selected) is ChangeNoteTypeResponse.ChangeNoteType -> showDialogFragment(ChangeNoteTypeDialog.newInstance(result.noteIds)) } @@ -1242,10 +1244,10 @@ class CardBrowserFragment : val (title, subtitle) = when (notification) { is SortChangeNotification.NoOrdering -> - getString(R.string.card_browser_order_no_sorting_title) to null + getString(CommonString.card_browser_order_no_sorting_title) to null is SortChangeNotification.CollectionOrdering -> { val subtitleRes = notification.type.humanReadableExplanation(descending = notification.reverse) - getString(R.string.card_browser_order_snackbar_sort_by, notification.columnLabel) to + getString(CommonString.card_browser_order_snackbar_sort_by, notification.columnLabel) to subtitleRes?.let(::getString) } } @@ -1517,7 +1519,7 @@ class CardBrowserFragment : if (availableColumns.isEmpty()) { Timber.w("No available columns to replace ${selectedColumn.label}") - showSnackbar(R.string.no_columns_available) + showSnackbar(CommonString.no_columns_available) return@launch } @@ -1533,7 +1535,7 @@ class CardBrowserFragment : return } startDeckSelection( - title = getString(R.string.move_all_to_deck), + title = getString(CommonString.move_all_to_deck), allowAll = false, allowFiltered = false, requestKey = REQUEST_DECK_SELECTION_CHANGE_DECK, @@ -1558,7 +1560,7 @@ class CardBrowserFragment : */ fun openNoteEditorForCurrentlySelectedRow() { if (!activityViewModel.openNoteEditorForCurrentlySelectedRow()) { - showSnackbar(R.string.no_note_to_edit) + showSnackbar(CommonString.no_note_to_edit) } } @@ -1593,7 +1595,7 @@ class CardBrowserFragment : val message = when (result.wasBuried) { true -> TR.studyingCardsBuried(result.count) - false -> resources.getQuantityString(R.plurals.unbury_cards_feedback, result.count, result.count) + false -> resources.getQuantityString(CommonPlurals.unbury_cards_feedback, result.count, result.count) } showUndoSnackbar(message) } @@ -1632,8 +1634,8 @@ class CardBrowserFragment : // No selected cards can be repositioned showDialogFragment( SimpleMessageDialog.newInstance( - title = getString(R.string.vague_error), - message = getString(R.string.reposition_card_not_new_error), + title = getString(CommonString.vague_error), + message = getString(CommonString.reposition_card_not_new_error), reload = false, ), ) @@ -1643,7 +1645,7 @@ class CardBrowserFragment : val top = repositionCardsResult.queueTop val bottom = repositionCardsResult.queueBottom if (top == null || bottom == null) { - showSnackbar(R.string.something_wrong) + showSnackbar(CommonString.something_wrong) return@launchCatchingTask } val repositionDialog = @@ -1662,10 +1664,10 @@ class CardBrowserFragment : fun deleteSelectedNotes() = launchCatchingTask { - withProgress(R.string.deleting_selected_notes) { + withProgress(CommonString.deleting_selected_notes) { activityViewModel.deleteSelectedNotes() }.ifNotZero { noteCount -> - val deletedMessage = resources.getQuantityString(R.plurals.card_browser_cards_deleted, noteCount, noteCount) + val deletedMessage = resources.getQuantityString(CommonPlurals.card_browser_cards_deleted, noteCount, noteCount) showUndoSnackbar(deletedMessage) } } @@ -1861,7 +1863,7 @@ class CardBrowserFragment : private fun showUndoSnackbar(message: CharSequence) { showSnackbar(message) { - setAction(R.string.undo) { launchCatchingTask { undoAndShowSnackbar() } } + setAction(CommonString.undo) { launchCatchingTask { undoAndShowSnackbar() } } undoSnackbar = this } } @@ -1946,39 +1948,39 @@ class CardBrowserFragment : val shortcuts get() = ShortcutGroup( listOf( - shortcut("Ctrl+Shift+A", R.string.edit_tags_dialog), - shortcut("Ctrl+A", R.string.card_browser_select_all), + shortcut("Ctrl+Shift+A", CommonString.edit_tags_dialog), + shortcut("Ctrl+A", CommonString.card_browser_select_all), shortcut("Ctrl+Shift+E", Translations::exportingExport), - shortcut("Ctrl+E", R.string.menu_add_note), - shortcut("E", R.string.cardeditor_title_edit_card), + shortcut("Ctrl+E", CommonString.menu_add_note), + shortcut("E", CommonString.cardeditor_title_edit_card), shortcut("Ctrl+D") { sentenceCase.changeDeck }, shortcut("Ctrl+K") { sentenceCase.toggleMark }, shortcut("Ctrl+Alt+R", Translations::browsingReschedule), - shortcut("DEL", R.string.delete_card_title), - shortcut("Ctrl+Alt+N", R.string.reset_card_dialog_title), + shortcut("DEL", CommonString.delete_card_title), + shortcut("Ctrl+Alt+N", CommonString.reset_card_dialog_title), shortcut("Ctrl+Alt+T") { sentenceCase.toggleCardsNotes }, - shortcut("Ctrl+T", R.string.card_browser_search_by_tag), + shortcut("Ctrl+T", CommonString.card_browser_search_by_tag), shortcut("Ctrl+Shift+S", Translations::actionsReposition), - shortcut("Ctrl+Alt+S", R.string.card_browser_list_my_searches), - shortcut("Ctrl+S", R.string.card_browser_list_my_searches_save), - shortcut("Alt+S", R.string.card_browser_show_suspended), + shortcut("Ctrl+Alt+S", CommonString.card_browser_list_my_searches), + shortcut("Ctrl+S", CommonString.card_browser_list_my_searches_save), + shortcut("Alt+S", CommonString.card_browser_show_suspended), shortcut("Ctrl+Shift+G") { sentenceCase.gradeNow }, shortcut("Ctrl+Shift+J") { sentenceCase.toggleBury }, shortcut("Ctrl+J") { sentenceCase.toggleSuspend }, shortcut("Ctrl+Shift+I") { sentenceCase.cardInfo }, - shortcut("Ctrl+O", R.string.show_order_dialog), - shortcut("Ctrl+Shift+P", R.string.card_editor_preview_card), - shortcut("Ctrl+M", R.string.card_browser_show_marked), - shortcut("Esc", R.string.card_browser_select_none), - shortcut("Ctrl+1", R.string.gesture_flag_red), - shortcut("Ctrl+2", R.string.gesture_flag_orange), - shortcut("Ctrl+3", R.string.gesture_flag_green), - shortcut("Ctrl+4", R.string.gesture_flag_blue), - shortcut("Ctrl+5", R.string.gesture_flag_pink), - shortcut("Ctrl+6", R.string.gesture_flag_turquoise), - shortcut("Ctrl+7", R.string.gesture_flag_purple), + shortcut("Ctrl+O", CommonString.show_order_dialog), + shortcut("Ctrl+Shift+P", CommonString.card_editor_preview_card), + shortcut("Ctrl+M", CommonString.card_browser_show_marked), + shortcut("Esc", CommonString.card_browser_select_none), + shortcut("Ctrl+1", CommonString.gesture_flag_red), + shortcut("Ctrl+2", CommonString.gesture_flag_orange), + shortcut("Ctrl+3", CommonString.gesture_flag_green), + shortcut("Ctrl+4", CommonString.gesture_flag_blue), + shortcut("Ctrl+5", CommonString.gesture_flag_pink), + shortcut("Ctrl+6", CommonString.gesture_flag_turquoise), + shortcut("Ctrl+7", CommonString.gesture_flag_purple), ), - R.string.card_browser_context_menu, + CommonString.card_browser_context_menu, ) private enum class TagsDialogListenerAction { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/ColumnSelectionDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/ColumnSelectionDialogFragment.kt index c22ffdcdfebf..b029960412e2 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/ColumnSelectionDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/ColumnSelectionDialogFragment.kt @@ -14,6 +14,7 @@ import androidx.core.os.BundleCompat import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.ItemColumnSelectionBinding import com.ichi2.anki.utils.ext.requireParcelable @@ -69,7 +70,7 @@ class ColumnSelectionDialogFragment : DialogFragment() { val column = getItem(position) binding.columnTitle.text = - column?.label ?: getString(R.string.no_columns_available) + column?.label ?: getString(CommonString.no_columns_available) binding.columnExample.text = if (column?.sampleValue.isNullOrBlank()) "-" else column.sampleValue @@ -95,7 +96,7 @@ class ColumnSelectionDialogFragment : DialogFragment() { listView.setOnItemClickListener { _, _, position, _ -> val selected = adapter.getItem(position) - if (selected == null || selected.label == getString(R.string.no_columns_available)) { + if (selected == null || selected.label == getString(CommonString.no_columns_available)) { Timber.d("Ignoring click on 'No Columns Available'") return@setOnItemClickListener } @@ -111,7 +112,7 @@ class ColumnSelectionDialogFragment : DialogFragment() { return AlertDialog .Builder(requireActivity()) - .setTitle(getString(R.string.manage_browser_column)) + .setTitle(getString(CommonString.manage_browser_column)) .setView(container) .setNegativeButton(android.R.string.cancel) { _, _ -> dismissAllowingStateLoss() } .create() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/FindAndReplaceDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/FindAndReplaceDialogFragment.kt index f8980c9635e7..0de70c8827bf 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/FindAndReplaceDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/FindAndReplaceDialogFragment.kt @@ -18,6 +18,7 @@ import androidx.lifecycle.lifecycleScope import com.ichi2.anki.CardBrowser import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.browser.FindAndReplaceDialogFragment.Companion.ARG_FIELD @@ -71,9 +72,9 @@ class FindAndReplaceDialogFragment : AnalyticsDialogFragment() { .show { title(text = title) customView(binding.root) - neutralButton(R.string.help) { openUrl(R.string.link_manual_browser_find_replace) } - negativeButton(R.string.dialog_cancel) { removeIdsFile() } - positiveButton(R.string.dialog_positive_replace) { startFindReplace() } + neutralButton(CommonString.help) { openUrl(R.string.link_manual_browser_find_replace) } + negativeButton(CommonString.dialog_cancel) { removeIdsFile() } + positiveButton(CommonString.dialog_positive_replace) { startFindReplace() } }.also { dialog -> dialog.positiveButton.isEnabled = false } @@ -102,7 +103,7 @@ class FindAndReplaceDialogFragment : AnalyticsDialogFragment() { val fetchNoteIdsResult = runCatching { idsFile.getIds() } val noteIds = fetchNoteIdsResult.getOrNull() if (fetchNoteIdsResult.isFailure || noteIds == null) { - requireActivity().showSnackbar(R.string.something_wrong) + requireActivity().showSnackbar(CommonString.something_wrong) dismiss() return@launch } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/RepositionCardFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/RepositionCardFragment.kt index 7d56308f8c55..428119f93821 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/RepositionCardFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/RepositionCardFragment.kt @@ -14,7 +14,7 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.setFragmentResult import com.ichi2.anki.CardBrowser import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.databinding.FragmentRepositionCardBinding import com.ichi2.anki.ui.internationalization.sentenceCase import com.ichi2.utils.create @@ -67,7 +67,7 @@ class RepositionCardFragment : DialogFragment() { AlertDialog.Builder(requireContext()).create { title(text = title) customView(binding.root) - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) positiveButton(text = TR.actionsReposition()) { val position = binding.startInputEditText.textAsIntOrNull() ?: return@positiveButton diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/AdvancedSearchFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/AdvancedSearchFragment.kt index 85feda7b59d3..4f60339ff88c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/AdvancedSearchFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/AdvancedSearchFragment.kt @@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.adapter.FragmentStateAdapter import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.browser.search.AdvancedSearchFieldsTab.FieldSearch import com.ichi2.anki.browser.search.AdvancedSearchFragment.OptionData @@ -218,7 +219,7 @@ class AdvancedSearchFragment : Fragment(R.layout.fragment_advanced_search) { val fieldSearch = AdvancedSearchFieldsTab.options[resultType] ?: run { Timber.w("resultType '%s' was unhandled", resultType) - showSnackbar(R.string.something_wrong) + showSnackbar(CommonString.something_wrong) return@registerFieldSelectionHandler } launchCatchingTask { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/SortOrderBottomSheetFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/SortOrderBottomSheetFragment.kt index 7d892e75309a..25865d0406e8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/SortOrderBottomSheetFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/SortOrderBottomSheetFragment.kt @@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView import anki.search.BrowserColumns.Sorting import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.browser.BrowserColumnKey import com.ichi2.anki.browser.CardBrowserColumn @@ -67,7 +68,7 @@ class SortOrderBottomSheetFragment( with(binding.title) { isVisible = true - text = getString(R.string.card_browser_change_display_order_title) + text = getString(CommonString.card_browser_change_display_order_title) } with(this.behavior) { @@ -144,14 +145,14 @@ class SortOrderBottomSheetFragment( // do for all cards in the collection. 'Sort Field' can be used instead CardBrowserColumn.QUESTION.ankiColumnKey, CardBrowserColumn.ANSWER.ankiColumnKey, - -> R.string.card_browser_order_subtitle_use_sort_field + -> CommonString.card_browser_order_subtitle_use_sort_field // FSRS data is stored in JSON in the card table, it's not yet feasible to // sort on this data CardBrowserColumn.FSRS_DIFFICULTY.ankiColumnKey, CardBrowserColumn.FSRS_STABILITY.ankiColumnKey, CardBrowserColumn.FSRS_RETRIEVABILITY.ankiColumnKey, - -> R.string.card_browser_order_subtitle_cards_mode_only + -> CommonString.card_browser_order_subtitle_cards_mode_only else -> null } @@ -179,7 +180,7 @@ class SortOrderBottomSheetFragment( @CheckResult fun getLabel(context: Context): String = when (this) { - NoOrdering -> context.getString(R.string.card_browser_order_no_sorting_title) + NoOrdering -> context.getString(CommonString.card_browser_order_no_sorting_title) is AnkiColumn -> this.label } @@ -191,7 +192,7 @@ class SortOrderBottomSheetFragment( val subtitleRes: Int? = when (this) { // No Ordering's subtitle is 'Faster' - is NoOrdering -> R.string.card_browser_order_no_sorting_subtitle + is NoOrdering -> CommonString.card_browser_order_no_sorting_subtitle // the selected column has an explanation of the sort: "Low to high" is AnkiColumn if sort != null && sort.key == key -> type.humanReadableExplanation(descending = sort.reverse) @@ -260,15 +261,15 @@ class SortOrderBottomSheetFragment( return buildList { add(NoOrdering) if (active.isNotEmpty()) { - add(SectionHeader(R.string.user_active_columns)) + add(SectionHeader(CommonString.user_active_columns)) addAll(active) } if (available.isNotEmpty()) { - add(SectionHeader(R.string.user_potential_columns)) + add(SectionHeader(CommonString.user_potential_columns)) addAll(available) } if (unavailable.isNotEmpty()) { - add(SectionHeader(R.string.card_browser_order_section_unavailable)) + add(SectionHeader(CommonString.card_browser_order_section_unavailable)) addAll(unavailable) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/StandardSearchFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/StandardSearchFragment.kt index 6c7ac4ddf977..bbfbf70c7e57 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/StandardSearchFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/StandardSearchFragment.kt @@ -17,6 +17,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import com.google.android.material.color.MaterialColors import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.Flag import com.ichi2.anki.R import com.ichi2.anki.browser.SearchHistory.SearchHistoryEntry @@ -97,7 +98,7 @@ class StandardSearchFragment : private fun setupChips() { binding.decksChip.setOnClickListener { // TODO: see onDeckSelected - startDeckSelection(title = getString(R.string.search_deck), asChild = true, skipEmptyDefault = true) + startDeckSelection(title = getString(CommonString.search_deck), asChild = true, skipEmptyDefault = true) } binding.tagsChip.setOnClickListener { @@ -244,7 +245,7 @@ class StandardSearchFragment : } viewModel.isHistoryExpandedFlow.launchCollectionInLifecycleScope { - binding.toggleSearchHistory.setText(if (it) R.string.card_browser_see_less else R.string.card_browser_see_more) + binding.toggleSearchHistory.setText(if (it) CommonString.card_browser_see_less else CommonString.card_browser_see_more) } viewModel.searchHistoryAvailableFlow.launchCollectionInLifecycleScope { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/ui/SortPill.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/ui/SortPill.kt index 2b6c9fee90d7..11011ac8e658 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/ui/SortPill.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/search/ui/SortPill.kt @@ -20,6 +20,7 @@ import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.color.MaterialColors import com.google.android.material.shape.AbsoluteCornerSize +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.browser.ColumnType import com.ichi2.utils.dp @@ -215,24 +216,24 @@ class SortPill setIconAndLabel( standardButton, R.drawable.ic_sort_pill_arrow_down_label, - context.getString(R.string.sort_pill_label_text_ascending), + context.getString(CommonString.sort_pill_label_text_ascending), ) setIconAndLabel( reverseButton, R.drawable.ic_sort_pill_arrow_down_label, - context.getString(R.string.sort_pill_label_text_descending), + context.getString(CommonString.sort_pill_label_text_descending), ) } ColumnType.NUMERIC -> { setIconAndLabel( standardButton, R.drawable.ic_sort_pill_arrow_down_label, - context.getString(R.string.sort_pill_label_numeric_ascending), + context.getString(CommonString.sort_pill_label_numeric_ascending), ) setIconAndLabel( reverseButton, R.drawable.ic_sort_pill_arrow_down_label, - context.getString(R.string.sort_pill_label_numeric_descending), + context.getString(CommonString.sort_pill_label_numeric_descending), ) } ColumnType.DATE, ColumnType.UNSPECIFIED -> { @@ -244,8 +245,8 @@ class SortPill private fun updateA11yDescriptions() { // set contentDescription to ", ascending" / ", descending" - val ascending = context.getString(R.string.sort_pill_a11y_ascending) - val descending = context.getString(R.string.sort_pill_a11y_descending) + val ascending = context.getString(CommonString.sort_pill_a11y_ascending) + val descending = context.getString(CommonString.sort_pill_a11y_descending) val prefix = columnContentDescription standardButton.contentDescription = if (prefix != null) "$prefix, $ascending" else ascending reverseButton.contentDescription = if (prefix != null) "$prefix, $descending" else descending @@ -253,7 +254,7 @@ class SortPill private fun updateA11yState() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return - val selected = context.getString(R.string.sort_pill_a11y_selected) + val selected = context.getString(CommonString.sort_pill_a11y_selected) standardButton.stateDescription = if (activeDirection == SortDirection.Ascending) selected else null reverseButton.stateDescription = if (activeDirection == SortDirection.Descending) selected else null } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/Gesture.kt b/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/Gesture.kt index ed34d275547a..4049f8a8e039 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/Gesture.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/Gesture.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.cardviewer import android.content.Context import android.content.SharedPreferences import android.os.Build -import com.ichi2.anki.R +import com.ichi2.anki.CommonString /** * https://www.fileformat.info/info/unicode/char/235d/index.htm (similar to a finger) @@ -29,24 +29,24 @@ enum class Gesture( @get:JvmName("getResourceId") val resourceId: Int, private val displayPrefix: String = GESTURE_PREFIX, ) { - SWIPE_UP(R.string.gestures_swipe_up), - SWIPE_DOWN(R.string.gestures_swipe_down), - SWIPE_LEFT(R.string.gestures_swipe_left), - SWIPE_RIGHT(R.string.gestures_swipe_right), - DOUBLE_TAP(R.string.gestures_double_tap), - TAP_TOP_LEFT(R.string.gestures_corner_tap_top_left), - TAP_TOP(R.string.gestures_tap_top), - TAP_TOP_RIGHT(R.string.gestures_corner_tap_top_right), - TAP_LEFT(R.string.gestures_tap_left), - TAP_CENTER(R.string.gestures_corner_tap_middle_center), - TAP_RIGHT(R.string.gestures_tap_right), - TAP_BOTTOM_LEFT(R.string.gestures_corner_tap_bottom_left), - TAP_BOTTOM(R.string.gestures_tap_bottom), - TAP_BOTTOM_RIGHT(R.string.gestures_corner_tap_bottom_right), - TWO_FINGER_TAP(R.string.gestures_two_finger_tap), - THREE_FINGER_TAP(R.string.gestures_three_finger_tap), - FOUR_FINGER_TAP(R.string.gestures_four_finger_tap), - SHAKE(R.string.gestures_shake, SHAKE_GESTURE_PREFIX), + SWIPE_UP(CommonString.gestures_swipe_up), + SWIPE_DOWN(CommonString.gestures_swipe_down), + SWIPE_LEFT(CommonString.gestures_swipe_left), + SWIPE_RIGHT(CommonString.gestures_swipe_right), + DOUBLE_TAP(CommonString.gestures_double_tap), + TAP_TOP_LEFT(CommonString.gestures_corner_tap_top_left), + TAP_TOP(CommonString.gestures_tap_top), + TAP_TOP_RIGHT(CommonString.gestures_corner_tap_top_right), + TAP_LEFT(CommonString.gestures_tap_left), + TAP_CENTER(CommonString.gestures_corner_tap_middle_center), + TAP_RIGHT(CommonString.gestures_tap_right), + TAP_BOTTOM_LEFT(CommonString.gestures_corner_tap_bottom_left), + TAP_BOTTOM(CommonString.gestures_tap_bottom), + TAP_BOTTOM_RIGHT(CommonString.gestures_corner_tap_bottom_right), + TWO_FINGER_TAP(CommonString.gestures_two_finger_tap), + THREE_FINGER_TAP(CommonString.gestures_three_finger_tap), + FOUR_FINGER_TAP(CommonString.gestures_four_finger_tap), + SHAKE(CommonString.gestures_shake, SHAKE_GESTURE_PREFIX), ; fun toDisplayString(context: Context): String = diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/OnRenderProcessGoneDelegate.kt b/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/OnRenderProcessGoneDelegate.kt index 5a1fb47a25d5..05fd97a54ad8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/OnRenderProcessGoneDelegate.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/OnRenderProcessGoneDelegate.kt @@ -9,7 +9,7 @@ import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.lifecycle.Lifecycle import com.ichi2.anki.AbstractFlashcardViewer -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.anki.libanki.CardId import com.ichi2.utils.cancelable @@ -99,7 +99,7 @@ open class OnRenderProcessGoneDelegate( Timber.d("Not showing toast - screen isn't visible") return } - val errorMessage = target.resources.getString(R.string.webview_crash_fatal, getErrorCause(detail)) + val errorMessage = target.resources.getString(CommonString.webview_crash_fatal, getErrorCause(detail)) showThemedToast(target, errorMessage, false) } @@ -109,14 +109,14 @@ open class OnRenderProcessGoneDelegate( Timber.d("Not showing toast - screen isn't visible") return } - val nonFatalError = target.resources.getString(R.string.webview_crash_nonfatal, getErrorCause(detail)) + val nonFatalError = target.resources.getString(CommonString.webview_crash_nonfatal, getErrorCause(detail)) showThemedToast(target, nonFatalError, false) } @RequiresApi(Build.VERSION_CODES.O) protected fun getErrorCause(detail: RenderProcessGoneDetail): String { // It's not necessarily an OOM crash, false implies a general code which is for "system terminated". - val errorCauseId = if (detail.didCrash()) R.string.webview_crash_unknown else R.string.webview_crash_oom + val errorCauseId = if (detail.didCrash()) CommonString.webview_crash_unknown else CommonString.webview_crash_oom return target.resources.getString(errorCauseId) } @@ -130,15 +130,15 @@ open class OnRenderProcessGoneDelegate( val errorDetails = if (detail.didCrash()) { res.getString( - R.string.webview_crash_unknwon_detailed, + CommonString.webview_crash_unknwon_detailed, ) } else { - res.getString(R.string.webview_crash_oom_details) + res.getString(CommonString.webview_crash_oom_details) } AlertDialog.Builder(target).show { - title(R.string.webview_crash_loop_dialog_title) - message(text = res.getString(R.string.webview_crash_loop_dialog_content, cardInformation, errorDetails)) - positiveButton(R.string.dialog_ok) { + title(CommonString.webview_crash_loop_dialog_title) + message(text = res.getString(CommonString.webview_crash_loop_dialog_content, cardInformation, errorDetails)) + positiveButton(CommonString.dialog_ok) { onCloseRenderLoopDialog() } cancelable(false) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/TypeAnswer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/TypeAnswer.kt index 3d109ebdfbdd..bd7881aff023 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/TypeAnswer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/cardviewer/TypeAnswer.kt @@ -6,7 +6,7 @@ import android.content.SharedPreferences import android.content.res.Resources import androidx.annotation.VisibleForTesting import com.ichi2.anki.CollectionManager -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.libanki.Card import com.ichi2.anki.libanki.Collection import com.ichi2.anki.servicelayer.LanguageHint @@ -59,7 +59,7 @@ class TypeAnswer( * Optional warning for when a typed answer can't be displayed * * * empty card - * * unknown field specified [R.string.unknown_type_field_warning] + * * unknown field specified [CommonString.unknown_type_field_warning] * */ var warning: String? = null private set @@ -115,7 +115,7 @@ class TypeAnswer( if (clozeIdx != 0) { CollectionManager.TR.cardTemplateRenderingEmptyFront() } else { - res.getString(R.string.unknown_type_field_warning, fldTag) + res.getString(CommonString.unknown_type_field_warning, fldTag) } } "" -> { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/BackgroundImage.kt b/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/BackgroundImage.kt index 362aff8d89b8..eabeee760734 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/BackgroundImage.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/BackgroundImage.kt @@ -7,8 +7,8 @@ import android.graphics.drawable.Drawable import android.net.Uri import android.provider.MediaStore import androidx.annotation.CheckResult +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker -import com.ichi2.anki.R import com.ichi2.anki.common.storage.CollectionHelper import com.ichi2.anki.preferences.AppearanceSettingsFragment import com.ichi2.anki.settings.Prefs @@ -60,14 +60,14 @@ object BackgroundImage { /** The bitmap is too large to draw safely. */ data object TooLarge : Failure { - override fun message(context: Context): String = context.getString(R.string.background_image_too_large) + override fun message(context: Context): String = context.getString(CommonString.background_image_too_large) } /** Decoding failed for some other reason. */ data class Failed( val cause: String?, ) : Failure { - override fun message(context: Context): String = context.getString(R.string.failed_to_apply_background_image, cause) + override fun message(context: Context): String = context.getString(CommonString.failed_to_apply_background_image, cause) } } @@ -169,7 +169,7 @@ object BackgroundImage { (target.requireContext().contentResolver.openInputStreamSafe(selectedImage) as FileInputStream).channel.use { sourceChannel -> FileOutputStream(destFile).channel.use { destChannel -> destChannel.transferFrom(sourceChannel, 0, sourceChannel.size()) - target.showSnackbar(R.string.background_image_applied) + target.showSnackbar(CommonString.background_image_applied) } } Prefs.isBackgroundEnabled = true diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt index 0f4936121018..209fd02b2bd6 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt @@ -8,6 +8,7 @@ import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.core.content.edit import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.R import com.ichi2.anki.common.crashreporting.CrashReportService @@ -117,21 +118,21 @@ class BackupPromptDialog private constructor( this.alertDialog = AlertDialog.Builder(windowContext).create { setIcon(if (isLoggedIn) R.drawable.ic_baseline_backup_24 else R.drawable.ic_backup_restore) - title(R.string.backup_your_collection) - message(R.string.backup_collection_message) - positiveButton(if (isLoggedIn) R.string.button_sync else R.string.button_backup) { + title(CommonString.backup_your_collection) + message(CommonString.backup_collection_message) + positiveButton(if (isLoggedIn) CommonString.button_sync else CommonString.button_backup) { Timber.i("User selected 'backup'") onBackup() performBackup() } if (allowUserToPermanentlyDismissDialog) { - checkBoxPrompt(R.string.button_do_not_show_again, isCheckedDefault = false) { checked -> + checkBoxPrompt(CommonString.button_do_not_show_again, isCheckedDefault = false) { checked -> Timber.d("Don't show again checked: %b", checked) userCheckedDoNotShowAgain = checked alertDialog.positiveButton.isEnabled = !checked } } - negativeButton(R.string.button_backup_later) { onDismiss() } + negativeButton(CommonString.button_backup_later) { onDismiss() } cancelable(false) } } @@ -169,7 +170,7 @@ class BackupPromptDialog private constructor( fun getPermanentlyDismissDialogMessageOrImmediatelyDismiss(context: Context): Int? { if (userIsPromptedToDeleteCollectionOnUninstall(context)) { Timber.d("User's collection may be deleted on uninstall") - return R.string.dismiss_backup_warning_new_user // message stating collection will be deleted (new user/migrated) + return CommonString.dismiss_backup_warning_new_user // message stating collection will be deleted (new user/migrated) } // Full build users/users on old Androids will see this dialog, but only if they are syncing @@ -192,7 +193,7 @@ class BackupPromptDialog private constructor( Timber.d("User will lose access to their collection") // message stating collection will be made inaccessible // (existing user, not migrated) - R.string.dismiss_backup_warning_upgrade + CommonString.dismiss_backup_warning_upgrade } else { // A user is on a Play Store Build. They are on a version of Android with storage restrictions // Their collection is in a 'legacy' location but they are not going to lose access to their collection when they uninstall @@ -200,7 +201,7 @@ class BackupPromptDialog private constructor( Timber.w("getPermanentlyDismissDialogMessage: unexpected state") CrashReportService.sendExceptionReport(IllegalStateException("unexpected state"), "getPermanentlyDismissDialogMessage") // assume this is a mistake and show a scary confirmation prompt - R.string.dismiss_backup_warning_new_user // message stating collection will be deleted + CommonString.dismiss_backup_warning_new_user // message stating collection will be deleted } } @@ -218,11 +219,11 @@ class BackupPromptDialog private constructor( } AlertDialog.Builder(context).show { - title(R.string.dismiss_backup_warning_title) + title(CommonString.dismiss_backup_warning_title) message(message) setIcon(R.drawable.ic_warning) - positiveButton(R.string.dialog_cancel) { onCancel() } - negativeButton(R.string.button_disable_reminder) { onDisableReminder() } + positiveButton(CommonString.dialog_cancel) { onCancel() } + negativeButton(CommonString.button_disable_reminder) { onDisableReminder() } } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BrowserOptionsDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BrowserOptionsDialog.kt index 5bc27f6ead84..3c58d7e33298 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BrowserOptionsDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BrowserOptionsDialog.kt @@ -11,6 +11,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.browser.BrowserColumnSelectionFragment import com.ichi2.anki.browser.CardBrowserViewModel @@ -107,8 +108,8 @@ class BrowserOptionsDialog : AppCompatDialogFragment(R.layout.dialog_browser_opt return MaterialAlertDialogBuilder(requireContext()).create { setView(binding.root) setTitle(TR.sentenceCase.browserOptions) - positiveButton(R.string.save) { saveChanges() } - negativeButton(R.string.dialog_cancel) + positiveButton(CommonString.save) { saveChanges() } + negativeButton(CommonString.dialog_cancel) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CardSideSelectionDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CardSideSelectionDialog.kt index 67837ef2fbfc..c14e26869db8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CardSideSelectionDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CardSideSelectionDialog.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.dialogs import android.annotation.SuppressLint import android.content.Context import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.reviewer.CardSide import com.ichi2.utils.show import com.ichi2.utils.title @@ -20,18 +20,18 @@ class CardSideSelectionDialog { ) { val items = listOf( - R.string.card_side_both, - R.string.card_side_question, - R.string.card_side_answer, + CommonString.card_side_both, + CommonString.card_side_question, + CommonString.card_side_answer, ) AlertDialog.Builder(ctx).show { - title(R.string.card_side_selection_title) + title(CommonString.card_side_selection_title) setItems(items.map { ctx.getString(it) }.toTypedArray()) { _, index -> when (items[index]) { - R.string.card_side_both -> callback(CardSide.BOTH) - R.string.card_side_question -> callback(CardSide.QUESTION) - R.string.card_side_answer -> callback(CardSide.ANSWER) + CommonString.card_side_both -> callback(CardSide.BOTH) + CommonString.card_side_question -> callback(CardSide.QUESTION) + CommonString.card_side_answer -> callback(CardSide.ANSWER) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt index a6feca6881d1..f039a9c6866d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ChangeNoteTypeDialog.kt @@ -40,6 +40,7 @@ import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.textview.MaterialTextView import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.CrashReportData.Companion.toCrashReportData import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment @@ -586,12 +587,12 @@ class ChangeNoteTypeDialog : AnalyticsDialogFragment(R.layout.dialog_change_note REGULAR_TO_REGULAR -> Standard CLOZE_TO_CLOZE, REGULAR_TO_CLOZE -> WithWarning( - warningRes = R.string.card_numbers_unchanged, + warningRes = CommonString.card_numbers_unchanged, ) // Improvement: we could detect this using the max ord of provided notes CLOZE_TO_REGULAR -> WithWarning( - warningRes = R.string.extra_cloze_deletions_removed, + warningRes = CommonString.extra_cloze_deletions_removed, ) } } @@ -711,5 +712,5 @@ private fun AnkiActivity.changeNoteType(viewModel: ChangeNoteTypeViewModel) = @VisibleForTesting fun ChangeNoteTypeException.Kind.toString(context: Context): String = when (this) { - ChangeNoteTypeException.Kind.NO_CHANGES -> context.getString(R.string.error_no_changes_to_save) + ChangeNoteTypeException.Kind.NO_CHANGES -> context.getString(CommonString.error_no_changes_to_save) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ConfirmationDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ConfirmationDialog.kt index 520ff3338191..44dbe51bf7f4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ConfirmationDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ConfirmationDialog.kt @@ -6,6 +6,7 @@ package com.ichi2.anki.dialogs import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.utils.ext.ifNullOrEmpty import com.ichi2.anki.utils.ext.requireString @@ -34,7 +35,7 @@ class ConfirmationDialog : DialogFragment() { get() = requireArguments() .getString(ARG_POSITIVE_BUTTON_TEXT) - .ifNullOrEmpty { getString(R.string.dialog_ok) } + .ifNullOrEmpty { getString(CommonString.dialog_ok) } private var confirm = Runnable {} // Do nothing by default private var cancel = Runnable {} // Do nothing by default @@ -76,7 +77,7 @@ class ConfirmationDialog : DialogFragment() { title(text = title) message(text = message) positiveButton(text = positiveButtonText) { confirm.run() } - negativeButton(R.string.dialog_cancel) { cancel.run() } + negativeButton(CommonString.dialog_cancel) { cancel.run() } } companion object { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CreateDeckDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CreateDeckDialog.kt index 7ba592b17cb0..c230871175b8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CreateDeckDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CreateDeckDialog.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AlertDialog import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.anki.libanki.DeckId @@ -86,16 +87,16 @@ class CreateDeckDialog( // Uses "Rename" for rename deck dialogs and "Create" for all other deck-related dialogs. val positiveButtonTextRes = when (deckDialogType) { - DeckDialogType.RENAME_DECK -> R.string.rename + DeckDialogType.RENAME_DECK -> CommonString.rename DeckDialogType.DECK, DeckDialogType.SUB_DECK, - -> R.string.dialog_positive_create + -> CommonString.dialog_positive_create } positiveButton(positiveButtonTextRes) { onPositiveButtonClicked() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) setView(R.layout.dialog_generic_text_input) }.input( hint = textInputHint, @@ -125,7 +126,7 @@ class CreateDeckDialog( } val existingDeckId = getColUnsafe.decks.idForName(maybeDeckName) if (existingDeckId != null && existingDeckId != renamedDeckId) { - dialog.getInputTextLayout().error = context.getString(R.string.error_name_exists) + dialog.getInputTextLayout().error = context.getString(CommonString.error_name_exists) dialog.positiveButton.isEnabled = false return@input } @@ -136,7 +137,7 @@ class CreateDeckDialog( // To fix: they need [01, 02, 10]. Show a hint to help them dialog.getInputTextLayout().helperText = if (text.containsNumberLargerThanNine()) { - context.getString(R.string.create_deck_numeric_hint) + context.getString(CommonString.create_deck_numeric_hint) } else { null } @@ -168,11 +169,11 @@ class CreateDeckDialog( if (Decks.isValidDeckName(deckName)) { if (createNewDeck(deckName)) { // 11668: Display feedback if a deck is created - displayFeedback(context.getString(R.string.deck_created)) + displayFeedback(context.getString(CommonString.deck_created)) } } else { Timber.d("CreateDeckDialog::createDeck - Not creating invalid deck name '%s'", deckName) - displayFeedback(context.getString(R.string.invalid_deck_name), Snackbar.LENGTH_LONG) + displayFeedback(context.getString(CommonString.invalid_deck_name), Snackbar.LENGTH_LONG) } // AlertDialog should be dismissed after the Keyboard 'Done' or Deck 'Ok' button is pressed shownDialog?.dismiss() @@ -215,7 +216,7 @@ class CreateDeckDialog( if (!Decks.isValidDeckName(newDeckName)) { Timber.w("CreateDeckDialog::renameDeck not renaming deck to invalid name") Timber.d("invalid deck name: %s", newDeckName) - displayFeedback(context.getString(R.string.invalid_deck_name), Snackbar.LENGTH_LONG) + displayFeedback(context.getString(CommonString.invalid_deck_name), Snackbar.LENGTH_LONG) } else { val deckId = renamedDeckId!! try { @@ -223,7 +224,7 @@ class CreateDeckDialog( if (getColUnsafe.decks.rename(deckId, newDeckName).deck) { onNewDeckCreated(deckId) // 11668: Display feedback if a deck is renamed - displayFeedback(context.getString(R.string.deck_renamed)) + displayFeedback(context.getString(CommonString.deck_renamed)) } } catch (e: BackendDeckIsFilteredException) { Timber.w(e) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DatabaseErrorDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DatabaseErrorDialog.kt index 92a8ae0c222c..1d8934aaa624 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DatabaseErrorDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DatabaseErrorDialog.kt @@ -19,6 +19,7 @@ import com.ichi2.anki.AnkiActivity import com.ichi2.anki.BackupManager import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.ConflictResolution import com.ichi2.anki.DatabaseRestorationListener import com.ichi2.anki.DeckPicker @@ -105,14 +106,14 @@ class DatabaseErrorDialog : AsyncDialogFragment() { // Collection failed to load; give user the option of either choosing from repair options, or closing // the activity alertDialog.show { - title(R.string.open_collection_failed_title) + title(CommonString.open_collection_failed_title) cancelable(false) message(text = message) setIcon(R.drawable.ic_warning) - positiveButton(R.string.error_handling_options) { + positiveButton(CommonString.error_handling_options) { showDatabaseErrorDialog(DIALOG_ERROR_HANDLING) } - negativeButton(R.string.close) { + negativeButton(CommonString.close) { closeCollectionAndFinish() } } @@ -123,17 +124,17 @@ class DatabaseErrorDialog : AsyncDialogFragment() { alertDialog .create { cancelable(false) - title(R.string.answering_error_title) + title(CommonString.answering_error_title) message(text = message) setIcon(R.drawable.ic_warning) - positiveButton(R.string.error_handling_options) { + positiveButton(CommonString.error_handling_options) { showDatabaseErrorDialog(DIALOG_ERROR_HANDLING) } - negativeButton(R.string.answering_error_report) { + negativeButton(CommonString.answering_error_report) { requireDeckPicker().sendErrorReport() requireActivity().dismissAllDialogFragments() } - neutralButton(R.string.close) { + neutralButton(CommonString.close) { closeCollectionAndFinish() } }.apply { @@ -148,20 +149,20 @@ class DatabaseErrorDialog : AsyncDialogFragment() { val values = ArrayList(8) if (!requireAnkiActivity().colIsOpenUnsafe()) { // retry - options.add(res.getString(R.string.backup_retry_opening)) + options.add(res.getString(CommonString.backup_retry_opening)) values.add(ErrorHandlingEntries.RETRY) } // repair db with sqlite if (sqliteInstalled) { - options.add(res.getString(R.string.backup_error_menu_repair)) + options.add(res.getString(CommonString.backup_error_menu_repair)) values.add(ErrorHandlingEntries.REPAIR) } // // restore from backup - options.add(res.getString(R.string.backup_restore)) + options.add(res.getString(CommonString.backup_restore)) values.add(ErrorHandlingEntries.RESTORE) // one-way sync from server if (isLoggedIn) { - options.add(res.getString(R.string.backup_one_way_sync_from_server)) + options.add(res.getString(CommonString.backup_one_way_sync_from_server)) values.add(ErrorHandlingEntries.ONE_WAY) } @@ -177,21 +178,21 @@ class DatabaseErrorDialog : AsyncDialogFragment() { } if (shouldOfferResetToDefaultDirectory) { - options.add(res.getString(R.string.backup_use_default_folder)) + options.add(res.getString(CommonString.backup_use_default_folder)) values.add(ErrorHandlingEntries.RESET_TO_DEFAULT_DIRECTORY) } // delete old collection and build new one - options.add(res.getString(R.string.backup_del_collection)) + options.add(res.getString(CommonString.backup_del_collection)) values.add(ErrorHandlingEntries.NEW) // copy stack trace and debug info options.add(TR.sentenceCase.copyDebugInfo) values.add(ErrorHandlingEntries.DEBUG_INFO) alertDialog.show { - title(R.string.error_handling_title) + title(CommonString.error_handling_title) setIcon(R.drawable.ic_warning) - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) listItems(items = options) { _, index -> when (values[index]) { ErrorHandlingEntries.RETRY -> { @@ -243,14 +244,14 @@ class DatabaseErrorDialog : AsyncDialogFragment() { DIALOG_REPAIR_COLLECTION -> { // Allow user to run BackupManager.repairCollection() alertDialog.show { - title(R.string.dialog_positive_repair) + title(CommonString.dialog_positive_repair) message(text = message) setIcon(R.drawable.ic_warning) - positiveButton(R.string.dialog_positive_repair) { + positiveButton(CommonString.dialog_positive_repair) { requireDeckPicker().repairCollection() requireActivity().dismissAllDialogFragments() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } DIALOG_RESTORE_BACKUP -> { @@ -258,9 +259,9 @@ class DatabaseErrorDialog : AsyncDialogFragment() { backups = BackupManager.getBackups(CollectionHelper.getCollectionPath(requireContext())) if (backups.isEmpty()) { alertDialog - .title(R.string.backup_restore) + .title(CommonString.backup_restore) .title(text = message) - .positiveButton(R.string.dialog_ok) { + .positiveButton(CommonString.dialog_ok) { showDatabaseErrorDialog(DIALOG_ERROR_HANDLING) } } else { @@ -270,12 +271,12 @@ class DatabaseErrorDialog : AsyncDialogFragment() { val dates = backups.map { formatter.getTimeOfBackupAsText(it) } alertDialog - .title(R.string.backup_restore_select_title) - .positiveButton(R.string.restore_backup_choose_another) { + .title(CommonString.backup_restore_select_title) + .positiveButton(CommonString.restore_backup_choose_another) { ankiActivity?.let { ImportFileSelectionFragment.openImportFilePicker(it, ImportFileSelectionFragment.ImportFileType.APKG) } - }.negativeButton(R.string.dialog_cancel) + }.negativeButton(CommonString.dialog_cancel) .setSingleChoiceItems(dates.toTypedArray(), -1) { _, index: Int -> if (backups[index].length() > 0) { // restore the backup if it's valid @@ -286,9 +287,9 @@ class DatabaseErrorDialog : AsyncDialogFragment() { } else { // otherwise show an error dialog AlertDialog.Builder(requireActivity()).show { - title(R.string.vague_error) - message(R.string.backup_invalid_file_error) - positiveButton(R.string.dialog_ok) + title(CommonString.vague_error) + message(CommonString.backup_invalid_file_error) + positiveButton(CommonString.dialog_ok) } } } @@ -298,9 +299,9 @@ class DatabaseErrorDialog : AsyncDialogFragment() { DIALOG_NEW_COLLECTION -> { // Allow user to create a new empty collection alertDialog.show { - title(R.string.backup_new_collection) + title(CommonString.backup_new_collection) message(text = message) - positiveButton(R.string.dialog_positive_create) { + positiveButton(CommonString.dialog_positive_create) { val time = TimeManager.time Timber.i( "closeCollection: %s", @@ -314,7 +315,7 @@ class DatabaseErrorDialog : AsyncDialogFragment() { showDatabaseErrorDialog(DIALOG_LOAD_FAILED) } } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } DIALOG_CONFIRM_DATABASE_CHECK -> { @@ -322,42 +323,42 @@ class DatabaseErrorDialog : AsyncDialogFragment() { alertDialog.show { title(text = TR.sentenceCase.checkDatabase) message(text = message) - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { requireDeckPicker().integrityCheck() requireActivity().dismissAllDialogFragments() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } DIALOG_CONFIRM_RESTORE_BACKUP -> { // Confirmation dialog for backup restore alertDialog.show { - title(R.string.restore_backup_title) + title(CommonString.restore_backup_title) message(text = message) - positiveButton(R.string.dialog_continue) { + positiveButton(CommonString.dialog_continue) { showDatabaseErrorDialog(DIALOG_RESTORE_BACKUP) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } DIALOG_ONE_WAY_SYNC_FROM_SERVER -> { // Allow user to do a full-sync from the server alertDialog.show { - title(R.string.backup_one_way_sync_from_server) + title(CommonString.backup_one_way_sync_from_server) message(text = message) - positiveButton(R.string.dialog_positive_overwrite) { + positiveButton(CommonString.dialog_positive_overwrite) { requireDeckPicker().sync(ConflictResolution.FULL_DOWNLOAD) requireActivity().dismissAllDialogFragments() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } DIALOG_DB_LOCKED -> { // If the database is locked, all we can do is ask the user to exit. alertDialog.show { - title(R.string.database_locked_title) + title(CommonString.database_locked_title) message(text = message) - positiveButton(R.string.close) { + positiveButton(CommonString.close) { closeCollectionAndFinish() } cancelable(false) @@ -366,17 +367,17 @@ class DatabaseErrorDialog : AsyncDialogFragment() { INCOMPATIBLE_DB_VERSION -> { val values: MutableList = ArrayList(2) val options = mutableListOf() - options.add(makeBold(res.getString(R.string.backup_restore))) + options.add(makeBold(res.getString(CommonString.backup_restore))) values.add(IncompatibleDbVersionEntries.RESTORE) if (isLoggedIn) { - options.add(makeBold(res.getString(R.string.backup_one_way_sync_from_server))) + options.add(makeBold(res.getString(CommonString.backup_one_way_sync_from_server))) values.add(IncompatibleDbVersionEntries.ONE_WAY) } alertDialog.show { cancelable(false) - title(R.string.incompatible_database_version_title) + title(CommonString.incompatible_database_version_title) setIcon(R.drawable.ic_warning) - positiveButton(R.string.close) { + positiveButton(CommonString.close) { closeCollectionAndFinish() } listItemsAndMessage(message = message, items = options) { _, index: Int -> @@ -395,9 +396,9 @@ class DatabaseErrorDialog : AsyncDialogFragment() { } DIALOG_DISK_FULL -> { alertDialog.show { - title(R.string.storage_full_title) + title(CommonString.storage_full_title) message(text = message) - positiveButton(R.string.close) { + positiveButton(CommonString.close) { closeCollectionAndFinish() } } @@ -405,7 +406,7 @@ class DatabaseErrorDialog : AsyncDialogFragment() { DIALOG_STORAGE_UNAVAILABLE_AFTER_UNINSTALL -> { val listItems = UninstallListItem.createList() alertDialog.show { - title(R.string.directory_inaccessible_after_uninstall) + title(CommonString.directory_inaccessible_after_uninstall) listItemsAndMessage(message = message, items = listItems.map { getString(it.stringRes) }) { _, index: Int -> val listItem = listItems[index] listItem.onClick(requireAnkiActivity()) @@ -454,7 +455,7 @@ class DatabaseErrorDialog : AsyncDialogFragment() { context.copyToClipboard( TruncatedString.from(combinedInfo), - failureMessageId = R.string.about_ankidroid_error_copy_debug_info, + failureMessageId = CommonString.about_ankidroid_error_copy_debug_info, ) } @@ -465,28 +466,28 @@ class DatabaseErrorDialog : AsyncDialogFragment() { val onClick: (AnkiActivity) -> Unit, ) { RESTORE_FROM_ANKIWEB( - R.string.restore_data_from_ankiweb, + CommonString.restore_data_from_ankiweb, dismissesDialog = true, { this.displayCreateNewCollectionDialog(it) }, ), INSTALL_NON_PLAY_APP_RECOMMENDED( - R.string.install_non_play_store_ankidroid_recommended, + CommonString.install_non_play_store_ankidroid_recommended, dismissesDialog = false, { it.openUrl(R.string.link_install_non_play_store_install) }, ), INSTALL_NON_PLAY_APP_NORMAL( - R.string.install_non_play_store_ankidroid, + CommonString.install_non_play_store_ankidroid, dismissesDialog = false, { it.openUrl(R.string.link_install_non_play_store_install) }, ), RESTORE_FROM_BACKUP( - R.string.restore_data_from_backup, + CommonString.restore_data_from_backup, dismissesDialog = false, { activity -> Timber.i("Restoring from colpkg") @@ -509,14 +510,14 @@ class DatabaseErrorDialog : AsyncDialogFragment() { }, ), GET_HELP( - R.string.help_title_get_help, + CommonString.help_title_get_help, dismissesDialog = false, { it.openUrl(R.string.link_forum) }, ), RECREATE_COLLECTION( - R.string.create_new_collection, + CommonString.create_new_collection, dismissesDialog = false, { this.displayCreateNewCollectionDialog(it) @@ -536,10 +537,10 @@ class DatabaseErrorDialog : AsyncDialogFragment() { return } AlertDialog.Builder(context).show { - title(R.string.backup_new_collection) + title(CommonString.backup_new_collection) setIcon(R.drawable.ic_warning) - message(R.string.new_unsafe_collection) - positiveButton(R.string.dialog_positive_create) { + message(CommonString.new_unsafe_collection) + positiveButton(CommonString.dialog_positive_create) { Timber.w("Creating new collection") Timber.i( "closeCollection: %s", @@ -549,7 +550,7 @@ class DatabaseErrorDialog : AsyncDialogFragment() { resetAnkiDroidDirectory(context, directory) context.closeCollectionAndFinish() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) cancelable(false) } } @@ -586,20 +587,20 @@ class DatabaseErrorDialog : AsyncDialogFragment() { if (DatabaseCorruption.isDetected) { // The sqlite database has been corrupted (DatabaseErrorHandler.onCorrupt() was called) // Show a specific message appropriate for the situation - res().getString(R.string.corrupt_db_message, res().getString(R.string.repair_deck)) + res().getString(CommonString.corrupt_db_message, res().getString(R.string.repair_deck)) } else { // Generic message shown when a libanki task failed - res().getString(R.string.access_collection_failed_message, res().getString(R.string.link_help)) + res().getString(CommonString.access_collection_failed_message, res().getString(R.string.link_help)) } - DIALOG_DB_ERROR -> res().getString(R.string.answering_error_message) - DIALOG_DISK_FULL -> res().getString(R.string.storage_full_message) - DIALOG_REPAIR_COLLECTION -> res().getString(R.string.repair_deck_dialog, BackupManager.BROKEN_COLLECTIONS_SUFFIX) - DIALOG_RESTORE_BACKUP -> res().getString(R.string.backup_restore_no_backups) - DIALOG_NEW_COLLECTION -> res().getString(R.string.backup_del_collection_question) - DIALOG_CONFIRM_DATABASE_CHECK -> res().getString(R.string.check_db_warning) - DIALOG_CONFIRM_RESTORE_BACKUP -> res().getString(R.string.restore_backup) - DIALOG_ONE_WAY_SYNC_FROM_SERVER -> res().getString(R.string.backup_full_sync_from_server_question) - DIALOG_DB_LOCKED -> res().getString(R.string.database_locked_summary) + DIALOG_DB_ERROR -> res().getString(CommonString.answering_error_message) + DIALOG_DISK_FULL -> res().getString(CommonString.storage_full_message) + DIALOG_REPAIR_COLLECTION -> res().getString(CommonString.repair_deck_dialog, BackupManager.BROKEN_COLLECTIONS_SUFFIX) + DIALOG_RESTORE_BACKUP -> res().getString(CommonString.backup_restore_no_backups) + DIALOG_NEW_COLLECTION -> res().getString(CommonString.backup_del_collection_question) + DIALOG_CONFIRM_DATABASE_CHECK -> res().getString(CommonString.check_db_warning) + DIALOG_CONFIRM_RESTORE_BACKUP -> res().getString(CommonString.restore_backup) + DIALOG_ONE_WAY_SYNC_FROM_SERVER -> res().getString(CommonString.backup_full_sync_from_server_question) + DIALOG_DB_LOCKED -> res().getString(CommonString.database_locked_summary) INCOMPATIBLE_DB_VERSION -> { var databaseVersion = -1 try { @@ -609,7 +610,7 @@ class DatabaseErrorDialog : AsyncDialogFragment() { } val schemaVersion = Consts.BACKEND_SCHEMA_VERSION res().getString( - R.string.incompatible_database_version_summary, + CommonString.incompatible_database_version_summary, schemaVersion, databaseVersion, ) @@ -617,31 +618,31 @@ class DatabaseErrorDialog : AsyncDialogFragment() { DIALOG_STORAGE_UNAVAILABLE_AFTER_UNINSTALL -> { val directory = context?.let { CollectionHelper.getCurrentAnkiDroidDirectory(it) } - ?: res().getString(R.string.card_browser_unknown_deck_name) - res().getString(R.string.directory_inaccessible_after_uninstall_summary, directory) + ?: res().getString(CommonString.card_browser_unknown_deck_name) + res().getString(CommonString.directory_inaccessible_after_uninstall_summary, directory) } DIALOG_ERROR_HANDLING -> requireArguments().getString("dialogMessage") } private val title: String get() = when (requireDialogType()) { - DIALOG_LOAD_FAILED -> res().getString(R.string.open_collection_failed_title) - DIALOG_ERROR_HANDLING -> res().getString(R.string.error_handling_title) - DIALOG_REPAIR_COLLECTION -> res().getString(R.string.dialog_positive_repair) - DIALOG_RESTORE_BACKUP -> res().getString(R.string.backup_restore) - DIALOG_NEW_COLLECTION -> res().getString(R.string.backup_new_collection) + DIALOG_LOAD_FAILED -> res().getString(CommonString.open_collection_failed_title) + DIALOG_ERROR_HANDLING -> res().getString(CommonString.error_handling_title) + DIALOG_REPAIR_COLLECTION -> res().getString(CommonString.dialog_positive_repair) + DIALOG_RESTORE_BACKUP -> res().getString(CommonString.backup_restore) + DIALOG_NEW_COLLECTION -> res().getString(CommonString.backup_new_collection) DIALOG_CONFIRM_DATABASE_CHECK -> TR.sentenceCase.checkDatabase - DIALOG_CONFIRM_RESTORE_BACKUP -> res().getString(R.string.restore_backup_title) - DIALOG_ONE_WAY_SYNC_FROM_SERVER -> res().getString(R.string.backup_one_way_sync_from_server) - DIALOG_DB_LOCKED -> res().getString(R.string.database_locked_title) - INCOMPATIBLE_DB_VERSION -> res().getString(R.string.incompatible_database_version_title) - DIALOG_DB_ERROR -> res().getString(R.string.answering_error_title) - DIALOG_DISK_FULL -> res().getString(R.string.storage_full_title) - DIALOG_STORAGE_UNAVAILABLE_AFTER_UNINSTALL -> res().getString(R.string.directory_inaccessible_after_uninstall) + DIALOG_CONFIRM_RESTORE_BACKUP -> res().getString(CommonString.restore_backup_title) + DIALOG_ONE_WAY_SYNC_FROM_SERVER -> res().getString(CommonString.backup_one_way_sync_from_server) + DIALOG_DB_LOCKED -> res().getString(CommonString.database_locked_title) + INCOMPATIBLE_DB_VERSION -> res().getString(CommonString.incompatible_database_version_title) + DIALOG_DB_ERROR -> res().getString(CommonString.answering_error_title) + DIALOG_DISK_FULL -> res().getString(CommonString.storage_full_title) + DIALOG_STORAGE_UNAVAILABLE_AFTER_UNINSTALL -> res().getString(CommonString.directory_inaccessible_after_uninstall) } override val notificationMessage: String? get() = message - override val notificationTitle: String get() = res().getString(R.string.answering_error_title) + override val notificationTitle: String get() = res().getString(CommonString.answering_error_title) override val dialogHandlerMessage get() = ShowDatabaseErrorDialog(requireDialogType()) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerAnalyticsOptInDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerAnalyticsOptInDialog.kt index 76ff5dd66b26..9b3af3006cbd 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerAnalyticsOptInDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerAnalyticsOptInDialog.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.dialogs import android.os.Bundle import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.analytics.AnkiDroidUsageAnalytics import com.ichi2.anki.utils.ext.dismissAllDialogFragments @@ -21,10 +21,10 @@ class DeckPickerAnalyticsOptInDialog : AnalyticsDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog { super.onCreateDialog(savedInstanceState) return AlertDialog.Builder(requireActivity()).create { - title(R.string.analytics_dialog_title) - message(R.string.analytics_summ) - checkBoxPrompt(R.string.analytics_title, isCheckedDefault = false) {} - positiveButton(R.string.dialog_continue) { + title(CommonString.analytics_dialog_title) + message(CommonString.analytics_summ) + checkBoxPrompt(CommonString.analytics_title, isCheckedDefault = false) {} + positiveButton(CommonString.dialog_continue) { AnkiDroidUsageAnalytics.isEnabled = (it as AlertDialog).getCheckBoxPrompt().isChecked activity?.dismissAllDialogFragments() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerBackupNoSpaceLeftDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerBackupNoSpaceLeftDialog.kt index 22c98b32901e..dd532a4f359c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerBackupNoSpaceLeftDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerBackupNoSpaceLeftDialog.kt @@ -6,8 +6,8 @@ package com.ichi2.anki.dialogs import android.os.Bundle import androidx.appcompat.app.AlertDialog import com.ichi2.anki.BackupManager +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker -import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.common.storage.CollectionHelper import com.ichi2.utils.create @@ -22,9 +22,9 @@ class DeckPickerBackupNoSpaceLeftDialog : AnalyticsDialogFragment() { return AlertDialog .Builder(requireContext()) .create { - title(R.string.storage_almost_full_title) - message(text = res.getString(R.string.storage_warning, space / 1024 / 1024)) - positiveButton(R.string.dialog_ok) { + title(CommonString.storage_almost_full_title) + message(text = res.getString(CommonString.storage_warning, space / 1024 / 1024)) + positiveButton(CommonString.dialog_ok) { (activity as DeckPicker).finish() } }.apply { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerConfirmDeleteDeckDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerConfirmDeleteDeckDialog.kt index 6711134f3ea2..732a40cdc630 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerConfirmDeleteDeckDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerConfirmDeleteDeckDialog.kt @@ -20,6 +20,8 @@ import android.app.Dialog import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.core.text.HtmlCompat +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment @@ -36,10 +38,10 @@ class DeckPickerConfirmDeleteDeckDialog : AnalyticsDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val message = if (isFilteredDeck) { - resources.getString(R.string.delete_cram_deck_message, "$deckName") + resources.getString(CommonString.delete_cram_deck_message, "$deckName") } else { resources.getQuantityString( - R.plurals.delete_deck_message, + CommonPlurals.delete_deck_message, totalCards, "$deckName", totalCards, @@ -48,17 +50,17 @@ class DeckPickerConfirmDeleteDeckDialog : AnalyticsDialogFragment() { super.onCreate(savedInstanceState) return AlertDialog .Builder(requireActivity()) - .setTitle(R.string.delete_deck_title) + .setTitle(CommonString.delete_deck_title) .setMessage( HtmlCompat.fromHtml( message, HtmlCompat.FROM_HTML_MODE_LEGACY, ), ).setIcon(R.drawable.ic_warning) - .setPositiveButton(R.string.dialog_positive_delete) { _, _ -> + .setPositiveButton(CommonString.dialog_positive_delete) { _, _ -> (activity as DeckPicker).deleteDeck(deckId) activity?.dismissAllDialogFragments() - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> activity?.dismissAllDialogFragments() }.create() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt index fe50daa91872..1ebd8fb14e8b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt @@ -24,7 +24,7 @@ import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.compat.requireSerializableCompat import com.ichi2.anki.contextmenu.DeckPickerMenuContentProvider @@ -87,16 +87,16 @@ class DeckPickerContextMenu : AnalyticsDialogFragment() { DECK_OPTIONS -> TR.sentenceCase.deckOptions CUSTOM_STUDY -> TR.sentenceCase.customStudy DELETE_DECK -> TR.sentenceCase.deleteDeck - EXPORT_DECK -> getString(R.string.export_deck) + EXPORT_DECK -> getString(CommonString.export_deck) UNBURY -> TR.studyingUnbury() CUSTOM_STUDY_REBUILD -> TR.actionsRebuild() - CUSTOM_STUDY_EMPTY -> getString(R.string.empty_cram_label) - CREATE_SUBDECK -> getString(R.string.create_subdeck) - CREATE_SHORTCUT -> getString(R.string.create_shortcut) - BROWSE_CARDS -> getString(R.string.browse_cards) - EDIT_DESCRIPTION -> getString(R.string.edit_deck_description) + CUSTOM_STUDY_EMPTY -> getString(CommonString.empty_cram_label) + CREATE_SUBDECK -> getString(CommonString.create_subdeck) + CREATE_SHORTCUT -> getString(CommonString.create_shortcut) + BROWSE_CARDS -> getString(CommonString.browse_cards) + EDIT_DESCRIPTION -> getString(CommonString.edit_deck_description) ADD_CARD -> TR.actionsAdd() - SCHEDULE_REMINDERS -> getString(R.string.schedule_reminders_title) + SCHEDULE_REMINDERS -> getString(CommonString.schedule_reminders_title) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerNoSpaceLeftDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerNoSpaceLeftDialog.kt index b499f37f1b39..c9dfbb57f794 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerNoSpaceLeftDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerNoSpaceLeftDialog.kt @@ -18,7 +18,7 @@ package com.ichi2.anki.dialogs import android.os.Bundle import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.utils.cancelable import com.ichi2.utils.create @@ -29,10 +29,10 @@ import com.ichi2.utils.title class DeckPickerNoSpaceLeftDialog : AnalyticsDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?) = AlertDialog.Builder(requireActivity()).create { - title(R.string.storage_full_title) - message(R.string.backup_deck_no_storage_left) + title(CommonString.storage_full_title) + message(CommonString.backup_deck_no_storage_left) cancelable(true) - positiveButton(R.string.dialog_ok) {} + positiveButton(CommonString.dialog_ok) {} setOnCancelListener {} } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.kt index f8f976137c5c..a7827b7b2b69 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.kt @@ -42,6 +42,7 @@ import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CardTemplateEditor import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.OnContextAndLongClickListener.Companion.setOnContextAndLongClickListener import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment @@ -86,7 +87,7 @@ class DeckSelectionDialog : AnalyticsDialogFragment() { private val title: String get() = requireArguments().getString(ARG_TITLE, null) - ?: getString(R.string.select_deck_title) + ?: getString(CommonString.select_deck_title) private val templateEditorMessage: String? get() = requireArguments().getString(ARG_TEMPLATE_EDITOR_MESSAGE, null) private val allowMultipleSelection: Boolean @@ -106,10 +107,10 @@ class DeckSelectionDialog : AnalyticsDialogFragment() { binding.decks.adapter = decksAdapter setupMenu() return AlertDialog.Builder(requireActivity()).create { - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) customView(view = binding.root) if (templateEditorMessage != null) { - positiveButton(R.string.restore_default) { + positiveButton(CommonString.restore_default) { onDeckSelected(null) } } @@ -133,7 +134,7 @@ class DeckSelectionDialog : AnalyticsDialogFragment() { toolbar.inflateMenu(R.menu.deck_picker_dialog_menu) val searchItem = toolbar.menu.findItem(R.id.action_filter) val searchView = searchItem.actionView as AccessibleSearchView - searchView.queryHint = getString(R.string.deck_picker_dialog_filter_decks) + searchView.queryHint = getString(CommonString.deck_picker_dialog_filter_decks) searchView.setOnQueryTextListener( object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { @@ -164,7 +165,7 @@ class DeckSelectionDialog : AnalyticsDialogFragment() { val createDeckDialog = CreateDeckDialog( context = requireActivity(), - title = getString(R.string.create_subdeck), + title = getString(CommonString.create_subdeck), deckDialogType = CreateDeckDialog.DeckDialogType.SUB_DECK, parentId = parentDeck.deckId, ) @@ -339,7 +340,7 @@ class DeckSelectionDialog : AnalyticsDialogFragment() { expander.apply { importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES setImageDrawable(if (node.collapsed) expandImage else collapseImage) - contentDescription = context.getString(if (node.collapsed) R.string.expand else R.string.collapse) + contentDescription = context.getString(if (node.collapsed) CommonString.expand else CommonString.collapse) visibility = View.VISIBLE } } else { @@ -500,7 +501,7 @@ fun Fragment.registerDeckSelectedHandler( * Displays a [DeckSelectionDialog] for the user to select a deck. Using the default parameters will * result in all possible decks('All Decks', normal, filtered and the 'Default' deck) being shown. * - * @param title the title for the selection dialog, uses [R.string.select_decks_title] if not provided + * @param title the title for the selection dialog, uses [CommonString.select_decks_title] if not provided * @param asChild if true [DeckSelectionDialog] will start as a child of [Fragment]([Fragment.getChildFragmentManager] * will be used to start it). If false, the [FragmentActivity.getSupportFragmentManager] will be used * instead to start [DeckSelectionDialog] @@ -561,7 +562,7 @@ fun Fragment.startDeckSelection( * Displays a [DeckSelectionDialog] for the user to select a deck. Using the default parameters will * result in all possible decks('All Decks', normal, filtered and the 'Default' deck) being shown. * - * @param title the title for the selection dialog, uses [R.string.select_decks_title] if not provided + * @param title the title for the selection dialog, uses [CommonString.select_decks_title] if not provided * @param templateEditorMessage an optional message shown only when used by [CardTemplateEditor] * @param requestKey specifies the request key to be used for this request * @param allowMultipleSelection if selection of multiple decks is permitted diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DialogHandler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DialogHandler.kt index 84d03cd1571c..ba9ed6c0d34a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DialogHandler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DialogHandler.kt @@ -8,10 +8,10 @@ import android.os.Message import androidx.annotation.VisibleForTesting import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionLoadingErrorDialog +import com.ichi2.anki.CommonString import com.ichi2.anki.CrashReportData.Companion.toCrashReportData import com.ichi2.anki.DeckPicker import com.ichi2.anki.IntentHandler -import com.ichi2.anki.R import com.ichi2.anki.common.analytics.Analytics import com.ichi2.anki.common.utils.android.HandlerUtils.getDefaultLooper import com.ichi2.anki.dialogs.DialogHandler.Companion.storeMessage @@ -141,7 +141,7 @@ fun AnkiActivity.requireDeckPickerOrShowError(): DeckPicker? { if (this is DeckPicker) return this showError( - message = getString(R.string.something_wrong), + message = getString(CommonString.something_wrong), crashReportData = ClassCastException( this.javaClass.simpleName + " is not " + DeckPicker::class.java.simpleName, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DiscardChangesDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DiscardChangesDialog.kt index 8326b1e84fe7..ced7e459c95b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DiscardChangesDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DiscardChangesDialog.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.ui.internationalization.sentenceCase import com.ichi2.utils.message import com.ichi2.utils.negativeButton @@ -18,7 +18,7 @@ import timber.log.Timber object DiscardChangesDialog { fun showDialog( context: Context, - positiveButtonText: String = context.getString(R.string.discard), + positiveButtonText: String = context.getString(CommonString.discard), negativeButtonText: String = with(context) { TR.sentenceCase.keepEditing }, neutralButtonText: String? = null, message: String = TR.cardTemplatesDiscardChanges(), diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt index 10447c6c3807..302e0d45568f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt @@ -16,6 +16,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.StudyOptionsFragment import com.ichi2.anki.databinding.DialogDeckDescriptionBinding @@ -65,8 +66,8 @@ class EditDeckDescriptionDialog : DialogFragment() { return MaterialAlertDialogBuilder(requireContext()) .create { setView(binding.root) - positiveButton(R.string.save) - negativeButton(R.string.close) + positiveButton(CommonString.save) + negativeButton(CommonString.close) }.apply { alertDialog = this setOnShowListener { @@ -109,7 +110,7 @@ class EditDeckDescriptionDialog : DialogFragment() { // setup 'Format as Markdown' help binding.markdownFormattingHelp.apply { contentDescription = - getString(R.string.help_button_content_description, getString(R.string.format_deck_description_as_markdown)) + getString(CommonString.help_button_content_description, getString(CommonString.format_deck_description_as_markdown)) setOnClickListener { MaterialAlertDialogBuilder(requireContext()).show { setTitle(binding.formatAsMarkdownInput.text) @@ -140,7 +141,7 @@ class EditDeckDescriptionDialog : DialogFragment() { DismissType.ClosedWithoutSaving -> requireActivity().dismissAllDialogFragments() DismissType.Saved -> { requireActivity().dismissAllDialogFragments() - showSnackbar(R.string.deck_description_saved) + showSnackbar(CommonString.deck_description_saved) // notify DeckPicker to invalidate its toolbar menu, otherwise the undo // action to revert the description change is not going to be visible // when there are no other undo actions diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EmptyCardsDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EmptyCardsDialogFragment.kt index a6f60f2599ba..eef818459856 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EmptyCardsDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EmptyCardsDialogFragment.kt @@ -31,8 +31,8 @@ import androidx.lifecycle.repeatOnLifecycle import anki.card_rendering.EmptyCardsReport import com.ichi2.anki.CardBrowser import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker -import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.common.destinations.BrowserDestination import com.ichi2.anki.common.destinations.navigate @@ -73,7 +73,7 @@ class EmptyCardsDialogFragment : AnalyticsDialogFragment() { .Builder(requireContext()) .show { setTitle(TR.sentenceCase.emptyCardsTitle) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setPositiveButton(CommonString.dialog_ok) { _, _ -> val state = viewModel.uiState.value if (state is EmptyCardsSearchResult) { // this dialog is only shown from DeckPicker so we use it directly to avoid @@ -86,7 +86,7 @@ class EmptyCardsDialogFragment : AnalyticsDialogFragment() { ) } } - setNegativeButton(R.string.dialog_cancel) { _, _ -> + setNegativeButton(CommonString.dialog_cancel) { _, _ -> Timber.i("Empty cards dialog cancelled") } setView(binding.root) @@ -103,12 +103,12 @@ class EmptyCardsDialogFragment : AnalyticsDialogFragment() { viewModel.uiState.collect { state -> when (state) { is SearchingForEmptyCards -> { - binding.loadingMessage.text = getString(R.string.emtpy_cards_finding) + binding.loadingMessage.text = getString(CommonString.emtpy_cards_finding) binding.loadingContainer.isVisible = true binding.emptyCardsResultsContainer.isVisible = false (dialog as? AlertDialog)?.positiveButton?.apply { isEnabled = false - text = getString(R.string.dialog_ok) + text = getString(CommonString.dialog_ok) } } @@ -121,7 +121,7 @@ class EmptyCardsDialogFragment : AnalyticsDialogFragment() { // nothing to delete so also hide the preserve notes check box binding.keepNotesWithNoValidCards.isVisible = false (dialog as? AlertDialog)?.positiveButton?.text = - getString(R.string.dialog_ok) + getString(CommonString.dialog_ok) (dialog as? AlertDialog)?.negativeButton?.visibility = View.GONE } else { binding.reportScrollView.updateViewHeight() @@ -132,7 +132,7 @@ class EmptyCardsDialogFragment : AnalyticsDialogFragment() { binding.keepNotesWithNoValidCards.isVisible = true binding.emptyReportMessage.isVisible = false (dialog as? AlertDialog)?.positiveButton?.text = - getString(R.string.dialog_positive_delete) + getString(CommonString.dialog_positive_delete) binding.emptyCardsResultsContainer.isVisible = true } (dialog as? AlertDialog)?.positiveButton?.isEnabled = true @@ -142,9 +142,9 @@ class EmptyCardsDialogFragment : AnalyticsDialogFragment() { // the dialog is informational so there's nothing to do but show the // error and exit AlertDialog.Builder(requireActivity()).show { - title(R.string.vague_error) + title(CommonString.vague_error) message(text = state.throwable.toString()) - positiveButton(R.string.dialog_ok) { } + positiveButton(CommonString.dialog_ok) { } } dismissNow() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ExportReadyDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ExportReadyDialog.kt index 2712f515634a..ea40a215ccee 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ExportReadyDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ExportReadyDialog.kt @@ -7,7 +7,7 @@ import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import com.ichi2.anki.AnkiActivity -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.dialogs.viewmodel.ExportReadyViewModel.ExportReadyParams import com.ichi2.anki.utils.ext.requireString import com.ichi2.utils.negativeButton @@ -24,13 +24,13 @@ class ExportReadyDialog : DialogFragment() { val dialog = AlertDialog.Builder(requireActivity()) dialog - .setTitle(getString(R.string.export_ready_title)) - .positiveButton(R.string.export_choice_save_to) { + .setTitle(getString(CommonString.export_ready_title)) + .positiveButton(CommonString.export_choice_save_to) { parentFragmentManager.setFragmentResult( REQUEST_EXPORT_SAVE, Bundle().apply { putString(KEY_EXPORT_PATH, exportPath) }, ) - }.negativeButton(R.string.export_choice_share) { + }.negativeButton(CommonString.export_choice_share) { parentFragmentManager.setFragmentResult( REQUEST_EXPORT_SHARE, Bundle().apply { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FatalErrorDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FatalErrorDialog.kt index 15b5f5f04adb..a862564ca7a9 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FatalErrorDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FatalErrorDialog.kt @@ -5,8 +5,8 @@ package com.ichi2.anki.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.CommonString import com.ichi2.anki.InitialActivity.StartupFailure.InitializationError -import com.ichi2.anki.R import com.ichi2.utils.cancelable import com.ichi2.utils.create import com.ichi2.utils.message @@ -23,13 +23,13 @@ object FatalErrorDialog { val context: Context = activity Timber.i("Displaying 'Fatal error'") return AlertDialog.Builder(context).create { - title(R.string.ankidroid_init_failed_webview_title) + title(CommonString.ankidroid_init_failed_webview_title) message(text = failure.toHumanReadableString(context)) - positiveButton(R.string.close) { + positiveButton(CommonString.close) { activity.closeCollectionAndFinish() } failure.infoLink?.let { url -> - neutralButton(R.string.help) { + neutralButton(CommonString.help) { activity.openUrl(url) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FieldSelectionDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FieldSelectionDialog.kt index 4f7535ac86b2..b68c78ac1c46 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FieldSelectionDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FieldSelectionDialog.kt @@ -9,7 +9,7 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.dialogs.FieldSelectionDialog.Companion.RESULT_TYPE import com.ichi2.anki.dialogs.FieldSelectionDialog.Companion.registerFieldSelectionHandler import com.ichi2.anki.libanki.Field @@ -41,7 +41,7 @@ class FieldSelectionDialog : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = MaterialAlertDialogBuilder(requireContext()).create { - setTitle(R.string.card_template_editor_select_field) + setTitle(CommonString.card_template_editor_select_field) setItems(fieldNames.toTypedArray()) { _, which -> onFieldSelected(fieldNames[which]) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FlagRenameDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FlagRenameDialog.kt index 5f25bcd0e6e8..fcb66070a744 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FlagRenameDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/FlagRenameDialog.kt @@ -11,8 +11,8 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager +import com.ichi2.anki.CommonString import com.ichi2.anki.Flag -import com.ichi2.anki.R import com.ichi2.anki.databinding.DialogRenameFlagBinding import com.ichi2.utils.customView import com.ichi2.utils.title @@ -28,7 +28,7 @@ class FlagRenameDialog : DialogFragment() { val builder = AlertDialog.Builder(requireContext()).apply { customView(view = binding.root, 4, 4, 4, 4) - title(R.string.rename_flag) + title(CommonString.rename_flag) } val dialog = builder.create() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/GradeNowDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/GradeNowDialog.kt index 06392301cead..91cd2fb5494a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/GradeNowDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/GradeNowDialog.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity import anki.scheduler.CardAnswer.Rating import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.databinding.ItemGradeNowBinding @@ -55,7 +56,7 @@ object GradeNowDialog { MaterialAlertDialogBuilder(context).show { title(text = with(context) { TR.sentenceCase.gradeNow }) - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) setAdapter(adapter) { dialog, which -> val selectedGrade = adapter.getItem(which)!! Timber.i("selected '%s'", selectedGrade.name) @@ -75,7 +76,7 @@ object GradeNowDialog { undoableOp { this.backend.gradeNow(ids, grade.rating) } } showSnackbar(TR.schedulingGradedCardsDone(ids.size)) { - setAction(R.string.undo) { launchCatchingTask { undoAndShowSnackbar() } } + setAction(CommonString.undo) { launchCatchingTask { undoAndShowSnackbar() } } } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportDialog.kt index ef1abbc4ee99..1eed98a0ecb7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportDialog.kt @@ -7,7 +7,7 @@ import android.os.Bundle import androidx.annotation.CheckResult import androidx.appcompat.app.AlertDialog import androidx.fragment.app.activityViewModels -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.dialogs.ImportDialog.Type.DIALOG_IMPORT_ADD_CONFIRM import com.ichi2.anki.dialogs.ImportDialog.Type.DIALOG_IMPORT_REPLACE_CONFIRM @@ -35,22 +35,22 @@ class ImportDialog : AsyncDialogFragment() { return when (dialogType) { DIALOG_IMPORT_ADD_CONFIRM -> { dialog - .setTitle(R.string.import_title) - .setMessage(res().getString(R.string.import_dialog_message_add, displayFileName)) - .positiveButton(R.string.import_message_add) { + .setTitle(CommonString.import_title) + .setMessage(res().getString(CommonString.import_dialog_message_add, displayFileName)) + .positiveButton(CommonString.import_message_add) { importViewModel.triggerImportAdd(packagePath) activity?.dismissAllDialogFragments() - }.negativeButton(R.string.dialog_cancel) + }.negativeButton(CommonString.dialog_cancel) .create() } DIALOG_IMPORT_REPLACE_CONFIRM -> { dialog - .setTitle(R.string.import_title) - .setMessage(res().getString(R.string.import_message_replace_confirm, displayFileName)) - .positiveButton(R.string.dialog_positive_replace) { + .setTitle(CommonString.import_title) + .setMessage(res().getString(CommonString.import_message_replace_confirm, displayFileName)) + .positiveButton(CommonString.dialog_positive_replace) { importViewModel.triggerImportReplace(packagePath) activity?.dismissAllDialogFragments() - }.negativeButton(R.string.dialog_cancel) + }.negativeButton(CommonString.dialog_cancel) .create() } } @@ -69,12 +69,12 @@ class ImportDialog : AsyncDialogFragment() { override val notificationMessage: String get() { - return res().getString(R.string.import_interrupted) + return res().getString(CommonString.import_interrupted) } override val notificationTitle: String get() { - return res().getString(R.string.import_title) + return res().getString(CommonString.import_title) } enum class Type( diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportFileSelectionFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportFileSelectionFragment.kt index 29553578c023..62f321c68f68 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportFileSelectionFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/ImportFileSelectionFragment.kt @@ -15,7 +15,7 @@ import androidx.core.os.BundleCompat import androidx.fragment.app.DialogFragment import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.analytics.Analytics import com.ichi2.anki.common.analytics.AnalyticsEvent.LinkClicked import com.ichi2.anki.common.analytics.LinkAction @@ -57,7 +57,7 @@ class ImportFileSelectionFragment : DialogFragment() { if (options.importApkg) { add( ImportEntry( - R.string.import_deck_package, + CommonString.import_deck_package, LinkAction.IMPORT_APKG_FILE, ImportFileType.APKG, ), @@ -66,7 +66,7 @@ class ImportFileSelectionFragment : DialogFragment() { if (options.importColpkg) { add( ImportEntry( - R.string.import_collection_package, + CommonString.import_collection_package, LinkAction.IMPORT_COLPKG_FILE, ImportFileType.COLPKG, ), @@ -75,7 +75,7 @@ class ImportFileSelectionFragment : DialogFragment() { if (options.importTextFile) { add( ImportEntry( - R.string.import_csv, + CommonString.import_csv, LinkAction.IMPORT_CSV_FILE, ImportFileType.CSV, multiple = false, @@ -171,7 +171,7 @@ class ImportFileSelectionFragment : DialogFragment() { } } catch (ex: ActivityNotFoundException) { Timber.w("No activity to handle openImportFilePicker request") - activity.showSnackbar(R.string.activity_start_failed) + activity.showSnackbar(CommonString.activity_start_failed) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt index 3b520af9e2e7..d0710c2dd91f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/InsertFieldDialog.kt @@ -29,6 +29,7 @@ import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import com.ichi2.anki.CardTemplateEditor +import com.ichi2.anki.CommonString import com.ichi2.anki.Flag import com.ichi2.anki.R import com.ichi2.anki.databinding.DialogGenericRecyclerViewBinding @@ -71,7 +72,7 @@ class InsertFieldDialog : DialogFragment(R.layout.dialog_insert_field) { val binding = DialogInsertFieldBinding.bind(view) setupEdgeToEdge(binding) - binding.toolbar.title = getString(R.string.card_template_editor_select_field) + binding.toolbar.title = getString(CommonString.card_template_editor_select_field) binding.toolbar.setNavigationOnClickListener { dismiss() } binding.viewPager.adapter = InsertFieldDialogAdapter(this) @@ -261,19 +262,19 @@ fun SpecialField.buildDescription( ): Spanned { fun buildSuffix(value: String?): String { if (value == null) return "" - return context.getString(R.string.special_field_example_suffix, value) + return context.getString(CommonString.special_field_example_suffix, value) } return when (this) { - SpecialFields.FrontSide -> context.getString(R.string.special_field_front_side_help) + SpecialFields.FrontSide -> context.getString(CommonString.special_field_front_side_help) SpecialFields.Deck -> - context.getString(R.string.special_field_deck_help, buildSuffix(metadata.deck)) + context.getString(CommonString.special_field_deck_help, buildSuffix(metadata.deck)) SpecialFields.Subdeck -> - context.getString(R.string.special_field_subdeck_help, buildSuffix(metadata.subdeck)) + context.getString(CommonString.special_field_subdeck_help, buildSuffix(metadata.subdeck)) SpecialFields.Flag -> { val code = metadata.flag ?: "N" context.getString( - R.string.special_field_flag_help, + CommonString.special_field_flag_help, if (code == "N") "flag$code" else "flag$code", "$code", Flag.entries.minOf { it.code }.toString(), @@ -282,20 +283,20 @@ fun SpecialField.buildDescription( } SpecialFields.Tags -> { val tags = if (metadata.tags.isNullOrBlank()) null else metadata.tags - context.getString(R.string.special_field_tags_help, buildSuffix(tags)) + context.getString(CommonString.special_field_tags_help, buildSuffix(tags)) } SpecialFields.CardId -> - context.getString(R.string.special_field_card_id_help, buildSuffix(metadata.cardId?.toString())) + context.getString(CommonString.special_field_card_id_help, buildSuffix(metadata.cardId?.toString())) SpecialFields.CardTemplate -> context.getString( - R.string.special_field_card_help, + CommonString.special_field_card_help, buildSuffix(metadata.cardTemplateName), ) SpecialFields.NoteType -> context.getString( - R.string.special_field_type_help, + CommonString.special_field_type_help, buildSuffix(metadata.noteTypeName), ) // this shouldn't happen @@ -309,7 +310,7 @@ private val Tab.title: String get() = dialog.requireContext().getString( when (this) { - Tab.FIELDS -> R.string.standard_fields_tab_header - Tab.SPECIAL -> R.string.special_fields_tab_header + Tab.FIELDS -> CommonString.standard_fields_tab_header + Tab.SPECIAL -> CommonString.special_fields_tab_header }, ) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/IntegerDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/IntegerDialog.kt index decf6a405433..56789b6d7a84 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/IntegerDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/IntegerDialog.kt @@ -19,6 +19,7 @@ package com.ichi2.anki.dialogs import android.os.Bundle import android.text.InputType import androidx.appcompat.app.AlertDialog +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.snackbar.showSnackbar @@ -61,8 +62,8 @@ open class IntegerDialog : AnalyticsDialogFragment() { .Builder(requireActivity()) .show { title(text = requireArguments().getString("title")) - positiveButton(R.string.dialog_ok) - negativeButton(R.string.dialog_cancel) + positiveButton(CommonString.dialog_ok) + negativeButton(CommonString.dialog_cancel) setMessage(requireArguments().getString("content")) setView(R.layout.dialog_generic_text_input) }.input( @@ -79,7 +80,7 @@ open class IntegerDialog : AnalyticsDialogFragment() { } catch (e: Exception) { Timber.w(e) // TODO: find a good place in the foreground to show snackbar - showSnackbar(R.string.something_wrong) + showSnackbar(CommonString.something_wrong) return@input } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/LoadingDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/LoadingDialogFragment.kt index 53251532c99b..2e101a36fb34 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/LoadingDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/LoadingDialogFragment.kt @@ -12,6 +12,7 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.commitNow import com.google.android.material.loadingindicator.LoadingIndicator import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.utils.cancelable import com.ichi2.utils.create @@ -26,7 +27,7 @@ class LoadingDialogFragment : DialogFragment() { val canBeCancelled = arguments?.getBoolean(KEY_CANCELLABLE) ?: true isCancelable = canBeCancelled dialogView.findViewById(R.id.text).text = - arguments?.getString(KEY_MESSAGE) ?: getString(R.string.dialog_processing) + arguments?.getString(KEY_MESSAGE) ?: getString(CommonString.dialog_processing) return AlertDialog .Builder(requireContext()) .create { @@ -43,7 +44,7 @@ class LoadingDialogFragment : DialogFragment() { /** * Creates an instance of [LoadingDialogFragment]. * @param message optional message for the loading operation, will default to - * [R.string.dialog_processing] if not provided + * [CommonString.dialog_processing] if not provided * @param cancellable if the dialog should be cancellable or not (also affects cancel when * touching outside the dialog window) */ @@ -90,7 +91,7 @@ fun AnkiActivity.showLoadingDialog( val fragmentView = fragment.view // sanity check if (fragmentView != null && cancellable == fragment.isCancelable) { fragmentView.findViewById(R.id.text)?.text = - message ?: getString(R.string.dialog_processing) + message ?: getString(CommonString.dialog_processing) return } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenu.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenu.kt index 3c4fde31e60e..d52a35009ba7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenu.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/NoteTypeFieldEditorContextMenu.kt @@ -8,8 +8,8 @@ import android.os.Bundle import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AlertDialog +import com.ichi2.anki.CommonString import com.ichi2.anki.NoteTypeFieldEditor -import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.utils.create import timber.log.Timber @@ -35,11 +35,11 @@ class NoteTypeFieldEditorContextMenu : AnalyticsDialogFragment() { val order: Int, @StringRes val actionTextId: Int, ) { - Reposition(0, R.string.model_field_editor_reposition_menu), - Sort(1, R.string.model_field_editor_sort_field), - Rename(2, R.string.model_field_editor_rename), - Delete(3, R.string.model_field_editor_delete), - AddLanguageHint(4, R.string.model_field_editor_language_hint), + Reposition(0, CommonString.model_field_editor_reposition_menu), + Sort(1, CommonString.model_field_editor_sort_field), + Rename(2, CommonString.model_field_editor_rename), + Delete(3, CommonString.model_field_editor_delete), + AddLanguageHint(4, CommonString.model_field_editor_language_hint), } companion object { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SaveBrowserSearchDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SaveBrowserSearchDialogFragment.kt index 7bffb06cb0e1..1253d00e174d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SaveBrowserSearchDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SaveBrowserSearchDialogFragment.kt @@ -11,6 +11,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.setFragmentResult import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.CardBrowser +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.browser.search.SavedSearch import com.ichi2.anki.dialogs.SaveBrowserSearchDialogFragment.Companion.ARG_SEARCH_QUERY @@ -39,12 +40,12 @@ class SaveBrowserSearchDialogFragment : DialogFragment() { return AlertDialog .Builder(requireContext()) .show { - title(text = getString(R.string.card_browser_list_my_searches_save)) - positiveButton(R.string.dialog_ok) - negativeButton(R.string.dialog_cancel) + title(text = getString(CommonString.card_browser_list_my_searches_save)) + positiveButton(CommonString.dialog_ok) + negativeButton(CommonString.dialog_cancel) setView(R.layout.dialog_generic_text_input) }.input( - hint = getString(R.string.card_browser_list_my_searches_new_name), + hint = getString(CommonString.card_browser_list_my_searches_new_name), allowEmpty = false, displayKeyboard = true, waitForPositiveButton = true, @@ -97,7 +98,7 @@ fun CardBrowser.registerSaveSearchHandler() { bundle.getString(ARG_SEARCH_QUERY_NAME) if (savedSearchName.isNullOrEmpty()) { showSnackbar( - R.string.card_browser_list_my_searches_new_search_error_empty_name, + CommonString.card_browser_list_my_searches_new_search_error_empty_name, Snackbar.LENGTH_SHORT, ) return@setFragmentResultListener @@ -128,7 +129,7 @@ fun Fragment.registerSaveSearchHandler(onSaveSearch: (SavedSearch) -> Unit) { bundle.getString(ARG_SEARCH_QUERY_NAME) if (savedSearchName.isNullOrEmpty()) { showSnackbar( - R.string.card_browser_list_my_searches_new_search_error_empty_name, + CommonString.card_browser_list_my_searches_new_search_error_empty_name, Snackbar.LENGTH_SHORT, ) return@setFragmentResultListener diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SavedBrowserSearchesDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SavedBrowserSearchesDialogFragment.kt index 3797c1bd24f7..174e2d1167d1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SavedBrowserSearchesDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SavedBrowserSearchesDialogFragment.kt @@ -10,7 +10,7 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView import com.ichi2.anki.CardBrowser -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.browser.search.SavedSearch import com.ichi2.anki.browser.search.SavedSearches @@ -65,15 +65,15 @@ class SavedBrowserSearchesDialogFragment : AnalyticsDialogFragment() { ) return AlertDialog .Builder(requireContext()) - .title(text = resources.getString(R.string.card_browser_list_my_searches_title)) - .negativeButton(R.string.dialog_cancel) + .title(text = resources.getString(CommonString.card_browser_list_my_searches_title)) + .negativeButton(CommonString.dialog_cancel) .apply { customListAdapter(adapter) } .create() } private fun removeSearch(searchName: String) { AlertDialog.Builder(requireActivity()).show { - message(text = resources.getString(R.string.card_browser_list_my_searches_remove_content, searchName)) + message(text = resources.getString(CommonString.card_browser_list_my_searches_remove_content, searchName)) positiveButton(android.R.string.ok) { parentFragmentManager.setFragmentResult( REQUEST_SAVED_SEARCH_ACTION, @@ -84,7 +84,7 @@ class SavedBrowserSearchesDialogFragment : AnalyticsDialogFragment() { ) dialog?.dismiss() // Dismiss the root dialog } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SchedulerUpgradeDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SchedulerUpgradeDialog.kt index 2016b306b183..69761a278b69 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SchedulerUpgradeDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SchedulerUpgradeDialog.kt @@ -8,6 +8,7 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.android.AdaptionUtil import com.ichi2.anki.utils.openUrl @@ -31,10 +32,10 @@ class SchedulerUpgradeDialog( fun showDialog() { val dialog = MaterialAlertDialogBuilder(activity) - .setTitle(R.string.scheduler_upgrade_required_dialog_title) + .setTitle(CommonString.scheduler_upgrade_required_dialog_title) .setMessage(TR.schedulingUpdateRequired()) - .setPositiveButton(R.string.dialog_ok, null) - .setNegativeButton(R.string.dialog_cancel, null) + .setPositiveButton(CommonString.dialog_ok, null) + .setNegativeButton(CommonString.dialog_cancel, null) .apply { if (AdaptionUtil.hasWebBrowser(activity)) { setNeutralButton(TR.schedulingUpdateMoreInfoButton(), null) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SimpleMessageDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SimpleMessageDialog.kt index 2c72ce2cbe34..c073e3fa8c04 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SimpleMessageDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SimpleMessageDialog.kt @@ -8,6 +8,7 @@ import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.fragment.app.FragmentActivity import com.ichi2.anki.AnkiDroidApp +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.R import com.ichi2.anki.utils.ext.dismissAllDialogFragments @@ -19,7 +20,7 @@ class SimpleMessageDialog : AsyncDialogFragment() { return AlertDialog.Builder(requireContext()).create { setTitle(notificationTitle) setMessage(notificationMessage) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setPositiveButton(CommonString.dialog_ok) { _, _ -> activity?.dismissSimpleMessageDialog(requireArguments().getBoolean(ARG_RELOAD)) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt index a639da0e4afb..741b8cc98193 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/SyncErrorDialog.kt @@ -13,6 +13,7 @@ import androidx.annotation.CheckResult import androidx.appcompat.app.AlertDialog import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.ConflictResolution import com.ichi2.anki.R import com.ichi2.anki.common.time.TimeManager @@ -74,16 +75,16 @@ class SyncErrorDialog : AsyncDialogFragment() { .setIcon(R.drawable.ic_sync_problem) .setPositiveButton(TR.sentenceCase.logIn) { _, _ -> requireSyncErrorDialogListener().loginToSyncServer() - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_CONNECTION_ERROR -> { // Connection error; allow user to retry or cancel dialog .setIcon(R.drawable.ic_sync_problem) - .setPositiveButton(R.string.retry) { _, _ -> + .setPositiveButton(CommonString.retry) { _, _ -> syncAndDismissAllDialogFragments() - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> activity?.dismissAllDialogFragments() }.create() } @@ -91,15 +92,15 @@ class SyncErrorDialog : AsyncDialogFragment() { // Sync conflict; allow user to cancel, or choose between local and remote versions dialog .titleWithHelpIcon( - text = getString(R.string.sync_conflict_title_new), + text = getString(CommonString.sync_conflict_title_new), startIcon = R.drawable.ic_sync_problem, ) { requireContext().openUrl(getString(R.string.link_sync_conflict_help)) - }.setPositiveButton(R.string.sync_conflict_keep_local_new) { _, _ -> + }.setPositiveButton(CommonString.sync_conflict_keep_local_new) { _, _ -> requireSyncErrorDialogListener().showSyncErrorDialog(DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_LOCAL) - }.setNegativeButton(R.string.sync_conflict_keep_remote_new) { _, _ -> + }.setNegativeButton(CommonString.sync_conflict_keep_remote_new) { _, _ -> requireSyncErrorDialogListener().showSyncErrorDialog(DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_REMOTE) - }.setNeutralButton(R.string.dialog_cancel) { _, _ -> + }.setNeutralButton(CommonString.dialog_cancel) { _, _ -> activity?.dismissAllDialogFragments() }.create() } @@ -107,44 +108,44 @@ class SyncErrorDialog : AsyncDialogFragment() { // Confirmation before pushing local collection to server after sync conflict dialog .setIcon(R.drawable.ic_sync_problem) - .setPositiveButton(R.string.dialog_positive_replace) { _, _ -> + .setPositiveButton(CommonString.dialog_positive_replace) { _, _ -> syncAndDismissAllDialogFragments(ConflictResolution.FULL_UPLOAD) - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_REMOTE -> { // Confirmation before overwriting local collection with server collection after sync conflict dialog .setIcon(R.drawable.ic_sync_problem) - .setPositiveButton(R.string.dialog_positive_replace) { _, _ -> + .setPositiveButton(CommonString.dialog_positive_replace) { _, _ -> syncAndDismissAllDialogFragments(ConflictResolution.FULL_DOWNLOAD) - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_SYNC_SANITY_ERROR -> { // Sync sanity check error; allow user to cancel, or choose between local and remote versions dialog - .setPositiveButton(R.string.sync_sanity_local) { _, _ -> + .setPositiveButton(CommonString.sync_sanity_local) { _, _ -> requireSyncErrorDialogListener().showSyncErrorDialog(DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_LOCAL) - }.setNeutralButton(R.string.sync_sanity_remote) { _, _ -> + }.setNeutralButton(CommonString.sync_sanity_remote) { _, _ -> requireSyncErrorDialogListener().showSyncErrorDialog(DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_REMOTE) - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_LOCAL -> { // Confirmation before pushing local collection to server after sanity check error dialog - .setPositiveButton(R.string.dialog_positive_replace) { _, _ -> + .setPositiveButton(CommonString.dialog_positive_replace) { _, _ -> syncAndDismissAllDialogFragments(ConflictResolution.FULL_UPLOAD) - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_REMOTE -> { // Confirmation before overwriting local collection with server collection after sanity check error dialog - .setPositiveButton(R.string.dialog_positive_replace) { _, _ -> + .setPositiveButton(CommonString.dialog_positive_replace) { _, _ -> syncAndDismissAllDialogFragments(ConflictResolution.FULL_DOWNLOAD) - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_MEDIA_SYNC_ERROR -> { @@ -152,13 +153,13 @@ class SyncErrorDialog : AsyncDialogFragment() { .setPositiveButton(TR.sentenceCase.checkMediaAction) { _, _ -> requireSyncErrorDialogListener().mediaCheck() activity?.dismissAllDialogFragments() - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_SYNC_CORRUPT_COLLECTION -> { dialog - .setPositiveButton(R.string.dialog_ok) { _, _ -> } - .setNegativeButton(R.string.help) { _, _ -> + .setPositiveButton(CommonString.dialog_ok) { _, _ -> } + .setNegativeButton(CommonString.help) { _, _ -> requireContext().openUrl(R.string.repair_deck) }.setCancelable(false) .create() @@ -168,13 +169,13 @@ class SyncErrorDialog : AsyncDialogFragment() { .setPositiveButton(TR.sentenceCase.checkDatabase) { _, _ -> requireSyncErrorDialogListener().integrityCheck() activity?.dismissAllDialogFragments() - }.setNegativeButton(R.string.dialog_cancel) { _, _ -> } + }.setNegativeButton(CommonString.dialog_cancel) { _, _ -> } .create() } DIALOG_SYNC_CLOCK_OFF -> { dialog - .setPositiveButton(R.string.open_settings, null) - .setNegativeButton(R.string.dialog_cancel, null) + .setPositiveButton(CommonString.open_settings, null) + .setNegativeButton(CommonString.dialog_cancel, null) .create() .apply { // Override the default handler so the dialog remains open if no date-settings activity exists. @@ -197,12 +198,12 @@ class SyncErrorDialog : AsyncDialogFragment() { private val title: String get() = when (dialogType) { - DIALOG_USER_NOT_LOGGED_IN_SYNC -> res().getString(R.string.not_logged_in_title) + DIALOG_USER_NOT_LOGGED_IN_SYNC -> res().getString(CommonString.not_logged_in_title) DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_LOCAL, DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_REMOTE -> res().getString( - R.string.sync_conflict_replace_title, + CommonString.sync_conflict_replace_title, ) - DIALOG_SYNC_CONFLICT_RESOLUTION -> res().getString(R.string.sync_conflict_title_new) + DIALOG_SYNC_CONFLICT_RESOLUTION -> res().getString(CommonString.sync_conflict_title_new) DIALOG_CONNECTION_ERROR, DIALOG_SYNC_SANITY_ERROR, DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_LOCAL, @@ -210,8 +211,8 @@ class SyncErrorDialog : AsyncDialogFragment() { DIALOG_MEDIA_SYNC_ERROR, DIALOG_SYNC_CORRUPT_COLLECTION, DIALOG_SYNC_BASIC_CHECK_ERROR, - -> res().getString(R.string.sync_error) - DIALOG_SYNC_CLOCK_OFF -> res().getString(R.string.vague_error) + -> res().getString(CommonString.sync_error) + DIALOG_SYNC_CLOCK_OFF -> res().getString(CommonString.vague_error) } /** @@ -222,7 +223,7 @@ class SyncErrorDialog : AsyncDialogFragment() { override val notificationTitle: String get() { return if (dialogType == DIALOG_USER_NOT_LOGGED_IN_SYNC) { - res().getString(R.string.sync_error) + res().getString(CommonString.sync_error) } else { title } @@ -231,26 +232,26 @@ class SyncErrorDialog : AsyncDialogFragment() { private val message: String? get() = when (dialogType) { - DIALOG_USER_NOT_LOGGED_IN_SYNC -> res().getString(R.string.login_create_account_message) - DIALOG_CONNECTION_ERROR -> res().getString(R.string.connection_error_message) - DIALOG_SYNC_CONFLICT_RESOLUTION -> res().getString(R.string.sync_conflict_message_new) + DIALOG_USER_NOT_LOGGED_IN_SYNC -> res().getString(CommonString.login_create_account_message) + DIALOG_CONNECTION_ERROR -> res().getString(CommonString.connection_error_message) + DIALOG_SYNC_CONFLICT_RESOLUTION -> res().getString(CommonString.sync_conflict_message_new) DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_LOCAL, DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_LOCAL -> res().getString( - R.string.sync_conflict_local_confirm_new, + CommonString.sync_conflict_local_confirm_new, ) DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_REMOTE, DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_REMOTE -> res().getString( - R.string.sync_conflict_remote_confirm_new, + CommonString.sync_conflict_remote_confirm_new, ) DIALOG_SYNC_CORRUPT_COLLECTION -> { val syncMessage = requireArguments().getString(DIALOG_MESSAGE_KEY) val repairUrl = res().getString(R.string.repair_deck) - val dialogMessage = res().getString(R.string.sync_corrupt_database, repairUrl) + val dialogMessage = res().getString(CommonString.sync_corrupt_database, repairUrl) joinSyncMessages(dialogMessage, syncMessage) } DIALOG_SYNC_CLOCK_OFF -> res().getString( - R.string.sync_clock_off_with_current_time, + CommonString.sync_clock_off_with_current_time, requireArguments().getString(DIALOG_MESSAGE_KEY).orEmpty(), DateFormat.getDateTimeInstance().format(TimeManager.time.currentDate), ) @@ -283,7 +284,7 @@ class SyncErrorDialog : AsyncDialogFragment() { override val notificationMessage: String? get() { return if (dialogType == DIALOG_USER_NOT_LOGGED_IN_SYNC) { - res().getString(R.string.not_logged_in_title) + res().getString(CommonString.not_logged_in_title) } else { message } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt index 00146fa4c9a4..00bc25dba0a8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt @@ -20,6 +20,7 @@ import android.app.Activity import android.content.Intent import androidx.appcompat.app.AlertDialog import androidx.fragment.app.FragmentManager +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.TtsVoices import com.ichi2.anki.common.crashreporting.CrashReportService @@ -37,13 +38,13 @@ object TtsPlaybackErrorDialog { Timber.i("Dialog is shown to guide users correctly to troubleshoot the Tts error: Missing voice error") activity.runOnUiThread { AlertDialog.Builder(activity).show { - setTitle(activity.getString(R.string.tts_error_dialog_title)) - setMessage(activity.getString(R.string.tts_error_dialog_reason_text, TtsVoices.ttsEngine, ttsTag?.lang)) - setNegativeButton(context.getString(R.string.tts_error_dialog_change_button_text)) { _, _ -> openSettings(activity) } + setTitle(activity.getString(CommonString.tts_error_dialog_title)) + setMessage(activity.getString(CommonString.tts_error_dialog_reason_text, TtsVoices.ttsEngine, ttsTag?.lang)) + setNegativeButton(context.getString(CommonString.tts_error_dialog_change_button_text)) { _, _ -> openSettings(activity) } setPositiveButton( - activity.getString(R.string.tts_error_dialog_supported_voices_button_text), + activity.getString(CommonString.tts_error_dialog_supported_voices_button_text), ) { _, _ -> showVoicesDialog(fragmentManager) } - setNeutralButton(context.getString(R.string.help)) { _, _ -> + setNeutralButton(context.getString(CommonString.help)) { _, _ -> activity.openUrl(R.string.link_faq_tts) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsVoicesDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsVoicesDialogFragment.kt index a4db1ed21b85..dbd3354e8ec4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsVoicesDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsVoicesDialogFragment.kt @@ -21,6 +21,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.ichi2.anki.AndroidTtsVoice +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.anki.databinding.DialogTtsVoicesBinding @@ -114,7 +115,7 @@ class TtsVoicesDialogFragment : DialogFragment(R.layout.dialog_tts_voices) { ) } catch (e: ActivityNotFoundException) { Timber.w(e) - showThemedToast(requireContext(), R.string.tts_voices_failed_opening_tts_system_settings, shortLength = true) + showThemedToast(requireContext(), CommonString.tts_voices_failed_opening_tts_system_settings, shortLength = true) } } @@ -146,15 +147,15 @@ class TtsVoicesDialogFragment : DialogFragment(R.layout.dialog_tts_voices) { } viewModel.uninstalledVoicePlayed.observe { voice -> - dialog?.window?.decorView?.showSnackbar(R.string.tts_voices_selected_voice_should_be_installed) { - setAction(R.string.tts_voices_use_selected_voice_without_install) { viewModel.copyToClipboard(voice) } + dialog?.window?.decorView?.showSnackbar(CommonString.tts_voices_selected_voice_should_be_installed) { + setAction(CommonString.tts_voices_use_selected_voice_without_install) { viewModel.copyToClipboard(voice) } } } viewModel.ttsPlaybackErrorFlow.observe { val string = it.localizedErrorMessage(requireContext()) dialog?.window?.decorView?.showSnackbar(string) { - setAction(R.string.help) { + setAction(CommonString.help) { // TODO: Should do this in ViewModel, but we need an Activity requireContext().openUrl(R.string.link_faq_tts) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/WhiteBoardWidthDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/WhiteBoardWidthDialog.kt index 6ec7e8580845..412beb1c7bf4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/WhiteBoardWidthDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/WhiteBoardWidthDialog.kt @@ -22,7 +22,7 @@ import android.widget.LinearLayout import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.ui.FixedTextView import com.ichi2.utils.negativeButton import com.ichi2.utils.positiveButton @@ -77,11 +77,11 @@ class WhiteBoardWidthDialog( ), ) AlertDialog.Builder(context).show { - title(R.string.whiteboard_stroke_width) - positiveButton(R.string.save) { + title(CommonString.whiteboard_stroke_width) + positiveButton(CommonString.save) { onStrokeWidthChanged?.accept(wbStrokeWidth) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) setView(layout) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt index 9ce37bdda7a3..b8a2216b11c3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt @@ -36,6 +36,8 @@ import anki.search.SearchNode import anki.search.searchNode import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.asyncIO @@ -193,7 +195,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { if (item.checkAvailability != null) { val defaults = withProgress { deferredDefaults.await() } if (!item.checkAvailability(defaults)) { - showSnackbar(getString((R.string.studyoptions_no_cards_due))) + showSnackbar(getString((CommonString.studyoptions_no_cards_due))) return } } @@ -338,9 +340,9 @@ class CustomStudyDialog : AnalyticsDialogFragment() { if (contextMenuOption == STUDY_TAGS) { TR.sentenceCase.chooseTags } else if (contextMenuOption == STUDY_AHEAD || contextMenuOption == STUDY_PREVIEW) { - getString(R.string.dialog_positive_create) + getString(CommonString.dialog_positive_create) } else { - getString(R.string.dialog_ok) + getString(CommonString.dialog_ok) } // Set material dialog parameters @@ -361,7 +363,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { paddingTop = verticalPadding, paddingBottom = verticalPadding, ).positiveButton(text = positiveBtnLabel, click = null) - .negativeButton(R.string.dialog_cancel) { + .negativeButton(CommonString.dialog_cancel) { requireActivity().dismissAllDialogFragments() }.create() @@ -447,7 +449,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { /** Sets the suffix of the days input: `[1] day`, `[3] days` */ private fun setSuffixText(days: Int) { - binding.detailsEditText2Layout.suffixText = resources.getQuantityString(R.plurals.set_due_date_label_suffix, days) + binding.detailsEditText2Layout.suffixText = resources.getQuantityString(CommonPlurals.set_due_date_label_suffix, days) } /** @@ -472,7 +474,7 @@ class CustomStudyDialog : AnalyticsDialogFragment() { days: Int?, ) { if (days == null || days == 0) { - binding.detailsEditText2Layout.error = if (days == 0) getString(R.string.minimum_value_is, 1) else null + binding.detailsEditText2Layout.error = if (days == 0) getString(CommonString.minimum_value_is, 1) else null dialog.positiveButton.isEnabled = false return } @@ -588,12 +590,12 @@ class CustomStudyDialog : AnalyticsDialogFragment() { get() { val res = resources return when (selectedSubDialog) { - EXTEND_NEW -> res.getString(R.string.custom_study_new_extend) - EXTEND_REV -> res.getString(R.string.custom_study_rev_extend) - STUDY_FORGOT -> res.getString(R.string.custom_study_forgotten) - STUDY_AHEAD -> res.getString(R.string.custom_study_ahead_description) - STUDY_PREVIEW -> res.getString(R.string.custom_study_preview) - STUDY_TAGS -> res.getString(R.string.custom_study_tags) + EXTEND_NEW -> res.getString(CommonString.custom_study_new_extend) + EXTEND_REV -> res.getString(CommonString.custom_study_rev_extend) + STUDY_FORGOT -> res.getString(CommonString.custom_study_forgotten) + STUDY_AHEAD -> res.getString(CommonString.custom_study_ahead_description) + STUDY_PREVIEW -> res.getString(CommonString.custom_study_preview) + STUDY_TAGS -> res.getString(CommonString.custom_study_tags) null -> "" } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/TagLimitFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/TagLimitFragment.kt index 1fa0519bf8f4..e33deea64214 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/TagLimitFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/TagLimitFragment.kt @@ -13,7 +13,7 @@ import androidx.fragment.app.setFragmentResult import androidx.lifecycle.lifecycleScope import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.databinding.FragmentTagLimitBinding import com.ichi2.anki.dialogs.customstudy.IncludedExcludedTagsAdapter.TagsSelectionMode.Exclude import com.ichi2.anki.dialogs.customstudy.IncludedExcludedTagsAdapter.TagsSelectionMode.Include @@ -69,8 +69,8 @@ class TagLimitFragment : DialogFragment() { .Builder(requireContext()) .title(text = TR.sentenceCase.selectiveStudy) .customView(binding.root) - .negativeButton(R.string.dialog_cancel) - .positiveButton(R.string.dialog_ok, null) + .negativeButton(CommonString.dialog_cancel) + .positiveButton(CommonString.dialog_ok, null) .create() var allowSubmit = true diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpDialog.kt index ff5fc454161c..8e0ad1126150 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpDialog.kt @@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.commit import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.BuildConfig +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.ankiActivity @@ -119,7 +120,7 @@ class HelpDialog : AnalyticsDialogFragment() { return HelpDialog().apply { arguments = Bundle().apply { - putInt(ARG_MENU_TITLE, R.string.help) + putInt(ARG_MENU_TITLE, CommonString.help) putParcelableArray(ARG_MENU_ITEMS, mainHelpMenuItems) } } @@ -132,7 +133,7 @@ class HelpDialog : AnalyticsDialogFragment() { return HelpDialog().apply { arguments = Bundle().apply { - putInt(ARG_MENU_TITLE, R.string.help_title_privacy) + putInt(ARG_MENU_TITLE, CommonString.help_title_privacy) putParcelableArray(ARG_MENU_ITEMS, privacyItems.toTypedArray()) } } @@ -150,7 +151,7 @@ class HelpDialog : AnalyticsDialogFragment() { return HelpDialog().apply { arguments = Bundle().apply { - putInt(ARG_MENU_TITLE, R.string.help_title_support_ankidroid) + putInt(ARG_MENU_TITLE, CommonString.help_title_support_ankidroid) putParcelableArray(ARG_MENU_ITEMS, actualMenuItems.toTypedArray()) } } @@ -214,25 +215,25 @@ class HelpPageFragment : Fragment(R.layout.fragment_help_page) { internal val mainHelpMenuItems = arrayOf( HelpItem( - titleResId = R.string.help_title_using_ankidroid, + titleResId = CommonString.help_title_using_ankidroid, iconResId = R.drawable.ic_manual_black_24dp, analyticsId = LinkAction.OPENED_USING_ANKIDROID, id = 1, ), HelpItem( - titleResId = R.string.help_title_get_help, + titleResId = CommonString.help_title_get_help, iconResId = R.drawable.ic_help_black_24dp, analyticsId = LinkAction.OPENED_GET_HELP, id = 2, ), HelpItem( - titleResId = R.string.help_title_community, + titleResId = CommonString.help_title_community, iconResId = R.drawable.ic_people_black_24dp, analyticsId = LinkAction.OPENED_COMMUNITY, id = 3, ), HelpItem( - titleResId = R.string.help_title_privacy, + titleResId = CommonString.help_title_privacy, iconResId = R.drawable.ic_baseline_privacy_tip_24, analyticsId = LinkAction.OPENED_PRIVACY, id = 4, @@ -244,42 +245,42 @@ internal val mainHelpMenuItems = internal val supportMenuItems = arrayOf( HelpItem( - titleResId = R.string.help_item_support_opencollective_donate, + titleResId = CommonString.help_item_support_opencollective_donate, iconResId = R.drawable.ic_round_favorite_24, analyticsId = LinkAction.OPENED_DONATE, id = 5, action = OpenUrlResource(R.string.link_opencollective_donate), ), HelpItem( - titleResId = R.string.multimedia_editor_trans_translate, + titleResId = CommonString.multimedia_editor_trans_translate, iconResId = R.drawable.ic_language_black_24dp, analyticsId = LinkAction.OPENED_TRANSLATE, id = 6, action = OpenUrlResource(R.string.link_translation), ), HelpItem( - titleResId = R.string.help_item_support_develop_ankidroid, + titleResId = CommonString.help_item_support_develop_ankidroid, iconResId = R.drawable.ic_build_black_24, analyticsId = LinkAction.OPENED_DEVELOP, id = 7, action = OpenUrlResource(R.string.link_ankidroid_development_guide), ), HelpItem( - titleResId = R.string.help_item_support_rate_ankidroid, + titleResId = CommonString.help_item_support_rate_ankidroid, iconResId = R.drawable.ic_star_black_24, analyticsId = LinkAction.OPENED_RATE, id = 8, action = Rate, ), HelpItem( - titleResId = R.string.help_item_support_other_ankidroid, + titleResId = CommonString.help_item_support_other_ankidroid, iconResId = R.drawable.ic_help_black_24dp, analyticsId = LinkAction.OPENED_OTHER, id = 9, action = OpenUrlResource(R.string.link_contribution), ), HelpItem( - titleResId = R.string.send_feedback, + titleResId = CommonString.send_feedback, iconResId = R.drawable.ic_email_black_24dp, analyticsId = LinkAction.OPENED_SEND_FEEDBACK, id = 10, @@ -292,7 +293,7 @@ internal val supportMenuItems = internal val childHelpMenuItems = arrayOf( HelpItem( - titleResId = R.string.help_item_ankidroid_manual, + titleResId = CommonString.help_item_ankidroid_manual, iconResId = R.drawable.ic_manual_black_24dp, analyticsId = LinkAction.OPENED_ANKIDROID_MANUAL, id = 100, @@ -300,7 +301,7 @@ internal val childHelpMenuItems = action = OpenUrl(AnkiDroidApp.manualUrl), ), HelpItem( - titleResId = R.string.help_item_anki_manual, + titleResId = CommonString.help_item_anki_manual, iconResId = R.drawable.ic_manual_black_24dp, analyticsId = LinkAction.OPENED_ANKI_MANUAL, id = 101, @@ -308,7 +309,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_anki_manual), ), HelpItem( - titleResId = R.string.help_item_ankidroid_faq, + titleResId = CommonString.help_item_ankidroid_faq, iconResId = R.drawable.ic_help_black_24dp, analyticsId = LinkAction.OPENED_ANKIDROID_FAQ, id = 102, @@ -316,7 +317,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_ankidroid_faq), ), HelpItem( - titleResId = R.string.help_item_mailing_list, + titleResId = CommonString.help_item_mailing_list, iconResId = R.drawable.ic_email_black_24dp, analyticsId = LinkAction.OPENED_MAILING_LIST, id = 200, @@ -324,7 +325,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_forum), ), HelpItem( - titleResId = R.string.help_item_report_bug, + titleResId = CommonString.help_item_report_bug, iconResId = R.drawable.ic_bug_report_black_24dp, analyticsId = LinkAction.OPENED_REPORT_BUG, id = 201, @@ -332,7 +333,7 @@ internal val childHelpMenuItems = action = OpenUrl(AnkiDroidApp.feedbackUrl), ), HelpItem( - titleResId = R.string.help_title_send_exception, + titleResId = CommonString.help_title_send_exception, iconResId = R.drawable.ic_round_assignment_24, analyticsId = LinkAction.EXCEPTION_REPORT, id = 202, @@ -340,7 +341,7 @@ internal val childHelpMenuItems = action = SendReport, ), HelpItem( - titleResId = R.string.help_item_anki_forums, + titleResId = CommonString.help_item_anki_forums, iconResId = R.drawable.ic_forum_black_24dp, analyticsId = LinkAction.OPENED_ANKI_FORUMS, id = 300, @@ -348,7 +349,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_anki_forum), ), HelpItem( - titleResId = R.string.help_item_mailing_list, + titleResId = CommonString.help_item_mailing_list, iconResId = R.drawable.ic_email_black_24dp, analyticsId = LinkAction.OPENED_MAILING_LIST, id = 301, @@ -356,7 +357,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_forum), ), HelpItem( - titleResId = R.string.help_item_reddit, + titleResId = CommonString.help_item_reddit, iconResId = R.drawable.ic_link, analyticsId = LinkAction.OPENED_REDDIT, id = 302, @@ -364,7 +365,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_reddit), ), HelpItem( - titleResId = R.string.help_item_discord, + titleResId = CommonString.help_item_discord, iconResId = R.drawable.ic_link, analyticsId = LinkAction.OPENED_DISCORD, id = 303, @@ -372,7 +373,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_discord), ), HelpItem( - titleResId = R.string.help_item_facebook, + titleResId = CommonString.help_item_facebook, iconResId = R.drawable.ic_link, analyticsId = LinkAction.OPENED_FACEBOOK, id = 304, @@ -380,7 +381,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_facebook), ), HelpItem( - titleResId = R.string.help_item_twitter, + titleResId = CommonString.help_item_twitter, iconResId = R.drawable.ic_link, analyticsId = LinkAction.OPENED_TWITTER, id = 305, @@ -388,7 +389,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_twitter), ), HelpItem( - titleResId = R.string.help_item_ankidroid_privacy_policy, + titleResId = CommonString.help_item_ankidroid_privacy_policy, iconResId = R.drawable.ic_baseline_policy_24, analyticsId = LinkAction.OPENED_ANKIDROID_PRIVACY_POLICY, id = 400, @@ -396,7 +397,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_ankidroid_privacy_policy), ), HelpItem( - titleResId = R.string.help_item_ankiweb_privacy_policy, + titleResId = CommonString.help_item_ankiweb_privacy_policy, iconResId = R.drawable.ic_baseline_policy_24, analyticsId = LinkAction.OPENED_ANKIWEB_PRIVACY_POLICY, id = 401, @@ -404,7 +405,7 @@ internal val childHelpMenuItems = action = OpenUrlResource(R.string.link_ankiweb_privacy_policy), ), HelpItem( - titleResId = R.string.help_item_ankiweb_terms_and_conditions, + titleResId = CommonString.help_item_ankiweb_terms_and_conditions, iconResId = R.drawable.ic_baseline_description_24, analyticsId = LinkAction.OPENED_ANKIWEB_TERMS_AND_CONDITIONS, id = 402, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt index 9d363c5a8740..f637e6c35db8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.dialogs.help import androidx.annotation.StringRes import com.ichi2.anki.AnkiActivity import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.android.AdaptionUtil import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.snackbar.showSnackbar @@ -49,14 +49,14 @@ class AnkiActivityHelpActionsDispatcher( override fun onSendReport() { if (AdaptionUtil.isUserATestClient) { - ankiActivity.showSnackbar(ankiActivity.getString(R.string.user_is_a_robot)) + ankiActivity.showSnackbar(ankiActivity.getString(CommonString.user_is_a_robot)) } else { val wasReportSent = CrashReportService.sendReport(ankiActivity) if (!wasReportSent) { - ankiActivity.showSnackbar(ankiActivity.getString(R.string.help_dialog_exception_report_debounce)) + ankiActivity.showSnackbar(ankiActivity.getString(CommonString.help_dialog_exception_report_debounce)) return } - ankiActivity.showSnackbar(ankiActivity.getString(R.string.help_dialog_exception_report_sent)) + ankiActivity.showSnackbar(ankiActivity.getString(CommonString.help_dialog_exception_report_sent)) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsArrayAdapter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsArrayAdapter.kt index 7ee6148ea54e..7efa15552d94 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsArrayAdapter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsArrayAdapter.kt @@ -11,6 +11,7 @@ import android.widget.ImageButton import android.widget.LinearLayout import androidx.annotation.VisibleForTesting import androidx.recyclerview.widget.RecyclerView +import com.ichi2.anki.CommonString import com.ichi2.anki.OnContextAndLongClickListener import com.ichi2.anki.OnContextAndLongClickListener.Companion.setOnContextAndLongClickListener import com.ichi2.anki.R @@ -284,7 +285,8 @@ class TagsArrayAdapter( // do not add padding if there is no visible nested tag binding.expandButton.visibility = View.GONE } - binding.expandButton.contentDescription = holder.itemView.context.getString(R.string.expand_tag, holder.node.tag.replace("::", " ")) + binding.expandButton.contentDescription = + holder.itemView.context.getString(CommonString.expand_tag, holder.node.tag.replace("::", " ")) binding.textView.text = TagsUtil.getTagParts(holder.node.tag).last() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt index 62627511d88c..24bab21cc278 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt @@ -31,6 +31,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.OnContextAndLongClickListener import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment @@ -173,9 +174,9 @@ class TagsDialog : AnalyticsDialogFragment { val positiveText = if (type == DialogType.EDIT_TAGS) { - getString(R.string.dialog_confirm) + getString(CommonString.dialog_confirm) } else { - getString(R.string.select) + getString(CommonString.select) } val tagsListLayout: RecyclerView.LayoutManager = LinearLayoutManager(requireContext()) @@ -201,7 +202,7 @@ class TagsDialog : AnalyticsDialogFragment { AlertDialog .Builder(requireActivity()) .positiveButton(text = positiveText) { onPositiveButton() } - .negativeButton(R.string.dialog_cancel) + .negativeButton(CommonString.dialog_cancel) .customView(view = binding.root) .create() @@ -217,7 +218,7 @@ class TagsDialog : AnalyticsDialogFragment { binding.progressText.text = when (progress) { TagsDialogViewModel.InitProgress.Processing -> - getString(R.string.dialog_processing) + getString(CommonString.dialog_processing) is TagsDialogViewModel.InitProgress.FetchingNoteTags -> "${progress.noteNumber}/${progress.noteCount}" TagsDialogViewModel.InitProgress.Finished -> null @@ -303,7 +304,7 @@ class TagsDialog : AnalyticsDialogFragment { private fun adjustToolbar(tagsDialogView: View) { val toolbar: Toolbar = binding.toolbar.root - val titleRes = if (type == DialogType.EDIT_TAGS) R.string.card_details_tags else R.string.studyoptions_limit_select_tags + val titleRes = if (type == DialogType.EDIT_TAGS) CommonString.card_details_tags else CommonString.studyoptions_limit_select_tags toolbar.setTitle(titleRes) val toolbarAddItem = toolbar.menu.findItem(R.id.tags_dialog_action_add) @@ -326,7 +327,7 @@ class TagsDialog : AnalyticsDialogFragment { toolbarSearchView = toolbarSearchItem?.actionView as AccessibleSearchView val queryET = toolbarSearchView!!.findViewById(androidx.appcompat.R.id.search_src_text) queryET.filters = arrayOf(addTagFilter) - toolbarSearchView!!.queryHint = getString(R.string.filter_tags) + toolbarSearchView!!.queryHint = getString(CommonString.filter_tags) toolbarSearchView!!.setOnQueryTextListener( object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { @@ -353,7 +354,7 @@ class TagsDialog : AnalyticsDialogFragment { true } if (type == DialogType.EDIT_TAGS) { - toolbarSearchView!!.queryHint = getString(R.string.add_new_filter_tags) + toolbarSearchView!!.queryHint = getString(CommonString.add_new_filter_tags) } else { toolbarAddItem.isVisible = false } @@ -372,9 +373,9 @@ class TagsDialog : AnalyticsDialogFragment { AlertDialog .Builder(requireActivity()) .show { - title(text = getString(R.string.add_tag)) - positiveButton(R.string.menu_add) - negativeButton(R.string.dialog_cancel) + title(text = getString(CommonString.add_tag)) + positiveButton(CommonString.menu_add) + negativeButton(CommonString.dialog_cancel) setView(R.layout.dialog_generic_text_input) }.input( hint = TR.actionsName().dropLastWhile { it == ':' }, @@ -421,7 +422,7 @@ class TagsDialog : AnalyticsDialogFragment { if (exists) { textInputLayout?.error = - getString(R.string.tag_already_exists) + getString(CommonString.tag_already_exists) positiveButton.isEnabled = false } else { @@ -444,10 +445,10 @@ class TagsDialog : AnalyticsDialogFragment { if (tags.add(tag)) { binding.noTagsTextView.isVisible = false tags.add(tag) - val positiveText = (dialog as? AlertDialog)?.positiveButton?.text ?: getString(R.string.dialog_ok) - feedbackText = getString(R.string.tag_editor_add_feedback, tag, positiveText) + val positiveText = (dialog as? AlertDialog)?.positiveButton?.text ?: getString(CommonString.dialog_ok) + feedbackText = getString(CommonString.tag_editor_add_feedback, tag, positiveText) } else { - feedbackText = getString(R.string.tag_editor_add_feedback_existing, tag) + feedbackText = getString(CommonString.tag_editor_add_feedback_existing, tag) } tags.check(tag) tagsArrayAdapter?.sortData() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/export/ExportDialogFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/export/ExportDialogFragment.kt index 5e1bf59db56d..89148ffe841d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/export/ExportDialogFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/export/ExportDialogFragment.kt @@ -25,6 +25,7 @@ import anki.notes.noteIds import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.browser.IdsFile @@ -93,7 +94,7 @@ class ExportDialogFragment : AnalyticsDialogFragment() { .Builder(requireActivity()) .setTitle(TR.actionsExport()) .setView(binding.root) - .negativeButton(R.string.dialog_cancel) + .negativeButton(CommonString.dialog_cancel) .positiveButton(text = TR.actionsExport()) { val selectedIndex = binding.exportTypeSelector.selectedItemPosition // just to be safe, if not exporting a collection and the decks spinner is not diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/filtered/FilteredDeckOptionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/filtered/FilteredDeckOptionsFragment.kt index c8551ff70dc4..462aac38ae88 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/filtered/FilteredDeckOptionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/filtered/FilteredDeckOptionsFragment.kt @@ -33,6 +33,7 @@ import com.google.android.material.materialswitch.MaterialSwitch import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.destinations.BrowserDestination import com.ichi2.anki.common.destinations.navigate @@ -126,10 +127,10 @@ class FilteredDeckOptionsFragment : Fragment(R.layout.fragment_filtered_deck_opt if (state.throwable != null) { // we can't recover from an initialization error so just exit AlertDialog.Builder(requireActivity()).show { - setTitle(R.string.import_title_error) + setTitle(CommonString.import_title_error) message(text = state.throwable.toString()) cancelable(false) - positiveButton(R.string.dialog_exit) { requireActivity().finish() } + positiveButton(CommonString.dialog_exit) { requireActivity().finish() } } } else { binding.loadingIndicator.isVisible = true @@ -151,9 +152,9 @@ class FilteredDeckOptionsFragment : Fragment(R.layout.fragment_filtered_deck_opt // show any errors we might have if (state.throwable != null) { AlertDialog.Builder(requireActivity()).show { - title(R.string.import_title_error) + title(CommonString.import_title_error) message(text = state.throwable.message) - positiveButton(R.string.dialog_ok) + positiveButton(CommonString.dialog_ok) setOnDismissListener { viewModel.clearError() } } return@collect @@ -187,8 +188,8 @@ class FilteredDeckOptionsFragment : Fragment(R.layout.fragment_filtered_deck_opt binding.deckNameInput.setTextIfChanged(state.name) binding.deckNameInputLayout.error = when (state.nameInputError) { - FilteredNameInputError.Empty -> getString(R.string.empty_cram_label) - FilteredNameInputError.AlreadyExists -> getString(R.string.error_name_exists) + FilteredNameInputError.Empty -> getString(CommonString.empty_cram_label) + FilteredNameInputError.AlreadyExists -> getString(CommonString.error_name_exists) null -> null } binding.checkBoxAllowEmpty.setCheckedIfChanged(state.allowEmpty) @@ -201,7 +202,7 @@ class FilteredDeckOptionsFragment : Fragment(R.layout.fragment_filtered_deck_opt binding.filterLimitInput.setTextIfChanged(filter1State.limit) binding.filterLimitInputLayout.error = when (state.filter1State.error) { - SearchInputError.Empty -> getString(R.string.empty_cram_label) + SearchInputError.Empty -> getString(CommonString.empty_cram_label) SearchInputError.NotANumber -> TR.errorsInvalidInputEmpty() null -> null } @@ -229,7 +230,7 @@ class FilteredDeckOptionsFragment : Fragment(R.layout.fragment_filtered_deck_opt binding.secondFilterLimitInput.setTextIfChanged(filter2State.limit) binding.secondFilterLimitInputLayout.error = when (state.filter2State.error) { - SearchInputError.Empty -> getString(R.string.empty_cram_label) + SearchInputError.Empty -> getString(CommonString.empty_cram_label) SearchInputError.NotANumber -> TR.errorsInvalidInputEmpty() null -> null } @@ -333,9 +334,9 @@ class FilteredDeckOptionsFragment : Fragment(R.layout.fragment_filtered_deck_opt binding.checkBoxAllowEmpty.text = TR.decksCreateEvenIfEmpty() // reschedule options binding.rescheduleInfoLabel.text = TR.decksZeroMinutesHint() - binding.rescheduleDelayAgainLayout.hint = getString(R.string.filtered_option_delay_again) - binding.rescheduleDelayHardLayout.hint = getString(R.string.filtered_option_delay_hard) - binding.rescheduleDelayGoodLayout.hint = getString(R.string.filtered_option_delay_good) + binding.rescheduleDelayAgainLayout.hint = getString(CommonString.filtered_option_delay_again) + binding.rescheduleDelayHardLayout.hint = getString(CommonString.filtered_option_delay_hard) + binding.rescheduleDelayGoodLayout.hint = getString(CommonString.filtered_option_delay_good) } /** Sets text of [TextInputEditText] to [newText] only if it's different from its current text */ diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/EditorChips.kt b/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/EditorChips.kt index f02fa2e0b4ef..f5481bea1dbf 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/EditorChips.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/EditorChips.kt @@ -26,7 +26,7 @@ import com.google.android.material.badge.ExperimentalBadgeUtils import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup import com.ichi2.anki.AnkiActivity -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.dialogs.IntegerDialog import com.ichi2.anki.utils.ext.showDialogFragment @@ -134,8 +134,8 @@ private fun AnkiActivity.displayUpdateClozeNumberDialog( val repositionDialog = IntegerDialog().apply { setArgs( - title = this@displayUpdateClozeNumberDialog.getString(R.string.change_cloze_number), - prompt = this@displayUpdateClozeNumberDialog.getString(R.string.cloze_number), + title = this@displayUpdateClozeNumberDialog.getString(CommonString.change_cloze_number), + prompt = this@displayUpdateClozeNumberDialog.getString(CommonString.cloze_number), digits = 2, defaultValue = clozeNumber.toString(), ) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt index c03e6a187c5c..71970f6b1ca6 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt @@ -27,6 +27,7 @@ import com.google.android.material.textfield.TextInputLayout import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.CustomActionModeCallback import com.ichi2.anki.R import com.ichi2.anki.common.destinations.NoteEditorDestination @@ -385,7 +386,7 @@ class InstantNoteEditorActivity : AnkiActivity(R.layout.activity_instant_note_ed when (result) { is SaveNoteResult.Failure -> { Timber.d("Failed to save note") - savingErrorDialog(result.message ?: getString(R.string.something_wrong)) + savingErrorDialog(result.message ?: getString(CommonString.something_wrong)) } SaveNoteResult.Success -> { @@ -396,7 +397,7 @@ class InstantNoteEditorActivity : AnkiActivity(R.layout.activity_instant_note_ed is SaveNoteResult.Warning -> { Timber.d("Showing warning to the user") - viewModel.setWarningMessage(result.message ?: getString(R.string.something_wrong)) + viewModel.setWarningMessage(result.message ?: getString(CommonString.something_wrong)) } } } @@ -451,12 +452,12 @@ class InstantNoteEditorActivity : AnkiActivity(R.layout.activity_instant_note_ed * AnkiDroid Note Editor **/ private fun noClozeNoteTypesFoundDialog() { AlertDialog.Builder(this).show { - title(R.string.cloze_note_required) - message(R.string.cloze_not_found_message) - positiveButton(R.string.open) { + title(CommonString.cloze_note_required) + message(CommonString.cloze_not_found_message) + positiveButton(CommonString.open) { openNoteEditor() } - negativeButton(R.string.dialog_cancel) { + negativeButton(CommonString.dialog_cancel) { finish() } } @@ -469,7 +470,7 @@ class InstantNoteEditorActivity : AnkiActivity(R.layout.activity_instant_note_ed .onEach { errorMessage -> AlertDialog .Builder(this) - .setTitle(R.string.vague_error) + .setTitle(CommonString.vague_error) .setMessage(errorMessage) .show() }.launchIn(lifecycleScope) @@ -497,10 +498,10 @@ class InstantNoteEditorActivity : AnkiActivity(R.layout.activity_instant_note_ed private fun savingErrorDialog(message: String) { AlertDialog.Builder(this).show { message(text = message) - positiveButton(R.string.try_again) { + positiveButton(CommonString.try_again) { checkAndSave() } - negativeButton(R.string.dialog_cancel) { + negativeButton(CommonString.dialog_cancel) { instantAlertDialog.dismiss() } } @@ -514,14 +515,14 @@ class InstantNoteEditorActivity : AnkiActivity(R.layout.activity_instant_note_ed positiveButton(text = TR.actionsSave()) { saveNoteWithProgress(skipClozeCheck = true) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } private fun saveNoteWithProgress(skipClozeCheck: Boolean) { lifecycleScope.launch { val result = - withProgress(resources.getString(R.string.saving_facts)) { + withProgress(resources.getString(CommonString.saving_facts)) { viewModel.checkAndSaveNote(skipClozeCheck = skipClozeCheck) } handleSaveNoteResult(result) @@ -550,7 +551,7 @@ class InstantNoteEditorActivity : AnkiActivity(R.layout.activity_instant_note_ed CustomActionModeCallback( // we always have cloze type notes here isClozeType = true, - getString(R.string.multimedia_editor_popup_cloze), + getString(CommonString.multimedia_editor_popup_cloze), clozeMenuId, onActionItemSelected = { mode, item -> val itemId = item.itemId diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/jsaddons/TgzPackageExtract.kt b/AnkiDroid/src/main/java/com/ichi2/anki/jsaddons/TgzPackageExtract.kt index b657dabef50e..5f0f14e4535b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/jsaddons/TgzPackageExtract.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/jsaddons/TgzPackageExtract.kt @@ -35,7 +35,7 @@ package com.ichi2.anki.jsaddons import android.content.Context import android.text.format.Formatter -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.compat.CompatHelper.Companion.compat import com.ichi2.utils.FileUtil import org.apache.commons.compress.archivers.ArchiveException @@ -130,13 +130,13 @@ class TgzPackageExtract( tarballFile: File, addonsPackageDir: AddonsPackageDir, ) { - require(isGzip(tarballFile)) { context.getString(R.string.not_valid_js_addon_package, tarballFile.absolutePath) } + require(isGzip(tarballFile)) { context.getString(CommonString.not_valid_js_addon_package, tarballFile.absolutePath) } try { compat.createDirectories(addonsPackageDir) } catch (e: IOException) { Timber.w(e) - throw IOException(context.getString(R.string.could_not_create_dir, addonsPackageDir.absolutePath)) + throw IOException(context.getString(CommonString.could_not_create_dir, addonsPackageDir.absolutePath)) } // Make sure we have 2x the tar file size in free space (1x for tar file, 1x for unarchived tar file contents @@ -259,7 +259,7 @@ class TgzPackageExtract( outputDir.deleteRecursively() throw ArchiveException( context.getString( - R.string.malicious_archive_exceeds_limit, + CommonString.malicious_archive_exceeds_limit, Formatter.formatFileSize(context, TOO_BIG_SIZE), TOO_MANY_FILES, ), @@ -293,7 +293,7 @@ class TgzPackageExtract( } catch (e: IOException) { // clean up Timber.w(e) - throw IOException(context.getString(R.string.could_not_create_dir, parent.absolutePath)) + throw IOException(context.getString(CommonString.could_not_create_dir, parent.absolutePath)) } FileOutputStream(outputFile).use { outputFileStream -> @@ -338,7 +338,7 @@ class TgzPackageExtract( compat.createDirectories(outputFile) } catch (e: IOException) { Timber.w(e) - throw IOException(context.getString(R.string.could_not_create_dir, outputFile.absolutePath)) + throw IOException(context.getString(CommonString.could_not_create_dir, outputFile.absolutePath)) } } @@ -359,19 +359,19 @@ class TgzPackageExtract( destDirectory.canonicalPath } catch (_: IOException) { // Path may contain PII; removing the %s param would churn translations — use a placeholder. - throw ArchiveException(context.getString(R.string.malicious_archive_entry_outside, ARCHIVE_ENTRY_PATH_OMITTED)) + throw ArchiveException(context.getString(CommonString.malicious_archive_entry_outside, ARCHIVE_ENTRY_PATH_OMITTED)) } val childCanonical = try { outputFile.canonicalPath } catch (_: IOException) { - throw ArchiveException(context.getString(R.string.malicious_archive_entry_outside, ARCHIVE_ENTRY_PATH_OMITTED)) + throw ArchiveException(context.getString(CommonString.malicious_archive_entry_outside, ARCHIVE_ENTRY_PATH_OMITTED)) } val destPrefix = destCanonical + File.separator // Allow the destination directory itself (e.g. `./` entries). Reject anything else // that is not dest and not under dest + separator (sibling-prefix safe). if (childCanonical != destCanonical && !childCanonical.startsWith(destPrefix)) { - throw ArchiveException(context.getString(R.string.malicious_archive_entry_outside, ARCHIVE_ENTRY_PATH_OMITTED)) + throw ArchiveException(context.getString(CommonString.malicious_archive_entry_outside, ARCHIVE_ENTRY_PATH_OMITTED)) } } @@ -409,7 +409,7 @@ class TgzPackageExtract( private fun enforceSpaceUsedLessThanHalfAvailable(outputDir: File) { val newAvailableSpace: Long = FileUtil.determineBytesAvailable(outputDir.canonicalPath) if (newAvailableSpace <= availableSpace / 2) { - throw ArchiveException(context.getString(R.string.file_extract_exceeds_storage_space)) + throw ArchiveException(context.getString(CommonString.file_extract_exceeds_storage_space)) } } @@ -452,7 +452,7 @@ class TgzPackageExtract( private const val TOO_BIG_SIZE: Long = 0x6400000 // max size of unzipped data, 100MB private const val TOO_MANY_FILES = 1024 // max number of files - /** Placeholder for [R.string.malicious_archive_entry_outside]; do not pass real archive paths. */ + /** Placeholder for [CommonString.malicious_archive_entry_outside]; do not pass real archive paths. */ private const val ARCHIVE_ENTRY_PATH_OMITTED = "archive entry" } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/mediacheck/MediaCheckFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/mediacheck/MediaCheckFragment.kt index 3186c1294eef..f7aa110c484d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/mediacheck/MediaCheckFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/mediacheck/MediaCheckFragment.kt @@ -21,6 +21,8 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.SingleFragmentActivity import com.ichi2.anki.common.utils.android.getColorFromAttr @@ -149,7 +151,7 @@ class MediaCheckFragment : Fragment(R.layout.fragment_media_check) { launchCatchingTask { viewModel.tagMissing(TR.mediaCheckMissingMediaTag()).join() showResultDialog( - R.string.check_media_tags_added, + CommonString.check_media_tags_added, TR.browsingNotesUpdated(viewModel.taggedFiles), ) } @@ -182,8 +184,8 @@ class MediaCheckFragment : Fragment(R.layout.fragment_media_check) { private fun deleteConfirmationDialog() { AlertDialog.Builder(requireContext()).show { message(text = TR.mediaCheckDeleteUnusedConfirm()) - positiveButton(R.string.dialog_positive_delete) { handleDeleteConfirmation() } - negativeButton(R.string.dialog_cancel) + positiveButton(CommonString.dialog_positive_delete) { handleDeleteConfirmation() } + negativeButton(CommonString.dialog_cancel) } } @@ -203,9 +205,9 @@ class MediaCheckFragment : Fragment(R.layout.fragment_media_check) { */ private fun showDeletionResult() { showResultDialog( - R.string.delete_media_result_title, + CommonString.delete_media_result_title, resources.getQuantityString( - R.plurals.delete_media_result_message, + CommonPlurals.delete_media_result_message, viewModel.deletedFiles, viewModel.deletedFiles, ), @@ -215,7 +217,7 @@ class MediaCheckFragment : Fragment(R.layout.fragment_media_check) { private fun showTrashRestoredDialog() { AlertDialog.Builder(requireContext()).show { message(text = TR.mediaCheckTrashRestored()) - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { requireActivity().finish() } cancelable(false) @@ -225,7 +227,7 @@ class MediaCheckFragment : Fragment(R.layout.fragment_media_check) { private fun showTrashDeletedDialog() { AlertDialog.Builder(requireContext()).show { message(text = TR.mediaCheckTrashEmptied()) - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { requireActivity().finish() } cancelable(false) @@ -239,7 +241,7 @@ class MediaCheckFragment : Fragment(R.layout.fragment_media_check) { AlertDialog.Builder(requireContext()).show { title(titleRes) message(text = message) - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { requireActivity().finish() } cancelable(false) @@ -255,7 +257,7 @@ class MediaCheckFragment : Fragment(R.layout.fragment_media_check) { private val MediaCheckProgress.messageRes: Int get() = when (this) { - MediaCheckProgress.CHECKING_MEDIA -> R.string.check_media_message - MediaCheckProgress.ADDING_TAGS -> R.string.check_media_adding_missing_tag - MediaCheckProgress.DELETING_MEDIA -> R.string.delete_media_message + MediaCheckProgress.CHECKING_MEDIA -> CommonString.check_media_message + MediaCheckProgress.ADDING_TAGS -> CommonString.check_media_adding_missing_tag + MediaCheckProgress.DELETING_MEDIA -> CommonString.delete_media_message } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt index b794c4a76dd3..ce01fd42e5ee 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt @@ -25,6 +25,7 @@ import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.VisibleForTesting import androidx.lifecycle.lifecycleScope +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.databinding.FragmentAudioRecordingBinding @@ -40,7 +41,7 @@ class AudioRecordingFragment : MultimediaFragment(R.layout.fragment_audio_record internal val binding by viewBinding(FragmentAudioRecordingBinding::bind) override val title: String - get() = resources.getString(R.string.multimedia_editor_field_editing_audio) + get() = resources.getString(CommonString.multimedia_editor_field_editing_audio) private var audioRecordingController: AudioRecordingController? = null @@ -49,7 +50,7 @@ class AudioRecordingFragment : MultimediaFragment(R.layout.fragment_audio_record ankiCacheDirectory = FileUtil.getAnkiCacheDirectory(requireContext(), "temp-media") if (ankiCacheDirectory == null) { Timber.e("createUI() failed to get cache directory") - showErrorDialog(errorMessage = resources.getString(R.string.multimedia_editor_failed)) + showErrorDialog(errorMessage = resources.getString(CommonString.multimedia_editor_failed)) return } } @@ -65,7 +66,7 @@ class AudioRecordingFragment : MultimediaFragment(R.layout.fragment_audio_record setupDoneButton() } else { Timber.d("Audio permission denied") - showErrorDialog(resources.getString(R.string.multimedia_editor_audio_permission_refused)) + showErrorDialog(resources.getString(CommonString.multimedia_editor_audio_permission_refused)) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt index 6d012c69ecca..2a1ef4a55d46 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt @@ -33,6 +33,7 @@ import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.common.crashreporting.CrashReportService @@ -144,7 +145,7 @@ class AudioVideoFragment : MultimediaFragment(R.layout.fragment_audio_video) { "audio/*", // #9226: allows ogg on Android 8 arrayOf("audio/*", "application/ogg"), - R.string.multimedia_editor_popup_audio_clip, + CommonString.multimedia_editor_popup_audio_clip, ) } @@ -153,7 +154,7 @@ class AudioVideoFragment : MultimediaFragment(R.layout.fragment_audio_video) { openMediaChooser( "video/*", emptyArray(), - R.string.multimedia_editor_popup_video_clip, + CommonString.multimedia_editor_popup_video_clip, ) } } @@ -408,7 +409,7 @@ class AudioVideoFragment : MultimediaFragment(R.layout.fragment_audio_video) { context: Context, ): String = when (mediaOption) { - AUDIO_CLIP -> context.getString(R.string.multimedia_editor_popup_audio_clip) - VIDEO_CLIP -> context.getString(R.string.multimedia_editor_popup_video_clip) + AUDIO_CLIP -> context.getString(CommonString.multimedia_editor_popup_audio_clip) + VIDEO_CLIP -> context.getString(CommonString.multimedia_editor_popup_video_clip) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt index 159bec820410..2c5d7b1bf7df 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt @@ -26,7 +26,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat @@ -201,7 +201,7 @@ abstract class MultimediaFragment( * "Something went wrong" message from the application's resources. */ fun showSomethingWentWrong() { - showSnackbar(resources.getString(R.string.multimedia_editor_something_wrong)) + showSnackbar(resources.getString(CommonString.multimedia_editor_something_wrong)) } /** @@ -211,8 +211,8 @@ abstract class MultimediaFragment( */ fun showErrorDialog(errorMessage: String? = null) { AlertDialog.Builder(requireContext()).show { - setMessage(errorMessage ?: resources.getString(R.string.something_wrong)) - setPositiveButton(getString(R.string.dialog_ok)) { _, _ -> + setMessage(errorMessage ?: resources.getString(CommonString.something_wrong)) + setPositiveButton(getString(CommonString.dialog_ok)) { _, _ -> requireActivity().finish() } setOnCancelListener { requireActivity().finish() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaImageFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaImageFragment.kt index 15729873606a..a7fc7e17b099 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaImageFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaImageFragment.kt @@ -26,6 +26,7 @@ import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.DrawingFragment import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest @@ -80,7 +81,7 @@ class MultimediaImageFragment : private var previewedImage: Uri? = null override val title: String - get() = resources.getString(R.string.multimedia_editor_popup_image) + get() = resources.getString(CommonString.multimedia_editor_popup_image) private lateinit var selectedImageOptions: ImageOptions @@ -110,7 +111,7 @@ class MultimediaImageFragment : val selectedImage = PickedImage(data).trustedUri if (selectedImage == null) { - showSnackbar(getString(R.string.select_image_failed)) + showSnackbar(getString(CommonString.select_image_failed)) return@registerForActivityResult } handleSelectImageIntent(selectedImage) @@ -249,7 +250,7 @@ class MultimediaImageFragment : ankiCacheDirectory = FileUtil.getAnkiCacheDirectory(requireContext(), "temp-photos") if (ankiCacheDirectory == null) { Timber.e("createUI() failed to get cache directory") - showErrorDialog(errorMessage = resources.getString(R.string.multimedia_editor_failed)) + showErrorDialog(errorMessage = resources.getString(CommonString.multimedia_editor_failed)) return } @@ -349,7 +350,7 @@ class MultimediaImageFragment : pickImageLauncher.launch(intent) } catch (e: ActivityNotFoundException) { Timber.w(e, "MultimediaImageFragment:: No app found to select image") - showSnackbar(R.string.activity_start_failed) + showSnackbar(CommonString.activity_start_failed) } } @@ -380,7 +381,7 @@ class MultimediaImageFragment : cameraLauncher.launch(photoURI) } catch (e: ActivityNotFoundException) { Timber.w(e, "MultimediaImageFragment:: No camera found") - showSnackbar(R.string.activity_start_failed) + showSnackbar(CommonString.activity_start_failed) } } } @@ -431,7 +432,7 @@ class MultimediaImageFragment : viewModel.currentMultimediaUri.value?.let { previewImage(it) } updateAndDisplayImageSize(imageFile) - showCropDialog(getString(R.string.crop_image)) + showCropDialog(getString(CommonString.crop_image)) } private fun updateAndDisplayImageSize(imageUri: String) { @@ -447,24 +448,24 @@ class MultimediaImageFragment : val numberFormat = NumberFormat.getInstance() // length is in bits, other elements have MB, convert to MB val size = numberFormat.format(length / 1000000.0) - val message = getString(R.string.save_dialog_content, size) + val message = getString(CommonString.save_dialog_content, size) showCompressImageDialog(message) } private fun showCompressImageDialog(message: String) { AlertDialog.Builder(requireActivity()).show { message(text = message) - positiveButton(R.string.compress) { + positiveButton(CommonString.compress) { viewModel.currentMultimediaPath.value.let { if (it == null) return@positiveButton if (!rotateAndCompress(it)) { Timber.d("Unable to compress the clicked image") - showErrorDialog(errorMessage = resources.getString(R.string.multimedia_editor_image_compression_failed)) + showErrorDialog(errorMessage = resources.getString(CommonString.multimedia_editor_image_compression_failed)) return@positiveButton } } } - negativeButton(R.string.dialog_no) { + negativeButton(CommonString.dialog_no) { finishAddingImage() } } @@ -478,10 +479,10 @@ class MultimediaImageFragment : AlertDialog.Builder(requireActivity()).show { message(text = message) - positiveButton(R.string.dialog_yes) { + positiveButton(CommonString.dialog_yes) { requestCrop() } - negativeButton(R.string.dialog_no) + negativeButton(CommonString.dialog_no) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/audio/AudioRecordingController.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/audio/AudioRecordingController.kt index 0e65f915ab84..b124a9c29679 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/audio/AudioRecordingController.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/audio/AudioRecordingController.kt @@ -38,6 +38,7 @@ import androidx.lifecycle.lifecycleScope import com.google.android.material.button.MaterialButton import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.progressindicator.LinearProgressIndicator +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.Reviewer import com.ichi2.anki.common.annotations.NeedsTest @@ -162,7 +163,7 @@ class AudioRecordingController( sv.addView(previewLayout) val label = FixedTextView(this) label.textSize = 20f - label.text = UiUtil.makeBold(this.getString(R.string.audio_recording_field_list)) + label.text = UiUtil.makeBold(this.getString(CommonString.audio_recording_field_list)) label.gravity = Gravity.CENTER_HORIZONTAL previewLayout.addView(label) var hasTextContents = false @@ -389,7 +390,7 @@ class AudioRecordingController( iconTint = ContextCompat.getColorStateList(context, R.color.audio_recorder_red) strokeColor = ContextCompat.getColorStateList(context, R.color.audio_recorder_red) setIconResource(R.drawable.ic_record) - contentDescription = context.getString(R.string.start_recording) + contentDescription = context.getString(CommonString.start_recording) } audioWaveform.clear() cancelAudioRecordingButton.isEnabled = false @@ -400,7 +401,7 @@ class AudioRecordingController( iconTint = ContextCompat.getColorStateList(context, R.color.audio_recorder_red) strokeColor = ContextCompat.getColorStateList(context, R.color.audio_recorder_red) setIconResource(R.drawable.ic_stop) - contentDescription = context.getString(R.string.stop_recording) + contentDescription = context.getString(CommonString.stop_recording) } cancelAudioRecordingButton.isEnabled = true audioProgressBar.isVisible = false @@ -410,7 +411,7 @@ class AudioRecordingController( iconTint = ContextCompat.getColorStateList(context, R.color.audio_recorder_grey) strokeColor = ContextCompat.getColorStateList(context, R.color.audio_recorder_grey) setIconResource(R.drawable.ic_skip_next) - contentDescription = context.getString(R.string.next_recording) + contentDescription = context.getString(CommonString.next_recording) } cancelAudioRecordingButton.isEnabled = true audioProgressBar.isVisible = true @@ -421,7 +422,7 @@ class AudioRecordingController( iconTint = ContextCompat.getColorStateList(context, R.color.audio_recorder_grey) strokeColor = ContextCompat.getColorStateList(context, R.color.audio_recorder_grey) setIconResource(R.drawable.ic_play) - contentDescription = context.getString(R.string.play_recording) + contentDescription = context.getString(CommonString.play_recording) } cancelAudioRecordingButton.isEnabled = true audioProgressBar.isVisible = true @@ -533,7 +534,7 @@ class AudioRecordingController( stopAndSaveRecording() // TODO: discuss if we want to keep the snackbar here // show this snackbar only in the edit field/multimedia activity - if (inEditField) (context as Activity).showSnackbar(context.resources.getString(R.string.audio_saved)) + if (inEditField) (context as Activity).showSnackbar(context.resources.getString(CommonString.audio_saved)) prepareAudioPlayer() } @@ -557,7 +558,7 @@ class AudioRecordingController( Timber.w("Audio recording permission denied.") showThemedToast( context, - context.resources.getString(R.string.multimedia_editor_audio_permission_denied), + context.resources.getString(CommonString.multimedia_editor_audio_permission_denied), true, ) return @@ -603,7 +604,7 @@ class AudioRecordingController( audioPlayer!!.start() } catch (e: Exception) { Timber.w(e, "error starting audioPlayer") - showThemedToast(context, context.resources.getString(R.string.multimedia_editor_audio_view_playing_failed), true) + showThemedToast(context, context.resources.getString(CommonString.multimedia_editor_audio_view_playing_failed), true) } audioTimer.start() setUiState(state.play()) @@ -671,7 +672,7 @@ class AudioRecordingController( audioRecorder.stop() } catch (e: RuntimeException) { Timber.i(e, "Recording stop failed, this happens if stop was hit immediately after start") - showThemedToast(context, context.resources.getString(R.string.multimedia_editor_audio_view_recording_failed), true) + showThemedToast(context, context.resources.getString(CommonString.multimedia_editor_audio_view_recording_failed), true) } isRecording = false isAudioRecordingSaved = true diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/FieldState.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/FieldState.kt index 7d97673d0300..a93a6928dfd8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/FieldState.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/FieldState.kt @@ -5,9 +5,9 @@ package com.ichi2.anki.noteeditor import android.content.Context import android.os.Bundle import android.view.View +import com.ichi2.anki.CommonString import com.ichi2.anki.FieldEditLine import com.ichi2.anki.NoteEditorFragment -import com.ichi2.anki.R import com.ichi2.anki.common.utils.annotation.KotlinCleanup import com.ichi2.anki.libanki.Field import com.ichi2.anki.libanki.NotetypeJson @@ -157,7 +157,7 @@ class FieldState private constructor( // Set the new field label text if (allowFieldRemapping(oldFields)) { // Show the content of old field if remapping is enabled - fields[i][0] = String.format(context.resources.getString(R.string.field_remapping), fname, oldFields[j][0]) + fields[i][0] = String.format(context.resources.getString(CommonString.field_remapping), fname, oldFields[j][0]) } else { fields[i][0] = fname } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/NoteEditorMultimediaController.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/NoteEditorMultimediaController.kt index 17f093bacc73..9ee364fcbf1f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/NoteEditorMultimediaController.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/NoteEditorMultimediaController.kt @@ -12,9 +12,9 @@ import androidx.activity.result.ActivityResultLauncher import androidx.annotation.VisibleForTesting import androidx.core.os.BundleCompat import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.MediaRegistration import com.ichi2.anki.NoteEditorFragment -import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat import com.ichi2.anki.exception.MediaSizeLimitExceededException @@ -92,7 +92,7 @@ internal class NoteEditorMultimediaController( if (imageUri == null) { Timber.d("NoteEditor:: Image Uri is null") - fragment.showSnackbar(R.string.something_wrong) + fragment.showSnackbar(CommonString.something_wrong) return } @@ -109,7 +109,7 @@ internal class NoteEditorMultimediaController( val cachedImagePath = copyUriToInternalCache(imageUri) if (cachedImagePath == null) { Timber.w("Failed to cache image") - fragment.showSnackbar(R.string.something_wrong) + fragment.showSnackbar(CommonString.something_wrong) return } val cachedUri = Uri.fromFile(File(fragment.requireContext().cacheDir, cachedImagePath)) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt index 6acbec65c32e..1b8393b1c903 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/Toolbar.kt @@ -37,6 +37,7 @@ import androidx.core.view.children import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import com.ichi2.anki.CommonString import com.ichi2.anki.NoteEditorFragment import com.ichi2.anki.R import com.ichi2.anki.common.android.appContext @@ -302,7 +303,7 @@ class Toolbar : FrameLayout { ) onFormat(formatter) } - title(R.string.menu_font_size) + title(CommonString.menu_font_size) } } @@ -318,7 +319,7 @@ class Toolbar : FrameLayout { val formatter = TextWrapper(prefix = "<$charSequence>", suffix = "") onFormat(formatter) } - title(R.string.insert_heading) + title(CommonString.insert_heading) } } @@ -332,7 +333,7 @@ class Toolbar : FrameLayout { setItems(labels) { _, index -> onFormat(options[index].toTextWrapper()) } - title(R.string.insert_mathjax) + title(CommonString.insert_mathjax) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/AddNewNotesType.kt b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/AddNewNotesType.kt index 4234d45682cb..e982ea66724f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/AddNewNotesType.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/AddNewNotesType.kt @@ -26,7 +26,7 @@ import anki.notetypes.StockNotetype import anki.notetypes.copy import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.databinding.DialogNewNoteTypeBinding import com.ichi2.anki.launchCatchingTask @@ -87,7 +87,7 @@ class AddNewNotesType( paddingTop = 24.dp.toPx(activity), paddingBottom = 0, ) - positiveButton(R.string.dialog_add) { _ -> + positiveButton(CommonString.dialog_add) { _ -> val newName = binding.notetypeNewName.text .toString() @@ -102,7 +102,7 @@ class AddNewNotesType( cloneStandardNotetype(newName, selectedOption) } } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) }.show() dialog.initializeViewsWith(allOptions, currentNames) } @@ -111,8 +111,8 @@ class AddNewNotesType( optionsToDisplay: List, currentNames: List, ) { - val addPrefixStr = context.resources.getString(R.string.model_browser_add_add) - val clonePrefixStr = context.resources.getString(R.string.model_browser_add_clone) + val addPrefixStr = context.resources.getString(CommonString.model_browser_add_add) + val clonePrefixStr = context.resources.getString(CommonString.model_browser_add_clone) binding.notetypeTypeLabel.text = TR.notetypesType() binding.notetypeNewNameLayout.hint = TR.deckConfigNamePrompt() @@ -123,7 +123,7 @@ class AddNewNotesType( binding.notetypeNewNameLayout.error = if (alreadyExists) { - context.getString(R.string.error_name_exists) + context.getString(CommonString.error_name_exists) } else { null } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt index 799ec83de084..86fda6aea89c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt @@ -37,6 +37,8 @@ import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.behavior.HideViewOnScrollBehavior import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.databinding.ActivityManageNoteTypesBinding @@ -125,17 +127,17 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { if (userAcceptsSchemaChange()) { val selection = viewModel.selectedNoteTypes.joinToString { it.name } - getString(R.string.model_delete_multiple_warning, selection) + getString(CommonString.model_delete_multiple_warning, selection) } else { return@launchCatchingTask } AlertDialog.Builder(this@ManageNotetypes).show { - title(R.string.dialog_positive_delete) + title(CommonString.dialog_positive_delete) message(text = deleteMessage) - positiveButton(R.string.dialog_positive_delete) { + positiveButton(CommonString.dialog_positive_delete) { viewModel.deleteSelectedNoteTypes() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } } @@ -147,7 +149,7 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { if (state.message != null) { val snackbarMessage = when (state.message) { - UserMessage.DeletingLastModel -> getString(R.string.toast_last_model) + UserMessage.DeletingLastModel -> getString(CommonString.toast_last_model) } showSnackbar(snackbarMessage) viewModel.clearMessage() @@ -199,7 +201,7 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { } AlertDialog.Builder(this).show { message(text = state.error.source.message) - positiveButton(R.string.close) { viewModel.refreshNoteTypes() } + positiveButton(CommonString.close) { viewModel.refreshNoteTypes() } } viewModel.clearError() return @@ -225,7 +227,7 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { val selectedCount = state.noteTypes.count { it.isSelected } binding.selectedLabel.text = resources.getQuantityString( - R.plurals.note_types_selected, + CommonPlurals.note_types_selected, selectedCount, selectedCount, ) @@ -279,8 +281,8 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { AlertDialog .Builder(this@ManageNotetypes) .show { - title(R.string.rename_model) - positiveButton(R.string.rename) { + title(CommonString.rename_model) + positiveButton(CommonString.rename) { val userInput = (it as AlertDialog) .getInputField() @@ -290,7 +292,7 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { if (userInput.isEmpty()) return@positiveButton viewModel.rename(state.id, userInput) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) setView(R.layout.dialog_generic_text_input) }.input( hint = TR.deckConfigNamePrompt(), @@ -310,7 +312,7 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { dialog.positiveButton.isEnabled = false return@input } else if (isDuplicate && !isUnchanged) { - dialog.getInputTextLayout().error = getString(R.string.error_name_exists) + dialog.getInputTextLayout().error = getString(CommonString.error_name_exists) dialog.positiveButton.isEnabled = false return@input } @@ -329,17 +331,17 @@ class ManageNotetypes : AnkiActivity(R.layout.activity_manage_note_types) { launchCatchingTask { @StringRes val messageResourceId: Int? = if (userAcceptsSchemaChange()) { - R.string.model_delete_warning + CommonString.model_delete_warning } else { return@launchCatchingTask } AlertDialog.Builder(this@ManageNotetypes).show { - title(R.string.model_browser_delete) + title(CommonString.model_browser_delete) message(messageResourceId) - positiveButton(R.string.dialog_positive_delete) { + positiveButton(CommonString.dialog_positive_delete) { viewModel.delete(state.id) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/NoteTypesAdapter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/NoteTypesAdapter.kt index a1c43130f814..12930202013b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/NoteTypesAdapter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/NoteTypesAdapter.kt @@ -23,6 +23,7 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView +import com.ichi2.anki.CommonPlurals import com.ichi2.anki.R import com.ichi2.anki.databinding.ItemManageNoteTypeBinding import com.ichi2.anki.notetype.NoteTypesAdapter.NoteTypeViewHolder @@ -145,7 +146,7 @@ internal class NoteTypesAdapter( binding.noteName.text = state.name binding.noteUseCount.text = context.resources.getQuantityString( - R.plurals.model_browser_of_type, + CommonPlurals.model_browser_of_type, state.useCount, state.useCount, ) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RenameCardTypeDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RenameCardTypeDialog.kt index f61883866b04..98f108240d85 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RenameCardTypeDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RenameCardTypeDialog.kt @@ -5,6 +5,7 @@ package com.ichi2.anki.notetype import android.content.Context import androidx.appcompat.app.AlertDialog import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.utils.ValidationResult import com.ichi2.utils.input @@ -31,9 +32,9 @@ class RenameCardTypeDialog { AlertDialog .Builder(context) .show { - title(R.string.rename_card_type) - positiveButton(R.string.rename) { } - negativeButton(R.string.dialog_cancel) + title(CommonString.rename_card_type) + positiveButton(CommonString.rename) { } + negativeButton(CommonString.dialog_cancel) setView(R.layout.dialog_generic_text_input) }.input( hint = CollectionManager.TR.actionsNewName().removeSuffix(":"), @@ -45,7 +46,7 @@ class RenameCardTypeDialog { when { currentName == name -> ValidationResult.REJECTED !existingNames.contains(name) -> ValidationResult.VALID - else -> ValidationResult.error(context.getString(R.string.error_name_exists)) + else -> ValidationResult.error(context.getString(CommonString.error_name_exists)) } }, callback = { dialog, result -> diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RepositionCardTemplateDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RepositionCardTemplateDialog.kt index 913a271799a5..c4f54bfcb272 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RepositionCardTemplateDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/RepositionCardTemplateDialog.kt @@ -6,6 +6,7 @@ import android.content.Context import android.text.InputType import androidx.appcompat.app.AlertDialog import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.utils.getInputField import com.ichi2.utils.input @@ -26,7 +27,7 @@ class RepositionCardTemplateDialog { AlertDialog .Builder(context) .show { - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { result( displayedDialog!! .getInputField() @@ -35,7 +36,7 @@ class RepositionCardTemplateDialog { .toInt(), ) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) setMessage(CollectionManager.TR.cardTemplatesEnterNewCardPosition1(numberOfTemplates)) setView(R.layout.dialog_generic_text_input) }.input( diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/CongratsPage.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/CongratsPage.kt index 5eacf79ea941..3231227bdd5e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/CongratsPage.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/CongratsPage.kt @@ -20,6 +20,7 @@ import anki.scheduler.UnburyDeckRequest import com.google.android.material.appbar.MaterialToolbar import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.OnErrorListener import com.ichi2.anki.R @@ -85,7 +86,7 @@ class CongratsPage : .onEach { errorMessage -> AlertDialog .Builder(requireContext()) - .setTitle(R.string.vague_error) + .setTitle(CommonString.vague_error) .setMessage(errorMessage) .show() }.launchIn(lifecycleScope) @@ -103,7 +104,7 @@ class CongratsPage : ) AlertDialog.Builder(requireContext()).show { setTitle(TR.studyingWhatWouldYouLikeToUnbury()) - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) listItems( unburyOptions, ) { _, position -> @@ -220,7 +221,7 @@ class CongratsPage : activity.showSnackbar(message) } } else { - activity.showSnackbar(R.string.studyoptions_no_cards_due) + activity.showSnackbar(CommonString.studyoptions_no_cards_due) } } @@ -229,7 +230,7 @@ class CongratsPage : val info = withCol { sched.congratulationsInfo() } val secsUntilNextLearn = info.secsUntilNextLearn if (secsUntilNextLearn >= SECONDS_PER_DAY) { - return activity.getString(R.string.studyoptions_congrats_finished) + return activity.getString(CommonString.studyoptions_congrats_finished) } // https://github.com/ankitects/anki/blob/9b4dd54312de8798a3f2bee07892bb3a488d1f9b/ts/lib/tslib/time.ts#L22 val (unit, amount) = @@ -242,7 +243,7 @@ class CongratsPage : } val nextLearnDue = TR.schedulingNextLearnDue(unit, round(amount).toInt()) - return activity.getString(R.string.studyoptions_congrats_next_due_in, nextLearnDue) + return activity.getString(CommonString.studyoptions_congrats_next_due_in, nextLearnDue) } fun DeckPicker.onDeckCompleted() { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt index 4f926995fa37..001cbc7ed6cf 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt @@ -19,6 +19,7 @@ import anki.deck_config.UpdateDeckConfigsRequest import com.google.android.material.appbar.MaterialToolbar import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.ProgressContext import com.ichi2.anki.R import com.ichi2.anki.SingleFragmentActivity @@ -360,7 +361,7 @@ suspend fun FragmentActivity.updateDeckConfigsRaw(input: ByteArray): ByteArray { extractProgress = { // TODO: Don't use the amount yet, unused as a progress indicator, and // duplicates computeMemory's label - text = this.toProgressText() ?: getString(R.string.dialog_processing) + text = this.toProgressText() ?: getString(CommonString.dialog_processing) }, ) { withContext(Dispatchers.IO) { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt index b9c7f8e1e9ed..5ea668d65697 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt @@ -7,7 +7,7 @@ import android.webkit.JsResult import android.webkit.WebChromeClient import android.webkit.WebView import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.utils.cancelable import com.ichi2.utils.message @@ -27,7 +27,7 @@ open class PageChromeClient : WebChromeClient() { try { AlertDialog.Builder(view.context).show { message?.let { message(text = message) } - positiveButton(R.string.dialog_ok) { result?.confirm() } + positiveButton(CommonString.dialog_ok) { result?.confirm() } setOnCancelListener { result?.cancel() } } } catch (e: IllegalStateException) { @@ -54,8 +54,8 @@ open class PageChromeClient : WebChromeClient() { try { AlertDialog.Builder(view.context).show { message?.let { message(text = message) } - positiveButton(R.string.dialog_ok) { result?.confirm() } - negativeButton(R.string.dialog_cancel) { result?.cancel() } + positiveButton(CommonString.dialog_ok) { result?.confirm() } + negativeButton(CommonString.dialog_cancel) { result?.cancel() } cancelable(false) } } catch (e: WindowManager.BadTokenException) { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/RemoveAccountFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/RemoveAccountFragment.kt index 14c5c7f986a5..dd1f24715d6c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/RemoveAccountFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/RemoveAccountFragment.kt @@ -13,6 +13,7 @@ import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import com.google.android.material.appbar.MaterialToolbar +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.utils.bottomCornerClearance @@ -77,7 +78,7 @@ class RemoveAccountFragment : webViewLayout = view.findViewById(R.id.webview_layout) setupWebView() view.findViewById(R.id.toolbar)?.apply { - title = getString(R.string.remove_account) + title = getString(CommonString.remove_account) setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/Statistics.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/Statistics.kt index bb2b1b3bf5f1..0ccdbc002a96 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/Statistics.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/Statistics.kt @@ -10,6 +10,7 @@ import android.view.View import androidx.core.content.ContextCompat.getSystemService import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.common.time.getTimestamp @@ -71,7 +72,7 @@ class Statistics : PageFragment(R.layout.page_statistics) { private fun exportWebViewContentAsPDF() { if (pendingPrintJob?.isActive == true) { Timber.w("Duplicate print attempted - skipping") - showSnackbar(R.string.already_in_progress) + showSnackbar(CommonString.already_in_progress) return } Timber.i("Saving Stats to PDF") diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AboutFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AboutFragment.kt index 61f8ee0197c8..19cfffca2ad1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AboutFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AboutFragment.kt @@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.BuildConfig import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.destinations.ChangelogDestination import com.ichi2.anki.common.destinations.navigate @@ -67,7 +68,7 @@ class AboutFragment : Fragment(R.layout.fragment_about) { val contributorsLink = getString(R.string.link_contributors) val contributingGuideLink = getString(R.string.link_contribution) binding.contributorsDescription.apply { - text = getString(R.string.about_contributors_description, contributorsLink, contributingGuideLink).parseAsHtml() + text = getString(CommonString.about_contributors_description, contributorsLink, contributingGuideLink).parseAsHtml() movementMethod = LinkMovementMethod.getInstance() } @@ -79,8 +80,8 @@ class AboutFragment : Fragment(R.layout.fragment_about) { binding.licenseDescription.apply { text = ( - getString(R.string.license_description, gplLicenseLink, agplLicenseLink, sourceCodeLink) + "
" + - getString(R.string.other_licenses, dependencyLicenseLink) + getString(CommonString.license_description, gplLicenseLink, agplLicenseLink, sourceCodeLink) + "
" + + getString(CommonString.other_licenses, dependencyLicenseLink) ).parseAsHtml() movementMethod = LinkMovementMethod.getInstance() } @@ -89,7 +90,7 @@ class AboutFragment : Fragment(R.layout.fragment_about) { if (BuildConfig.SHOW_DONATE_LINKS) { val donateLink = getString(R.string.link_opencollective_donate) binding.donateDescription.apply { - text = getString(R.string.donate_description, donateLink).parseAsHtml() + text = getString(CommonString.donate_description, donateLink).parseAsHtml() movementMethod = LinkMovementMethod.getInstance() } } else { @@ -120,7 +121,7 @@ class AboutFragment : Fragment(R.layout.fragment_about) { } requireContext().copyToClipboard( TruncatedString.from(debugInfo), - failureMessageId = R.string.about_ankidroid_error_copy_debug_info, + failureMessageId = CommonString.about_ankidroid_error_copy_debug_info, ) } } @@ -149,11 +150,11 @@ class AboutFragment : Fragment(R.layout.fragment_about) { */ fun showEnableDeveloperOptionsDialog(context: Context) { AlertDialog.Builder(context).show { - setTitle(R.string.dev_options_enabled_pref) + setTitle(CommonString.dev_options_enabled_pref) setIcon(R.drawable.ic_warning) - setMessage(R.string.dev_options_warning) - setPositiveButton(R.string.dialog_ok) { _, _ -> enableDeveloperOptions(context) } - setNegativeButton(R.string.dialog_cancel) { _, _ -> clickCount = 0 } + setMessage(CommonString.dev_options_warning) + setPositiveButton(CommonString.dialog_ok) { _, _ -> enableDeveloperOptions(context) } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> clickCount = 0 } setCancelable(false) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt index bba80e798126..830214ad5b65 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AdvancedSettingsFragment.kt @@ -12,6 +12,7 @@ import androidx.core.app.ActivityCompat import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.MetaDB import com.ichi2.anki.R @@ -49,12 +50,12 @@ class AdvancedSettingsFragment : SettingsFragment() { } AlertDialog.Builder(requireContext()).show { - setTitle(R.string.permission_denied) - setMessage(R.string.microphone_permission_denied_message) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setTitle(CommonString.permission_denied) + setMessage(CommonString.microphone_permission_denied_message) + setPositiveButton(CommonString.dialog_ok) { _, _ -> openAppSettingsScreen() } - setNegativeButton(R.string.dialog_cancel, null) + setNegativeButton(CommonString.dialog_cancel, null) } } @@ -78,9 +79,9 @@ class AdvancedSettingsFragment : SettingsFragment() { // TODO: Request MANAGE_EXTERNAL_STORAGE Timber.e(e, "Could not initialize directory: %s", newPath) AlertDialog.Builder(requireContext()).show { - setTitle(R.string.dialog_collection_path_not_dir) - setPositiveButton(R.string.dialog_ok) { _, _ -> } - setNegativeButton(R.string.reset_custom_buttons) { _, _ -> + setTitle(CommonString.dialog_collection_path_not_dir) + setPositiveButton(CommonString.dialog_ok) { _, _ -> } + setNegativeButton(CommonString.reset_custom_buttons) { _, _ -> value = getDefaultAnkiDroidDirectory(requireContext()).absolutePath } } @@ -94,13 +95,13 @@ class AdvancedSettingsFragment : SettingsFragment() { if (!(isChecked as Boolean)) return@setOnPreferenceChangeListener true AlertDialog.Builder(requireContext()).show { setIcon(R.drawable.ic_warning) - setMessage(R.string.readtext_deprecation_warn) - setNegativeButton(R.string.dialog_cancel) { _, _ -> ttsPref.isChecked = false } - setNeutralButton(R.string.scoped_storage_learn_more) { _, _ -> + setMessage(CommonString.readtext_deprecation_warn) + setNegativeButton(CommonString.dialog_cancel) { _, _ -> ttsPref.isChecked = false } + setNeutralButton(CommonString.scoped_storage_learn_more) { _, _ -> ttsPref.isChecked = false requireContext().openUrl(R.string.link_tts) } - setPositiveButton(R.string.dialog_ok) { _, _ -> } + setPositiveButton(CommonString.dialog_ok) { _, _ -> } setOnCancelListener { ttsPref.isChecked = false } } return@setOnPreferenceChangeListener true @@ -109,15 +110,15 @@ class AdvancedSettingsFragment : SettingsFragment() { // Configure "Reset languages" preference requirePreference(R.string.pref_reset_languages_key).setOnPreferenceClickListener { AlertDialog.Builder(requireContext()).show { - setTitle(R.string.reset_languages) + setTitle(CommonString.reset_languages) setIcon(R.drawable.ic_warning) - setMessage(R.string.reset_languages_question) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setMessage(CommonString.reset_languages_question) + setPositiveButton(CommonString.dialog_ok) { _, _ -> if (MetaDB.resetLanguages(requireContext())) { - showSnackbar(R.string.reset_confirmation) + showSnackbar(CommonString.reset_confirmation) } } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } false } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AppearanceSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AppearanceSettingsFragment.kt index a8aa66aee987..b737c620a86a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AppearanceSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AppearanceSettingsFragment.kt @@ -12,6 +12,7 @@ import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.anki.common.utils.android.systemIsInNightMode @@ -52,7 +53,7 @@ class AppearanceSettingsFragment : SettingsFragment() { backgroundImageResultLauncher.launch("image/*") } catch (ex: ActivityNotFoundException) { Timber.w(ex, "No app found to handle background preference change request") - activity?.showSnackbar(R.string.activity_start_failed) + activity?.showSnackbar(CommonString.activity_start_failed) } true } @@ -103,16 +104,16 @@ class AppearanceSettingsFragment : SettingsFragment() { private fun showRemoveBackgroundImageDialog() { AlertDialog.Builder(requireContext()).show { - title(R.string.remove_background_image) - positiveButton(R.string.dialog_remove) { + title(CommonString.remove_background_image) + positiveButton(CommonString.dialog_remove) { if (BackgroundImage.remove(requireContext())) { - showSnackbar(R.string.background_image_removed) + showSnackbar(CommonString.background_image_removed) updateRemoveBackgroundVisibility() } else { - showSnackbar(R.string.error_deleting_image) + showSnackbar(CommonString.error_deleting_image) } } - negativeButton(R.string.dialog_keep) + negativeButton(CommonString.dialog_keep) } } @@ -176,7 +177,7 @@ class AppearanceSettingsFragment : SettingsFragment() { if (BackgroundImage.shouldBeShown(requireContext())) { showRemoveBackgroundImageDialog() } else { - showSnackbar(R.string.no_image_selected) + showSnackbar(CommonString.no_image_selected) } return@registerForActivityResult } @@ -184,12 +185,12 @@ class AppearanceSettingsFragment : SettingsFragment() { try { when (val sizeResult = BackgroundImage.validateBackgroundImageFileSize(this, selectedImage)) { is FileSizeResult.FileTooLarge -> { - showThemedToast(requireContext(), getString(R.string.image_max_size_allowed, sizeResult.maxMB), false) + showThemedToast(requireContext(), getString(CommonString.image_max_size_allowed, sizeResult.maxMB), false) } is FileSizeResult.UncompressedBitmapTooLarge -> { showThemedToast( requireContext(), - getString(R.string.image_dimensions_too_large, sizeResult.width, sizeResult.height), + getString(CommonString.image_dimensions_too_large, sizeResult.width, sizeResult.height), false, ) } @@ -200,10 +201,10 @@ class AppearanceSettingsFragment : SettingsFragment() { } } catch (e: OutOfMemoryError) { Timber.w(e) - showSnackbar(getString(R.string.error_selecting_image, e.localizedMessage)) + showSnackbar(getString(CommonString.error_selecting_image, e.localizedMessage)) } catch (e: Exception) { Timber.w(e) - showSnackbar(getString(R.string.error_selecting_image, e.localizedMessage)) + showSnackbar(getString(CommonString.error_selecting_image, e.localizedMessage)) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt index 6b2f093feb54..57274fa25d31 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/ControlsSettingsFragment.kt @@ -13,6 +13,7 @@ import androidx.preference.children import com.bytehamster.lib.preferencesearch.SearchPreferenceResult import com.google.android.material.tabs.TabLayout import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.cardviewer.ViewerCommand import com.ichi2.anki.common.annotations.NeedsTest @@ -134,31 +135,31 @@ class ControlsSettingsFragment : it.dialogTitle = preferenceTitle } findPreference(getString(R.string.toggle_whiteboard_command_key))?.let { - it.title = TR.sentenceCase.gestureToggleWhiteboard(getString(R.string.gesture_toggle_whiteboard)) + it.title = TR.sentenceCase.gestureToggleWhiteboard(getString(CommonString.gesture_toggle_whiteboard)) } findPreference(getString(R.string.flag_red_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagRed(getString(R.string.gesture_flag_red)) + it.title = TR.sentenceCase.gestureFlagRed(getString(CommonString.gesture_flag_red)) } findPreference(getString(R.string.flag_orange_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagOrange(getString(R.string.gesture_flag_orange)) + it.title = TR.sentenceCase.gestureFlagOrange(getString(CommonString.gesture_flag_orange)) } findPreference(getString(R.string.flag_green_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagGreen(getString(R.string.gesture_flag_green)) + it.title = TR.sentenceCase.gestureFlagGreen(getString(CommonString.gesture_flag_green)) } findPreference(getString(R.string.flag_blue_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagBlue(getString(R.string.gesture_flag_blue)) + it.title = TR.sentenceCase.gestureFlagBlue(getString(CommonString.gesture_flag_blue)) } findPreference(getString(R.string.flag_pink_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagPink(getString(R.string.gesture_flag_pink)) + it.title = TR.sentenceCase.gestureFlagPink(getString(CommonString.gesture_flag_pink)) } findPreference(getString(R.string.flag_turquoise_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagTurquoise(getString(R.string.gesture_flag_turquoise)) + it.title = TR.sentenceCase.gestureFlagTurquoise(getString(CommonString.gesture_flag_turquoise)) } findPreference(getString(R.string.flag_purple_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagPurple(getString(R.string.gesture_flag_purple)) + it.title = TR.sentenceCase.gestureFlagPurple(getString(CommonString.gesture_flag_purple)) } findPreference(getString(R.string.remove_flag_command_key))?.let { - it.title = TR.sentenceCase.gestureFlagRemove(getString(R.string.gesture_flag_remove)) + it.title = TR.sentenceCase.gestureFlagRemove(getString(CommonString.gesture_flag_remove)) } findPreference(getString(R.string.bury_card_command_key))?.let { it.title = TR.sentenceCase.buryCard @@ -254,13 +255,13 @@ class ControlsSettingsFragment : AlertDialog.Builder(requireContext()).show { setTitle(answerPref.title) setIcon(answerPref.icon) - setMessage(getString(R.string.also_assign_binding_to_show_answer, getString(R.string.show_answer))) + setMessage(getString(CommonString.also_assign_binding_to_show_answer, getString(CommonString.show_answer))) - positiveButton(text = getString(R.string.dialog_assign)) { + positiveButton(text = getString(CommonString.dialog_assign)) { answerPref.addBinding(binding, CardSide.ANSWER) showAnswerPref?.addBinding(binding, CardSide.QUESTION) } - negativeButton(text = getString(R.string.dialog_skip)) { + negativeButton(text = getString(CommonString.dialog_skip)) { answerPref.addBinding(binding, CardSide.ANSWER) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt index 61cd0fcd36b6..529295ece888 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomButtonsSettingsFragment.kt @@ -8,6 +8,7 @@ import androidx.core.content.edit import androidx.preference.ListPreference import androidx.preference.Preference import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.preferences.sharedPrefs import com.ichi2.anki.ui.internationalization.sentenceCase @@ -30,8 +31,8 @@ class CustomButtonsSettingsFragment : SettingsFragment() { resetCustomButtons.onPreferenceClickListener = Preference.OnPreferenceClickListener { AlertDialog.Builder(requireContext()).show { - title(R.string.reset_settings_to_default) - positiveButton(R.string.reset) { + title(CommonString.reset_settings_to_default) + positiveButton(CommonString.reset) { // Reset the settings to default requireContext().sharedPrefs().edit { allKeys().forEach { @@ -43,7 +44,7 @@ class CustomButtonsSettingsFragment : SettingsFragment() { addPreferencesFromResource(preferenceResource) initSubscreen() } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } true } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomSyncServerSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomSyncServerSettingsFragment.kt index 703d64c098fc..4c6c3da7cd1f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomSyncServerSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/CustomSyncServerSettingsFragment.kt @@ -7,6 +7,7 @@ import android.os.Bundle import androidx.appcompat.app.AlertDialog import androidx.core.content.edit import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.SyncPreferences import com.ichi2.anki.snackbar.showSnackbar @@ -36,14 +37,14 @@ class CustomSyncServerSettingsFragment : SettingsFragment() { // Empty string input causes the certificate to be unset in the backend, i.e., no error if (!CollectionManager.updateCustomCertificate(newCert)) { AlertDialog.Builder(requireContext()).show { - setTitle(R.string.dialog_invalid_custom_certificate_title) - setMessage(R.string.dialog_invalid_custom_certificate) - setPositiveButton(R.string.dialog_ok) { _, _ -> } + setTitle(CommonString.dialog_invalid_custom_certificate_title) + setMessage(CommonString.dialog_invalid_custom_certificate) + setPositiveButton(CommonString.dialog_ok) { _, _ -> } } return@setOnPreferenceChangeListener false } - showSnackbar(R.string.dialog_updated_custom_certificate) + showSnackbar(CommonString.dialog_updated_custom_certificate) true } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt index 6471b8472342..e1e0aa2d940f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt @@ -10,6 +10,7 @@ import androidx.preference.SwitchPreferenceCompat import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.BuildConfig import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnkiDroidUsageAnalytics import com.ichi2.anki.common.crashreporting.CrashReportService @@ -97,7 +98,7 @@ class DeveloperOptionsFragment : SettingsFragment() { "This will most likely make it so that you cannot access your collection. " + "It will be very difficult to recover your data.", ) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setPositiveButton(CommonString.dialog_ok) { _, _ -> Timber.w("Setting collection path to /storage/emulated/0/AnkiDroid") AnkiDroidApp.sharedPrefs().edit { putString( @@ -106,7 +107,7 @@ class DeveloperOptionsFragment : SettingsFragment() { ) } } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } false } @@ -134,7 +135,7 @@ class DeveloperOptionsFragment : SettingsFragment() { setPositiveButton("OK") { _, _ -> generateNotes(numberOfNotes) } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } true } @@ -160,7 +161,7 @@ class DeveloperOptionsFragment : SettingsFragment() { setPositiveButton("OK") { _, _ -> generateFiles(sizeOfFiles, numberOfFiles) } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } true } @@ -274,13 +275,13 @@ class DeveloperOptionsFragment : SettingsFragment() { */ private fun showDisableDeveloperOptionsDialog() { AlertDialog.Builder(requireContext()).show { - setTitle(R.string.disable_dev_options) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setTitle(CommonString.disable_dev_options) + setPositiveButton(CommonString.dialog_ok) { _, _ -> Prefs.isDeveloperOptionsEnabled = false parentFragmentManager.popBackStack() ActivityCompat.recreate(requireActivity()) } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt index 01d26b1d4402..4b53b4c28ee1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt @@ -10,6 +10,7 @@ import anki.config.ConfigKey import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.utils.ext.AddingDefaultsMode @@ -56,11 +57,12 @@ class GeneralSettingsFragment : SettingsFragment() { } // Anki card context menu requirePreference(R.string.anki_card_external_context_menu_key).apply { - title = getString(R.string.card_browser_enable_external_context_menu, getString(R.string.context_menu_anki_card_label)) + title = + getString(CommonString.card_browser_enable_external_context_menu, getString(CommonString.context_menu_anki_card_label)) summary = getString( - R.string.card_browser_enable_external_context_menu_summary, - getString(R.string.context_menu_anki_card_label), + CommonString.card_browser_enable_external_context_menu_summary, + getString(CommonString.context_menu_anki_card_label), ) setOnPreferenceChangeListener { newValue -> AnkiCardContextMenu.ensureConsistentStateWithPreferenceStatus(requireContext(), newValue) @@ -68,8 +70,12 @@ class GeneralSettingsFragment : SettingsFragment() { } // Card browser context menu requirePreference(R.string.card_browser_external_context_menu_key).apply { - title = getString(R.string.card_browser_enable_external_context_menu, getString(R.string.card_browser_context_menu)) - summary = getString(R.string.card_browser_enable_external_context_menu_summary, getString(R.string.card_browser_context_menu)) + title = getString(CommonString.card_browser_enable_external_context_menu, getString(CommonString.card_browser_context_menu)) + summary = + getString( + CommonString.card_browser_enable_external_context_menu_summary, + getString(CommonString.card_browser_context_menu), + ) setOnPreferenceChangeListener { newValue -> CardBrowserContextMenu.ensureConsistentStateWithPreferenceStatus(requireContext(), newValue) } @@ -80,7 +86,7 @@ class GeneralSettingsFragment : SettingsFragment() { val sortedLanguages = LanguageUtil.APP_LANGUAGES.toSortedMap(java.lang.String.CASE_INSENSITIVE_ORDER) val systemLocale = getSystemLocale() requirePreference(R.string.pref_language_key).apply { - entries = arrayOf(getStringByLocale(R.string.language_system, systemLocale), *sortedLanguages.keys.toTypedArray()) + entries = arrayOf(getStringByLocale(CommonString.language_system, systemLocale), *sortedLanguages.keys.toTypedArray()) entryValues = arrayOf(LanguageUtil.SYSTEM_LANGUAGE_TAG, *sortedLanguages.values.toTypedArray()) setOnPreferenceChangeListener { selectedLanguage -> LanguageUtil.setDefaultBackendLanguages(selectedLanguage) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/HeaderFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/HeaderFragment.kt index 6afca21c6f42..6b62e880ac0e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/HeaderFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/HeaderFragment.kt @@ -12,6 +12,7 @@ import com.bytehamster.lib.preferencesearch.SearchConfiguration import com.bytehamster.lib.preferencesearch.SearchPreference import com.ichi2.anki.BuildConfig import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.android.AdaptionUtil import com.ichi2.anki.compat.CompatHelper @@ -40,7 +41,7 @@ class HeaderFragment : SettingsFragment() { requirePreference(R.string.pref_sync_screen_key).summary = HeaderPreference.buildHeaderSummary( TR.sentenceCase.ankiWebAccount, - getString(R.string.automatic_sync_choice), + getString(CommonString.automatic_sync_choice), ) requirePreference(R.string.pref_advanced_screen_key).apply { @@ -98,14 +99,14 @@ class HeaderFragment : SettingsFragment() { index(R.xml.preferences_reviewing) index(R.xml.preferences_sync) index(R.xml.preferences_custom_sync_server) - .addBreadcrumb(R.string.pref_cat_sync) + .addBreadcrumb(CommonString.pref_cat_sync) if (Prefs.newReviewRemindersEnabled) { indexItem() .withKey(activity.getString(R.string.pref_review_reminders_screen_key)) - .withTitle(activity.getString(R.string.review_reminders_title)) + .withTitle(activity.getString(CommonString.review_reminders_title)) .withResId(R.xml.preferences_review_reminders) - .withSummary(activity.getString(R.string.notification_pref)) + .withSummary(activity.getString(CommonString.notification_pref)) } else { index(R.xml.preferences_notifications) } @@ -117,11 +118,11 @@ class HeaderFragment : SettingsFragment() { } index(R.xml.preferences_controls) index(R.xml.preferences_reviewer_controls) - .addBreadcrumb(activity.getString(R.string.pref_cat_controls)) - .addBreadcrumb(activity.getString(R.string.pref_controls_reviews_tab)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_controls)) + .addBreadcrumb(activity.getString(CommonString.pref_controls_reviews_tab)) index(R.xml.preferences_previewer_controls) - .addBreadcrumb(activity.getString(R.string.pref_cat_controls)) - .addBreadcrumb(activity.getString(R.string.pref_controls_previews_tab)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_controls)) + .addBreadcrumb(activity.getString(CommonString.pref_controls_previews_tab)) index(R.xml.preferences_accessibility) index(R.xml.preferences_backup_limits) ignorePreference(activity.getString(R.string.pref_backups_help_key)) @@ -129,40 +130,40 @@ class HeaderFragment : SettingsFragment() { .withKey(activity.getString(R.string.reschedule_command_key)) .withTitle(setDuePreferenceTitle) .withResId(R.xml.preferences_controls) - .addBreadcrumb(activity.getString(R.string.pref_cat_controls)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_controls)) .addBreadcrumb(setDuePreferenceTitle) // Some strings can't be indexed from the XML document as they are loaded from the back-end. We add them manually. indexItem() .withKey(activity.getString(R.string.anki_card_external_context_menu_key)) .withTitle( activity.getString( - R.string.card_browser_enable_external_context_menu, - activity.getString(R.string.context_menu_anki_card_label), + CommonString.card_browser_enable_external_context_menu, + activity.getString(CommonString.context_menu_anki_card_label), ), ).withSummary( activity.getString( - R.string.card_browser_enable_external_context_menu_summary, - activity.getString(R.string.context_menu_anki_card_label), + CommonString.card_browser_enable_external_context_menu_summary, + activity.getString(CommonString.context_menu_anki_card_label), ), ).withResId(R.xml.preferences_general) - .addBreadcrumb(activity.getString(R.string.pref_cat_general)) - .addBreadcrumb(activity.getString(R.string.pref_cat_system_wide)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_general)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_system_wide)) indexItem() .withKey(activity.getString(R.string.card_browser_external_context_menu_key)) .withTitle( activity.getString( - R.string.card_browser_enable_external_context_menu, - activity.getString(R.string.card_browser_context_menu), + CommonString.card_browser_enable_external_context_menu, + activity.getString(CommonString.card_browser_context_menu), ), ).withSummary( activity.getString( - R.string.card_browser_enable_external_context_menu_summary, - activity.getString(R.string.card_browser_context_menu), + CommonString.card_browser_enable_external_context_menu_summary, + activity.getString(CommonString.card_browser_context_menu), ), ).withResId(R.xml.preferences_general) - .addBreadcrumb(activity.getString(R.string.pref_cat_general)) - .addBreadcrumb(activity.getString(R.string.pref_cat_system_wide)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_general)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_system_wide)) if (!Prefs.isNewStudyScreenEnabled) { indexItem() @@ -171,17 +172,17 @@ class HeaderFragment : SettingsFragment() { TR.preferencesShowPlayButtonsOnCardsWith(), ).withResId(R.xml.preferences_appearance) .addBreadcrumb(TR.preferencesAppearance()) - .addBreadcrumb(activity.getString(R.string.pref_cat_reviewer)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_reviewer)) } indexItem() .withKey(activity.getString(R.string.one_way_sync_key)) .withTitle( - activity.getString(R.string.one_way_sync_title), + activity.getString(CommonString.one_way_sync_title), ).withSummary(TR.preferencesOnNextSyncForceChangesIn()) .withResId(R.xml.preferences_sync) - .addBreadcrumb(activity.getString(R.string.pref_cat_sync)) - .addBreadcrumb(activity.getString(R.string.pref_cat_advanced)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_sync)) + .addBreadcrumb(activity.getString(CommonString.pref_cat_advanced)) } // Some preferences and categories are only shown conditionally, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt index b003c02fce4c..064b17805c67 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt @@ -8,6 +8,7 @@ import androidx.preference.Preference import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.account.AccountActivity import com.ichi2.anki.common.crashreporting.runCatchingWithReport @@ -43,7 +44,7 @@ class SyncSettingsFragment : SettingsFragment() { Preference.SummaryProvider { runCatchingWithReport("network_timeout") { TR.qtMiscSecond(this.getValue()) - }.getOrNull() ?: getString(R.string.pref__etc__summary__error) + }.getOrNull() ?: getString(CommonString.pref__etc__summary__error) } } @@ -52,15 +53,15 @@ class SyncSettingsFragment : SettingsFragment() { setSummary(TR.preferencesOnNextSyncForceChangesIn()) setOnPreferenceClickListener { AlertDialog.Builder(requireContext()).show { - setTitle(R.string.one_way_sync_title) + setTitle(CommonString.one_way_sync_title) setMessage(TR.preferencesOnNextSyncForceChangesIn()) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setPositiveButton(CommonString.dialog_ok) { _, _ -> launchCatchingTask { withCol { modSchema(check = false) } - showSnackbar(R.string.one_way_sync_confirmation, Snackbar.LENGTH_SHORT) + showSnackbar(CommonString.one_way_sync_confirmation, Snackbar.LENGTH_SHORT) } } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } false } @@ -77,14 +78,14 @@ class SyncSettingsFragment : SettingsFragment() { // Custom sync server requirePreference(R.string.custom_sync_server_key).setSummaryProvider { - customSyncBase() ?: getString(R.string.custom_sync_server_summary_none_of_the_two_servers_used) + customSyncBase() ?: getString(CommonString.custom_sync_server_summary_none_of_the_two_servers_used) } } private fun updateSyncAccountSummary() { requirePreference(R.string.sync_account_key) .summary = - Prefs.username.ifNullOrEmpty { getString(R.string.sync_account_summ_logged_out) } + Prefs.username.ifNullOrEmpty { getString(CommonString.sync_account_summ_logged_out) } } private fun updateOneWaySyncEnabledState() { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/AddProfileDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/AddProfileDialog.kt index d068e7936b6a..e1b2270088d1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/AddProfileDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/AddProfileDialog.kt @@ -6,7 +6,7 @@ package com.ichi2.anki.preferences.profiles import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.multiprofile.ProfileName import com.ichi2.anki.multiprofile.ProfileName.ValidationResult import com.ichi2.compose.theme.AnkiDroidTheme @@ -24,13 +24,13 @@ fun AddProfileDialog( onDismissRequest: () -> Unit, onConfirm: (ProfileName) -> Unit, ) { - val emptyError = stringResource(R.string.profile_name_empty_error) - val tooLongError = stringResource(R.string.profile_name_too_long_error, ProfileName.MAX_LENGTH) + val emptyError = stringResource(CommonString.profile_name_empty_error) + val tooLongError = stringResource(CommonString.profile_name_too_long_error, ProfileName.MAX_LENGTH) TextInputDialog( - title = stringResource(R.string.add_profile), - label = stringResource(R.string.profile_name), - confirmText = stringResource(R.string.dialog_add), + title = stringResource(CommonString.add_profile), + label = stringResource(CommonString.profile_name), + confirmText = stringResource(CommonString.dialog_add), onConfirm = { text -> (ProfileName.validate(text) as? ValidationResult.Valid)?.let { onConfirm(it.name) } }, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/SwitchProfilesScreen.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/SwitchProfilesScreen.kt index 08aa9142e5fa..9df62ce27221 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/SwitchProfilesScreen.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/profiles/SwitchProfilesScreen.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.multiprofile.ProfileId import com.ichi2.anki.multiprofile.ProfileName @@ -59,7 +60,7 @@ fun SwitchProfilesScreen( modifier = modifier, topBar = { TopAppBar( - title = { Text(stringResource(R.string.switch_profile)) }, + title = { Text(stringResource(CommonString.switch_profile)) }, navigationIcon = { IconButton(onClick = onNavigateUp) { Icon( @@ -76,7 +77,7 @@ fun SwitchProfilesScreen( icon = { Icon(painterResource(R.drawable.ic_switch_profile), contentDescription = null) }, - text = { Text(stringResource(R.string.add_profile)) }, + text = { Text(stringResource(CommonString.add_profile)) }, ) }, ) { contentPadding -> @@ -152,13 +153,13 @@ private fun ProfileRow( IconButton(onClick = onEditClick) { Icon( painterResource(R.drawable.ic_popup_menu_item_editor), - contentDescription = stringResource(R.string.edit_profile), + contentDescription = stringResource(CommonString.edit_profile), ) } IconButton(onClick = onDeleteClick) { Icon( painterResource(R.drawable.ic_delete), - contentDescription = stringResource(R.string.delete_profile), + contentDescription = stringResource(CommonString.delete_profile), ) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/MenuDisplayType.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/MenuDisplayType.kt index 4de9f64cebe4..c986d0b87ef4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/MenuDisplayType.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/MenuDisplayType.kt @@ -5,19 +5,19 @@ package com.ichi2.anki.preferences.reviewer import android.view.MenuItem import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting -import com.ichi2.anki.R +import com.ichi2.anki.CommonString enum class MenuDisplayType( @StringRes val title: Int, ) { /** Shows the action as [MenuItem.SHOW_AS_ACTION_ALWAYS] */ - ALWAYS(R.string.custom_buttons_setting_always_show), + ALWAYS(CommonString.custom_buttons_setting_always_show), /** Shows the action as [MenuItem.SHOW_AS_ACTION_NEVER] */ - MENU_ONLY(R.string.custom_buttons_setting_menu_only), + MENU_ONLY(CommonString.custom_buttons_setting_menu_only), /** Action isn't added to the menu */ - DISABLED(R.string.disabled), + DISABLED(CommonString.disabled), ; @VisibleForTesting diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/ViewerAction.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/ViewerAction.kt index a8debcc48aca..0a7935847bbd 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/ViewerAction.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/ViewerAction.kt @@ -8,6 +8,7 @@ import android.view.KeyEvent import androidx.annotation.DrawableRes import androidx.annotation.IdRes import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.Flag import com.ichi2.anki.R import com.ichi2.anki.preferences.reviewer.MenuDisplayType.ALWAYS @@ -250,32 +251,32 @@ enum class ViewerAction( STATISTICS -> TR.statisticsTitle() RESCHEDULE_NOTE -> TR.sentenceCase.setDueDate PREVIOUS_CARD_INFO -> TR.sentenceCase.previousCardInfo - UNDO -> getString(R.string.undo) - REDO -> getString(R.string.redo) + UNDO -> getString(CommonString.undo) + REDO -> getString(CommonString.redo) FLAG_MENU -> TR.browsingFlag() MARK -> TR.sentenceCase.markNote - EDIT -> getString(R.string.cardeditor_title_edit_card) + EDIT -> getString(CommonString.cardeditor_title_edit_card) BURY_MENU -> TR.studyingBury() SUSPEND_MENU -> TR.studyingSuspend() DELETE -> TR.sentenceCase.deleteNote - TOGGLE_WHITEBOARD -> getString(R.string.gesture_toggle_whiteboard) + TOGGLE_WHITEBOARD -> getString(CommonString.gesture_toggle_whiteboard) DECK_OPTIONS -> TR.sentenceCase.deckOptions CARD_INFO -> TR.sentenceCase.cardInfo - ADD_NOTE -> getString(R.string.menu_add_note) - TAG -> getString(R.string.menu_edit_tags) - RESET_PROGRESS -> getString(R.string.card_editor_reset_card) - TOGGLE_AUTO_ADVANCE -> getString(R.string.toggle_auto_advance) - RECORD_VOICE -> getString(R.string.record_voice) - PLAY_MEDIA -> getString(R.string.replay_media) - USER_ACTION_1 -> getString(R.string.user_action_1) - USER_ACTION_2 -> getString(R.string.user_action_2) - USER_ACTION_3 -> getString(R.string.user_action_3) - USER_ACTION_4 -> getString(R.string.user_action_4) - USER_ACTION_5 -> getString(R.string.user_action_5) - USER_ACTION_6 -> getString(R.string.user_action_6) - USER_ACTION_7 -> getString(R.string.user_action_7) - USER_ACTION_8 -> getString(R.string.user_action_8) - USER_ACTION_9 -> getString(R.string.user_action_9) + ADD_NOTE -> getString(CommonString.menu_add_note) + TAG -> getString(CommonString.menu_edit_tags) + RESET_PROGRESS -> getString(CommonString.card_editor_reset_card) + TOGGLE_AUTO_ADVANCE -> getString(CommonString.toggle_auto_advance) + RECORD_VOICE -> getString(CommonString.record_voice) + PLAY_MEDIA -> getString(CommonString.replay_media) + USER_ACTION_1 -> getString(CommonString.user_action_1) + USER_ACTION_2 -> getString(CommonString.user_action_2) + USER_ACTION_3 -> getString(CommonString.user_action_3) + USER_ACTION_4 -> getString(CommonString.user_action_4) + USER_ACTION_5 -> getString(CommonString.user_action_5) + USER_ACTION_6 -> getString(CommonString.user_action_6) + USER_ACTION_7 -> getString(CommonString.user_action_7) + USER_ACTION_8 -> getString(CommonString.user_action_8) + USER_ACTION_9 -> getString(CommonString.user_action_9) BURY_NOTE -> TR.sentenceCase.buryNote BURY_CARD -> TR.sentenceCase.buryCard SUSPEND_NOTE -> TR.sentenceCase.suspendNote @@ -306,7 +307,7 @@ enum class ViewerAction( PAGE_UP, PAGE_DOWN, EXIT, - -> getString(R.string.empty_string) + -> getString(CommonString.empty_string) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt index 6408902ae7bb..f9192b41de47 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt @@ -26,6 +26,7 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.fragment.app.Fragment import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.ViewerResourceHandler import com.ichi2.anki.compat.CompatHelper.Companion.resolveActivityCompat @@ -96,10 +97,10 @@ abstract class CardViewerFragment( if (ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), Manifest.permission.RECORD_AUDIO)) return AlertDialog.Builder(requireContext()).show { - title(R.string.permission_denied) - message(R.string.microphone_permission_denied_message) - positiveButton(R.string.dialog_ok) { openAppSettingsScreen() } - negativeButton(R.string.dialog_cancel) + title(CommonString.permission_denied) + message(CommonString.microphone_permission_denied_message) + positiveButton(CommonString.dialog_ok) { openAppSettingsScreen() } + negativeButton(CommonString.dialog_cancel) } } @@ -124,8 +125,8 @@ abstract class CardViewerFragment( activeRequest = request optInDialog = AlertDialog.Builder(requireContext()).show { - message(R.string.template_is_trying_to_record_audio) - positiveButton(R.string.dialog_allow) { + message(CommonString.template_is_trying_to_record_audio) + positiveButton(CommonString.dialog_allow) { if (canRecordAudio) { activeRequest = null Prefs.allowTemplatesToRecordAudio = true @@ -135,7 +136,7 @@ abstract class CardViewerFragment( microphonePermissionLauncher.launch(Manifest.permission.RECORD_AUDIO) } } - negativeButton(R.string.dialog_cancel) { decline() } + negativeButton(CommonString.dialog_cancel) { decline() } setOnCancelListener { decline() } setOnDismissListener { optInDialog = null } } @@ -215,7 +216,7 @@ abstract class CardViewerFragment( .onEach { errorMessage -> AlertDialog .Builder(requireContext()) - .setTitle(R.string.vague_error) + .setTitle(CommonString.vague_error) .setMessage(errorMessage) .show() }.launchIn(lifecycleScope) @@ -293,11 +294,11 @@ abstract class CardViewerFragment( "intent" -> handleIntentUrl(url, Intent.URI_INTENT_SCHEME) "missing-user-action" -> { val actionNumber = url.toString().substringAfter(":") - val message = getString(R.string.missing_user_action_dialog_message, actionNumber) + val message = getString(CommonString.missing_user_action_dialog_message, actionNumber) AlertDialog.Builder(requireContext()).show { setMessage(message) - setPositiveButton(R.string.dialog_ok) { _, _ -> } - setNeutralButton(R.string.help) { _, _ -> + setPositiveButton(CommonString.dialog_ok) { _, _ -> } + setNeutralButton(CommonString.help) { _, _ -> openUrl(R.string.link_user_actions_help) } } @@ -437,8 +438,8 @@ abstract class CardViewerFragment( } private fun showMediaErrorSnackbar(filename: String) { - showSnackbar(getString(R.string.card_viewer_could_not_find_image, filename)) { - setAction(R.string.help) { openUrl(R.string.link_faq_missing_media) } + showSnackbar(getString(CommonString.card_viewer_could_not_find_image, filename)) { + setAction(CommonString.help) { openUrl(R.string.link_faq_missing_media) } } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt index a4ba135229f4..099d8fcdec13 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt @@ -19,6 +19,7 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.google.android.material.slider.Slider import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.DispatchKeyEventListener import com.ichi2.anki.Flag import com.ichi2.anki.R @@ -78,7 +79,7 @@ class PreviewerFragment : val displayIndex = currentIndex + 1 binding.slider.value = displayIndex.toFloat() binding.progressIndicator.text = - getString(R.string.preview_progress_bar_text, displayIndex, cardsCount) + getString(CommonString.preview_progress_bar_text, displayIndex, cardsCount) } } // ************************************* Menu items ************************************* @@ -100,7 +101,7 @@ class PreviewerFragment : with(menu.findItem(R.id.action_mark)) { if (isMarked) { setIcon(R.drawable.ic_star) - setTitle(R.string.menu_unmark_note) + setTitle(CommonString.menu_unmark_note) } else { setIcon(R.drawable.ic_star_border_white) title = TR.sentenceCase.markNote @@ -249,10 +250,10 @@ class PreviewerFragment : menu.findItem(R.id.action_back_side_only).apply { if (isBackSideOnly) { setIcon(R.drawable.ic_card_answer) - setTitle(R.string.card_side_answer) + setTitle(CommonString.card_side_answer) } else { setIconRes(requireContext(), R.drawable.ic_card_question) - setTitle(R.string.card_side_both) + setTitle(CommonString.card_side_both) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt index ec2020849cd7..32a727270cea 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.FragmentTemplatePreviewerBinding import com.ichi2.anki.libanki.CardOrdinal @@ -52,9 +53,9 @@ class TemplatePreviewerFragment : .onEach { showingAnswer -> binding.showAnswer.text = if (showingAnswer) { - getString(R.string.hide_answer) + getString(CommonString.hide_answer) } else { - getString(R.string.show_answer) + getString(CommonString.show_answer) } }.launchIn(lifecycleScope) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt index 9d222b097b32..4f53ef11af03 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt @@ -10,6 +10,7 @@ import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment import androidx.fragment.app.commitNow import androidx.lifecycle.lifecycleScope +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.FragmentTemplatePreviewerContainerBinding import com.ichi2.anki.previewer.TemplatePreviewerFragment.Companion.ARG_KEY @@ -51,7 +52,7 @@ class TemplatePreviewerPage : Fragment(R.layout.fragment_template_previewer_cont for ((index, templateName) in viewModel.getTemplateNames().withIndex()) { val tabTitle = if (cardsWithEmptyFronts?.get(index) == true) { - getString(R.string.card_previewer_empty_front_indicator, templateName) + getString(CommonString.card_previewer_empty_front_indicator, templateName) } else { templateName } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/receiver/CopyToClipboardReceiver.kt b/AnkiDroid/src/main/java/com/ichi2/anki/receiver/CopyToClipboardReceiver.kt index 94e887b3bbe4..082f249bc146 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/receiver/CopyToClipboardReceiver.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/receiver/CopyToClipboardReceiver.kt @@ -6,7 +6,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import androidx.core.app.NotificationManagerCompat -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.anki.notifications.NotificationId import com.ichi2.utils.TruncatedString @@ -26,7 +26,7 @@ class CopyToClipboardReceiver : BroadcastReceiver() { val text = intent.getStringExtra(EXTRA_SYNC_ERROR_LOG) ?: run { Timber.w("CopyToClipboardReceiver: no error log found") - showThemedToast(context, R.string.something_wrong, shortLength = true) + showThemedToast(context, CommonString.something_wrong, shortLength = true) return } // only dismiss the notification once the text is safely on the clipboard diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/ReviewerBinding.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/ReviewerBinding.kt index 4c9043f39d30..9baca34f25e9 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/ReviewerBinding.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/ReviewerBinding.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.reviewer import android.content.Context import android.content.SharedPreferences import androidx.annotation.CheckResult -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.cardviewer.Gesture import com.ichi2.anki.cardviewer.ViewerCommand import java.util.Objects @@ -48,8 +48,8 @@ class ReviewerBinding( override fun toDisplayString(context: Context): String { val bindingString = binding.toDisplayString(context) return when (side) { - CardSide.QUESTION -> context.getString(R.string.display_binding_card_side_question, bindingString) - CardSide.ANSWER -> context.getString(R.string.display_binding_card_side_answer, bindingString) + CardSide.QUESTION -> context.getString(CommonString.display_binding_card_side_question, bindingString) + CardSide.ANSWER -> context.getString(CommonString.display_binding_card_side_answer, bindingString) CardSide.BOTH -> bindingString } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/AddEditReminderDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/AddEditReminderDialog.kt index 4f6fd234cf1d..265d8c6aa04c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/AddEditReminderDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/AddEditReminderDialog.kt @@ -19,6 +19,7 @@ import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.TimeFormat import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.ALL_DECKS_ID import com.ichi2.anki.databinding.DialogAddEditReminderBinding @@ -94,11 +95,11 @@ class AddEditReminderDialog : DialogFragment() { val dialogBuilder = AlertDialog.Builder(requireActivity()).apply { customView(binding.root) - positiveButton(R.string.dialog_ok) - neutralButton(R.string.dialog_cancel) + positiveButton(CommonString.dialog_ok) + neutralButton(CommonString.dialog_cancel) if (dialogMode is DialogMode.Edit) { - negativeButton(R.string.dialog_positive_delete) + negativeButton(CommonString.dialog_positive_delete) } } val dialog = dialogBuilder.create() @@ -154,8 +155,8 @@ class AddEditReminderDialog : DialogFragment() { binding.addEditReminderToolbar.title = getString( when (dialogMode) { - is DialogMode.Add -> R.string.add_review_reminder - is DialogMode.Edit -> R.string.edit_review_reminder + is DialogMode.Add -> CommonString.add_review_reminder + is DialogMode.Edit -> CommonString.edit_review_reminder }, ) } @@ -236,8 +237,8 @@ class AddEditReminderDialog : DialogFragment() { val value: Int? = text.toString().toIntOrNull() binding.addEditReminderCardThresholdInputWrapper.error = when { - (value == null) -> getString(R.string.add_edit_reminder_card_threshold_error_not_a_number) - (value < 0) -> getString(R.string.add_edit_reminder_card_threshold_error_negative) + (value == null) -> getString(CommonString.add_edit_reminder_card_threshold_error_not_a_number) + (value < 0) -> getString(CommonString.add_edit_reminder_card_threshold_error_negative) else -> null } viewModel.setCardTriggerThreshold(value ?: 0) @@ -289,7 +290,7 @@ class AddEditReminderDialog : DialogFragment() { Timber.i("Submitted dialog") // Do nothing if numerical fields are invalid binding.addEditReminderCardThresholdInputWrapper.error?.let { - binding.root.showSnackbar(R.string.something_wrong) + binding.root.showSnackbar(CommonString.something_wrong) return } @@ -318,8 +319,8 @@ class AddEditReminderDialog : DialogFragment() { val confirmationDialog = ConfirmationDialog() confirmationDialog.setArgs( - getString(R.string.add_edit_reminder_delete_confirmation_title), - getString(R.string.add_edit_reminder_delete_confirmation_message), + getString(CommonString.add_edit_reminder_delete_confirmation_title), + getString(CommonString.add_edit_reminder_delete_confirmation_message), ) confirmationDialog.setConfirm { setFragmentResult( diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingFragment.kt index 94873628523e..3db774bd2118 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingFragment.kt @@ -32,6 +32,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.NotificationChannel import com.ichi2.anki.R import com.ichi2.anki.common.utils.android.getColorFromAttr @@ -163,21 +164,21 @@ class ReminderTroubleshootingFragment : Fragment(R.layout.fragment_reminder_trou Triple( R.drawable.ic_cancel_24, context.getColor(android.R.color.holo_red_dark), - context.getString(R.string.reminder_troubleshooting_summary_error), + context.getString(CommonString.reminder_troubleshooting_summary_error), ) SummaryStatus.Warning -> Triple( R.drawable.ic_warning_24, getColorFromAttr(context, R.attr.reminderTroubleshootingWarning), - context.getString(R.string.reminder_troubleshooting_summary_warning), + context.getString(CommonString.reminder_troubleshooting_summary_warning), ) SummaryStatus.Ok -> Triple( R.drawable.ic_check_circle_24, getColorFromAttr(context, R.attr.reminderTroubleshootingOk), - context.getString(R.string.reminder_troubleshooting_summary_ok), + context.getString(CommonString.reminder_troubleshooting_summary_ok), ) } binding.summaryIcon.setImageResource(iconRes) @@ -215,7 +216,7 @@ class ReminderTroubleshootingFragment : Fragment(R.layout.fragment_reminder_trou val debugInfo = ReminderLogTree.readReminderLog() + "\n\n" + ReviewRemindersDatabase.dumpContentsToString() requireContext().copyToClipboard( TruncatedString.from(debugInfo), - failureMessageId = R.string.about_ankidroid_error_copy_debug_info, + failureMessageId = CommonString.about_ankidroid_error_copy_debug_info, ) } } @@ -351,12 +352,12 @@ context(context: Context) private fun TroubleshootingCheck.title(): String = context.getString( when (this) { - is TroubleshootingCheck.NotificationPermission -> R.string.reminder_troubleshooting_check_notification_permission - is TroubleshootingCheck.NotificationChannelEnabled -> R.string.reminder_troubleshooting_check_notification_channel - is TroubleshootingCheck.DoNotDisturbOff -> R.string.reminder_troubleshooting_check_do_not_disturb - is TroubleshootingCheck.UnrestrictedOptimizationEnabled -> R.string.reminder_troubleshooting_check_battery_optimization - is TroubleshootingCheck.PowerSavingModeOff -> R.string.reminder_troubleshooting_check_power_saving_mode - is TroubleshootingCheck.ExactAlarmPermission -> R.string.reminder_troubleshooting_check_exact_alarm_permission + is TroubleshootingCheck.NotificationPermission -> CommonString.reminder_troubleshooting_check_notification_permission + is TroubleshootingCheck.NotificationChannelEnabled -> CommonString.reminder_troubleshooting_check_notification_channel + is TroubleshootingCheck.DoNotDisturbOff -> CommonString.reminder_troubleshooting_check_do_not_disturb + is TroubleshootingCheck.UnrestrictedOptimizationEnabled -> CommonString.reminder_troubleshooting_check_battery_optimization + is TroubleshootingCheck.PowerSavingModeOff -> CommonString.reminder_troubleshooting_check_power_saving_mode + is TroubleshootingCheck.ExactAlarmPermission -> CommonString.reminder_troubleshooting_check_exact_alarm_permission }, ) @@ -369,24 +370,24 @@ private fun TroubleshootingCheck.statusName(): String? { is TroubleshootingCheck.ExactAlarmPermission, -> if (passed) { - R.string.reminder_troubleshooting_status_granted + CommonString.reminder_troubleshooting_status_granted } else { - R.string.reminder_troubleshooting_status_denied + CommonString.reminder_troubleshooting_status_denied } - is TroubleshootingCheck.NotificationChannelEnabled -> if (passed) R.string.enabled else R.string.disabled + is TroubleshootingCheck.NotificationChannelEnabled -> if (passed) CommonString.enabled else CommonString.disabled is TroubleshootingCheck.DoNotDisturbOff, is TroubleshootingCheck.PowerSavingModeOff, -> if (passed) { - R.string.reminder_troubleshooting_status_off + CommonString.reminder_troubleshooting_status_off } else { - R.string.reminder_troubleshooting_status_on + CommonString.reminder_troubleshooting_status_on } is TroubleshootingCheck.UnrestrictedOptimizationEnabled -> when (result) { - is CheckResult.Passed -> R.string.reminder_troubleshooting_status_unrestricted - is CheckResult.Warning -> R.string.reminder_troubleshooting_status_optimized - is CheckResult.Failed -> R.string.reminder_troubleshooting_status_restricted + is CheckResult.Passed -> CommonString.reminder_troubleshooting_status_unrestricted + is CheckResult.Warning -> CommonString.reminder_troubleshooting_status_optimized + is CheckResult.Failed -> CommonString.reminder_troubleshooting_status_restricted else -> null } } @@ -400,19 +401,19 @@ private fun TroubleshootingCheck.explanation(): String? { // no need for an explanation: the 'grant permission' action should be sufficient is TroubleshootingCheck.NotificationPermission -> null is TroubleshootingCheck.NotificationChannelEnabled -> - if (result.hasIssue) R.string.reminder_troubleshooting_explanation_notification_channel else null + if (result.hasIssue) CommonString.reminder_troubleshooting_explanation_notification_channel else null is TroubleshootingCheck.DoNotDisturbOff -> - if (result.hasIssue) R.string.reminder_troubleshooting_explanation_do_not_disturb else null + if (result.hasIssue) CommonString.reminder_troubleshooting_explanation_do_not_disturb else null is TroubleshootingCheck.UnrestrictedOptimizationEnabled -> when (result) { - is CheckResult.Warning -> R.string.reminder_troubleshooting_explanation_battery_optimized - is CheckResult.Failed -> R.string.reminder_troubleshooting_explanation_battery_restricted + is CheckResult.Warning -> CommonString.reminder_troubleshooting_explanation_battery_optimized + is CheckResult.Failed -> CommonString.reminder_troubleshooting_explanation_battery_restricted else -> null } is TroubleshootingCheck.PowerSavingModeOff -> - if (result.hasIssue) R.string.reminder_troubleshooting_explanation_power_saving_mode else null + if (result.hasIssue) CommonString.reminder_troubleshooting_explanation_power_saving_mode else null is TroubleshootingCheck.ExactAlarmPermission -> - if (result.hasIssue) R.string.reminder_troubleshooting_explanation_exact_alarm else null + if (result.hasIssue) CommonString.reminder_troubleshooting_explanation_exact_alarm else null } return explanationRes?.let { context.getString(it) } } @@ -446,7 +447,7 @@ private fun TroubleshootingCheck.resolveAction(): ResolveCheckAction? { fun requestNotificationPermission(): ResolveCheckAction? = ResolveCheckAction( - label = context.getString(R.string.reminder_troubleshooting_action_grant_permission), + label = context.getString(CommonString.reminder_troubleshooting_action_grant_permission), logDescription = "requesting POST_NOTIFICATIONS via system dialog or app settings", ) { fragment.attemptToEnableNotifications(fragment.notificationPermissionLauncher) @@ -455,7 +456,7 @@ private fun TroubleshootingCheck.resolveAction(): ResolveCheckAction? { fun requestReminderNotifChannelPermission(): ResolveCheckAction? { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null return ResolveCheckAction( - label = context.getString(R.string.reminder_troubleshooting_action_enable_notification_channel), + label = context.getString(CommonString.reminder_troubleshooting_action_enable_notification_channel), logDescription = "opening app notification settings screen", ) { fragment.openAppNotificationsSettingsScreen(highlightedChannel = NotificationChannel.REVIEW_REMINDERS) @@ -467,7 +468,7 @@ private fun TroubleshootingCheck.resolveAction(): ResolveCheckAction? { return if (Permissions.canRequestIgnoreBatteryOptimizations(context)) { ResolveCheckAction( - label = context.getString(R.string.reminder_troubleshooting_action_disable_battery_optimization), + label = context.getString(CommonString.reminder_troubleshooting_action_disable_battery_optimization), logDescription = "opening ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", ) { try { @@ -484,7 +485,7 @@ private fun TroubleshootingCheck.resolveAction(): ResolveCheckAction? { } } else { ResolveCheckAction( - label = context.getString(R.string.reminder_troubleshooting_action_open_battery_settings), + label = context.getString(CommonString.reminder_troubleshooting_action_open_battery_settings), logDescription = "opening ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS", ) { openBatteryOptimizationList() @@ -494,7 +495,7 @@ private fun TroubleshootingCheck.resolveAction(): ResolveCheckAction? { fun openBatterySaverSettings() = ResolveCheckAction( - label = context.getString(R.string.reminder_troubleshooting_action_open_battery_settings), + label = context.getString(CommonString.reminder_troubleshooting_action_open_battery_settings), logDescription = "opening ACTION_BATTERY_SAVER_SETTINGS", ) { context.startActivity(Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)) @@ -503,7 +504,7 @@ private fun TroubleshootingCheck.resolveAction(): ResolveCheckAction? { fun openExactAlarmSettings(): ResolveCheckAction? { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return null return ResolveCheckAction( - label = context.getString(R.string.reminder_troubleshooting_action_grant_permission), + label = context.getString(CommonString.reminder_troubleshooting_action_grant_permission), logDescription = "opening ACTION_REQUEST_SCHEDULE_EXACT_ALARM", ) { context.startActivity( diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt index a411421d1b7c..a2b2ff7909f6 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt @@ -7,7 +7,7 @@ import android.content.Context import android.content.SharedPreferences import androidx.annotation.VisibleForTesting import androidx.core.content.edit -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.android.appContext import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager @@ -448,7 +448,7 @@ object ReviewRemindersDatabase { if (errorString.isEmpty()) return context.showError( - message = context.getString(R.string.review_reminders_load_error) + "\n\n" + errorString.ellipsize(1000), + message = context.getString(CommonString.review_reminders_load_error) + "\n\n" + errorString.ellipsize(1000), crashReportData = null, // Crash reports are sent when the error is first encountered ) Prefs.reviewReminderDeserializationErrors = "" diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersAdapter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersAdapter.kt index faa4ab4867a1..8673fd41fb62 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersAdapter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersAdapter.kt @@ -15,6 +15,7 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.google.android.material.materialswitch.MaterialSwitch import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.ItemScheduleRemindersBinding import com.ichi2.anki.libanki.DeckId @@ -96,7 +97,7 @@ class ScheduleRemindersAdapter( setTextViewStrikethrough(holder.timeTextView, false) setViewHolderColors(holder, activeTextColor, activeTrackColor) } else { - holder.deckTextView.text = holder.context.getString(R.string.schedule_reminders_deck_not_found) + holder.deckTextView.text = holder.context.getString(CommonString.schedule_reminders_deck_not_found) setTextViewStrikethrough(holder.timeTextView, true) setViewHolderColors(holder, inactiveTextColor, inactiveTrackColor) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersFragment.kt index 2bd9a84fc0c3..7f6e8223263a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ScheduleRemindersFragment.kt @@ -31,6 +31,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.canUserAccessDeck import com.ichi2.anki.databinding.FragmentScheduleRemindersBinding @@ -276,8 +277,8 @@ class ScheduleRemindersFragment : } if (troubleshootingSnackbar?.isShown == true) return troubleshootingSnackbar = - showSnackbar(text = getString(R.string.schedule_reminders_unavailable), duration = Snackbar.LENGTH_INDEFINITE) { - setAction(R.string.schedule_reminders_fix) { openTroubleshootingScreen() } + showSnackbar(text = getString(CommonString.schedule_reminders_unavailable), duration = Snackbar.LENGTH_INDEFINITE) { + setAction(CommonString.schedule_reminders_fix) { openTroubleshootingScreen() } } } @@ -290,7 +291,7 @@ class ScheduleRemindersFragment : Lifecycle.State.RESUMED, ) retrieveSubtitle { subtitle -> - setToolbarText(title = getString(R.string.review_reminders_title), subtitle = subtitle) + setToolbarText(title = getString(CommonString.review_reminders_title), subtitle = subtitle) } invalidateMenu() } @@ -316,7 +317,7 @@ class ScheduleRemindersFragment : Lifecycle.State.RESUMED, ) - title = getString(R.string.review_reminders_title) + title = getString(CommonString.review_reminders_title) retrieveSubtitle { subtitle = it } setNavigationOnClickListener { @@ -461,11 +462,11 @@ class ScheduleRemindersFragment : if (flowOfShowTroubleshooting.value) return // Don't override the troubleshooting snackbar, which is more important showSnackbar( when (modeOfFinishedDialog) { - is AddEditReminderDialog.DialogMode.Add -> getString(R.string.schedule_reminders_added) + is AddEditReminderDialog.DialogMode.Add -> getString(CommonString.schedule_reminders_added) is AddEditReminderDialog.DialogMode.Edit -> { when (newOrModifiedReminder) { - null -> getString(R.string.schedule_reminders_deleted) - else -> getString(R.string.schedule_reminders_edited) + null -> getString(CommonString.schedule_reminders_deleted) + else -> getString(CommonString.schedule_reminders_edited) } } }, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/ForgetCardsDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/ForgetCardsDialog.kt index 477b14a2566a..4817c673c5d3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/ForgetCardsDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/ForgetCardsDialog.kt @@ -11,6 +11,8 @@ import androidx.fragment.app.setFragmentResultListener import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.common.annotations.NeedsTest @@ -101,10 +103,10 @@ class ForgetCardsDialog : AnalyticsDialogFragment() { return MaterialAlertDialogBuilder(requireContext()).create { // BUG: this is 'Reset Card'/'Forget Card' in Anki Desktop (24.04) // "Reset card progress" is less explicit on the singular/plural dimension - titleWithHelpIcon(stringRes = R.string.reset_card_dialog_title) { + titleWithHelpIcon(stringRes = CommonString.reset_card_dialog_title) { requireContext().openUrl(R.string.link_help_forget_cards) } - positiveButton(R.string.reset) { + positiveButton(CommonString.reset) { sharedPrefs.edit { putBoolean(ARG_RESTORE_ORIGINAL, restoreOriginalPositionIfPossible) putBoolean(ARG_RESET_REPETITION, resetRepetitionAndLapseCounts) @@ -117,7 +119,7 @@ class ForgetCardsDialog : AnalyticsDialogFragment() { }, ) } - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) setView(binding.root) } } @@ -192,7 +194,7 @@ private fun FragmentActivity.forgetCards( Timber.d("forgot %d cards", cardsIds.size) showSnackbar( resources.getQuantityString( - R.plurals.reset_cards_dialog_acknowledge, + CommonPlurals.reset_cards_dialog_acknowledge, cardsIds.size, cardsIds.size, ), diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateDialog.kt index 5fad852119fe..7176fc7708d0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateDialog.kt @@ -31,6 +31,8 @@ import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.analytics.AnalyticsDialogFragment import com.ichi2.anki.asyncCatching @@ -110,7 +112,7 @@ class SetDueDateDialog : AnalyticsDialogFragment() { } catch (e: IOException) { // the file may be missing, or truncated if the write was interrupted Timber.w(e, "Failed to read cardIds") - showThemedToast(requireContext(), R.string.something_wrong, false) + showThemedToast(requireContext(), CommonString.something_wrong, false) dismiss() return } @@ -173,8 +175,8 @@ class SetDueDateDialog : AnalyticsDialogFragment() { openUrl(R.string.link_set_due_date_help) } title(text = TR.sentenceCase.setDueDate) - positiveButton(R.string.dialog_ok) { launchUpdateDueDate() } - negativeButton(R.string.dialog_cancel) + positiveButton(CommonString.dialog_ok) { launchUpdateDueDate() } + negativeButton(CommonString.dialog_cancel) setView(binding.root) }.apply { show() @@ -231,7 +233,7 @@ class SetDueDateDialog : AnalyticsDialogFragment() { tv.isVisible = true tv.text = resources.getQuantityString( - R.plurals.set_due_date_current_interval, + CommonPlurals.set_due_date_current_interval, currentInterval, currentInterval, ) @@ -349,18 +351,18 @@ class SetDueDateDialog : AnalyticsDialogFragment() { viewModel.nextSingleDayDueDate = currentValue suffixText = resources.getQuantityString( - R.plurals.set_due_date_label_suffix, + CommonPlurals.set_due_date_label_suffix, currentValue ?: 0, ) } - suffixText = resources.getQuantityString(R.plurals.set_due_date_label_suffix, 0) + suffixText = resources.getQuantityString(CommonPlurals.set_due_date_label_suffix, 0) helperText = getString( - R.string.set_due_date_hintText, + CommonString.set_due_date_hintText, // 0 days - resources.getQuantityString(R.plurals.set_due_date_label_suffix, 0), + resources.getQuantityString(CommonPlurals.set_due_date_label_suffix, 0), // 1 day - resources.getQuantityString(R.plurals.set_due_date_label_suffix, 1), + resources.getQuantityString(CommonPlurals.set_due_date_label_suffix, 1), ) setOnEditorActionListener { _, actionId, event -> return@setOnEditorActionListener if (actionId == EditorInfo.IME_ACTION_DONE || @@ -380,7 +382,7 @@ class SetDueDateDialog : AnalyticsDialogFragment() { } binding.dateSingleLabel.text = resources.getQuantityString( - R.plurals.set_due_date_single_day_label, + CommonPlurals.set_due_date_single_day_label, viewModel.cardCount, ) } @@ -415,11 +417,11 @@ class SetDueDateDialog : AnalyticsDialogFragment() { viewModel.setNextDateRangeStart(value) suffixText = resources.getQuantityString( - R.plurals.set_due_date_label_suffix, + CommonPlurals.set_due_date_label_suffix, value ?: 0, ) } - suffixText = resources.getQuantityString(R.plurals.set_due_date_label_suffix, 0) + suffixText = resources.getQuantityString(CommonPlurals.set_due_date_label_suffix, 0) selectAllWhenFocused() } } @@ -431,11 +433,11 @@ class SetDueDateDialog : AnalyticsDialogFragment() { viewModel.setNextDateRangeEnd(value) suffixText = resources.getQuantityString( - R.plurals.set_due_date_label_suffix, + CommonPlurals.set_due_date_label_suffix, value ?: 0, ) } - suffixText = resources.getQuantityString(R.plurals.set_due_date_label_suffix, 0) + suffixText = resources.getQuantityString(CommonPlurals.set_due_date_label_suffix, 0) viewModel.dateRange.end?.let { end -> setText(end.toString()) } setOnEditorActionListener { _, actionId, event -> return@setOnEditorActionListener if (actionId == EditorInfo.IME_ACTION_DONE || @@ -454,7 +456,7 @@ class SetDueDateDialog : AnalyticsDialogFragment() { } } binding.dateRangeLabel.text = - resources.getQuantityString(R.plurals.set_due_date_range_label, viewModel.cardCount) + resources.getQuantityString(CommonPlurals.set_due_date_range_label, viewModel.cardCount) } override fun onResume() { @@ -482,7 +484,7 @@ private fun AnkiActivity.updateDueDate( if (cardsUpdated == null) { Timber.w("unable to update due date") if (showError) { - showThemedToast(this@updateDueDate, R.string.something_wrong, true) + showThemedToast(this@updateDueDate, CommonString.something_wrong, true) } return@asyncCatching null } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateViewModel.kt index f91160f6b7f4..1ddee60333b1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/scheduling/SetDueDateViewModel.kt @@ -6,6 +6,7 @@ import androidx.annotation.DrawableRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.libanki.CardId import com.ichi2.anki.libanki.CardType @@ -198,10 +199,10 @@ class SetDueDateViewModel : ViewModel() { val text: Int, ) { /** Set the due date to a single day */ - SINGLE_DAY(0, R.drawable.calendar_single_day, R.string.set_due_date_day), + SINGLE_DAY(0, R.drawable.calendar_single_day, CommonString.set_due_date_day), /** Sets the due date randomly between a range of days */ - DATE_RANGE(1, R.drawable.calendar_date_range, R.string.set_due_date_date_range), + DATE_RANGE(1, R.drawable.calendar_date_range, CommonString.set_due_date_date_range), } class DateRange( diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/security/AppPermissions.kt b/AnkiDroid/src/main/java/com/ichi2/anki/security/AppPermissions.kt index bc717f80e39f..2d44b55f6866 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/security/AppPermissions.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/security/AppPermissions.kt @@ -4,6 +4,7 @@ package com.ichi2.anki.security import android.content.Context import androidx.annotation.VisibleForTesting +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.preferences.sharedPrefs import timber.log.Timber @@ -52,7 +53,7 @@ class AppPermissions( // Avoid decision fatigue by limiting the number of times the warning is shown. // Reset on app restart. if (launchDenialsSinceProcessStart.incrementAndGet() > MAX_SNACKBARS_PER_PROCESS) return - showSnackbar(context.getString(R.string.card_external_launch_denied_snackbar)) + showSnackbar(context.getString(CommonString.card_external_launch_denied_snackbar)) } /** @@ -75,7 +76,7 @@ class AppPermissions( // Avoid decision fatigue by limiting the number of times the warning is shown. // Reset on app restart. if (jaApiDenialsSinceProcessStart.incrementAndGet() > MAX_SNACKBARS_PER_PROCESS) return - showSnackbar(context.getString(R.string.dangerous_js_api_denied_snackbar)) + showSnackbar(context.getString(CommonString.dangerous_js_api_denied_snackbar)) } companion object { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/SchedulerService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/SchedulerService.kt index 88530e2a7a78..3f01387b8a4c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/SchedulerService.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/SchedulerService.kt @@ -5,7 +5,7 @@ package com.ichi2.anki.servicelayer import androidx.fragment.app.FragmentActivity import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.R +import com.ichi2.anki.CommonPlurals import com.ichi2.anki.libanki.CardId import com.ichi2.anki.observability.undoableOp import com.ichi2.anki.snackbar.showSnackbar @@ -37,7 +37,7 @@ suspend fun FragmentActivity.resetCards( val count = cardIds.size showSnackbar( resources.getQuantityString( - R.plurals.reset_cards_dialog_acknowledge, + CommonPlurals.reset_cards_dialog_acknowledge, count, count, ), diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/services/BootService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/services/BootService.kt index 600bf68e5ab8..52775dfa80a4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/services/BootService.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/services/BootService.kt @@ -9,6 +9,7 @@ import android.content.Context import android.content.Intent import androidx.core.app.PendingIntentCompat import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.IntentHandler.Companion.grantedStoragePermissions import com.ichi2.anki.R import com.ichi2.anki.common.android.AnkiBroadcastReceiver @@ -95,10 +96,10 @@ class BootService : AnkiBroadcastReceiver() { runnable.run() } catch (ex: SecurityException) { Timber.w(ex) - error = R.string.boot_service_too_many_notifications + error = CommonString.boot_service_too_many_notifications } catch (e: Exception) { Timber.w(e) - error = R.string.boot_service_failed_to_schedule_notifications + error = CommonString.boot_service_failed_to_schedule_notifications } if (error != null) { if (!failedToShowNotifications) { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.kt index 4ee4f0e84df5..206c1fa497ba 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.kt @@ -12,6 +12,8 @@ import androidx.core.app.NotificationCompat import androidx.core.app.PendingIntentCompat import androidx.core.content.getSystemService import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.IntentHandler import com.ichi2.anki.NotificationChannel @@ -218,20 +220,20 @@ class NotificationService : AnkiBroadcastReceiver() { val title = when (reviewReminder.scope) { - is ReviewReminderScope.Global -> context.getString(R.string.review_reminder_notification_title_all_decks) + is ReviewReminderScope.Global -> context.getString(CommonString.review_reminder_notification_title_all_decks) is ReviewReminderScope.DeckSpecific -> { val fullDeckName = reviewReminder.scope.getDeckName() val deckName = Decks.basename(fullDeckName) // don't show the full path with "::" included context.getString( - R.string.review_reminder_notification_title_deck, + CommonString.review_reminder_notification_title_deck, withUnicodeIsolation(deckName), ) } } val description = context.resources.getQuantityString( - R.plurals.review_reminder_notification_cards_due, + CommonPlurals.review_reminder_notification_cards_due, dueCardsTotal, dueCardsTotal, ) @@ -277,9 +279,12 @@ class NotificationService : AnkiBroadcastReceiver() { .setAutoCancel(true) // Dismiss on click .setTicker(title) // Accessibility .addAction( - context.getString(R.string.review_reminder_notification_snooze_button, 5.minutes.toString()), + context.getString(CommonString.review_reminder_notification_snooze_button, 5.minutes.toString()), fiveMinuteSnooze, - ).addAction(context.getString(R.string.review_reminder_notification_snooze_button, 1.hours.toString()), oneHourSnooze) + ).addAction( + context.getString(CommonString.review_reminder_notification_snooze_button, 1.hours.toString()), + oneHourSnooze, + ) // Vibration and priority are set here for backwards compatibility; they are set via channel for API 33+ .setVibrate(longArrayOf(0, 500)) .setPriority(NotificationCompat.PRIORITY_DEFAULT) @@ -362,7 +367,7 @@ class NotificationService : AnkiBroadcastReceiver() { val cardsDueText = context.resources .getQuantityString( - R.plurals.widget_minimum_cards_due_notification_ticker_text, + CommonPlurals.widget_minimum_cards_due_notification_ticker_text, dueCardsCount, dueCardsCount, ) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksActivity.kt index 052a7482e165..ec0a0fc52037 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksActivity.kt @@ -27,6 +27,7 @@ import androidx.core.view.updatePadding import androidx.fragment.app.commit import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_INDEFINITE import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.utils.ext.requireSystemService import com.ichi2.anki.databinding.ActivitySharedDecksBinding @@ -173,12 +174,12 @@ class SharedDecksActivity : AnkiActivity(R.layout.activity_shared_decks) { */ private fun redirectUserToSignUpOrLogin() { // inform the user they need to log in as they've hit a rate limit - showSnackbar(R.string.shared_decks_login_required, LENGTH_INDEFINITE) { + showSnackbar(CommonString.shared_decks_login_required, LENGTH_INDEFINITE) { if (isLoggedIn()) return@showSnackbar // If a user is not logged in inside AnkiDroid, assume they have no AnkiWeb account // and give them the option to sign up - setAction(R.string.sign_up) { + setAction(CommonString.sign_up) { binding.webView.loadUrl(getString(R.string.shared_decks_sign_up_url)) } } @@ -222,7 +223,7 @@ class SharedDecksActivity : AnkiActivity(R.layout.activity_shared_decks) { // // statusBarStyle matches the status bar theme of the rest of the app enableEdgeToEdge(statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT)) - setTitle(R.string.download_deck) + setTitle(CommonString.download_deck) setupEdgeToEdge() binding.webviewToolbar.setTitleTextColor(getColor(R.color.white)) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksDownloadFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksDownloadFragment.kt index 8742f4bbc301..e3280accb96b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksDownloadFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/shareddeck/SharedDecksDownloadFragment.kt @@ -31,6 +31,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.IntentHandler import com.ichi2.anki.R import com.ichi2.anki.common.android.AnkiBroadcastReceiver @@ -193,13 +194,13 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down } private fun render(state: SharedDecksDownloadUiState) { - binding.downloadingTitle.text = state.fileName?.let { getString(R.string.downloading_file, it) } + binding.downloadingTitle.text = state.fileName?.let { getString(CommonString.downloading_file, it) } binding.downloadPercentageText.text = when { - state.phase == DownloadPhase.Failed -> getString(R.string.download_failed) + state.phase == DownloadPhase.Failed -> getString(CommonString.download_failed) // 19812: DownloadManager could not be queried, so all we can say is that it is running state.percent == null -> TR.syncDownloadingFromAnkiweb() - else -> getString(R.string.percentage, formatDownloadPercent(state.percent)) + else -> getString(CommonString.percentage, formatDownloadPercent(state.percent)) } binding.downloadProgressBar.progress = state.percent?.toInt() ?: 0 binding.checkNetworkInfoText.isVisible = state.isWaitingForNetwork @@ -234,7 +235,7 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down private fun downloadFile(fileToBeDownloaded: DownloadFile) { val externalFilesFolder = requireContext().getExternalFilesDir(null) if (externalFilesFolder == null) { - showSnackbar(R.string.external_storage_unavailable) + showSnackbar(CommonString.external_storage_unavailable) parentFragmentManager.popBackStack() return } @@ -521,7 +522,7 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down try { context.startActivity(fileIntent) } catch (e: ActivityNotFoundException) { - showThemedToast(context, R.string.something_wrong, false) + showThemedToast(context, CommonString.something_wrong, false) Timber.w(e) } } @@ -537,14 +538,14 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down if (isInvalidDeckFile) { Timber.i("File is not a valid deck, hence return from the download screen") if (isVisible) { - context?.let { showThemedToast(it, R.string.import_log_no_apkg, false) } + context?.let { showThemedToast(it, CommonString.import_log_no_apkg, false) } // Go back if file is not a deck and cannot be imported activity?.onBackPressedDispatcher?.onBackPressed() } } else { Timber.i("Download failed, offer a retry") if (isVisible) { - context?.let { showThemedToast(it, R.string.something_wrong, false) } + context?.let { showThemedToast(it, CommonString.something_wrong, false) } } viewModel.onDownloadFailed() } @@ -561,15 +562,15 @@ class SharedDecksDownloadFragment : Fragment(R.layout.fragment_shared_decks_down Timber.i("displaying cancel download confirmation dialog") downloadCancelConfirmationDialog = AlertDialog.Builder(requireContext()).create { - setTitle(R.string.cancel_download_question_title) - setPositiveButton(R.string.dialog_yes) { _, _ -> + setTitle(CommonString.cancel_download_question_title) + setPositiveButton(CommonString.dialog_yes) { _, _ -> Timber.i("cancelling download") downloadManager.remove(downloadId) isDownloadInProgress = false onBackPressedCallback.isEnabled = isDownloadInProgress parentFragmentManager.popBackStack() } - setNegativeButton(R.string.dialog_no) { _, _ -> + setNegativeButton(CommonString.dialog_no) { _, _ -> Timber.i("dismissed cancel download confirmation dialog") downloadCancelConfirmationDialog?.dismiss() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/sync/MeteredSyncPolicy.kt b/AnkiDroid/src/main/java/com/ichi2/anki/sync/MeteredSyncPolicy.kt index 39783b225339..f30e05a1f156 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/sync/MeteredSyncPolicy.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/sync/MeteredSyncPolicy.kt @@ -4,7 +4,7 @@ package com.ichi2.anki.sync import android.content.Context import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.settings.Prefs import com.ichi2.utils.NetworkUtils.isActiveNetworkMetered import com.ichi2.utils.checkBoxPrompt @@ -47,10 +47,10 @@ object MeteredSyncPolicy { return } AlertDialog.Builder(context).show { - message(R.string.metered_sync_data_warning) - positiveButton(R.string.dialog_continue) { onConfirm() } - negativeButton(R.string.dialog_cancel) - checkBoxPrompt(R.string.button_do_not_show_again) { checked -> + message(CommonString.metered_sync_data_warning) + positiveButton(CommonString.dialog_continue) { onConfirm() } + negativeButton(CommonString.dialog_cancel) + checkBoxPrompt(CommonString.button_do_not_show_again) { checked -> setAlwaysAllow(checked) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/sync/SchemaChanges.kt b/AnkiDroid/src/main/java/com/ichi2/anki/sync/SchemaChanges.kt index 20962b548dbb..28bda256881d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/sync/SchemaChanges.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/sync/SchemaChanges.kt @@ -6,7 +6,7 @@ import androidx.appcompat.app.AlertDialog import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.dialogs.ConfirmationDialog import com.ichi2.anki.launchCatchingTask import com.ichi2.anki.libanki.Collection @@ -20,7 +20,7 @@ import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine /** - * [launchCatchingTask], showing a one-way sync dialog: [R.string.full_sync_confirmation] + * [launchCatchingTask], showing a one-way sync dialog: [CommonString.full_sync_confirmation] * * @param block calls a backend method which unconditionally performs a schema change, * such as [Collection.changeNotetypeRaw] @@ -31,7 +31,7 @@ fun AnkiActivity.launchCatchingRequiringOneWaySync(block: suspend () -> Unit) = // .also is used to ensure the activity is used as context val confirmModSchemaDialog = ConfirmationDialog().also { dialog -> - dialog.setArgs(message = getString(R.string.full_sync_confirmation)) + dialog.setArgs(message = getString(CommonString.full_sync_confirmation)) dialog.setConfirm { launchCatchingTask { block() @@ -46,7 +46,7 @@ fun AnkiActivity.launchCatchingRequiringOneWaySync(block: suspend () -> Unit) = } /** - * [launchCatchingTask], showing a one-way sync dialog: [R.string.full_sync_confirmation] + * [launchCatchingTask], showing a one-way sync dialog: [CommonString.full_sync_confirmation] * * **This method discards the undo and study queues when consent is provided** */ @@ -60,7 +60,7 @@ fun AnkiActivity.launchCatchingRequiringOneWaySyncDiscardUndo(block: suspend () // .also is used to ensure the activity is used as context val confirmModSchemaDialog = ConfirmationDialog().also { dialog -> - dialog.setArgs(message = getString(R.string.full_sync_confirmation)) + dialog.setArgs(message = getString(CommonString.full_sync_confirmation)) dialog.setConfirm { launchCatchingTask { withCol { modSchema(check = false) } @@ -86,8 +86,8 @@ suspend fun AnkiActivity.userAcceptsSchemaChange(): Boolean { suspendCoroutine { coroutine -> AlertDialog.Builder(this).show { message(text = TR.deckConfigWillRequireFullSync().replace("\\s+".toRegex(), " ")) - positiveButton(R.string.dialog_ok) { coroutine.resume(true) } - negativeButton(R.string.dialog_cancel) { coroutine.resume(false) } + positiveButton(CommonString.dialog_ok) { coroutine.resume(true) } + negativeButton(CommonString.dialog_cancel) { coroutine.resume(false) } setOnCancelListener { coroutine.resume(false) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/dialogs/tools/AsyncDialogs.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/dialogs/tools/AsyncDialogs.kt index e505606ab812..404333b9d2c2 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/dialogs/tools/AsyncDialogs.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/dialogs/tools/AsyncDialogs.kt @@ -135,8 +135,8 @@ sealed interface DialogResult { * val dialogResult = awaitDialog { * setTitle("Title") * setMessage("Message") - * setPositiveButton(R.string.dialog_ok) - * setNegativeButton(R.string.dialog_cancel) + * setPositiveButton(CommonString.dialog_ok) + * setNegativeButton(CommonString.dialog_cancel) * } * * if (dialogResult is DialogResult.Ok) { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/preferences/screens/BackupLimitsPresenter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/preferences/screens/BackupLimitsPresenter.kt index c6f6569bce48..0640e7d62bd9 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/preferences/screens/BackupLimitsPresenter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/preferences/screens/BackupLimitsPresenter.kt @@ -26,6 +26,7 @@ import androidx.preference.PreferenceFragmentCompat import anki.config.Preferences.BackupLimits import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.launchCatchingTask import com.ichi2.anki.preferences.requirePreference @@ -149,17 +150,17 @@ class BackupLimitsPresenter( preference.summaryProvider = Preference.SummaryProvider { when (viewModel.flowOfState.value) { - is State.Fetching -> fragment.getString(R.string.pref__etc__summary__fetching) - is State.Error.NoCollection -> fragment.getString(R.string.pref__etc__summary__no_collection) - is State.Error.Exception -> fragment.getString(R.string.pref__etc__summary__error) + is State.Fetching -> fragment.getString(CommonString.pref__etc__summary__fetching) + is State.Error.NoCollection -> fragment.getString(CommonString.pref__etc__summary__no_collection) + is State.Error.Exception -> fragment.getString(CommonString.pref__etc__summary__error) is State.Fetched -> preference.text } } preference.onClickListener = listener@{ when (val state = viewModel.flowOfState.value) { - is State.Fetching -> fragment.showSnackbar(R.string.pref__etc__snackbar__fetching) - is State.Error.NoCollection -> fragment.showSnackbar(R.string.pref__etc__snackbar__no_collection) + is State.Fetching -> fragment.showSnackbar(CommonString.pref__etc__snackbar__fetching) + is State.Error.NoCollection -> fragment.showSnackbar(CommonString.pref__etc__snackbar__no_collection) is State.Error.Exception -> fragment.showSnackbar( text = fragment.requireContext().getUserFriendlyErrorText(state.exception), diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/FileUtils.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/FileUtils.kt index 5af62e3cd9ed..5e6e449f2604 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/FileUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/FileUtils.kt @@ -10,7 +10,7 @@ import android.os.storage.StorageManager import android.os.storage.StorageVolume import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.utils.TranslatableException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -200,7 +200,7 @@ class CanNotWriteToOrCreateFileException( TranslatableException { override val message get() = "Can not write to or create file: $file" - override fun getTranslatedMessage(context: Context) = context.getString(R.string.error__etc__cannot_write_to_or_create_file, file) + override fun getTranslatedMessage(context: Context) = context.getString(CommonString.error__etc__cannot_write_to_or_create_file, file) } suspend fun CollectionDirectoryProvider.ensureCanWriteToOrCreateCollectionDirectory() { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/ManageSpaceFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/ManageSpaceFragment.kt index f95a43d4472b..bbe2ac778ef2 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/ManageSpaceFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/managespace/ManageSpaceFragment.kt @@ -18,6 +18,8 @@ import androidx.preference.Preference import com.ichi2.anki.BackupManager import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.LocalizedUnambiguousBackupTimeFormatter import com.ichi2.anki.R import com.ichi2.anki.launchCatchingTask @@ -53,7 +55,7 @@ sealed interface Size { class Error( val exception: Exception, - @StringRes val widgetTextId: Int = R.string.pref__widget_text__error, + @StringRes val widgetTextId: Int = CommonString.pref__widget_text__error, ) : Size } @@ -231,7 +233,7 @@ class ManageSpaceFragment : SettingsFragment() { ) { super.onViewCreated(view, savedInstanceState) binding.toolbar.apply { - title = getString(R.string.pref__manage_space__screen_title) + title = getString(CommonString.pref__manage_space__screen_title) } } @@ -245,17 +247,17 @@ class ManageSpaceFragment : SettingsFragment() { val deleteFilesPromptResult = requireContext().awaitDialog { - setTitle(R.string.dialog__delete_unused_media_files__title) + setTitle(CommonString.dialog__delete_unused_media_files__title) setMultiChoiceItems(unusedFileNames, CheckedItems.All) - setPositiveButton(R.string.dialog_positive_delete) - setNegativeButton(R.string.dialog_cancel) + setPositiveButton(CommonString.dialog_positive_delete) + setNegativeButton(CommonString.dialog_cancel) } if (deleteFilesPromptResult is DialogResult.Ok.MultipleChoice) { val checkedItems = deleteFilesPromptResult.checkedItems val filesNamesToDelete = unusedFileNames.filterIndexed { index, _ -> checkedItems[index] } - withProgress(R.string.delete_media_message) { + withProgress(CommonString.delete_media_message) { viewModel.deleteMediaFiles(filesNamesToDelete) } } @@ -275,17 +277,17 @@ class ManageSpaceFragment : SettingsFragment() { val chooseBackupsPromptResult = requireContext().awaitDialog { - setTitle(R.string.dialog__delete_backups__title) + setTitle(CommonString.dialog__delete_backups__title) setMultiChoiceItems(backupNames, CheckedItems.None) - setPositiveButton(R.string.dialog_positive_delete) - setNegativeButton(R.string.dialog_cancel) + setPositiveButton(CommonString.dialog_positive_delete) + setNegativeButton(CommonString.dialog_cancel) } if (chooseBackupsPromptResult is DialogResult.Ok.MultipleChoice) { val checkedItems = chooseBackupsPromptResult.checkedItems val backupsToDelete = backupFiles.filterIndexed { index, _ -> checkedItems[index] } - withProgress(R.string.progress__deleting_backups) { + withProgress(CommonString.progress__deleting_backups) { viewModel.deleteBackups(backupsToDelete) } } @@ -308,15 +310,15 @@ class ManageSpaceFragment : SettingsFragment() { if (size is Size.Bytes) { val deleteCollectionPromptResult = requireContext().awaitDialog { - setTitle(R.string.dialog__delete_collection__title) - setMessage(R.string.dialog__delete_collection__message) - setPositiveButton(R.string.dialog_positive_delete) - setNegativeButton(R.string.dialog_cancel) + setTitle(CommonString.dialog__delete_collection__title) + setMessage(CommonString.dialog__delete_collection__message) + setPositiveButton(CommonString.dialog_positive_delete) + setNegativeButton(CommonString.dialog_cancel) } if (deleteCollectionPromptResult is DialogResult.Ok) { try { - withProgress(R.string.progress__deleting_collection) { + withProgress(CommonString.progress__deleting_collection) { viewModel.deleteCollection() } } finally { @@ -336,15 +338,15 @@ class ManageSpaceFragment : SettingsFragment() { private fun adjustDeleteEverythingStringsDependingOnCollectionLocation(preference: Preference) { if (viewModel.collectionDirectory.isInsideDirectoriesRemovedWithTheApp(requireContext())) { - preference.setTitle(R.string.pref__delete_everything__title) - preference.setSummary(R.string.pref__delete_everything__summary) - deleteEverythingDialogTitle = R.string.dialog__delete_everything__title - deleteEverythingDialogMessage = R.string.dialog__delete_everything__message + preference.setTitle(CommonString.pref__delete_everything__title) + preference.setSummary(CommonString.pref__delete_everything__summary) + deleteEverythingDialogTitle = CommonString.dialog__delete_everything__title + deleteEverythingDialogMessage = CommonString.dialog__delete_everything__message } else { - preference.setTitle(R.string.pref__delete_app_data__title) - preference.setSummary(R.string.pref__delete_app_data__summary) - deleteEverythingDialogTitle = R.string.dialog__delete_app_data__title - deleteEverythingDialogMessage = R.string.dialog__delete_app_data__message + preference.setTitle(CommonString.pref__delete_app_data__title) + preference.setSummary(CommonString.pref__delete_app_data__summary) + deleteEverythingDialogTitle = CommonString.dialog__delete_app_data__title + deleteEverythingDialogMessage = CommonString.dialog__delete_app_data__message } } @@ -353,8 +355,8 @@ class ManageSpaceFragment : SettingsFragment() { requireContext().awaitDialog { setTitle(deleteEverythingDialogTitle) setMessage(deleteEverythingDialogMessage) - setPositiveButton(R.string.dialog_positive_delete) - setNegativeButton(R.string.dialog_cancel) + setPositiveButton(CommonString.dialog_positive_delete) + setNegativeButton(CommonString.dialog_cancel) } if (deleteEverythingPromptResult is DialogResult.Ok) { @@ -373,12 +375,12 @@ class ManageSpaceFragment : SettingsFragment() { widgetText = when (size) { - is Size.Calculating -> getString(R.string.pref__widget_text__calculating) + is Size.Calculating -> getString(CommonString.pref__widget_text__calculating) is Size.Error -> getString(size.widgetTextId) is Size.Bytes -> size.totalSize.toHumanReadableSize() is Size.FilesAndBytes -> resources.getQuantityString( - R.plurals.pref__widget_text__n_files_n_bytes, + CommonPlurals.pref__widget_text__n_files_n_bytes, size.files.size, size.files.size, size.totalSize.toHumanReadableSize(), @@ -401,7 +403,7 @@ class ManageSpaceFragment : SettingsFragment() { private fun showSnackbarIfCalculatingOrError(size: Size) { when (size) { - is Size.Calculating -> showSnackbar(R.string.pref__etc__snackbar__calculating) + is Size.Calculating -> showSnackbar(CommonString.pref__etc__snackbar__calculating) is Size.Error -> showSnackbar(requireContext().getUserFriendlyErrorText(size.exception)) else -> {} } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AllPermissionsExplanationFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AllPermissionsExplanationFragment.kt index 0b7f421b4192..bf913ef960e9 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AllPermissionsExplanationFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AllPermissionsExplanationFragment.kt @@ -9,6 +9,7 @@ import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi import androidx.core.view.isVisible +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.permissions.LEGACY_POST_NOTIFICATIONS import com.ichi2.anki.databinding.FragmentAllPermissionsExplanationBinding @@ -82,7 +83,7 @@ class AllPermissionsExplanationFragment : PermissionsFragment(R.layout.fragment_ isVisible = true // If it's already granted, offer to revoke it on click; otherwise, request it revokeIfGrantedOnClickElse { - showToastAndOpenAppSettingsScreenForPermission(LEGACY_POST_NOTIFICATIONS, R.string.manually_grant_permissions) + showToastAndOpenAppSettingsScreenForPermission(LEGACY_POST_NOTIFICATIONS, CommonString.manually_grant_permissions) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AndroidPermanentlyRevokedPermissionsDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AndroidPermanentlyRevokedPermissionsDialog.kt index 46d147895c76..45acaa074d0e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AndroidPermanentlyRevokedPermissionsDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/AndroidPermanentlyRevokedPermissionsDialog.kt @@ -7,7 +7,7 @@ import android.content.Context import android.content.DialogInterface import androidx.appcompat.app.AlertDialog import com.ichi2.anki.AnkiActivity -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.storage.CollectionHelper import com.ichi2.anki.dialogs.DatabaseErrorDialog.DatabaseErrorDialogType import com.ichi2.anki.dialogs.DatabaseErrorDialog.UninstallListItem.Companion.createNoStorageList @@ -35,7 +35,7 @@ object AndroidPermanentlyRevokedPermissionsDialog { val message = context.getString( - R.string.directory_revoked_after_inactivity, + CommonString.directory_revoked_after_inactivity, "WRITE_EXTERNAL_STORAGE", getCurrentAnkiDroidDirectoryPath(context), ) @@ -59,6 +59,6 @@ object AndroidPermanentlyRevokedPermissionsDialog { CollectionHelper.getCurrentAnkiDroidDirectory(context).absolutePath } catch (e: Exception) { Timber.w(e) - context.getString(R.string.card_browser_unknown_deck_name) + context.getString(CommonString.card_browser_unknown_deck_name) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/LegacyNotificationsPermissionFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/LegacyNotificationsPermissionFragment.kt index 70987c876c01..b036fa9f1a4c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/LegacyNotificationsPermissionFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/LegacyNotificationsPermissionFragment.kt @@ -6,6 +6,7 @@ package com.ichi2.anki.ui.windows.permissions import android.os.Bundle import android.view.View import androidx.fragment.app.setFragmentResult +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.permissions.LEGACY_POST_NOTIFICATIONS import com.ichi2.anki.common.permissions.canPostNotifications @@ -43,7 +44,7 @@ class LegacyNotificationsPermissionFragment : PermissionsFragment(R.layout.fragm savedInstanceState: Bundle?, ) { binding.legacyNotificationPermission.revokeIfGrantedOnClickElse { - showToastAndOpenAppSettingsScreenForPermission(LEGACY_POST_NOTIFICATIONS, R.string.manually_grant_permissions) + showToastAndOpenAppSettingsScreenForPermission(LEGACY_POST_NOTIFICATIONS, CommonString.manually_grant_permissions) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivity.kt index 77660ce5397e..0ff66eb17d8e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivity.kt @@ -10,6 +10,7 @@ import androidx.activity.addCallback import androidx.activity.enableEdgeToEdge import androidx.fragment.app.commit import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.StoragePermissionSet import com.ichi2.anki.common.utils.android.showThemedToast @@ -51,7 +52,7 @@ class PermissionsActivity : AnkiActivity(R.layout.activity_permissions) { val permissionSet = intent.getParcelableExtraCompat(EXTRA_PERMISSIONS_SET) if (permissionSet == null) { Timber.w("EXTRA_PERMISSIONS_SET not set; finishing") - showThemedToast(this, R.string.something_wrong, false) + showThemedToast(this, CommonString.something_wrong, false) setResult(RESULT_CANCELED) finish() return diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsFragment.kt index 9b756b081b3f..bb3cade2921f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsFragment.kt @@ -16,7 +16,7 @@ import androidx.core.view.allViews import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.setFragmentResult -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.common.permissions.MANAGE_EXTERNAL_STORAGE import com.ichi2.anki.common.permissions.hasPermission @@ -52,7 +52,7 @@ abstract class PermissionsFragment( Timber.i("Internet permission denied") showToastAndOpenAppSettingsScreenForPermission( Manifest.permission.INTERNET, - getString(R.string.permission_required_message, getString(R.string.internet_access_title)), + getString(CommonString.permission_required_message, getString(CommonString.internet_access_title)), ) } } @@ -127,7 +127,7 @@ abstract class PermissionsFragment( protected fun PermissionsItem.revokeIfGrantedOnClickElse(callback: () -> Unit) { setOnPermissionsRequested { areAlreadyGranted -> if (areAlreadyGranted) { - showToastAndOpenAppSettingsScreenForPermission(permissions.singleOrNull(), R.string.revoke_permissions) + showToastAndOpenAppSettingsScreenForPermission(permissions.singleOrNull(), CommonString.revoke_permissions) } else { callback() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsUntil29Fragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsUntil29Fragment.kt index d7697fba3425..c071a90711ed 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsUntil29Fragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/permissions/PermissionsUntil29Fragment.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.activity.result.contract.ActivityResultContracts import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.FragmentPermissionsUntil29Binding import com.ichi2.utils.Permissions @@ -32,7 +33,7 @@ class PermissionsUntil29Fragment : PermissionsFragment(R.layout.fragment_permiss // manually grant the permission. showToastAndOpenAppSettingsScreenForPermission( requestedPermissions.keys.singleOrNull(), - R.string.startup_no_storage_permission, + CommonString.startup_no_storage_permission, ) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt index 1379f5e747e1..0753a0ba9ea7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt @@ -39,6 +39,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import anki.scheduler.CardAnswer.Rating import com.ichi2.anki.CollectionManager +import com.ichi2.anki.CommonString import com.ichi2.anki.DispatchKeyEventListener import com.ichi2.anki.Flag import com.ichi2.anki.R @@ -609,7 +610,7 @@ class ReviewerFragment : val message = CollectionManager.TR.studyingCardStudiedIn(timebox.reps) + " " + CollectionManager.TR.studyingMinute(minutes) AlertDialog.Builder(requireContext()).show { - setTitle(R.string.timebox_reached_title) + setTitle(CommonString.timebox_reached_title) setMessage(message) setPositiveButton(CollectionManager.TR.studyingContinue()) { _, _ -> Timber.i("ReviewerFragment: Timebox 'Continue'") @@ -745,8 +746,8 @@ class ReviewerFragment : if (hasShownUnsupportedFeatureWarning) return true hasShownUnsupportedFeatureWarning = true AlertDialog.Builder(requireContext()).show { - setMessage(R.string.feature_not_supported_by_study_screen) - setPositiveButton(R.string.dialog_ok) { _, _ -> } + setMessage(CommonString.feature_not_supported_by_study_screen) + setPositiveButton(CommonString.dialog_ok) { _, _ -> } } true } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerMenu.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerMenu.kt index 53540b36ec63..b4edd70fbeb3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerMenu.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerMenu.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.coroutineScope import androidx.lifecycle.flowWithLifecycle import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.preferences.reviewer.ReviewerMenuView import com.ichi2.anki.preferences.reviewer.ViewerAction @@ -42,7 +43,7 @@ fun ReviewerMenuView.setup( .collectLatestIn(lifecycle.coroutineScope) { isMarked -> if (isMarked) { markItem.setPaddedIcon(context, R.drawable.ic_star) - markItem.setTitle(R.string.menu_unmark_note) + markItem.setTitle(CommonString.menu_unmark_note) } else { markItem.setPaddedIcon(context, R.drawable.ic_star_border_white) markItem.title = with(context) { TR.sentenceCase.markNote } @@ -110,10 +111,10 @@ fun ReviewerMenuView.setup( recordVoiceFlow.collectLatestIn(lifecycle.coroutineScope) { isEnabled -> if (isEnabled) { recordVoiceItem.setPaddedIcon(context, R.drawable.ic_action_mic) - recordVoiceItem.setTitle(R.string.disable_voice_recording) + recordVoiceItem.setTitle(CommonString.disable_voice_recording) } else { recordVoiceItem.setPaddedIcon(context, R.drawable.ic_mic_outlined) - recordVoiceItem.setTitle(R.string.enable_voice_recording) + recordVoiceItem.setTitle(CommonString.enable_voice_recording) } } } @@ -123,10 +124,10 @@ fun ReviewerMenuView.setup( toggleWhiteboardFlow.collectLatestIn(lifecycle.coroutineScope) { isEnabled -> if (isEnabled) { toggleWhiteboardItem.setPaddedIcon(context, R.drawable.ic_draw_filled) - toggleWhiteboardItem.setTitle(R.string.disable_whiteboard) + toggleWhiteboardItem.setTitle(CommonString.disable_whiteboard) } else { toggleWhiteboardItem.setPaddedIcon(context, R.drawable.ic_enable_whiteboard) - toggleWhiteboardItem.setTitle(R.string.enable_whiteboard) + toggleWhiteboardItem.setTitle(CommonString.enable_whiteboard) } } } @@ -136,10 +137,10 @@ fun ReviewerMenuView.setup( isAutoAdvancedEnabledFlow.collectLatestIn(lifecycle.coroutineScope) { isEnabled -> if (isEnabled) { autoAdvanceItem.setPaddedIcon(context, R.drawable.ic_fast_forward) - autoAdvanceItem.setTitle(R.string.disable_auto_advance) + autoAdvanceItem.setTitle(CommonString.disable_auto_advance) } else { autoAdvanceItem.setPaddedIcon(context, R.drawable.ic_fast_forward_outlined) - autoAdvanceItem.setTitle(R.string.enable_auto_advance) + autoAdvanceItem.setTitle(CommonString.enable_auto_advance) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayView.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayView.kt index 4151f09f2ed8..e5481e17b4fd 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayView.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayView.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.animation.DecelerateInterpolator import androidx.annotation.DrawableRes import androidx.constraintlayout.widget.ConstraintLayout +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.ViewAudioPlayBinding @@ -67,7 +68,7 @@ class AudioPlayView : ConstraintLayout { @DrawableRes iconRes: Int, ) { binding.playButton.contentDescription = - context.getString(if (iconRes == R.drawable.ic_replay) R.string.replay_voice else R.string.play_recording) + context.getString(if (iconRes == R.drawable.ic_replay) CommonString.replay_voice else CommonString.play_recording) binding.playIconView .animate() .alpha(0f) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/CheckPronunciationFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/CheckPronunciationFragment.kt index 7b3dfccafe4c..3d8567471136 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/CheckPronunciationFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/CheckPronunciationFragment.kt @@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.FragmentCheckPronunciationBinding import com.ichi2.anki.ui.windows.reviewer.ReviewerViewModel @@ -35,15 +36,15 @@ class CheckPronunciationFragment : Fragment(R.layout.fragment_check_pronunciatio registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> if (!isGranted) { AlertDialog.Builder(requireContext()).show { - setTitle(R.string.permission_denied) - setMessage(R.string.microphone_permission_denied_message) - setPositiveButton(R.string.dialog_ok) { _, _ -> + setTitle(CommonString.permission_denied) + setMessage(CommonString.microphone_permission_denied_message) + setPositiveButton(CommonString.dialog_ok) { _, _ -> val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val uri = Uri.fromParts("package", requireContext().packageName, null) intent.data = uri startActivity(intent) } - setNegativeButton(R.string.dialog_cancel, null) + setNegativeButton(CommonString.dialog_cancel, null) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/ColorPickerDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/ColorPickerDialog.kt index ce22d753182b..57d69c09bd2b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/ColorPickerDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/ColorPickerDialog.kt @@ -11,6 +11,7 @@ import android.widget.FrameLayout import androidx.annotation.ColorInt import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.utils.Dp import com.ichi2.utils.dp @@ -80,9 +81,9 @@ fun Context.showColorPickerDialog( this.height = 12.dp.toPx(this@showColorPickerDialog) } } - setTitle(R.string.choose_color) - setPositiveButton(R.string.dialog_ok, onPositiveCallback) - negativeButton(R.string.dialog_cancel) + setTitle(CommonString.choose_color) + setPositiveButton(CommonString.dialog_ok, onPositiveCallback) + negativeButton(CommonString.dialog_cancel) setBottomSpace(12.dp) val callback = object : Choreographer.FrameCallback { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/WhiteboardFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/WhiteboardFragment.kt index e81b7723847c..4315da56bc67 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/WhiteboardFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/whiteboard/WhiteboardFragment.kt @@ -26,6 +26,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.AnkiDroidApp +import com.ichi2.anki.CommonString import com.ichi2.anki.DispatchKeyEventListener import com.ichi2.anki.R import com.ichi2.anki.android.back.doubleBackPressCallback @@ -95,7 +96,7 @@ class WhiteboardFragment : doubleBackCallback = doubleBackPressCallback( enabled = computeDoubleBackEnabled(), - onFirstBack = { showSnackbar(R.string.back_pressed_once, Snackbar.LENGTH_SHORT) }, + onFirstBack = { showSnackbar(CommonString.back_pressed_once, Snackbar.LENGTH_SHORT) }, shouldReEnable = { computeDoubleBackEnabled() }, ).also { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, it) @@ -177,7 +178,7 @@ class WhiteboardFragment : showRemoveColorDialog(index) } else { Timber.i("Tried to remove the last brush of the whiteboard") - showSnackbar(R.string.cannot_remove_last_brush_message) + showSnackbar(CommonString.cannot_remove_last_brush_message) } } @@ -295,11 +296,11 @@ class WhiteboardFragment : private fun showRemoveColorDialog(index: Int) { AlertDialog .Builder(requireContext()) - .setMessage(R.string.whiteboard_remove_brush_message) - .setPositiveButton(R.string.dialog_remove) { dialog, _ -> + .setMessage(CommonString.whiteboard_remove_brush_message) + .setPositiveButton(CommonString.dialog_remove) { dialog, _ -> Timber.i("Removed brush of index %d", index) viewModel.removeBrush(index) - }.setNegativeButton(R.string.dialog_cancel, null) + }.setNegativeButton(CommonString.dialog_cancel, null) .show() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/AndroidUtils.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/AndroidUtils.kt index 3a8024ff1619..d971d5058431 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/AndroidUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/AndroidUtils.kt @@ -18,7 +18,7 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.android.AdaptionUtil import com.ichi2.anki.common.android.appContext import com.ichi2.anki.common.utils.android.showThemedToast @@ -69,7 +69,7 @@ fun Context.openUrl(url: String) { fun Context.openUrl(uri: Uri) { if (!AdaptionUtil.hasWebBrowser(this)) { - val noBrowserMessage = getString(R.string.no_browser_msg, uri.toString()) + val noBrowserMessage = getString(CommonString.no_browser_msg, uri.toString()) if (this is FragmentActivity) { showSnackbar(noBrowserMessage) { setAction(android.R.string.copyUrl) { @@ -86,9 +86,9 @@ fun Context.openUrl(uri: Uri) { } catch (ex: Exception) { Timber.w("No app found to handle opening an external url from ${this::class.java.name}") if (this is FragmentActivity) { - showSnackbar(R.string.activity_start_failed) + showSnackbar(CommonString.activity_start_failed) } else { - showThemedToast(this, R.string.activity_start_failed, false) + showThemedToast(this, CommonString.activity_start_failed, false) } } } @@ -96,14 +96,14 @@ fun Context.openUrl(uri: Uri) { // necessary for Fragments that are BaseSnackbarBuilderProvider to work correctly fun Fragment.openUrl(uri: Uri) { if (!AdaptionUtil.hasWebBrowser(requireContext())) { - showSnackbar(getString(R.string.no_browser_msg, uri.toString())) + showSnackbar(getString(CommonString.no_browser_msg, uri.toString())) return } try { startActivity(Intent(Intent.ACTION_VIEW, uri)) } catch (ex: ActivityNotFoundException) { Timber.w("No app found to handle opening an external url from ${Fragment::class.java.name}") - showSnackbar(R.string.activity_start_failed) + showSnackbar(CommonString.activity_start_failed) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/Exceptions.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/Exceptions.kt index 3ad5c57670e2..9bab44cb242a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/Exceptions.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/Exceptions.kt @@ -15,7 +15,7 @@ package com.ichi2.anki.utils import android.content.Context -import com.ichi2.anki.R +import com.ichi2.anki.CommonString fun interface TranslatableException { fun getTranslatedMessage(context: Context): String @@ -34,7 +34,7 @@ fun Context.getUserFriendlyErrorText(e: Exception): String = e.localizedMessage?.ifBlank { null } ?: e.message?.ifBlank { null } ?: e::class.simpleName?.ifBlank { null } - ?: getString(R.string.error__etc__unknown_error) + ?: getString(CommonString.error__etc__unknown_error) } /** diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/Time.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/Time.kt index be02bfb2976f..500b9270b550 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/Time.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/Time.kt @@ -6,7 +6,7 @@ import android.content.Context import android.media.MediaPlayer import android.os.Handler import android.view.View -import com.ichi2.anki.R +import com.ichi2.anki.CommonPlurals import com.ichi2.anki.common.time.TIME_HOUR import com.ichi2.anki.common.time.TIME_MINUTE import kotlin.math.max @@ -40,7 +40,7 @@ fun remainingTime( (timeSeconds / TIME_MINUTE).roundToInt(), 1, ) - res.getQuantityString(R.plurals.reviewer_window_title, timeX, timeX) + res.getQuantityString(CommonPlurals.reviewer_window_title, timeX, timeX) // It used to be minutes only. So the word "minutes" is not // explicitly written in the resource name. /* @@ -58,7 +58,7 @@ fun remainingTime( remaining = (remainingSeconds.toFloat() / TIME_MINUTE).roundToInt() res.getQuantityString( - R.plurals.reviewer_window_title_hours_new, + CommonPlurals.reviewer_window_title_hours_new, timeX, timeX, remaining, @@ -69,7 +69,7 @@ fun remainingTime( remaining = (remainingSeconds / TIME_HOUR).roundToInt() res.getQuantityString( - R.plurals.reviewer_window_title_days_new, + CommonPlurals.reviewer_window_title_days_new, timeX, timeX, remaining, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt index 2ecfb9aca9ea..146cfa5f5e20 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt @@ -17,6 +17,7 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.ItemDeckBinding import com.ichi2.anki.deckpicker.DisplayDeckNode @@ -208,10 +209,10 @@ class DeckAdapter( expander.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES if (node.collapsed) { expander.setImageDrawable(expandImage) - expander.contentDescription = expander.context.getString(R.string.expand) + expander.contentDescription = expander.context.getString(CommonString.expand) } else { expander.setImageDrawable(collapseImage) - expander.contentDescription = expander.context.getString(R.string.collapse) + expander.contentDescription = expander.context.getString(CommonString.collapse) } } else { // Siblings must perfectly align their text with each other regardless of whether they have children. diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt b/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt index 640e26193785..262f84dac262 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt @@ -6,7 +6,7 @@ import android.app.Activity import android.os.Bundle import android.os.Process import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.exception.ManuallyReportedException import com.ichi2.anki.common.utils.android.showThemedToast @@ -44,7 +44,7 @@ object AppLoadedFromBackupWorkaround { Timber.w("Activity started with no application instance") showThemedToast( this, - getString(R.string.ankidroid_cannot_open_after_backup_try_again), + getString(CommonString.ankidroid_cannot_open_after_backup_try_again), false, ) CrashReportService.sendExceptionReport( diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncMediaWorker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncMediaWorker.kt index f868bac9fcff..549d8d0d9fb3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncMediaWorker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncMediaWorker.kt @@ -26,6 +26,7 @@ import anki.sync.SyncAuth import anki.sync.syncAuth import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.NotificationChannel import com.ichi2.anki.R import com.ichi2.anki.cancelMediaSync @@ -133,7 +134,7 @@ class SyncMediaWorker( } override suspend fun getForegroundInfo(): ForegroundInfo { - val title = applicationContext.getString(R.string.syncing_media) + val title = applicationContext.getString(CommonString.syncing_media) val cancelTitle = TR.syncAbortButton() val notification = buildNotification { @@ -179,7 +180,7 @@ class SyncMediaWorker( }.build() private fun getProgressNotification(progress: CharSequence): Notification { - val title = applicationContext.getString(R.string.syncing_media) + val title = applicationContext.getString(CommonString.syncing_media) val cancelTitle = TR.syncAbortButton() return buildNotification { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncWorker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncWorker.kt index 7adb7776b9a9..ddf554c25035 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncWorker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/worker/SyncWorker.kt @@ -25,6 +25,7 @@ import anki.sync.syncAuth import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.NotificationChannel import com.ichi2.anki.R import com.ichi2.anki.cancelSync @@ -94,7 +95,7 @@ class SyncWorker( } catch (throwable: Throwable) { Timber.w(throwable, "SyncWorker failed") notify { - setContentTitle(applicationContext.getString(R.string.sync_error)) + setContentTitle(applicationContext.getString(CommonString.sync_error)) throwable.localizedMessage?.let { message -> setContentText(message) } @@ -182,7 +183,7 @@ class SyncWorker( } override suspend fun getForegroundInfo(): ForegroundInfo { - val cancelTitle = applicationContext.getString(R.string.dialog_cancel) + val cancelTitle = applicationContext.getString(CommonString.dialog_cancel) val notification = buildNotification { setContentTitle(TR.syncSyncing()) @@ -218,7 +219,7 @@ class SyncWorker( }.build() private fun getProgressNotification(progress: CharSequence): Notification { - val cancelTitle = applicationContext.getString(R.string.dialog_cancel) + val cancelTitle = applicationContext.getString(CommonString.dialog_cancel) return buildNotification { setContentTitle(TR.syncSyncing()) diff --git a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt index f2eba8cb1a76..47a71c76152f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt +++ b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt @@ -15,7 +15,7 @@ import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsServiceConnection import androidx.browser.customtabs.CustomTabsSession import androidx.core.content.pm.PackageInfoCompat -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper.Companion.getPackageInfoCompat import com.ichi2.anki.compat.PackageInfoFlagsCompat @@ -204,7 +204,7 @@ class CustomTabActivityHelper : ServiceConnectionCallback { customTabsIntent.launchUrl(activity, uri) } catch (ex: ActivityNotFoundException) { Timber.w("No app found to handle opening an external url from CustomTabsActivityHelper") - activity.showSnackbar(R.string.activity_start_failed) + activity.showSnackbar(CommonString.activity_start_failed) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt index 723e3d1e1aac..2acd56b6e976 100644 --- a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt +++ b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt @@ -6,7 +6,7 @@ package com.ichi2.compat.customtabs import android.app.Activity import android.content.Intent import android.net.Uri -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.compat.customtabs.CustomTabActivityHelper.CustomTabFallback @@ -28,7 +28,7 @@ class CustomTabsFallback : CustomTabFallback { // and I assume an exported intent will take priority over a non-exported intent. // Add an exception report to see if I'm wrong CrashReportService.sendExceptionReport(e, "CustomTabsFallback::openUri") - showThemedToast(activity, activity.getString(R.string.web_page_error, uri), false) + showThemedToast(activity, activity.getString(CommonString.web_page_error, uri), false) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/compose/ui/dialogs/TextInputDialog.kt b/AnkiDroid/src/main/java/com/ichi2/compose/ui/dialogs/TextInputDialog.kt index bcdb980d9351..640337092f62 100644 --- a/AnkiDroid/src/main/java/com/ichi2/compose/ui/dialogs/TextInputDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/compose/ui/dialogs/TextInputDialog.kt @@ -36,7 +36,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.unit.dp -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.compose.theme.AnkiDroidTheme import com.ichi2.compose.ui.preview.ThemePreviews @@ -58,7 +58,7 @@ fun TextInputDialog( confirmText: String, onConfirm: (String) -> Unit, onDismissRequest: () -> Unit, - dismissText: String = stringResource(R.string.dialog_cancel), + dismissText: String = stringResource(CommonString.dialog_cancel), initialText: String = "", maxLengthCounter: Int? = null, capitalization: KeyboardCapitalization = KeyboardCapitalization.Sentences, diff --git a/AnkiDroid/src/main/java/com/ichi2/imagecropper/ImageCropper.kt b/AnkiDroid/src/main/java/com/ichi2/imagecropper/ImageCropper.kt index 668f79a8dbd3..d5cc191f50df 100644 --- a/AnkiDroid/src/main/java/com/ichi2/imagecropper/ImageCropper.kt +++ b/AnkiDroid/src/main/java/com/ichi2/imagecropper/ImageCropper.kt @@ -37,6 +37,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import com.canhub.cropper.CropImageView +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.FragmentImageCropperBinding import com.ichi2.anki.snackbar.showSnackbar @@ -195,7 +196,7 @@ class ImageCropper : ) { if (error != null) { Timber.e(error, "Failed to load image by URI") - showSnackbar(R.string.something_wrong) + showSnackbar(CommonString.something_wrong) } } @@ -216,7 +217,7 @@ class ImageCropper : activity?.finish() } else { Timber.e(result.error, "Failed to crop image") - showSnackbar(R.string.something_wrong) + showSnackbar(CommonString.something_wrong) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt b/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt index 00d90d0a22d2..8c33d7626ca8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt +++ b/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt @@ -12,6 +12,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.preference.DialogPreference import androidx.preference.PreferenceFragmentCompat +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.DialogControlPreferenceBinding import com.ichi2.anki.dialogs.GestureSelectionDialogUtils @@ -78,7 +79,7 @@ open class ControlPreference : val bindingPreference = getPreferenceAssignedTo(binding) ?: return false if (bindingPreference == this) return false val actionTitle = bindingPreference.title ?: "" - val warning = context.getString(R.string.bindings_already_bound, actionTitle) + val warning = context.getString(CommonString.bindings_already_bound, actionTitle) warningDisplay.setWarning(warning) return true } @@ -103,13 +104,13 @@ open class ControlPreference : setTitle(title) setIcon(icon) val gesturePicker = createGesturePicker() - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { val gesture = gesturePicker.getGesture() ?: return@positiveButton val binding = Binding.GestureInput(gesture) onGestureSelected(binding) it.dismiss() } - negativeButton(R.string.dialog_cancel) { it.dismiss() } + negativeButton(CommonString.dialog_cancel) { it.dismiss() } customView(view = gesturePicker) gesturePicker.onGestureChanged { gesture -> warnIfUsedOrClearWarning(Binding.GestureInput(gesture), gesturePicker) @@ -128,12 +129,12 @@ open class ControlPreference : keyPicker.setBindingChangedListener { binding -> warnIfUsedOrClearWarning(binding, keyPicker) } - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { val binding = keyPicker.getBinding() ?: return@positiveButton onKeySelected(binding) it.dismiss() } - negativeButton(R.string.dialog_cancel) { it.dismiss() } + negativeButton(CommonString.dialog_cancel) { it.dismiss() } keyPicker.setKeycodeValidation(KeySelectionDialogUtils.disallowModifierKeyCodes()) } } @@ -145,20 +146,20 @@ open class ControlPreference : customView(view = axisPicker.binding.root) setTitle(title) setIcon(icon) - negativeButton(R.string.dialog_cancel) { it.dismiss() } + negativeButton(CommonString.dialog_cancel) { it.dismiss() } } axisPicker.setBindingChangedListener { binding -> dialog.dismiss() val bindingPref = getPreferenceAssignedTo(binding) if (bindingPref != null && bindingPref != this) { AlertDialog.Builder(context).show { - val warning = context.getString(R.string.bindings_already_bound, bindingPref.title) + val warning = context.getString(CommonString.bindings_already_bound, bindingPref.title) setTitle(binding.toDisplayString(context)) setMessage(warning) - setPositiveButton(R.string.dialog_positive_replace) { _, _ -> + setPositiveButton(CommonString.dialog_positive_replace) { _, _ -> onAxisSelected(binding) } - setNegativeButton(R.string.dialog_cancel) { _, _ -> } + setNegativeButton(CommonString.dialog_cancel) { _, _ -> } } } else { onAxisSelected(binding) @@ -234,7 +235,7 @@ open class ControlPreferenceDialogFragment : DialogFragment() { setTitle(preference.title) setIcon(preference.icon) customView(binding.root, paddingTop = 16.dp.toPx(context)) - negativeButton(R.string.dialog_cancel) + negativeButton(CommonString.dialog_cancel) } } @@ -266,7 +267,7 @@ open class ControlPreferenceDialogFragment : DialogFragment() { } val titles = bindings.map { - getString(R.string.binding_remove_binding, getDisplayString(it)) + getString(CommonString.binding_remove_binding, getDisplayString(it)) } binding.listView.apply { adapter = ArrayAdapter(requireContext(), R.layout.item_control_preference, titles) diff --git a/AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt b/AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt index fb742c1ad785..2b004719075c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt +++ b/AnkiDroid/src/main/java/com/ichi2/preferences/ExternalDirectorySelectionPreference.kt @@ -15,6 +15,7 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.preference.ListPreference import androidx.preference.ListPreferenceDialogFragmentCompat +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.crashreporting.runCatchingWithLog import com.ichi2.anki.common.utils.android.showThemedToast @@ -42,7 +43,7 @@ class ExternalDirectorySelectionPreference( init { summaryProvider = SummaryProvider { pref -> - pref.value.takeUnless { it.isNullOrEmpty() } ?: context.getString(R.string.pref_directory_not_set) + pref.value.takeUnless { it.isNullOrEmpty() } ?: context.getString(CommonString.pref_directory_not_set) } } @@ -137,7 +138,7 @@ class ExternalDirectorySelectionPreference( class FullWidthListPreferenceDialogFragment : ListPreferenceDialogFragmentCompat() { override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) { super.onPrepareDialogBuilder(builder) - builder.setNeutralButton(R.string.pref_custom_path) { _, _ -> showCustomPathInput() } + builder.setNeutralButton(CommonString.pref_custom_path) { _, _ -> showCustomPathInput() } } private fun showCustomPathInput() { @@ -146,7 +147,7 @@ class FullWidthListPreferenceDialogFragment : ListPreferenceDialogFragmentCompat AlertDialog .Builder(context) .show { - setTitle(R.string.pref_enter_custom_path) + setTitle(CommonString.pref_enter_custom_path) setView(R.layout.dialog_generic_text_input) positiveButton(android.R.string.ok) negativeButton(android.R.string.cancel) @@ -162,7 +163,7 @@ class FullWidthListPreferenceDialogFragment : ListPreferenceDialogFragmentCompat if (!Files.isWritable(pathObj)) { showThemedToast( context, - context.getString(R.string.pref_directory_not_writable), + context.getString(CommonString.pref_directory_not_writable), true, ) return@input @@ -174,7 +175,7 @@ class FullWidthListPreferenceDialogFragment : ListPreferenceDialogFragmentCompat } catch (e: Exception) { Timber.w(e, "Failed to set custom path") AlertDialog.Builder(context).show { - setTitle(context.getString(R.string.could_not_create_dir, text.toString())) + setTitle(context.getString(CommonString.could_not_create_dir, text.toString())) setMessage(e.stackTraceToString()) positiveButton(android.R.string.ok) } diff --git a/AnkiDroid/src/main/java/com/ichi2/preferences/IncrementerNumberRangePreferenceCompat.kt b/AnkiDroid/src/main/java/com/ichi2/preferences/IncrementerNumberRangePreferenceCompat.kt index 724ef2b13b63..2f1b5ba0e4ca 100644 --- a/AnkiDroid/src/main/java/com/ichi2/preferences/IncrementerNumberRangePreferenceCompat.kt +++ b/AnkiDroid/src/main/java/com/ichi2/preferences/IncrementerNumberRangePreferenceCompat.kt @@ -23,7 +23,7 @@ import android.view.View import android.widget.Button import androidx.core.widget.doAfterTextChanged import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.databinding.DialogIncrementerPreferenceBinding import com.ichi2.anki.ui.NonLeadingZeroInputFilter import com.ichi2.utils.moveCursorToEnd @@ -160,14 +160,14 @@ class IncrementerNumberRangePreferenceCompat : } ValidationResult.OVERFLOW -> { binding.decrementButton.isEnabled = true - binding.textInputLayout.error = getString(R.string.maximum_value_is, numberRangePreference.max) + binding.textInputLayout.error = getString(CommonString.maximum_value_is, numberRangePreference.max) } ValidationResult.UNDERFLOW -> { binding.incrementButton.isEnabled = true - binding.textInputLayout.error = getString(R.string.minimum_value_is, numberRangePreference.min) + binding.textInputLayout.error = getString(CommonString.minimum_value_is, numberRangePreference.min) } ValidationResult.INVALID -> { - binding.textInputLayout.error = getString(R.string.invalid_value) + binding.textInputLayout.error = getString(CommonString.invalid_value) } ValidationResult.EMPTY -> { // Empty input is invalid for submission, but doesn't warrant an error message yet diff --git a/AnkiDroid/src/main/java/com/ichi2/ui/AccessibleSearchView.kt b/AnkiDroid/src/main/java/com/ichi2/ui/AccessibleSearchView.kt index ca4ca7ce9bc1..5936b87a1df4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/ui/AccessibleSearchView.kt +++ b/AnkiDroid/src/main/java/com/ichi2/ui/AccessibleSearchView.kt @@ -19,7 +19,7 @@ package com.ichi2.ui import android.content.Context import android.util.AttributeSet import android.widget.ImageView -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.compat.setTooltipTextCompat /** @@ -35,7 +35,7 @@ open class AccessibleSearchView : androidx.appcompat.widget.SearchView { init { // close_btn is the cross that deletes the search field content. It does not close the search view. findViewById(androidx.appcompat.R.id.search_close_btn) - ?.setTooltipTextCompat(context.getString(R.string.discard)) + ?.setTooltipTextCompat(context.getString(CommonString.discard)) } // SearchView contains four buttons. The three others seems never to appear in ankidroid. // there is also an arrow to the trailing side, that should get a tooltip. Alas, I fail to see the id of this button, so I can't add it. diff --git a/AnkiDroid/src/main/java/com/ichi2/ui/GesturePicker.kt b/AnkiDroid/src/main/java/com/ichi2/ui/GesturePicker.kt index 20398b26aa1c..71f4d8b64592 100644 --- a/AnkiDroid/src/main/java/com/ichi2/ui/GesturePicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/ui/GesturePicker.kt @@ -12,7 +12,7 @@ import android.widget.ArrayAdapter import android.widget.Spinner import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.cardviewer.Gesture import com.ichi2.anki.cardviewer.GestureListener import com.ichi2.anki.databinding.ViewGesturePickerBinding @@ -80,7 +80,7 @@ open class GesturePicker( inner class GestureWrapper( val gesture: Gesture?, ) { - override fun toString(): String = gesture?.toDisplayString(context) ?: resources.getString(R.string.gestures_none) + override fun toString(): String = gesture?.toDisplayString(context) ?: resources.getString(CommonString.gestures_none) override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/AlarmManagement.kt b/AnkiDroid/src/main/java/com/ichi2/utils/AlarmManagement.kt index 42453070cd89..a733b4b43141 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/AlarmManagement.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/AlarmManagement.kt @@ -7,7 +7,7 @@ import android.app.AlarmManager import android.content.Context import androidx.annotation.StringRes import androidx.core.content.getSystemService -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.anki.reviewreminders.ReviewReminderAlarmManager import timber.log.Timber @@ -49,10 +49,10 @@ object AlarmManagement { } catch (ex: SecurityException) { // #6332 - Too Many Alarms on Samsung Devices - this stops a fatal startup crash. // We warn the user if they breach this limit - error = R.string.boot_service_too_many_notifications + error = CommonString.boot_service_too_many_notifications errorHandler(ex, "Too many alarms set") } catch (e: Exception) { - error = R.string.boot_service_failed_to_schedule_notifications + error = CommonString.boot_service_failed_to_schedule_notifications errorHandler(e, "Failed to schedule alarm") } if (error != null && showToastOnFailure) { diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/AlertDialogFacade.kt b/AnkiDroid/src/main/java/com/ichi2/utils/AlertDialogFacade.kt index d1c5c284d42f..1446d8df72de 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/AlertDialogFacade.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/AlertDialogFacade.kt @@ -469,7 +469,7 @@ fun AlertDialog.Builder.listItemsAndMessage( * **Example:** * ```kotlin * MaterialAlertDialogBuilder(context).create { - * titleWithHelpIcon(stringRes = R.string.reset_card_dialog_title) { + * titleWithHelpIcon(stringRes = CommonString.reset_card_dialog_title) { * requireActivity().openUrl(Uri.parse(getString(R.string.link_manual))) * } * } diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/ClipboardUtil.kt b/AnkiDroid/src/main/java/com/ichi2/utils/ClipboardUtil.kt index 27d831c2bbf6..ea9511cf4617 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/ClipboardUtil.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/ClipboardUtil.kt @@ -14,7 +14,7 @@ import androidx.annotation.CheckResult import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting import androidx.core.content.getSystemService -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.utils.android.showThemedToast import com.ichi2.anki.snackbar.canProperlyShowSnackbars import com.ichi2.anki.snackbar.showSnackbar @@ -118,8 +118,8 @@ value class TruncatedString private constructor( */ fun Context.copyToClipboard( text: TruncatedString, - @StringRes successMessageId: Int = R.string.about_ankidroid_successfully_copied_debug_info, - @StringRes failureMessageId: Int = R.string.failed_to_copy, + @StringRes successMessageId: Int = CommonString.about_ankidroid_successfully_copied_debug_info, + @StringRes failureMessageId: Int = CommonString.failed_to_copy, ): Boolean { val copied = copyTextToClipboard(text) // in Android S_V2 and above, the system is guaranteed to show a message on a successful copy diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt b/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt index c4a3f06580ea..f62214ff7a5a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt @@ -4,7 +4,7 @@ package com.ichi2.utils import android.content.Context import androidx.annotation.CheckResult -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.utils.android.showThemedToast import java.io.PrintWriter @@ -51,7 +51,7 @@ object ExceptionUtil { CrashReportService.sendExceptionReport(e, origin) showThemedToast( context, - context.getString(R.string.multimedia_editor_something_wrong), + context.getString(CommonString.multimedia_editor_something_wrong), true, ) } diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt b/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt index 9c0efe8b7049..0afc66b6aadb 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt @@ -17,6 +17,7 @@ import androidx.appcompat.app.AlertDialog import com.ichi2.anki.AnkiActivity import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.android.appContext import com.ichi2.anki.common.annotations.NeedsTest @@ -135,7 +136,7 @@ object ImportUtils { CrashReportService.sendExceptionReport(e, "handleFileImport") Timber.e(e, "failed to handle import intent") ImportResult.Failure( - humanReadableMessage = context.getString(R.string.import_error_handle_exception, e.localizedMessage), + humanReadableMessage = context.getString(CommonString.import_error_handle_exception, e.localizedMessage), exception = e, ) } @@ -149,7 +150,7 @@ object ImportUtils { return if (importPathUri != null) { handleContentProviderFile(context, importPathUri, intent) } else { - ImportResult.Failure(context.getString(R.string.import_error_handle_exception)) + ImportResult.Failure(context.getString(CommonString.import_error_handle_exception)) } } @@ -194,7 +195,7 @@ object ImportUtils { ): ImportResult { // Note: intent.getData() can be null. Use data instead. if (!isValidImportType(context, importPathUri)) { - return ImportResult.Failure(context.getString(R.string.import_log_no_apkg)) + return ImportResult.Failure(context.getString(CommonString.import_log_no_apkg)) } // Get the original filename from the content provider URI var filename = getFileNameFromContentProvider(context, importPathUri) @@ -216,7 +217,7 @@ object ImportUtils { ) return ImportResult.Failure( AnkiDroidApp.appResources.getString( - R.string.import_error_content_provider, + CommonString.import_error_content_provider, AnkiDroidApp.manualUrl + "#importing", ), ) @@ -230,10 +231,10 @@ object ImportUtils { return if (isAnkiDatabase(filename)) { // .anki2 files aren't supported by Anki Desktop, we should eventually support them, because we can // but for now, show a "nice" error. - ImportResult.Failure(context.resources.getString(R.string.import_error_load_imported_database)) + ImportResult.Failure(context.resources.getString(CommonString.import_error_load_imported_database)) } else { // Don't import if file doesn't have an Anki package extension - ImportResult.Failure(context.resources.getString(R.string.import_error_not_apkg_extension, filename)) + ImportResult.Failure(context.resources.getString(CommonString.import_error_not_apkg_extension, filename)) } } @@ -245,11 +246,11 @@ object ImportUtils { } catch (_: SecurityException) { // Do not log the exception: it may interpolate attacker-controlled paths (PII). Timber.e("Path traversal detected in handleContentProviderFile") - return ImportResult.Failure(context.getString(R.string.import_error_handle_exception, "Invalid path")) + return ImportResult.Failure(context.getString(CommonString.import_error_handle_exception, "Invalid path")) } catch (_: IllegalArgumentException) { // Do not log the exception: it may interpolate attacker-controlled paths (PII). Timber.e("Path traversal detected in handleContentProviderFile") - return ImportResult.Failure(context.getString(R.string.import_error_handle_exception, "Invalid path")) + return ImportResult.Failure(context.getString(CommonString.import_error_handle_exception, "Invalid path")) } val tempOutDir: String = Uri.fromFile(checkFile).encodedPath!! @@ -371,13 +372,13 @@ object ImportUtils { } Timber.d("showImportUnsuccessfulDialog() message %s", failure.humanReadableMessage) - val title = failure.title ?: activity.getString(R.string.import_title_error) + val title = failure.title ?: activity.getString(CommonString.import_title_error) val dialog = AlertDialog.Builder(activity).show { title(text = title) message(text = failure.humanReadableMessage) setCancelable(false) - positiveButton(R.string.dialog_ok) { + positiveButton(CommonString.dialog_ok) { if (exitActivity) { activity.finish() } @@ -475,7 +476,7 @@ object ImportUtils { val exceptionForReport: Exception?, val userFacingException: Exception?, ) { - fun buildTitle(context: Context) = context.getString(R.string.import_error_copy_to_cache_title) + fun buildTitle(context: Context) = context.getString(CommonString.import_error_copy_to_cache_title) fun buildHumanReadableMessage(context: Context) = buildString { @@ -495,7 +496,7 @@ object ImportUtils { appendLine("\u2022 $line") } } - append(context.getString(R.string.import_error_copy_to_cache_explanation, suggestedFixes)) + append(context.getString(CommonString.import_error_copy_to_cache_explanation, suggestedFixes)) } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/IntentUtil.kt b/AnkiDroid/src/main/java/com/ichi2/utils/IntentUtil.kt index 7ac11bd5f36a..c48adf3d0a23 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/IntentUtil.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/IntentUtil.kt @@ -7,7 +7,7 @@ import android.content.Intent import android.webkit.MimeTypeMap import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.AnkiActivity -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.snackbar.showSnackbar import timber.log.Timber @@ -63,12 +63,12 @@ object IntentUtil { if (canOpenIntent(activity, intent)) { activity.startActivity(intent) } else { - val errorMsg = activity.getString(R.string.feedback_no_suitable_app_found) + val errorMsg = activity.getString(CommonString.feedback_no_suitable_app_found) activity.showSnackbar(errorMsg, Snackbar.LENGTH_SHORT) } } catch (e: Exception) { Timber.w(e) - val errorMsg = activity.getString(R.string.feedback_no_suitable_app_found) + val errorMsg = activity.getString(CommonString.feedback_no_suitable_app_found) activity.showSnackbar(errorMsg, Snackbar.LENGTH_SHORT) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/LanguageUtil.kt b/AnkiDroid/src/main/java/com/ichi2/utils/LanguageUtil.kt index 0ebf5bfc83c8..b76722010e02 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/LanguageUtil.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/LanguageUtil.kt @@ -8,7 +8,7 @@ import android.content.res.Resources import androidx.annotation.StringRes import androidx.core.os.ConfigurationCompat import androidx.fragment.app.Fragment -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.android.appContext import com.ichi2.anki.common.preferences.sharedPrefs import com.ichi2.anki.compat.CompatHelper @@ -361,7 +361,7 @@ object LanguageUtil { * Uses ListFormatter on API 26+ to dynamically get the locale-specific separator */ fun getListSeparator(context: Context): String = - CompatHelper.compat.getListSeparator(context, context.getString(R.string.list_separator)) + CompatHelper.compat.getListSeparator(context, context.getString(CommonString.list_separator)) /** * Wraps the provided string in Unicode isolation markers (FSI and PDI) to ensure bidirectional markers diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/Permissions.kt b/AnkiDroid/src/main/java/com/ichi2/utils/Permissions.kt index a62b808da68f..fac9135b3903 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/Permissions.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/Permissions.kt @@ -17,9 +17,9 @@ import androidx.core.app.ActivityCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager +import com.ichi2.anki.CommonString import com.ichi2.anki.NotificationChannel import com.ichi2.anki.OptionalPermissionSet -import com.ichi2.anki.R import com.ichi2.anki.common.permissions.LEGACY_POST_NOTIFICATIONS import com.ichi2.anki.common.permissions.MANAGE_EXTERNAL_STORAGE import com.ichi2.anki.common.permissions.canPostNotifications @@ -86,7 +86,7 @@ object Permissions { permissionRequestedFlag.setter.call(true) permissionRequestLauncher.launch(permission) } else { - showToastAndOpenAppSettingsScreenForPermission(permission, R.string.manually_grant_permissions) + showToastAndOpenAppSettingsScreenForPermission(permission, CommonString.manually_grant_permissions) } } @@ -104,7 +104,7 @@ object Permissions { permissionRequestLauncher = notificationPermissionLauncher, ) } else { - showToastAndOpenAppSettingsScreenForPermission(LEGACY_POST_NOTIFICATIONS, R.string.manually_grant_permissions) + showToastAndOpenAppSettingsScreenForPermission(LEGACY_POST_NOTIFICATIONS, CommonString.manually_grant_permissions) } } @@ -273,7 +273,7 @@ object Permissions { ) } catch (e: ActivityNotFoundException) { Timber.w(e, "No app can show the app settings screen") - showThemedToast(this, R.string.activity_start_failed, false) + showThemedToast(this, CommonString.activity_start_failed, false) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt b/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt index bdd9ce9262b7..d20156f4fa23 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt @@ -14,6 +14,7 @@ import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AlertDialog import androidx.core.content.pm.PackageInfoCompat import androidx.webkit.WebViewCompat +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.utils.openUrl @@ -175,8 +176,8 @@ private fun showOutdatedWebViewDialog( onDismiss: () -> Unit = {}, ) { AlertDialog.Builder(context).show { - setMessage(context.getString(R.string.webview_update_message, installedVersion, OLDEST_WORKING_WEBVIEW_VERSION.value)) - setPositiveButton(R.string.scoped_storage_learn_more) { _, _ -> + setMessage(context.getString(CommonString.webview_update_message, installedVersion, OLDEST_WORKING_WEBVIEW_VERSION.value)) + setPositiveButton(CommonString.scoped_storage_learn_more) { _, _ -> context.openUrl(learnMoreUrl) } setOnDismissListener { onDismiss() } diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/AnkiDroidWidgetSmall.kt b/AnkiDroid/src/main/java/com/ichi2/widget/AnkiDroidWidgetSmall.kt index 848d9f3d3518..917563f462eb 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/AnkiDroidWidgetSmall.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/AnkiDroidWidgetSmall.kt @@ -20,6 +20,7 @@ import androidx.annotation.LayoutRes import androidx.core.app.PendingIntentCompat import androidx.core.content.ContextCompat import androidx.core.content.edit +import com.ichi2.anki.CommonPlurals import com.ichi2.anki.R import com.ichi2.anki.common.android.appContext import com.ichi2.anki.common.destinations.DeferredNavigation @@ -147,7 +148,7 @@ class AnkiDroidWidgetSmall : AnalyticsWidgetProvider() { updateViews.setTextViewText(R.id.widget_due, dueCardsCount.toString()) updateViews.setContentDescription( R.id.widget_due, - context.resources.getQuantityString(R.plurals.widget_cards_due, dueCardsCount, dueCardsCount), + context.resources.getQuantityString(CommonPlurals.widget_cards_due, dueCardsCount, dueCardsCount), ) if (eta <= 0) { @@ -158,7 +159,7 @@ class AnkiDroidWidgetSmall : AnalyticsWidgetProvider() { updateViews.setTextViewText(R.id.widget_eta, etaText) updateViews.setContentDescription( R.id.widget_eta, - context.resources.getQuantityString(R.plurals.widget_eta, eta, eta), + context.resources.getQuantityString(CommonPlurals.widget_eta, eta, eta), ) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt b/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt index 3997e058f87f..8e65a91109e2 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt @@ -11,6 +11,7 @@ import android.content.Context import android.content.Intent import android.view.View import android.widget.RemoteViews +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.coroutines.applicationScope import com.ichi2.anki.common.crashreporting.CrashReportService @@ -110,7 +111,7 @@ class CardAnalysisWidget : AnalyticsWidgetProvider() { appWidgetId: AppWidgetId, remoteViews: RemoteViews, ) { - remoteViews.setTextViewText(R.id.empty_widget, context.getString(R.string.empty_collection_state_in_widget)) + remoteViews.setTextViewText(R.id.empty_widget, context.getString(CommonString.empty_collection_state_in_widget)) remoteViews.setViewVisibility(R.id.empty_widget, View.VISIBLE) remoteViews.setViewVisibility(R.id.cardAnalysisDataHolder, View.GONE) remoteViews.setViewVisibility(R.id.deckNameCardAnalysis, View.GONE) @@ -139,7 +140,7 @@ class CardAnalysisWidget : AnalyticsWidgetProvider() { remoteViews: RemoteViews, ) { // Show empty_widget and set click listener to open configuration - remoteViews.setTextViewText(R.id.empty_widget, context.getString(R.string.empty_widget_state)) + remoteViews.setTextViewText(R.id.empty_widget, context.getString(CommonString.empty_widget_state)) remoteViews.setViewVisibility(R.id.empty_widget, View.VISIBLE) remoteViews.setViewVisibility(R.id.cardAnalysisDataHolder, View.GONE) remoteViews.setViewVisibility(R.id.deckNameCardAnalysis, View.GONE) diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidgetConfig.kt b/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidgetConfig.kt index aa8e8d7be1ec..1f4722ecadec 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidgetConfig.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidgetConfig.kt @@ -19,6 +19,7 @@ import androidx.core.view.WindowInsetsCompat.Type.systemBars import androidx.core.view.updateMargins import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.android.AnkiBroadcastReceiver import com.ichi2.anki.common.utils.android.showThemedToast @@ -118,7 +119,7 @@ class CardAnalysisWidgetConfig : AnkiActivity(R.layout.activity_card_analysis_wi private fun onDeckSelected(deck: SelectableDeck?) { if (deck == null || deck !is SelectableDeck.Deck?) { - showThemedToast(this, R.string.something_wrong, false) + showThemedToast(this, CommonString.something_wrong, false) setResult(RESULT_CANCELED) finish() return @@ -142,7 +143,7 @@ class CardAnalysisWidgetConfig : AnkiActivity(R.layout.activity_card_analysis_wi Timber.w("CardAnalysisWidgetConfig: collection is empty") showThemedToast( this@CardAnalysisWidgetConfig, - R.string.no_cards_placeholder_title, + CommonString.no_cards_placeholder_title, false, ) finish() @@ -161,7 +162,7 @@ class CardAnalysisWidgetConfig : AnkiActivity(R.layout.activity_card_analysis_wi private fun showDeckSelectionDialog() { startDeckSelection( - title = getString(R.string.select_deck_title), + title = getString(CommonString.select_deck_title), allowAll = false, skipEmptyDefault = true, ) diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt b/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt index ece3b92dcfd7..a5dcb0910079 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt @@ -12,6 +12,7 @@ import android.content.Intent import android.view.View import android.widget.RemoteViews import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.coroutines.applicationScope import com.ichi2.anki.common.crashreporting.CrashReportService @@ -159,7 +160,7 @@ class DeckPickerWidget : AnalyticsWidgetProvider() { appWidgetId: AppWidgetId, remoteViews: RemoteViews, ) { - remoteViews.setTextViewText(R.id.empty_widget, context.getString(R.string.empty_collection_state_in_widget)) + remoteViews.setTextViewText(R.id.empty_widget, context.getString(CommonString.empty_collection_state_in_widget)) remoteViews.setViewVisibility(R.id.empty_widget, View.VISIBLE) remoteViews.setViewVisibility(R.id.deckCollection, View.GONE) @@ -186,7 +187,7 @@ class DeckPickerWidget : AnalyticsWidgetProvider() { appWidgetId: AppWidgetId, remoteViews: RemoteViews, ) { - remoteViews.setTextViewText(R.id.empty_widget, context.getString(R.string.empty_widget_state)) + remoteViews.setTextViewText(R.id.empty_widget, context.getString(CommonString.empty_widget_state)) remoteViews.setViewVisibility(R.id.empty_widget, View.VISIBLE) remoteViews.setViewVisibility(R.id.deckCollection, View.GONE) diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidgetConfig.kt b/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidgetConfig.kt index b5c81420b804..0cea9c030a18 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidgetConfig.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidgetConfig.kt @@ -27,6 +27,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.snackbar.Snackbar import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.CommonPlurals +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.common.android.AnkiBroadcastReceiver import com.ichi2.anki.common.utils.android.showThemedToast @@ -121,7 +123,7 @@ class DeckPickerWidgetConfig : Timber.w("Closing: Collection is empty") showThemedToast( this@DeckPickerWidgetConfig, - R.string.empty_collection_state_in_widget, + CommonString.empty_collection_state_in_widget, false, ) finish() @@ -154,7 +156,7 @@ class DeckPickerWidgetConfig : // anchors to it instead of the Save button while the async // updateFabVisibility() is still in flight. binding.fabWidgetDeckPicker.isVisible = true - showSnackbar(R.string.deck_removed_from_widget) + showSnackbar(CommonString.deck_removed_from_widget) updateViewVisibility() updateFabVisibility() updateDoneButtonVisibility() @@ -174,7 +176,7 @@ class DeckPickerWidgetConfig : binding.fabWidgetDeckPicker.setOnClickListener { // TODO previous code filtered the already selected decks, we guard against this for now startDeckSelection( - title = getString(R.string.select_decks_title), + title = getString(CommonString.select_decks_title), allowAll = false, skipEmptyDefault = true, allowMultipleSelection = true, @@ -257,7 +259,7 @@ class DeckPickerWidgetConfig : * and the activity is finished. */ private fun setupDoneButton() { - val saveText = getString(R.string.save).uppercase() + val saveText = getString(CommonString.save).uppercase() // Set the button text and click listener only once during initialization binding.submitButton.text = saveText @@ -351,7 +353,7 @@ class DeckPickerWidgetConfig : val isDeckAlreadySelected = deckAdapter.deckIds.contains(deck.deckId) if (isDeckAlreadySelected) { - showSnackbar(getString(R.string.deck_already_selected_message)) + showSnackbar(getString(CommonString.deck_already_selected_message)) return } @@ -359,7 +361,7 @@ class DeckPickerWidgetConfig : if (deckAdapter.itemCount >= MAX_DECKS_ALLOWED) { // Snackbar will only be shown when adding the 5th deck if (deckAdapter.itemCount == MAX_DECKS_ALLOWED) { - showSnackbar(resources.getQuantityString(R.plurals.deck_limit_reached, MAX_DECKS_ALLOWED, MAX_DECKS_ALLOWED)) + showSnackbar(resources.getQuantityString(CommonPlurals.deck_limit_reached, MAX_DECKS_ALLOWED, MAX_DECKS_ALLOWED)) } // The FAB visibility should be handled in updateFabVisibility() return @@ -373,7 +375,7 @@ class DeckPickerWidgetConfig : setUnsavedChanges(true) // Show snackbar and dismiss the selection dialog once the 5th deck is reached if (deckAdapter.itemCount == MAX_DECKS_ALLOWED) { - showSnackbar(resources.getQuantityString(R.plurals.deck_limit_reached, MAX_DECKS_ALLOWED, MAX_DECKS_ALLOWED)) + showSnackbar(resources.getQuantityString(CommonPlurals.deck_limit_reached, MAX_DECKS_ALLOWED, MAX_DECKS_ALLOWED)) dismissDeckSelectionDialog() } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/CardTemplateEditorTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/CardTemplateEditorTest.kt index 1466d178d9a7..c47a942a5ce6 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/CardTemplateEditorTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/CardTemplateEditorTest.kt @@ -225,7 +225,7 @@ class CardTemplateEditorTest : RobolectricTest() { advanceRobolectricLooper() assertEquals( "Did not show dialog about deleting only card?", - getResourceString(R.string.card_template_editor_cant_delete), + getResourceString(CommonString.card_template_editor_cant_delete), getAlertDialogText(true), ) assertEquals( @@ -373,7 +373,7 @@ class CardTemplateEditorTest : RobolectricTest() { advanceRobolectricLooper() assertEquals( "Did not show dialog about deleting only card?", - getResourceString(R.string.orphan_note_message), + getResourceString(CommonString.orphan_note_message), getAlertDialogText(true), ) clickAlertDialogButton(DialogInterface.BUTTON_POSITIVE, true) @@ -459,7 +459,7 @@ class CardTemplateEditorTest : RobolectricTest() { advanceRobolectricLooper() assertEquals( "Did not show dialog about deleting template and it's card?", - getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 1"), + getQuantityString(CommonPlurals.card_template_editor_confirm_delete, 1, 1, "Card 1"), getAlertDialogText(true), ) clickAlertDialogButton(DialogInterface.BUTTON_NEGATIVE, true) @@ -539,7 +539,7 @@ class CardTemplateEditorTest : RobolectricTest() { advanceRobolectricLooper() assertEquals( "Did not show dialog about deleting template and it's card?", - getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 1"), + getQuantityString(CommonPlurals.card_template_editor_confirm_delete, 1, 1, "Card 1"), getAlertDialogText(true), ) clickAlertDialogButton(DialogInterface.BUTTON_POSITIVE, true) @@ -550,7 +550,7 @@ class CardTemplateEditorTest : RobolectricTest() { advanceRobolectricLooper() assertEquals( "Did not show dialog about deleting template and it's card?", - getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 2"), + getQuantityString(CommonPlurals.card_template_editor_confirm_delete, 1, 1, "Card 2"), getAlertDialogText(true), ) clickAlertDialogButton(DialogInterface.BUTTON_POSITIVE, true) @@ -641,7 +641,7 @@ class CardTemplateEditorTest : RobolectricTest() { advanceRobolectricLooper() assertEquals( "Did not show dialog about deleting template and it's card?", - getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 2"), + getQuantityString(CommonPlurals.card_template_editor_confirm_delete, 1, 1, "Card 2"), getAlertDialogText(true), ) clickAlertDialogButton(DialogInterface.BUTTON_POSITIVE, true) @@ -675,7 +675,7 @@ class CardTemplateEditorTest : RobolectricTest() { advanceRobolectricLooper() assertEquals( "Did not show dialog about deleting template and it's card?", - getQuantityString(R.plurals.card_template_editor_confirm_delete, 0, 0, CollectionManager.TR.cardTemplatesCard(2)), + getQuantityString(CommonPlurals.card_template_editor_confirm_delete, 0, 0, CollectionManager.TR.cardTemplatesCard(2)), getAlertDialogText(true), ) clickAlertDialogButton(DialogInterface.BUTTON_POSITIVE, true) @@ -1158,7 +1158,7 @@ class CardTemplateEditorTest : RobolectricTest() { } assertEquals( "Did not show dialog about adding template and it's card?", - getQuantityString(R.plurals.card_template_editor_confirm_add, numAffectedCards, numAffectedCards), + getQuantityString(CommonPlurals.card_template_editor_confirm_add, numAffectedCards, numAffectedCards), getAlertDialogText(true), ) clickAlertDialogButton(DialogInterface.BUTTON_POSITIVE, true) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt index e8d4d6cfa3fb..05139fd36fba 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt @@ -932,14 +932,14 @@ class DeckPickerTest : RobolectricTest() { advanceRobolectricLooper() val binding = floatingActionButtonBinding - assertThat(binding.fabMain.text.toString(), equalTo(getString(R.string.menu_add))) + assertThat(binding.fabMain.text.toString(), equalTo(getString(CommonString.menu_add))) assertThat( binding.addSharedButton.text.toString(), - equalTo(getString(R.string.menu_get_shared_decks)), + equalTo(getString(CommonString.menu_get_shared_decks)), ) assertThat( binding.addFilteredDeckButton.text.toString(), - equalTo(getString(R.string.new_dynamic_deck)), + equalTo(getString(CommonString.new_dynamic_deck)), ) // 'Create deck' uses a backend string rather than an android:text resource assertThat( @@ -1263,13 +1263,13 @@ class DeckPickerTest : RobolectricTest() { .build() ViewCompat.performReceiveContent(findViewById(R.id.pull_to_sync_wrapper), payload) - val snackbar = showSnackbar(getString(R.string.import_log_no_apkg)) + val snackbar = showSnackbar(getString(CommonString.import_log_no_apkg)) assertThat("snackbar must be shown for unsupported file drop", snackbar, notNullValue()) val snackbarText = snackbar?.text assertThat( snackbarText, - equalTo(getString(R.string.import_log_no_apkg)), + equalTo(getString(CommonString.import_log_no_apkg)), ) } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt index 2a3c83e8ce94..7c5b153bb1cb 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt @@ -211,7 +211,7 @@ class NoteEditorTest : RobolectricTest() { // .withThirdField("C") // .build() // val actualResourceId = noteEditor.addNoteErrorResource -// assertThat(actualResourceId, equalTo(R.string.note_editor_no_cards_created_all_fields)) +// assertThat(actualResourceId, equalTo(CommonString.note_editor_no_cards_created_all_fields)) // } // // @Test @@ -222,7 +222,7 @@ class NoteEditorTest : RobolectricTest() { // .withThirdField("C") // .build() // val actualResourceId = noteEditor.addNoteErrorResource -// assertThat(actualResourceId, equalTo(R.string.note_editor_no_cards_created)) +// assertThat(actualResourceId, equalTo(CommonString.note_editor_no_cards_created)) // } @Test @@ -261,7 +261,7 @@ class NoteEditorTest : RobolectricTest() { .build() noteEditor.saveNote() val actualResourceId = noteEditor.snackbarErrorText - assertThat(actualResourceId, equalTo(getString(R.string.note_editor_no_cards_created))) + assertThat(actualResourceId, equalTo(getString(CommonString.note_editor_no_cards_created))) } @Test diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt index 978cd4f8854d..16b09a839a7d 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/SyncTest.kt @@ -65,11 +65,11 @@ class SyncTest : RobolectricTest() { assertTrue(supportFragmentManager.fragments.any { it is SyncErrorDialog }) assertEquals( - getString(R.string.sync_clock_off_with_current_time, exception.localizedMessage, currentTime), + getString(CommonString.sync_clock_off_with_current_time, exception.localizedMessage, currentTime), dialog.findViewById(android.R.id.message)?.text?.toString(), ) - assertEquals(getString(R.string.dialog_cancel), dialog.getButton(DialogInterface.BUTTON_NEGATIVE).text) - assertEquals(getString(R.string.open_settings), dialog.getButton(DialogInterface.BUTTON_POSITIVE).text) + assertEquals(getString(CommonString.dialog_cancel), dialog.getButton(DialogInterface.BUTTON_NEGATIVE).text) + assertEquals(getString(CommonString.open_settings), dialog.getButton(DialogInterface.BUTTON_POSITIVE).text) assertEquals(View.GONE, dialog.getButton(DialogInterface.BUTTON_NEUTRAL).visibility) assertEquals("test", Prefs.hkey) @@ -121,7 +121,7 @@ class SyncTest : RobolectricTest() { val dialog = failSync(BackendSyncException(backendError { this.message = message })) assertEquals(message, dialog.findViewById(android.R.id.message)?.text?.toString()) - assertEquals(getString(R.string.dialog_ok), dialog.getButton(DialogInterface.BUTTON_POSITIVE).text) + assertEquals(getString(CommonString.dialog_ok), dialog.getButton(DialogInterface.BUTTON_POSITIVE).text) assertEquals(View.GONE, dialog.getButton(DialogInterface.BUTTON_NEGATIVE).visibility) assertEquals(View.GONE, dialog.getButton(DialogInterface.BUTTON_NEUTRAL).visibility) assertEquals("test", Prefs.hkey) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CreateDeckDialogTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CreateDeckDialogTest.kt index ae6c9861e072..68b06361b4b7 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CreateDeckDialogTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CreateDeckDialogTest.kt @@ -13,9 +13,9 @@ import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker import com.ichi2.anki.IntroductionActivity -import com.ichi2.anki.R import com.ichi2.anki.RobolectricTest import com.ichi2.anki.dialogs.CreateDeckDialog.DeckDialogType import com.ichi2.anki.dialogs.utils.input @@ -335,7 +335,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "error is displayed", getInputTextLayout().error?.toString(), - equalTo(getResourceString(R.string.error_name_exists)), + equalTo(getResourceString(CommonString.error_name_exists)), ) assertThat("rename is disabled", positiveButton.isEnabled, equalTo(false)) } @@ -442,7 +442,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Snackbar should confirm deck creation for valid name", activity.latestSnackbarText(), - equalTo(getResourceString(R.string.deck_created)), + equalTo(getResourceString(CommonString.deck_created)), ) } } @@ -459,7 +459,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Snackbar should show invalid name error for blank name", activity.latestSnackbarText(), - equalTo(getResourceString(R.string.invalid_deck_name)), + equalTo(getResourceString(CommonString.invalid_deck_name)), ) } } @@ -475,7 +475,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Toast should confirm deck creation for valid name", ShadowToast.getTextOfLatestToast(), - equalTo(getResourceString(R.string.deck_created)), + equalTo(getResourceString(CommonString.deck_created)), ) } } @@ -491,7 +491,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Toast should show invalid name error for blank name", ShadowToast.getTextOfLatestToast(), - equalTo(getResourceString(R.string.invalid_deck_name)), + equalTo(getResourceString(CommonString.invalid_deck_name)), ) } } @@ -508,7 +508,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Snackbar should confirm rename for valid name", activity.latestSnackbarText(), - equalTo(getResourceString(R.string.deck_renamed)), + equalTo(getResourceString(CommonString.deck_renamed)), ) } } @@ -526,7 +526,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Snackbar should show invalid name error for blank name", activity.latestSnackbarText(), - equalTo(getResourceString(R.string.invalid_deck_name)), + equalTo(getResourceString(CommonString.invalid_deck_name)), ) } } @@ -549,7 +549,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Toast should confirm rename for valid name", ShadowToast.getTextOfLatestToast(), - equalTo(getResourceString(R.string.deck_renamed)), + equalTo(getResourceString(CommonString.deck_renamed)), ) } } @@ -572,7 +572,7 @@ class CreateDeckDialogTest : RobolectricTest() { assertThat( "Toast should show invalid name error for blank name", ShadowToast.getTextOfLatestToast(), - equalTo(getResourceString(R.string.invalid_deck_name)), + equalTo(getResourceString(CommonString.invalid_deck_name)), ) } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/DeckPickerContextMenuTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/DeckPickerContextMenuTest.kt index f1a4ef237a66..9f9d114ea6d0 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/DeckPickerContextMenuTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/DeckPickerContextMenuTest.kt @@ -11,6 +11,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.CollectionManager.TR +import com.ichi2.anki.CommonString import com.ichi2.anki.IntroductionActivity import com.ichi2.anki.R import com.ichi2.anki.common.preferences.sharedPrefs @@ -96,12 +97,12 @@ class DeckPickerContextMenuTest : JvmTest() { fun `Shows standard options`() { launch(withArguments()).onFragment { fragment -> with(fragment.requireContext()) { - fragment.assertOptionPresent(R.string.menu_add) - fragment.assertOptionPresent(R.string.browse_cards) + fragment.assertOptionPresent(CommonString.menu_add) + fragment.assertOptionPresent(CommonString.browse_cards) fragment.assertOptionPresent(TR.sentenceCase.renameDeck) fragment.assertOptionPresent(TR.sentenceCase.deckOptions) - fragment.assertOptionPresent(R.string.export_deck) - fragment.assertOptionPresent(R.string.create_shortcut) + fragment.assertOptionPresent(CommonString.export_deck) + fragment.assertOptionPresent(CommonString.create_shortcut) fragment.assertOptionPresent(TR.sentenceCase.deleteDeck) } } @@ -135,7 +136,7 @@ class DeckPickerContextMenuTest : JvmTest() { fun `Shows options to empty and rebuild when deck is dynamic`() { launch(withArguments(isDynamic = true)).onFragment { fragment -> assertTrue( - fragment.foundOptions().contains(fragment.getString(R.string.empty_cram_label)), + fragment.foundOptions().contains(fragment.getString(CommonString.empty_cram_label)), "'Empty' should be present when deck is dynamic", ) assertTrue( @@ -149,7 +150,7 @@ class DeckPickerContextMenuTest : JvmTest() { fun `Shows option to create subdeck when deck is not dynamic`() { launch(withArguments()).onFragment { fragment -> assertTrue( - fragment.foundOptions().contains(fragment.getString(R.string.create_subdeck)), + fragment.foundOptions().contains(fragment.getString(CommonString.create_subdeck)), "'Create subdeck' should be present when deck is not dynamic", ) } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ImportFileSelectionFragmentTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ImportFileSelectionFragmentTest.kt index 563f84b6726c..4be298f2fec3 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ImportFileSelectionFragmentTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/ImportFileSelectionFragmentTest.kt @@ -6,7 +6,7 @@ package com.ichi2.anki.dialogs import android.content.Intent import android.widget.ListView import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.RobolectricTest import com.ichi2.anki.dialogs.ImportFileSelectionFragment.Companion.buildImportFilePickerIntent import com.ichi2.anki.dialogs.ImportFileSelectionFragment.ImportOptions @@ -71,7 +71,7 @@ class ImportFileSelectionFragmentTest : RobolectricTest() { dialog.listView.itemLabels(), equalTo( listOf( - targetContext.getString(R.string.import_deck_package), + targetContext.getString(CommonString.import_deck_package), importCollectionPackageLabel, importCsvLabel, ), @@ -99,7 +99,7 @@ class ImportFileSelectionFragmentTest : RobolectricTest() { ImportOptions(importColpkg = false, importApkg = true, importTextFile = false), ) { dialog -> assertThat(dialog.listView.count, equalTo(1)) - assertThat(dialog.listView.itemLabels(), equalTo(listOf(targetContext.getString(R.string.import_deck_package)))) + assertThat(dialog.listView.itemLabels(), equalTo(listOf(targetContext.getString(CommonString.import_deck_package)))) } } @@ -133,9 +133,9 @@ class ImportFileSelectionFragmentTest : RobolectricTest() { } } - private val importCollectionPackageLabel = targetContext.getString(R.string.import_collection_package) + private val importCollectionPackageLabel = targetContext.getString(CommonString.import_collection_package) - private val importCsvLabel = targetContext.getString(R.string.import_csv) + private val importCsvLabel = targetContext.getString(CommonString.import_csv) private fun ListView.itemLabels(): List = (0 until adapter.count).map { adapter.getItem(it).toString() } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/help/HelpDialogTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/help/HelpDialogTest.kt index 49803edcd9b4..f2e9c7d75ce5 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/help/HelpDialogTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/help/HelpDialogTest.kt @@ -15,6 +15,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.BuildConfig +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.dialogs.help.HelpItem.Action.Rate import io.mockk.mockk @@ -56,12 +57,12 @@ class HelpDialogTest { // checking the support menu val expectedSupportItems = listOfNotNull( - R.string.help_item_support_opencollective_donate.takeIf { BuildConfig.SHOW_DONATE_LINKS }, - R.string.multimedia_editor_trans_translate, - R.string.help_item_support_develop_ankidroid, - R.string.help_item_support_rate_ankidroid, - R.string.help_item_support_other_ankidroid, - R.string.send_feedback, + CommonString.help_item_support_opencollective_donate.takeIf { BuildConfig.SHOW_DONATE_LINKS }, + CommonString.multimedia_editor_trans_translate, + CommonString.help_item_support_develop_ankidroid, + CommonString.help_item_support_rate_ankidroid, + CommonString.help_item_support_other_ankidroid, + CommonString.send_feedback, ) val actualSupportItems = HelpDialog.newSupportInstance(true).requireArgsHelpEntries().map { it.titleResId } @@ -73,10 +74,10 @@ class HelpDialogTest { // checking the help menu val expectedHelpItems = listOf( - R.string.help_title_using_ankidroid, - R.string.help_title_get_help, - R.string.help_title_community, - R.string.help_title_privacy, + CommonString.help_title_using_ankidroid, + CommonString.help_title_get_help, + CommonString.help_title_community, + CommonString.help_title_privacy, ) val actualHelpItems = HelpDialog.newHelpInstance().requireArgsHelpEntries().map { it.titleResId } @@ -113,39 +114,39 @@ class HelpDialogTest { @Test fun `Help menu handles submenus correctly`() { withHelpDialog { - onView(withText(R.string.help_title_community)).inRoot(isDialog()).perform(click()) + onView(withText(CommonString.help_title_community)).inRoot(isDialog()).perform(click()) // check that the expected six children are shown - onView(withText(R.string.help_item_discord)) + onView(withText(CommonString.help_item_discord)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_item_reddit)) + onView(withText(CommonString.help_item_reddit)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_item_facebook)) + onView(withText(CommonString.help_item_facebook)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_item_mailing_list)) + onView(withText(CommonString.help_item_mailing_list)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_item_twitter)) + onView(withText(CommonString.help_item_twitter)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_item_anki_forums)) + onView(withText(CommonString.help_item_anki_forums)) .inRoot(isDialog()) .check(matches(isDisplayed())) // press back pressBackUnconditionally() // check that the expected initial four menu items are shown - onView(withText(R.string.help_title_community)) + onView(withText(CommonString.help_title_community)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_title_get_help)) + onView(withText(CommonString.help_title_get_help)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_title_privacy)) + onView(withText(CommonString.help_title_privacy)) .inRoot(isDialog()) .check(matches(isDisplayed())) - onView(withText(R.string.help_title_using_ankidroid)) + onView(withText(CommonString.help_title_using_ankidroid)) .inRoot(isDialog()) .check(matches(isDisplayed())) } @@ -154,27 +155,27 @@ class HelpDialogTest { @Test fun `Help menu survives recreating its view`() { withHelpDialog { scenario -> - onView(withText(R.string.help_title_community)).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(CommonString.help_title_community)).inRoot(isDialog()).check(matches(isDisplayed())) scenario.moveToState(Lifecycle.State.CREATED) scenario.moveToState(Lifecycle.State.RESUMED) - onView(withText(R.string.help_title_community)).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(CommonString.help_title_community)).inRoot(isDialog()).check(matches(isDisplayed())) } } @Test fun `Help submenu and back navigation survive recreating the view`() { withHelpDialog { scenario -> - onView(withText(R.string.help_title_community)).inRoot(isDialog()).perform(click()) - onView(withText(R.string.help_item_discord)).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(CommonString.help_title_community)).inRoot(isDialog()).perform(click()) + onView(withText(CommonString.help_item_discord)).inRoot(isDialog()).check(matches(isDisplayed())) scenario.moveToState(Lifecycle.State.CREATED) scenario.moveToState(Lifecycle.State.RESUMED) - onView(withText(R.string.help_item_discord)).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(CommonString.help_item_discord)).inRoot(isDialog()).check(matches(isDisplayed())) pressBackUnconditionally() - onView(withText(R.string.help_title_community)).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(CommonString.help_title_community)).inRoot(isDialog()).check(matches(isDisplayed())) } } @@ -183,25 +184,25 @@ class HelpDialogTest { withHelpDialog { scenario -> scenario.onFragment { it.actionsDispatcher = mockActionDispatcher } // start the first submenu - onView(withText(R.string.help_title_using_ankidroid)) + onView(withText(CommonString.help_title_using_ankidroid)) .inRoot(isDialog()) .perform(click()) // the manual url is being shown - onView(withText(R.string.help_item_ankidroid_manual)) + onView(withText(CommonString.help_item_ankidroid_manual)) .inRoot(isDialog()) .perform(click()) verify(exactly = 1) { mockActionDispatcher.onOpenUrl(AnkiDroidApp.manualUrl) } // an url resource is being shown - onView(withText(R.string.help_item_anki_manual)).inRoot(isDialog()).perform(click()) + onView(withText(CommonString.help_item_anki_manual)).inRoot(isDialog()).perform(click()) verify(exactly = 1) { mockActionDispatcher.onOpenUrlResource(R.string.link_anki_manual) } pressBackUnconditionally() // start the second submenu - onView(withText(R.string.help_title_get_help)).inRoot(isDialog()).perform(click()) + onView(withText(CommonString.help_title_get_help)).inRoot(isDialog()).perform(click()) // the feedback url is being shown - onView(withText(R.string.help_item_report_bug)).inRoot(isDialog()).perform(click()) + onView(withText(CommonString.help_item_report_bug)).inRoot(isDialog()).perform(click()) verify(exactly = 1) { mockActionDispatcher.onOpenUrl(AnkiDroidApp.feedbackUrl) } // a report is sent - onView(withText(R.string.help_title_send_exception)) + onView(withText(CommonString.help_title_send_exception)) .inRoot(isDialog()) .perform(click()) verify(exactly = 1) { mockActionDispatcher.onSendReport() } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/progress/ProgressObserverTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/progress/ProgressObserverTest.kt index ef33cb102d41..86ac6b24bef1 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/progress/ProgressObserverTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/progress/ProgressObserverTest.kt @@ -6,6 +6,7 @@ import android.os.Looper import android.widget.TextView import androidx.fragment.app.DialogFragment import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.RobolectricTest import com.ichi2.anki.dialogs.LoadingDialogFragment @@ -99,7 +100,7 @@ class ProgressObserverTest : RobolectricTest() { val controller = startActivity() val activity = controller.get() activity.observeProgress(viewModel, delayMillis = SHOW_DELAY) { message -> - getString(R.string.progress_amount_bytes, message, "2 MB") + getString(CommonString.progress_amount_bytes, message, "2 MB") } val gate = CompletableDeferred() diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingResolveActionTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingResolveActionTest.kt index aebd4eb4f114..c78c580b9016 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingResolveActionTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/reviewreminders/ReminderTroubleshootingResolveActionTest.kt @@ -11,7 +11,7 @@ import androidx.core.net.toUri import androidx.core.view.children import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.RobolectricTest import com.ichi2.anki.databinding.ItemTroubleshootingCheckBinding import com.ichi2.testutils.withRequestIgnoreBatteryOptimizationsInManifest @@ -90,7 +90,7 @@ class ReminderTroubleshootingResolveActionTest : RobolectricTest() { /** The displayed 'Battery optimization' check */ private val ReminderTroubleshootingFragment.batteryOptimizationCheck: ItemTroubleshootingCheckBinding get() { - val title = requireContext().getString(R.string.reminder_troubleshooting_check_battery_optimization) + val title = requireContext().getString(CommonString.reminder_troubleshooting_check_battery_optimization) return binding.checksList.children .map { ItemTroubleshootingCheckBinding.bind(it) } .single { it.title.text.toString() == title } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivityTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivityTest.kt index 1a46a39fb65f..5d352c5cf556 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivityTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/permissions/PermissionsActivityTest.kt @@ -10,6 +10,7 @@ import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario.ActivityAction import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.CommonString import com.ichi2.anki.OptionalPermissionSet import com.ichi2.anki.PermissionSet import com.ichi2.anki.R @@ -48,7 +49,7 @@ class PermissionsActivityTest : RobolectricTest() { testInvalidActivityFinishes() assertThat( ShadowToast.getTextOfLatestToast(), - equalTo(getResourceString(R.string.something_wrong)), + equalTo(getResourceString(CommonString.something_wrong)), ) } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayViewTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayViewTest.kt index 6a171ccd9a2a..2ac86feb47b1 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayViewTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioPlayViewTest.kt @@ -7,6 +7,7 @@ import android.content.Context import android.os.Looper import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.databinding.ViewAudioPlayBinding import com.ichi2.themes.Themes @@ -32,12 +33,12 @@ class AudioPlayViewTest { @Test fun `play button exposes a label to TalkBack`() { - assertEquals(context.getString(R.string.play_recording), binding.playButton.contentDescription) + assertEquals(context.getString(CommonString.play_recording), binding.playButton.contentDescription) } @Test fun `cancel button exposes a label to TalkBack`() { - assertEquals(context.getString(R.string.dialog_cancel), binding.cancelButton.contentDescription) + assertEquals(context.getString(CommonString.dialog_cancel), binding.cancelButton.contentDescription) } @Test @@ -45,6 +46,6 @@ class AudioPlayViewTest { audioPlayView.changePlayIcon(R.drawable.ic_replay) shadowOf(Looper.getMainLooper()).idle() - assertEquals(context.getString(R.string.replay_voice), binding.playButton.contentDescription) + assertEquals(context.getString(CommonString.replay_voice), binding.playButton.contentDescription) } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioRecordViewTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioRecordViewTest.kt index 363287a5795f..da60feb7620d 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioRecordViewTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ui/windows/reviewer/audiorecord/AudioRecordViewTest.kt @@ -6,7 +6,7 @@ package com.ichi2.anki.ui.windows.reviewer.audiorecord import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.databinding.ViewAudioRecordBinding import com.ichi2.themes.Themes import org.junit.Before @@ -30,6 +30,6 @@ class AudioRecordViewTest { @Test fun `record button exposes a label to TalkBack`() { - assertEquals(context.getString(R.string.record_voice), binding.recordButton.contentDescription) + assertEquals(context.getString(CommonString.record_voice), binding.recordButton.contentDescription) } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/widget/DeckAdapterTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/widget/DeckAdapterTest.kt index 34aae588c595..045a3a3b572a 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/widget/DeckAdapterTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/widget/DeckAdapterTest.kt @@ -17,8 +17,8 @@ package com.ichi2.anki.widget import androidx.recyclerview.widget.RecyclerView import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.CommonString import com.ichi2.anki.DeckPicker -import com.ichi2.anki.R import com.ichi2.anki.RobolectricTest import com.ichi2.anki.deckpicker.DeckFilters import com.ichi2.anki.deckpicker.filterAndFlattenDisplay @@ -104,7 +104,7 @@ class DeckAdapterTest : RobolectricTest() { assertTrue(changePayloads.all { it != null }, "A full row update interrupts the arrow ripple") assertSame(parent, deckPicker.deckHolder(parentDeck)) assertEquals( - deckPicker.getString(if (wasCollapsed) R.string.collapse else R.string.expand), + deckPicker.getString(if (wasCollapsed) CommonString.collapse else CommonString.expand), parent.binding.deckExpander.contentDescription, ) changePayloads.clear() diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/widget/cardanalysis/CardAnalysisWidgetConfigTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/widget/cardanalysis/CardAnalysisWidgetConfigTest.kt index 6787514f821d..cb379bc0603c 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/widget/cardanalysis/CardAnalysisWidgetConfigTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/widget/cardanalysis/CardAnalysisWidgetConfigTest.kt @@ -28,6 +28,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.CommonString import com.ichi2.anki.R import com.ichi2.anki.RobolectricTest import com.ichi2.widget.AppWidgetId @@ -90,7 +91,7 @@ class CardAnalysisWidgetConfigTest : RobolectricTest() { // first setup of the widget, no deck selected so deck name view should be empty onView(withId(R.id.deck_name)).check(matches(withText(""))) // select dialog already open, select deck 1 - onView(withText(R.string.select_deck_title)) + onView(withText(CommonString.select_deck_title)) .inRoot(isDialog()) .check(matches(isDisplayed())) onView(withText(TEST_DECK_NAME1)).inRoot(isDialog()).perform(click()) @@ -129,7 +130,7 @@ class CardAnalysisWidgetConfigTest : RobolectricTest() { // no selection yet onView(withId(R.id.deck_name)).check(matches(withText(""))) // cancel deck selection - onView(withText(R.string.dialog_cancel)).inRoot(isDialog()).perform(click()) + onView(withText(CommonString.dialog_cancel)).inRoot(isDialog()).perform(click()) // still no deck selected, so deck name should still be empty onView(withId(R.id.deck_name)).check(matches(withText(""))) // set configuration as done diff --git a/AnkiDroid/src/test/java/com/ichi2/utils/ClipboardUtilTest.kt b/AnkiDroid/src/test/java/com/ichi2/utils/ClipboardUtilTest.kt index 54c8da48e48c..8d66ac3af50d 100644 --- a/AnkiDroid/src/test/java/com/ichi2/utils/ClipboardUtilTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/utils/ClipboardUtilTest.kt @@ -10,7 +10,7 @@ import android.content.ContextWrapper import android.os.Build import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.common.utils.ext.requireSystemService import com.ichi2.utils.ClipboardUtil.AUDIO_MIME_TYPES import com.ichi2.utils.ClipboardUtil.IMAGE_MIME_TYPES @@ -146,7 +146,7 @@ class ClipboardUtilTest { assertTrue(context.copyToClipboard(TruncatedString.from("hello"))) assertThat( ShadowToast.getTextOfLatestToast(), - equalTo(context.getString(R.string.about_ankidroid_successfully_copied_debug_info)), + equalTo(context.getString(CommonString.about_ankidroid_successfully_copied_debug_info)), ) } @@ -163,7 +163,7 @@ class ClipboardUtilTest { assertFalse(contextWithClipboard(null).copyToClipboard(TruncatedString.from("hello"))) assertThat( ShadowToast.getTextOfLatestToast(), - equalTo(context.getString(R.string.failed_to_copy)), + equalTo(context.getString(CommonString.failed_to_copy)), ) } @@ -177,7 +177,7 @@ class ClipboardUtilTest { assertFalse(contextWithClipboard(throwingClipboard).copyToClipboard(TruncatedString.from("hello"))) assertThat( ShadowToast.getTextOfLatestToast(), - equalTo(context.getString(R.string.failed_to_copy)), + equalTo(context.getString(CommonString.failed_to_copy)), ) } diff --git a/AnkiDroid/src/test/java/com/ichi2/utils/ImportUtilsTest.kt b/AnkiDroid/src/test/java/com/ichi2/utils/ImportUtilsTest.kt index 9ebccb5f75bb..f17f298d4ac8 100644 --- a/AnkiDroid/src/test/java/com/ichi2/utils/ImportUtilsTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/utils/ImportUtilsTest.kt @@ -11,7 +11,7 @@ import android.net.Uri import androidx.annotation.CheckResult import androidx.core.net.toUri import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.ichi2.anki.R +import com.ichi2.anki.CommonString import com.ichi2.anki.RobolectricTest import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.crashreporting.CrashReporter @@ -67,7 +67,7 @@ class ImportUtilsTest : RobolectricTest() { val failure = assertIs(result) assertIs(failure.exception) - assertEquals(targetContext.getString(R.string.import_error_copy_to_cache_title), failure.title) + assertEquals(targetContext.getString(CommonString.import_error_copy_to_cache_title), failure.title) verifyNoInteractions(crashReporter) } From 02c7cf2184b0be82c7612e77a2c7c6a44f241137 Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:32:20 +0100 Subject: [PATCH 41/43] fix(widgets): define RTL support The widget configuration layout uses start/end attributes We will add `checkDependencies = true` for 21500 `supportsRtl` will be needed when lint checks this dependency. Part of 21500 Assisted-by: Claude Fable 5 --- widgets/src/main/AndroidManifest.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/widgets/src/main/AndroidManifest.xml b/widgets/src/main/AndroidManifest.xml index e10007615799..670d9116afe1 100644 --- a/widgets/src/main/AndroidManifest.xml +++ b/widgets/src/main/AndroidManifest.xml @@ -1,4 +1,7 @@ - + + + + From 7a91a4aca1c11c4532690d272aaf4dce2b48a281 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Tue, 15 Sep 2026 14:12:05 +0530 Subject: [PATCH 42/43] feat(bottom-nav): report tab screen views Switching tabs never sent a screen view, since no activity resumes. Report each tab when it opens and when you come back to it. Assisted-by: Claude Opus 5 [helped with tests] --- .../com/ichi2/anki/BottomNavController.kt | 14 ++- .../main/java/com/ichi2/anki/DeckPicker.kt | 3 + .../com/ichi2/anki/HomeScreenNavigation.kt | 11 +++ .../anki/BottomNavigationAnalyticsTest.kt | 95 +++++++++++++++++++ 4 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 AnkiDroid/src/test/java/com/ichi2/anki/BottomNavigationAnalyticsTest.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt b/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt index e246c36d54eb..a4a746c3c50a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt @@ -16,6 +16,11 @@ import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.common.annotations.NeedsTest import timber.log.Timber +private const val DECK_PICKER_SCREEN_NAME = "DeckPicker" +private const val CARD_BROWSER_SCREEN_NAME = "CardBrowser" +private const val STATISTICS_SCREEN_NAME = "Statistics" +private const val MORE_SCREEN_NAME = "MoreFragment" + /** * Controls bottom navigation bar behaviour. * @@ -55,11 +60,12 @@ class BottomNavController( /** Fragment tag used to find and reuse this destination's fragment across tab switches. */ val tag: String, @StringRes val shortcutLabel: Int, + val analyticsScreenName: String, ) { - HOME(R.id.nav_home, "home", CommonString.deck_picker_group), - BROWSER(R.id.nav_browser, "browser", CommonString.card_browser_context_menu), - STATS(R.id.nav_stats, "stats", CommonString.open_statistics), - MORE(R.id.nav_more, "more", CommonString.bottom_nav_more), + HOME(R.id.nav_home, "home", CommonString.deck_picker_group, DECK_PICKER_SCREEN_NAME), + BROWSER(R.id.nav_browser, "browser", CommonString.card_browser_context_menu, CARD_BROWSER_SCREEN_NAME), + STATS(R.id.nav_stats, "stats", CommonString.open_statistics, STATISTICS_SCREEN_NAME), + MORE(R.id.nav_more, "more", CommonString.bottom_nav_more, MORE_SCREEN_NAME), ; fun title(context: Context): String = diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index 982271ef543b..2dcf1822dbdf 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -283,6 +283,9 @@ open class DeckPicker : get() = binding.studyoptionsFragment != null set(_) = throw UnsupportedOperationException() + override val analyticsScreenName: String + get() = selectedBottomNavItem()?.analyticsScreenName ?: super.analyticsScreenName + // Short animation duration from system private var shortAnimDuration = 0 diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt b/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt index ea89f0abfe9f..81ec254396d0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt @@ -20,6 +20,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationView import com.ichi2.anki.BottomNavController.NavigationItem import com.ichi2.anki.browser.CardBrowserFragment import com.ichi2.anki.browser.CardBrowserViewModel +import com.ichi2.anki.common.analytics.Analytics import com.ichi2.anki.common.annotations.NeedsTest import com.ichi2.anki.pages.Statistics import com.ichi2.anki.settings.Prefs @@ -64,6 +65,9 @@ fun setupBottomNavigation() { bottomNav.setOnItemSelectedListener { item -> val navItem = NavigationItem.fromId(item.itemId) ?: return@setOnItemSelectedListener false + if (item.itemId != bottomNav.selectedItemId) { + Analytics.sendAnalyticsScreenView(navItem.analyticsScreenName) + } handleNavigationItemSelected(navItem, contentWrapper, fragmentContainer, bottomNavBackCallback) } @@ -81,6 +85,13 @@ fun showRestoredBottomNavTab() { bottomNav.selectedItemId = bottomNav.selectedItemId } +context(deckPicker: DeckPicker) +fun selectedBottomNavItem(): NavigationItem? { + if (!Prefs.devBottomNavEnabled || deckPicker.fragmented) return null + val bottomNav = deckPicker.binding.bottomNavigation ?: return null + return NavigationItem.fromId(bottomNav.selectedItemId) +} + context(deckPicker: DeckPicker) private fun handleNavigationItemSelected( item: NavigationItem, diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/BottomNavigationAnalyticsTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/BottomNavigationAnalyticsTest.kt new file mode 100644 index 000000000000..d3dd57da9d3b --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/BottomNavigationAnalyticsTest.kt @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: Copyright (c) 2026 Ashish Yadav + +package com.ichi2.anki + +import android.content.Intent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.BottomNavController.NavigationItem +import com.ichi2.anki.common.analytics.Analytics +import com.ichi2.testutils.withBooleanPreference +import io.mockk.every +import io.mockk.just +import io.mockk.mockkObject +import io.mockk.runs +import io.mockk.unmockkObject +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.empty +import org.hamcrest.Matchers.equalTo +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.android.controller.ActivityController + +@RunWith(AndroidJUnit4::class) +class BottomNavigationAnalyticsTest : RobolectricTest() { + private val screenViews = mutableListOf() + + @Before + override fun setUp() { + super.setUp() + mockkObject(Analytics) + every { Analytics.sendAnalyticsScreenView(capture(screenViews)) } just runs + } + + @After + override fun tearDown() { + super.tearDown() + unmockkObject(Analytics) + } + + @Test + fun `each tab is reported under its screen name`() = + withBottomNavigation { + mapOf( + NavigationItem.BROWSER to "CardBrowser", + NavigationItem.STATS to "Statistics", + NavigationItem.MORE to "MoreFragment", + NavigationItem.HOME to "DeckPicker", + ).forEach { (tab, screenName) -> + assertThat(screenViewsDuring { select(tab) }, equalTo(listOf(screenName))) + } + } + + @Test + fun `selecting the open tab again is not reported`() = + withBottomNavigation { + select(NavigationItem.BROWSER) + + assertThat(screenViewsDuring { select(NavigationItem.BROWSER) }, empty()) + } + + @Test + fun `resuming reports the open tab, not the deck picker`() = + withBottomNavigation { + select(NavigationItem.BROWSER) + + assertThat(screenViewsDuring { pause().resume() }, equalTo(listOf("CardBrowser"))) + } + + @Test + fun `recreating on a tab reports it once`() = + withBottomNavigation { + select(NavigationItem.BROWSER) + + assertThat(screenViewsDuring { recreate() }, equalTo(listOf("CardBrowser"))) + } + + private fun withBottomNavigation(test: ActivityController.() -> Unit) = + withBooleanPreference(R.string.dev_bottom_nav_key, true) { + setIntroductionSlidesShown(true) + startActivityControllerNormallyOpenCollectionWithIntent(DeckPicker::class.java, Intent()).test() + } + + private fun screenViewsDuring(action: () -> Unit): List { + screenViews.clear() + action() + return screenViews.toList() + } + + private fun ActivityController.select(tab: NavigationItem) { + get().binding.bottomNavigation!!.selectedItemId = tab.id + advanceRobolectricLooper() + } +} From 0c3be716deb05e8d97d015e2e9da4bf5e585b75c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2026 05:23:01 +0000 Subject: [PATCH 43/43] build(deps): bump undici from 6.28.0 to 6.29.0 in /tools/localization Bumps [undici](https://github.com/nodejs/undici) from 6.28.0 to 6.29.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v6.28.0...v6.29.0) --- updated-dependencies: - dependency-name: undici dependency-version: 6.29.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- tools/localization/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/localization/yarn.lock b/tools/localization/yarn.lock index c11c51c83c2e..93527ff95bdf 100644 --- a/tools/localization/yarn.lock +++ b/tools/localization/yarn.lock @@ -5993,9 +5993,9 @@ __metadata: linkType: hard "undici@npm:^6.25.0": - version: 6.28.0 - resolution: "undici@npm:6.28.0" - checksum: 10c0/3029a70df06b38b5b2f30732932a1e92544c753cd82c8abdf0d35afad48e0ba91612e79fe3a442dbbb9434d6a9eba2b714d5ea28984c903dda2b5d5444f38354 + version: 6.29.0 + resolution: "undici@npm:6.29.0" + checksum: 10c0/3fb2117985be977ba8b6bd6a242fec915e1b1d0f9681f5adb3f508fb1717352452d65bcfd3bed09e7c776cfda403a4e8edb24a318492f8b2d793439af2b8699b languageName: node linkType: hard