11

I would like to be able to load the value of user-visible strings from res/values/strings.xml from our CMS (or some DB), during the gradle build.

E.g.

<string name="button_label">OK, do it</string>

Could be changed to

<string name="button_label">OK, do it now!</string>

... or whatever.

The idea being that the new value would be read AT BUILD TIME from our CMS, and then would get baked into the APK file. ( The motivation is to automate changes to app strings, so that any text from the app would be read from an existing CMS).

What's the best way to achieve this? Is it possible to generate/modify the resource files (e.g. strings.xml ) during build, just before they are used by the Android build system?


Adding some more requirements:

treesAreEverywhere
  • 3,722
  • 4
  • 28
  • 51
  • 1
    the easy way: http://tools.android.com/tech-docs/new-build-system variant.resValue ... other: build own gradle plugin ... – Selvin Feb 08 '16 at 15:03
  • Thanks Selvin, that looks promising. One question about this though: How would I go about providing fallback values for different languages? Is that possible with this function? – treesAreEverywhere Feb 08 '16 at 15:05
  • `applicationVariants.all { variant -> variant.resValue "string", "button_xxx", "Here goes the text" }` ... of course you need to learn some Groovy basics (for example to get those values online or from your specific xml/json file) ... fx https://gist.github.com/SelvinPL/f89476b6585690cb252b ... i use it to generate fx `colorPrimaryAX` which is colorPrimary with X% alpha https://gist.github.com/SelvinPL/8e673e38c5fed8e2132b – Selvin Feb 08 '16 at 15:09
  • ah, two more problems ... you cannot override those values ... so if there is already `button_xxx` in your res folder you can't use it with resValue ... – Selvin Feb 08 '16 at 15:13
  • By "languages" I meant.. "Language Resource qualifiers" as defined here: http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources – treesAreEverywhere Feb 08 '16 at 15:13
  • Overriding: Yes, that'd be the requirement, otherwise it's very difficult to work with these strings as a developer ;-) – treesAreEverywhere Feb 08 '16 at 15:14

1 Answers1

6

Here's a solution where your strings are modified via an external properties file. Not sure that this fits your requirement exactly, but it should get you off to a start.

I do something similar in my builds - I have some custom gradle tasks with this type of replacement called inside of their doFirst(). The regex here could probably use some polishing, and the input format may change per your requirement, but in a few local tests this seems to work for me.

ext.updateStrings = {
println("Changing strings!")
Properties props = new Properties()
props.load(new FileInputStream(file('cms.properties')))
def stringsFile = file("src/main/res/values/strings.xml")
def stringsText = stringsFile.getText()
for (p in props) {
    def pattern = Pattern.compile(String.format("%s\">.*<", p.key));
    def match = pattern.matcher(stringsText);
    match.find();
    println("found key " + p.key + ", replacing string with " + p.value)
    stringsText = match.replaceAll(String.format("%s\">%s<", p.key, p.value))
    stringsFile.write(stringsText)
    }
}

I keep my external properties/functions in a separate file (for instance common.gradle). In your build.gradle, you could add a custom task like so:

apply from: "common.gradle"
task updateStringsXml() {
    doFirst {
        updateStrings()
    }
}

Then, your gradle command might look like gradle updateStringsXml assembleRelease.

lase
  • 2,508
  • 2
  • 24
  • 35
  • The interesting part is... how do you integrate this into the build process, also if you have different flavours/buildTypes, as well as language overrides? – treesAreEverywhere Feb 08 '16 at 20:12
  • See my edit - as for flavours/build types, you could easily add project properties and conditionally change your strings in this way. I like to use them as described [here](http://stackoverflow.com/questions/11696521/how-to-pass-arguments-from-command-line-to-gradle). Same with languages, your "cms.properties" could contain whatever language you like. – lase Feb 08 '16 at 22:03
  • great answer, the code was helpful thanks – Thiago Jul 25 '22 at 16:20