Search code examples
kotlinillegalstateexception

Android Kotlin StringRes quantityString


Ok so this:

fun Context.quantityFromRes(id_: Int, qtt:Int, vararg format: Any) = resources.getQuantityString(id_, qtt, format)

xml:

<plurals name="header_view">
        <item quantity="one">Oh no! You just lost %1$d Point</item>
        <item quantity="other">Oh no! You just lost %1$d Points</item>
    </plurals>

Gives this error:

"java.util.IllegalFormatConversionException: %d can't format [Ljava.lang.Object; arguments"

Apparent Java fix:

public class XmlPluralFormatter {
    private XmlPluralFormatter() {
        throw new IllegalStateException("You can't fuck me =(");
    }

    public static String getFormattedString(Context context, int stringRes, int qtt, Object... formatArgs){
        return context.getResources().getQuantityString(stringRes,qtt, formatArgs);
    }

    public static String getFormattedString(Context context, int stringRes, int qtt){
        return context.getResources().getQuantityString(stringRes,qtt);
    }
}
  • I just realised using this via Java would solve the problem, but i have no idea if there is a Kotlin way of accomplish the same.

PS: Forgot the call:

val qtt: Int = 123
context.quantityFromRes(R.plurals.header, qty)

I can also do this:

fun Context.quantityFromRes(id_: Int, qtt:Int, vararg format: Object) = resources.getQuantityString(id_, qtt, format)

but then

Required Object, found Int

I can also cast:

context.quantityFromRes(R.plurals.header, qty, qt as Object)

but also gives:

"java.util.IllegalFormatConversionException: %d can't format [Ljava.lang.Object; arguments"

Also, using code directly without an extension function works:

context.resources.getQuantityString(R.plurals.header, qtt, qtt)

Solution

  • The problem is you're passing format argument as single parameter instead of spreading it to Object... args. The extension method:

    fun Context.quantityFromRes(id_: Int, qtt:Int, vararg format: Any) = resources.getQuantityString(id_, qtt, format)
    

    is equivalent to:

    fun Context.quantityFromRes(id_: Int, qtt: Int, vararg format: Any): String? {
        val args: Array<out Any> = format
        return resources.getQuantityString(id_, qtt, args)
    }
    

    Which in Java terms looks like:

    public static final String quantityFromRes(Context $receiver, int id_, int qtt, Object... format) {
        return $receiver.getResources().getQuantityString(id_, qtt, new Object[]{format});
    }
    

    What you want to do instead is to use spread operator:

    fun Context.quantityFromRes(id_: Int, qtt: Int, vararg format: Any): String? {
        return resources.getQuantityString(id_, qtt, *format)
    }