This is in continuation to an answer which helped me on this post
We can add the string resource as follows from build.gradle
:
productFlavors { main{ resValue "string", "app_name", "InTouch Messenger" } googlePlay{ resValue "string", "app_name", "InTouch Messenger: GPE Edition" } }
It works like a charm and serves the purpose of having different app names per flavor. (with the original app_name
string resource deleted from strings.xml
file.
But, how do we add localized strings for this string resource added from build.gradle
?
Is there an additional parameter we can pass specifying the locale?
OR
Possible to do it using a gradle
task?
Note: I cannot do this using strings.xml
(not feasible because of several ways in which my project is structured)
You're operating on different levels here, BuildConfig
is code, and as such not localized, that's why we have Lint warnings for hard-coded strings. Localization in Android is done via <string
resources, there's no way around that if you want the system to choose the language at runtime depending on user settings. There are many ways to have resources though: values
folder, resValue
in build.gradle, and generated resources.
You should look into the buildSrc
project in Gradle, for example I use it to generate SQL Inserts from src/main/values/stuff.xml
. Here's some code to start with.
buildSrc/build.gradle
// To enable developing buildSrc in IDEA import buildSrc/build.gradle as a separate project
// Create a settings.gradle in buildSrc as well to prevent importing as subproject
apply plugin: 'groovy'
repositories { jcenter() }
dependencies {
compile localGroovy()
compile gradleApi()
testCompile 'junit:junit:4.12'
}
buildSrc/src/main/groovy/Plugin.groovy
import org.gradle.api.*
/**
* Use it as
* <code>
* apply plugin: MyPlugin
* myEntities {
* categories {
* input = file(path to Android res xml with Strings)
* output = file(path to asset SQL file)
* conversion = "structure|SQL"
* }
* }
* </code>
*/
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
def entities = project.container(MyEntity)
// this gives the name for the block in build.gradle
project.extensions.myEntities = entities
def allTasks = project.task('generateYourStuff')
def allTasksClean = project.task('cleanGenerateYourStuff')
project.afterEvaluate {
entities.all { entity ->
//println "Creating task for ${entity.name} (${entity.input} --${entity.conversion}--> ${entity.output})"
def task = project.task(type: GenerateTask, "generateYourStuff${entity.name.capitalize()}") {
input = entity.input
output = entity.output
conversion = entity.conversion
}
allTasks.dependsOn task
// clean task is automagically generated for every task that has output
allTasksClean.dependsOn "clean${task.name.capitalize()}"
}
}
}
}
class MyEntity {
def input
def output
String conversion
final String name
MyEntity(String name) {
this.name = name
}
}
buildSrc/src/main/groovy/GenerateTask.groovy
import net.twisterrob.inventory.database.*
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.*
class GenerateTask extends DefaultTask {
@InputFile File input
@OutputFile File output
@Optional @Input String conversion
@TaskAction void generate() {
input.withReader { reader ->
// you may need to treat output as a folder
output.parentFile.mkdirs()
output.withWriter { writer ->
// custom transformation here read from reader, write to writer
}
}
}
}
This is just the skeleton you can go wild and do anything from here: e.g. retrieve a CSV through the network and spread the contents into generated variant*/res/values-*/gen.xml
files.
You can run it manually when you need to or run it at the right point in the build lifecycle (in build.gradle
:
android.applicationVariants.all { com.android.build.gradle.api.ApplicationVariant variant ->
variant.mergeAssets.dependsOn tasks.generateYourStuff
}
If you do not have to operate on those strings, the best option is moving to strings.xml
, but that would make you share all res
folder between flavors.
If you generate these strings based on some property on build.gradle
, then I think you're out of luck, unfortunately.
EDIT: clarifying what I mean by operate above and add some options:
By operating on those strings I mean some sort of concatenation with a build parameter, a reading from command line or environment variable during the build process (e.g., getting the commit SHA1 so that it's easier to trace bugs later). If no operation is necessary, strings.xml
may be an option. But when you overwrite a res
folder for flavor, all of it is overwritten and that could pose a problem if several flavors share the same res
except for a limited number of strings.
If each APK has its own locale, then it's just a resValue
or buildConfigField
in a flavor. You can define variables to for easier reuse of values. Something like
def myVar = "var"
...
flavor1 {
resValue "string", "my_res_string", "${myVar}"
}
flavor2 {
resValue "string", "my_res_string", "${myVar}"
}
But if several locales are needed in the same APK and it will be chosen at runtime by Android, then the string must be in the correct values-<locale>
folder.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With