XCSKit is a Swift Package for working with Apple's .xcstrings string catalog files through a layered set of modules.
The repository includes a working parser implementation, a representative sample catalog, an automated test suite, and a public software requirements specification that tracks implemented behavior and near-term roadmap items.
Add XCSKit to a Swift Package Manager project with the repository URL:
dependencies: [
.package(url: "https://github.com/chatoutsidis/XCSKit.git", from: "0.3.1")
]Then add whichever product you need to your target dependencies:
.target(
name: "YourTarget",
dependencies: [
.product(name: "XCSParser", package: "XCSKit"),
.product(name: "XCSValidator", package: "XCSKit"),
.product(name: "XCSWriter", package: "XCSKit")
]
)In Xcode, use File > Add Package Dependencies..., enter the repository URL, and select the products your app or tool imports. Use XCSKit when you want the umbrella import, or choose individual products for narrower module boundaries.
| Requirement | Current Support |
|---|---|
| Swift tools | 6.3 |
| Package manager | Swift Package Manager |
| Runtime dependencies | None |
| Verified CI environment | macOS 15 runner with Swift 6.3 |
The package is Foundation-based and intended for local tooling, server-side utilities, and Apple-platform build workflows. Broader platform minimums should be treated as uncommitted until they are added to the package manifest and CI matrix.
- Parse
.xcstringscatalogs fromDataor file URLs. - Model catalog entries, localizations, string units, and variant trees.
- Model entry metadata including
extractionStateusing a forward-compatible typed representation. - Support lookup by source key, locale, and variant selector.
- Surface malformed catalog structure through deterministic typed errors.
- Provide read-only analytics helpers for keys, locales, coverage, and translation progress.
The package currently supports:
- Root catalog fields such as
sourceLanguage,strings, andversion. - Entry metadata including
comment,isCommentAutoGenerated,shouldTranslate, andextractionState. - Direct localized
stringUnitvalues with translation state and value. - Variant trees for dimensions such as
pluralanddevice. - Exact preservation of localized string values and placeholder tokens such as
%@,%lld, and%1$@.
The package still does not include:
- Multi-file catalog merge workflows.
The repository now also includes separate modules for validation, translation orchestration, and writing so these concerns remain isolated from the parser itself.
The package is organized as one repository with multiple library products:
XCSCore: shared domain models such asCatalog,Entry,Localization, andStringUnit.XCSParser: parsing plus fatal structural validation only.XCSValidator: non-mutating semantic validation and finding generation.XCSTranslator: translation orchestration through a provider protocol.XCSWriter: serialization and patch-oriented persistence helpers.XCSAnalytics: read-only catalog reporting and statistics helpers.
This keeps parsing, validation, translation, and writing separate while still sharing the same .xcstrings model and fixtures.
The repository includes a sample catalog at docs/example.xcstrings. It demonstrates:
- Empty entries
- Simple localized strings
- Metadata-bearing entries
- Plural variants
- Device-specific variants
- Placeholder-bearing values
import Foundation
import XCSParser
let data = try Data(contentsOf: catalogURL)
let catalog = try XCSParser.parse(data: data)
let entry = catalog.entry(for: "Add")
let greekValue = catalog
.localization(forKey: "Add", locale: "el")?
.stringUnit?
.value
let extractionState = catalog.entry(for: "spectrum")?.extractionStateThe parser remains intentionally read-only. A higher-level workflow can compose the new modules without coupling them together:
import Foundation
import XCSParser
import XCSValidator
import XCSTranslator
import XCSWriter
let data = try Data(contentsOf: catalogURL)
let catalog = try XCSParser.parse(data: data)
let findings = XCSValidator.validate(catalog)
if findings.isEmpty {
let result = try await XCSTranslator.translate(
catalog,
targetLocales: ["fr"],
provider: MyTranslationProvider()
)
let updatedData = try XCSWriter.apply(patches: result.patches, to: data)
try XCSWriter.write(updatedData, to: outputURL)
}Notes:
XCSValidatorreports findings and never mutates the catalog.XCSTranslatorproduces translation patches and does not write files directly.XCSTranslatorsupports whole-catalog, single-key, batch-key, exact-leaf, and variation-branch translation entry points.XCSWriterserializes full catalogs or applies targeted string patches to existing JSON trees.
The translator defaults to translating every eligible direct leaf and variation leaf in the selected entries. You can also narrow translation to one exact leaf path or to an entire variation branch.
Translate every eligible entry and leaf in the catalog:
let fullCatalogResult = try await XCSTranslator.translate(
catalog,
targetLocales: ["fr", "de"],
provider: MyTranslationProvider()
)Translate every eligible leaf for one entry key:
let singleKeyResult = try await XCSTranslator.translate(
catalog,
entryKey: "Add",
targetLocales: ["fr"],
provider: MyTranslationProvider()
)Translate every eligible leaf for a selected set of keys:
let batchResult = try await XCSTranslator.translate(
catalog,
entryKeys: ["Add", "Save"],
targetLocales: ["fr", "de"],
provider: MyTranslationProvider()
)Translate only one requested variation leaf, such as the plural=one leaf or the device=mac leaf:
let pluralOneOnlyResult = try await XCSTranslator.translate(
catalog,
entryKey: "%lld items",
targetLocales: ["fr"],
leafSelection: .exact([
VariationSelection(dimension: "plural", selector: "one")
]),
provider: MyTranslationProvider()
)
let deviceMacOnlyResult = try await XCSTranslator.translate(
catalog,
entryKeys: ["Open on", "Tap to see"],
targetLocales: ["fr"],
leafSelection: .exact([
VariationSelection(dimension: "device", selector: "mac")
]),
provider: MyTranslationProvider()
)Translate every plural leaf for the selected entries while preserving the original plural selector path in each emitted patch:
let pluralBranchResult = try await XCSTranslator.translate(
catalog,
entryKey: "%lld items",
targetLocales: ["fr"],
leafSelection: .branch(dimension: "plural"),
provider: MyTranslationProvider()
)Translate every device leaf for the selected entries, or narrow device translation to a nested prefix such as plural=one:
let deviceBranchResult = try await XCSTranslator.translate(
catalog,
entryKey: "Tap to see",
targetLocales: ["fr"],
leafSelection: .branch(dimension: "device"),
provider: MyTranslationProvider()
)
let nestedDeviceBranchResult = try await XCSTranslator.translate(
catalog,
entryKey: "Shared Items",
targetLocales: ["fr"],
leafSelection: .branch(
dimension: "device",
prefix: [VariationSelection(dimension: "plural", selector: "one")]
),
provider: MyTranslationProvider()
)Use .exact([]) if you want to translate only an entry's direct stringUnit leaf and skip all variation leaves.
For read-only reporting, use the analytics module on top of the shared catalog model:
import XCSAnalytics
let keys = XCSAnalytics.keys(in: catalog)
let sourceLanguage = XCSAnalytics.sourceLanguage(in: catalog)
let targetLanguages = XCSAnalytics.targetLanguages(in: catalog)
let untranslatedFrenchKeys = XCSAnalytics.untranslatedKeys(in: catalog, locale: "fr")
let helloCoverage = XCSAnalytics.keyCoverage(forKey: "Hello", locale: "ja", in: catalog)
let stats = XCSAnalytics.statistics(in: catalog)The analytics APIs are read-only and reuse the same catalog model as the parser, validator, translator, and writer layers.
Requirements:
- Swift 6.3
- Swift Package Manager
Common commands:
swift build
swift testContinuous integration:
- GitHub Actions runs the Swift CI workflow on pull requests, pushes to
main, and manual dispatches. - The workflow currently uses a macOS runner, installs Swift 6.3, and runs
swift buildplusswift test. - If CI fails, reproduce the same checks locally from the repository root before opening or updating a pull request.
- Software requirements specification: docs/SRS.md
- Sample catalog fixture: docs/example.xcstrings
- Module guide index: docs/modules/README.md
XCSCoreAPI guide: docs/modules/XCSCore.mdXCSParserAPI guide: docs/modules/XCSParser.mdXCSValidatorAPI guide: docs/modules/XCSValidator.mdXCSTranslatorAPI guide: docs/modules/XCSTranslator.mdXCSWriterAPI guide: docs/modules/XCSWriter.mdXCSAnalyticsAPI guide: docs/modules/XCSAnalytics.mdXCSKitumbrella guide: docs/modules/XCSKit.md- Contribution guide: CONTRIBUTING.md
- Security policy: SECURITY.md
- Release notes: CHANGELOG.md
XCSKit is available under the MIT License. See LICENSE for details.
Current repository status:
- Package manifest is present in Package.swift and now exposes parser, core, validator, translator, and writer modules.
- Parser implementation supports core
.xcstringsparsing, typed lookup helpers, and forward-compatible extraction-state metadata. - Validation, translation orchestration, writing, and analytics are implemented as separate layers over the shared catalog model.
- The checked-in tests cover parsing, validation, placeholder preservation, representative variant behavior, translation workflows, writer behavior, analytics, and non-functional requirements.
- The public API is still pre-1.0. Minor releases may refine API names or module boundaries before a stable 1.0 baseline.
If you are contributing implementation work, start with docs/SRS.md. It defines the committed v1 requirements, validation behavior, non-functional constraints, and planned verification coverage.