【问题标题】:Localizing string resources added via build.gradle using "resValue"使用“resValue”本地化通过 build.gradle 添加的字符串资源
【发布时间】:2016-03-19 18:27:38
【问题描述】:

这是对this post 帮助我的答案的延续

我们可以从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 来执行此操作(由于我的项目结构有多种方式,这是不可行的)

【问题讨论】:

  • 我认为如果你需要本地化它,那么可能不应该在 gradle 上。例如,主要用途是拥有两个不同的 API 密钥,一个用于 Prod,一个用于 Dev。
  • 在我的情况下,产品风格不是针对环境,而是针对应用程序的不同变体。所有这些都投入生产。我的环境是通过构建类型控制的。不能为每种口味使用单独的 xml,因为我有很多而且还在不断增加。因此选择通过 gradle 来实现。
  • 很抱歉提供strings.xml 解决方案,但我认为您不能这样做。如果您有分离问题,您可以根据本地化内容添加不同的res.srcDirs,但您添加的文件夹可以包含多个values-<lang>/strings.xml 文件。
  • 顺便说一句,我认为这只会更改启动器图标的标签,可能是设置应用程序中的标题,但 Play 商店中的应用程序名称取决于您在开发者控制台中输入的内容,它是本地化。
  • 是的,这就是我想要的

标签: android android-studio android-gradle-plugin android-resources android-productflavors


【解决方案1】:

不过,我对生成资源的其他回答可能对您的用例来说太过分了。根据我目前对您项目的了解,我认为这个项目更合适: (并不是说您仍然可以将其与生成的资源结合使用)

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 处理:

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

【讨论】:

  • @AndroidMechanic 你有什么运气吗?
  • 没有没有按我的意愿工作。现在已经创建了单独的 xml。但应该有办法解决这个问题。
【解决方案2】:

如果您不必对这些字符串进行操作,最好的选择是移至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-&lt;locale&gt; 文件夹中。

【讨论】:

  • 是否有机会在构建之前使用任务替换构建配置字段中的字符串 res?
  • 你的意思是字符串对于给定的 APK 是固定的?例如,一个 APK 仅用于 en_rUS 语言环境,另一个仅用于 pt_rBR,其他 APK 用于 ja?这样,是的,这是可能的。如果是这样,我会更新我的答案。不可能在同一个 APK 中有多个语言环境,例如我们使用 values-envalues-pt 等所做的事情。为此,您需要 res/ 文件夹
  • 哦,不,对不起,我的错。没有想通。对不起! :) 它不是固定的。需要每个语言环境的本地化。
  • 事实证明,如果不使用单独的 strings.xml,目前没有可用的方法来做到这一点。因此,将赏金奖励给这个答案。但希望在未来的框架中能提供一些东西。如果有人知道它,请在此处发布答案。谢谢道格拉斯!
【解决方案3】:

您在此处操作不同级别,BuildConfig 是代码,因此未本地化,这就是我们对硬编码字符串发出 Lint 警告的原因。 Android 中的本地化是通过&lt;string 资源完成的,如果您希望系统在运行时根据用户设置选择语言,则无法解决。不过获取资源的方式有很多:values 文件夹、build.gradle 中的resValue 以及生成的资源。

您应该查看buildSrc project in Gradle,例如我使用它从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
}

【讨论】:

  • 谢谢!我一到家就会检查这个
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多