strings-catalog-plugin

main

Structured Strings Catalog Generator
xtro/strings-catalog-plugin

Structured Strings Catalog Generator
Type-safe localization for modern Swift projects.

Why

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.


What You Get

  • 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.


Configuration

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.

Minimal example

{
    "input": "Resources/App.xcstrings",
    "outputDir": "Generated",
    "output": "L10n.swift",
    "table": "App",
    "name": "L10n",
    "access": "internal",
    "locale": "en",
    "separator": "_"
}

Usage

  • 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)
    }

Supported Formatting

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.


Installation

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.


Design Constraints

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.


Hacker News Compatibility Statement

  • 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.


Status

Actively used. Stable surface. Small footprint.
Expect incremental improvements, not churn.


License

MIT.
Fork it. Ship it. Improve it.
Please don’t turn it into a framework.

Description

  • Swift Tools 5.11.0
View More Packages from this Author

Dependencies

  • None
Last updated: Tue Dec 30 2025 10:54:05 GMT-1000 (Hawaii-Aleutian Standard Time)