Android strings.xml — escaping, xliff:g and markup rules
Every escaping rule aapt2 enforces on a strings.xml, in one file: backslash-escaped apostrophes and quotes, quoted values that preserve leading whitespace, an escaped @ and ?, CDATA-wrapped inline HTML, <xliff:g> placeholder protection with examples, formatted="false", translatable="false" and a scoped tools:ignore.
<?xml version="1.0" encoding="utf-8"?>
<!-- Novus example app - Android string-resource escaping and markup edge cases (res/values/).
Everything in this file is a rule the aapt2 resource compiler enforces and a hand-written
strings.xml routinely gets wrong. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools">
<!-- An apostrophe MUST be backslash-escaped (or the whole value wrapped in double quotes) or
aapt2 fails the build with "Unescaped apostrophe in string". -->
<string name="cannot_open">Can\'t open that folder</string>
<string name="quoted_alternative">"Can't open that folder either"</string>
<!-- Leading and trailing whitespace is stripped unless the value is quoted. -->
<string name="stripped"> collapses to a single word </string>
<string name="preserved">" keeps three leading spaces"</string>
<!-- A literal double quote is escaped; @ and ? at the start of a value are escaped so they
are not read as a resource or attribute reference. -->
<string name="literal_quote">Wrote \"%1$s\" to disk</string>
<string name="literal_at">\@novus mentions you</string>
<string name="literal_question">\?? is not an attribute reference</string>
<!-- Placeholders that must not be translated or reordered are wrapped in <xliff:g>, which
translation tools treat as opaque. `example` shows the translator what goes there. -->
<string name="move_confirm">Move <xliff:g id="source" example="report.pdf">%1$s</xliff:g> to <xliff:g id="destination" example="Archive">%2$s</xliff:g>?</string>
<!-- Inline markup has to be CDATA-wrapped to survive as HTML rather than being flattened. -->
<string name="terms"><![CDATA[Read the <b>terms</b> and our <a href="https://example.invalid/privacy">privacy notice</a>.]]></string>
<!-- formatted="false" turns off the positional-argument check for a string that uses bare %s
more than once; translatable="false" keeps a value out of the translation export. -->
<string name="legacy_format" formatted="false">%s of %s</string>
<string name="brand" translatable="false">Novus</string>
<!-- Non-breaking space as a numeric entity, and an escaped newline/tab. -->
<string name="price">12 345,60 z\u0142</string>
<string name="two_lines">First line\nSecond line</string>
<string name="tabbed">Name\tSize</string>
<!-- A string-array and an integer-array; note that arrays are NOT plurals and are not
quantity-aware. -->
<string-array name="sort_modes">
<item>Name</item>
<item>Size</item>
<item>Date modified</item>
</string-array>
<!-- tools:ignore documents a deliberate lint exception rather than silencing it globally. -->
<string name="untranslated_debug" tools:ignore="MissingTranslation">Debug build</string>
</resources>Specifications
- Format
- Android string resources
- Locale
- en (default values/)
- Canonical Name
- strings.xml
- Strings
- 12
- Arrays
- 1
- Features
- escaped apostrophe | quoted whitespace | CDATA markup | xliff:g | formatted=false | translatable=false | tools:ignore
- Namespaces
- xliff, tools
- Seed
- 20260807
- Wave
- p7
- Line Endings
- LF
Testing contract
Expected to pass- Scenario
- Compile with aapt2, then read each resource back and compare with the source.
- Expected result
- The file compiles clean; the quoted resource keeps its three leading spaces while the unquoted twin loses them; the CDATA value survives as markup; and the translatable="false" and xliff:g contents are absent from a translation export.
What is a .xml file?
XML (Extensible Markup Language) is a verbose, self-describing markup language using nested tags, attributes, and namespaces to represent structured, hierarchical data. It supports schemas, entities, and validation and underlies many document and data formats. It remains common in enterprise, publishing, and interchange contexts.
How to use this file
Use an example XML file to test parsers, namespace and schema validation, XPath queries, and protection against entity-expansion and external-entity attacks.
How to use this file for testing
“Android strings.xml — escaping, xliff:g and markup rules” is a deterministic Novus Examples fixture for Internationalization, Localization catalogs, Error handling. Parallel text and accented, multi-script content — for testing translation pipelines, Unicode handling, and localization tooling.
Documented properties for this file: seed 20260807 · LF · Android string resources. Compare results against paired or grouped companions on this page when present (clean↔damaged, searchable↔scanned, or format twins) so scores stay reproducible across runs.
Download the file once, keep the path stable in CI or local scripts, and treat the spec table as the contract: dimensions, seeds, field lists, and roles are intentional. Corrupt or invalid samples are labelled as such — expect parsers to fail loudly rather than silently accept them.
Translation-catalog fixtures carry the same message set across formats, each with its native placeholder syntax. Test your i18n loader, catalog converter, or translation-memory tool, and use the RTL and CJK variants to check bidirectional text and Unicode handling.
Load the catalog with your i18n framework and verify placeholder interpolation and plural handling; the RTL and CJK variants exercise bidirectional text and font fallback.
Code examples
import xml.etree.ElementTree as ET
tree = ET.parse("strings-escaping.xml")
root = tree.getroot()
print(root.tag, [c.tag for c in root][:5])Related files
- pogettext PO — fuzzy, obsolete and untranslated entriesA German catalog carrying every state a PO entry can be in but the happy one: two fuzzy entries (one of them a plural) with #| previous-msgid comments, an entry that is present but untranslated, three obsolete #~ entries including an obsolete plural and an obsolete msgctxt entry, and two complete entries as the control.

- jsonICU MessageFormat — Apostrophe and brace quotingThe quoting rules that make ICU messages surprising: a single quote starts a literal section only when the next character is one ICU cares about, a doubled '' is always one apostrophe, and '#' inside a plural branch is a literal hash. Every message is paired with the exact string it must produce.

- xmlAndroid <plurals> — Arabic (6 quantities)Arabic Android quantity strings: 4 <plurals> blocks with an <item> for each of the 6 CLDR quantities ar actually selects. Rename to plurals.xml and drop into res/values-ar/ to use it as-is.

- xmlAndroid <plurals> — English (2 quantities)English Android quantity strings: 4 <plurals> blocks with an <item> for each of the 2 CLDR quantities en actually selects. Rename to plurals.xml and drop into res/values/ to use it as-is.

- xmlAndroid <plurals> — Japanese (1 quantities)Japanese Android quantity strings: 4 <plurals> blocks with an <item> for each of the 1 CLDR quantities ja actually selects. Rename to plurals.xml and drop into res/values-ja/ to use it as-is.

- xmlAndroid <plurals> — Polish (4 quantities)Polish Android quantity strings: 4 <plurals> blocks with an <item> for each of the 4 CLDR quantities pl actually selects. Rename to plurals.xml and drop into res/values-pl/ to use it as-is.

Generated by generation/localization_p7.py. Free for any use, no attribution required — license.