Localizely logo
  • Home
  • Getting started
  • How it works
  • Pricing
  • FAQ
Sign up

I18n questions · Android

How do I use a percent sign in Android strings.xml? 

It depends on whether the string is a format string. If you pass arguments to it, write the percent sign as %%. If it is never formatted, a plain % is fine, and formatted="false" tells the resource compiler so.

Android only formats a resource when you pass arguments: getString(R.string.discount, 20) and Compose's stringResource(R.string.discount, 20) run the text through String.format(), where % starts a format specifier and %% produces a literal percent sign. getString(R.string.progress_done) without arguments returns the raw text and does not look at % at all.

<!-- Formatted: getString(R.string.discount, 20) gives "Save 20% today" -->
<string name="discount">Save %1$d%% today</string>

<!-- Never formatted: getString(R.string.progress_done) gives "100% done" -->
<string name="progress_done">100% done</string>

The resource compiler checks every translatable string that is not marked formatted="false". When it finds more than one substitution and at least one of them has no position index, it reports multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute?. A % followed by a space or a letter counts as a substitution, so text such as "Save 50% on shoes and 70% on hats" triggers it even though it is not meant to be formatted. The Android Gradle plugin passes --legacy to aapt2, so today this is a warning in the build output; the original aapt failed the build on it. For a string that is not a format string, mark it:

<string name="sale" formatted="false">Save 50% on shoes and 70% on hats</string>

For a real format string, fix the string instead: give every placeholder a position (%1$s, %2$d) and write %% for the literal sign. Positions are required as soon as there is more than one placeholder, and they let translators reorder the arguments in languages with a different word order.

Pitfalls:

  • formatted="false" only silences the compile-time check. If you still call getString(id, args) on that string, String.format() runs and a lone % throws an IllegalFormatException.
  • Strings marked translatable="false" are not checked either, so a broken format string there only fails at runtime.
  • Translators sometimes change %% back to %. Check translated files for it before a release.

Reference: the Format strings section of the Android string resource guide and the java.util.Formatter syntax.

Published Oct 9, 2026 · Markdown version

Related Android localization questions:

Android localization files:

Learn about Android XML string resources
Localizely logo
About
Free tools
Boring stuff
  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Compare
Newsletter
Copyrights 2026 © Localizely