是否可能在string.xml中的字符串值中有占位符,可以在运行时分配值?
例子:
PLACEHOLDER1一些字符串
是否可能在string.xml中的字符串值中有占位符,可以在运行时分配值?
例子:
PLACEHOLDER1一些字符串
当前回答
Kotlin版本的公认答案…
val res = resources
val text = String.format(res.getString(R.string.welcome_messages), username, mailCount)
其他回答
是的!你可以不写任何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>
多语言项目
作为一个曾经致力于大型白标解决方案的人,我可以说有很多需要考虑的地方。 除了文本方向,语法本身也会让你头疼。 例如,项目的顺序可以改变这样
<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代码。
当你想使用实际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文件中。
在你的字符串文件中使用这个
<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)));
但是,你也应该阅读Elias Mårtenson关于Android复数处理“零”的回答。对某些值(如“零”)的解释存在问题。