By the strings.dev team — maintainers of native .xcloc and strings.xml localization tooling for iOS and Android.
Exporting from Xcode: two paths
There are two supported ways to produce an .xcloc, and they generate identical bundles.
Xcode GUI
Select your project in the navigator, then choose Product > Export Localizations… (on some Xcode versions the same command lives under Editor > Export Localizations… when the project editor is focused). Xcode presents a sheet where you tick the languages you've already added under Project > Info > Localizations. It writes one .xcloc per selected language — named by locale code — into the folder you pick.
Only languages already configured in your project are offered — export doesn't create locales, it packages the ones you've declared. If a target locale is missing, add it first.
Command line
For CI or scripting, xcodebuild does the same thing headlessly:
xcodebuild -exportLocalizations \
-project MyApp.xcodeproj \
-localizationPath ./Localizations \
-exportLanguage fr \
-exportLanguage de \
-exportLanguage ja
This writes fr.xcloc, de.xcloc, and ja.xcloc into ./Localizations — each catalog is named after its locale. Use -workspace MyApp.xcworkspace instead of -project if you build through a workspace. Omit -exportLanguage and Xcode exports every configured localization at once.
Anatomy of an .xcloc bundle
An .xcloc is a directory bundle, not a single file — Finder shows it as one icon, but it's a folder tree:
fr.xcloc/
├── contents.json
├── Localized Contents/
│ └── fr.xliff
├── Source Contents/
│ ├── MyApp/
│ │ ├── Localizable.xcstrings
│ │ └── InfoPlist.xcstrings
│ └── ...
└── Notes/
Each part has a job:
| Path |
What it holds |
contents.json |
Bundle metadata: development region, targetLocale, tool info, format version |
Localized Contents/<locale>.xliff |
The editable file — XLIFF 1.2, one <trans-unit> per string, target-language set to the locale |
Source Contents/ |
Read-only copies of your source resources, including Localizable.xcstrings |
Notes/ |
Optional screenshots and context attachments for translators |
The Localized Contents XLIFF is the only file a translation step should modify. contents.json looks like this:
{
"developmentRegion" : "en",
"targetLocale" : "fr",
"toolInfo" : {
"toolID" : "com.apple.dt.xcode",
"toolName" : "Xcode",
"toolVersion" : "16.0"
},
"version" : "1.0"
}
Inside the XLIFF, each string is a <trans-unit>. Xcode fills <source> from your development language and leaves <target> empty (or copied) for the translator:
<trans-unit id="welcome_title">
<source>Welcome back, %@</source>
<target>Bon retour, %@</target>
<note>Greeting shown on the home screen after sign-in</note>
</trans-unit>
Note the %@ placeholder living inside both source and target — it must survive translation byte-for-byte, and the <note> is the per-string context Xcode carries through from your String Catalog comments.
Opening and inspecting an .xcloc
Because it's a bundle, you inspect it like a folder:
- Finder: right-click the
.xcloc and choose Show Package Contents to browse the tree.
- Terminal:
cd straight into it — cd fr.xcloc — then find . -type f to list every file.
- Read the metadata:
plutil -p contents.json pretty-prints the JSON and confirms the targetLocale.
- The XLIFF is plain XML — open
Localized Contents/fr.xliff in any editor to see the untranslated <target> elements waiting to be filled.
To re-open a catalog inside Xcode itself, use Product > Import Localizations… and point at the .xcloc; Xcode shows a diff preview before applying anything.
Round-tripping: importing back
Once the XLIFF's <target> elements are filled with translations, you import the catalog to merge them into your project. Xcode routes each translated string back to the right Localizable.xcstrings entry by its id.
xcodebuild -importLocalizations \
-project MyApp.xcodeproj \
-localizationPath ./Localizations/fr.xcloc
Run one import per locale. The GUI equivalent is Product > Import Localizations…, which surfaces a review sheet listing added, changed, and problematic strings so you can catch a dropped placeholder before it lands. The id continuity is what makes the round-trip lossless: same format in, same format out, no XLIFF-to-CSV conversion to corrupt your %1$@ arguments or plural variations.
Where strings.dev fits: the translate step
Export and import are mechanical. The hard, error-prone middle — actually translating every <target> correctly — is what strings.dev automates. You upload the exact .xcloc Xcode produced and download a completed .xcloc back:
en.xcloc -> strings.dev -> fr.xcloc
Nothing about the format changes — you feed the same bundle back into xcodebuild -importLocalizations. During translation:
- Placeholders are protected.
%@, %lld, and positional %1$@ forms are preserved so reordering for French or Japanese word order never breaks an argument. String Catalog "Vary by Plural" variations are handled natively per locale.
- URLs, emails, @handles, and proper nouns pass through untouched, and you can mark brand phrases as protected.
- Context feeds the translation. Your app description and the per-string
<note> comments from the catalog shape the output, so it reflects what the string actually does.
For catching a string that silently failed or a plural category left empty, the QA & analytics dashboard flags failed translations for retry — that's part of Indie Plus ($20/mo, or $10/mo billed annually), which also unlocks unlimited languages and 2 projects. The free Indie tier covers 1 project and 1 language with unlimited word translations, which is enough to round-trip a single locale end to end.
Full CLI and API setup for wiring the export/translate/import loop into CI lives in the strings.dev docs, and locale-code details are in the iOS & Android locale reference. More platform walkthroughs are in the guides hub.