{"id":289,"date":"2026-06-11T15:16:31","date_gmt":"2026-06-11T15:16:31","guid":{"rendered":"https:\/\/www.forethought-studio.com\/translate\/android\/"},"modified":"2026-06-11T18:13:27","modified_gmt":"2026-06-11T18:13:27","slug":"android","status":"publish","type":"page","link":"https:\/\/www.forethought-studio.com\/translate\/android\/","title":{"rendered":"Android strings.xml translator that runs in your browser"},"content":{"rendered":"<p class=\"dft-page-lede\">Drop an Android strings.xml file in below, pick a target language, download the translated file. The strings.xml file stays on your device.<\/p>\n<div class=\"dft-mount dft-app\" data-format=\"android\"><noscript><p class=\"df-noscript-message\">This translator requires JavaScript in your browser.<\/p><\/noscript><section class=\"dft-step dft-step-file dft-skeleton\" data-state=\"active\" aria-hidden=\"true\"><header class=\"dft-step-header\"><span class=\"dft-step-number\">1<\/span><h2 class=\"dft-step-title\">Add your ANDROID file<\/h2><span class=\"dft-step-hint\">or paste anywhere<\/span><\/header><div class=\"dft-dropzone-wrap\"><div class=\"dft-dropzone\"><div class=\"dft-dropzone-icon\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" width=\"48\" height=\"48\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 3v4a1 1 0 0 0 1 1h4\"\/><path d=\"M17 21H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2z\"\/><path d=\"M12 11v6\"\/><path d=\"M9 14l3-3 3 3\"\/><\/svg><\/div><p class=\"dft-dropzone-primary\">Drop a ANDROID file anywhere, <span class=\"dft-dropzone-click\">click to choose<\/span>, or paste contents<\/p><\/div><p class=\"dft-dropzone-secondary\">ANDROID files only. Files stay on your device.<\/p><\/div><\/section><section class=\"dft-step dft-step-language dft-skeleton\" data-state=\"future\" aria-hidden=\"true\"><header class=\"dft-step-header\"><span class=\"dft-step-number\">2<\/span><h2 class=\"dft-step-title\">Choose target language<\/h2><\/header><div class=\"dft-language-body\"><label class=\"dft-target-label\">Translate to<\/label><select class=\"dft-target-select\" disabled aria-hidden=\"true\"><option>Select language<\/option><\/select><\/div><\/section><section class=\"dft-step dft-step-progress dft-skeleton\" data-state=\"future\" aria-hidden=\"true\"><header class=\"dft-step-header\"><span class=\"dft-step-number\">3<\/span><h2 class=\"dft-step-title\">Translating<\/h2><\/header><\/section><section class=\"dft-step dft-step-review dft-skeleton\" data-state=\"future\" aria-hidden=\"true\"><header class=\"dft-step-header\"><span class=\"dft-step-number\">4<\/span><h2 class=\"dft-step-title\">Review<\/h2><\/header><\/section><div class=\"df-loading-overlay\" role=\"status\" aria-live=\"polite\"><span class=\"df-loading-overlay-spinner\" aria-hidden=\"true\"><\/span><span class=\"df-loading-overlay-label\">Loading translator...<\/span><\/div><\/div>\n<h2>How it works<\/h2>\n<p>Drop a res\/values\/strings.xml file on the page, click to choose one, or paste the contents straight in. The parser reads the file in your browser and lists every translatable entry inside <code>&lt;resources&gt;<\/code>: every <code>&lt;string&gt;<\/code>, every quantity slot of every <code>&lt;plurals&gt;<\/code>, and every <code>&lt;item&gt;<\/code> of every <code>&lt;string-array&gt;<\/code>. Pick a target language and translation starts immediately. The translated file mirrors the original layout: namespace declarations, XML comments, <code>translatable=\"false\"<\/code> entries, resource references, and the order of every entry round trip unchanged.<\/p>\n<h2>Quality Warning<\/h2>\n<p>Please don&#8217;t rely on the quality of these translations to ship a finalized product. This is just a starting point. It&#8217;s machine translation and there are no guarantees about its quality.<\/p>\n<h2>What we do not do<\/h2>\n<p>The file itself never leaves your device. We do not see what you are translating, we do not store any of the strings, and there is nothing about your file in our logs. The privacy wedge in DropFormat is structural: the strings.xml parser, the Google Translate caller, and the file writer all run inside your browser tab.<\/p>\n<p>What does leave your device: the individual strings go to <code>translate.googleapis.com<\/code> the same way they would if you pasted them into translate.google.com yourself. We are not in the middle. If your strings.xml file contains internal product strings you do not want a third party to see, do not translate it here; use an offline tool.<\/p>\n<h2>What it supports<\/h2>\n<p>Android resource files saved under <code>res\/values\/<\/code> and locale variants like <code>res\/values-de\/<\/code>, <code>res\/values-fr\/<\/code>. The parser is hand-written for the strings.xml subset Android Studio and gradle&#8217;s resource tooling produce, so it is tolerant of common real-world variations: BOM-prefixed files, Windows line endings, leading blank lines, namespace declarations, and the <code>tools:locale<\/code> attribute Android Studio adds.<\/p>\n<p><strong>Singular strings.<\/strong> A <code>&lt;string name=\"X\"&gt;value&lt;\/string&gt;<\/code> is one translatable entry. The <code>name<\/code> attribute is the stable identity used by the application code; we never rewrite it. The value text is sent for translation and the result is written back to the same slot.<\/p>\n<p><strong>Plurals with CLDR quantity slots.<\/strong> Every <code>&lt;plurals name=\"X\"&gt;<\/code> becomes one entry with one slot per <code>&lt;item quantity=\"zero|one|two|few|many|other\"&gt;<\/code> child. The quantity attribute on each item round-trips untouched. The Android runtime picks the right slot for the target locale at lookup time, so DropFormat translates the slots the file already declared without scaling to the target language&#8217;s CLDR plural rules. If the target locale needs additional quantity classes, add the new <code>&lt;item&gt;<\/code> elements by hand after download.<\/p>\n<p><strong>String-arrays preserve item order.<\/strong> A <code>&lt;string-array name=\"X\"&gt;<\/code> becomes one entry with one slot per <code>&lt;item&gt;<\/code> child. Translation is done in source order; the index that the application code looks up by stays valid. Each item is translated as an independent string so a planet-name array does not get a single shared phrase reused across slots.<\/p>\n<p><strong>translatable=&#8221;false&#8221; entries round trip untouched.<\/strong> The <code>translatable=\"false\"<\/code> attribute is Android&#8217;s signal that an entry holds an internal identifier (an API URL, a country code, a build constant). DropFormat reads the attribute, skips the entry from the translation queue, and round-trips the original value unchanged. The load banner shows how many entries this affected.<\/p>\n<p><strong>Resource references round trip untouched.<\/strong> Values whose content is wholly a resource reference (<code>@string\/foo<\/code>, <code>@android:string\/cancel<\/code>, <code>@plurals\/items<\/code>, <code>@string-array\/colors<\/code>, <code>@null<\/code>) point at another resource and must not be rewritten; doing so would break the reference graph at runtime. DropFormat detects them and skips them. The load banner shows the count.<\/p>\n<p><strong>Inline markup is preserved by skipping.<\/strong> Strings that contain inline XML elements (<code>&lt;xliff:g&gt;<\/code> placeholder wrappers, <code>&lt;b&gt;<\/code> \/ <code>&lt;i&gt;<\/code> \/ <code>&lt;u&gt;<\/code> formatting) carry markup Google Translate is likely to mangle. v1 round-trips those entries untouched and reports the count in the load banner so a translator can hand-edit them. Hand-translation of those entries is on the roadmap; for now, the safe default is to keep the original markup intact.<\/p>\n<p><strong>XML comments and namespace declarations survive.<\/strong> XML comments between entries, the <code>xmlns:tools<\/code> \/ <code>xmlns:xliff<\/code> declarations on <code>&lt;resources&gt;<\/code>, and Android Studio&#8217;s <code>tools:locale<\/code> attribute round-trip exactly.<\/p>\n<p>Not yet: <code>&lt;xliff:g&gt;<\/code> placeholder preservation across translation (the v1 parser skips entries that contain inline markup so the markup never breaks; future work will protect placeholders during translation), Android escape decoding for review-pane display (<code>\\'<\/code>, <code>\\\"<\/code>, <code>\\n<\/code>, <code>\\@<\/code>, <code>\\u####<\/code>, <code>\"...\"<\/code>-quoted leading whitespace; the underlying text round-trips verbatim in both directions today), and <code>&lt;integer-array&gt;<\/code> translation (its items are integers, not user-facing strings; it round-trips untouched).<\/p>\n<p><!-- TOLERANCES:START --><\/p>\n<h2>What we tolerate, and what we reject<\/h2>\n<p>Some malformed files we repair on the way in. Others we refuse to load so we do not make things worse. This list is the parser&#8217;s actual behaviour, generated from the format module itself, not aspirational copy.<\/p>\n<p><strong>Repaired automatically:<\/strong><\/p>\n<ul class=\"dft-tolerance-list dft-tolerance-repairs\">\n<li data-tolerance-id=\"bom-stripped\">UTF-8 byte order mark (BOM) at the start of the file<\/li>\n<li data-tolerance-id=\"crlf-normalized\">Windows (CRLF) or mixed line endings<\/li>\n<li data-tolerance-id=\"leading-blank-lines\">Leading blank lines before the XML declaration<\/li>\n<li data-tolerance-id=\"multiple-resources-roots\">Multiple &lt;resources&gt; elements (we use the first)<\/li>\n<li data-tolerance-id=\"whitespace-formatting\">Indented or whitespace-heavy XML formatting<\/li>\n<li data-tolerance-id=\"xml-comments\">XML comments anywhere in the document<\/li>\n<\/ul>\n<p><strong>Refused with an explanation:<\/strong><\/p>\n<ul class=\"dft-tolerance-list dft-tolerance-rejects\">\n<li data-tolerance-id=\"ANDROID_EMPTY\"><strong>Empty or whitespace-only file.<\/strong> Add at least one &lt;string name=&quot;&#8230;&quot;&gt; entry under &lt;resources&gt; and re-upload.<\/li>\n<li data-tolerance-id=\"ANDROID_PARSE\"><strong>XML syntax error or unclosed element (e.g. &lt;string&gt; never closed).<\/strong> Fix the XML so it parses, then re-upload.<\/li>\n<li data-tolerance-id=\"ANDROID_NO_ENTRIES\"><strong>No translatable &lt;string&gt; entries (all marked translatable=&quot;false&quot; or empty).<\/strong> Confirm at least one &lt;string&gt; entry is non-empty and not marked translatable=&quot;false&quot;.<\/li>\n<li data-tolerance-id=\"ANDROID_NOT_ANDROID\"><strong>Looks like a different translation format (JSON, PO, .resx, XLIFF, YAML).<\/strong> Use the \/translate\/ hub to pick the right translator.<\/li>\n<\/ul>\n<p><!-- TOLERANCES:END --><\/p>\n<h2>Common questions<\/h2>\n<p><strong>Which file should I drop here?<\/strong> The source-language file (<code>res\/values\/strings.xml<\/code>) if you want to seed a new locale, or an existing localized file (<code>res\/values-de\/strings.xml<\/code>, <code>res\/values-fr\/strings.xml<\/code>) if you want to re-translate it from one language into another. Android strings.xml files are per-locale; one file targets one locale.<\/p>\n<p><strong>What is res\/values\/?<\/strong> Android&#8217;s resource system stores per-configuration string resources under <code>res\/values*\/<\/code>. The default-locale file lives at <code>res\/values\/strings.xml<\/code>; a locale-specific variant lives at <code>res\/values-LANG\/strings.xml<\/code> (e.g. <code>res\/values-de\/strings.xml<\/code>). The application code looks strings up by name (<code>getString(R.string.cancel)<\/code>) and Android resolves the right locale at runtime.<\/p>\n<p><strong>What about CLDR plurals?<\/strong> Android&#8217;s <code>&lt;plurals&gt;<\/code> element wraps one <code>&lt;item quantity=\"...\"&gt;<\/code> per CLDR plural case the developer chose to declare. At runtime, the application calls <code>getQuantityString(R.plurals.items_in_cart, count)<\/code> and Android picks the matching quantity slot for the target locale. DropFormat translates each slot in place; the runtime continues to do the selection.<\/p>\n<p><strong>What about placeholders like %1$s or %d?<\/strong> They pass through to the translator as part of the source text. Google Translate usually leaves them intact, but always spot-check the output before shipping. Placeholders wrapped in <code>&lt;xliff:g&gt;<\/code> currently land in the &#8220;kept original&#8221; bucket; remove the wrapper temporarily if you want machine translation to take a pass.<\/p>\n<p><strong>Does it cache anything?<\/strong> No. Each run starts fresh.<\/p>\n<p><strong>What about PO, XLIFF, TMX, Qt .ts, ARB, .xcstrings, .properties, JSON, .strings, Fluent, .resx, YAML?<\/strong> Each has its own translator at <code>\/translate\/po\/<\/code>, <code>\/translate\/xliff\/<\/code>, <code>\/translate\/xliff2\/<\/code>, <code>\/translate\/tmx\/<\/code>, <code>\/translate\/ts\/<\/code>, <code>\/translate\/arb\/<\/code>, <code>\/translate\/xcstrings\/<\/code>, <code>\/translate\/properties\/<\/code>, <code>\/translate\/json\/<\/code>, <code>\/translate\/strings\/<\/code>, <code>\/translate\/fluent\/<\/code>, <code>\/translate\/resx\/<\/code>, and <code>\/translate\/yaml\/<\/code>. Drop your file at the page that matches it.<\/p>\n<h2>Why this exists<\/h2>\n<p>Most Android projects ship one source-language locale file and copy it manually into a translation tool when seeding a new language. The tools that exist either require a paid subscription, an API key, or pasting strings into a website that holds onto them forever. DropFormat sits in between: free, browser-based, no upload, designed for the &#8220;I just added a feature; give me a first-pass target draft I can paste back into res\/values-LANG\/ and clean up&#8221; workflow.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Drop an Android strings.xml file in below, pick a target language, download the translated file. The strings.xml file stays on your device. How it works Drop a res\/values\/strings.xml file on the page, click to choose one, or paste the contents straight in. The parser reads the file in your browser and lists every translatable entry [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"parent":274,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-289","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/pages\/289","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/types\/page"}],"replies":[{"embeddable":true,"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/comments?post=289"}],"version-history":[{"count":1,"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/pages\/289\/revisions"}],"predecessor-version":[{"id":304,"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/pages\/289\/revisions\/304"}],"up":[{"embeddable":true,"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/pages\/274"}],"wp:attachment":[{"href":"https:\/\/www.forethought-studio.com\/convert\/wp-json\/wp\/v2\/media?parent=289"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}