Structured Strings Catalog Generator
Type-safe localization for modern Swift projects.
A focused Swift Package Manager plugin that generates a structured, strongly‑typed interface for your Strings Catalog (.xcstrings). This is a pragmatic, Apple‑quality solution you can use today when popular tools (like SwiftGen) can’t fully support your current strings catalog workflow. It’s working, production‑friendly — but not yet a complete replacement for every edge case.
Motivation: We love SwiftGen, but for modern Strings Catalogs and certain dynamic bundle layouts, it can’t deliver exactly what we need. This plugin fills that gap by generating a clean, namespaced Swift API from your .xcstrings, ready to use in app and package targets.
Strings Catalogs are Apple’s present and future.
Swift’s type system is merciless.
Manually wiring localization keys is a tax nobody should pay twice.
This Swift Package Manager plugin generates a structured, namespaced, type-safe Swift API directly from your .xcstrings.
✅ No scripts.
✅ No runtime magic.
✅ No guessing how plurals or placeholders behave.
If it builds, it localizes.
- Deterministic, CI-friendly code generation
- Namespaced APIs instead of raw string keys
- Compile-time checked formatting arguments
- Plural rules driven by the catalog itself
- A single translation entry point you control
- Works in app targets and Swift packages
- Compatible with UIKit and SwiftUI
Focused scope. Boring output. Predictable behavior.
Place a configuration file named StringsCatalogPluginConfig.json at the root of your package (same directory as Package.swift). The plugin discovers this file automatically during swift build and in Xcode builds.
{
"input": "Resources/App.xcstrings",
"outputDir": "Generated",
"output": "L10n.swift",
"table": "App",
"name": "L10n",
"access": "internal",
"locale": "en",
"separator": "_"
}- Static strings → computed property
// .xcstrings: "home_title" = "Home"
let title = L10n.Home.title- Formatted strings → typed functions
// .xcstrings: "profile_greeting" = "Hello, %@! You have %d messages."
// %@ → String, %d → Int
let greeting = L10n.Profile.greeting("John", 3)
// .xcstrings: "format_price" = "Price: %f USD"
// %f → Double
let price = L10n.Shop.formatPrice(12.5)- Plurals → functions that take an Int (driven by the catalog)
// .xcstrings:
// "inbox_messages_quantity" has plural variations (one/other)
// "one": "You have %d message"
// "other": "You have %d messages"
let one = L10n.Inbox.quantity(1)
let many = L10n.Inbox.quantity(7)- Dynamic lookup within a scope (if you need it)
// .xcstrings:
// Key: "settings_general_about"
// Dynamically: "settings_general_\(key.camelCased(with: separator))"
if let text = L10n.Settings.General.get("about") {
print(text)
}Fully typed printf-style placeholders, including:
%@ %d %f %u %ld %lld %x/%X %o %c %e/%E %g/%G
If your catalog allows it, the generator respects it.
Add the dependency:
.package(
url: "https://github.com/your-org/strings-catalog-plugin.git",
from: "1.0.0"
)Attach the plugin to the target owning the .xcstrings file:
.target(
name: "AppCore",
plugins: [
.plugin(
name: "StringsCatalogPlugin",
package: "strings-catalog-plugin"
)
]
)Run a build. Generated code appears. No extra steps.
This project is intentionally opinionated:
- Explicit over clever
- Compile-time errors over runtime surprises
- Generated code should be boring
- If Xcode supports it, the plugin should too
It is not a drop-in replacement for legacy .strings pipelines.
It is built for modern projects using Strings Catalogs correctly.
- No YAML
- No hidden build steps
- No runtime reflection
- No global state
- No code generation during app launch
Just SwiftPM doing what SwiftPM was designed to do.
Actively used. Stable surface. Small footprint.
Expect incremental improvements, not churn.
MIT.
Fork it. Ship it. Improve it.
Please don’t turn it into a framework.
