使用String.xml的动态字符串?

时间:2010-09-07 06:55:03

标签: android string string-formatting android-resources

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

示例:

  

某些字符串 PLACEHOLDER1 更多字符串

14 个答案:

答案 0 :(得分:897)

格式和样式

是的,请参阅String Resources: Formatting and Styling

中的以下内容
  

如果需要使用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);

有关详细信息,请参阅String Resources: Plurals

答案 1 :(得分:202)

补充答案

当我第一次在接受的答案中看到%1$s%2$d时,这没有任何意义。这是一个更多的解释。

它们被称为格式说明符。在xml字符串中,它们的格式为

%[parameter_index$][format_type] 
  • :百分号表示格式说明符的开头。
  • 参数索引:这是一个后跟美元符号的数字。如果您要将三个参数插入到字符串中,那么它们将被称为1$2$3$。您将它们放在资源字符串中的顺序并不重要,只是您提供参数的顺序。
  • 格式类型:您可以使用批次格式化事物(see the documentation)。以下是一些常见的内容:

    • s字符串
    • d十进制整数
    • f浮点数

实施例

我们将创建以下格式化字符串,其中以编程方式插入灰色部分。

  

我姐姐Mary已年12岁。

string.xml

<string name="my_xml_string">My sister %1$s is %2$d years old.</string>

MyActivity.java

String myString = "Mary";
int myInt = 12;
String formatted = getString(R.string.my_xml_string, myString, myInt);

注释

  • 我可以使用getString,因为我在一个活动中。如果不可用,您可以使用context.getResources().getString(...)
  • String.format()也会格式化一个字符串。
  • 1$2$条款不需要按此顺序使用。也就是说,2$可以在1$之前出现。在将应用程序国际化为使用不同单词顺序的语言时,这非常有用。
  • 如果要重复,可以在xml中多次使用%1$s等格式说明符。
  • Use %% to get the actual % character.
  • 有关详细信息,请阅读以下有用的教程:Android SDK Quick Tip: Formatting Resource Strings

答案 2 :(得分:112)

如果要在不使用任何Java代码的情况下使用实际strings.xml文件中的参数:

<?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文件中。

答案 3 :(得分:16)

寻找同样的东西,终于找到了以下非常简单的解决方案。最好的:它开箱即用。
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是Context, name 是一个字符串变量而 count 是你的int变量

您需要包含

<resources xmlns:xliff="http://schemas.android.com/apk/res-auto">
你的res / strings.xml中的

。 适合我。 :)

答案 4 :(得分:2)

但是,您还应该在Elias Mårtenson上阅读Android plurals treatment of “zero”的答案。某些值的解释存在问题,例如“零”。

答案 5 :(得分:2)

如果要写百分比(%),请复制它:

<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

答案 6 :(得分:1)

在Kotlin中你只需要设置你的字符串值:

<TextView android:id="@+id/song_number_and_title"/>

在布局上创建文字视图:

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

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

{{1}}

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

答案 7 :(得分: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);

答案 8 :(得分:0)

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

def setupUi(self, MainWindow):
    self._mainWindow = MainWindow
    mainWindow.setUi(self)

答案 9 :(得分:0)

您可以使用cleaner = lambda x: ' '.join(x.split()) if isinstance(x, str) else x df = df.applymap(cleaner)

MessageFormat

在Java代码中:

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

API级别1:

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

答案 10 :(得分:0)

是的!您无需编写任何Java / Kotlin代码,而只需编写XML,就可以使用我创建的这个小型库(在构建时就这样做了),这样您的应用就不会受到它的影响:https://github.com/LikeTheSalad/android-string-reference

用法

您的字符串:

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

答案 11 :(得分:0)

该问题的直接Kotlin解决方案:

strings.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 )

答案 12 :(得分:0)

在您的字符串文件中使用

public class MyDTO {
    @NotNull
    @JsonDeserialize(using = BooleanDeserializer.class)
    private Boolean confirmation;
}

然后在您的代码中相应使用

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

答案 13 :(得分:0)

用于多语言项目

作为一个主要的白标解决方案工作过的人,每个变体都有许多不同的语言和配置,我可以说需要考虑很多。 撇开文本方向不谈,仅凭语法就会让您头疼。 例如,项目的顺序可以这样改变

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

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

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

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

也不可行,让非技术人员尝试找出 xlift 的想法甚至无法接受,那么我目前知道的最佳解决方案是使用明确的、命名的占位符< /strong> 如此

<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 方面工作的可怜的傻瓜,这是我的经验所要求的用于修改 .xcodeproj 文件并生成 swift 代码的 python 脚本。