mcpField
A
axiom-localization

axiom-localization

github.com/charleswiltgen开发1.2k178 次安装今天更新
代码仓库

关于 axiom-localization

面向 Apple 平台开发者的本地化与国际化指南,重点介绍 Xcode String Catalog、SwiftUI/UIKit API、复数形式、RTL 支持及旧格式迁移。

主要功能

  1. 01管理 Xcode String Catalog
  2. 02本地化 SwiftUI 和 UIKit
  3. 03处理复数与 RTL 布局
  4. 04按区域设置格式化数据
  5. 05迁移旧本地化文件

适用场景

  1. 01为应用添加多语言支持
  2. 02排查缺失翻译或复数错误
  3. 03支持阿拉伯语和希伯来语布局
  4. 04将 .strings 文件迁移至 .xcstrings

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-localization

安装后在 Claude Code 中输入/axiom-localization确认已加载。

详细介绍

本地化与国际化

使用 String Catalogs 进行应用本地化的综合指南。荣获 Apple Design Award 包容性奖项的应用始终支持多种语言,并提供出色的 RTL(从右向左)支持。

概述

String Catalogs(.xcstrings)是 Xcode 15 用于管理应用本地化的统一格式。它们以单一的 JSON 格式取代旧版 .strings 和 .stringsdict 文件,更易于维护、比较差异并集成到翻译工作流中。

本技能涵盖 String Catalogs、SwiftUI/UIKit 本地化 API、复数处理、RTL 支持、区域设置感知格式化,以及从旧版格式迁移的策略。

何时使用本技能

  • 在 Xcode 15+ 中设置 String Catalogs
  • 本地化 SwiftUI 和 UIKit 应用
  • 正确处理复数形式(对许多语言至关重要)
  • 支持 RTL 语言(阿拉伯语、希伯来语)
  • 根据区域设置格式化日期、数字和货币
  • 从旧版 .strings/.stringsdict 文件迁移
  • 为 App Shortcuts 和 App Intents 准备本地化内容
  • 排查缺失的翻译或不正确的复数形式

系统要求

  • Xcode 15+:支持 String Catalogs(.xcstrings)
  • Xcode 26+:支持自动符号生成、#bundle 宏和 AI 驱动的注释生成
  • iOS 15+:支持 LocalizedStringResource
  • iOS 16+:支持 App Shortcuts 本地化
  • 更早版本的 iOS 使用旧版 .strings 文件

第 1 部分:String Catalogs(WWDC 2023/10155)

创建 String Catalog

方法 1:Xcode 导航器

  1. File → New → File
  2. 选择 "String Catalog"
  3. 为其命名(例如,Localizable.xcstrings)
  4. 添加到目标

方法 2:自动提取

Xcode 15 可以自动提取以下内容中的字符串:

  • SwiftUI 视图(Text、Label、Button 中的字符串字面量)
  • Swift 代码(String(localized:))
  • Objective-C(NSLocalizedString)
  • C(CFCopyLocalizedString)
  • Interface Builder 文件(.storyboard、.xib)
  • Info.plist 值
  • App Shortcuts 短语

所需构建设置:

  • "Use Compiler to Extract Swift Strings" → Yes
  • "Localization Prefers String Catalogs" → Yes
String Catalog 结构

每个条目包含:

  • 键:唯一标识符(默认值:英文字符串)
  • 默认值:缺少翻译时的回退值
  • 注释:为译者提供的上下文
  • 字符串表:组织容器(默认值:"Localizable")

.xcstrings JSON 示例:

{
  "sourceLanguage" : "en",
  "strings" : {
    "Thanks for shopping with us!" : {
      "comment" : "Label above checkout button",
      "localizations" : {
        "en" : {
          "stringUnit" : {
            "state" : "translated",
            "value" : "Thanks for shopping with us!"
          }
        },
        "es" : {
          "stringUnit" : {
            "state" : "translated",
            "value" : "¡Gracias por comprar con nosotros!"
          }
        }
      }
    }
  },
  "version" : "1.0"
}
翻译状态

Xcode 会跟踪每项翻译的状态:

  • 新增(⚪)- 字符串尚未翻译
  • 需要审核(🟡)- 源内容已更改,翻译可能已过时
  • 已审核(✅)- 翻译已批准且为最新版本
  • 过时(🔴)- 源代码中已找不到该字符串

工作流:

  1. 开发者添加字符串 → 新增
  2. 译者添加翻译 → 已审核
  3. 开发者更改源内容 → 需要审核
  4. 译者更新翻译 → 已审核
  5. 开发者移除代码 → 过时

第 2 部分:SwiftUI 本地化

LocalizedStringKey(自动)

带有 String 参数的 SwiftUI 视图会自动支持本地化:

// ✅ Automatically localizable
Text("Welcome to WWDC!")
Label("Thanks for shopping with us!", systemImage: "bag")
Button("Checkout") { }

// Xcode extracts these strings to String Catalog

工作原理:SwiftUI 在内部使用 LocalizedStringKey,从 String Catalogs 中查找字符串。

使用注释的 String(localized:)

在 Swift 代码中显式进行本地化:

// Basic
let title = String(localized: "Welcome to WWDC!")

// With comment for translators
let title = String(localized: "Welcome to WWDC!",
                   comment: "Notification banner title")

// With custom table
let title = String(localized: "Welcome to WWDC!",
                   table: "WWDCNotifications",
                   comment: "Notification banner title")

// With default value (key ≠ English text)
let title = String(localized: "WWDC_NOTIFICATION_TITLE",
                   defaultValue: "Welcome to WWDC!",
                   comment: "Notification banner title")

最佳实践:始终添加 comment,为译者提供上下文。

LocalizedStringResource(延迟本地化)

用于将可本地化字符串传递给其他函数:

import Foundation

struct CardView: View {
    let title: LocalizedStringResource
    let subtitle: LocalizedStringResource

    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 10.0)
            VStack {
                Text(title)      // Resolved at render time
                Text(subtitle)
            }
            .padding()
        }
    }
}

// Usage
CardView(
    title: "Recent Purchases",
    subtitle: "Items you've ordered in the past week."
)

主要区别:LocalizedStringResource 会延迟到使用时才查找字符串,因此自定义视图可以完全支持本地化。

使用 Markdown 的 AttributedString
// Markdown formatting is preserved across localizations
let subtitle = AttributedString(localized: "**Bold** and _italic_ text")

第 3 部分:UIKit 与 Foundation

NSLocalizedString 宏
// Basic
let title = NSLocalizedString("Recent Purchases", comment: "Button Title")

// With table
let title = NSLocalizedString("Recent Purchases",
                             tableName: "Shopping",
                             comment: "Button Title")

// With bundle
let title = NSLocalizedString("Recent Purchases",
                             tableName: nil,
                             bundle: .main,
                             value: "",
                             comment: "Button Title")
Bundle.localizedString
let customBundle = Bundle(for: MyFramework.self)
let text = customBundle.localizedString(forKey: "Welcome",
                                        value: nil,
                                        table: "MyFramework")
自定义宏
// Objective-C
#define MyLocalizedString(key, comment) \
    [myBundle localizedStringForKey:key value:nil table:nil]
Info.plist 本地化

本地化应用名称、权限等内容:

  1. 选择 Info.plist
  2. Editor → Add Localization
  3. 为每种语言创建 InfoPlist.strings:
// InfoPlist.strings (Spanish)
"CFBundleName" = "Mi Aplicación";
"NSCameraUsageDescription" = "La app necesita acceso a la cámara para tomar fotos.";

第 4 部分:复数形式

不同语言的复数规则各不相同:

  • 英语:2 种形式(one、other)
  • 俄语:3 种形式(one、few、many)
  • 波兰语:3 种形式(one、few、other)
  • 阿拉伯语:6 种形式(zero、one、two、few、many、other)
SwiftUI 复数处理
// Xcode automatically creates plural variations
Text("\(count) items")

// With custom formatting
Text("\(visitorCount) Recent Visitors")

在 String Catalog 中:

{
  "strings" : {
    "%lld Recent Visitors" : {
      "localizations" : {
        "en" : {
          "variations" : {
            "plural" : {
              "one" : {
                "stringUnit" : {
                  "state" : "translated",
                  "value" : "%lld Recent Visitor"
                }
              },
              "other" : {
                "stringUnit" : {
                  "state" : "translated",
                  "value" : "%lld Recent Visitors"
                }
              }
            }
          }
        }
      }
    }
  }
}
XLIFF 导出格式

导出以供翻译时(文件 → 导出本地化):

旧版(stringsdict):

<trans-unit id="/%lld Recent Visitors:dict/NSStringLocalizedFormatKey:dict/:string">
    <source>%#@recentVisitors@</source>
</trans-unit>

<trans-unit id="/%lld Recent Visitors:dict/recentVisitors:dict/one:dict/:string">
    <source>%lld Recent Visitor</source>
    <target>%lld Visitante Recente</target>
</trans-unit>

String Catalog(更简洁):

<trans-unit id="%lld Recent Visitors|==|plural.one">
    <source>%lld Recent Visitor</source>
    <target>%lld Visitante Recente</target>
</trans-unit>

<trans-unit id="%lld Recent Visitors|==|plural.other">
    <source>%lld Recent Visitors</source>
    <target>%lld Visitantes Recentes</target>
</trans-unit>
使用复数变量进行替换
// Multiple variables with different plural forms
let message = String(localized: "\(songCount) songs on \(albumCount) albums")

Xcode 会为每个变量的复数形式创建变体:

  • songCount:one、other
  • albumCount:one、other
  • 组合总数:2 × 2 = 4 个翻译条目

第 5 部分:设备和宽度变体

特定设备的字符串

不同平台使用不同文本:

// Same code, different strings per device
Text("Bird Food Shop")

String Catalog 变体:

{
  "Bird Food Shop" : {
    "localizations" : {
      "en" : {
        "variations" : {
          "device" : {
            "applewatch" : {
              "stringUnit" : {
                "value" : "Bird Food"
              }
            },
            "other" : {
              "stringUnit" : {
                "value" : "Bird Food Shop"
              }
            }
          }
        }
      }
    }
  }
}

结果:

  • iPhone/iPad:“鸟食商店”
  • Apple Watch:“鸟食”(适用于小屏幕的较短文本)
宽度变体

用于动态字体和尺寸类别:

Text("Application Settings")

String Catalog 可以为较窄的宽度提供较短的文本。


第 6 部分:RTL 支持

布局镜像

SwiftUI 会自动为 RTL 语言镜像布局:

// ✅ Automatically mirrors for Arabic/Hebrew
HStack {
    Image(systemName: "chevron.right")
    Text("Next")
}

// iPhone (English): [>] Next
// iPhone (Arabic):  Next [<]
Leading/Trailing 与 Left/Right

始终使用语义方向:

// ✅ Correct - mirrors automatically
.padding(.leading, 16)
.frame(maxWidth: .infinity, alignment: .leading)

// ❌ Wrong - doesn't mirror
.padding(.left, 16)
.frame(maxWidth: .infinity, alignment: .left)
图像和图标

标记应翻转或不应翻转的图像:

// ✅ Directional - mirrors for RTL
Image(systemName: "chevron.forward")

// ✅ Non-directional - never mirrors
Image(systemName: "star.fill")

// Custom images
Image("backButton")
    .flipsForRightToLeftLayoutDirection(true)
在 RTL 模式下测试

Xcode 方案:

  1. 编辑方案 → 运行 → 选项
  2. 应用语言:阿拉伯语 / 希伯来语
  3. 或:应用语言 → 从右到左伪语言

模拟器: 设置 → 通用 → 语言与地区 → 首选语言顺序

SwiftUI 预览:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environment(\.layoutDirection, .rightToLeft)
            .environment(\.locale, Locale(identifier: "ar"))
    }
}

第 7 部分:区域设置感知格式化

DateFormatter
let formatter = DateFormatter()
formatter.locale = Locale.current  // ✅ Use current locale
formatter.dateStyle = .long
formatter.timeStyle = .short

let dateString = formatter.string(from: Date())

// US: "January 15, 2024 at 3:30 PM"
// France: "15 janvier 2024 à 15:30"
// Japan: "2024年1月15日 15:30"

切勿硬编码日期格式字符串:

// ❌ Wrong - breaks in other locales
formatter.dateFormat = "MM/dd/yyyy"

// ✅ Correct - adapts to locale
formatter.dateStyle = .short
用于货币的 NumberFormatter
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .currency

let priceString = formatter.string(from: 29.99)

// US: "$29.99"
// UK: "£29.99"
// Japan: "¥30" (rounds to integer)
// France: "29,99 €" (comma decimal, space before symbol)
MeasurementFormatter
let distance = Measurement(value: 100, unit: UnitLength.meters)

let formatter = MeasurementFormatter()
formatter.locale = Locale.current

let distanceString = formatter.string(from: distance)

// US: "328 ft" (converts to imperial)
// Metric countries: "100 m"
特定区域设置的排序
let names = ["Ångström", "Zebra", "Apple"]

// ✅ Locale-aware sort
let sorted = names.sorted { (lhs, rhs) in
    lhs.localizedStandardCompare(rhs) == .orderedAscending
}

// Sweden: ["Ångström", "Apple", "Zebra"]  (Å comes first in Swedish)
// US: ["Ångström", "Apple", "Zebra"]      (Å treated as A)

第 8 部分:App Shortcuts 本地化

带参数的短语
import AppIntents

struct ShowTopDonutsIntent: AppIntent {
    static var title: LocalizedStringResource = "Show Top Donuts"

    @Parameter(title: "Timeframe")
    var timeframe: Timeframe

    static var parameterSummary: some ParameterSummary {
        Summary("\(.applicationName) Trends for \(\.$timeframe)") {
            \.$timeframe
        }
    }
}

String Catalog 会自动提取:

  • 意图标题
  • 参数名称
  • 带占位符的短语模板

本地化短语:

English: "Food Truck Trends for this week"
Spanish: "Tendencias de Food Truck para esta semana"
AppShortcutsProvider 本地化
struct FoodTruckShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: ShowTopDonutsIntent(),
            phrases: [
                "\(.applicationName) Trends for \(\.$timeframe)",
                "Show trending donuts for \(\.$timeframe) in \(.applicationName)",
                "Give me trends for \(\.$timeframe) in \(.applicationName)"
            ]
        )
    }
}

Xcode 会将全部 3 个短语提取到 String Catalog 中以供翻译。


第 9 部分:从旧版迁移

将 .strings 转换为 .xcstrings

自动迁移:

  1. 在导航器中选择 .strings 文件
  2. 编辑器 → 转换为 String Catalog
  3. Xcode 会创建 .xcstrings 并保留翻译

手动方式:

  1. 创建新的 String Catalog
  2. 构建项目(Xcode 会从代码中提取字符串)
  3. 通过文件 → 导入本地化(XLIFF)导入翻译
  4. 删除旧的 .strings 文件
转换 .stringsdict

复数文件会自动合并:

  1. 将 .strings 和 .stringsdict 放在一起
  2. 转换 → 两者会合并为单个 .xcstrings
  3. 保留复数变体
渐进式迁移策略

阶段 1:新代码使用 String Catalog

  • 创建 Localizable.xcstrings
  • 使用 String(localized:) 编写新代码
  • 为旧代码保留旧版 .strings 文件

阶段 2:迁移现有字符串

  • 每次转换一个 .strings 表
  • 每次转换后测试翻译
  • 更新使用旧版 NSLocalizedString 调用的代码

阶段 3:移除旧版文件

  • 删除 .strings 和 .stringsdict 文件
  • 确认所有字符串都在 String Catalog 中
  • 提交到 App Store

共存:.strings 和 .xcstrings 可以协同工作,Xcode 会同时检查两者。


常见错误

硬编码字符串
// ❌ Wrong - not localizable
Text("Welcome")
let title = "Settings"

// ✅ Correct - localizable
Text("Welcome")  // SwiftUI auto-localizes
let title = String(localized: "Settings")
拼接本地化字符串
// ❌ Wrong - word order varies by language
let message = String(localized: "You have") + " \(count) " + String(localized: "items")

// ✅ Correct - single localizable string with substitution
let message = String(localized: "You have \(count) items")

错误原因:有些语言将数字放在名词前面,有些则放在后面。

缺少复数形式
// ❌ Wrong - grammatically incorrect for many languages
Text("\(count) item(s)")

// ✅ Correct - proper plural handling
Text("\(count) items")  // Xcode creates plural variations
忽略 RTL
// ❌ Wrong - breaks in RTL languages
.padding(.left, 20)
HStack {
    backButton
    Spacer()
    title
}

// ✅ Correct - mirrors automatically
.padding(.leading, 20)
HStack {
    backButton  // Appears on right in RTL
    Spacer()
    title
}
日期/数字格式错误
// ❌ Wrong - US-only format
let formatter = DateFormatter()
formatter.dateFormat = "MM/dd/yyyy"

// ✅ Correct - adapts to locale
formatter.dateStyle = .short
formatter.locale = Locale.current
忘记添加注释
// ❌ Wrong - translator has no context
String(localized: "Confirm")

// ✅ Correct - clear context
String(localized: "Confirm", comment: "Button to confirm delete action")

影响:“Confirm” 可能表示“验证”或“确认收到”,准确翻译需要结合上下文。


故障排除

字符串未出现在 String Catalog 中

原因:未启用构建设置

解决方案:

  1. Build Settings → "Use Compiler to Extract Swift Strings" → Yes
  2. 清理构建文件夹(Cmd+Shift+K)
  3. 构建项目
应用中未显示翻译

原因 1:项目中未添加语言

  1. Project → Info → Localizations → + 按钮
  2. 添加目标语言

原因 2:字符串被标记为 "Stale"

  • 移除过时字符串,或确认代码仍在使用这些字符串
复数形式不正确

原因:使用 String.localizedStringWithFormat 而不是 String Catalog

解决方案:使用 String Catalog 的自动复数处理:

// ✅ Correct
Text("\(count) items")

// ❌ Wrong
Text(String.localizedStringWithFormat(NSLocalizedString("%d items", comment: ""), count))
导出的 XLIFF 缺少字符串

原因:未设置 "Localization Prefers String Catalogs"

解决方案:

  1. Build Settings → "Localization Prefers String Catalogs" → Yes
  2. 再次导出本地化文件
未显示生成的符号(Xcode 26+)

原因 1:未启用构建设置

解决方案:

  1. Build Settings → "Generate String Catalog Symbols" → Yes
  2. 清理构建文件夹(Cmd+Shift+K)
  3. 重新构建项目

原因 2:未将字符串手动添加到目录中

解决方案:符号仅会为手动添加的字符串生成(String Catalog 中的 + 按钮)。自动提取的字符串不会生成符号。

#bundle 宏无法使用(Xcode 26+)

原因:语法错误或缺少导入

解决方案:

import Foundation  // Required for #bundle
Text("My Collections", bundle: #bundle, comment: "Section title")

确认使用的是 #bundle,而不是 .module。

重构为符号失败(Xcode 26+)

原因 1:字符串不在 String Catalog 中

  1. 确保字符串存在于 .xcstrings 文件中
  2. 构建项目以刷新目录
  3. 再次尝试重构

原因 2:未启用构建设置

  • 在 Build Settings 中启用 "Generate String Catalog Symbols"
  • 清理并重新构建

第 10 部分:Xcode 26 本地化增强功能

Xcode 26 引入了通过生成符号实现的类型安全本地化、使用设备端 AI 自动生成注释,以及通过 #bundle 宏改进 Swift Package 支持。这些内容基于 WWDC 2025 第 225 场会议「Explore localization with Xcode」。

生成的符号(类型安全本地化)

问题:基于字符串的本地化在出现拼写错误时会静默失败。

// ❌ Typo - fails silently at runtime
Text("App.HomeScren.Title")  // Missing 'e' in Screen

解决方案:Xcode 26 会根据手动添加的字符串生成类型安全符号。

工作原理
  1. 使用 + 按钮将字符串手动添加到 String Catalog
  2. 启用构建设置:"Generate String Catalog Symbols"(新项目中默认开启)
  3. 使用符号而不是字符串
// ✅ Type-safe - compiler catches typos
Text(.appHomeScreenTitle)
符号生成规则
字符串类型生成的符号类型用法示例
无占位符静态属性Text(.introductionTitle)
有占位符带标签参数的函数.subtitle(friendsPosts: 42)

关键命名转换:

  • App.HomeScreen.Title → .appHomeScreenTitle
  • 移除句点,转换为驼峰命名
  • 可用于 LocalizedStringResource
代码示例
// SwiftUI views
struct ContentView: View {
    var body: some View {
        NavigationStack {
            Text(.introductionTitle)
                .navigationSubtitle(.subtitle(friendsPosts: 42))
        }
    }
}

// Foundation String
let message = String(localized: .curatedCollection)

// Custom views with LocalizedStringResource
struct CollectionDetailEditingView: View {
    let title: LocalizedStringResource

    init(title: LocalizedStringResource) {
        self.title = title
    }

    var body: some View {
        Text(title)
    }
}

CollectionDetailEditingView(title: .editingTitle)

自动生成注释

Xcode 26 使用设备端模型,为可本地化字符串自动生成上下文注释。

启用此功能
  1. 打开 Xcode Settings → Editing
  2. 启用 "automatically generate string catalog comments"
  3. 添加到代码中的新字符串会自动获得生成的注释
示例

对于按钮字符串,Xcode 会生成:

"用于取消删除集合的按钮文本标签"

这些上下文信息有助于译者了解字符串的使用位置和方式。

XLIFF 导出

导出的 XLIFF 文件会标记自动生成的注释:

<trans-unit id="Grand Canyon" xml:space="preserve">
    <source>Grand Canyon</source>
    <target state="new">Grand Canyon</target>
    <note from="auto-generated">Suggestion for searching landmarks</note>
</trans-unit>

优点:

  • 节省开发者编写译者上下文说明的时间
  • 提供一致、清晰的描述
  • 提高翻译质量

Swift Package 和框架的本地化
问题

SwiftUI 默认使用 .main 包。Swift Package 和框架需要引用自己的包:

// ❌ Wrong - uses main bundle, strings not found
Text("My Collections", comment: "Section title")
解决方案:#bundle 宏(Xcode 26 新增)

#bundle 宏会自动引用当前目标对应的正确 bundle:

// ✅ Correct - automatically uses package/framework bundle
Text("My Collections", bundle: #bundle, comment: "Section title")

主要优势:

  • 适用于主应用、框架和 Swift Packages
  • 向后兼容旧版操作系统
  • 无需手动管理 .module bundle
使用自定义表名
// Main app
Text("My Collections",
     tableName: "Discover",
     comment: "Section title")

// Framework or Swift Package
Text("My Collections",
     tableName: "Discover",
     bundle: #bundle,
     comment: "Section title")

自定义表的符号访问

使用多个 String Catalog 进行组织时:

默认的“Localizable”表

符号可直接通过 LocalizedStringResource 访问:

Text(.welcomeMessage)  // From Localizable.xcstrings

注意:Xcode 会自动解析默认“Localizable”表中的符号。通常无需显式选择表,只有在调试或测试特定目录时才需要这样做。

自定义表

符号嵌套在表的命名空间中:

// From Discover.xcstrings
Text(Discover.featuredCollection)

// From Settings.xcstrings
Text(Settings.privacyPolicy)

大型应用的组织策略:

  • Localizable.xcstrings - 应用核心字符串
  • FeatureName.xcstrings - 特定功能的字符串(例如,Onboarding、Settings、Discover)
  • 优势:更易管理、归属更清晰、XLIFF 组织更完善

两种本地化工作流

Xcode 26 支持两种互为补充的工作流:

工作流 1:字符串提取(推荐用于新项目)

流程:

  1. 直接在代码中编写字符串
  2. 使用 SwiftUI 视图(Text、Button)和 String(localized:)
  3. Xcode 会自动提取到 String Catalog
  4. 利用自动生成的注释

优点:初始设置简单,可立即开始

缺点:对字符串组织的控制较少

// ✅ String extraction workflow
Text("Welcome to WWDC!", comment: "Main welcome message")
工作流 2:生成符号(推荐用于复杂度增加时)

流程:

  1. 手动将字符串添加到 String Catalog
  2. 通过类型安全的符号进行引用
  3. 组织到自定义表中

优点:控制力更强、类型安全、跨框架维护更轻松

缺点:需要预先规划字符串目录结构

// ✅ Generated symbols workflow
Text(.welcomeMessage)
工作流最适合权衡
字符串提取新项目、简单应用、原型开发自动提取,但对组织方式的控制较少
生成符号大型应用、框架、多个团队类型安全、组织更完善,但需要预先规划

在工作流之间重构

Xcode 26 支持在工作流之间转换,无需手动重写。

将字符串转换为符号
  1. 在代码中的字符串字面量上右键单击
  2. 选择 “Refactor > Convert Strings to Symbols”
  3. 预览所有受影响的位置
  4. 确认前自定义符号名称
  5. 应用到整个表或单个字符串

示例:

// Before
Text("Welcome to WWDC!", comment: "Main welcome message")

// After refactoring
Text(.welcomeToWWDC)

优点:

  • 批量转换整个 String Catalog
  • 应用前预览更改
  • 无需重写代码即可保留本地化

实现检查清单

采用 Xcode 26 生成的符号后,请验证:

构建配置:

  • 已启用“Generate String Catalog Symbols”构建设置
  • 项目构建时不会出现“Cannot find 'symbolName' in scope”错误
  • 清理后构建成功(Cmd+Shift+K,然后 Cmd+B)

字符串目录设置:

  • 使用 + 按钮手动将字符串添加到目录中(而非自动提取)
  • 符号名称遵循约定(驼峰命名,不含句点)
  • 按功能组织自定义表(如果使用多个目录)

Swift Package 集成:

  • 包中的所有 Text() 和 String(localized:) 调用都使用 bundle: #bundle
  • 在使用 #bundle 的位置添加了 Import Foundation
  • 已分别测试包独立构建和作为依赖项时的构建

重构与迁移:

  • 已在示例字符串上测试重构工具
  • 应用更改前,预览显示了预期结果
  • 过渡期间,旧的基于字符串的调用仍可正常使用

可选功能:

  • 已在 Xcode Settings → Editing 中启用自动生成注释(可选)
  • 已测试 AI 生成的注释是否准确
  • XLIFF 导出内容包含自动生成的注释

测试:

  • 符号在 SwiftUI 预览中可正确解析
  • 本地化在所有支持的语言中均正常工作
  • 应用可在最低支持的 iOS 版本上运行

资源

WWDC:2025-225、2023-10155、2022-10110

文档:/xcode/localization、/xcode/localizing-and-varying-text-with-a-string-catalog

技能:axiom-app-intents-ref、axiom-hig、axiom-accessibility-diag

评论

支持 Markdown