diff --git a/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDesc.kt b/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDesc.kt index 176410408..156892dfc 100644 --- a/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDesc.kt +++ b/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDesc.kt @@ -5,6 +5,7 @@ package dev.icerock.moko.resources.desc import dev.icerock.moko.resources.PluralsResource +import dev.icerock.moko.resources.desc.Utils.BASE_LOCALIZATION import kotlinx.cinterop.BetaInteropApi import platform.Foundation.NSBundle import platform.Foundation.NSLocale @@ -39,10 +40,13 @@ internal fun pluralizedString( resourceId: String, number: Int ): String { + val fallbackLocale = bundle.developmentLocalization ?: BASE_LOCALIZATION val localized = bundle .localizedStringForKey(resourceId, null, null) .takeUnless { it == resourceId } ?: baseBundle.localizedStringForKey(resourceId, null, null) + .takeUnless { it == resourceId } ?: StringDesc.LocaleType.Custom(fallbackLocale) + .getLocaleBundle(bundle).localizedStringForKey(resourceId, null, null) @Suppress("CAST_NEVER_SUCCEEDS") return NSString.create( format = localized, @@ -50,3 +54,4 @@ internal fun pluralizedString( args = arrayOf(number) ) as String } + diff --git a/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/Utils.kt b/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/Utils.kt index 3dd0f7220..1320113bc 100644 --- a/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/Utils.kt +++ b/resources/src/appleMain/kotlin/dev/icerock/moko/resources/desc/Utils.kt @@ -9,16 +9,43 @@ import platform.Foundation.NSString import platform.Foundation.stringWithFormat object Utils { + const val BASE_LOCALIZATION: String = "Base" + fun processArgs(args: List): Array { return args.map { (it as? StringDesc)?.localized() ?: it }.toTypedArray() } fun localizedString(stringRes: StringResource): String { val bundle = StringDesc.localeType.getLocaleBundle(stringRes.bundle) - val string = bundle.localizedStringForKey(stringRes.resourceId, null, null) - return if (string == stringRes.resourceId) { - stringRes.bundle.localizedStringForKey(stringRes.resourceId, null, null) - } else string + val stringInCurrentLocale = bundle.localizedStringForKey( + key = stringRes.resourceId, + value = null, + table = null + ) + + return if (stringInCurrentLocale == stringRes.resourceId) { + val stringInDefaultBundle = stringRes.bundle.localizedStringForKey( + key = stringRes.resourceId, + value = null, + table = null + ) + + if (stringInDefaultBundle == stringRes.resourceId) { + val fallbackLocale = stringRes.bundle.developmentLocalization ?: BASE_LOCALIZATION + val fallbackLocaleBundle = StringDesc.LocaleType + .Custom(fallbackLocale) + .getLocaleBundle(stringRes.bundle) + fallbackLocaleBundle.localizedStringForKey( + key = stringRes.resourceId, + value = null, + table = null + ) + } else { + stringInDefaultBundle + } + } else { + stringInCurrentLocale + } } fun stringWithFormat(format: String, args: Array): String { diff --git a/resources/src/iosTest/kotlin/dev/icerock/moko/resources/desc/AppleLocalizationBundleTests.kt b/resources/src/iosTest/kotlin/dev/icerock/moko/resources/desc/AppleLocalizationBundleTests.kt new file mode 100644 index 000000000..b9b0eb509 --- /dev/null +++ b/resources/src/iosTest/kotlin/dev/icerock/moko/resources/desc/AppleLocalizationBundleTests.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2024 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.resources.desc + +import dev.icerock.moko.resources.StringResource +import platform.Foundation.NSBundle +import kotlin.test.Test +import kotlin.test.assertEquals + +class AppleLocalizationBundleTests { + + @Test + fun localizedStringWithLocalizationCaseTest() { + val resource = StringResource( + resourceId = "noResultsFound", + bundle = NSBundle.bundleWithPath(NSBundle.mainBundle.bundlePath + "/tests.bundle")!! + ) + StringDesc.localeType = StringDesc.LocaleType.Custom("es-US") + val stringDesc = ResourceStringDesc( + resource + ) + assertEquals( + expected = "No se han encontrado resultados", + actual = stringDesc.localized() + ) + StringDesc.localeType = StringDesc.LocaleType.System + } + + @Test + fun localizedStringMissingLocalizationCaseTest() { + val resource = StringResource( + resourceId = "noInternetConnection", + bundle = NSBundle.bundleWithPath(NSBundle.mainBundle.bundlePath + "/tests.bundle")!! + ) + StringDesc.localeType = StringDesc.LocaleType.Custom("es-US") + val stringDesc = ResourceStringDesc( + resource + ) + assertEquals( + expected = "No internet connection", + actual = stringDesc.localized() + ) + StringDesc.localeType = StringDesc.LocaleType.System + } +} diff --git a/resources/src/iosTest/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDescStringTests.kt b/resources/src/iosTest/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDescStringTests.kt index a6a9eb6ea..7326b2b89 100644 --- a/resources/src/iosTest/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDescStringTests.kt +++ b/resources/src/iosTest/kotlin/dev/icerock/moko/resources/desc/PluralFormattedStringDescStringTests.kt @@ -50,6 +50,16 @@ class PluralFormattedStringDescStringTests { ) } + @Test + fun testMissingLocalizationCase() { + StringDesc.localeType = StringDesc.LocaleType.Custom("es-US") + assertEquals( + expected = "6/10 items", + actual = createPluralFormatted(6).localized() + ) + StringDesc.localeType = StringDesc.LocaleType.System + } + private fun createPluralFormatted(number: Int): PluralFormattedStringDesc { val pluralResource = PluralsResource( resourceId = "stringFormatted", diff --git a/resources/src/iosTest/resources/tests.bundle/Contents/Resources/Base.lproj/Localizable.strings b/resources/src/iosTest/resources/tests.bundle/Contents/Resources/Base.lproj/Localizable.strings new file mode 100644 index 000000000..d8a3af0a7 --- /dev/null +++ b/resources/src/iosTest/resources/tests.bundle/Contents/Resources/Base.lproj/Localizable.strings @@ -0,0 +1,2 @@ +"noResultsFound" = "No results found"; +"noInternetConnection" = "No internet connection"; \ No newline at end of file diff --git a/resources/src/iosTest/resources/tests.bundle/Contents/Resources/es-US.lproj/Localizable.strings b/resources/src/iosTest/resources/tests.bundle/Contents/Resources/es-US.lproj/Localizable.strings new file mode 100644 index 000000000..d42e46e4a --- /dev/null +++ b/resources/src/iosTest/resources/tests.bundle/Contents/Resources/es-US.lproj/Localizable.strings @@ -0,0 +1 @@ +"noResultsFound" = "No se han encontrado resultados"; \ No newline at end of file diff --git a/samples/resources-gallery/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Testing.kt b/samples/resources-gallery/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Testing.kt index bd9a5e03d..943a58183 100644 --- a/samples/resources-gallery/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Testing.kt +++ b/samples/resources-gallery/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Testing.kt @@ -28,6 +28,7 @@ import dev.icerock.moko.resources.getImageByFileName public object Testing { public fun getStrings(): List { return listOf( + MR.strings.text_only_in_base.desc(), MR.strings.test_simple.desc(), MR.strings.test2.desc(), MR.strings.test3.desc(), diff --git a/samples/resources-gallery/mpp-library/src/commonMain/moko-resources/base/strings.xml b/samples/resources-gallery/mpp-library/src/commonMain/moko-resources/base/strings.xml index c173d9ca6..94193e268 100644 --- a/samples/resources-gallery/mpp-library/src/commonMain/moko-resources/base/strings.xml +++ b/samples/resources-gallery/mpp-library/src/commonMain/moko-resources/base/strings.xml @@ -7,4 +7,5 @@ first line\nsecond line\nthird line. Alex009 said "hello world" & "write tests". Alex009 said 'hello' + Text from base locale