DailyRoundup uses Xcode’s .xcstrings format as the single source of truth for all
user-facing strings. This document explains how the system works and how to add new
strings correctly.
All user-facing strings live in:
DailyRoundup/Localizable.xcstrings
The file is automatically included in the app target because the project uses Xcode’s
PBXFileSystemSynchronizedRootGroup — any file added to the DailyRoundup/ folder is
picked up automatically without editing project.pbxproj.
SwiftUI’s Text, Label, Button, Section, and similar initializers accept
LocalizedStringKey when a string literal is passed. The system looks up the
literal as a key in Localizable.xcstrings at runtime and returns the translation for
the current locale.
// ✅ Already LocalizedStringKey — just make sure the key is in Localizable.xcstrings
Text("Settings")
Label("Sync now", systemImage: "arrow.clockwise")
Button("Cancel") { … }
Section("Troubleshooting") { … }
When a String variable is passed instead of a literal, SwiftUI uses the
StringProtocol overload, which displays the string verbatim (no lookup). Avoid this
for user-facing strings:
let title: String = "Settings"
Text(title) // ❌ verbatim — does NOT go through Localizable.xcstrings
let key: LocalizedStringKey = "Settings"
Text(key) // ✅ LocalizedStringKey — looked up in Localizable.xcstrings
Use a string literal directly. Add a matching entry to Localizable.xcstrings:
Text("My new string")
Localizable.xcstrings entry:
"My new string" : {
"comment" : "Brief description of where this string appears"
}
Use String(localized:) and add a matching entry to Localizable.xcstrings:
let message = String(localized: "My new string")
This is required wherever a String value is later passed to a SwiftUI view via a
variable (since the variable loses the LocalizedStringKey type), or to a system API
that takes String (such as UNNotificationAction.title).
Use String(localized:) with a String.LocalizationValue that contains an
interpolation. The key stored in Localizable.xcstrings uses a printf-style specifier:
| Swift interpolated type | Format specifier in .xcstrings key |
|---|---|
Int / Int64 |
%lld |
String |
%@ |
Double / Float |
%f |
// Code
let progress = String(localized: "Importing \(count) items…")
// Localizable.xcstrings key
"Importing %lld items…" : {
"comment" : "Progress message; %lld is the item count"
}
For proper plural handling, add variations to the entry and use the
String(localized:) interpolation form:
let title: LocalizedStringKey = count == 1
? "Resolve 1 Conflict…"
: "Resolve \(count) Conflicts…"
This generates separate keys ("Resolve 1 Conflict…" and "Resolve %lld Conflicts…")
that translators can vary independently per locale. Alternatively, use .xcstrings
plural variations for a single key.
Some private structs in the app are deliberately typed to accept LocalizedStringKey
so that string literals passed at the call site go through the localization system
automatically:
| Struct | Member | Type |
|---|---|---|
MetricRow |
label |
LocalizedStringKey |
MenuBarButton |
title |
LocalizedStringKey |
When creating new reusable view structs, prefer LocalizedStringKey over String for
any member that will be rendered as visible text.
DailyRoundup/Localizable.xcstrings in Xcode.localizations, Xcode shows them with
state "new" — translate those first before releasing to that locale.String(localized:) used in the code (not a plain String
assignment).Localizable.xcstrings with a descriptive comment.%lld, %@) used in the key when the string contains
interpolated values..xcstrings
variations entries are present.