Universal Placeholders
Universal placeholders allow you to work with one set of translations for all platforms, automatically converting them to the appropriate format during export.
Why do you need universal placeholders?
The problem
Imagine you have an application for three platforms with the same message:
Web (JSON):
{"welcome": "Hello, {{username}}! You have {{count}} new messages."}
iOS (.strings):
"welcome" = "Hello, %@! You have %li new messages.";
Android (XML):
<string name="welcome">Hello, %s! You have %d new messages.</string>
Without universal placeholders, you need to translate the same text three times, spend triple the budget, and manually ensure consistency.
The solution2
With universal placeholders:
-
Translate once for all platforms
-
Automatic conversion during export
-
Consistent translations everywhere
-
Save time and money
Universal format
Localit.io uses a unified format with square brackets:
Basic types
Type | Format | Example |
---|---|---|
String |
|
|
Integer |
|
|
Float |
|
|
Named parameters
Add :name
after the type for convenience:
-
[%s:username]
— string with name -
[%d:count]
— number with name -
[%.2f:price]
— float with name and precision
Export conversion
Source text in Localit.io:
"Hello, [%s:username]! You have [%d:count] new messages."
Result for different platforms:
Platform | Result |
---|---|
Web (i18n) |
|
iOS |
|
Android |
|
Web (ICU) |
|
Web (.NET) |
|
Web (Symfony) |
|
Import with auto-conversion
When uploading files, enable the "Convert to universal placeholders" option.
Before upload (iOS):
"welcome" = "Hello, %@! You have %li messages.";
After upload:
"welcome" = "Hello, [%s]! You have [%d] messages.";
Practical example
Let's look at a real scenario: you have a mobile shopping app with a web version. You need to localize order notifications.
Source files
Web application (messages.json):
{"order_status": "Your order {{orderId}} for {{amount}} is ready for pickup!","delivery_time": "Delivery will take {{hours}} hours."}
iOS application (Localizable.strings):
"order_status" = "Your order %@ for %.2f is ready for pickup!";
"delivery_time" = "Delivery will take %li hours.";
Android application (strings.xml):
<?xml version="1.0" encoding="UTF-8"?><resources>
<string name="order_status">Your order %s for %.2f is ready for pickup!</string>
<string name="delivery_time">Delivery will take %d hours.</string></resources>
Upload to Localit.io
-
Create a project and select "English" as source language
-
Upload first file (messages.json):
-
Choose "JSON" format
-
Enable "Convert to universal placeholders"
-
Click "Upload"
-
-
Upload second file (Localizable.strings):
-
Choose "iOS Strings" format
-
Enable "Convert to universal placeholders"
-
Click "Upload"
-
-
Upload third file (strings.xml):
-
Choose "Android XML" format
-
Enable "Convert to universal placeholders"
-
Click "Upload"
-
Result in editor
After upload, Localit.io will automatically merge keys and attempt to convert placeholders:
Initial result:
order_status = "Your order [%1$s:orderId] for [%1$s:amount] is ready for pickup!"
delivery_time = "Delivery will take [%1$s:hours] hours."
Problem: Placeholder types for different platforms are incompatible. JSON uses named string placeholders, while iOS and Android use typed ones with positional numbers. The system automatically detected all as string [%1$s]
.
Manual placeholder editing
You need to fix data types manually:
-
orderId remains string:
[%1$s:orderId]
→[%s:orderId]
-
amount should be float:
[%1$s:amount]
→[%.2f:amount]
-
hours should be integer:
[%1$s:hours]
→[%d:hours]
Final result:
order_status = "Your order [%s:orderId] for [%.2f:amount] is ready for pickup!"
delivery_time = "Delivery will take [%d:hours] hours."
Now you have properly typed universal placeholders that export correctly for all platforms.
Translate to French
Translate keys once:
order_status = "Votre commande [%s:orderId] pour [%.2f:amount] est prête à récupérer!"
delivery_time = "La livraison prendra [%d:hours] heures."
Export for each platform
Export for web (JSON with i18n placeholders):
-
Go to "Export" section
-
Select "French" language
-
Choose "JSON" format
-
In settings select "i18n placeholders"
-
Click "Download"
Result (messages_fr.json):
{"order_status": "Votre commande {{orderId}} pour {{amount}} est prête à récupérer!","delivery_time": "La livraison prendra {{hours}} heures."}
Export for iOS (.strings with iOS placeholders):
-
Choose "iOS Strings" format
-
In settings select "iOS placeholders"
-
Click "Download"
Result (fr.lproj/Localizable.strings):
"order_status" = "Votre commande %@ pour %.2f est prête à récupérer!";
"delivery_time" = "La livraison prendra %li heures.";
Export for Android (XML with printf placeholders):
-
Choose "Android XML" format
-
In settings select "Printf placeholders"
-
Click "Download"
Result (values-fr/strings.xml):
<?xml version="1.0" encoding="UTF-8"?><resources>
<string name="order_status">Votre commande %s pour %.2f est prête à récupérer!</string>
<string name="delivery_time">La livraison prendra %d heures.</string></resources>
Savings
Without universal placeholders:
-
6 keys to translate (2 for each platform)
-
Risk of translation inconsistencies
-
Need to synchronize changes
With universal placeholders:
-
2 keys to translate
-
Automatic consistency
-
One place for making changes
Result: 67% time savings on translations and 100% consistency guarantee.
Export settings
In the "Placeholder format" section, choose:
-
Printf —
%s
,%d
,%f
-
iOS —
%@
,%li
,%f
-
ICU —
{name}
,{0}
-
.NET —
{0}
,{1:0.00}
-
Symfony —
%name%
,%placeholder_1%
-
i18n —
{{name}}
,{{0}}
-
Raw — no changes
Recommendations
Use named parameters
✅ "Price: [%.2f:price]$"
❌ "Price: [%.2f]$"
Plan types in advance
-
String for names and text
-
Integer for counters
-
Float for prices and percentages
Test the result
Check export on all target platforms.
Troubleshooting
Placeholders don't convert during import
Enable "Convert to universal placeholders" option during upload.
Wrong format after export
Check the selected placeholder format in export settings.
Loss of named parameters
Make sure the target format supports named parameters.