mirror of
https://github.com/Darkrock-Studios/hammer-editor.git
synced 2026-10-19 17:43:21 +00:00
Add Crowdin screenshot upload task and taller screen previews
- uploadCrowdinScreenshots Gradle task (composeUi/crowdin-screenshots.gradle.kts): renders Compose previews and uploads the tablet screen previews to Crowdin with auto-tagging. Cross-platform via ProcessBuilder; auth from -P props, env vars, or interactive prompt. Defaults to Screen*Tablet*.png. - crowdin.yml: declare env-based project_id/api_token. - TABLET_TALL_HEIGHT_DP for screens whose content scrolls past a normal tablet height; applied to ProjectStats and ViewEntry tablet previews so their full content renders instead of clipping.
This commit is contained in:
parent
44d252d0dc
commit
21d1719bd4
6 changed files with 130 additions and 5 deletions
|
|
@ -149,4 +149,6 @@ kover {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply(from = "crowdin-screenshots.gradle.kts")
|
||||
113
composeUi/crowdin-screenshots.gradle.kts
Normal file
113
composeUi/crowdin-screenshots.gradle.kts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// Renders Compose previews and uploads them to Crowdin with automatic string
|
||||
// tagging. Auto-tag is a best-effort text match; mono labels and format
|
||||
// strings (%1$s) usually still need a manual pass in the Crowdin editor.
|
||||
//
|
||||
// ./gradlew :composeUi:uploadCrowdinScreenshots
|
||||
//
|
||||
// By default only the tablet-form screen previews (Screen*Tablet*.png) are
|
||||
// uploaded.
|
||||
//
|
||||
// Auth resolves in order: -Pcrowdin.projectId / -Pcrowdin.token, then the
|
||||
// CROWDIN_PROJECT_ID / CROWDIN_PERSONAL_TOKEN env vars, then an interactive
|
||||
// prompt (token hidden). Interactive prompting needs a real console — run with
|
||||
// --console=plain --no-daemon if a prompt appears blank.
|
||||
//
|
||||
// Options (Gradle properties):
|
||||
// -Pcrowdin.noRender Skip the render step; upload existing PNGs.
|
||||
// -Pcrowdin.screenshots.pattern=X Filename glob to override the default, e.g. 'Screen*'.
|
||||
// -Pcrowdin.branch=NAME Attach screenshots to a Crowdin branch.
|
||||
|
||||
fun globToRegex(glob: String): Regex {
|
||||
val sb = StringBuilder()
|
||||
for (c in glob) when (c) {
|
||||
'*' -> sb.append(".*")
|
||||
'?' -> sb.append('.')
|
||||
'.', '(', ')', '+', '|', '^', '$', '@', '%', '\\', '{', '}', '[', ']' ->
|
||||
sb.append('\\').append(c)
|
||||
else -> sb.append(c)
|
||||
}
|
||||
return Regex(sb.toString(), RegexOption.IGNORE_CASE)
|
||||
}
|
||||
|
||||
fun promptFor(message: String, hidden: Boolean): String? {
|
||||
val console = System.console()
|
||||
val value = if (console != null) {
|
||||
if (hidden) console.readPassword(message)?.concatToString()
|
||||
else console.readLine(message)
|
||||
} else {
|
||||
print(message)
|
||||
System.out.flush()
|
||||
readlnOrNull()
|
||||
}
|
||||
return value?.trim()?.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
tasks.register("uploadCrowdinScreenshots") {
|
||||
group = "crowdin"
|
||||
description = "Render Compose previews and upload them to Crowdin with auto-tagging."
|
||||
|
||||
if (!providers.gradleProperty("crowdin.noRender").isPresent) {
|
||||
dependsOn("composePreviewRender")
|
||||
}
|
||||
|
||||
val rendersDir = layout.buildDirectory.dir("compose-previews/renders")
|
||||
val patternProp = providers.gradleProperty("crowdin.screenshots.pattern").orNull
|
||||
val branchProp = providers.gradleProperty("crowdin.branch").orNull
|
||||
val projectIdProp = providers.gradleProperty("crowdin.projectId").orNull
|
||||
val tokenProp = providers.gradleProperty("crowdin.token").orNull
|
||||
val repoRoot = rootProject.layout.projectDirectory.asFile
|
||||
val isWindows = System.getProperty("os.name").lowercase().contains("win")
|
||||
|
||||
doLast {
|
||||
val dir = rendersDir.get().asFile
|
||||
require(dir.isDirectory) {
|
||||
"No renders at ${dir.path}. Run without -Pcrowdin.noRender first."
|
||||
}
|
||||
|
||||
val glob = patternProp ?: "Screen*Tablet*.png"
|
||||
val regex = globToRegex(glob)
|
||||
val files = dir.listFiles { f ->
|
||||
f.isFile && f.name.endsWith(".png", ignoreCase = true) && regex.matches(f.name)
|
||||
}?.sortedBy { it.name }.orEmpty()
|
||||
require(files.isNotEmpty()) { "No PNGs in ${dir.path} matching '$glob'." }
|
||||
|
||||
val projectId = projectIdProp
|
||||
?: System.getenv("CROWDIN_PROJECT_ID")
|
||||
?: promptFor("Crowdin project ID: ", hidden = false)
|
||||
?: throw GradleException("No Crowdin project ID provided.")
|
||||
val token = tokenProp
|
||||
?: System.getenv("CROWDIN_PERSONAL_TOKEN")
|
||||
?: promptFor("Crowdin personal token (hidden): ", hidden = true)
|
||||
?: throw GradleException("No Crowdin personal token provided.")
|
||||
|
||||
val launcher = if (isWindows) listOf("cmd", "/c", "crowdin") else listOf("crowdin")
|
||||
|
||||
logger.lifecycle("Uploading ${files.size} screenshot(s) to Crowdin with auto-tagging")
|
||||
var failed = 0
|
||||
for (f in files) {
|
||||
logger.lifecycle("--> ${f.name}")
|
||||
val command = launcher + listOf(
|
||||
"screenshot", "upload", f.absolutePath,
|
||||
"--name", f.name,
|
||||
"--auto-tag",
|
||||
"--update-strings",
|
||||
) + (branchProp?.let { listOf("--branch", it) } ?: emptyList())
|
||||
|
||||
val process = ProcessBuilder(command)
|
||||
.directory(repoRoot)
|
||||
.inheritIO()
|
||||
.apply {
|
||||
environment()["CROWDIN_PROJECT_ID"] = projectId
|
||||
environment()["CROWDIN_PERSONAL_TOKEN"] = token
|
||||
}
|
||||
.start()
|
||||
if (process.waitFor() != 0) {
|
||||
failed++
|
||||
logger.error(" upload failed for ${f.name}")
|
||||
}
|
||||
}
|
||||
|
||||
if (failed > 0) throw GradleException("$failed screenshot upload(s) failed.")
|
||||
logger.lifecycle("Done. Review tag placement in the Crowdin editor.")
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,13 @@ import com.darkrockstudios.apps.hammer.common.uiNeedsExplicitCloseButtons
|
|||
const val TABLET_WIDTH_DP: Int = 1280
|
||||
const val TABLET_HEIGHT_DP: Int = 800
|
||||
|
||||
/**
|
||||
* Extra-tall canvas for screens whose content scrolls well past a normal tablet
|
||||
* height (e.g. project stats, encyclopedia entries). Use it as the `heightDp` so
|
||||
* the whole screen renders for translation screenshots instead of being clipped.
|
||||
*/
|
||||
const val TABLET_TALL_HEIGHT_DP: Int = 1400
|
||||
|
||||
/**
|
||||
* Wraps [content] in the app theme and a wide [ScreenCharacteristics] so screens
|
||||
* that branch on `LocalScreenCharacteristic` render their expanded/wide layouts
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import com.darkrockstudios.apps.hammer.common.compose.rememberRootSnackbarHostState
|
||||
import com.darkrockstudios.apps.hammer.common.encyclopedia.ViewEntryUi
|
||||
import com.darkrockstudios.apps.hammer.common.preview.KoinApplicationPreview
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TABLET_HEIGHT_DP
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TABLET_TALL_HEIGHT_DP
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TABLET_WIDTH_DP
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TabletPreviewSurface
|
||||
import com.darkrockstudios.apps.hammer.common.preview.fakeViewEntryComponent
|
||||
|
|
@ -37,7 +37,7 @@ fun ScreenViewEntryUiPreview() {
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Preview(widthDp = TABLET_WIDTH_DP, heightDp = TABLET_HEIGHT_DP)
|
||||
@Preview(widthDp = TABLET_WIDTH_DP, heightDp = TABLET_TALL_HEIGHT_DP)
|
||||
@Composable
|
||||
fun ScreenViewEntryUiTabletPreview() {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import com.darkrockstudios.apps.hammer.common.data.projectbackup.ProjectBackupDe
|
|||
import com.darkrockstudios.apps.hammer.common.data.projectstatistics.EntryAppearance
|
||||
import com.darkrockstudios.apps.hammer.common.fileio.HPath
|
||||
import com.darkrockstudios.apps.hammer.common.preview.KoinApplicationPreview
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TABLET_HEIGHT_DP
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TABLET_TALL_HEIGHT_DP
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TABLET_WIDTH_DP
|
||||
import com.darkrockstudios.apps.hammer.common.preview.TabletPreviewSurface
|
||||
import com.darkrockstudios.apps.hammer.common.preview.dummyBackHandler
|
||||
|
|
@ -57,7 +57,7 @@ fun ScreenProjectStatsUiPreview() {
|
|||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = TABLET_WIDTH_DP, heightDp = TABLET_HEIGHT_DP)
|
||||
@Preview(widthDp = TABLET_WIDTH_DP, heightDp = TABLET_TALL_HEIGHT_DP)
|
||||
@Composable
|
||||
fun ScreenProjectStatsUiTabletPreview() {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
project_id_env: CROWDIN_PROJECT_ID
|
||||
api_token_env: CROWDIN_PERSONAL_TOKEN
|
||||
|
||||
files:
|
||||
- source: /common/src/commonMain/composeResources/values/strings*.xml
|
||||
translation: /common/src/commonMain/composeResources/values-%android_code%/%original_file_name%
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue