I know there is support in Android for 18n an application, but can I give parameters to such a string? In Rails, I can do something like this:
en:
hello: "Hello %{name}! You've got %{count} messages."
t("hello", name: "Klaus", count: 5)
Is there something similar in Android or do I have to do it myself?
To elaborate on Heiko's answer, and to show your specific example, if you want to have more than one string you need to number them:
<string name="hello">Hello %1$s! You've got %2$d messages.</string>
This way you can switch the order of the strings in each translation. Using it would be:
String hello = getString(R.strings.hello, "Klaus", 5);
You can do the same
In strings.xml you can put
<string name="xyz">Do you really want to report [%s] as spammer?</string>
and then in your code you put
String foo = getString(R.strings.xyz,"Joe Doe");
See Context.getString()
Phrase seems to do exactly what you're looking for. Here's a article about it's implementation. Below sample from the article.
<string name="greeting"> Hello {name}, today\'s cook yielded {yield} {unit}.</string>
and then...
// Call put(...) in any order
CharSequence greeting = Phrase.from(context, R.string.greeting)
.put("unit", unit)
.put("name", name)
.put("yield", yield)
.format();
Related
Is it possible to reference a string in strings.xml
Eg:
<string name="application_name">#string/first_name Browser</string>
<string name="first_name">Chrome</string>
Where depending on requirements, i can switch the value of first_name to "Chrome", "Firefox" or "Opera".
You can give the reference of string resource, but limitation are as follows
<string name="first_name">Chrome</string>
<string name="application_name">#string/first_name</string> // gives "Chrome"
<string name="application_name">Chrome #string/first_name</string> // gives "Chrome #string/first_name"
<string name="application_name">#string/first_name Chrome </string> // gives error
If content starts with "#" then Android considers this is a referenced string, see last case which gives an error because Android's tools take # and the next string to it as the string's reference name, it will try to find a resource called "#string/first_name Chrome" which doesn't exist.
You can use String Format to dynamically assign sub-strings like <string name="application_name">%1$s browser</string>
to use
String text = String.format(res.getString(R.string.application_name), "Chrome");
Yes, you can do so without having to add any Java/Kotlin code, using this small library that allows you to do so using XML only at buildtime. So for your case, you'd have to set up your strings like this:
<string name="application_name">${first_name} Browser</string>
<string name="first_name">Chrome</string>
And then after running the gradle plugin, you'll get this:
<!-- Auto generated during compilation -->
<string name="application_name">Chrome Browser</string>
This is the link to the library: https://github.com/LikeTheSalad/android-stem
Disclaimer: I'm the author of this library.
The Strings in the strings.xml are fixed and cannot be changed at run time. You will have to define a string for each case, and do the switch in the code.
String name;
if (/* browser is Chrome*/) {
name = getString(R.string.first_name_chrome);
} else if (/* browser is Firefox */) {
name = getString(R.string.first_name_firefox);
}
You can however make the application select the correct string for different languages automatically. This can be done by placing string files in localized folders (values-en, values-fr, values-pl etc).
You can read more about localization at http://www.icanlocalize.com/site/tutorials/android-application-localization-tutorial/
I could not find an answer to this question anywhere so I have posted it.
Let's say I have multiple resource strings, like:
<string name="hello">Hello World, MainScreen!</string>
<string name="app_name">My Title</string>
And I reference them in my code like so:
R.values.hello
R.values.app_name
But how can I access them like this?
String test = "hello";
String value = R.values.test;
I am trying to do something like this, but on a much larger scale. Or is there a different, but better way of doing it?
In an Activity, this will work...
String value = getString(R.values.hello);
R.values.hello is an integer used as a 'lookup' for the actual string itself. All resources are handled this way.
The correct way to obtain those values would be
String name =
CurrentClassName.this.getString(R.string.hello);
By using CurrentClassName.this you will assure that you can use it inside functions and nested classes.
I think this link might help you http://steven.bitsetters.com/2007/11/27/accessing-android-resources-by-name-at-runtime/
int id = getResources().getIdentifier("hello", "values", getPackageName());
Is it possible to have placeholders in string values in string.xml that can be assigned values at run time?
Example:
some string PLACEHOLDER1 some more string
Formatting and Styling
Yes, see the following from String Resources: Formatting and Styling
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);
Basic Usage
Note that getString has an overload that uses the string as a format string:
String text = res.getString(R.string.welcome_messages, username, mailCount);
Plurals
If you need to handle plurals, use this:
<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>
The first mailCount param is used to decide which format to use (single or plural), the other params are your substitutions:
Resources res = getResources();
String text = res.getQuantityString(R.plurals.welcome_messages, mailCount, username, mailCount);
See String Resources: Plurals for more details.
Supplemental Answer
When I first saw %1$s and %2$d in the accepted answer, it made no sense. Here is a little more explanation.
They are called format specifiers. In the xml string they are in the form of
%[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
Example
We will create the following formatted string where the gray parts are inserted programmatically.
My sister Mary is 12 years old.
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);
Notes
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
When you want to use a parameter from the actual strings.xml file without using any Java code:
<?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>
This does not work across resource files, i.e. variables must be copied into each XML file that needs them.
Was looking for the same and finally found the following very simple solution. Best: it works out of the box.
1. alter your string ressource:
<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. use string substitution:
c.getString(R.string.welcome_messages,name,count);
where c is the Context, name is a string variable and count your int variable
You'll need to include
<resources xmlns:xliff="http://schemas.android.com/apk/res-auto">
in your res/strings.xml.
Works for me. :)
If you want to write percent (%), duplicate it:
<string name="percent">%1$d%%</string>
label.text = getString(R.string.percent, 75) // Output: 75%.
If you write simply %1$d%, you will get the error: Format string 'percent' is not a valid format string so it should not be passed to String.format.
Or use formatted=false" instead.
In Kotlin you just need to set your string value like this:
<string name="song_number_and_title">"%1$d ~ %2$s"</string>
Create a text view on your layout:
<TextView android:text="#string/song_number_and_title"/>
Then do this in your code if you using 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))
You might need to get your resources from the application context.
In your string file use this
<string name="redeem_point"> You currently have %s points(%s points = 1 %s)</string>
And in your code use as accordingly
coinsTextTV.setText(String.format(getContext().getString(R.string.redeem_point), rewardPoints.getReward_points()
, rewardPoints.getConversion_rate(), getString(R.string.rs)));
However, you should also read Elias Mårtenson's answer on Android plurals treatment of “zero”. There is a problem with the interpretation of certain values such as "zero".
For Multilingual Projects
As someone who has worked on a major white label solution with many and varying languages and configurations per variant, I can say that there's a lot to consider.
Text direction aside, grammar alone can give you some headaches.
For instance can the order of items change thus
<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>
is to be preferred over
<string name="welcome_messages">Hello, %s! You have %d new messages.</string>
but once you work with translators who often don't know what a string or an integer is, let alone what formatting character to use for each type, or people in general who have no idea in what order the parameters are applied in your code, or even you yourself forget that, or things change which then must be updated at multiple places at once, so using MessageFormat like
<string name="welcome_message">Hello, {0}! You have {1} new messages.</string>
MessageFormat(R.string.welcome_message).format("Name", numMessages)
isn't viable either and the idea of having non tech people try to figure out xlift can't even be entertained then the best solution I know so far is to use explicit, named placeholders as such
<string name="placeholder_data" translatable="false">DATA</string>
<string name="placeholder_data" translatable="false">$DATA</string>
<string name="placeholder_data" translatable="false">%DATA%</string>
..or whatever else doesn't conflict with your texts.
And while you could use DOCTYPE like
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
<!ENTITY placeholder_data "$DATA">
]>
<string name="text_with_data">Your data is &placeholder_data;.</string>
this will not work with separate files for each language.
Thus in your main/res/values/strings.xml provide the placeholders and default strings like this
<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>
and then in your variant 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>
and to use it, write something like
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)
}
with the use of some helper functions like
fun CharSequence.resolveData(data: JsonObject) =
toString().replace(getString(placeholder_data), data.toString())
fun CharSequence.resolveError(error: Throwable) =
toString().replace(getString(placeholder_error), error.toString())
for simply the reason of having a reference for the translation files and development. Hence there should not be a default file per build flavor. Only the single default file and then a file per language x variant.
Now there's also the issue of numeric grammar. This can be solved with plurals but here again the complexity of the xml file increases. And, as pointed out, zero does not work as one would expect. But also you may want to have a limit to which your app counts due to display size limitations or number of pre-rendered images for your UI and need to display 99+ instead of 100. A solution would be to use a helper function like
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)
}
to override and extend the behavior of the plurals resolver.
And if you have optional features per variant then add a res/values/strings-beans.xml like:
<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>
while the variant in variant-with-beans/res/value-en/strings-beans.xml only needs to contain
<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>
and language specific overrides can be provided on a per file basis.
To use this, the code can look like this
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)
which resolves to the outputs
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!"
and due to the resulting simplicity of the language specific resource files, they then can be generated with deployment tools from spread sheets or your company's own server endpoints, etc.
I hope you enjoyed my maddening ride into the world of dynamic string resources for Android and hopefully appreciate that you're not the poor fools having to get the same functionality to work on the iOS side of the product which from my experience required python scripts to modify .xcodeproj files and generate swift code.
in res/values/string.xml
<resources>
<string name="app_name">Hello World</string>
<string name="my_application">Application name: %s, package name: %s</string>
</resources>
in java code
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);
You can use MessageFormat:
<string name="customer_address">Wellcome: {0} {1}</string>
In Java code :
String text = MessageFormat(R.string.customer_address).format("Name","Family");
API level 1:
https://developer.android.com/reference/java/text/MessageFormat.html
Yes! you can do so without writing any Java/Kotlin code, only XML by using this small library I created, which does so at buildtime, so your app won't be affected by it: https://github.com/LikeTheSalad/android-stem
Usage
Your strings:
<resources>
<string name="app_name">My App Name</string>
<string name="welcome_message">Welcome to ${app_name}</string>
</resources>
The generated string after building:
<!-- Auto generated during compilation -->
<resources>
<string name="welcome_message">Welcome to My App Name</string>
</resources>
A Direct Kotlin Solution to the problem:
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 )
Kotlin version of the accepted answer...
val res = resources
val text = String.format(res.getString(R.string.welcome_messages), username, mailCount)
This question already has answers here:
Is it possible to have placeholders in strings.xml for runtime values?
(14 answers)
Closed 5 years ago.
In my Android app I'am going to implement my strings with internationalization. I have a problem with the grammar and the way sentences build in different languages.
For example:
"5 minutes ago" - English
"vor 5 Minuten" - German
Can I do something like the following in strings.xml?
<string name="timeFormat">{0} minutes ago</string>
And then some magic like
getString(R.id.timeFormat, dynamicTimeValue)
This behaviour would solve the other problem with different word orders as well.
Yes, just format your strings in the standard String.format() way.
See the method Context.getString(int, Object...) and the Android or Java Formatter documentation.
In your case, the string definition would be:
<string name="timeFormat">%1$d minutes ago</string>
If you need two variables in the XML, you can use:
%1$d text... %2$d or %1$s text... %2$s for string variables.
Example :
strings.xml
<string name="notyet">Website %1$s isn\'t yet available, I\'m working on it, please wait %2$s more days</string>
activity.java
String site = "site.tld";
String days = "11";
//Toast example
String notyet = getString(R.string.notyet, site, days);
Toast.makeText(getApplicationContext(), notyet, Toast.LENGTH_LONG).show();
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);
If you wish more look at: http://developer.android.com/intl/pt-br/guide/topics/resources/string-resource.html#FormattingAndStyling
There is many ways to use it and i recomend you to see this documentation about String Format.
http://developer.android.com/intl/pt-br/reference/java/util/Formatter.html
But, if you need only one variable, you'll need to use %[type] where [type] could be any Flag (see Flag types inside site above). (i.e. "My name is %s" or to set my name UPPERCASE, use this "My name is %S")
<string name="welcome_messages">Hello, %1$S! You have %2$d new message(s) and your quote is %3$.2f%%.</string>
Hello, ANDROID! You have 1 new message(s) and your quote is 80,50%.
Note that for this particular application there's a standard library function, android.text.format.DateUtils.getRelativeTimeSpanString().
I have to concatenate these two strings from my resource/value files:
<string name="Toast_Memory_GameWon_part1">you found ALL PAIRS ! on </string>
<string name="Toast_Memory_GameWon_part2"> flips !</string>
I do it this way :
String message_all_pairs_found = getString(R.string.Toast_Memory_GameWon_part1)+total_flips+getString(R.string.Toast_Memory_GameWon_part2);
Toast.makeText(this, message_all_pairs_found, 1000).show();
But the spaces at the end of the first string and at the beginning of the second string
have disappeared (when the Toast is shown) ...
What should I do ?
I guess the answer is somewhere here in this documentation link
or is it something like using & ; for the "&" character ??
Even if you use string formatting, sometimes you still need white spaces at the beginning or the end of your string. For these cases, neither escaping with \, nor xml:space attribute helps. You must use HTML entity for a whitespace.
Use for non-breakable whitespace.
Use for regular space.
I ran into the same issue. I wanted to leave a blank at the end of a resource string representing an on-screen field name.
I found a solution on this issue report : https://github.com/iBotPeaches/Apktool/issues/124
This is the same idea that Duessi suggests. Insert \u0020 directly in the XML for a blank you would like to preserve.
Example :
<string name="your_id">Score :\u0020</string>
The replacement is done at build time, therefore it will not affect the performance of your game.
This documentation suggests quoting will work:
<string name="my_str_spaces">" Before and after? "</string>
I just use the UTF code for space "\u0020" in the strings.xml file.
<string name="some_string">\u0020The name of my string.\u0020\u0020</string>
works great. (Android loves UTF codes)
This question may be old, but as of now the easiest way to do it is to add quotation marks.
For example:
<string name="Toast_Memory_GameWon_part1">"you found ALL PAIRS ! on "</string>
<string name="Toast_Memory_GameWon_part2">" flips !"</string>
There is possible to space with different widths:
<string name="space_demo">| | | ||</string>
| SPACE | THIN SPACE | HAIR SPACE | no space |
Visualisation:
use "" with the string resource value.
Example :
<string>"value with spaces"</string>
OR
use \u0020 code for spaces.
If you really want to do it the way you were doing then I think you have to tell it that the whitespace is relevant by escaping it:
<string name="Toast_Memory_GameWon_part1">you found ALL PAIRS ! on\ </string>
<string name="Toast_Memory_GameWon_part2">\ flips !</string>
However, I'd use string formatting for this. Something like the following:
<string name="Toast_Memory_GameWon">you found ALL PAIRS ! on %d flips !</string>
then
String message_all_pairs_found = String.format(getString(R.string.Toast_Memory_GameWon), total_flips);
Working well
I'm using \u0020
<string name="hi"> Hi \u0020 </string>
<string name="ten"> \u0020 out of 10 </string>
<string name="youHaveScored">\u0020 you have Scored \u0020</string>
Java file
String finalScore = getString(R.string.hi) +name+ getString(R.string.youHaveScored)+score+ getString(R.string.ten);
Toast.makeText(getApplicationContext(),finalScore,Toast.LENGTH_LONG).show();
Screenshot
here Image of Showing Working of this code
All answers here did not work for me. Instead, to add a space at the end of a string in XML i did this
<string name="more_store">more store<b> </b> </string>
An argument can be made for adding the space programmatically. Since these cases will be often used in concatenations, I decided to stop the madness and just do the old + " " +. These will make sense in most European languages, I would gather.
I've no idea about Android in particular, but this looks like the usual XML whitespace handling - leading and trailing whitespace within an element is generally considered insignificant and removed. Try xml:space:
<string name="Toast_Memory_GameWon_part1" xml:space="preserve">you found ALL PAIRS ! on </string>
<string name="Toast_Memory_GameWon_part2" xml:space="preserve"> flips !</string>
This may not actually answer the question (How to keep whitespaces in XML) but it may solve the underlying problem more gracefully.
Instead of relying only on the XML resources, concatenate using format strings.
So first remove the whitespaces
<string name="Toast_Memory_GameWon_part1">you found ALL PAIRS ! on</string>
<string name="Toast_Memory_GameWon_part2">flips !</string>
And then build your string differently:
String message_all_pairs_found =
String.format(Locale.getDefault(),
"%s %d %s",
getString(R.string.Toast_Memory_GameWon_part1),
total_flips,
getString(R.string.Toast_Memory_GameWon_part2);
Toast.makeText(this, message_all_pairs_found, 1000).show();
There is also the solution of using CDATA. Example:
<string name="test"><![CDATA[Hello world]]></string>
But in general I think \u0020 is good enough.
If you need the space for the purpose of later concatenating it with other strings, then you can use the string formatting approach of adding arguments to your string definition:
<string name="error_">Error: %s</string>
Then for format the string (eg if you have an error returned by the server, otherwise use getString(R.string.string_resource_example)):
String message = context.getString(R.string.error_, "Server error message here")
Which results in:
Error: Server error message here
It does not work with xml:space="preserve"
so I did it the quickest way =>
I simply added a +" "+ where I needed it ...
String message_all_pairs_found = getString(R.string.Toast_Memory_GameWon_part1)+" "+total_flips+" "+getString(R.string.Toast_Memory_GameWon_part2);