使用"resValue"添加的字符串资源在build.gradle中本地化

24

这是对我在这篇文章上得到帮助的续篇。

我们可以通过build.gradle将字符串资源添加如下:

productFlavors {
    main{
        resValue "string", "app_name", "InTouch Messenger"
    }

    googlePlay{
        resValue "string", "app_name", "InTouch Messenger: GPE Edition"
    }
}
它的作用非常好,可以实现每个味道的不同应用程序名称。(原始的app_name字符串资源已从strings.xml文件中删除。但是,我们如何为从build.gradle添加的此字符串资源添加本地化字符串?是否有一个额外的参数可以传递指定语言环境?或者可以使用gradle任务完成吗?请注意:我不能使用strings.xml来完成这个任务(因为我的项目结构有几种不同的方式)。

2
我认为如果你需要本地化它,那么可能不应该在Gradle上。主要用途是例如拥有两个不同的API密钥,一个用于生产环境,一个用于开发环境。 - hmartinezd
在我的情况下,产品口味不是针对环境的,而是针对应用程序的不同变体。所有这些变体都会进入生产环境。我的环境是通过构建类型进行控制的。不能为每个口味使用单独的XML文件,因为我有很多口味,并且数量还在不断增加。因此,选择通过Gradle来完成。 - Viral Patel
很抱歉只能提供strings.xml的解决方案,但我认为你无法用其他方式实现。如果你有分离问题,可以根据你要本地化的内容添加不同的res.srcDir,但是你添加的文件夹可以包含多个values-<lang>/strings.xml文件。 - TWiStErRob
顺便说一下,我认为这只会更改您的启动器图标标签,可能是设置应用程序中的标题,但Play Store中的应用程序名称基于您在开发人员控制台中输入的内容,并且在那里进行本地化。 - TWiStErRob
是的,那就是我想要的。 - Viral Patel
显示剩余2条评论
3个回答

13

关于生成的资源文件的那个答案可能对你的用例来说有些过度了。基于我目前了解到的你的项目,我认为这一个更适合: (并且你仍然可以将其与生成的资源文件结合使用)

src/flavor1/res/values/strings.xml

<string name="app_name_base">InTouch Messenger"</string>
<string name="app_name_gpe">InTouch Messenger: GPE Edition"</string>

src/flavor1/res/values-hu/strings.xml

<string name="app_name_base">InTouch Üzenetküldő"</string>
<string name="app_name_gpe">InTouch Üzenetküldő: GPE Változat"</string>

src/flavor2/res/values/strings.xml

<string name="app_name_base">Whatever Messenger"</string>
<string name="app_name_gpe">Whatever Messenger: GPE Edition"</string>

src/flavor2/res/values-hu/strings.xml`

<string name="app_name_base">Whatever Üzenetküldő"</string>
<string name="app_name_gpe">Whatever Üzenetküldő: GPE Változat"</string>

build.gradle

android {
    sourceSets {
        [flavor1, flavor3].each {
            it.res.srcDirs = ['src/flavor1/res']
        }
        [flavor2, flavor4].each {
            it.res.srcDirs = ['src/flavor2/res']
        }
    }
    productFlavors { // notice the different numbers than sourceSets
        [flavor1, flavor2].each {
            it.resValue "string", "app_name", "@string/app_name_base"
        }
        [flavor3, flavor4].each {
            it.resValue "string", "app_name", "@string/app_name_gpe"
        }
    }
}
这意味着flavor1/2将有一个额外未使用的 app_name_gpe 字符串资源,但这些都会被 aapt 处理:

这意味着flavor1/2将会有一个多余且未使用的app_name_gpe字符串资源,但是aapt会处理它:

android {
    buildTypes {
        release {
            shrinkResources true // http://tools.android.com/tech-docs/new-build-system/resource-shrinking
        }

@AndroidMechanic,你有没有在这两个方面有什么好运? - TWiStErRob
没有按照我想要的方式工作。现在已经创建了单独的XML文件。但应该有一些解决方法。 - Viral Patel

5
如果您不需要操作这些字符串,最好的选择是转移到strings.xml,但这将使您在口味之间共享所有res文件夹。
如果您根据build.gradle上的某个属性生成这些字符串,那么我想您可能会遇到麻烦。
编辑:澄清我所说的“操作”,并添加一些选项:
通过“操作这些字符串”我指的是某种与构建参数连接、从命令行或构建过程中的环境变量读取(例如获取提交SHA1以便更轻松地跟踪错误)等。如果没有必要进行任何操作,则可以使用strings.xml。但是,当您为风味覆盖res文件夹时,所有内容都将被覆盖,如果多个风味共享相同的res,除了有限数量的字符串之外,这可能会导致问题。
如果每个APK具有自己的语言环境,则只需在风味中使用resValuebuildConfigField。您可以定义变量以更轻松地重用值。类似于:
def myVar = "var"

...

flavor1 {
    resValue "string", "my_res_string", "${myVar}"
}

flavor2 {
    resValue "string", "my_res_string", "${myVar}"
}

但是如果需要在同一个APK中使用多个语言环境,并且Android会在运行时选择语言环境,那么字符串必须位于正确的values-<locale>文件夹中。


有没有可能在构建之前使用任务来替换构建配置字段中的字符串 res? - Viral Patel
你的意思是说字符串是针对特定APK固定的吗?例如,一个APK只适用于en_rUS区域设置,另一个只适用于pt_rBR,另一个适用于ja?这样是可以的。如果是这样,我会更新我的答案。不可能的是在同一个APK中拥有多个区域设置,例如我们使用的values-envalues-pt等。为此,您需要res/文件夹。 - Douglas Drumond Kayama
哦不,抱歉我的错。没有好好想过。对不起! :) 这还没有解决。需要为每个区域设置本地化。 - Viral Patel
结果发现目前没有可用的方法来做到这一点,除非使用单独的strings.xml。因此将赏金授予此答案。但希望在未来的框架中会有一些可用的东西。如果有人知道,请在这里发布答案。谢谢Douglas! - Viral Patel

2
您在进行不同的操作,其中BuildConfig是代码,因此不具有本地化功能,这就是为什么我们需要使用Lint警告进行硬编码字符串的原因。Android中的本地化是通过<string资源来完成的,如果您希望系统根据用户设置在运行时选择语言,则没有其他方法。但是,有很多资源可用: values文件夹、build.gradle中的resValue和生成的资源。

您应该查看Gradle中的buildSrc项目,例如我使用它从src/main/values/stuff.xml生成SQL插入语句。以下是一些起步代码。

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
            }
        }
    }
}

这只是一个框架,你可以自由发挥并从中进行任何操作:例如通过网络检索CSV,并将其内容分散到生成的variant*/res/values-*/gen.xml文件中。

你可以在需要时手动运行它,或在构建生命周期的正确时刻运行它(在build.gradle中):

android.applicationVariants.all { com.android.build.gradle.api.ApplicationVariant variant ->
    variant.mergeAssets.dependsOn tasks.generateYourStuff
}

谢谢!我一回家就会检查这个。 - Viral Patel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接