是否有可能在strings.xml运行时值占位符?

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

例子:

一些字符串PLACEHOLDER1

349670 次浏览

格式和样式

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

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

更多细节请参见字符串资源:复数

但是,你也应该阅读伊莱亚斯好好地Android对“0”的复数处理的回答。对某些值(如“零”)的解释存在问题。

当你想使用实际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. 使用字符串替换:

# EYZ0

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

你需要包括

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

在你的res/strings.xml。 对我有用。:) < / p >

补充回答

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

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

%[parameter_index$][format_type]
  • :百分号标志着格式说明符的开始。

  • 参数指标:这是一个数字后面跟着一个美元符号。如果有三个参数要插入到字符串中,那么它们将被称为1$2$3$。在资源字符串中放置参数的顺序并不重要,重要的是提供参数的顺序。

  • 格式类型:有很多的方式,你可以格式化的东西(参见文档)。以下是一些常见的错误:

  • < p > # EYZ0字符串

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

笔记

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

API等级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%,就会得到错误:Format string 'percent' is not a valid format string so it should not be passed to String.format

或者用使用 formatted=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>

然后在你的变种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代码。