Skip to main content

Apple XCStrings (.xcstrings)

Ilya Krukowski avatar
Written by Ilya Krukowski
Updated today

Xcode String Catalogs or XCStrings (.xcstrings) files are the new localization format introduced by Apple for Xcode projects. They replace traditional .strings files with a more structured, JSON-based format that supports advanced features such as pluralization, variations, and richer metadata.

These files are used in iOS, macOS, watchOS, and tvOS applications to manage localized text and enable flexible, context-aware translations across multiple languages.

Technical information

Overview

Format name:
Apple XCStrings (String Catalog)

File extension:
.xcstrings

Common use cases:
Managing localized text for iOS, macOS, watchOS, and tvOS applications using Xcode’s modern String Catalog format.

Technical details:

  • Structure: XCStrings files are JSON-based and contain localization entries organized into keys, each holding metadata such as translations, comments, and optional plural or variation data. Each file must include:

    • version — specifies the format version.

    • sourceLanguage — defines the source language code.

    • strings — contains all translation entries for each key.

  • Encoding: UTF-8

  • Introduced in: Xcode 15

  • Supports: plurals, device and platform variations, and context-specific translations.

Example:
Here’s an example of an .xcstrings file:

{
"version": "1.0",
"sourceLanguage" : "en",
"strings" : {
"hello_world" : {
"shouldTranslate": true,
"comment" : "Here is a comment for the translator",
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Hello World"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Witaj świecie"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Hallo Welt"
}
}
}
}
}
}

In this example, the hello_world key contains translations for English, Polish, and German.


Using XCStrings files with Lokalise

Lokalise fully supports Apple XCStrings (.xcstrings) files, allowing you to import, manage, and export translations in Apple’s new String Catalog format directly from your project.

Supported project types

XCStrings files can be uploaded to Web and mobile projects only.

Supported elements in XCStrings files

An .xcstrings (String Catalog) file can include several elements. The table below shows which of these are currently supported in Lokalise:

Element

Supported

Translatable content

✅ Yes

Status

✅ Yes

Comments

✅ Yes

Device types

✅ Yes

Uploading XCStrings files to Lokalise

Let’s say you’re uploading a new i18n.xcstrings file to Lokalise with translations for English, German, and Polish, similar to the example shown above.

Open your Web and mobile project (or create a new one if needed), go to the Upload page, and select the file. Lokalise will automatically detect the language as English.

When everything looks correct, click Import files to start the upload.

Once the import is complete, return to the project editor:

You’ll see the translations for the base language (English in this case) along with any comments extracted from the comment field.

If the file includes additional metadata, such as extraction state or translation flags, you can find these values by clicking the key name and navigating to the Custom attributes tab:

View image

Uploading translations for more languages

To add translations for other languages, repeat the upload process. Go back to the Upload page, select the same .xcstrings file again, and change the detected language to German (or any other target language you wish to import).

Continue this process for each target language until all translations have been uploaded.

Device variants

Device variants allow you to provide different translations for specific Apple devices such as iPhone, iPad, Mac, or Apple Watch. In .xcstrings files, these variants are defined within the localizations field under the variations.device structure.

View sample code

{
"version": "1.0",
"sourceLanguage": "en",
"strings": {
"search_label": {
"comment": "Device variant translation example",
"extractionState": "manual",
"localizations": {
"en": {
"variations": {
"device": {
"mac": {
"stringUnit": {
"state": "translated",
"value": "Find"
}
},
"other": {
"stringUnit": {
"state": "translated",
"value": "Find"
}
}
}
}
},
"de": {
"variations": {
"device": {
"mac": {
"stringUnit": {
"state": "translated",
"value": "Suche"
}
},
"other": {
"stringUnit": {
"state": "translated",
"value": "Finde"
}
}
}
}
}
}
}
}
}

Lokalise aims to support all device variants available in Xcode. At the moment, the following types are supported:

  • mac

  • ipad

  • iphone

  • applewatch

  • other

    • If Lokalise encounters an unknown device type, it will automatically map it to other

When importing .xcstrings files, Lokalise creates a separate key for each device variant. The device type is appended to the base key name using a double colon (::) separator.


For example, a key named search_label with multiple device-specific translations will appear in Lokalise as:

search_label::device.mac
search_label::device.other

This approach ensures that each device variation is handled as an independent, translatable key within the project while maintaining compatibility with Xcode’s format.

Pluralized translations

.xcstrings files support pluralization, allowing you to define different translation forms depending on quantity. Plural variations are stored under the variations.plural field, where each form (such as one, other, few, etc.) includes a corresponding translation string.

View sample code

{
"version": "1.0",
"sourceLanguage" : "en",
"strings" : {
"things_label" : {
"comment" : "Pluralized translation",
"localizations" : {
"en" : {
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%lld thing"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%lld things"
}
}
}
}
},
"de" : {
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%lld Ding"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%lld Dinge"
}
}
}
}
}
}
}
}
}

When imported into Lokalise, plural keys are automatically recognized and displayed with a plural badge in the editor, making it easy to view and edit all plural forms in one place.

If the source language is missing certain plural forms that exist in the target language, Lokalise will automatically create those forms in the source and leave them empty, ensuring all plural variants stay aligned across languages.

Substitutions

The .xcstrings format supports string substitutions, which are flexible placeholders used for inserting dynamic content (for example, numbers, names, or variable data) into localized strings. These substitutions define how dynamic values should be formatted and can also include plural variations.

View sample code

{
"version": "1.0",
"sourceLanguage": "en",
"strings": {
"%d of %d left": {
"localizations": {
"en": {
"substitutions": {
"arg1_iphone": {
"argNum": 1,
"formatSpecifier": "d",
"variations": {
"plural": {
"one": {
"stringUnit": {
"state": "needs_review",
"value": "%arg"
}
},
"other": {
"stringUnit": {
"state": "needs_review",
"value": "%arg"
}
}
}
}
},
"left_iphone": {
"formatSpecifier": "d",
"variations": {
"plural": {
"other": {
"stringUnit": {
"state": "needs_review",
"value": "%@ left"
}
}
}
}
}
}
},
"es": {
"substitutions": {
"arg1_iphone": {
"argNum": 1,
"formatSpecifier": "d",
"variations": {
"plural": {
"one": {
"stringUnit": {
"state": "needs_review",
"value": "%arg"
}
},
"other": {
"stringUnit": {
"state": "needs_review",
"value": "%arg"
}
}
}
}
},
"left_iphone": {
"formatSpecifier": "d",
"variations": {
"plural": {
"one": {
"stringUnit": {
"state": "needs_review",
"value": "%@ queda"
}
},
"other": {
"stringUnit": {
"state": "needs_review",
"value": "%@ quedan"
}
}
}
}
}
}
}
}
}
}
}

When importing .xcstrings files, Lokalise handles substitutions by creating separate keys for each substitution element. The substitution name is appended to the base key using the :: separator, ensuring each variant can be translated independently while maintaining proper linkage to the original structure.

For instance, in the example below, the base key %d of %d left generates the following keys in Lokalise:

%d of %d left::substitution.arg1_iphone
%d of %d left::substitution.left_iphone

In this case, Lokalise imports two substitution-specific keys, allowing you to manage and translate them independently. Each key preserves its formatting rules and plural logic, and on export, Lokalise automatically merges them back into the correct .xcstrings structure.


Known issues and limitations

Uploading XCStrings files

Empty keys

Keys with an empty name or no values are ignored during import. For example:

"": {},                   // empty key name — ignored
"key_name": {} // key with no values — ignored

However, if an otherwise empty key includes any field (such as a comment), Lokalise will import it:

"key_name": {
"comment": "Here is a comment for the translator"
} // valid key — will be imported

Target values without a source

If the source language (for example, "en") has no value, but target localizations are present, Lokalise will ignore those target entries and skip the import.

{
"version": "1.0",
"sourceLanguage": "en",
"strings": {
"hello_world": {
"localizations": {
"de": {
"stringUnit": {
"state": "translated",
"value": "Hallo Welt"
}
}
}
}
}
}

In this example, since there is no English ("en") source value, the German translation is not imported.

Key order preservation

Lokalise preserves the original order of keys from the .xcstrings file. By default, the sorting option Last added is applied, but you can switch to First added in the project editor to view the original file order.

Manual file import only

Currently, .xcstrings files can be imported manually into Web and Mobile project types. Support for other upload methods (e.g., CLI, API, integrations) will be added in future updates.

Multi-language file upload

Currently, you can upload one language file at a time. To import all languages, repeat the upload process for each target language included in your String Catalog.

Mixed key types in source and target languages

If a target language contains keys that do not exist in the source language, Lokalise will:

  • Ignore those extra keys during import.

  • Log a warning to notify you about the mismatch.

Keys marked as non-translatable

Keys marked as non-translatable will still be imported.
Lokalise will mark them using custom attributes with the following flag:

{ "translatable": false }

Display limitations

Some metadata fields included in .xcstrings files are not currently visible in the Lokalise editor and can only be found in the custom key attributes.

Specifically, extractionState and state fields are imported and preserved in the file structure but are not displayed in the project editor UI.

Exporting XCStrings files and adding new keys

Formatting on export

Lokalise does not preserve the original .xcstrings file indentation or spacing.
All exported files will follow standardized JSON formatting.

Adding new keys to XCStrings files via Lokalise UI

You can add new keys in Lokalise that will be correctly exported back into the .xcstrings file, as long as they follow the proper naming and metadata conventions.

To ensure the new key is exported correctly, follow these rules:

Using device variants when adding new keys

For device variants, structure the key name as described in the Device variants section: <keyName>::device.<validDeviceKey>.


Example:

new_key::device.mac

Any unrecognized device type will be automatically mapped to other during export.

Using substitution when adding new keys

For substitution keys, structure the name as described in the Substitutions section:
<keyName>::substitution.<substitutionKey>.


Example:

new_key::substitution.left

Substitution keys also require at least one metadata field (formatSpecifier) to be defined in the Custom attributes section. To set it, click on the key name in the Lokalise editor and open the Custom attributes tab.

View example of required custom attributes

{
"content_conversion.metadata": {
"source": {
"xcstrings": {
"formatSpecifier": "d"
}
}
},
"content_conversion.text_codes": {
"source": [],
"targets": []
}
}

Did this answer your question?