From 2b96e872977ebc99396207dac31ec2fc115ab2f9 Mon Sep 17 00:00:00 2001 From: adrienbusin Date: Thu, 30 May 2024 14:35:34 +0200 Subject: [PATCH] fix: issue #18 - Duplicate default strings.xml https://github.com/Decathlon/gradle-plugin-phraseapp/issues/18 --- .../phraseapp/internal/platforms/Platform.kt | 28 ++++--- .../internal/printers/FileOperation.kt | 1 + .../internal/printers/FileOperationImpl.kt | 6 ++ .../network/PhraseAppNetworkDataSource.kt | 1 - .../network/PhraseAppNetworkDataSourceImpl.kt | 23 ++++-- .../checks/CheckRepositoryImpl.kt | 2 +- .../repositories/operations/Downloader.kt | 44 +++++++---- .../operations/helpers/LocalHelper.kt | 10 +-- .../operations/helpers/PrinterHelper.kt | 79 +++++++++++++++++-- .../operations/helpers/ReducerHelper.kt | 20 +++-- .../phraseapp/internal/PlatformAndroidTest.kt | 7 +- .../phraseapp/internal/PlatformFlutterTest.kt | 5 +- .../phraseapp/internal/PlatformIOSTest.kt | 7 +- .../network/PhraseAppNetworkDataSourceTest.kt | 16 +++- .../checks/CheckRepositoryTest.kt | 6 +- .../repositories/operations/CleanerTest.kt | 2 +- .../repositories/operations/DownloaderTest.kt | 13 ++- .../operations/helpers/PrinterHelperTest.kt | 50 +++++++++--- .../android-remote/values-en-rGB/strings.xml | 13 +++ 19 files changed, 244 insertions(+), 89 deletions(-) create mode 100644 client/src/test/resources/android-remote/values-en-rGB/strings.xml diff --git a/client/src/main/kotlin/phraseapp/internal/platforms/Platform.kt b/client/src/main/kotlin/phraseapp/internal/platforms/Platform.kt index a71a93f..0df1a86 100644 --- a/client/src/main/kotlin/phraseapp/internal/platforms/Platform.kt +++ b/client/src/main/kotlin/phraseapp/internal/platforms/Platform.kt @@ -3,7 +3,6 @@ package phraseapp.internal.platforms import phraseapp.internal.xml.ArbPrinterScanner import phraseapp.internal.xml.Visitor import phraseapp.internal.xml.XmlPrinterScanner -import phraseapp.repositories.operations.DefaultType import phraseapp.repositories.operations.LanguageType import phraseapp.repositories.operations.LocaleType import phraseapp.repositories.operations.ResFolderType @@ -32,10 +31,11 @@ object Android : Platform() { return defaultStringsFile } - override fun getResPath(type: ResFolderType): String = when (type) { - is DefaultType -> "values" - is LanguageType -> "values-${type.language.lowercase()}" - is LocaleType -> "values-${type.language.lowercase()}-r${type.country.uppercase()}" + override fun getResPath(type: ResFolderType): String = when { + type.isDefault -> "values" + type is LanguageType -> "values-${type.language.lowercase()}" + type is LocaleType -> "values-${type.language.lowercase()}-r${type.country.uppercase()}" + else -> throw NotImplementedError() } override fun getStringsFilesExceptDefault(resFolder: String): List { @@ -63,10 +63,11 @@ object iOS : Platform() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } - override fun getResPath(type: ResFolderType): String = when (type) { - is DefaultType -> "Base.lproj" - is LanguageType -> "${type.language.lowercase()}.lproj" - is LocaleType -> "${type.language.lowercase()}-${type.country.uppercase()}.lproj" + override fun getResPath(type: ResFolderType): String = when { + type.isDefault -> "Base.lproj" + type is LanguageType -> "${type.language.lowercase()}.lproj" + type is LocaleType -> "${type.language.lowercase()}-${type.country.uppercase()}.lproj" + else -> throw NotImplementedError() } override fun getStringsFilesExceptDefault(resFolder: String): List { @@ -90,14 +91,15 @@ object Flutter : Platform() { override val format: String get() = "xml" - override fun getFilename(type: ResFolderType): String = when (type) { - is DefaultType -> defaultStringsFile - is LanguageType -> { + override fun getFilename(type: ResFolderType): String = when { + type.isDefault -> defaultStringsFile + type is LanguageType -> { "strings_${type.language.lowercase()}.arb" } - is LocaleType -> { + type is LocaleType -> { "strings_${type.language.lowercase()}_${type.country.uppercase()}.arb" } + else -> throw NotImplementedError() } override fun getResPath(type: ResFolderType): String = "values" diff --git a/client/src/main/kotlin/phraseapp/internal/printers/FileOperation.kt b/client/src/main/kotlin/phraseapp/internal/printers/FileOperation.kt index 8196c93..a0fcf5f 100644 --- a/client/src/main/kotlin/phraseapp/internal/printers/FileOperation.kt +++ b/client/src/main/kotlin/phraseapp/internal/printers/FileOperation.kt @@ -5,4 +5,5 @@ import java.io.File interface FileOperation { fun print(path: String, content: String) fun delete(file: File) + fun copy(path: String, newPath: String) } \ No newline at end of file diff --git a/client/src/main/kotlin/phraseapp/internal/printers/FileOperationImpl.kt b/client/src/main/kotlin/phraseapp/internal/printers/FileOperationImpl.kt index 24f2ef8..47f3fdb 100644 --- a/client/src/main/kotlin/phraseapp/internal/printers/FileOperationImpl.kt +++ b/client/src/main/kotlin/phraseapp/internal/printers/FileOperationImpl.kt @@ -8,6 +8,12 @@ class FileOperationImpl : FileOperation { content.writeTo(path) } + override fun copy(path: String, newPath: String){ + val file = File(path) + val duplicateFile = File(newPath) + file.copyTo(duplicateFile, overwrite = true) + } + override fun delete(file: File) { if (file.parentFile.listFiles()?.size == 1 && file.exists()) { file.parentFile.deleteRecursively() diff --git a/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSource.kt b/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSource.kt index 9055143..2a586bb 100644 --- a/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSource.kt +++ b/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSource.kt @@ -19,7 +19,6 @@ data class LocaleContent(val content: String, val isDefault: Boolean) interface PhraseAppNetworkDataSource { suspend fun downloadAllLocales( - overrideDefaultFile: Boolean = DEFAULT_OVERRIDE_DEFAULT_FILE, exceptions: Map = DEFAULT_EXCEPTIONS, placeHolder: Boolean = DEFAULT_PLACEHOLDER, localeNameRegex: String = DEFAULT_REGEX, diff --git a/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSourceImpl.kt b/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSourceImpl.kt index 68ab7bf..8748f82 100644 --- a/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSourceImpl.kt +++ b/client/src/main/kotlin/phraseapp/network/PhraseAppNetworkDataSourceImpl.kt @@ -1,6 +1,9 @@ package phraseapp.network -import kotlinx.coroutines.* +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody.Companion.FORM import okhttp3.MultipartBody.Part @@ -18,21 +21,19 @@ class PhraseAppNetworkDataSourceImpl( private val token: String, private val projectId: String, private val fileFormat: String, - private val service: PhraseAppService + private val service: PhraseAppService, ) : PhraseAppNetworkDataSource { override suspend fun downloadAllLocales( - overrideDefaultFile: Boolean, exceptions: Map, placeHolder: Boolean, localeNameRegex: String, - allowedLocaleCodes: List + allowedLocaleCodes: List, ): Map = coroutineScope { val namePattern = Pattern.compile(localeNameRegex) val locales = service.getLocales(token, projectId) .filter { - (it.isDefault.not() or overrideDefaultFile) && - (allowedLocaleCodes.isEmpty() || allowedLocaleCodes.contains(it.code)) + (allowedLocaleCodes.isEmpty() || allowedLocaleCodes.contains(it.code)) } val chunked = locales.chunked(CONCURRENT_LIMIT) val localesResponse = iterative(chunked, THROTTLING_LIMIT, placeHolder) @@ -50,7 +51,7 @@ class PhraseAppNetworkDataSourceImpl( private suspend fun iterative( list: List>, onePerMillis: Long, - placeHolder: Boolean + placeHolder: Boolean, ) = coroutineScope> { val target = arrayListOf() for (item in list) { @@ -58,7 +59,13 @@ class PhraseAppNetworkDataSourceImpl( item .map { locale -> async { - val file = service.download(token, projectId, locale.id, fileFormat, placeHolder) + val file = service.download( + token, + projectId, + locale.id, + fileFormat, + placeHolder + ) return@async LocaleResponse(locale, file.string()) } } diff --git a/client/src/main/kotlin/phraseapp/repositories/checks/CheckRepositoryImpl.kt b/client/src/main/kotlin/phraseapp/repositories/checks/CheckRepositoryImpl.kt index fa31f9a..f4a3057 100644 --- a/client/src/main/kotlin/phraseapp/repositories/checks/CheckRepositoryImpl.kt +++ b/client/src/main/kotlin/phraseapp/repositories/checks/CheckRepositoryImpl.kt @@ -36,7 +36,7 @@ class CheckRepositoryImpl( } private suspend fun check(checkType: CheckType): List = coroutineScope { - val localesContent = phraseAppNetworkDataSource.downloadAllLocales(true, emptyMap(), true, localeRegex) + val localesContent = phraseAppNetworkDataSource.downloadAllLocales(emptyMap(), true, localeRegex) val defaultContent = localesContent.values.first { it.isDefault }.content.parse(platform.format) val targetsContent = localesContent.entries .filter { it.value.isDefault.not() } diff --git a/client/src/main/kotlin/phraseapp/repositories/operations/Downloader.kt b/client/src/main/kotlin/phraseapp/repositories/operations/Downloader.kt index f65b605..9eea448 100644 --- a/client/src/main/kotlin/phraseapp/repositories/operations/Downloader.kt +++ b/client/src/main/kotlin/phraseapp/repositories/operations/Downloader.kt @@ -4,7 +4,12 @@ import kotlinx.coroutines.coroutineScope import phraseapp.internal.platforms.Platform import phraseapp.internal.printers.FileOperation import phraseapp.internal.xml.Resource -import phraseapp.network.* +import phraseapp.network.DEFAULT_ALLOWED_LOCALE_CODES +import phraseapp.network.DEFAULT_EXCEPTIONS +import phraseapp.network.DEFAULT_OVERRIDE_DEFAULT_FILE +import phraseapp.network.DEFAULT_PLACEHOLDER +import phraseapp.network.DEFAULT_REGEX +import phraseapp.network.PhraseAppNetworkDataSource import phraseapp.parsers.xml.DEFAULT_IGNORE_COMMENTS import phraseapp.repositories.operations.helpers.LocalHelper import phraseapp.repositories.operations.helpers.PrinterHelper @@ -14,7 +19,7 @@ class Downloader( platform: Platform, buildDir: String, fileOperation: FileOperation, - private val network: PhraseAppNetworkDataSource + private val network: PhraseAppNetworkDataSource, ) { private val localHelper = LocalHelper(platform) private val reducerHelper = ReducerHelper(platform) @@ -27,19 +32,26 @@ class Downloader( placeholder: Boolean = DEFAULT_PLACEHOLDER, localeNameRegex: String = DEFAULT_REGEX, ignoreComments: Boolean = DEFAULT_IGNORE_COMMENTS, - allowedLocaleCodes: List = DEFAULT_ALLOWED_LOCALE_CODES + allowedLocaleCodes: List = DEFAULT_ALLOWED_LOCALE_CODES, ) = coroutineScope { - val strings = localHelper.getStringsFileByResFolder(resFolders) + val strings = localHelper.getStringsFileByResFolder(resFolders = resFolders) val locales = network.downloadAllLocales( - overrideDefaultFile, - exceptions, - placeholder, - localeNameRegex, - allowedLocaleCodes + exceptions = exceptions, + placeHolder = placeholder, + localeNameRegex = localeNameRegex, + allowedLocaleCodes = allowedLocaleCodes ) - val resources = reducerHelper.reduceKeysForAllStringsFilesAndForAllLocales(strings, locales, ignoreComments) - printerHelper.printResources(resources) - printerHelper.printLocales(getTypes(resources)) + val resources = reducerHelper.reduceKeysForAllStringsFilesAndForAllLocales( + strings = strings, + remoteStrings = locales, + ignoreComments = ignoreComments + ) + printerHelper.printResources( + configurations = resources, + overrideDefaultFile = overrideDefaultFile + ) + printerHelper.printLocales(types = getTypes(resources)) + return@coroutineScope resources } @@ -47,7 +59,7 @@ class Downloader( configurations.entries.first().value.keys.toList() } -sealed class ResFolderType -object DefaultType : ResFolderType() -class LanguageType(val language: String) : ResFolderType() -class LocaleType(val language: String, val country: String) : ResFolderType() +sealed class ResFolderType(open val language: String, val isDefault: Boolean) +class LanguageType(language: String, isDefault: Boolean) : ResFolderType(language, isDefault) +class LocaleType(language: String, val country: String, isDefault: Boolean) : + ResFolderType(language, isDefault) diff --git a/client/src/main/kotlin/phraseapp/repositories/operations/helpers/LocalHelper.kt b/client/src/main/kotlin/phraseapp/repositories/operations/helpers/LocalHelper.kt index d21a848..e6be85c 100644 --- a/client/src/main/kotlin/phraseapp/repositories/operations/helpers/LocalHelper.kt +++ b/client/src/main/kotlin/phraseapp/repositories/operations/helpers/LocalHelper.kt @@ -6,7 +6,7 @@ import phraseapp.internal.platforms.Platform import phraseapp.internal.xml.PluralsTranslation import phraseapp.internal.xml.StringTranslation import phraseapp.internal.xml.StringsArrayTranslation -import phraseapp.repositories.operations.DefaultType +import phraseapp.repositories.operations.LanguageType import phraseapp.repositories.operations.ResFolderType import java.io.File @@ -46,7 +46,7 @@ class LocalHelper(val platform: Platform) { resFolder: String, filenames: List ): List = filenames - .map { getResFolderFile(resFolder, it, DefaultType) } + .map { getResFolderFile(resFolder, it) } .map { it.readText().parse(it) } .toList() @@ -56,7 +56,7 @@ class LocalHelper(val platform: Platform) { */ private fun getStringsFile(resFolder: String, filenames: List): ResourceTranslation { val resources: List = filenames - .map { getResFolderFile(resFolder, it, DefaultType) } + .map { getResFolderFile(resFolder, it) } .map { it.readText().parse(it) } .toList() return mergeResourceTranslations(resources) @@ -66,8 +66,8 @@ class LocalHelper(val platform: Platform) { * Build resource folder file from res folder path, filename and the type of the res folder (default or locale). * @return File if exist or throw NoSuchFileException */ - private fun getResFolderFile(resFolder: String, filename: String, type: ResFolderType): File { - val value = platform.getResPath(type) + private fun getResFolderFile(resFolder: String, filename: String): File { + val value = platform.getResPath(LanguageType("", true)) val file = File("${resFolder}${File.separator}${value}${File.separator}${filename}") if (file.exists().not()) throw NoSuchFileException(file) return file diff --git a/client/src/main/kotlin/phraseapp/repositories/operations/helpers/PrinterHelper.kt b/client/src/main/kotlin/phraseapp/repositories/operations/helpers/PrinterHelper.kt index fc17fb5..c4250c5 100644 --- a/client/src/main/kotlin/phraseapp/repositories/operations/helpers/PrinterHelper.kt +++ b/client/src/main/kotlin/phraseapp/repositories/operations/helpers/PrinterHelper.kt @@ -8,8 +8,9 @@ import phraseapp.internal.printers.FileOperation import phraseapp.internal.printers.FileOperationImpl import phraseapp.internal.xml.ArbPrinterScanner import phraseapp.internal.xml.Resource -import phraseapp.internal.xml.Visitor import phraseapp.internal.xml.XmlPrinterScanner +import phraseapp.network.DEFAULT_OVERRIDE_DEFAULT_FILE +import phraseapp.repositories.operations.LanguageType import phraseapp.repositories.operations.LocaleType import phraseapp.repositories.operations.ResFolderType import java.io.File @@ -17,7 +18,7 @@ import java.io.File class PrinterHelper( val platform: Platform, val buildDir: String, - val fileOperation: FileOperation = FileOperationImpl() + val fileOperation: FileOperation = FileOperationImpl(), ) { val tempStringFilePath: String = if (platform is Flutter) { "$buildDir${File.separator}string.xml" @@ -56,27 +57,93 @@ class PrinterHelper( /** * Print all resources in all paths. */ - fun printResources(configurations: Map>) { + fun printResources( + configurations: Map>, + overrideDefaultFile: Boolean = DEFAULT_OVERRIDE_DEFAULT_FILE, + ) { configurations.forEach { configuration -> configuration.value.forEach { resource -> - printResourceByType(configuration.key, resource.key, resource.value) + if ((!resource.key.isDefault || overrideDefaultFile)) { + printResourceByType( + resFolder = configuration.key, + type = resource.key, + resource = resource.value + ) + } + copyDefaultFileIfNeeded(configuration, resource.key) } } } + + /** + * Copy default file strings.xml to value-DEFAULT_LANGUAGE/strings.xml + * if there is no variant, default file will be taken instead so we don't need to copy it. + * + * This method corrects this issue : https://github.com/Decathlon/gradle-plugin-phraseapp/issues/18 + * When a default language (ex: "en") has some variants (ex: en-GB) which have a translation which is different between them, + * before this fix, there was no values-en file created, so, for an other variant (ex: en-IE) the translation will be pull from en-GB and not from "en". + * See google "resource resolution order" doc for more details -> + * https://developer.android.com/guide/topics/resources/multilingual-support?hl=fr#resource-resolution-examples + * + * + */ + private fun copyDefaultFileIfNeeded( + configuration: Map.Entry>, + type: ResFolderType + ) { + // Variant of default language ex: default = en, variant = en-IE or en-GB + val hasVariant = + configuration.value.filter { it.key.language.contains(type.language) && !it.key.isDefault } + .isNotEmpty() + + if (type.isDefault && hasVariant) { + duplicateFile( + resFolder = configuration.key, + type = type + ) + } + } + + private fun duplicateFile( + resFolder: String, + type: ResFolderType, + ) { + val defaultPath = + "$resFolder${File.separator}${platform.getResPath(type)}${File.separator}${ + platform.getFilename(type) + }" + + val duplicateFileType = LanguageType(type.language, false) + val duplicatePath = + "$resFolder${File.separator}${platform.getResPath(duplicateFileType)}${File.separator}${ + platform.getFilename(duplicateFileType) + }" + + fileOperation.copy(defaultPath, duplicatePath) + } + /** * Build the path from res folder path and its type and print the resource at this target path. */ private fun printResourceByType(resFolder: String, type: ResFolderType, resource: Resource) { val path = - "$resFolder${File.separator}${platform.getResPath(type)}${File.separator}${platform.getFilename(type)}" + "$resFolder${File.separator}${platform.getResPath(type)}${File.separator}${ + platform.getFilename( + type + ) + }" printResource(path, resource) } /** * Print resource on the target path. */ - private fun printResource(targetPath: String, resource: Resource, forceXMLPrinter: Boolean = false) { + private fun printResource( + targetPath: String, + resource: Resource, + forceXMLPrinter: Boolean = false, + ) { val content = if (forceXMLPrinter) { XmlPrinterScanner().start(resource) } else { diff --git a/client/src/main/kotlin/phraseapp/repositories/operations/helpers/ReducerHelper.kt b/client/src/main/kotlin/phraseapp/repositories/operations/helpers/ReducerHelper.kt index 2ccf9af..b18c0a3 100644 --- a/client/src/main/kotlin/phraseapp/repositories/operations/helpers/ReducerHelper.kt +++ b/client/src/main/kotlin/phraseapp/repositories/operations/helpers/ReducerHelper.kt @@ -5,7 +5,6 @@ import phraseapp.extensions.parse import phraseapp.internal.platforms.Platform import phraseapp.internal.xml.Resource import phraseapp.network.LocaleContent -import phraseapp.repositories.operations.DefaultType import phraseapp.repositories.operations.LanguageType import phraseapp.repositories.operations.LocaleType import phraseapp.repositories.operations.ResFolderType @@ -18,7 +17,7 @@ class ReducerHelper(val platform: Platform) { fun reduceKeysForAllStringsFilesAndForAllLocales( strings: Map, remoteStrings: Map, - ignoreComments: Boolean + ignoreComments: Boolean, ): Map> = strings.map { it.key to reduceKeysForAllLocales(it.value, remoteStrings, ignoreComments) @@ -31,13 +30,20 @@ class ReducerHelper(val platform: Platform) { private fun reduceKeysForAllLocales( stringsFile: ResourceTranslation, remoteStrings: Map, - ignoreComments: Boolean + ignoreComments: Boolean, ): Map { - val keys: Set = stringsFile.strings.map { it.key }.union(stringsFile.plurals.map { it.key }) + val keys: Set = + stringsFile.strings.map { it.key }.union(stringsFile.plurals.map { it.key }) return remoteStrings.map { - val type = if (it.value.isDefault) DefaultType - else if (it.key.split("-").size > 1) LocaleType(it.key.split("-")[0], it.key.split("-")[1]) - else LanguageType(it.key) + val type = when { + it.key.split("-").size > 1 -> LocaleType( + it.key.split("-")[0], + it.key.split("-")[1], + it.value.isDefault + ) + + else -> LanguageType(it.key, it.value.isDefault) + } val resource = it.value.content.parse(platform.format, ignoreComments) return@map type to reduceKeys(keys, resource) }.toMap() diff --git a/client/src/test/kotlin/phraseapp/internal/PlatformAndroidTest.kt b/client/src/test/kotlin/phraseapp/internal/PlatformAndroidTest.kt index 3b35545..c33cf9f 100644 --- a/client/src/test/kotlin/phraseapp/internal/PlatformAndroidTest.kt +++ b/client/src/test/kotlin/phraseapp/internal/PlatformAndroidTest.kt @@ -3,24 +3,23 @@ package phraseapp.internal import org.junit.Assert.assertEquals import org.junit.Test import phraseapp.internal.platforms.Android -import phraseapp.repositories.operations.DefaultType import phraseapp.repositories.operations.LanguageType import phraseapp.repositories.operations.LocaleType class PlatformAndroidTest { @Test fun testGetPathResFolderByDefaultLocale() { - assertEquals("values", Android.getResPath(DefaultType)) + assertEquals("values", Android.getResPath(LanguageType("", isDefault = true))) } @Test fun testGetPathResFolderByLocale() { - assertEquals("values-fr-rFR", Android.getResPath(LocaleType("fr", "FR"))) + assertEquals("values-fr-rFR", Android.getResPath(LocaleType("fr", "FR", false))) } @Test fun testGetPathResFolderByLanguage() { - assertEquals("values-fr", Android.getResPath(LanguageType("fr"))) + assertEquals("values-fr", Android.getResPath(LanguageType("fr", false))) } @Test diff --git a/client/src/test/kotlin/phraseapp/internal/PlatformFlutterTest.kt b/client/src/test/kotlin/phraseapp/internal/PlatformFlutterTest.kt index 5821f69..5ce2fd5 100644 --- a/client/src/test/kotlin/phraseapp/internal/PlatformFlutterTest.kt +++ b/client/src/test/kotlin/phraseapp/internal/PlatformFlutterTest.kt @@ -2,16 +2,13 @@ package phraseapp.internal import org.junit.Assert.assertEquals import org.junit.Test -import phraseapp.internal.platforms.Android import phraseapp.internal.platforms.Flutter -import phraseapp.repositories.operations.DefaultType import phraseapp.repositories.operations.LanguageType -import phraseapp.repositories.operations.LocaleType class PlatformFlutterTest { @Test fun testGetPathResFolderByDefaultLocale() { - assertEquals("values", Flutter.getResPath(DefaultType)) + assertEquals("values", Flutter.getResPath(LanguageType("", isDefault = true))) } @Test diff --git a/client/src/test/kotlin/phraseapp/internal/PlatformIOSTest.kt b/client/src/test/kotlin/phraseapp/internal/PlatformIOSTest.kt index e6190fd..4affcdc 100644 --- a/client/src/test/kotlin/phraseapp/internal/PlatformIOSTest.kt +++ b/client/src/test/kotlin/phraseapp/internal/PlatformIOSTest.kt @@ -3,24 +3,23 @@ package phraseapp.internal import org.junit.Assert.assertEquals import org.junit.Test import phraseapp.internal.platforms.iOS -import phraseapp.repositories.operations.DefaultType import phraseapp.repositories.operations.LanguageType import phraseapp.repositories.operations.LocaleType class PlatformIOSTest { @Test fun testGetPathResFolderByDefaultLocale() { - assertEquals("Base.lproj", iOS.getResPath(DefaultType)) + assertEquals("Base.lproj", iOS.getResPath(LanguageType("", isDefault = true))) } @Test fun testGetPathResFolderByLocale() { - assertEquals("fr-FR.lproj", iOS.getResPath(LocaleType("fr", "FR"))) + assertEquals("fr-FR.lproj", iOS.getResPath(LocaleType("fr", "FR", false))) } @Test fun testGetPathResFolderByLanguage() { - assertEquals("fr.lproj", iOS.getResPath(LanguageType("fr"))) + assertEquals("fr.lproj", iOS.getResPath(LanguageType("fr",false))) } @Test diff --git a/client/src/test/kotlin/phraseapp/network/PhraseAppNetworkDataSourceTest.kt b/client/src/test/kotlin/phraseapp/network/PhraseAppNetworkDataSourceTest.kt index 7bb1026..4d9c05d 100644 --- a/client/src/test/kotlin/phraseapp/network/PhraseAppNetworkDataSourceTest.kt +++ b/client/src/test/kotlin/phraseapp/network/PhraseAppNetworkDataSourceTest.kt @@ -13,7 +13,13 @@ class PhraseAppNetworkDataSourceTest { fun shouldDownloadAllXmlContentsExceptedDefaultLocale() = runBlocking { val networkDataSource = PhraseAppNetworkDataSourceImpl("", "", "", service) val xmlContents = networkDataSource.downloadAllLocales() - assertEquals(2, xmlContents.size) + assertEquals(3, xmlContents.size) + assertTrue(xmlContents.containsKey("en")) + assertTrue(xmlContents["en"]!!.isDefault) + assertEquals( + File("src/test/resources/android-remote/values/strings.xml").readText(), + xmlContents["en"]?.content + ) assertTrue(xmlContents.containsKey("es-ES")) assertFalse(xmlContents["es-ES"]!!.isDefault) assertEquals( @@ -31,7 +37,7 @@ class PhraseAppNetworkDataSourceTest { @Test fun shouldDownloadAllXmlContentsIncludingDefaultLocale() = runBlocking { val networkDataSource = PhraseAppNetworkDataSourceImpl("", "", "", service) - val xmlContents = networkDataSource.downloadAllLocales(overrideDefaultFile = true) + val xmlContents = networkDataSource.downloadAllLocales() assertEquals(3, xmlContents.size) assertTrue(xmlContents.containsKey("en")) assertTrue(xmlContents["en"]!!.isDefault) @@ -56,7 +62,7 @@ class PhraseAppNetworkDataSourceTest { @Test fun shouldSkipLocaleWhenLocaleDoNotMatchRegexApplyOnLocaleName() = runBlocking { val networkDataSource = PhraseAppNetworkDataSourceImpl("", "", "", service) - val xmlContents = networkDataSource.downloadAllLocales(overrideDefaultFile = true, localeNameRegex = "") + val xmlContents = networkDataSource.downloadAllLocales(localeNameRegex = "") assertEquals(2, xmlContents.size) assertTrue(xmlContents.containsKey("en")) assertTrue(xmlContents.containsKey("fr-FR")) @@ -74,9 +80,11 @@ class PhraseAppNetworkDataSourceTest { fun shouldRedirectLocaleToNewLocaleNameWhenItIsPresentInExceptionList() = runBlocking { val networkDataSource = PhraseAppNetworkDataSourceImpl("", "", "", service) val xmlContents = networkDataSource.downloadAllLocales(exceptions = mapOf("es-ES" to "ca-ES")) - assertEquals(2, xmlContents.size) + assertEquals(3, xmlContents.size) assertFalse(xmlContents.containsKey("es-ES")) assertTrue(xmlContents.containsKey("ca-ES")) + assertTrue(xmlContents.containsKey("en")) + assertTrue(xmlContents.containsKey("fr-FR")) } @Test diff --git a/client/src/test/kotlin/phraseapp/repositories/checks/CheckRepositoryTest.kt b/client/src/test/kotlin/phraseapp/repositories/checks/CheckRepositoryTest.kt index a63edcd..d83b701 100644 --- a/client/src/test/kotlin/phraseapp/repositories/checks/CheckRepositoryTest.kt +++ b/client/src/test/kotlin/phraseapp/repositories/checks/CheckRepositoryTest.kt @@ -18,7 +18,7 @@ class CheckRepositoryTest { @Test fun shouldGetFilePrintedInPhraseAppOutputsWhenThereAreErrorsInChecks() = runBlocking { val phraseAppNetworkDataSource: PhraseAppNetworkDataSource = mock() - whenever(phraseAppNetworkDataSource.downloadAllLocales(any(), any(), any(), any(), any())) + whenever(phraseAppNetworkDataSource.downloadAllLocales(any(), any(), any(), any())) .thenReturn( mapOf( "en" to LocaleContent( @@ -63,7 +63,7 @@ es-ES :: PLACEHOLDER :: hello @Test fun shouldNotGetErrorsWhenThereIsNoErrorInStringsFiles() = runBlocking { val phraseAppNetworkDataSource: PhraseAppNetworkDataSource = mock() - whenever(phraseAppNetworkDataSource.downloadAllLocales(any(), any(), any(), any(), any())) + whenever(phraseAppNetworkDataSource.downloadAllLocales(any(), any(), any(), any())) .thenReturn( mapOf( "en" to LocaleContent( @@ -95,7 +95,7 @@ es-ES :: PLACEHOLDER :: hello @Test fun shouldNotGetErrorWhenThereAreMissingTranslations() = runBlocking { val phraseAppNetworkDataSource: PhraseAppNetworkDataSource = mock() - whenever(phraseAppNetworkDataSource.downloadAllLocales(any(), any(), any(), any(), any())) + whenever(phraseAppNetworkDataSource.downloadAllLocales(any(), any(), any(), any())) .thenReturn( mapOf( "en" to LocaleContent( diff --git a/client/src/test/kotlin/phraseapp/repositories/operations/CleanerTest.kt b/client/src/test/kotlin/phraseapp/repositories/operations/CleanerTest.kt index 21a3a94..b7c329f 100644 --- a/client/src/test/kotlin/phraseapp/repositories/operations/CleanerTest.kt +++ b/client/src/test/kotlin/phraseapp/repositories/operations/CleanerTest.kt @@ -29,7 +29,7 @@ class CleanerTest { val resFolderModule1 = "src/test/resources/android" to arrayListOf("strings.xml") val resFolderModule2 = "src/test/resources/android-remote" to arrayListOf("strings.xml") Cleaner(Android, fileOperation).clean(mapOf(resFolderModule1, resFolderModule2)) - verify(fileOperation, times(3)).delete(any()) + verify(fileOperation, times(4)).delete(any()) verify(fileOperation).delete(File("$rootDir${File.separator}src${File.separator}test${File.separator}resources${File.separator}android${File.separator}values-fr-rFR${File.separator}strings.xml")) verify(fileOperation).delete(File("$rootDir${File.separator}src${File.separator}test${File.separator}resources${File.separator}android-remote${File.separator}values-es-rES${File.separator}strings.xml")) verify(fileOperation).delete(File("$rootDir${File.separator}src${File.separator}test${File.separator}resources${File.separator}android-remote${File.separator}values-fr-rFR${File.separator}strings.xml")) diff --git a/client/src/test/kotlin/phraseapp/repositories/operations/DownloaderTest.kt b/client/src/test/kotlin/phraseapp/repositories/operations/DownloaderTest.kt index 77026f6..a0b4a38 100644 --- a/client/src/test/kotlin/phraseapp/repositories/operations/DownloaderTest.kt +++ b/client/src/test/kotlin/phraseapp/repositories/operations/DownloaderTest.kt @@ -22,7 +22,9 @@ class DownloaderTest { whenever(network.downloadAllLocales()).thenReturn( mapOf( "fr-FR" to LocaleContent(File("src/test/resources/android-remote/values-fr-rFR/strings.xml").readText(), false), - "es-ES" to LocaleContent(File("src/test/resources/android-remote/values-es-rES/strings.xml").readText(), false) + "es-ES" to LocaleContent(File("src/test/resources/android-remote/values-es-rES/strings.xml").readText(), false), + "en" to LocaleContent(File("src/test/resources/android-remote/values/strings.xml").readText(), true), + "en-GB" to LocaleContent(File("src/test/resources/android-remote/values-en-rGB/strings.xml").readText(), false) ) ) val fileOperation: FileOperation = mock() @@ -32,11 +34,16 @@ class DownloaderTest { ) val results = Downloader(Android, "build", fileOperation, network).download(resFolders) Assert.assertEquals(2, results.size) - verify(fileOperation, times(5)).print(any(), any()) - verify(fileOperation).print(eq("build/languages.json"), eq("""{"FR":["fr"],"ES":["es"]}""")) + verify(fileOperation, times(7)).print(any(), any()) + verify(fileOperation, times(2)).copy(any(), any()) + verify(fileOperation).print(eq("build/languages.json"), eq("""{"FR":["fr"],"ES":["es"],"GB":["en"]}""")) verify(fileOperation).print(eq("src/test/resources/android/values-fr-rFR/strings.xml"), any()) verify(fileOperation).print(eq("src/test/resources/android/values-es-rES/strings.xml"), any()) + verify(fileOperation).print(eq("src/test/resources/android/values-en-rGB/strings.xml"), any()) + verify(fileOperation).copy(eq("src/test/resources/android/values/strings.xml"), eq("src/test/resources/android/values-en/strings.xml")) verify(fileOperation).print(eq("src/test/resources/android-local/values-fr-rFR/strings.xml"), any()) verify(fileOperation).print(eq("src/test/resources/android-local/values-es-rES/strings.xml"), any()) + verify(fileOperation).print(eq("src/test/resources/android-local/values-en-rGB/strings.xml"), any()) + verify(fileOperation).copy(eq("src/test/resources/android-local/values/strings.xml"), eq("src/test/resources/android-local/values-en/strings.xml")) } } \ No newline at end of file diff --git a/client/src/test/kotlin/phraseapp/repositories/operations/helpers/PrinterHelperTest.kt b/client/src/test/kotlin/phraseapp/repositories/operations/helpers/PrinterHelperTest.kt index 5040c7e..6f7c314 100644 --- a/client/src/test/kotlin/phraseapp/repositories/operations/helpers/PrinterHelperTest.kt +++ b/client/src/test/kotlin/phraseapp/repositories/operations/helpers/PrinterHelperTest.kt @@ -1,13 +1,14 @@ package phraseapp.repositories.operations.helpers import org.junit.Test +import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify +import org.mockito.kotlin.any import phraseapp.extensions.parse import phraseapp.internal.platforms.Android import phraseapp.internal.printers.FileOperation import phraseapp.internal.xml.Resource -import phraseapp.repositories.operations.DefaultType import phraseapp.repositories.operations.LanguageType import phraseapp.repositories.operations.LocaleType import phraseapp.repositories.operations.ResFolderType @@ -43,10 +44,10 @@ class PrinterHelperTest { val fileOperation: FileOperation = mock() val helper = PrinterHelper(Android, "build", fileOperation) helper.printLocales(arrayListOf( - LocaleType("fr", "FR"), - LanguageType("en"), - LocaleType("fr", "BE"), - LocaleType("nl", "BE") + LocaleType("fr", "FR", false), + LanguageType("en", false), + LocaleType("fr", "BE", false), + LocaleType("nl", "BE", false) )) verify(fileOperation).print("build${File.separator}languages.json", """ @@ -59,22 +60,53 @@ class PrinterHelperTest { val fileOperation: FileOperation = mock() val helper = PrinterHelper(Android, "build", fileOperation) val mapOf: Map = mapOf( - DefaultType to getResource(EMPTY) + LanguageType(language = "", isDefault = true) to getResource(EMPTY) ) - helper.printResources(mapOf("src/test/resources/android" to mapOf)) + helper.printResources(mapOf("src/test/resources/android" to mapOf), true) val expected = "${EMPTY}".trimIndent() verify(fileOperation).print("src/test/resources/android/values/strings.xml", expected) } + @Test + fun testWhenPrintResourcesDefaultFileIsDuplicated() { + val fileOperation: FileOperation = mock() + val helper = PrinterHelper(Android, "build", fileOperation) + val mapOf: Map = mapOf( + LanguageType(language = "fr", isDefault = true) to getResource(EMPTY), + LocaleType(language = "fr", country = "FR", isDefault = false) to getResource(EMPTY), + LocaleType(language = "en", country = "GB", isDefault = false) to getResource(EMPTY), + LocaleType(language = "en", country = "IE", isDefault = false) to getResource(EMPTY), + ) + helper.printResources(mapOf("src/test/resources/android" to mapOf), false) + + verify(fileOperation).copy("src/test/resources/android/values/strings.xml", + "src/test/resources/android/values-fr/strings.xml") + } + + @Test + fun testWhenPrintResourcesDefaultFileIsNotDuplicated() { + val fileOperation: FileOperation = mock() + val helper = PrinterHelper(Android, "build", fileOperation) + val mapOf: Map = mapOf( + LanguageType(language = "fr", isDefault = true) to getResource(EMPTY), + LocaleType(language = "en", country = "GB", isDefault = false) to getResource(EMPTY), + LocaleType(language = "en", country = "IE", isDefault = false) to getResource(EMPTY), + ) + helper.printResources(mapOf("src/test/resources/android" to mapOf), false) + + verify(fileOperation, Mockito.times(0)).copy(any(), any()) + + } + @Test fun testWhenThereAreStringsAndPluralsToPrint() { val fileOperation: FileOperation = mock() val helper = PrinterHelper(Android, "build", fileOperation) val mapOf: Map = mapOf( - DefaultType to getResource(EXAMPLE_1) + LanguageType(language = "", isDefault = true) to getResource(EXAMPLE_1) ) - helper.printResources(mapOf("src/test/resources/android" to mapOf)) + helper.printResources(mapOf("src/test/resources/android" to mapOf), true) val expected = "${EXAMPLE_1}".trimIndent() verify(fileOperation).print("src/test/resources/android/values/strings.xml", expected) diff --git a/client/src/test/resources/android-remote/values-en-rGB/strings.xml b/client/src/test/resources/android-remote/values-en-rGB/strings.xml new file mode 100644 index 0000000..2ebd713 --- /dev/null +++ b/client/src/test/resources/android-remote/values-en-rGB/strings.xml @@ -0,0 +1,13 @@ + + + Hello From GB! + World! + + Hello World! + Hello Worlds! + + + %d song found. + %d songs found. + + \ No newline at end of file