Skip to content

This is a Gradle plugin for Android applications that concatenates XML strings during compilation

License

Notifications You must be signed in to change notification settings

LikeTheSalad/android-stem

Repository files navigation

Android Stem

version

paypal ko-fi


What It Is

Android Stem is a Gradle plugin that resolves placeholders in XML string resources at compile time.
No Java or Kotlin code is required — the plugin generates fully resolved strings accessible just like any other resource in your app.

If you want to do this:

Input

<resources>
    <string name="app_name">My App Name</string>
    <string name="welcome_message">Welcome to ${app_name}</string>
</resources>

Output

<!-- Auto-generated during compilation -->
<resources>
    <string name="welcome_message">Welcome to My App Name</string>
</resources>

— without writing any additional code, Android Stem is for you.


How to Use

1. Add It to Your Project

Add the plugin to your Android application's build.gradle[.kts] file:

plugins {
    id("com.android.application") version "8.4.0" // Minimum supported AGP version
    id("com.likethesalad.stem") version "LATEST_VERSION"
}

Find the latest version here.


2. Create and use Templates

A template is a string that references one or more placeholders.
You can define templates in your app's res directories or import them from external libraries.

Defining Templates in Your App

<resources>
    <string name="app_name">My App Name</string>
    <string name="welcome_message">Welcome to ${app_name}</string>
</resources>

The string welcome_message is a template because it includes a placeholder (${app_name}).
Templates and regular strings can coexist in the same XML files, such as values/strings.xml, or in separate ones.

Localization and flavor-specific templates are supported — you can define them under:

  • main/res/values-[lang]/ for localized templates, or
  • [flavor]/res/values/ for variant-specific templates.

Templates can also reference other templates, which will be resolved recursively.

Both values and templates can be overridden per language or flavor.
For instance, if you override app_name in a flavor's values folder, all templates using ${app_name} will reflect the new value.
Similarly, language-specific overrides (e.g., values-es) can provide localized template translations.


Using Strings from Libraries

You can import templates from other modules or external libraries by defining them as stemProvider dependencies:

plugins {
    id("com.likethesalad.stem") version "LATEST_VERSION"
}

dependencies {
    stemProvider(project(":my-submodule-with-templates"))
    stemProvider("an.external.lib:with-templates:0.0.0")
}

3. Running It

The placeholder resolution process runs automatically during your app's compilation.
You can trigger it in multiple ways:

  • In Android Studio:
    • Click the Run button Play button
    • Or click the Make Project button Make button
  • From the command line:
    ./gradlew build
    # or
    ./gradlew assemble
    # or to run only the placeholder task:
    ./gradlew [BUILD_VARIANT]ResolvePlaceholders

More details under Running It Manually.

4. Checking its results

At Runtime

You can check the resolved strings at runtime by using the templates created on Step 2 on your layout files. For example:

<!-- my_activity.xml -->
<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@string/welcome_message" />

By the time your app is running, the welcome_message template will be already resolved, so your TextView in this example will read: Welcome to My App Name.

In Unit Tests

You could also check resolved strings in unit tests by using Robolectric, and ensuring you include android resources for your tests, as explained in their setup guide.

Example

@RunWith(RobolectricTestRunner::class)
class StringResolvingTest {

    @Test
    fun check_welcomeMessageStringIsResolved() {
        val application = RuntimeEnvironment.getApplication()

        val welcomeMessage = application.getString(R.string.welcome_message)

        assertEquals("Welcome to My App Name", welcomeMessage)
    }
}

Configuration

Stem's default configuration should suit most projects, but you can adjust its behavior via your module's build.gradle[.kts]:

androidStem {
    // By default, Stem searches for templates only within "values" to use as a reference for all languages.
    // Enable this flag if your translations (e.g., values-es) contain templates not present in the default folder.
    includeLocalizedOnlyTemplates = false // Default: false

    // Customize the placeholder format.
    placeholder {
        start = "\${" // Default
        end = "}"     // Default
    }
}

Enabling includeLocalizedOnlyTemplates may slow down processing for large projects.


Use Case Examples

1. Simple Case

<!-- strings.xml -->
<resources>
    <string name="app_name">Test</string>
    <string name="welcome_message">Welcome to ${app_name}</string>
</resources>

Output:

<!-- Auto-generated -->
<resources>
    <string name="welcome_message">Welcome to Test</string>
</resources>

2. Multiple Files

<!-- strings.xml -->
<resources>
    <string name="app_name">Test</string>
    <string name="welcome_message">Welcome to ${app_name}</string>
    <string name="app_version_name">The version for ${app_name} is ${my_version}</string>
</resources>
<!-- my_configs.xml -->
<resources>
    <string name="my_version">1.0.0</string>
</resources>

Output:

<!-- Auto-generated -->
<resources>
    <string name="app_version_name">The version for Test is 1.0.0</string>
    <string name="welcome_message">Welcome to Test</string>
</resources>

3. Multiple Languages

Default:

<!-- values/strings.xml -->
<resources>
    <string name="app_name">Test</string>
    <string name="welcome_message">Welcome to ${app_name}</string>
</resources>

Spanish:

<!-- values-es/any_file.xml -->
<resources>
    <string name="welcome_message">Bienvenido a ${app_name}</string>
</resources>

Output:

<!-- values -->
<string name="welcome_message">Welcome to Test</string>
<!-- values-es -->
<string name="welcome_message">Bienvenido a Test</string>

4. Flavors

Default:

<resources>
    <string name="app_name">Test</string>
    <string name="welcome_message">Welcome to ${app_name}</string>
    <string name="app_version_name">The version for ${app_name} is ${my_version}</string>
    <string name="my_version">1.0.0</string>
</resources>

Flavor (demo):

<resources>
    <string name="app_name">Demo app</string>
</resources>

Output for demo build:

<!-- Auto-generated -->
<resources>
    <string name="app_version_name">The version for Demo app is 1.0.0</string>
    <string name="welcome_message">Welcome to Demo app</string>
</resources>

You can override both values and templates in flavors or localized folders — all combinations are supported.


Running It Manually

To run only the placeholder resolution task (without building the app):

./gradlew [BUILD_VARIANT]ResolvePlaceholders

Examples:

./gradlew debugResolvePlaceholders
./gradlew demoDebugResolvePlaceholders

Donations ♥

If you find this plugin useful, please consider supporting its development — even a small contribution helps maintain and improve it.
If you can't donate, sharing it with your developer friends is also a great way to help!

<string>"Thanks for your support, ${your_beautiful_name}!"</string>

paypal ko-fi


License

MIT License

Copyright (c) 2019 LikeTheSalad.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

About

This is a Gradle plugin for Android applications that concatenates XML strings during compilation

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published

Contributors 6