Neatbo.

Platform translation placeholder checker

Compare Android Java Formatter or Apple NSString placeholder signatures across paired strings, including argument order, types and formatting changes.

Browser-local processingInputString-pair JSONOutputJSON / CSVUp to 1 MiB per file · File limit: 1
  1. 1Add input
  2. 2Adjust settings
  3. 3Get your result

Tool input and files are processed in this browser without being uploaded.

Your input

Inputs are kept temporarily in this tab when switching tools. Refreshing or closing clears them; large results may need to be regenerated.

⌘ / Ctrl + Enter to run

or drag and drop it here

Files stay on this device. Your originals stay unchanged.

.json

Up to 1 MiB per file · File limit: 1

    0 characters · 0 bytes
    Options

    Complete the required options first. You can keep the defaults for the rest.

    Preparing the tool…

    Before you start

    Catch a translated format string that changes an argument or drops a literal percent. Select the actual platform, supply source/target pairs, and inspect changes before handing strings to the runtime.

    How to use this tool

    1. Choose Android Java Formatter or Apple NSString. Paste a JSON array of {key?,source,target}.
    2. Inspect argument, formatting and literal-token differences. Explicit positional placeholders can be reordered while keeping signatures.
    3. Download the review report and validate actual argument values/types in the target application.

    Supported inputs and limits

    One UTF-8 JSON file or pasted array up to 1 MiB, at most 10,000 pairs. Each pair has source, target and optional unique key of at most 256 UTF-16 units. At most 1,000 tokens per string and 100,000 combined tokens. Selected file takes precedence. JSON parsing also has a 200,000-node and depth-128 budget.

    Android grammar supports Java general b/h/s, character c, integral d/o/x, floating e/f/g/a and documented t/T date suffixes, including uppercase variants, explicit n$, independent ordinary indices, < reuse, static width/precision, %% and %n. Conversion-specific flags and invalid duplicate/conflicting flags are checked. An explicit index combined with < is deliberately unsupported. Runtime argument compatibility is not evaluated.

    Apple grammar supports @, d/D, u/U, x/X, o/O, f/F, e/E, g/G, a/A, c/C, s/S, p and %% with declared static flags/width/precision and integer hh/h/l/ll/q/z/t/j or floating L lengths. Mixed positional/ordinary forms, dynamic * width/precision, < reuse, %n, wide length aliases and unsupported flag/conversion combinations reject the whole run.

    Argument/conversion/length occurrence counts are compared separately from flags/width/precision and literal-percent/newline tokens. A format change is a review result, not an automatic rewrite or proof of a runtime error. No XML/strings-file extraction, ICU parsing, translation or runtime execution. Preview shows 200 pairs; JSON includes token positions and all differences.

    Worked example

    Example input

    [
      {
        "key": "reordered",
        "source": "%1$s has %2$d files %%",
        "target": "%2$d 个文件,%1$s %%"
      },
      {
        "key": "type",
        "source": "%1$s: %2$d",
        "target": "%1$s: %2$s"
      },
      {
        "key": "precision",
        "source": "%1$.2f",
        "target": "%1$.3f"
      }
    ]
    Example options
    {"params": {"platform": "android"}}

    Example output

    {
      "platform": "android",
      "scope": "finite platform Formatter/NSString syntax; signature comparison only, no rendering or runtime type guarantee",
      "records": [
        {
          "key": "reordered",
          "source": "%1$s has %2$d files %%",
          "target": "%2$d 个文件,%1$s %%",
          "sourceTokens": [
            {
              "raw": "%1$s",
              "offset": 0,
              "argument": 1,
              "conversion": "s",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            },
            {
              "raw": "%2$d",
              "offset": 9,
              "argument": 2,
              "conversion": "d",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            },
            {
              "raw": "%%",
              "offset": 20,
              "argument": null,
              "conversion": "%",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            }
          ],
          "targetTokens": [
            {
              "raw": "%2$d",
              "offset": 0,
              "argument": 2,
              "conversion": "d",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            },
            {
              "raw": "%1$s",
              "offset": 9,
              "argument": 1,
              "conversion": "s",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            },
            {
              "raw": "%%",
              "offset": 14,
              "argument": null,
              "conversion": "%",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            }
          ],
          "argumentsDifference": [],
          "formatDifference": [],
          "literalDifference": false,
          "status": "match"
        },
        {
          "key": "type",
          "source": "%1$s: %2$d",
          "target": "%1$s: %2$s",
          "sourceTokens": [
            {
              "raw": "%1$s",
              "offset": 0,
              "argument": 1,
              "conversion": "s",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            },
            {
              "raw": "%2$d",
              "offset": 6,
              "argument": 2,
              "conversion": "d",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            }
          ],
          "targetTokens": [
            {
              "raw": "%1$s",
              "offset": 0,
              "argument": 1,
              "conversion": "s",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            },
            {
              "raw": "%2$s",
              "offset": 6,
              "argument": 2,
              "conversion": "s",
              "length": "",
              "flags": "",
              "width": null,
              "precision": null
            }
          ],
          "argumentsDifference": [
            {
              "signature": [
                2,
                "d",
                ""
              ],
              "sourceCount": 1,
              "targetCount": 0
            },
            {
              "signature": [
                2,
                "s",
                ""
              ],
              "sourceCount": 0,
              "targetCount": 1
            }
          ],
          "formatDifference": [
            {
              "signature": [
                2,
                "d",
                "",
                "",
                null,
                null
              ],
              "sourceCount": 1,
              "targetCount": 0
            },
            {
              "signature": [
                2,
                "s",
                "",
                "",
                null,
                null
              ],
              "sourceCount": 0,
              "targetCount": 1
            }
          ],
          "literalDifference": false,
          "status": "review"
        },
        {
          "key": "precision",
          "source": "%1$.2f",
          "target": "%1$.3f",
          "sourceTokens": [
            {
              "raw": "%1$.2f",
              "offset": 0,
              "argument": 1,
              "conversion": "f",
              "length": "",
              "flags": "",
              "width": null,
              "precision": 2
            }
          ],
          "targetTokens": [
            {
              "raw": "%1$.3f",
              "offset": 0,
              "argument": 1,
              "conversion": "f",
              "length": "",
              "flags": "",
              "width": null,
              "precision": 3
            }
          ],
          "argumentsDifference": [],
          "formatDifference": [
            {
              "signature": [
                1,
                "f",
                "",
                "",
                null,
                2
              ],
              "sourceCount": 1,
              "targetCount": 0
            },
            {
              "signature": [
                1,
                "f",
                "",
                "",
                null,
                3
              ],
              "sourceCount": 0,
              "targetCount": 1
            }
          ],
          "literalDifference": false,
          "status": "review"
        }
      ]
    }

    When something does not work

    Select the actual platform and repair the first unsupported/malformed token. Replace dynamic widths with an explicitly supported contract or validate them in the native runtime. Keep Apple placeholders consistently positional or ordinary, and supply unique pair keys.

    Frequently asked questions

    Does a review result mean the translation is definitely wrong?

    No. It identifies signature/format changes that need a deliberate decision; repeated argument use or precision changes can be intentional.

    Can Android < reuse be used in an Apple string?

    No. The grammars are separate. Apple strings should use supported explicit positional placeholders.

    Does it check ICU braces or parse translation resource files?

    No. Supply already decoded printf-style strings. ICU and resource extraction require their own format contracts.

    Documentation & further reading

    Related tools