diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d109289..e67d4b1 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,13 +1,13 @@
[versions]
agp = "9.3.0"
-kotlin = "2.4.0"
+kotlin = "2.4.10"
android-compileSdk = "37"
android-minSdk = "26"
android-targetSdk = "37"
app-version-name = "2.0.0"
-app-version-code = "58"
+app-version-code = "59"
androidx-activity = "1.13.0"
-androidx-lifecycle = "2.10.0"
+androidx-lifecycle = "2.11.0"
composeHotReload = "1.1.1"
composeMultiplatform = "1.11.1"
kotlinx-coroutines = "1.11.0"
@@ -18,7 +18,7 @@ iconsExtended = "1.7.3"
koin = "4.2.2"
jgit = "5.13.5.202508271544-r"
-bcprovJdk18on = "1.84"
+bcprovJdk18on = "1.85"
jmx = "1.2.1"
kotlinx-io = "0.9.1"
@@ -31,7 +31,7 @@ preferences-core = "1.2.1"
filekit = "0.14.2"
htmlconverter = "1.1.1"
foundation = "1.11.4"
-oshi-core = "7.3.2"
+oshi-core = "7.4.0"
logger = "3.0.3"
foreground = "0.1.0"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a9db115..ec103a8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,3 +1,4 @@
+#Tue Jul 14 19:25:38 GET 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
diff --git a/shared/src/commonMain/composeResources/values/strings.xml b/shared/src/commonMain/composeResources/values/strings.xml
index 418de55..7199b76 100644
--- a/shared/src/commonMain/composeResources/values/strings.xml
+++ b/shared/src/commonMain/composeResources/values/strings.xml
@@ -140,6 +140,7 @@
Regular
Unlocked Literal Bible
Unlocked Dynamic Bible
+ Resource Options
Open Bible Stories
Notes
Questions
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/core/TargetTranslation.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/core/TargetTranslation.kt
index be5818c..c6b749e 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/core/TargetTranslation.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/core/TargetTranslation.kt
@@ -4,6 +4,7 @@ import org.bibletranslationtools.logger.Logger
import org.bibletranslationtools.resourcecatalog.library.models.TargetLanguage
import org.bibletranslationtools.resourcecatalog.library.models.Translation
import org.bibletranslationtools.resourcecontainer.ContainerTools
+import org.bibletranslationtools.resourcecontainer.Resource
import org.bibletranslationtools.resourcecontainer.ResourceContainer
import org.bibletranslationtools.writer.DirectoryProvider
import org.bibletranslationtools.writer.Platform
@@ -531,6 +532,16 @@ class TargetTranslation private constructor(
targetLanguageName = targetLanguage.name
}
+ fun changeResourceType(resourceSlug: String) {
+ if (translationType != ResourceType.TEXT) return
+ val resourceName = getResourceName(resourceSlug)
+ manifestAccessor.save(
+ manifest.copy(resource = Manifest.Resource(resourceSlug, resourceName))
+ )
+ this.resourceSlug = resourceSlug
+ this.resourceName = resourceName
+ }
+
fun unlockRepo(): Boolean {
var cleaned = false
val gitDir = File(path.absolutePath, ".git")
@@ -714,7 +725,6 @@ class TargetTranslation private constructor(
const val LICENSE_FILE = "LICENSE.md"
const val APPLICATION_NAME = "ts-android"
- const val OBS_PROJECT_TYPE = "obs"
fun generateTargetTranslationId(
targetLanguageSlug: String,
@@ -730,7 +740,7 @@ class TargetTranslation private constructor(
}
fun isObsProject(projectId: String): Boolean =
- OBS_PROJECT_TYPE.equals(projectId, ignoreCase = true)
+ Resource.OBS_SLUG.equals(projectId, ignoreCase = true)
suspend fun open(targetTranslationDir: File, onError: (suspend () -> Unit)? = null): TargetTranslation? {
if (targetTranslationDir.exists()) {
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/core/TranslationWordsType.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/core/TranslationWordsType.kt
new file mode 100644
index 0000000..804d4f3
--- /dev/null
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/core/TranslationWordsType.kt
@@ -0,0 +1,6 @@
+package org.bibletranslationtools.writer.core
+
+import java.util.regex.Pattern
+
+const val WORDS_CHAPTER = "01"
+val WORD_PATTERN: Pattern = Pattern.compile("#+([^\\n]+)\\n+([\\s\\S]*)")
\ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/data/Preference.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/data/Preference.kt
index 1c8c441..b3cabe7 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/data/Preference.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/data/Preference.kt
@@ -363,6 +363,28 @@ class Preference(private val settings: ObservableSettings) {
)
}
+ fun moveTargetTranslationAppSettings(
+ targetTranslationId: String,
+ newTargetTranslationId: String
+ ) {
+ val sources = getOpenSourceTranslations(targetTranslationId)
+ for (source in sources) {
+ addOpenSourceTranslation(newTargetTranslationId, source)
+ }
+
+ val source = getSelectedSourceTranslationId(targetTranslationId)
+ setSelectedSourceTranslation(newTargetTranslationId, source)
+
+ val lastFocusChapterId = getLastFocusChapterId(targetTranslationId)
+ val lastFocusFrameId = getLastFocusFrameId(targetTranslationId)
+ setLastFocus(newTargetTranslationId, lastFocusChapterId, lastFocusFrameId)
+
+ val lastViewMode = getLastViewMode(targetTranslationId)
+ setLastViewMode(newTargetTranslationId, lastViewMode)
+
+ clearTargetTranslationSettings(targetTranslationId)
+ }
+
/**
* Removes all settings for a target translation
* @param targetTranslationId
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/dialogs/project/ProjectDetailsDialog.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/dialogs/project/ProjectDetailsDialog.kt
index 90261bc..12e9a5a 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/dialogs/project/ProjectDetailsDialog.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/dialogs/project/ProjectDetailsDialog.kt
@@ -1,5 +1,6 @@
package org.bibletranslationtools.writer.ui.dialogs.project
+import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -49,11 +50,18 @@ import btt_writer.shared.generated.resources.label_unknown
import btt_writer.shared.generated.resources.print
import btt_writer.shared.generated.resources.progress
import btt_writer.shared.generated.resources.publish
+import btt_writer.shared.generated.resources.reg_type
+import btt_writer.shared.generated.resources.resource_options
import btt_writer.shared.generated.resources.target_language
+import btt_writer.shared.generated.resources.title_cancel
import btt_writer.shared.generated.resources.translators
import btt_writer.shared.generated.resources.type_label
+import btt_writer.shared.generated.resources.udb_type
+import btt_writer.shared.generated.resources.ulb_type
+import org.bibletranslationtools.resourcecontainer.Resource
import org.bibletranslationtools.writer.DirectoryProvider
import org.bibletranslationtools.writer.core.NativeSpeaker
+import org.bibletranslationtools.writer.core.ResourceType
import org.bibletranslationtools.writer.core.TextStyleType
import org.bibletranslationtools.writer.core.TranslationType
import org.bibletranslationtools.writer.core.Typography
@@ -74,6 +82,7 @@ fun ProjectDetailsDialog(
project: TranslationItem,
onDismiss: () -> Unit,
onChangeLanguage: () -> Unit,
+ onChangeResourceType: (String) -> Unit,
onDelete: () -> Unit,
onPublish: () -> Unit,
onExport: (Boolean) -> Unit
@@ -141,6 +150,10 @@ fun ProjectDetailsDialog(
}
var showContributorsDialog by rememberSaveable { mutableStateOf(false) }
var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
+ var showResourceOptions by rememberSaveable { mutableStateOf(false) }
+
+ val canChangeResourceType = project.translation.translationType == ResourceType.TEXT &&
+ project.translation.resourceSlug != Resource.OBS_SLUG
Dialog(onDismissRequest = onDismiss) {
Surface(
@@ -196,11 +209,31 @@ fun ProjectDetailsDialog(
}
}
- DetailRow(
- label = stringResource(Res.string.type_label),
- value = project.translation.resourceSlug?.uppercase()
- ?: project.translation.translationType.title
- )
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(Res.string.type_label),
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+
+ Text(
+ text = project.translation.resourceSlug?.uppercase()
+ ?: project.translation.translationType.title,
+ modifier = Modifier.weight(1f)
+ )
+
+ if (canChangeResourceType) {
+ TextButton(onClick = { showResourceOptions = true }) {
+ Text(
+ text = stringResource(Res.string.label_change),
+ fontWeight = FontWeight.Bold
+ )
+ }
+ }
+ }
DetailRow(
label = stringResource(Res.string.progress),
@@ -305,6 +338,85 @@ fun ProjectDetailsDialog(
onDismiss = { showDeleteDialog = false }
)
}
+
+ if (showResourceOptions) {
+ ResourceOptionsDialog(
+ currentResourceSlug = project.translation.resourceSlug,
+ onSelect = { slug ->
+ showResourceOptions = false
+ onChangeResourceType(slug)
+ },
+ onDismiss = { showResourceOptions = false }
+ )
+ }
+}
+
+@Composable
+private fun ResourceOptionsDialog(
+ currentResourceSlug: String?,
+ onSelect: (String) -> Unit,
+ onDismiss: () -> Unit
+) {
+ val options = listOf(
+ Resource.ULB_SLUG to stringResource(Res.string.ulb_type),
+ Resource.UDB_SLUG to stringResource(Res.string.udb_type),
+ Resource.REGULAR_SLUG to stringResource(Res.string.reg_type)
+ )
+
+ Dialog(onDismissRequest = onDismiss) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(0.8f)
+ .padding(32.dp),
+ shape = RoundedCornerShape(8.dp),
+ color = MaterialTheme.colorScheme.surface
+ ) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ Text(
+ text = stringResource(Res.string.resource_options),
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier
+ .align(Alignment.CenterHorizontally)
+ .padding(24.dp)
+ )
+
+ options.forEach { (slug, name) ->
+ val isCurrent = slug == currentResourceSlug
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .then(
+ if (isCurrent) {
+ Modifier.background(
+ MaterialTheme.colorScheme.surfaceVariant
+ )
+ } else {
+ Modifier.clickable { onSelect(slug) }
+ }
+ )
+ ) {
+ Text(
+ text = name,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier.padding(
+ horizontal = 24.dp,
+ vertical = 16.dp
+ )
+ )
+ HorizontalDivider()
+ }
+ }
+
+ TextButton(
+ onClick = onDismiss,
+ modifier = Modifier
+ .align(Alignment.End)
+ .padding(8.dp)
+ ) {
+ Text(stringResource(Res.string.title_cancel))
+ }
+ }
+ }
+ }
}
private fun getTranslatorNames(contributors: List): String {
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/HomeComponent.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/HomeComponent.kt
index 0b3d0f4..ab32c29 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/HomeComponent.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/HomeComponent.kt
@@ -2,9 +2,11 @@ package org.bibletranslationtools.writer.ui.home
import btt_writer.shared.generated.resources.Res
import btt_writer.shared.generated.resources.duplicate_target_translation
+import btt_writer.shared.generated.resources.error
import btt_writer.shared.generated.resources.exporting
import btt_writer.shared.generated.resources.loading
import btt_writer.shared.generated.resources.log_out
+import btt_writer.shared.generated.resources.warn_existing_target_translation
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.slot.ChildSlot
import com.arkivanov.decompose.router.slot.SlotNavigation
@@ -42,6 +44,7 @@ import org.bibletranslationtools.writer.core.Profile
import org.bibletranslationtools.writer.core.Progress
import org.bibletranslationtools.writer.core.ProgressManager
import org.bibletranslationtools.writer.core.ProgressOwner
+import org.bibletranslationtools.writer.core.ResourceType
import org.bibletranslationtools.writer.core.TargetTranslation
import org.bibletranslationtools.writer.core.TaskHandle
import org.bibletranslationtools.writer.core.Translator
@@ -64,6 +67,7 @@ import org.bibletranslationtools.writer.ui.dialogs.update.UpdateLibraryComponent
import org.bibletranslationtools.writer.ui.navigation.RootComponent
import org.bibletranslationtools.writer.usecases.BackupRC
import org.bibletranslationtools.writer.usecases.GogsLogout
+import org.bibletranslationtools.writer.usecases.MergeTargetTranslation
import org.bibletranslationtools.writer.usecases.TranslationProgress
import org.jetbrains.compose.resources.getString
import org.koin.core.component.KoinComponent
@@ -109,6 +113,9 @@ interface HomeComponent {
suspend fun getLastOpened(): TargetTranslation?
fun deleteProject(project: TranslationItem)
+ fun changeResourceType(project: TranslationItem, resourceSlug: String)
+ fun confirmResourceMerge()
+ fun dismissResourceMerge()
fun changeProjectSort(sort: ProjectSort)
fun changeBookSort(sort: BookSort)
fun showProjectInfo(item: TranslationItem)
@@ -144,11 +151,18 @@ interface HomeComponent {
val translations: List
)
+ data class ResourceMerge(
+ val project: TranslationItem,
+ val destination: TargetTranslation,
+ val message: String
+ )
+
data class HomeState(
val translations: List = emptyList(),
val projectSort: ProjectSort = ProjectSort.ProjectThenLanguage,
val bookSort: BookSort = BookSort.BibleOrder,
val projectInfo: TranslationItem? = null,
+ val resourceMerge: ResourceMerge? = null,
val scrollToTopTrigger: Int = 0
)
@@ -219,6 +233,7 @@ class DefaultHomeComponent(
private val profile: Profile by inject()
private val gogsLogout: GogsLogout by inject()
private val backupRC: BackupRC by inject()
+ private val mergeTargetTranslation: MergeTargetTranslation by inject()
private val catalogClient: ResourceCatalogClient by inject()
private val platform: Platform by inject()
@@ -340,6 +355,81 @@ class DefaultHomeComponent(
}
}
+ override fun changeResourceType(project: TranslationItem, resourceSlug: String) {
+ launchWithProgress {
+ val translation = project.translation
+ if (translation.resourceSlug == resourceSlug) return@launchWithProgress
+
+ val newId = TargetTranslation.generateTargetTranslationId(
+ translation.targetLanguageId,
+ translation.projectId,
+ ResourceType.TEXT,
+ resourceSlug
+ )
+ val existing = withContext(Dispatchers.IO) { getTargetTranslation(newId) }
+
+ if (existing != null) {
+ val message = getString(
+ Res.string.warn_existing_target_translation,
+ project.name,
+ translation.targetLanguageName
+ )
+ _state.update {
+ it.copy(
+ projectInfo = null,
+ resourceMerge = HomeComponent.ResourceMerge(
+ project = project,
+ destination = existing,
+ message = message
+ )
+ )
+ }
+ } else {
+ withContext(Dispatchers.IO) {
+ val oldId = translation.id
+ translation.changeResourceType(resourceSlug)
+ translation.normalizePath()
+ preference.moveTargetTranslationAppSettings(
+ targetTranslationId = oldId,
+ newTargetTranslationId = translation.id
+ )
+ }
+ hideProjectInfo()
+ loadProjects()
+ }
+ }
+ }
+
+ override fun confirmResourceMerge() {
+ val merge = _state.value.resourceMerge ?: return
+ launchWithProgress {
+ _state.update { it.copy(resourceMerge = null) }
+ val result = withContext(Dispatchers.IO) {
+ mergeTargetTranslation.execute(
+ merge.destination,
+ merge.project.translation,
+ true
+ )
+ }
+
+ when (result.status) {
+ MergeTargetTranslation.Status.MERGE_CONFLICTS -> {
+ loadProjects()
+ openProject(merge.destination.id, true)
+ }
+ MergeTargetTranslation.Status.SUCCESS -> loadProjects()
+ else -> {
+ val error = getString(Res.string.error)
+ _event.send(HomeComponent.Event.SnackbarMessage(error))
+ }
+ }
+ }
+ }
+
+ override fun dismissResourceMerge() {
+ _state.update { it.copy(resourceMerge = null) }
+ }
+
override fun changeProjectSort(sort: ProjectSort) {
preference.setPref(SORT_BY_PROJECT, sort.ordinal)
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/TranslationListScreen.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/TranslationListScreen.kt
index b2b5b1c..de89832 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/TranslationListScreen.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/home/TranslationListScreen.kt
@@ -47,8 +47,10 @@ import btt_writer.shared.generated.resources.sort_progress_then_project
import btt_writer.shared.generated.resources.sort_project_then_language
import btt_writer.shared.generated.resources.sort_projects
import btt_writer.shared.generated.resources.type_label
+import btt_writer.shared.generated.resources.warn_existing_target_translation_label
import org.bibletranslationtools.writer.core.Typography
import org.bibletranslationtools.writer.ui.components.PlatformPullToRefresh
+import org.bibletranslationtools.writer.ui.dialogs.ConfirmDialog
import org.bibletranslationtools.writer.ui.dialogs.project.ProjectDetailsDialog
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.koinInject
@@ -178,6 +180,9 @@ fun TranslationListScreen(
component.hideProjectInfo()
onChangeLanguage(project)
},
+ onChangeResourceType = { resourceSlug ->
+ component.changeResourceType(project, resourceSlug)
+ },
onDelete = {
component.deleteProject(project)
},
@@ -191,6 +196,15 @@ fun TranslationListScreen(
}
)
}
+
+ state.resourceMerge?.let { merge ->
+ ConfirmDialog(
+ title = stringResource(Res.string.warn_existing_target_translation_label),
+ message = merge.message,
+ onConfirm = component::confirmResourceMerge,
+ onDismiss = component::dismissResourceMerge
+ )
+ }
}
@OptIn(ExperimentalMaterial3Api::class)
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/newtranslation/NewTranslationComponent.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/newtranslation/NewTranslationComponent.kt
index ca0c523..5eb271c 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/newtranslation/NewTranslationComponent.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/newtranslation/NewTranslationComponent.kt
@@ -190,7 +190,10 @@ class DefaultNewTranslationComponent(
val resourceSlug = sourceTranslation.resourceSlug
val existingTranslation = getTargetTranslation(
TargetTranslation.generateTargetTranslationId(
- targetLanguage.slug, projectId, ResourceType.TEXT, resourceSlug
+ targetLanguageSlug = targetLanguage.slug,
+ projectSlug = projectId,
+ resourceType = ResourceType.TEXT,
+ resourceSlug = resourceSlug
)
)
@@ -212,7 +215,10 @@ class DefaultNewTranslationComponent(
sourceTranslation.changeTargetLanguage(targetLanguage)
sourceTranslation.normalizePath()
val newId = sourceTranslation.id
- moveTargetTranslationAppSettings(originalId, newId)
+ preference.moveTargetTranslationAppSettings(
+ originalId,
+ newId
+ )
withContext(Dispatchers.Main) {
onResult(NewTranslationComponent.Result.Success)
}
@@ -557,28 +563,6 @@ class DefaultNewTranslationComponent(
return translator.getTargetTranslation(translationId)
}
- private fun moveTargetTranslationAppSettings(
- targetTranslationId: String,
- newTargetTranslationId: String
- ) {
- val sources = preference.getOpenSourceTranslations(targetTranslationId)
- for (source in sources) {
- preference.addOpenSourceTranslation(newTargetTranslationId, source)
- }
-
- val source = preference.getSelectedSourceTranslationId(targetTranslationId)
- preference.setSelectedSourceTranslation(newTargetTranslationId, source)
-
- val lastFocusChapterId = preference.getLastFocusChapterId(targetTranslationId)
- val lastFocusFrameId = preference.getLastFocusFrameId(targetTranslationId)
- preference.setLastFocus(newTargetTranslationId, lastFocusChapterId, lastFocusFrameId)
-
- val lastViewMode = preference.getLastViewMode(targetTranslationId)
- preference.setLastViewMode(newTargetTranslationId, lastViewMode)
-
- preference.clearTargetTranslationSettings(targetTranslationId)
- }
-
private suspend fun createTargetTranslation(
projectId: String,
resourceType: ResourceType,
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/TranslateItem.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/TranslateItem.kt
index 2648fa6..1fc8799 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/TranslateItem.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/TranslateItem.kt
@@ -1,5 +1,6 @@
package org.bibletranslationtools.writer.ui.translate
+import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.text.AnnotatedString
import btt_writer.shared.generated.resources.Res
@@ -17,7 +18,7 @@ import org.bibletranslationtools.writer.core.ProjectTypeClass
import org.bibletranslationtools.writer.core.TranslationHelp
import org.bibletranslationtools.writer.ui.translate.review.TargetMode
import org.bibletranslationtools.writer.usecases.ParseMergeConflicts
-import org.bibletranslationtools.writer.utils.getStringBlocking
+import org.jetbrains.compose.resources.stringResource
interface Swipable {
@@ -69,17 +70,18 @@ abstract class TranslateItem {
// "Project Title", " Title", " :") —
// never from pt/ct content, which for helps projects holds JSON
open val targetTitle: String
+ @Composable
get() {
val language = chunk.target.targetLanguage.name
val book = chunk.source.project.name.trim()
val chapter = chunk.chapterSlug.toIntOrNull() ?: chunk.chapterSlug
+
return when {
chunk.isProjectTitle ->
- "${getStringBlocking(Res.string.project_title)} - $language"
- chunk.isChapterTitle ->
- "${getStringBlocking(Res.string.project_chapter_title, book, chapter)} - $language"
+ "${stringResource(Res.string.project_title)} - $language"
+ chunk.isChapterTitle -> "${stringResource(Res.string.project_chapter_title, book, chapter)} - $language"
chunk.isChapterReference ->
- "$book $chapter ${getStringBlocking(Res.string.reference)} - $language"
+ "$book $chapter ${stringResource(Res.string.reference)} - $language"
else -> {
val verseSpan = Frame.parseVerseTitle(sourceText, chunk.sourceTranslationFormat)
val span = verseSpan.ifEmpty {
@@ -163,6 +165,7 @@ data class ReadItem(
}
override val targetTitle: String
+ @Composable
get() = "${sourceTitle.trim()} - ${chunk.target.targetLanguage.name}"
}
@@ -210,6 +213,7 @@ data class ReviewItem(
get() = customSourceTitle ?: super.sourceTitle
override val targetTitle: String
+ @Composable
get() = if (projectTypeClass == ProjectTypeClass.EXTANT) {
// words are titled by the word itself, not chapter:verse
"$sourceTitle - ${chunk.target.targetLanguage.name}"
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/review/DefaultReviewModeComponent.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/review/DefaultReviewModeComponent.kt
index 24165d1..fe430b0 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/review/DefaultReviewModeComponent.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/ui/translate/review/DefaultReviewModeComponent.kt
@@ -52,6 +52,8 @@ import org.bibletranslationtools.writer.core.TranslationFormat
import org.bibletranslationtools.writer.core.TranslationHelp
import org.bibletranslationtools.writer.core.TranslationViewMode
import org.bibletranslationtools.writer.core.Translator
+import org.bibletranslationtools.writer.core.WORDS_CHAPTER
+import org.bibletranslationtools.writer.core.WORD_PATTERN
import org.bibletranslationtools.writer.core.launchWithProgress
import org.bibletranslationtools.writer.data.Preference
import org.bibletranslationtools.writer.data.getPref
@@ -94,8 +96,6 @@ class DefaultReviewModeComponent(
companion object {
private const val TAG = "ReviewModeComponent"
- private const val WORDS_CHAPTER = "01"
- private val WORD_PATTERN: Pattern = Pattern.compile("#+([^\\n]+)\\n+([\\s\\S]*)")
}
private val preference: Preference by inject()
diff --git a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/usecases/ValidateProject.kt b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/usecases/ValidateProject.kt
index 5c47232..0656127 100644
--- a/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/usecases/ValidateProject.kt
+++ b/shared/src/commonMain/kotlin/org/bibletranslationtools/writer/usecases/ValidateProject.kt
@@ -14,10 +14,11 @@ import org.bibletranslationtools.writer.core.TranslationFormat
import org.bibletranslationtools.writer.core.TranslationHelp
import org.bibletranslationtools.writer.core.Translator
import org.bibletranslationtools.writer.core.Validation
+import org.bibletranslationtools.writer.core.WORDS_CHAPTER
+import org.bibletranslationtools.writer.core.WORD_PATTERN
import org.bibletranslationtools.writer.utils.StringUtilities
import org.bibletranslationtools.writer.utils.sortedNumerically
import org.jetbrains.compose.resources.getString
-import java.util.regex.Pattern
class ValidateProject(
private val catalogClient: ResourceCatalogClient,
@@ -411,9 +412,4 @@ class ValidateProject(
val title = container.readChunk(chapterSlug, chunkSlug)
return title.trim() + " - " + type
}
-
- companion object {
- private const val WORDS_CHAPTER = "01"
- private val WORD_PATTERN: Pattern = Pattern.compile("#+([^\\n]+)\\n+([\\s\\S]*)")
- }
}
\ No newline at end of file
diff --git a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/home/FakeHomeComponent.kt b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/home/FakeHomeComponent.kt
index 1194ddf..59e67c3 100644
--- a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/home/FakeHomeComponent.kt
+++ b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/home/FakeHomeComponent.kt
@@ -34,6 +34,9 @@ class FakeHomeComponent : HomeComponent {
override suspend fun getLastOpened(): TargetTranslation? = null
override fun deleteProject(project: TranslationItem) {}
+ override fun changeResourceType(project: TranslationItem, resourceSlug: String) {}
+ override fun confirmResourceMerge() {}
+ override fun dismissResourceMerge() {}
override fun changeProjectSort(sort: ProjectSort) {}
override fun changeBookSort(sort: BookSort) {}
override fun showProjectInfo(item: TranslationItem) {}
diff --git a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/chunk/ChunkModeSectionTest.kt b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/chunk/ChunkModeSectionTest.kt
index 99f6975..75adb3a 100644
--- a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/chunk/ChunkModeSectionTest.kt
+++ b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/chunk/ChunkModeSectionTest.kt
@@ -50,7 +50,6 @@ class ChunkModeSectionTest : ScreenTestBase() {
val mockChunkItem = mockk(relaxed = true) {
every { id } returns "chunk_item_1"
every { sourceTitle } returns "Mock Source Title"
- every { targetTitle } returns "Mock Target Title"
every { renderedSourceText } returns AnnotatedString("Mock Source Text")
every { renderedTargetText } returns AnnotatedString("")
every { sourceOnTop } returns false
@@ -106,7 +105,6 @@ class ChunkModeSectionTest : ScreenTestBase() {
val mockChunkItem = mockk(relaxed = true) {
every { id } returns "chunk_item_1"
every { sourceTitle } returns "Mock Source Title"
- every { targetTitle } returns "Mock Target Title"
every { renderedSourceText } returns AnnotatedString("Mock Source Text")
every { renderedTargetText } returns AnnotatedString("")
every { sourceOnTop } returns false
diff --git a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/read/ReadModeSectionTest.kt b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/read/ReadModeSectionTest.kt
index 846ce07..b5db72b 100644
--- a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/read/ReadModeSectionTest.kt
+++ b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/read/ReadModeSectionTest.kt
@@ -47,7 +47,6 @@ class ReadModeSectionTest : ScreenTestBase() {
val mockReadItem = mockk(relaxed = true) {
every { id } returns "read_item_1"
every { sourceTitle } returns "Mock Source Title"
- every { targetTitle } returns "Mock Target Title"
every { renderedSourceText } returns AnnotatedString("Mock Source Text")
every { renderedTargetText } returns AnnotatedString("") // blank to trigger begin translating button
every { sourceOnTop } returns false
@@ -75,7 +74,7 @@ class ReadModeSectionTest : ScreenTestBase() {
)
}
- onNodeWithText("Mock Target Title", substring = true).assertIsDisplayed()
+ onNodeWithText("Mock Source Title", substring = true).assertIsDisplayed()
onNodeWithText(getStringBlocking(Res.string.begin_translating)).performClick()
assertEquals("3", beginTranslationCalledWith)
@@ -93,7 +92,6 @@ class ReadModeSectionTest : ScreenTestBase() {
val mockReadItem = mockk(relaxed = true) {
every { id } returns "read_item_1"
every { sourceTitle } returns "Mock Source Title"
- every { targetTitle } returns "Mock Target Title"
every { renderedSourceText } returns AnnotatedString("Mock Source Text")
every { renderedTargetText } returns AnnotatedString("Existing translation text")
every { sourceOnTop } returns false
diff --git a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/review/ReviewModeSectionTest.kt b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/review/ReviewModeSectionTest.kt
index 01cf564..ced2574 100644
--- a/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/review/ReviewModeSectionTest.kt
+++ b/shared/src/commonTest/kotlin/org/bibletranslationtools/writer/integration/ui/translate/review/ReviewModeSectionTest.kt
@@ -58,7 +58,6 @@ class ReviewModeSectionTest : ScreenTestBase() {
val mockReviewItem = mockk(relaxed = true) {
every { id } returns "review_item_1"
every { sourceTitle } returns "Mock Source Title"
- every { targetTitle } returns "Mock Target Title"
every { renderedSourceText } returns AnnotatedString("Mock Source Text")
every { renderedTargetText } returns AnnotatedString("Mock Target Text")
every { targetMode } returns TargetMode.MARKER
@@ -93,7 +92,7 @@ class ReviewModeSectionTest : ScreenTestBase() {
)
}
- onNodeWithText("Mock Target Title", substring = true).assertIsDisplayed()
+ onNodeWithText("Mock Source Title", substring = true).assertIsDisplayed()
onNodeWithContentDescription("toggle edit").performClick()
assertTrue(toggleEditCalled)
@@ -118,7 +117,6 @@ class ReviewModeSectionTest : ScreenTestBase() {
val mockReviewItem = mockk(relaxed = true) {
every { id } returns "review_item_1"
every { sourceTitle } returns "Mock Source Title"
- every { targetTitle } returns "Mock Target Title"
every { renderedSourceText } returns AnnotatedString("Mock Source Text")
every { renderedTargetText } returns AnnotatedString("Mock Target Text")
every { targetMode } returns TargetMode.EDIT
@@ -177,7 +175,6 @@ class ReviewModeSectionTest : ScreenTestBase() {
val mockReviewItem = mockk(relaxed = true) {
every { id } returns "review_item_1"
every { sourceTitle } returns "Mock Source Title"
- every { targetTitle } returns "Mock Target Title"
every { renderedSourceText } returns AnnotatedString("Mock Source Text")
every { renderedTargetText } returns AnnotatedString("Mock Target Text")
every { targetMode } returns TargetMode.MARKER