From 1eb3fe9e336de3fddd08fad1715c337c2a089d83 Mon Sep 17 00:00:00 2001 From: Viktor Kaderabek Date: Mon, 2 Mar 2026 15:07:35 +0100 Subject: [PATCH 1/7] =?UTF-8?q?=E2=9C=A8Get=20rid=20of=20twine=20replace?= =?UTF-8?q?=20with=20Sentiary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/an_pr_build.yml | 1 - build-logic/convention/build.gradle.kts | 5 + .../src/main/kotlin/config/TwineConfig.kt | 57 --------- .../AndroidApplicationConventionPlugin.kt | 3 - .../main/kotlin/plugin/SentiaryConvention.kt | 24 ++++ build.gradle.kts | 1 + gradle.properties | 3 + gradle/libs.versions.toml | 6 + ios/scripts/generate-strings.sh | 3 - shared/base/build.gradle.kts | 39 ++++++ .../moko-resources/base/strings.xml | 72 +++++------ .../commonMain/moko-resources/cs/strings.xml | 72 +++++------ .../commonMain/moko-resources/sk/strings.xml | 72 +++++------ twine/strings.txt | 119 ------------------ 14 files changed, 165 insertions(+), 312 deletions(-) delete mode 100644 build-logic/convention/src/main/kotlin/config/TwineConfig.kt create mode 100644 build-logic/convention/src/main/kotlin/plugin/SentiaryConvention.kt delete mode 100644 twine/strings.txt diff --git a/.github/workflows/an_pr_build.yml b/.github/workflows/an_pr_build.yml index 69829062..8fb57b8f 100644 --- a/.github/workflows/an_pr_build.yml +++ b/.github/workflows/an_pr_build.yml @@ -45,5 +45,4 @@ jobs: - name: Build run: | - ./gradlew generateTwine ./gradlew :android:app:bundleDebug diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 558cb80f..4f464eb3 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.compose.gradlePlugin) compileOnly(libs.ktlint.gradlePlugin) + compileOnly(libs.sentiary.gradlePlugin) } gradlePlugin { @@ -49,6 +50,10 @@ gradlePlugin { dependency = libs.plugins.mateeStarter.kmp.framework.library, pluginName = "KmpFrameworkLibraryConventionPlugin", ) + plugin( + dependency = libs.plugins.mateeStarter.kmp.sentiary, + pluginName = "SentiaryConvention", + ) } } diff --git a/build-logic/convention/src/main/kotlin/config/TwineConfig.kt b/build-logic/convention/src/main/kotlin/config/TwineConfig.kt deleted file mode 100644 index 948cd0f6..00000000 --- a/build-logic/convention/src/main/kotlin/config/TwineConfig.kt +++ /dev/null @@ -1,57 +0,0 @@ -package config - -import org.gradle.api.Project -import org.gradle.internal.os.OperatingSystem -import java.io.File - -fun Project.configureTwine() { - tasks.register("generateTwine") { - Twine.generateAllStringFiles( - project = project, - twineFile = "${rootProject.file("twine").absolutePath}/strings.txt", - targetPath = "${project.rootDir.absolutePath}/shared/base/src/commonMain/moko-resources", - targetFileName = "strings.xml", - languages = listOf("sk", "en", "cs"), - baseLanguage = "en", - ) - } -} - -private object Twine { - - fun generateAllStringFiles( - project: Project, - twineFile: String, - targetPath: String, - targetFileName: String, - languages: List, - baseLanguage: String, - ) { - val scripts = languages.map { language -> - val path = "$targetPath/${if (language == baseLanguage) "base" else language}" - val file = "$path/$targetFileName" - - File(path).mkdirs() - File(file).createNewFile() - - "twine generate-localization-file $twineFile $file -f android --lang $language" - } - scripts.forEach { script -> - project.exec { - // Add twine into path - // This should be also refactored - val twinePath = project.findProperty("twinePath") - if (twinePath != null) { - environment["PATH"] = - "${environment["PATH"]}${File.pathSeparator}$twinePath" - } - - if (OperatingSystem.current().isMacOsX || OperatingSystem.current().isLinux) { - commandLine("sh", "-c", script) - } else if (OperatingSystem.current().isWindows) { - commandLine("cmd", "/c", script) - } - } - } - } -} diff --git a/build-logic/convention/src/main/kotlin/plugin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/plugin/AndroidApplicationConventionPlugin.kt index 6d8d2481..82f1bacb 100644 --- a/build-logic/convention/src/main/kotlin/plugin/AndroidApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/plugin/AndroidApplicationConventionPlugin.kt @@ -5,7 +5,6 @@ import config.configureApplicationVariants import config.configureBuildVariants import config.configureKotlinAndroid import config.configureSingingConfigs -import config.configureTwine import constants.Application import extensions.apply import extensions.implementation @@ -59,8 +58,6 @@ class AndroidApplicationConventionPlugin : Plugin { configureApplicationVariants() } - configureTwine() - dependencies { implementation(libs.material) implementation(libs.androidX.core) diff --git a/build-logic/convention/src/main/kotlin/plugin/SentiaryConvention.kt b/build-logic/convention/src/main/kotlin/plugin/SentiaryConvention.kt new file mode 100644 index 00000000..4b65e5b8 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/plugin/SentiaryConvention.kt @@ -0,0 +1,24 @@ +package plugin + +import com.sentiary.SentiaryPluginExtension +import extensions.apply +import extensions.libs +import extensions.pluginManager +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.invoke + +class SentiaryConvention : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager { + apply(libs.plugins.sentiary) + } + + extensions.configure { + defaultLanguage.set("en-GB") + } + } + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 5c3b1175..73932bfe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,7 @@ plugins { alias(libs.plugins.jetbrains.compose.compiler) apply false alias(libs.plugins.versions) alias(libs.plugins.versionCatalogUpdate) + alias(libs.plugins.sentiary) apply false alias(libs.plugins.jetbrains.kotlin.jvm) apply false } diff --git a/gradle.properties b/gradle.properties index 672dc552..1f686da8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,9 @@ android.useAndroidX=true kotlin.mpp.androidSourceSetLayoutVersion=2 # Moko moko.resources.disableStaticFrameworkWarning=true +# Sentiary +sentiary.projectId=sL617ZwnlaW151ZJfDYc +sentiary.projectApiKey=tJr4F92ZaVxN4vBy/GFHICa9DhmimkhdBjETSAMXLxPn+j17ngXD8y+yQBvTgmuhhm3VvVRtv7qzpSoGvPLwNQ== # Caches kotlin.native.cacheKind.iosArm64=static kotlin.native.cacheKind.iosSimulatorArm64=static diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 530e6067..92195bda 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -43,6 +43,7 @@ skie = "0.10.6" firebase = "22.5.0" googleServices = "4.4.3" jetbrainsKotlinJvm = "2.2.10" +sentiary = "1.0.1" [libraries] # Kotlin @@ -131,6 +132,8 @@ molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref firebase-analytics = { module = "com.google.firebase:firebase-analytics-ktx", version.ref = "firebase" } # Ktlint ktlint-gradlePlugin = { module = "org.jlleitschuh.gradle.ktlint:org.jlleitschuh.gradle.ktlint.gradle.plugin", version.ref = "ktLint" } +# Sentiary +sentiary-gradlePlugin = { module = "com.sentiary:gradle-plugin", version.ref = "sentiary" } [bundles] settings = [ @@ -163,6 +166,8 @@ skie = { id = "co.touchlab.skie", version.ref = "skie" } google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } jetbrains-compose-plugin = { id = "org.jetbrains.compose", version.ref = "jetbrains-composePlugin" } jetbrains-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +sentiary = { id = "com.sentiary.gradle", version.ref = "sentiary" } + # Convention plugins mateeStarter-android-application-compose = "android-application-compose:none" mateeStarter-android-application-core = "android-application:none" @@ -171,4 +176,5 @@ mateeStarter-android-library-core = "android-library:none" mateeStarter-kmp-library-core = "kmp-library:none" mateeStarter-kmp-library-compose = "kmp-library-compose:none" mateeStarter-kmp-framework-library = "kmp-framework-library:none" +mateeStarter-kmp-sentiary = "kmp-sentiary-library-convention:none" jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" } diff --git a/ios/scripts/generate-strings.sh b/ios/scripts/generate-strings.sh index 98353959..f9ba33d0 100755 --- a/ios/scripts/generate-strings.sh +++ b/ios/scripts/generate-strings.sh @@ -5,8 +5,5 @@ cd "$(dirname "$0")" cd ../.. -echo "Generating Localizable files" -./gradlew generateTwine < /dev/null - echo "Generating MR resources from .xml files" ./gradlew :shared:base:generateMRcommonMain < /dev/null diff --git a/shared/base/build.gradle.kts b/shared/base/build.gradle.kts index 5068ae90..628d49b1 100644 --- a/shared/base/build.gradle.kts +++ b/shared/base/build.gradle.kts @@ -1,5 +1,8 @@ +import com.sentiary.config.Format + plugins { alias(libs.plugins.mateeStarter.kmp.library.compose) + alias(libs.plugins.mateeStarter.kmp.sentiary) } android { @@ -19,3 +22,39 @@ ktlint { dependencies { commonMainImplementation(libs.molecule.runtime) } + +tasks.named("generateMRcommonMain") { + dependsOn("sentiaryUpdateLocalizations") +} + +sentiary { + exportPaths { + fun String.dropSubtag() = split("-").first() + + create("Android") { + format.set(Format.Android) + outputDirectory.set(layout.projectDirectory.dir("src/commonMain/moko-resources")) + + folderNamingStrategy { language, isDefault -> + if (isDefault) { + "base" + } else { + language.dropSubtag() + } + } + } + + create("iOS") { + format.set(Format.Apple) + outputDirectory.set(rootProject.layout.projectDirectory.dir("ios/PresentationLayer/Sources/PresentationLayer/Sources/PresentationLayer/Resources/Localizable")) + + folderNamingStrategy { language, isDefault -> + if (isDefault) { + "Base.lproj" + } else { + "${language.dropSubtag()}.lproj" + } + } + } + } +} diff --git a/shared/base/src/commonMain/moko-resources/base/strings.xml b/shared/base/src/commonMain/moko-resources/base/strings.xml index 0ca2741c..5fca6f3b 100644 --- a/shared/base/src/commonMain/moko-resources/base/strings.xml +++ b/shared/base/src/commonMain/moko-resources/base/strings.xml @@ -1,44 +1,30 @@ - - - - + - - CS - Czech - SK - Slovak - EN - English - - - Sample feature - - - Done - Close - Back - Cancel - Skip - Next - Finish - Ok - Yes - No - Clear - - - Photo selection - Choose location of the photo - Camera - Library - Cancel - - - Close - - - Unknown error - User is not authorized - You are not connected to the internet. - + Close + Finish + Done + CS + Library + No + Clear + Unknown error + Photo selection + Close + Skip + English + Cancel + Yes + Next + Choose location of the photo + You are not connected to the internet. + Ok + Sample feature + Czech + Back + User is not authorized + Camera + Slovak + EN + SK + Cancel + \ No newline at end of file diff --git a/shared/base/src/commonMain/moko-resources/cs/strings.xml b/shared/base/src/commonMain/moko-resources/cs/strings.xml index c0fa3e62..ab3c6aa7 100644 --- a/shared/base/src/commonMain/moko-resources/cs/strings.xml +++ b/shared/base/src/commonMain/moko-resources/cs/strings.xml @@ -1,44 +1,30 @@ - - - - + - - CS - Čeština - SK - Slovenština - EN - Angličtina - - - Sample feature - - - Hotovo - Zavřít - Zpět - Zrušit - Přeskočit - Další - Dokončit - Ok - Ano - Ne - Vymazat - - - Výběr fotografie - Vyberte, odkud chcete fotografii nahrát. - Fotoaparát - Knihovna - Zrušit - - - Zavřít - - - Neznámá chyba - Uživatel není autorizován - Nejsi připojen k internetu. - + Zavřít + Dokončit + Hotovo + CS + Knihovna + Ne + Vymazat + Neznámá chyba + Výběr fotografie + Zavřít + Přeskočit + Angličtina + Zrušit + Ano + Další + Vyberte, odkud chcete fotografii nahrát. + Nejsi připojen k internetu. + Ok + Sample feature + Čeština + Zpět + Uživatel není autorizován + Fotoaparát + Slovenština + EN + SK + Zrušit + \ No newline at end of file diff --git a/shared/base/src/commonMain/moko-resources/sk/strings.xml b/shared/base/src/commonMain/moko-resources/sk/strings.xml index 93f187d3..3cca3b70 100644 --- a/shared/base/src/commonMain/moko-resources/sk/strings.xml +++ b/shared/base/src/commonMain/moko-resources/sk/strings.xml @@ -1,44 +1,30 @@ - - - - + - - CS - Čeština - SK - Slovenčina - EN - Angličtina - - - Sample feature - - - Hotovo - Zavrieť - Späť - Zrušiť - Preskočiť - Ďalší - Dokončiť - Ok - Áno - Nie - Vymazať - - - Výber fotografie - Vyberte, odkiaľ chcete fotografiu nahrať. - Fotoaparát - Knihovňa - Zrušiť - - - Zavrieť - - - Neznáma chyba - Užívateľ nie je autorizovaný - Nie ste pripojení k internetu. - + Zavrieť + Dokončiť + Hotovo + CS + Knihovňa + Nie + Vymazať + Neznáma chyba + Výber fotografie + Zavrieť + Preskočiť + Angličtina + Zrušiť + Áno + Ďalší + Vyberte, odkiaľ chcete fotografiu nahrať. + Nie ste pripojení k internetu. + Ok + Sample feature + Čeština + Späť + Užívateľ nie je autorizovaný + Fotoaparát + Slovenčina + EN + SK + Zrušiť + \ No newline at end of file diff --git a/twine/strings.txt b/twine/strings.txt deleted file mode 100644 index 93127953..00000000 --- a/twine/strings.txt +++ /dev/null @@ -1,119 +0,0 @@ -[[Languages]] - [language_cs] - cs = CS - en = CS - sk = CS - [language_cs_fullname] - cs = Čeština - en = Czech - sk = Čeština - [language_sk] - cs = SK - en = SK - sk = SK - [language_sk_fullname] - cs = Slovenština - en = Slovak - sk = Slovenčina - [language_en] - cs = EN - en = EN - sk = EN - [language_en_fullname] - cs = Angličtina - en = English - sk = Angličtina - -[[Screens]] - [sample_feature_title] - cs = Sample feature - en = Sample feature - sk = Sample feature - -[[General navigation]] - [done] - cs = Hotovo - en = Done - sk = Hotovo - [close] - cs = Zavřít - en = Close - sk = Zavrieť - [back] - cs = Zpět - en = Back - sk = Späť - [cancel] - cs = Zrušit - en = Cancel - sk = Zrušiť - [skip] - cs = Přeskočit - en = Skip - sk = Preskočiť - [next] - cs = Další - en = Next - sk = Ďalší - [finish] - cs = Dokončit - en = Finish - sk = Dokončiť - [ok] - cs = Ok - en = Ok - sk = Ok - [yes] - cs = Ano - en = Yes - sk = Áno - [no] - cs = Ne - en = No - sk = Nie - [clear] - cs = Vymazat - en = Clear - sk = Vymazať - -[[Image picker]] - [image_picker_title] - cs = Výběr fotografie - en = Photo selection - sk = Výber fotografie - [image_picker_subtitle] - cs = Vyberte, odkud chcete fotografii nahrát. - en = Choose location of the photo - sk = Vyberte, odkiaľ chcete fotografiu nahrať. - [image_picker_camera] - cs = Fotoaparát - en = Camera - sk = Fotoaparát - [image_picker_library] - cs = Knihovna - en = Library - sk = Knihovňa - [image_picker_cancel] - cs = Zrušit - en = Cancel - sk = Zrušiť - -[[General dialogs]] - [dialog_error_close_text] - cs = Zavřít - en = Close - sk = Zavrieť - -[[Errors]] - [unknown_error] - cs = Neznámá chyba - en = Unknown error - sk = Neznáma chyba - [not_authorized] - cs = Uživatel není autorizován - en = User is not authorized - sk = Užívateľ nie je autorizovaný - [error_no_internet_connection] - cs = Nejsi připojen k internetu. - en = You are not connected to the internet. - sk = Nie ste pripojení k internetu. From dfb50661ba312082fc0b342def2f4ea5e60d95c6 Mon Sep 17 00:00:00 2001 From: Viktor Kaderabek Date: Mon, 2 Mar 2026 15:11:04 +0100 Subject: [PATCH 2/7] setup.sh updated --- ios/scripts/setup.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ios/scripts/setup.sh b/ios/scripts/setup.sh index 5bf1df92..2ee7034c 100755 --- a/ios/scripts/setup.sh +++ b/ios/scripts/setup.sh @@ -15,14 +15,6 @@ else echo "✅ File header is properly set" fi -echo "⚙️ Checking whether Twine is installed" -if command -v twine &> /dev/null; then - echo "✅ Twine is installed" -else - echo "❌ Twine is not installed" - echo "Check https://github.com/MateeDevs/wiki/blob/master/tooling/ruby.md for more info" -fi - if [ ! -f ../../shared/core/src/commonMain/resources/MR/base/strings.xml ]; then echo "⚙️ Building Moko strings for the first time" ./generate-strings.sh From c2e53367e4f34ab3b636eab92ace8eb39ecc432f Mon Sep 17 00:00:00 2001 From: Viktor Kaderabek Date: Mon, 2 Mar 2026 15:40:49 +0100 Subject: [PATCH 3/7] readme --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c9324b55..46c903a9 100644 --- a/README.md +++ b/README.md @@ -460,13 +460,13 @@ The `AuthProviderImpl` will automatically use the provided `TokenRefresher` when ### Resources -#### Twine +#### Sentiary +All strings and localizations in the application are managed via the [Sentiary](https://sentiary.com/) platform. +We use the Sentiary Gradle Plugin to automatically fetch the latest translations during the build process. -All strings in the application are localized and shared with the iOS team -via [Twine](https://github.com/scelis/twine). Strings are stored in the `twine/strings.txt` file. -TwinePlugin then generates appropriate `strings.xml` files from the mentioned `strings.txt` file. -When modifying `strings.txt` it is required to comply with the specified syntax and to pull/push all -the changes frequently +The plugin is configured to automatically generate two formats simultaneously whenever you build the app: +- **Compose Resources**: Generated into the `commonMain` module for Android. +- **Apple Strings**: Native iOS `.strings` files (e.g., `Localizable.strings`) exported directly into the Xcode project structure. #### Moko From b7f82e8a3746aa1574862eb5b336f5d8e1d39e7a Mon Sep 17 00:00:00 2001 From: Viktor Kaderabek Date: Mon, 2 Mar 2026 16:05:14 +0100 Subject: [PATCH 4/7] remove file --- ios/MateeStarter.xcodeproj/project.pbxproj | 4 ---- ios/README.md | 16 +++++++--------- ios/scripts/rename.sh | 2 -- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/ios/MateeStarter.xcodeproj/project.pbxproj b/ios/MateeStarter.xcodeproj/project.pbxproj index 81622897..0a2f620d 100644 --- a/ios/MateeStarter.xcodeproj/project.pbxproj +++ b/ios/MateeStarter.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 4508332A28435E7800BC9460 /* SharedDomain in Frameworks */ = {isa = PBXBuildFile; productRef = 4508332928435E7800BC9460 /* SharedDomain */; }; 4508333028435E7900BC9460 /* Utilities in Frameworks */ = {isa = PBXBuildFile; productRef = 4508332F28435E7900BC9460 /* Utilities */; }; 4508333728437BB700BC9460 /* UIToolkit in Frameworks */ = {isa = PBXBuildFile; productRef = 4508333628437BB700BC9460 /* UIToolkit */; }; - 453580EF284806FE007AEC9E /* strings.txt in Resources */ = {isa = PBXBuildFile; fileRef = 453580EE284806FE007AEC9E /* strings.txt */; }; 454F6EC426CE8B3900C386B8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 454F6EC226CE8B3500C386B8 /* InfoPlist.strings */; }; 4558D30B20FCC8FF005BC325 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4558D30A20FCC8FF005BC325 /* AppDelegate.swift */; }; 4575E65625F6FA7600CCC003 /* AppIcon.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4575E65525F6FA7600CCC003 /* AppIcon.xcassets */; }; @@ -42,7 +41,6 @@ 4508333128436EFB00BC9460 /* UIToolkit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = UIToolkit; sourceTree = ""; }; 45238BDC2ACD7A5D0084E7E5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 4534C1C42603D6AD0022ACF4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; - 453580EE284806FE007AEC9E /* strings.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = strings.txt; path = ../../twine/strings.txt; sourceTree = ""; }; 453C8807283FAB4C0027BDFB /* SharedDomain */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = SharedDomain; sourceTree = ""; }; 454F6EC326CE8B3500C386B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; 454F6EC526CE8B4100C386B8 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -92,7 +90,6 @@ children = ( A3C5389B2F0EA941008A995E /* SampleFeature */, 4508333128436EFB00BC9460 /* UIToolkit */, - 453580EE284806FE007AEC9E /* strings.txt */, ); path = PresentationLayer; sourceTree = ""; @@ -279,7 +276,6 @@ files = ( 4575E65625F6FA7600CCC003 /* AppIcon.xcassets in Resources */, 7E277A062D8C65FA0035B990 /* GoogleService-Info-Prod.plist in Resources */, - 453580EF284806FE007AEC9E /* strings.txt in Resources */, 454F6EC426CE8B3900C386B8 /* InfoPlist.strings in Resources */, 7E277A082D8C663C0035B990 /* GoogleService-Info-Alpha.plist in Resources */, 4597626F20FDF0810034DE29 /* LaunchScreen.storyboard in Resources */, diff --git a/ios/README.md b/ios/README.md index d01872cf..de7b0ac8 100644 --- a/ios/README.md +++ b/ios/README.md @@ -60,15 +60,13 @@ ## Localization -### Twine - -- All strings in the application are localized and shared with the Android team - via [Twine](https://github.com/scelis/twine) -- Strings are stored in the `twine/strings.txt` file -- TwinePlugin then generates appropriate `Localizable.strings` files from the - mentioned `strings.txt` file -- When modifying `strings.txt` it is required to comply with the specified syntax and to pull/push - all the changes frequently +#### Sentiary +All strings and localizations in the application are managed via the [Sentiary](https://sentiary.com/) platform. +We use the Sentiary Gradle Plugin to automatically fetch the latest translations during the build process. + +The plugin is configured to automatically generate two formats simultaneously whenever you build the app: +- **Compose Resources**: Generated into the `commonMain` module for Android. +- **Apple Strings**: Native iOS `.strings` files (e.g., `Localizable.strings`) exported directly into the Xcode project structure. ### Moko diff --git a/ios/scripts/rename.sh b/ios/scripts/rename.sh index ef9d200b..abfc6253 100755 --- a/ios/scripts/rename.sh +++ b/ios/scripts/rename.sh @@ -28,7 +28,6 @@ mv ../${new_name}.xcodeproj/xcshareddata/xcschemes/${old_name}.xcscheme ../${new sed -i '' -e "s/${old_name}/${new_name}/g" ../${new_name}.xcodeproj/xcshareddata/xcschemes/${new_name}.xcscheme echo "Renaming support files" -sed -i '' -e "s/${old_name_lowercase}/${new_name_lowercase}/g" ../scripts/twine.sh sed -i '' -e "s/${old_name}/${new_name}/g" ../scripts/swiftlint-analyze.sh sed -i '' -e "s/${old_name}/${new_name}/g" ../fastlane/Fastfile sed -i '' -e "s/${old_name_lowercase}/${new_name_lowercase}/g" ../fastlane/Fastfile @@ -36,4 +35,3 @@ sed -i '' -e "s/${old_name}/${new_name}/g" ../.github/workflows/develop.yml sed -i '' -e "s/${old_name}/${new_name}/g" ../scripts/setup.sh echo "✅ Renaming successful" -echo "!!! Replace GoogleService-Info plists and ensure that twine directory exists !!!" From 613da7963e7f91127e541d12ecda7474d9279bf0 Mon Sep 17 00:00:00 2001 From: Viktor Kaderabek Date: Mon, 2 Mar 2026 16:23:37 +0100 Subject: [PATCH 5/7] PR fixes --- README.md | 3 ++- ios/.gitignore | 2 +- ios/README.md | 3 ++- scripts/rename-project.sh | 2 +- shared/base/src/commonMain/moko-resources/cs/strings.xml | 2 +- shared/base/src/commonMain/moko-resources/sk/strings.xml | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 46c903a9..48532c9b 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,7 @@ The project uses Gradle convention plugins (located in `build-logic/convention`) #### Android Modules - **`android-application-compose`** - For Android application modules with Compose support - Applies Android application plugin, Compose compiler, and Compose dependencies - - Configures build variants (alpha/production), signing, and Twine string generation + - Configures build variants (alpha/production), signing - **`android-application-core`** - For Android application modules without Compose - Same as above but without Compose configuration - **`android-library-compose`** - For Android library modules with Compose support @@ -461,6 +461,7 @@ The `AuthProviderImpl` will automatically use the provided `TokenRefresher` when ### Resources #### Sentiary + All strings and localizations in the application are managed via the [Sentiary](https://sentiary.com/) platform. We use the Sentiary Gradle Plugin to automatically fetch the latest translations during the build process. diff --git a/ios/.gitignore b/ios/.gitignore index b5d5c127..7dcddef8 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -13,7 +13,7 @@ DerivedData/ ## KMP generated DomainLayer/KMPShared.xcframework -## Twine generated +## Sentiary generated Localizable.strings ## Generated code diff --git a/ios/README.md b/ios/README.md index de7b0ac8..adbf3e8e 100644 --- a/ios/README.md +++ b/ios/README.md @@ -60,7 +60,8 @@ ## Localization -#### Sentiary +### Sentiary + All strings and localizations in the application are managed via the [Sentiary](https://sentiary.com/) platform. We use the Sentiary Gradle Plugin to automatically fetch the latest translations during the build process. diff --git a/scripts/rename-project.sh b/scripts/rename-project.sh index 35981904..ff39e43c 100755 --- a/scripts/rename-project.sh +++ b/scripts/rename-project.sh @@ -158,7 +158,7 @@ The project uses Gradle convention plugins (located in \`build-logic/convention\ #### Android Modules - **\`android-application-compose\`** - For Android application modules with Compose support - Applies Android application plugin, Compose compiler, and Compose dependencies - - Configures build variants (alpha/production), signing, and Twine string generation + - Configures build variants (alpha/production), signing - **\`android-application-core\`** - For Android application modules without Compose - Same as above but without Compose configuration - **\`android-library-compose\`** - For Android library modules with Compose support diff --git a/shared/base/src/commonMain/moko-resources/cs/strings.xml b/shared/base/src/commonMain/moko-resources/cs/strings.xml index ab3c6aa7..a46e6a38 100644 --- a/shared/base/src/commonMain/moko-resources/cs/strings.xml +++ b/shared/base/src/commonMain/moko-resources/cs/strings.xml @@ -18,7 +18,7 @@ Vyberte, odkud chcete fotografii nahrát. Nejsi připojen k internetu. Ok - Sample feature + Ukázková funkcionalita Čeština Zpět Uživatel není autorizován diff --git a/shared/base/src/commonMain/moko-resources/sk/strings.xml b/shared/base/src/commonMain/moko-resources/sk/strings.xml index 3cca3b70..095a1ad2 100644 --- a/shared/base/src/commonMain/moko-resources/sk/strings.xml +++ b/shared/base/src/commonMain/moko-resources/sk/strings.xml @@ -18,7 +18,7 @@ Vyberte, odkiaľ chcete fotografiu nahrať. Nie ste pripojení k internetu. Ok - Sample feature + Ukážková funkcionalita Čeština Späť Užívateľ nie je autorizovaný From 8c7a62a6b2da9e1c7731f9b1c772fcdb2303e6c6 Mon Sep 17 00:00:00 2001 From: Viktor Kaderabek Date: Mon, 2 Mar 2026 16:46:44 +0100 Subject: [PATCH 6/7] readme changed --- README.md | 7 ++----- ios/README.md | 8 ++------ shared/base/build.gradle.kts | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 48532c9b..9aa7fc37 100644 --- a/README.md +++ b/README.md @@ -471,11 +471,8 @@ The plugin is configured to automatically generate two formats simultaneously wh #### Moko -Error messages are shared via [Moko Resources](https://github.com/icerockdev/moko-resources), so -that we can use the strings in the shared code and avoid duplicities when converting errors to -string messages. Error strings are stored in the `twine/errors.txt` file. Gradle task -`generateErrorsTwine` first generates `strings.xml` files from `errors.txt` and then gradle task -`generateMRCommonMain` generates `MR` class that can be used in the common code. +Strings are shared via [Moko Resources](https://github.com/icerockdev/moko-resources), so +that we can use the strings in the shared code ### UI - Compose Multiplatform diff --git a/ios/README.md b/ios/README.md index adbf3e8e..6acea639 100644 --- a/ios/README.md +++ b/ios/README.md @@ -71,12 +71,8 @@ The plugin is configured to automatically generate two formats simultaneously wh ### Moko -- Error messages are shared via [Moko Resources](https://github.com/icerockdev/moko-resources), so - that we can use the strings in the shared code and avoid duplicities when converting errors to - string messages -- Error strings are stored in the `twine/errors.txt` file -- Script `generate-error-messages.sh` calls needed gradle tasks (`generateErrorsTwine` - and `generateMRCommonMain`) to generate `MR` class +Strings are shared via [Moko Resources](https://github.com/icerockdev/moko-resources), so +that we can use the strings in the shared code ## Debug diff --git a/shared/base/build.gradle.kts b/shared/base/build.gradle.kts index 628d49b1..94fd12b3 100644 --- a/shared/base/build.gradle.kts +++ b/shared/base/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { commonMainImplementation(libs.molecule.runtime) } -tasks.named("generateMRcommonMain") { +tasks.named("prepareComposeResourcesTaskForCommonMain") { dependsOn("sentiaryUpdateLocalizations") } From aecdcc8a0fc34ba98d2cb9e4a7ed7f0619bba241 Mon Sep 17 00:00:00 2001 From: Viktor Kaderabek Date: Mon, 2 Mar 2026 16:51:09 +0100 Subject: [PATCH 7/7] remove whitespace --- ios/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/README.md b/ios/README.md index 6acea639..c0635ec8 100644 --- a/ios/README.md +++ b/ios/README.md @@ -72,7 +72,7 @@ The plugin is configured to automatically generate two formats simultaneously wh ### Moko Strings are shared via [Moko Resources](https://github.com/icerockdev/moko-resources), so -that we can use the strings in the shared code +that we can use the strings in the shared code ## Debug