Skip to main content
Ship globally from day one with a production-ready localization system featuring type-safe string keys and automatic pluralization support.
Package location: Packages/Localization/

What’s new

  • v2.2 — Localized error surface: AppError.localizedUserMessage (en + es) lives here in Localization, now linked by the app target, FeatureSettings, and FeatureChat. All UI error call sites resolve user-facing copy through it.
  • v2.0L10n.swift is a root enum L10n plus one L10n+<Namespace>.swift per nested enum (L10n+Auth.swift, L10n+Chat.swift, L10n+Settings.swift, and so on). The public API is identical: L10n.Auth.tagline, L10n.Chat.placeholder, and every other call site compile unchanged.

Overview

The Localization module provides compile-time safe string access, eliminating missing translation errors before they reach users.

Type-Safe Strings

Compile-time safety catches missing translations

Pluralization

Automatic plural rules for every language

2 Languages Included

English and Spanish out of the box

100+ Strings

Pre-localized Auth, Chat, Settings, Payments, Errors

Quick Start

Basic Usage

import Localization

// Type-safe, autocomplete-friendly
Text(L10n.Auth.tagline)           // "Your AI assistant"
Text(L10n.Chat.placeholder)       // "Type a message..."
Text(L10n.Settings.theme)         // "Theme"

// Error messages that make sense
errorLabel.text = L10n.Error.networkOffline
// "You're offline. Please check your internet connection."

Pluralization

Plural forms are driven by Localizable.stringsdict, so each language can define its own rules (English ships with zero/one/other; languages like Russian or Arabic can add their full form sets in their own .stringsdict):
// English: "5 messages remaining" / "1 message remaining" / "No messages remaining"
Text(L10n.Chat.messagesRemaining(count))

File Structure

Packages/Localization/
├── Package.swift
├── README.md
├── Sources/Localization/
│   ├── L10n.swift                # Root enum + locale utilities
│   ├── L10n+Auth.swift           # One file per namespace
│   ├── L10n+Chat.swift
│   ├── L10n+Settings.swift
│   ├── L10n+Payments.swift
│   ├── L10n+Onboarding.swift
│   ├── L10n+Profile.swift
│   ├── L10n+Theme.swift
│   ├── L10n+Common.swift
│   ├── L10n+Error.swift
│   ├── L10n+A11y.swift
│   ├── L10n+AppError.swift       # AppError.localizedUserMessage (v2.2)
│   ├── Localization.swift        # Module entry
│   └── Resources/
│       ├── en.lproj/             # English
│       │   ├── Localizable.strings
│       │   └── Localizable.stringsdict
│       └── es.lproj/             # Spanish
│           └── Localizable.strings
└── Tests/LocalizationTests/
    ├── L10nTests.swift
    └── L10nAppErrorTests.swift

Adding New Languages

Adding a new language takes just minutes:
1

Copy existing language folder

cd Packages/Localization/Sources/Localization/Resources
cp -r en.lproj de.lproj
2

Translate strings

Edit de.lproj/Localizable.strings with German translations. Adapt the copied Localizable.stringsdict if the language needs different plural rules.
3

Build and test

Rebuild — the app automatically picks up new languages.

Adding Custom Strings

1. Add to Localizable.strings

// en.lproj/Localizable.strings
"myFeature.title" = "My Feature";
"myFeature.subtitle" = "This is my new feature";
Add the same keys to every .strings file in Resources/ (e.g. es.lproj/).

2. Add Type-Safe Key

Create a new L10n+MyFeature.swift extension (one namespace per file, matching the v2.0 layout):
// L10n+MyFeature.swift
import Foundation

public extension L10n {
    enum MyFeature {
        public static var title: String {
            String(localized: "myFeature.title", bundle: bundle)
        }

        public static var subtitle: String {
            String(localized: "myFeature.subtitle", bundle: bundle)
        }
    }
}

3. Use in Your Views

Text(L10n.MyFeature.title)
Text(L10n.MyFeature.subtitle)

Pre-Localized Strings

The module includes 100+ pre-localized strings covering common app needs:
  • Sign in / Sign up labels
  • Password fields
  • Error messages
  • Social login buttons
  • Email confirmation
  • Message placeholders
  • Send button
  • Typing indicators
  • Message status (sent, delivered, read)
  • Error states
  • Section headers
  • Theme options
  • Account settings
  • Privacy options
  • About section
  • Subscription labels
  • Price formatting
  • Purchase buttons
  • Restore purchases
  • Error messages
  • Network errors
  • Authentication errors
  • Permission errors
  • Generic errors
  • Retry prompts

Best Practices

Always use L10n

Never hardcode user-facing strings

Test with pseudolocalization

Catch layout issues early

Keep strings short

Some languages expand 30%+

Avoid string concatenation

Use string interpolation instead

Testing

func testLocalizationKeys() {
    // Verify all keys have translations
    XCTAssertFalse(L10n.Auth.tagline.isEmpty)
    XCTAssertFalse(L10n.Chat.placeholder.isEmpty)
    
    // Test pluralization
    XCTAssertEqual(L10n.Chat.messagesRemaining(0), "No messages remaining")
    XCTAssertEqual(L10n.Chat.messagesRemaining(1), "1 message remaining")
    XCTAssertEqual(L10n.Chat.messagesRemaining(5), "5 messages remaining")
}

Quick Start

Run the app before adding more languages

Architecture

Understand module boundaries

Accessibility Module

VoiceOver and Dynamic Type support

Design System

UI tokens and components