From e1c5a18077bbfc6972126912c961737208d272f0 Mon Sep 17 00:00:00 2001 From: Evan Sarkar Date: Thu, 9 Apr 2026 07:42:10 +0530 Subject: [PATCH 1/3] fix: catch NotFoundException in CrowdinResources for non-app resource IDs Wrap getResourceEntryName() calls in try-catch so that resource IDs from system components (e.g. WebView/Chromium) that don't exist in the host app's resource table fall back to the parent Resources implementation instead of crashing with Resources$NotFoundException. --- .../com/crowdin/platform/CrowdinResources.kt | 103 +++++++++++------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt b/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt index eef721a..a27467f 100755 --- a/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt +++ b/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt @@ -43,11 +43,14 @@ internal class CrowdinResources( ) { @Throws(NotFoundException::class) override fun getString(id: Int): String { - val entryName = getResourceEntryName(id) - val string = getStringFromRepository(id)?.replaceNewLine() ?: res.getString(id) - saveStringDataToCopy(entryName, string) - - return string + return try { + val entryName = getResourceEntryName(id) + val string = getStringFromRepository(id)?.replaceNewLine() ?: res.getString(id) + saveStringDataToCopy(entryName, string) + string + } catch (_: NotFoundException) { + super.getString(id) + } } @Throws(NotFoundException::class) @@ -55,53 +58,64 @@ internal class CrowdinResources( id: Int, vararg formatArgs: Any, ): String { - val entryName = getResourceEntryName(id) - val string = getStringFromRepository(id)?.replaceNewLine() - val formattedString = - if (string == null) { - res.getString(id, *formatArgs) - } else { - try { - String.format(string, *formatArgs) - } catch (_: Exception) { + return try { + val entryName = getResourceEntryName(id) + val string = getStringFromRepository(id)?.replaceNewLine() + val formattedString = + if (string == null) { res.getString(id, *formatArgs) + } else { + try { + String.format(string, *formatArgs) + } catch (_: Exception) { + res.getString(id, *formatArgs) + } } - } - - saveStringDataToCopy(entryName, formattedString, formatArgs) - - return formattedString + saveStringDataToCopy(entryName, formattedString, formatArgs) + formattedString + } catch (_: NotFoundException) { + super.getString(id, *formatArgs) + } } @Throws(NotFoundException::class) override fun getStringArray(id: Int): Array { - val entryName = getResourceEntryName(id) - val stringArray = getStringArrayFromRepository(id) ?: res.getStringArray(id) - saveStringArrayDataToCopy(entryName, stringArray) - - return stringArray + return try { + val entryName = getResourceEntryName(id) + val stringArray = getStringArrayFromRepository(id) ?: res.getStringArray(id) + saveStringArrayDataToCopy(entryName, stringArray) + stringArray + } catch (_: NotFoundException) { + super.getStringArray(id) + } } @Throws(NotFoundException::class) override fun getText(id: Int): CharSequence { - val entryName = getResourceEntryName(id) - val string = getStringFromRepository(id) - val formattedString = string?.fromHtml() ?: res.getText(id) - saveStringDataToCopy(entryName, formattedString.toString()) - - return formattedString + return try { + val entryName = getResourceEntryName(id) + val string = getStringFromRepository(id) + val formattedString = string?.fromHtml() ?: res.getText(id) + saveStringDataToCopy(entryName, formattedString.toString()) + formattedString + } catch (_: NotFoundException) { + super.getText(id) + } } override fun getText( id: Int, default: CharSequence, ): CharSequence { - val entryName = getResourceEntryName(id) - val string = getStringFromRepository(id) - val formattedString = string?.fromHtml() ?: res.getText(id, default) - saveStringDataToCopy(entryName, formattedString.toString(), default = default) - - return formattedString + return try { + val entryName = getResourceEntryName(id) + val string = getStringFromRepository(id) + val formattedString = string?.fromHtml() ?: res.getText(id, default) + saveStringDataToCopy(entryName, formattedString.toString(), default = default) + formattedString + } catch (_: NotFoundException) { + super.getText(id, default) + } } @Throws(NotFoundException::class) @@ -356,7 +370,11 @@ internal class CrowdinResources( defaultText: String, formatArgs: Array = arrayOf(), ) { - val entryName = getResourceEntryName(id) + val entryName = try { + getResourceEntryName(id) + } catch (_: NotFoundException) { + return + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val rule = PluralRules.forLocale(configuration.getLocale()) val ruleName = rule.select(quantity.toDouble()) @@ -383,10 +401,13 @@ internal class CrowdinResources( } private fun getStringArrayFromRepository(id: Int): Array? { - val entryName = getResourceEntryName(id) - val localeCode = configuration.getLocale().getFormattedCode() - - return dataManager.getStringArray(localeCode, entryName) + return try { + val entryName = getResourceEntryName(id) + val localeCode = configuration.getLocale().getFormattedCode() + dataManager.getStringArray(localeCode, entryName) + } catch (_: NotFoundException) { + null + } } private fun getPluralFromRepository( From 37e557ae06741225545b6adf327adc6e3ea60e6f Mon Sep 17 00:00:00 2001 From: Evan Sarkar Date: Thu, 9 Apr 2026 07:56:12 +0530 Subject: [PATCH 2/3] fix: also guard getPluralFromRepository against NotFoundException for non-app resource IDs --- .../com/crowdin/platform/CrowdinResources.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt b/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt index a27467f..8ad47ae 100755 --- a/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt +++ b/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt @@ -413,15 +413,17 @@ internal class CrowdinResources( private fun getPluralFromRepository( id: Int, quantity: Int, - ): String? = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val locale = configuration.getLocale() - val localeCode = locale.getFormattedCode() - val entryName = getResourceEntryName(id) - val rule = PluralRules.forLocale(locale) - val ruleName = rule.select(quantity.toDouble()) - dataManager.getStringPlural(localeCode, entryName, ruleName) - } else { - null + ): String? { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return null + val locale = configuration.getLocale() + val localeCode = locale.getFormattedCode() + val entryName = try { + getResourceEntryName(id) + } catch (_: NotFoundException) { + return null } + val rule = PluralRules.forLocale(locale) + val ruleName = rule.select(quantity.toDouble()) + return dataManager.getStringPlural(localeCode, entryName, ruleName) + } } From f2126baa1605fa6e055f1eb4dd5213c043034c56 Mon Sep 17 00:00:00 2001 From: Evan Sarkar Date: Thu, 9 Apr 2026 08:05:22 +0530 Subject: [PATCH 3/3] fix: wrap quantity method overrides in try-catch to match other overrides --- .../com/crowdin/platform/CrowdinResources.kt | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt b/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt index 8ad47ae..238ce2d 100755 --- a/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt +++ b/crowdin/src/main/java/com/crowdin/platform/CrowdinResources.kt @@ -123,11 +123,14 @@ internal class CrowdinResources( id: Int, quantity: Int, ): CharSequence { - val plural = getPluralFromRepository(id, quantity) - val formattedPlural = plural?.fromHtml() ?: res.getQuantityText(id, quantity) - savePluralToCopy(id, quantity, formattedPlural.toString()) - - return formattedPlural + return try { + val plural = getPluralFromRepository(id, quantity) + val formattedPlural = plural?.fromHtml() ?: res.getQuantityText(id, quantity) + savePluralToCopy(id, quantity, formattedPlural.toString()) + formattedPlural + } catch (_: NotFoundException) { + super.getQuantityText(id, quantity) + } } @Throws(NotFoundException::class) @@ -136,21 +139,23 @@ internal class CrowdinResources( quantity: Int, vararg formatArgs: Any?, ): String { - val plural = getPluralFromRepository(id, quantity) - val formattedPlural = - if (plural == null) { - res.getQuantityString(id, quantity, *formatArgs) - } else { - try { - String.format(plural, *formatArgs) - } catch (ex: Exception) { + return try { + val plural = getPluralFromRepository(id, quantity) + val formattedPlural = + if (plural == null) { res.getQuantityString(id, quantity, *formatArgs) + } else { + try { + String.format(plural, *formatArgs) + } catch (_: Exception) { + res.getQuantityString(id, quantity, *formatArgs) + } } - } - - savePluralToCopy(id, quantity, formattedPlural, formatArgs) - - return formattedPlural + savePluralToCopy(id, quantity, formattedPlural, formatArgs) + formattedPlural + } catch (_: NotFoundException) { + super.getQuantityString(id, quantity, *formatArgs) + } } @RequiresApi(Build.VERSION_CODES.R) @@ -237,12 +242,14 @@ internal class CrowdinResources( id: Int, quantity: Int, ): String { - val plural = getPluralFromRepository(id, quantity) - val formattedPlural = plural ?: res.getQuantityString(id, quantity) - - savePluralToCopy(id, quantity, formattedPlural) - - return formattedPlural + return try { + val plural = getPluralFromRepository(id, quantity) + val formattedPlural = plural ?: res.getQuantityString(id, quantity) + savePluralToCopy(id, quantity, formattedPlural) + formattedPlural + } catch (_: NotFoundException) { + super.getQuantityString(id, quantity) + } } override fun toString(): String = res.toString()