585

是否可以在字符串值中有占位符string.xml,可以在运行时分配值?

例子:

一些字符串PLACEHOLDER1更多字符串

4

14 回答 14

1179

格式化和样式

是的,请参阅字符串资源中的以下内容:格式和样式

如果您需要使用 格式化字符串String.format(String, Object...),则可以通过将格式参数放入字符串资源中来实现。例如,使用以下资源:

<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>

在这个例子中,格式字符串有两个参数:%1$s一个字符串和%2$d一个十进制数。您可以使用应用程序中的参数格式化字符串,如下所示:

Resources res = getResources();
String text = String.format(res.getString(R.string.welcome_messages), username, mailCount);

基本用法

请注意,getString有一个使用字符串作为格式字符串的重载:

String text = res.getString(R.string.welcome_messages, username, mailCount);

复数

如果您需要处理复数,请使用:

<plurals name="welcome_messages">
    <item quantity="one">Hello, %1$s! You have a new message.</item>
    <item quantity="other">Hello, %1$s! You have %2$d new messages.</item>
</plurals>

第一个mailCount参数用于决定使用哪种格式(单数或复数),其他参数是您的替换:

Resources res = getResources();
String text = res.getQuantityString(R.plurals.welcome_messages, mailCount, username, mailCount);

有关更多详细信息,请参阅字符串资源:复数

于 2010-09-07T07:01:34.260 回答
353
于 2016-11-21T08:04:29.173 回答
135

当您想使用实际 strings.xml 文件中的参数而不使用任何 Java 代码时:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
  <!ENTITY appname "WhereDat">
  <!ENTITY author "Oded">
]>

<resources>
    <string name="app_name">&appname;</string>
    <string name="description">The &appname; app was created by &author;</string>
</resources>

这不适用于跨资源文件,即必须将变量复制到需要它们的每个 XML 文件中。

于 2014-07-23T06:12:44.107 回答
20

一直在寻找相同的,最后找到了以下非常简单的解决方案。最好的:它开箱即用。
1.改变你的字符串资源:

<string name="welcome_messages">Hello, <xliff:g name="name">%s</xliff:g>! You have 
<xliff:g name="count">%d</xliff:g> new messages.</string>

2.使用字符串替换:

c.getString(R.string.welcome_messages,name,count);

其中 c 是上下文,名称是字符串变量并计算您的 int 变量

你需要包括

<resources xmlns:xliff="http://schemas.android.com/apk/res-auto">

在您的 res/strings.xml 中。为我工作。:)

于 2015-06-05T13:16:46.657 回答
6

如果你想写百分比 (%),复制它:

<string name="percent">%1$d%%</string>

label.text = getString(R.string.percent, 75) // Output: 75%.

如果你写简单%1$d%,你会得到错误:Format string 'percent' is not a valid format string so it should not be passed to String.format

或改为使用 formatted=false"

于 2020-05-14T14:50:31.903 回答
5

在 Kotlin 中,您只需要像这样设置字符串值:

<string name="song_number_and_title">"%1$d ~ %2$s"</string>

在布局上创建一个文本视图:

<TextView android:text="@string/song_number_and_title"/>

如果您使用 Anko,请在您的代码中执行此操作:

val song = database.use { // get your song from the database }
song_number_and_title.setText(resources.getString(R.string.song_number_and_title, song.number, song.title))  

您可能需要从应用程序上下文中获取资源。

于 2017-10-10T19:11:42.953 回答
3

在你的字符串文件中使用这个

<string name="redeem_point"> You currently have %s points(%s points = 1 %s)</string>

并在您的代码中相应地使用

coinsTextTV.setText(String.format(getContext().getString(R.string.redeem_point), rewardPoints.getReward_points()
                        , rewardPoints.getConversion_rate(), getString(R.string.rs)));
于 2020-05-28T11:28:51.323 回答
2
于 2012-04-09T23:22:30.900 回答
1

您可以使用MessageFormat

<string name="customer_address">Wellcome: {0} {1}</string>

在 Java 代码中:

String text = MessageFormat(R.string.customer_address).format("Name","Family");

API 级别 1:

https://developer.android.com/reference/java/text/MessageFormat.html

于 2017-09-20T06:39:11.623 回答
1

在 res/values/string.xml

<resources>
    <string name="app_name">Hello World</string>
    <string name="my_application">Application name: %s, package name: %s</string>
</resources>

在java代码中

String[] args = new String[2];
args[0] = context.getString(R.string.app_name);
args[1] = context.getPackageName();
String textMessage = context.getString(R.string.my_application,(Object[]) args);
于 2018-07-17T08:12:37.393 回答
1

是的!您可以在不编写任何 Java/Kotlin 代码的情况下执行此操作,只需使用我创建的这个小型库(在构建时执行此操作),因此您的应用程序不会受到它的影响:https ://github.com/LikeTheSalad/android -字符串引用

用法

你的字符串:

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

构建后生成的字符串:

<!--resolved.xml-->
<resources>
    <string name="welcome_message">Welcome to My App Name</string>
</resources>
于 2019-11-07T10:17:32.843 回答
1

对于多语言项目

作为一个曾为每个变体使用多种不同语言和配置的主要白标解决方案工作的人,我可以说有很多需要考虑的地方。抛开文字方向不谈,单单语法就可以让你有些头疼。例如,项目的顺序是否可以改变

<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>

是优先于

<string name="welcome_messages">Hello, %s! You have %d new messages.</string>

但是一旦您与通常不知道字符串或整数是什么的翻译人员一起工作,更不用说每种类型使用什么格式字符,或者一般不知道参数在代码中应用的顺序的人,甚至您自己也忘记了这一点,或者事情发生了变化,必须同时在多个地方更新,所以使用MessageFormatlike

<string name="welcome_message">Hello, {0}! You have {1} new messages.</string>

MessageFormat(R.string.welcome_message).format("Name", numMessages)

也不可行,让非技术人员试图弄清楚的想法xlift甚至不能被接受,那么到目前为止我所知道的最好的解决方案就是使用明确的、命名的占位符

<string name="placeholder_data" translatable="false">DATA</string>
<string name="placeholder_data" translatable="false">$DATA</string>
<string name="placeholder_data" translatable="false">%DATA%</string>

..或其他与您的文本不冲突的内容。

虽然你可以使用 DOCTYPE

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
  <!ENTITY placeholder_data "$DATA">
]>
<string name="text_with_data">Your data is &placeholder_data;.</string>

这不适用于每种语言的单独文件。

因此,在您main/res/values/strings.xml提供这样的占位符和默认字符串

<resources>

    <string name="placeholder_data" translatable="false">$DATA</string>
    <string name="placeholder_error" translatable="false">$ERROR</string>

    <string name="app_name">The App</string>

    <string name="content_loading">loading..</string>
    <string name="content_success">success: $DATA</string>
    <string name="content_error">error: $ERROR</string>

</resources>

然后在您的变体中variant/res/values-de/strings.xml

<resources>

    <string name="app_name">Die Applikation</string>

    <string name="content_loading">Ladevorgang..</string>
    <string name="content_success">Erfolg: $DATA</string>
    <string name="content_error">Netzwerkkommunikationsfehler: $ERROR</string>

</resources>

并使用它,写一些类似的东西

    textView.text = when (response) {
        is Data -> getText(content_success).resolveData(response.data)
        is Error -> getText(content_error).resolveError(response.error)
        is Loading -> getText(content_loading)
    }

使用一些辅助功能,例如

    fun CharSequence.resolveData(data: JsonObject) =
        toString().replace(getString(placeholder_data), data.toString())

    fun CharSequence.resolveError(error: Throwable) =
        toString().replace(getString(placeholder_error), error.toString())

仅出于对翻译文件和开发有参考的原因。因此,每个构建风格不应该有一个默认文件。只有一个默认文件,然后是每个语言 x 变体的一个文件。

现在还有数字语法的问题。这可以解决,plurals但这里 xml 文件的复杂性再次增加。而且,正如所指出的,zero它并不像人们期望的那样工作。但是,由于显示大小限制或 UI 的预渲染图像数量,您可能希望限制应用计数,并且需要显示99+而不是100. 一个解决方案是使用一个辅助函数,如

    fun Context.getText(
        quantity: Int,
        @PluralsRes resIdQuantity: Int,
        @StringRes resIdNone: Int? = null,
        @StringRes resIdMoreThan: Int? = null,
        maxQuantity: Int? = null,
    ): CharSequence {
        if (resIdMoreThan != null && maxQuantity != null && quantity > maxQuantity)
            return getText(resIdMoreThan)
        return if (resIdNone != null && quantity == 0) return getText(resIdNone)
        else resources.getQuantityText(resIdQuantity, quantity)
    }

覆盖和扩展复数解析器的行为。

如果每个变体都有可选功能,请添加res/values/strings-beans.xml类似:

<resources>

    <string name="placeholder_name" translatable="false">$NAME</string>
    <string name="placeholder_count" translatable="false">$COUNT</string>

    <string name="beans_content_bean_count_zero">Hello $NAME! You have no beans.</string>
    <string name="beans_content_bean_count_one">Hello $NAME! You have one bean.</string>
    <string name="beans_content_bean_count_many">Hello $NAME! You have $COUNT beans.</string>
    <string name="beans_content_bean_count_more_than_9000">Hello $NAME! You have over $COUNT beans!</string>
    <string name="beans_content_bean_count_two">@string/beans_content_bean_count_many</string>
    <string name="beans_content_bean_count_few">@string/beans_content_bean_count_many</string>
    <string name="beans_content_bean_count_other">@string/beans_content_bean_count_many</string>
    <plurals name="beans_content_bean_count">
        <item quantity="zero">@string/beans_content_bean_count_zero</item>
        <item quantity="one">@string/beans_content_bean_count_one</item>
        <item quantity="two">@string/beans_content_bean_count_two</item>
        <item quantity="few">@string/beans_content_bean_count_few</item>
        <item quantity="many">@string/beans_content_bean_count_many</item>
        <item quantity="other">@string/beans_content_bean_count_other</item>
    </plurals>

</resources>

而其中的变体variant-with-beans/res/value-en/strings-beans.xml只需要包含

<resources>

    <string name="beans_content_bean_count_zero">Hello $NAME! You have no beans.</string>
    <string name="beans_content_bean_count_one">Hello $NAME! You have one bean.</string>
    <string name="beans_content_bean_count_many">Hello $NAME! You have $COUNT beans.</string>
    <string name="beans_content_bean_count_more_than_9000">Hello $NAME! You have over 9000 beans!</string>

</resources>

可以在每个文件的基础上提供特定于语言的覆盖。要使用它,代码看起来像这样

    val name = "Bob"
    val beanCount = 3
    val limit = 9000
    text = getText(
        beanCount,
        beans_content_bean_count,
        beans_content_bean_count_zero,
        beans_content_bean_count_more_than_9000,
        limit,
    )
        .resolveCount(beanCount)
        .resolveName(name)

解析为输出

    beanCount = 0 -> "Hello Bob! You have no beans."
    beanCount = 1 -> "Hello Bob! You have one bean."
    beanCount = 3 -> "Hello Bob! You have 3 beans."
    beanCount = 9001 -> "Hello Bob! You have over 9000 beans!"

并且由于语言特定资源文件的简单性,然后可以使用电子表格或您公司自己的服务器端点等的部署工具生成它们。

我希望你喜欢我进入 Android 动态字符串资源世界的疯狂旅程,并希望你不是那些不得不在产品的 iOS 端获得相同功能的可怜的傻瓜,根据我的经验,这需要 python 脚本修改 .xcodeproj 文件并生成 swift 代码。

于 2021-07-10T06:18:35.533 回答
0

问题的直接 Kotlin 解决方案:

字符串.xml

<string name="customer_message">Hello, %1$s!\nYou have %2$d Products in your cart.</string>

kotlinActivityORFragmentFile.kt:

val username = "Andrew"
val products = 1000
val text: String = String.format(
      resources.getString(R.string.customer_message), username, products )
于 2020-05-02T10:20:41.033 回答
-1

已接受答案的 Kotlin 版本...

val res = resources
val text = String.format(res.getString(R.string.welcome_messages), username, mailCount)
于 2017-06-22T15:36:48.037 回答