File Extension(s) | .xcstrings |
i18n type(s) | XCSTRINGS |
With the introduction of the String Catalogs in Xcode 15, developers can specify device variation and substitution rules using a user-friendly UI. Those rules can be extracted in .xcstrings format and uploaded to Transifex.
Simple device rule
The developer decides to have a label on their app showing the current device type. For that, it creates a new string in the String Catalog, with the key ‘device’ and varies the values by devices like so:
A sample file, that corresponds to the device variations listed above, is the following:
{
"sourceLanguage" : "en",
"strings" : {
"deviceForEveryMac" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"variations" : {
"device" : {
"applevision" : {
"stringUnit" : {
"state" : "translated",
"value" : "This is Apple Vision"
}
},
"applewatch" : {
"stringUnit" : {
"state" : "translated",
"value" : "This is an Apple Watch"
}
},
"iphone" : {
"stringUnit" : {
"state" : "translated",
"value" : "This is an iPhone"
}
},
"mac" : {
"stringUnit" : {
"state" : "translated",
"value" : "This is a Mac"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "This is a device"
}
}
}
}
}
}
}
}
}
Substitutions
Similar to the above example, the developer can create a string in the String Catalog that features two (or more) different tokens, and based on the number that is passed during rendering for each token, it can display a different pluralization rule:
Similarly, a sample file for the substitutions above is the following:
{
"sourceLanguage" : "en",
"strings" : {
"substitutions" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Found %#@arg1@ having %#@arg2@"
},
"substitutions" : {
"arg1" : {
"argNum" : 1,
"formatSpecifier" : "ld",
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg user"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg users"
}
}
}
}
},
"arg2" : {
"argNum" : 2,
"formatSpecifier" : "ld",
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg device"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg devices"
}
}
}
}
}
}
}
}
}
}
}
More complex rules
The developer can construct more complex rules inside the String Catalog: A string can feature substitutions on top of device variations or device variations on plural rules.
In this case, the sample file looks like this:
{
"sourceLanguage" : "en",
"strings" : {
"substitutions_and_device" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"substitutions" : {
"folder_iphone" : {
"argNum" : 2,
"formatSpecifier" : "ld",
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg folder"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg folders"
}
}
}
}
},
"folder_mac" : {
"argNum" : 2,
"formatSpecifier" : "ld",
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg folder"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg folders"
}
}
}
}
},
"user_iphone" : {
"argNum" : 1,
"formatSpecifier" : "ld",
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg user"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg users"
}
}
}
}
},
"user_mac" : {
"argNum" : 1,
"formatSpecifier" : "ld",
"variations" : {
"plural" : {
"one" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg user"
}
},
"other" : {
"stringUnit" : {
"state" : "translated",
"value" : "%arg users"
}
}
}
}
}
},
"variations" : {
"device" : {
"iphone" : {
"stringUnit" : {
"state" : "translated",
"value" : "This iPhone contains %#@user_iphone@ with %#@folder_iphone@ "
}
},
"mac" : {
"stringUnit" : {
"state" : "translated",
"value" : "This Mac contains %#@user_mac@ with %#@folder_mac@ "
}
}
}
}
}
}
}
}
}
📝 Note: For languages with only one plural rule (e.g., Chinese - zh_CN or Japanese - ja), the pluralized entries in the resource or translation file downloaded from Transifex will be returned as non-pluralized.
Online source editing and source update via file upload
You can edit .xcstrings files directly in the Transifex Source Editor. When you upload a new version of this file (whether newer or older), the outcome is the following:
The source string and its translation in Transifex are updated based on the localization content found in the uploaded file, regardless of whether the source string matches the latest version in Transifex.
Mismatches between languages in the file and the project
In Transifex, each project is associated with a specific set of target languages for translation. If there are any discrepancies between the languages included in the uploaded file and those defined for the project, Transifex will attempt to manage this situation as gracefully as possible.
Here are a few possible scenarios to keep in mind:
File Languages Not in Project:
If the file you upload includes languages that are not part of the project, Transifex will ignore those languages. Translations from these columns will not be added to Transifex nor appear in the Editor. To include translations for these languages in Transifex, first add the languages to the project and then re-upload the file. Any content not added to Transifex will remain unchanged in the file when downloaded.
Project Languages Not in File:
If the project includes languages that are not present in the uploaded file, the upload will proceed without issues. After the upload, no translations will be present for these languages in Transifex. You can translate these languages using Transifex's Web Editor. When you download the file, the additional languages will be added to the translation file.
Source strings removal
To remove a source string from Transifex, you need to remove the entire block (key) corresponding to that string from the new version of your source file. The following example shows how Transifex responds based on the content of your source file.
Let's say that you create a new resource in Transifex and upload the following source file:
{
"sourceLanguage" : "en",
"strings" : {
"I find your lack of faith disturbing." : {
"extractionState" : "stale",
"localizations" : {
"el" : {
"stringUnit" : {
"state" : "translated",
"value" : "Βρίσκω ενοχλητική την έλλειψη πίστης σας."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "I find your lack of faith disturbing."
}
}
}
},
"I beg your pardon." : {
"extractionState" : "stale",
"localizations" : {
"el" : {
"stringUnit" : {
"state" : "translated",
"value" : "Σας παρακαλώ."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "I beg your pardon."
}
}
}
}
},
"version" : "1.0"
}
Next, you create a newer version of that file where you specifically remove the value 'I find your lack of faith disturbing.'
from the string you intend to delete.
{
"sourceLanguage" : "en",
"strings" : {
"I find your lack of faith disturbing." : {
"extractionState" : "stale",
"localizations" : {
"el" : {
"stringUnit" : {
"state" : "translated",
"value" : "Βρίσκω ενοχλητική την έλλειψη πίστης σας."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "" // Remove the source value for the string
}
}
}
},
"I beg your pardon." : {
"extractionState" : "stale",
"localizations" : {
"el" : {
"stringUnit" : {
"state" : "translated",
"value" : "Σας παρακαλώ."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "I beg your pardon."
}
}
}
}
},
"version" : "1.0"
}
If you attempt to upload the file to Transifex, the system will generate an error message:
Empty strings in source language are not allowed for entry: 'I find your lack of faith disturbing.'
To successfully remove the desired source string, update your source file by deleting the entire block that includes the string you wish to remove. Make sure that the corresponding key for that string is no longer present in the JSON structure, as illustrated below:
{
"sourceLanguage" : "en",
"strings" : {
"I beg your pardon." : {
"extractionState" : "stale",
"localizations" : {
"el" : {
"stringUnit" : {
"state" : "translated",
"value" : "Σας παρακαλώ."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "I beg your pardon."
}
}
}
}
},
"version" : "1.0"
}
📝 Note:
If you attempt to create a new resource instead of updating an existing one, the system will behave the same, returning the relevant error message.
Source updates to an existing resource occur only when you use the source update option.
If you choose a specific target language for a file upload where the value of a previously uploaded source string is missing but translations are present, the system will parse the translations without issue. The file will be successfully uploaded to Transifex without applying any changes to your source content.
If you remove the block that contains both source and target language(s) but leave the key intact (as shown in the file sample below), the source string itself will not be deleted from Transifex; only its translations will be removed.
{
"sourceLanguage" : "en",
"strings" : {
"I beg your pardon." : {}
},
"version" : "1.0"
}
Uploading and Downloading Options
Uploading Files:
Update source file: When you use Transifex's Update Source File function, all strings in the uploaded file, including source strings and their translations in all languages, will be extracted and updated.
Upload File: When you select a target language to upload a file, all strings from every target language included in the file will be uploaded to Transifex, regardless of the specific language you initially selected.
Add resources: The system will behave the same way as above. When you use the Add resources function, strings from all target languages, including source, will be uploaded to Transifex.
Downloading Files:
When you download a file, it will include all available translations from all languages in your project.
You can select your preferred download mode (as described in the "Download Modes" section) to extract translations based on the status you are interested in across all languages.
Download All Translations (ZIP) – 100% Translated, Reviewed, and Proofread
When a project is not fully translated into its target languages, selecting the option to download ‘All Translations (ZIP) with 100% (translated/reviewed/proofread)’ will generate a single file. In this file, untranslated strings will be returned in the source language, while translations that have already been submitted, reviewed, or proofread will be preserved.
Download Modes
The following table summarizes the behavior of XCSTRINGS files for different download modes: