Universal placeholders

Last updated: March 13, 2026Author: Jakub Pomykała

Universal placeholders are currently in beta. We'd love to hear your feedback! Share your suggestions and observations on our feedback board.

Universal placeholders allow you to manage translations with a single, platform-independent placeholder format, so you can import from one platform and export to another without worrying about format specifier differences.

Universal placeholders

The problem

Every platform uses its own placeholder syntax for dynamic values in translations. For example, to insert a user's name and the number of unread messages:

PlatformTranslation
AndroidHello %1$s, you have %2$d new messages
iOS/macOSHello %1$@, you have %2$lld new messages

Both strings mean the same thing, but the placeholders are different. If you manage translations for both platforms in a single project, you would need to maintain two separate translations for the same message, or manually convert placeholders every time you import or export.

The solution

Universal placeholders solve this by introducing a platform-independent format that sits between your source files and the translation editor:

Hello {%1$s}, you have {%2$i} new messages

During import, native placeholders are automatically converted to the universal format. During export, universal placeholders are automatically converted back to the native format of the target platform.

This means you maintain one translation that works for all platforms:

Universal placeholders

Universal format specification

Universal placeholders use curly braces {} around a format specifier with three possible types:

TypeSymbolDescriptionExample
StringsText value{%s}
IntegeriWhole number{%i}
FloatfDecimal number{%f}

Placeholders can also include:

  • Positional index - {%1$s}, {%2$i} - to specify the order of arguments
  • Precision - {%.2f} - to specify decimal places for floats
Universal placeholders (positional)

Supported file formats

Universal placeholders are currently available for two file formats: Android Strings and Localizable XC Strings. When you import files in these formats with the UNIVERSAL_PLACEHOLDERS option, native placeholders are converted to the universal format. When you export to these formats with the same option, universal placeholders are converted back to the native format.

Android Strings

Android nativeUniversal placeholderDescription
%s{%s}String
%d{%i}Integer
%f{%f}Float
%1$s{%1$s}Positional string
%1$d{%1$i}Positional integer
%.2f{%.2f}Float with precision
%1$.2f{%1$.2f}Positional float with precision

Learn more about Android Strings format.

Localizable XC Strings

iOS/macOS nativeUniversal placeholderDescription
%@{%s}String (object)
%lld{%i}Integer (long long)
%d{%i}Integer
%f{%f}Float
%1$@{%1$s}Positional string
%1$lld{%1$i}Positional integer
%.2f{%.2f}Float with precision
%1$.2f{%1$.2f}Positional float with precision

Learn more about Localizable XC Strings format.

Pluralization support

Universal placeholders work with plural forms as well, as they are by default stored in SimpleLocalize as ICU messages, which support the universal placeholder format. When you import a file with plural forms, placeholders inside plural variants are also converted to the universal format.

For example, an Android Strings file:

<plurals name="items_count">
    <item quantity="one">%d item</item>
    <item quantity="other">%d items</item>
</plurals>

is stored in SimpleLocalize as an ICU message with universal placeholders:

{value, plural, one {{%i} item} other {{%i} items}}

When exported to iOS/macOS, the same translation becomes:

{value, plural, one {%lld item} other {%lld items}}

which is then written as XCStrings plural variations with native %lld specifiers.

Plural forms (zero, one, two, few, many, other) are fully preserved during cross-platform conversion.

How to enable

Universal placeholders are enabled by adding the UNIVERSAL_PLACEHOLDERS option to both import and export. You need to enable this option on both sides. During import to convert native placeholders to universal format, and during export to convert them back to the target platform's native format.

SimpleLocalize CLI

Import - converts native placeholders to universal format during upload:

simplelocalize upload \
  --apiKey PROJECT_API_KEY \
  --uploadFormat android-strings \
  --uploadLanguageKey en \
  --uploadPath ./values/strings.xml \
  --uploadOptions UNIVERSAL_PLACEHOLDERS

Export - converts universal placeholders to native format during download:

simplelocalize download \
  --apiKey PROJECT_API_KEY \
  --downloadFormat localizable-xcstrings \
  --downloadPath ./Localizable.xcstrings \
  --downloadOptions UNIVERSAL_PLACEHOLDERS

Learn more about upload and download commands.

Cross-platform workflow example

Here is a typical workflow for managing translations across Android and iOS platforms:

1. Import Android source translations

simplelocalize upload \
  --apiKey PROJECT_API_KEY \
  --uploadFormat android-strings \
  --uploadLanguageKey en \
  --uploadPath ./android/res/values/strings.xml \
  --uploadOptions UNIVERSAL_PLACEHOLDERS

2. Translate - translations are stored with universal placeholders, so translators see {%s} and {%i} instead of platform-specific specifiers.

3. Export to Android and iOS

# Android
simplelocalize download \
  --apiKey PROJECT_API_KEY \
  --downloadFormat android-strings \
  --downloadPath ./android/res/values-{lang}/strings.xml \
  --downloadOptions UNIVERSAL_PLACEHOLDERS

# iOS
simplelocalize download \
  --apiKey PROJECT_API_KEY \
  --downloadFormat localizable-xcstrings \
  --downloadPath ./ios/Localizable.xcstrings \
  --downloadOptions UNIVERSAL_PLACEHOLDERS

Migration from native placeholders

If you already have translations with native placeholders in your project, you can migrate them to the universal format by re-importing the source files with the UNIVERSAL_PLACEHOLDERS option and the "Update translations" option checked. This will convert all native placeholders to the universal format in your existing translations.

You can always go back to native placeholders by importing the same files without the UNIVERSAL_PLACEHOLDERS option.

Resources