Jetpack Compose Concepts

String literals

This brings up an interesting consideration. In the above example we hardcoded "Press Me" as a button label. We're assuming the language and possibly regional dialect used by the user.

Fortunately, Android makes it simple to apply internationalization (aka "I18N" - the "18" is how many letters between the "I" and "N") to your application.

Internationalization is the process of externalizing locale-specific details from your code. At its simplest, you extract string literals into a file, and organize copies of that file with different translations into different resource directories. Each translation is made available based on the language settings of the user.

More fine-grained I18N includes extracting details of how currency, numbers and pluralization is represented in your application.

We'll use the most-basic I18N support in this class - extracting string literals. To do this, you'll edit (or create) app/src/main/res/values/strings.xml and add any user-facing string literal there. (Do not add things like fixed file names in there, as they could end up with different values based on the device configuration!) For example:

<resources>
    <string name="app_name">Movies</string>
    <string name="reset_database">Reset Database</string>

    <string name="movies">Movies that are rated %1$s</string>
    <string name="filmography">Films that feature %1$s</string>
    <string name="cast">Cast</string>

    ...
</resources>

When the Android Gradle Plugin is building your application, it generates numeric ids for each string name. These will look like R.string.app_name, R.string.cast, etc. (The generated R class will be in the package specified by the android.namespace property in your app/build.gradle.kts) Each of these is known as a "string resource".

Connecting resource values and formats based on the user settings is known as Localization (aka L10N for similar reasons).

To use these strings in your Composable functions:

@Composable
fun Display(
    @StringRes labelId: Int,
    value: String,
) {
    Row {
        Text(text = stringResource(id = labelId))
        Text(text = value)
    }
}

...

Display(
    labelId = R.string.name,
    value = name // probably read from a database or typed by a user
)

Note

The @StringRes is a nice little annotation that the lint tool uses to ensure that the caller is passing a R.string.xyz. It's not required, but I like adding it as it helps me remember the intent of the parameter.

If the string resource has a placeholder, such as

<string name="filmography">Films that feature %1$s</string>

You can pass the required value as additional parameters:

Text(text = stringResource(id = R.string.filmography, actorName))

The app/src/main/res/values/strings.xml file acts as the default for all strings. To define alternatives, you add a configuation qualifier suffix to the values directory. For example:

app/src/main/res/values-en/strings.xml        - English (no region)
app/src/main/res/values-en-rGB/strings.xml    - English (United Kingdom)
app/src/main/res/values-es/strings.xml        - Spanish (no region)
app/src/main/res/values-es-sp/strings.xml     - Spanish (Mexico)

There are many other configuration qualifiers you can use, indicating device parameters such as screen size, orientation, Android version, UI mode (car, TV, watch, etc) and many others. See (Providing resources)[https://developer.android.com/guide/topics/resources/providing-resources] for more detail.

For more details on I18N/L10N, see Localize your app.