Internationalization (i18n) Guide
Last Updated: 2026-02-03 | Reading Time: 12 minutes
Localization strategy and implementation for PasteShelf.
Table of Contents
Section titled “Table of Contents”Overview
Section titled “Overview”Localization Strategy
Section titled “Localization Strategy”PasteShelf uses Apple’s modern localization system:
- String Catalogs (.xcstrings): Primary localization format
- Automatic extraction: Xcode extracts localizable strings
- Pluralization: Built-in plural rules
- Format strings: Type-safe formatting
Architecture
Section titled “Architecture”┌─────────────────────────────────────────────────────────────┐│ Localization Flow │├─────────────────────────────────────────────────────────────┤│ ││ Source Code String Catalog Runtime ││ ─────────── ────────────── ─────── ││ ││ String(localized:) Localizable.xcstrings NSLocalizedString││ │ │ │ ││ └─────► Extract ─────┘ │ ││ │ │ ││ ▼ │ ││ Translation │ ││ │ │ ││ └──────────► Load ────────────►│ ││ │└─────────────────────────────────────────────────────────────┘String Catalogs
Section titled “String Catalogs”File Structure
Section titled “File Structure”PasteShelf/├── Localizable.xcstrings # Main strings├── InfoPlist.xcstrings # App metadata└── Intents.xcstrings # Shortcuts stringsString Catalog Format
Section titled “String Catalog Format”{ "sourceLanguage": "en", "strings": { "clipboard.empty": { "extractionState": "manual", "localizations": { "en": { "stringUnit": { "state": "translated", "value": "No clipboard items" } }, "de": { "stringUnit": { "state": "translated", "value": "Keine Zwischenablage-Elemente" } }, "ja": { "stringUnit": { "state": "translated", "value": "クリップボードアイテムがありません" } } } }, "items.count": { "extractionState": "manual", "localizations": { "en": { "variations": { "plural": { "one": { "stringUnit": { "state": "translated", "value": "%lld item" } }, "other": { "stringUnit": { "state": "translated", "value": "%lld items" } } } } } } } }}Implementation
Section titled “Implementation”Basic Strings
Section titled “Basic Strings”// ✅ Use String(localized:) for SwiftUIstruct EmptyStateView: View { var body: some View { Text("clipboard.empty", comment: "Shown when clipboard history is empty") }}
// ✅ Or explicit localized initializerText(String(localized: "clipboard.empty"))String Interpolation
Section titled “String Interpolation”// ✅ Use string interpolation with localized stringslet appName = "PasteShelf"Text("Welcome to \(appName)")
// In String Catalog:// "Welcome to %@" -> "Willkommen bei %@" (German)Pluralization
Section titled “Pluralization”// ✅ Automatic pluralizationstruct ItemCountView: View { let count: Int
var body: some View { Text("items.count \(count)", comment: "Number of clipboard items") }}
// String Catalog handles plural forms:// English: "1 item" / "5 items"// Russian: "1 элемент" / "2 элемента" / "5 элементов"// Arabic: Different forms for 0, 1, 2, few, many, otherFormatted Values
Section titled “Formatted Values”// ✅ Use formatters for dates, numbers, etc.struct ClipboardRow: View { let item: ClipboardItem
var body: some View { VStack { Text(item.content) Text(item.createdDate, format: .relative(presentation: .named)) .foregroundColor(.secondary) } }}
// Output adapts to locale:// English: "5 minutes ago"// German: "vor 5 Minuten"// Japanese: "5分前"Attributed Strings
Section titled “Attributed Strings”// ✅ Localized attributed stringsvar attributedMessage: AttributedString { var result = AttributedString(localized: "Copied **\(itemName)** to clipboard") // Markdown formatting preserved across translations return result}Best Practices
Section titled “Best Practices”String Keys
Section titled “String Keys”// ✅ Good: Descriptive, namespaced keys"clipboard.empty""search.placeholder""settings.general.title""error.sync.failed"
// ❌ Bad: Ambiguous keys"empty""title""error"Comments
Section titled “Comments”// ✅ Always provide context for translatorsText("Delete", comment: "Button to delete a clipboard item")Text("Delete", comment: "Menu item to delete selected items")
// These might be translated differently in some languagesAvoid Concatenation
Section titled “Avoid Concatenation”// ❌ Bad: Concatenation breaks translationlet message = String(localized: "Copied") + " " + itemName
// ✅ Good: Single string with placeholderlet message = String(localized: "Copied \(itemName)")Handle Text Length
Section titled “Handle Text Length”// ✅ Use flexible layoutsstruct ButtonView: View { var body: some View { Button(action: {}) { Text("settings.clear_history") } .fixedSize(horizontal: false, vertical: true) // Allow wrapping }}
// German text is often 30% longer than English// "Clear History" -> "Verlauf löschen"Right-to-Left Support
Section titled “Right-to-Left Support”// ✅ Use semantic layout directionsHStack { Image(systemName: "doc.on.clipboard") Text(item.content) Spacer() Text(item.date, format: .dateTime)}// Automatically mirrors for RTL languages (Arabic, Hebrew)
// ✅ Use .leading/.trailing instead of .left/.right.padding(.leading, 16)
// ✅ Check layout direction if needed@Environment(\.layoutDirection) var layoutDirectionTesting
Section titled “Testing”Preview Localization
Section titled “Preview Localization”// ✅ Preview in different localesstruct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .environment(\.locale, Locale(identifier: "en")) .previewDisplayName("English")
ContentView() .environment(\.locale, Locale(identifier: "de")) .previewDisplayName("German")
ContentView() .environment(\.locale, Locale(identifier: "ar")) .environment(\.layoutDirection, .rightToLeft) .previewDisplayName("Arabic (RTL)") }}Pseudo-Localization
Section titled “Pseudo-Localization”// Enable in scheme:// Edit Scheme → Run → Options → App Language → "Accented Pseudolanguage"
// Transforms "Hello" → "[Ĥéļļö]"// Helps identify:// - Hardcoded strings// - Truncation issues// - Layout problemsUI Testing
Section titled “UI Testing”func testLocalizedUI() { let app = XCUIApplication() app.launchArguments += ["-AppleLanguages", "(de)"] app.launchArguments += ["-AppleLocale", "de_DE"] app.launch()
// Verify German strings appear XCTAssertTrue(app.buttons["Einstellungen"].exists)}Export for Translation
Section titled “Export for Translation”# Export localizable stringsxcodebuild -exportLocalizations \ -project PasteShelf.xcodeproj \ -localizationPath ./Localizations \ -exportLanguage de \ -exportLanguage ja
# Import translationsxcodebuild -importLocalizations \ -project PasteShelf.xcodeproj \ -localizationPath ./Localizations/de.xclocSupported Languages
Section titled “Supported Languages”Launch Languages
Section titled “Launch Languages”| Language | Code | Status |
|---|---|---|
| English | en | ✅ Complete |
| German | de | 🚧 In Progress |
| French | fr | 📋 Planned |
| Spanish | es | 📋 Planned |
| Japanese | ja | 📋 Planned |
| Chinese (Simplified) | zh-Hans | 📋 Planned |
| Chinese (Traditional) | zh-Hant | 📋 Planned |
| Korean | ko | 📋 Planned |
| Portuguese (Brazil) | pt-BR | 📋 Planned |
| Russian | ru | 📋 Planned |
Contribution
Section titled “Contribution”Community translations welcome! See Contributing for guidelines.
Translation Workflow
Section titled “Translation Workflow”┌─────────────────────────────────────────────────────────────┐│ Translation Process │├─────────────────────────────────────────────────────────────┤│ ││ 1. Export strings xcodebuild -exportLocalizations ││ 2. Translate Using Xcode or external tools ││ 3. Review Native speaker review ││ 4. Import xcodebuild -importLocalizations ││ 5. Test Run app in target locale ││ 6. Release Include in next version ││ │└─────────────────────────────────────────────────────────────┘Localization Checklist
Section titled “Localization Checklist”Development
Section titled “Development”- All user-visible strings use String(localized:)
- Comments provided for context
- No hardcoded strings
- Flexible layouts accommodate text expansion
- RTL layouts tested
Translation
Section titled “Translation”- Strings exported for translators
- Context/comments provided
- Pluralization rules defined
- Screenshots for context
Testing
Section titled “Testing”- All languages previewed
- Pseudo-localization tested
- RTL layout verified
- Date/number formatting checked
Related Documentation
Section titled “Related Documentation”| Document | Description |
|---|---|
| Accessibility | VoiceOver localization |
| Development Guide | Setup |
| Contributing | Translation guidelines |
Last updated: 2026-02-03