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

例子:

PLACEHOLDER1一些字符串


格式和样式

是的,请参阅下面的字符串资源:格式和样式

If you need to format your strings using String.format(String, Object...), then you can do so by putting your format arguments in the string resource. For example, with the following resource: <string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string> In this example, the format string has two arguments: %1$s is a string and %2$d is a decimal number. You can format the string with arguments from your application like this: 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);

有关更多细节,请参阅字符串资源:复数。


但是,你也应该阅读Elias Mårtenson关于Android复数处理“零”的回答。对某些值(如“零”)的解释存在问题。


当你想使用实际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文件中。


一直在寻找同样的方法,最后找到了下面这个非常简单的方法。最好的:它可以开箱即用。 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、名称、数);

其中c是上下文,名称是一个字符串变量和计数你的int变量

你需要包括

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

在res/strings.xml中。 对我有用。:)


补充回答

当我第一次在接受的答案中看到%1$s和%2$d时,它毫无意义。这里有更多的解释。

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

%[parameter_index$][format_type] 

%: The percent sign marks the beginning of the format specifier. parameter index: This is a number followed by a dollar sign. If you had three parameters that you wanted to insert into the string, then they would be called 1$, 2$, and 3$. The order you place them in the resource string doesn't matter, only the order that you supply the parameters. format type: There are a lot of ways that you can format things (see the documentation). Here are some common ones: s string d decimal integer f floating point number

例子

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

我妹妹玛丽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);

笔记

I could use getString because I was in an Activity. You can use context.getResources().getString(...) if it is not available. String.format() will also format a String. The 1$ and 2$ terms don't need to be used in that order. That is, 2$ can come before 1$. This is useful when internationalizing an app for languages that use a different word order. You can use a format specifier like %1$s multiple times in the xml if you want to repeat it. Use %% to get the actual % character. For more details read the following helpful tutorial: Android SDK Quick Tip: Formatting Resource Strings


Kotlin版本的公认答案…

val res = resources
val text = String.format(res.getString(R.string.welcome_messages), username, mailCount)

你可以使用MessageFormat:

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

在Java代码中:

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

火力等级1:

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


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

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


在res /价值/ 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);

是的!你可以不写任何Java/Kotlin代码,只使用XML,使用我创建的这个小库,它在构建时这样做,所以你的应用程序不会受到它的影响:https://github.com/LikeTheSalad/android-stem

使用

你的字符串:

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

构建后生成的字符串:

<!-- Auto generated during compilation -->
<resources>
    <string name="welcome_message">Welcome to My App Name</string>
</resources>

问题的直接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 )

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

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

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

如果你只写%1$d%,你会得到一个错误:格式字符串'percent'不是一个有效的格式字符串,所以它不应该被传递给string . Format。

或者使用格式化=false"代替。


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

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

多语言项目

作为一个曾经致力于大型白标解决方案的人,我可以说有很多需要考虑的地方。 除了文本方向,语法本身也会让你头疼。 例如,项目的顺序可以改变这样

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

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

然后在你的变体中,变体/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变量一个文件。

还有一个数字语法的问题。这可以用复数来解决,但这里xml文件的复杂性又增加了。而且,正如所指出的,零并不像人们所期望的那样工作。但你也可能想要限制你的应用的计数,因为显示大小限制或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代码。