diff --git a/.github/workflows/ios-develop.yml b/.github/workflows/ios-develop.yml
index e51eba6a..b1e1dc0e 100644
--- a/.github/workflows/ios-develop.yml
+++ b/.github/workflows/ios-develop.yml
@@ -21,7 +21,7 @@ jobs:
- name: Build KMP
working-directory: ios
run: |
- ./scripts/generate-error-messages.sh
+ ./scripts/generate-strings.sh
./scripts/build-kmp.sh release false true false
- name: Setup tools
working-directory: ios
diff --git a/.github/workflows/ios-release.yml b/.github/workflows/ios-release.yml
index 7d11f45a..3ba5fd73 100644
--- a/.github/workflows/ios-release.yml
+++ b/.github/workflows/ios-release.yml
@@ -16,7 +16,7 @@ jobs:
- name: Build KMP
working-directory: ios
run: |
- ./scripts/generate-error-messages.sh
+ ./scripts/generate-strings.sh
./scripts/build-kmp.sh release false true false
- name: Setup tools
working-directory: ios
diff --git a/.github/workflows/ios_pr_build.yml b/.github/workflows/ios_pr_build.yml
index 0e92b9a8..ba381f9b 100644
--- a/.github/workflows/ios_pr_build.yml
+++ b/.github/workflows/ios_pr_build.yml
@@ -20,10 +20,10 @@ jobs:
working-directory: ios
run: |
./scripts/setup.sh
- - name: Generate Error Messages
+ - name: Generate Strings
working-directory: ios
run: |
- ./scripts/generate-error-messages.sh
+ ./scripts/generate-strings.sh
- name: Build KMP
working-directory: ios
run: |
diff --git a/android/app/src/main/kotlin/kmp/android/navigation/NavBarFeature.kt b/android/app/src/main/kotlin/kmp/android/navigation/NavBarFeature.kt
index ec8a60b7..23b1fd97 100644
--- a/android/app/src/main/kotlin/kmp/android/navigation/NavBarFeature.kt
+++ b/android/app/src/main/kotlin/kmp/android/navigation/NavBarFeature.kt
@@ -1,13 +1,14 @@
package kmp.android.navigation
-import androidx.annotation.StringRes
+import dev.icerock.moko.resources.desc.StringDesc
+import dev.icerock.moko.resources.desc.desc
import kmp.android.sample.navigation.SampleGraph
import kmp.android.samplecomposemultiplatform.navigation.SampleComposeMultiplatformGraph
import kmp.android.samplesharedviewmodel.navigation.SampleSharedViewModelGraph
-import kmp.android.shared.R
+import kmp.shared.base.MR
-enum class NavBarFeature(val route: String, @StringRes val titleRes: Int) {
- Sample(SampleGraph.rootPath, R.string.bottom_bar_item_1),
- SampleSharedViewModel(SampleSharedViewModelGraph.rootPath, R.string.bottom_bar_item_2),
- SampleComposeMultiplatform(SampleComposeMultiplatformGraph.rootPath, R.string.bottom_bar_item_3),
+enum class NavBarFeature(val route: String, val titleRes: StringDesc) {
+ Sample(SampleGraph.rootPath, MR.strings.bottom_bar_item_1.desc()),
+ SampleSharedViewModel(SampleSharedViewModelGraph.rootPath, MR.strings.bottom_bar_item_2.desc()),
+ SampleComposeMultiplatform(SampleComposeMultiplatformGraph.rootPath, MR.strings.bottom_bar_item_3.desc()),
}
diff --git a/android/app/src/main/kotlin/kmp/android/ui/Root.kt b/android/app/src/main/kotlin/kmp/android/ui/Root.kt
index 1c1871da..5b658d1b 100644
--- a/android/app/src/main/kotlin/kmp/android/ui/Root.kt
+++ b/android/app/src/main/kotlin/kmp/android/ui/Root.kt
@@ -18,12 +18,12 @@ import androidx.compose.material.primarySurface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
+import dev.icerock.moko.resources.compose.localized
import kmp.android.navigation.NavBarFeature
import kmp.android.sample.navigation.SampleGraph
import kmp.android.sample.navigation.sampleNavGraph
@@ -84,7 +84,7 @@ private fun BottomBar(navController: NavHostController, modifier: Modifier = Mod
)
}
},
- label = { Text(stringResource(screen.titleRes)) },
+ label = { Text(screen.titleRes.localized()) },
selected = currentRoute?.startsWith(screen.route + "/") ?: false,
onClick = {
navController.navigate(screen.route) {
diff --git a/android/shared/src/main/res/values-en/generated_strings.xml b/android/shared/src/main/res/values-en/generated_strings.xml
deleted file mode 100644
index c05dd63b..00000000
--- a/android/shared/src/main/res/values-en/generated_strings.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
- CS
- Czech
- SK
- Slovak
- EN
- English
-
-
- Classic
- Shared VMs
- Compose Multiplatform
-
-
- Done
- Close
- Back
- Cancel
- Skip
- Next
- Finish
- Ok
- Yes
- No
- Clear
-
-
- Photo selection
- Choose location of the photo
- Camera
- Library
- Cancel
-
-
- Close
-
diff --git a/android/shared/src/main/res/values-sk/generated_strings.xml b/android/shared/src/main/res/values-sk/generated_strings.xml
deleted file mode 100644
index a93001bc..00000000
--- a/android/shared/src/main/res/values-sk/generated_strings.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
- CS
- Čeština
- SK
- Slovenčina
- EN
- Angličtina
-
-
- Classic
- Shared VMs
- Compose Multiplatform
-
-
- 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ť
-
diff --git a/android/shared/src/main/res/values/generated_strings.xml b/android/shared/src/main/res/values/generated_strings.xml
deleted file mode 100644
index d7089307..00000000
--- a/android/shared/src/main/res/values/generated_strings.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
- CS
- Čeština
- SK
- Slovenština
- EN
- Angličtina
-
-
- Classic
- Shared VMs
- Compose Multiplatform
-
-
- 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
-
diff --git a/build-logic/convention/src/main/kotlin/config/TwineConfig.kt b/build-logic/convention/src/main/kotlin/config/TwineConfig.kt
index 3b8ce652..948cd0f6 100644
--- a/build-logic/convention/src/main/kotlin/config/TwineConfig.kt
+++ b/build-logic/convention/src/main/kotlin/config/TwineConfig.kt
@@ -6,17 +6,9 @@ import java.io.File
fun Project.configureTwine() {
tasks.register("generateTwine") {
- Twine.generateAllRegularFiles(
+ Twine.generateAllStringFiles(
project = project,
twineFile = "${rootProject.file("twine").absolutePath}/strings.txt",
- moduleName = "android/shared",
- )
- }
-
- tasks.register("generateErrorsTwine") {
- Twine.generateAllErrorFiles(
- project = project,
- twineFile = "${rootProject.file("twine").absolutePath}/errors.txt",
targetPath = "${project.rootDir.absolutePath}/shared/base/src/commonMain/moko-resources",
targetFileName = "strings.xml",
languages = listOf("sk", "en", "cs"),
@@ -27,38 +19,7 @@ fun Project.configureTwine() {
private object Twine {
- fun generateAllRegularFiles(
- project: Project,
- moduleName: String,
- twineFile: String,
- ) {
- val script =
- when {
- OperatingSystem.current().isLinux || OperatingSystem.current().isMacOsX ->
- "twine generate-all-localization-files $twineFile ${project.rootDir.absolutePath}/$moduleName/src/main/res/ -f android -n generated_strings.xml -d en -r"
-
- OperatingSystem.current().isWindows -> "twine generate-all-localization-files $twineFile ${project.rootDir.absolutePath}/$moduleName/src/main/res/ -f android -n generated_strings.xml -d en -r"
- else -> "unsupported"
- }
-
- project.exec {
- // Add twine into path
- // This should be also refactored
- val twinePath = project.findProperty("twinePath")
- if (twinePath != null) {
- environment["PATH"] =
- "${environment["PATH"]}${System.getProperty("path.separator")}$twinePath"
- }
-
- if (OperatingSystem.current().isMacOsX || OperatingSystem.current().isLinux) {
- this.commandLine("sh", "-c", script)
- } else if (OperatingSystem.current().isWindows) {
- this.commandLine("cmd", "/c", script)
- }
- }
- }
-
- fun generateAllErrorFiles(
+ fun generateAllStringFiles(
project: Project,
twineFile: String,
targetPath: String,
diff --git a/ios/Application/MainFlowController.swift b/ios/Application/MainFlowController.swift
index 2403ca28..5415e4ad 100644
--- a/ios/Application/MainFlowController.swift
+++ b/ios/Application/MainFlowController.swift
@@ -3,6 +3,7 @@
// Copyright © 2019 Matee. All rights reserved.
//
+import KMPShared
import Sample
import SampleComposeMultiplatform
import SampleSharedViewModel
@@ -28,7 +29,7 @@ final class MainFlowController: FlowController {
private func setupSampleTab() -> UINavigationController {
let sampleNC = BaseNavigationController(statusBarStyle: .lightContent)
sampleNC.tabBarItem = UITabBarItem(
- title: L10n.bottom_bar_item_1,
+ title: MR.strings().bottom_bar_item_1.toLocalized(),
image: AppTheme.Images.person,
tag: MainTab.sample.rawValue
)
@@ -41,7 +42,7 @@ final class MainFlowController: FlowController {
private func setupSampleSharedViewModelTab() -> UINavigationController {
let sampleSharedViewModelNC = BaseNavigationController(statusBarStyle: .lightContent)
sampleSharedViewModelNC.tabBarItem = UITabBarItem(
- title: L10n.bottom_bar_item_2,
+ title: MR.strings().bottom_bar_item_2.toLocalized(),
image: AppTheme.Images.personCirle,
tag: MainTab.sampleSharedViewModel.rawValue
)
@@ -54,7 +55,7 @@ final class MainFlowController: FlowController {
private func setupSampleComposeMultiplatformTab() -> UINavigationController {
let sampleComposeMultiplatformNC = BaseNavigationController(statusBarStyle: .lightContent)
sampleComposeMultiplatformNC.tabBarItem = UITabBarItem(
- title: L10n.bottom_bar_item_3,
+ title: MR.strings().bottom_bar_item_3.toLocalized(),
image: AppTheme.Images.personSquare,
tag: MainTab.sampleComposeMultiplatform.rawValue
)
diff --git a/ios/MateeStarter.xcodeproj/xcshareddata/xcschemes/MateeStarter.xcscheme b/ios/MateeStarter.xcodeproj/xcshareddata/xcschemes/MateeStarter.xcscheme
index 7cdb621f..1826c91f 100644
--- a/ios/MateeStarter.xcodeproj/xcshareddata/xcschemes/MateeStarter.xcscheme
+++ b/ios/MateeStarter.xcodeproj/xcshareddata/xcschemes/MateeStarter.xcscheme
@@ -9,8 +9,8 @@
+ title = "Generate strings"
+ scriptText = "if [ -z "$CI" ]; then
cd "${PROJECT_DIR}"
scripts/generate-strings.sh
fi
">
+ title = "Generate strings"
+ scriptText = "if [ -z "$CI" ]; then
cd "${PROJECT_DIR}"
scripts/generate-strings.sh
fi
">
+ title = "Generate strings"
+ scriptText = "if [ -z "$CI" ]; then
cd "${PROJECT_DIR}"
scripts/generate-strings.sh
fi
">
(
get: { viewModel.state.toast },
set: { toast in viewModel.onIntent(.onToastChanged(data: toast)) }
@@ -56,6 +56,7 @@ import DependencyInjectionMocks
import Factory
#Preview {
+ fixMokoResourcesForPreviews()
Container.shared.registerUseCaseMocks()
let vm = SampleViewModel(flowController: nil)
diff --git a/ios/PresentationLayer/SampleComposeMultiplatform/Sources/SampleComposeMultiplatform/SampleComposeMultiplatformView.swift b/ios/PresentationLayer/SampleComposeMultiplatform/Sources/SampleComposeMultiplatform/SampleComposeMultiplatformView.swift
index 76d7d6c9..f264c036 100644
--- a/ios/PresentationLayer/SampleComposeMultiplatform/Sources/SampleComposeMultiplatform/SampleComposeMultiplatformView.swift
+++ b/ios/PresentationLayer/SampleComposeMultiplatform/Sources/SampleComposeMultiplatform/SampleComposeMultiplatformView.swift
@@ -31,6 +31,6 @@ struct SampleComposeMultiplatformView: View {
)
}
.toastView($toastData)
- .navigationTitle(L10n.bottom_bar_item_3)
+ .navigationTitle(MR.strings().bottom_bar_item_3.toLocalized())
}
}
diff --git a/ios/PresentationLayer/SampleSharedViewModel/Sources/SampleSharedViewModel/Main/SampleSharedViewModelView.swift b/ios/PresentationLayer/SampleSharedViewModel/Sources/SampleSharedViewModel/Main/SampleSharedViewModelView.swift
index 3e844113..9f2420ca 100644
--- a/ios/PresentationLayer/SampleSharedViewModel/Sources/SampleSharedViewModel/Main/SampleSharedViewModelView.swift
+++ b/ios/PresentationLayer/SampleSharedViewModel/Sources/SampleSharedViewModel/Main/SampleSharedViewModelView.swift
@@ -36,7 +36,7 @@ struct SampleSharedViewModelView: View {
}
}
}
- .navigationTitle(L10n.bottom_bar_item_2)
+ .navigationTitle(MR.strings().bottom_bar_item_2.toLocalized())
.onAppear {
viewModel.onIntent(intent: SampleSharedIntentOnAppeared())
}
@@ -59,6 +59,7 @@ import DependencyInjectionMocks
import Factory
#Preview {
+ fixMokoResourcesForPreviews()
Container.shared.registerUseCaseMocks()
Container.shared.registerViewModelMocks()
diff --git a/ios/PresentationLayer/UIToolkit/Package.swift b/ios/PresentationLayer/UIToolkit/Package.swift
index 106f81e7..f5198a03 100644
--- a/ios/PresentationLayer/UIToolkit/Package.swift
+++ b/ios/PresentationLayer/UIToolkit/Package.swift
@@ -18,6 +18,7 @@ let package = Package(
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(name: "Utilities", path: "../../DomainLayer/Utilities"),
+ .package(name: "SharedDomain", path: "../../DomainLayer/SharedDomain"),
.package(url: "https://github.com/SwiftGen/SwiftGenPlugin", .upToNextMajor(from: "6.6.0")),
.package(url: "https://github.com/SFSafeSymbols/SFSafeSymbols.git", .upToNextMajor(from: "5.3.0"))
],
@@ -28,21 +29,16 @@ let package = Package(
name: "UIToolkit",
dependencies: [
.product(name: "Utilities", package: "Utilities"),
+ .product(name: "SharedDomain", package: "SharedDomain"),
.product(name: "SFSafeSymbols", package: "SFSafeSymbols")
],
exclude: [
- "swiftgen-strings.stencil",
"swiftgen-xcassets.stencil",
"swiftgen.yml"
],
plugins: [
- "TwinePlugin",
.plugin(name: "SwiftGenPlugin", package: "SwiftGenPlugin")
]
- ),
- .plugin(
- name: "TwinePlugin",
- capability: .buildTool()
)
]
)
diff --git a/ios/PresentationLayer/UIToolkit/Plugins/TwinePlugin/TwinePlugin.swift b/ios/PresentationLayer/UIToolkit/Plugins/TwinePlugin/TwinePlugin.swift
deleted file mode 100644
index 6b4e9371..00000000
--- a/ios/PresentationLayer/UIToolkit/Plugins/TwinePlugin/TwinePlugin.swift
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// Created by Petr Chmelar on 02.08.2023
-// Copyright © 2023 Matee. All rights reserved.
-//
-
-import Foundation
-import PackagePlugin
-
-@main
-struct TwinePlugin: BuildToolPlugin {
-
- func createBuildCommands(
- context: PluginContext,
- target: Target
- ) throws -> [Command] {
- let rootPath = context.package.directory
- .removingLastComponent()
- .removingLastComponent()
-
- return [
- .prebuildCommand(
- displayName: "Run Twine",
- executable: rootPath.appending(["scripts", "twine.sh"]),
- arguments: [],
- environment: [
- "DERIVED_SOURCES_DIR": context.pluginWorkDirectory
- ],
- outputFilesDirectory: context.pluginWorkDirectory
- )
- ]
- }
-}
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/SwiftUI/UIHostingController/BaseHostingController.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/SwiftUI/UIHostingController/BaseHostingController.swift
index a707c7c0..9ffe9d07 100644
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/SwiftUI/UIHostingController/BaseHostingController.swift
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/SwiftUI/UIHostingController/BaseHostingController.swift
@@ -3,6 +3,7 @@
// Copyright © 2022 Matee. All rights reserved.
//
+import KMPShared
import OSLog
import SwiftUI
import Utilities
@@ -39,6 +40,6 @@ public class BaseHostingController: UIHostingController where
private func setupUI() {
// Setup background color and back button title
view.backgroundColor = UIColor(AppTheme.Colors.background)
- navigationItem.backBarButtonItem = UIBarButtonItem(title: L10n.back, style: .plain, target: nil, action: nil)
+ navigationItem.backBarButtonItem = UIBarButtonItem(title: MR.strings().back.toLocalized(), style: .plain, target: nil, action: nil)
}
}
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/BaseViewController.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/BaseViewController.swift
index fe5a7e84..0c35b409 100644
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/BaseViewController.swift
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/BaseViewController.swift
@@ -3,6 +3,7 @@
// Copyright © 2017 Matee. All rights reserved.
//
+import KMPShared
import OSLog
import UIKit
import Utilities
@@ -36,6 +37,6 @@ public class BaseViewController: UIViewController {
open func setupUI() {
// Setup background color and back button title
view.backgroundColor = UIColor(AppTheme.Colors.background)
- navigationItem.backBarButtonItem = UIBarButtonItem(title: L10n.back, style: .plain, target: nil, action: nil)
+ navigationItem.backBarButtonItem = UIBarButtonItem(title: MR.strings().back.toLocalized(), style: .plain, target: nil, action: nil)
}
}
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/ImagePickerViewController.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/ImagePickerViewController.swift
index f96d3433..7e4dd681 100644
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/ImagePickerViewController.swift
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/BaseViews/UIKit/UIViewController/ImagePickerViewController.swift
@@ -3,6 +3,7 @@
// Copyright © 2018 Matee. All rights reserved.
//
+import KMPShared
import UIKit
@objc public protocol ImagePickerViewControllerDelegate: AnyObject {
@@ -12,8 +13,8 @@ import UIKit
public final class ImagePickerViewController: BaseViewController {
// MARK: Stored properties
- public var imagePickerTitle: String = L10n.image_picker_title
- public var imagePickerSubtitle: String = L10n.image_picker_subtitle
+ public var imagePickerTitle: String = MR.strings().image_picker_title.toLocalized()
+ public var imagePickerSubtitle: String = MR.strings().image_picker_subtitle.toLocalized()
public weak var delegate: ImagePickerViewControllerDelegate?
@@ -25,17 +26,17 @@ public final class ImagePickerViewController: BaseViewController {
// Setup action sheet with camera/library options
let actionSheetController = UIAlertController(title: imagePickerTitle, message: imagePickerSubtitle, preferredStyle: .actionSheet)
- let photoLibrary = UIAlertAction(title: L10n.image_picker_library, style: .default, handler: { _ in
+ let photoLibrary = UIAlertAction(title: MR.strings().image_picker_library.toLocalized(), style: .default, handler: { _ in
self.selectPhoto(sourceType: .photoLibrary)
})
actionSheetController.addAction(photoLibrary)
- let takePhotoByCamera = UIAlertAction(title: L10n.image_picker_camera, style: .default, handler: { _ in
+ let takePhotoByCamera = UIAlertAction(title: MR.strings().image_picker_camera.toLocalized(), style: .default, handler: { _ in
self.selectPhoto(sourceType: .camera)
})
actionSheetController.addAction(takePhotoByCamera)
- let cancel = UIAlertAction(title: L10n.image_picker_cancel, style: .cancel, handler: nil)
+ let cancel = UIAlertAction(title: MR.strings().image_picker_cancel.toLocalized(), style: .cancel, handler: nil)
actionSheetController.addAction(cancel)
// Required for iPad
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Extensions/Bundle+Extensions.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Extensions/Bundle+Extensions.swift
deleted file mode 100644
index 99e29813..00000000
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Extensions/Bundle+Extensions.swift
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// Created by Petr Chmelar on 30.05.2022
-// Copyright © 2022 Matee. All rights reserved.
-//
-
-import Foundation
-
-extension Bundle {
- /// Workaround for crashing SwiftUI Previews when any of underlying modules uses .module
- /// Taken from: https://developer.apple.com/forums/thread/664295
- static var myModule: Bundle = {
- /* The name of your local package, prepended by "LocalPackages_" for iOS and "PackageName_" for macOS. You may have same PackageName and TargetName*/
- let bundleNameIOS = "LocalPackages_UIToolkit"
- let bundleNameMacOs = "UIToolkit_UIToolkit"
- let candidates = [
- /* Bundle should be present here when the package is linked into an App. */
- Bundle.main.resourceURL,
- /* Bundle should be present here when the package is linked into a framework. */
- Bundle(for: BundleToken.self).resourceURL,
- /* For command-line tools. */
- Bundle.main.bundleURL,
- /* Bundle should be present here when running previews from a different package (this is the path to "…/Debug-iphonesimulator/"). */
- Bundle(for: BundleToken.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent(),
- Bundle(for: BundleToken.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent()
- ]
-
- for candidate in candidates {
- let bundlePathiOS = candidate?.appendingPathComponent(bundleNameIOS + ".bundle")
- let bundlePathMacOS = candidate?.appendingPathComponent(bundleNameMacOs + ".bundle")
- if let bundle = bundlePathiOS.flatMap(Bundle.init(url:)) {
- return bundle
- } else if let bundle = bundlePathMacOS.flatMap(Bundle.init(url:)) {
- return bundle
- }
- }
- fatalError("unable to find bundle")
- }()
-}
-
-private final class BundleToken {}
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Extensions/Moko+Extensions.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Extensions/Moko+Extensions.swift
new file mode 100644
index 00000000..8a9510cf
--- /dev/null
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Extensions/Moko+Extensions.swift
@@ -0,0 +1,14 @@
+//
+// Created by Lukáš Matuška on 10.10.2024
+// Copyright © 2024 Matee. All rights reserved.
+//
+
+import Foundation
+import KMPShared
+
+public extension StringResource {
+
+ func toLocalized() -> String {
+ return self.desc().localized()
+ }
+}
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Alerts/AlertData.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Alerts/AlertData.swift
index 86de856a..74e6e658 100644
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Alerts/AlertData.swift
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Alerts/AlertData.swift
@@ -4,6 +4,7 @@
//
import Foundation
+import KMPShared
public struct AlertData: Equatable, Identifiable {
@@ -17,7 +18,7 @@ public struct AlertData: Equatable, Identifiable {
public init(
title: String,
message: String? = nil,
- primaryAction: AlertData.Action = AlertData.Action(title: L10n.dialog_error_close_text, style: .default),
+ primaryAction: AlertData.Action = AlertData.Action(title: MR.strings().dialog_error_close_text.toLocalized(), style: .default),
secondaryAction: AlertData.Action? = nil
) {
self.title = title
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/MokoFix.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/MokoFix.swift
new file mode 100644
index 00000000..9c6cb74c
--- /dev/null
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/MokoFix.swift
@@ -0,0 +1,21 @@
+//
+// Created by Julia Jakubcova on 11/10/2024
+// Copyright © 2024 Matee. All rights reserved.
+//
+
+import Foundation
+import KMPShared
+
+ #warning("TODO: Remove this workaround when issue [https://github.com/icerockdev/moko-resources/issues/714] is resolved")
+ public func fixMokoResourcesForTests() {
+ if ProcessInfo.processInfo.processName == "xctest" {
+ MokoResourcesWorkaroundKt.nsBundle = Bundle(for: KotlinBase.self)
+ }
+ }
+
+ #warning("TODO: Remove this workaround when issue [https://github.com/icerockdev/moko-resources/issues/714] is resolved")
+ public func fixMokoResourcesForPreviews() {
+ if ProcessInfo.processInfo.processName == "XCPreviewAgent" {
+ MokoResourcesWorkaroundKt.nsBundle = Bundle(for: KotlinBase.self)
+ }
+ }
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Plurals.swift b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Plurals.swift
index 52eeaedb..28e83260 100644
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Plurals.swift
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/Utilities/Plurals.swift
@@ -22,15 +22,15 @@ public enum Plurals: String {
func stringForCount(_ count: Int) -> String {
if count == 0 { // swiftlint:disable:this empty_count
- return String(format: NSLocalizedString("zero_\(rawValue)", bundle: .myModule, comment: ""), count)
+ return String(format: NSLocalizedString("zero_\(rawValue)", bundle: .module, comment: ""), count)
} else if abs(count) == 1 {
- return String(format: NSLocalizedString("one_\(rawValue)", bundle: .myModule, comment: ""), count)
+ return String(format: NSLocalizedString("one_\(rawValue)", bundle: .module, comment: ""), count)
} else if abs(count) > 1 && abs(count) < 5 {
- return String(format: NSLocalizedString("few_\(rawValue)", bundle: .myModule, comment: ""), count)
+ return String(format: NSLocalizedString("few_\(rawValue)", bundle: .module, comment: ""), count)
} else if abs(count) >= 5 {
- return String(format: NSLocalizedString("many_\(rawValue)", bundle: .myModule, comment: ""), count)
+ return String(format: NSLocalizedString("many_\(rawValue)", bundle: .module, comment: ""), count)
} else {
- return String(format: NSLocalizedString("other_\(rawValue)", bundle: .myModule, comment: ""), count)
+ return String(format: NSLocalizedString("other_\(rawValue)", bundle: .module, comment: ""), count)
}
}
}
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen-strings.stencil b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen-strings.stencil
deleted file mode 100644
index 3fb3ceae..00000000
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen-strings.stencil
+++ /dev/null
@@ -1,115 +0,0 @@
-// Template is based on the default strings/flat-swift5.stencil
-// https://github.com/SwiftGen/SwiftGen/blob/stable/Sources/SwiftGenCLI/templates/strings/flat-swift5.stencil
-// Revision: 7e13d641745b56775d9a7f983a5468d2d9952ada
-
-// Modifications:
-// - Use `.myModule` [workaround](https://developer.apple.com/forums/thread/664295) to address SwiftUI Previews crashes
-// - Use snake_case instead of camelCase, this is done by changing `swiftIdentifier:"pretty"` to `swiftIdentifier`
-// - Ability to change localization via Environment.localization
-
-// swiftlint:disable all
-// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
-
-{% if tables.count > 0 %}
-{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
-import Foundation
-import Utilities
-
-// swiftlint:disable superfluous_disable_command file_length implicit_return prefer_self_in_static_references
-
-// MARK: - Strings
-
-{% macro parametersBlock types %}
- {%- for type in types -%}
- {%- if type == "String" -%}
- _ p{{forloop.counter}}: Any
- {%- else -%}
- _ p{{forloop.counter}}: {{type}}
- {%- endif -%}
- {{ ", " if not forloop.last }}
- {%- endfor -%}
-{% endmacro %}
-{% macro argumentsBlock types %}
- {%- for type in types -%}
- {%- if type == "String" -%}
- String(describing: p{{forloop.counter}})
- {%- elif type == "UnsafeRawPointer" -%}
- Int(bitPattern: p{{forloop.counter}})
- {%- else -%}
- p{{forloop.counter}}
- {%- endif -%}
- {{ ", " if not forloop.last }}
- {%- endfor -%}
-{% endmacro %}
-{% macro recursiveBlock table item %}
- {% for string in item.strings %}
- {% if not param.noComments %}
- {% for line in string.comment|default:string.translation|split:"\n" %}
- /// {{line}}
- {% endfor %}
- {% endif %}
- {% set translation string.translation|replace:'"','\"'|replace:' ','\t' %}
- {% if string.types %}
- {{accessModifier}} static func {{string.key|swiftIdentifier|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String {
- return {{enumName}}.tr("{{table}}", "{{string.key}}", {%+ call argumentsBlock string.types %}, fallback: "{{translation}}")
- }
- {% elif param.lookupFunction %}
- {{accessModifier}} static var {{string.key|swiftIdentifier|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}", fallback: "{{translation}}") }
- {% else %}
- {{accessModifier}} static let {{string.key|swiftIdentifier|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}", fallback: "{{translation}}")
- {% endif %}
- {% endfor %}
- {% for child in item.children %}
- {% call recursiveBlock table child %}
- {% endfor %}
-{% endmacro %}
-// swiftlint:disable function_parameter_count identifier_name line_length type_body_length
-{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %}
-{{accessModifier}} enum {{enumName}} {
- {% if tables.count > 1 or param.forceFileNameEnum %}
- {% for table in tables %}
- {{accessModifier}} enum {{table.name|swiftIdentifier|escapeReservedKeywords}} {
- {% filter indent:2," ",true %}{% call recursiveBlock table.name table.levels %}{% endfilter %}
- }
- {% endfor %}
- {% else %}
- {% call recursiveBlock tables.first.name tables.first.levels %}
- {% endif %}
-}
-// swiftlint:enable function_parameter_count identifier_name line_length type_body_length
-
-// MARK: - Implementation Details
-
-extension {{enumName}} {
- private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String {
- if let preview = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"], preview == "1",
- let path = BundleToken.bundle.path(forResource: Environment.locale.identifier, ofType: "lproj"), let bundle = Bundle(path: path) {
- let format = bundle.localizedString(forKey: key, value: nil, table: table)
- return String(format: format, locale: Environment.locale, arguments: args)
- } else {
- {% if param.lookupFunction %}
- let format = {{ param.lookupFunction }}(key, table)
- {% else %}
- let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table)
- {% endif %}
- return String(format: format, locale: Locale.current, arguments: args)
- }
- }
-}
-{% if not param.bundle and not param.lookupFunction %}
-
-// swiftlint:disable convenience_type
-private final class BundleToken {
- static let bundle: Bundle = {
- #if SWIFT_PACKAGE
- return Bundle.myModule
- #else
- return Bundle(for: BundleToken.self)
- #endif
- }()
-}
-// swiftlint:enable convenience_type
-{% endif %}
-{% else %}
-// No string found
-{% endif %}
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen-xcassets.stencil b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen-xcassets.stencil
index cbdc65c9..0797b806 100644
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen-xcassets.stencil
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen-xcassets.stencil
@@ -3,7 +3,6 @@
// Revision: 937d05fa32ff389c63f93f0f9e2919d2c461b3c9
// Modifications:
-// - Use `.myModule` [workaround](https://developer.apple.com/forums/thread/664295) to address SwiftUI Previews crashes
// - Ensure SwiftUI-first behavior for colors by renaming `.color` to `.uiColor` and `.swiftUIColor` to `.color`
// - Ensure SwiftUI-first behavior for images by renaming `.image` to `.uiImage` and `.swiftUIImage` to `.image`
@@ -433,7 +432,7 @@
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
- return Bundle.myModule
+ return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
diff --git a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen.yml b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen.yml
index aed05306..b8686e92 100644
--- a/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen.yml
+++ b/ios/PresentationLayer/UIToolkit/Sources/UIToolkit/swiftgen.yml
@@ -1,14 +1,5 @@
output_dir: ${DERIVED_SOURCES_DIR}
-strings:
- inputs:
- - ${DERIVED_SOURCES_DIR}/../TwinePlugin/Base.lproj/Localizable.strings
- outputs:
- - templatePath: swiftgen-strings.stencil
- output: Localizable.swift
- params:
- publicAccess: true
-
xcassets:
inputs:
- Resources/Colors.xcassets
diff --git a/ios/scripts/generate-error-messages.sh b/ios/scripts/generate-strings.sh
similarity index 54%
rename from ios/scripts/generate-error-messages.sh
rename to ios/scripts/generate-strings.sh
index 87783401..417197bd 100755
--- a/ios/scripts/generate-error-messages.sh
+++ b/ios/scripts/generate-strings.sh
@@ -5,8 +5,8 @@ cd "$(dirname "$0")"
cd ../..
-echo "Generating Localizable files for error messages in specified languages"
-./gradlew generateErrorsTwine
+echo "Generating Localizable files"
+./gradlew generateTwine
echo "Generating MR resources from .xml files"
-./gradlew :shared:base:generateMRcommonMain
\ No newline at end of file
+./gradlew :shared:base:generateMRcommonMain
diff --git a/ios/scripts/setup.sh b/ios/scripts/setup.sh
index 887625df..ca9c6860 100755
--- a/ios/scripts/setup.sh
+++ b/ios/scripts/setup.sh
@@ -15,16 +15,6 @@ else
echo "✅ File header is properly set"
fi
-if [ ! -f ../../shared/core/src/commonMain/resources/MR/base/strings.xml ]; then
- echo "⚙️ Building Moko error strings for the first time"
- ./generate-error-messages.sh
-fi
-
-if [ ! -d ../DomainLayer/KMPShared.xcframework ]; then
- echo "⚙️ Building KMP for the first time"
- ./build-kmp.sh
-fi
-
echo "⚙️ Checking whether Twine is installed"
if command -v twine &> /dev/null; then
echo "✅ Twine is installed"
@@ -33,6 +23,16 @@ else
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
+fi
+
+if [ ! -d ../DomainLayer/KMPShared.xcframework ]; then
+ echo "⚙️ Building KMP for the first time"
+ ./build-kmp.sh
+fi
+
echo "⚙️ Checking whether Homebrew is installed"
if command -v brew &> /dev/null; then
echo "✅ Homebrew is installed"
@@ -46,7 +46,6 @@ echo "⚙️ Checking whether SwiftLint is installed"
if command -v swiftlint &> /dev/null; then
echo "✅ SwiftLint is installed"
else
- echo "❌ SwiftLint is not installed"
- echo "Trying to install swiftlint"
+ echo "❌ SwiftLint is not installed - installing now"
brew install swiftlint
-fi
\ No newline at end of file
+fi
diff --git a/ios/scripts/twine.sh b/ios/scripts/twine.sh
deleted file mode 100755
index dcdbc431..00000000
--- a/ios/scripts/twine.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/zsh -l
-
-# This ensures that relative paths are correct no matter where the script is executed
-cd "$(dirname "$0")"
-
-echo "⚙️ Generating Localizable files for specified languages"
-mkdir ${DERIVED_SOURCES_DIR}/Base.lproj
-twine generate-localization-file ../../twine/strings.txt ${DERIVED_SOURCES_DIR}/Base.lproj/Localizable.strings --lang en
-mkdir ${DERIVED_SOURCES_DIR}/cs.lproj
-twine generate-localization-file ../../twine/strings.txt ${DERIVED_SOURCES_DIR}/cs.lproj/Localizable.strings --lang cs
-mkdir ${DERIVED_SOURCES_DIR}/sk.lproj
-twine generate-localization-file ../../twine/strings.txt ${DERIVED_SOURCES_DIR}/sk.lproj/Localizable.strings --lang sk
-mkdir ${DERIVED_SOURCES_DIR}/en.lproj
-twine generate-localization-file ../../twine/strings.txt ${DERIVED_SOURCES_DIR}/en.lproj/Localizable.strings --lang en
diff --git a/shared/base/src/commonMain/moko-resources/base/strings.xml b/shared/base/src/commonMain/moko-resources/base/strings.xml
index ac64f1ae..c0e7eb36 100644
--- a/shared/base/src/commonMain/moko-resources/base/strings.xml
+++ b/shared/base/src/commonMain/moko-resources/base/strings.xml
@@ -1,9 +1,45 @@
-
+
-
+
+ CS
+ Czech
+ SK
+ Slovak
+ EN
+ English
+
+
+ Classic
+ Shared VMs
+ Compose Multiplatform
+
+
+ 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.
diff --git a/shared/base/src/commonMain/moko-resources/cs/strings.xml b/shared/base/src/commonMain/moko-resources/cs/strings.xml
index 9f412fb3..8193659f 100644
--- a/shared/base/src/commonMain/moko-resources/cs/strings.xml
+++ b/shared/base/src/commonMain/moko-resources/cs/strings.xml
@@ -1,9 +1,45 @@
-
+
-
+
+ CS
+ Čeština
+ SK
+ Slovenština
+ EN
+ Angličtina
+
+
+ Classic
+ Shared VMs
+ Compose Multiplatform
+
+
+ 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.
diff --git a/shared/base/src/commonMain/moko-resources/sk/strings.xml b/shared/base/src/commonMain/moko-resources/sk/strings.xml
index 3e4e8fe5..0ef029d5 100644
--- a/shared/base/src/commonMain/moko-resources/sk/strings.xml
+++ b/shared/base/src/commonMain/moko-resources/sk/strings.xml
@@ -1,9 +1,45 @@
-
+
-
+
+ CS
+ Čeština
+ SK
+ Slovenčina
+ EN
+ Angličtina
+
+
+ Classic
+ Shared VMs
+ Compose Multiplatform
+
+
+ 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.
diff --git a/shared/base/src/iosMain/kotlin/dev/icerock/moko/resources/utils/MokoResourcesWorkaround.kt b/shared/base/src/iosMain/kotlin/dev/icerock/moko/resources/utils/MokoResourcesWorkaround.kt
new file mode 100644
index 00000000..9f036f3f
--- /dev/null
+++ b/shared/base/src/iosMain/kotlin/dev/icerock/moko/resources/utils/MokoResourcesWorkaround.kt
@@ -0,0 +1,36 @@
+package dev.icerock.moko.resources.utils // must be same as in moko-resources to override method
+
+import platform.Foundation.NSBundle
+import platform.Foundation.NSDirectoryEnumerator
+import platform.Foundation.NSFileManager
+import platform.Foundation.NSURL
+import platform.Foundation.pathExtension
+
+/**
+ * Workaround by MAX-POLKOVNIK from https://github.com/icerockdev/moko-resources/issues/747#issuecomment-2330854244
+ * Remove when https://github.com/icerockdev/moko-resources/issues/747 is resolved
+ */
+
+var nsBundle: NSBundle = NSBundle.mainBundle // <-- this is where we should looking for resources, by default mainBundle
+
+fun NSBundle.Companion.loadableBundle(identifier: String): NSBundle {
+ val bundlePath: String = nsBundle.bundlePath // <-- path where we should search for bundle with resources
+ val enumerator: NSDirectoryEnumerator = requireNotNull(NSFileManager.defaultManager.enumeratorAtPath(bundlePath))
+ while (true) {
+ val relativePath: String = enumerator.nextObject() as? String ?: break
+ val url = NSURL(fileURLWithPath = relativePath)
+ if (url.pathExtension == "bundle") {
+ val fullPath = "$bundlePath/$relativePath"
+ val foundedBundle: NSBundle? = NSBundle.bundleWithPath(fullPath)
+ val loadedIdentifier: String? = foundedBundle?.bundleIdentifier
+
+ if (isBundleSearchLogEnabled) {
+ println("moko-resources auto-load bundle with identifier $loadedIdentifier at path $fullPath")
+ }
+
+ if (foundedBundle?.bundleIdentifier == identifier) return foundedBundle
+ }
+ }
+
+ throw IllegalArgumentException("bundle with identifier $identifier not found")
+}
diff --git a/twine/errors.txt b/twine/errors.txt
deleted file mode 100644
index c340ea8c..00000000
--- a/twine/errors.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-[[General]]
- [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.
diff --git a/twine/strings.txt b/twine/strings.txt
index 16fec51b..c56054a7 100644
--- a/twine/strings.txt
+++ b/twine/strings.txt
@@ -111,3 +111,17 @@
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.