CI Status Version: 4.14.0 Swift: 5.7 License: MIT
PayPal: Donate GitHub: Become a sponsor Patreon: Become a patron

安装配置使用构建脚本捐赠迁移指南问题贡献许可证

重要通知

苹果在 Xcode 15 中引入了 String Catalogs,它实现了 BartyCrouch 的许多功能,例如增量自动提取或警告空翻译。它也完全向后兼容所有 iOS 版本。迁移非常简单,只需右键单击 .strings 文件并选择“迁移到 String Catalog...”。我写了一篇关于 String Catalogs 的 详细常见问题解答,如果您想了解更多信息。它真的很棒,每个人都应该迁移到它!

它唯一缺少的功能是机器翻译,但我编写了一个应用程序来填补这一空白,它甚至支持比 BartyCrouch 更多的翻译服务。将来使用 TranslateKit,只需拖放 String Catalog 文件,让它处理翻译,真的很容易。

请注意,TranslateKit 正在积极开发中。相比之下,BartyCrouch 仅由社区志愿者维护更新。

BartyCrouch

BartyCrouch 从您的代码 Interface Builder 文件中增量更新您的 Strings 文件。“增量”意味着 BartyCrouch 默认情况下会保留您已经翻译的值,甚至您修改过的注释。此外,您还可以使用 BartyCrouch 从一种语言机器翻译到 60 多种其他语言。使用 BartyCrouch 非常简单,只需从命令行运行一些简单的命令,甚至可以使用项目中的构建脚本自动化

查看 这篇博客文章,了解如何在您的项目中有效地使用 BartyCrouch。

要求

入门

安装

通过 Homebrew

要首次安装 Bartycrouch,只需运行命令

brew install bartycrouch

更新到最新版本的 BartyCrouch(如果您已安装旧版本),请运行

brew upgrade bartycrouch
通过 Mint

安装或更新到最新版本的 BartyCrouch,只需运行此命令

mint install FlineDev/BartyCrouch

配置

要为您的项目配置 BartyCrouch,首先在您的项目根目录中创建一个配置文件。BartyCrouch 可以为您完成此操作

bartycrouch init

现在您应该有一个名为 .bartycrouch.toml 的文件,其中包含以下内容

[update]
tasks = ["interfaces", "code", "transform", "normalize"]

[update.interfaces]
paths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
defaultToBase = false
ignoreEmptyStrings = false
unstripped = false
ignoreKeys = ["#bartycrouch-ignore!", "#bc-ignore!", "#i!"]

[update.code]
codePaths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
localizablePaths = ["."]
defaultToKeys = false
additive = true
unstripped = false
ignoreKeys = ["#bartycrouch-ignore!", "#bc-ignore!", "#i!"]

[update.transform]
codePaths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
localizablePaths = ["."]
transformer = "foundation"
supportedLanguageEnumPath = "."
typeName = "BartyCrouch"
translateMethodName = "translate"

[update.normalize]
paths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
sourceLocale = "en"
harmonizeWithSource = true
sortByKeys = true

[lint]
paths = ["."]
subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"]
duplicateKeys = true
emptyValues = true

这是 BartyCrouch 的默认配置,应该适用于大多数项目。为了最大限度地使用 BartyCrouch,建议考虑进行以下更改

  1. 为了显着加快速度,请为任何包含 path 的键提供更具体的路径(如果可能,尤其是在 update.transform 部分,例如 ["App/Sources"] 用于 codePaths["App/Supporting Files"] 用于 supportedLanguageEnumPaths)。
  2. 如果您的项目仅使用 Swift,并且您使用了新的 transform 更新任务,请删除 code 任务。
  3. 如果您正在使用 SwiftGenstructured-swift4 模板,您可能需要使用 transform 任务,并将其 transformer 选项更改为 swiftgenStructured
  4. 如果您决定使用 transform 任务,请在您的项目中创建一个新文件(例如在 SupportingFiles 下),命名为 BartyCrouch.swift 并复制以下代码
//  This file is required in order for the `transform` task of the translation helper tool BartyCrouch to work.
//  See here for more details: https://github.com/FlineDev/BartyCrouch

import Foundation

enum BartyCrouch {
    enum SupportedLanguage: String {
        // TODO: remove unsupported languages from the following cases list & add any missing languages
        case arabic = "ar"
        case chineseSimplified = "zh-Hans"
        case chineseTraditional = "zh-Hant"
        case english = "en"
        case french = "fr"
        case german = "de"
        case hindi = "hi"
        case italian = "it"
        case japanese = "ja"
        case korean = "ko"
        case malay = "ms"
        case portuguese = "pt-BR"
        case russian = "ru"
        case spanish = "es"
        case turkish = "tr"
    }

    static func translate(key: String, translations: [SupportedLanguage: String], comment: String? = nil) -> String {
        let typeName = String(describing: BartyCrouch.self)
        let methodName = #function

        print(
            "Warning: [BartyCrouch]",
            "Untransformed \(typeName).\(methodName) method call found with key '\(key)' and base translations '\(translations)'.",
            "Please ensure that BartyCrouch is installed and configured correctly."
        )

        // fall back in case something goes wrong with BartyCrouch transformation
        return "BC: TRANSFORMATION FAILED!"
    }
}
  1. 如果您不以英语作为第一本地化语言进行开发,则应更新 normalize 任务的 sourceLocale
  2. 如果您想使用 BartyCrouch 的机器翻译功能,请将 translate 添加到顶部的任务列表中,并将以下部分复制到配置文件中,并将 secret 替换为您的 Microsoft Translator Text API 订阅密钥
[update.translate]
paths = "."
translator = "microsoftTranslator"
secret = "<#Subscription Key#>"
sourceLocale = "en"

使用

在使用 BartyCrouch 之前,请确保您已提交代码。此外,我们强烈建议使用下面描述的构建脚本方法


bartycrouch 接受以下子命令之一

还可以提供以下命令行选项

update 子命令

update 子命令可以与以下一个或多个任务一起运行

为了配置要执行的任务,请编辑配置文件中的此部分

[update]
tasks = ["interfaces", "code", "transform", "normalize"]
interfaces 的选项
code 的选项
transform 的选项
translate 的选项
normalize 的选项

lint 子命令

lint 子命令旨在分析项目中的典型翻译问题。当前的检查包括

请注意,lint 命令可以同时在 CI 和 Xcode 中通过构建脚本方法使用

通过 transform 的本地化工作流程

当配置了 transform 更新任务时(请参阅上面配置部分中的建议步骤 4)并且您正在使用 构建脚本方法,您可以使用以下简化的流程在开发期间编写本地化代码

  1. 您可以使用 BartyCrouch.translate 并指定键、翻译(如果有)以及可选的注释,而不是 NSLocalizedString 调用。例如
self.title = BartyCrouch.translate(key: "onboarding.first-page.header-title",  translations: [.english: "Welcome!"])
  1. 一旦您构建应用程序,BartyCrouch 将自动将新的翻译键添加到您的所有 Localizable.strings 文件,并将提供的翻译添加为所提供语言的值。
  2. 此外,在同一构建过程中,BartyCrouch 将根据您的 transformer 选项设置,自动将上述对 BartyCrouch.translate 的调用替换为正确的翻译调用。

结果代码取决于您的 transformer 选项设置

当设置为 foundation 时,上面的代码将转换为

self.title = NSLocalizedString("onboarding.first-page.header-title", comment: "")

当设置为 swiftgenStructured 时,它将转换为

self.title = L10n.Onboarding.FirstPage.headerTitle

transform 相对于 code 任务的优势

transform 相对于 code 任务的劣势

注意:截至 BartyCrouch 4.x 版本,此自动功能不支持格式化的本地化 Strings。

本地化 LocalizableStringResource 类型的字符串 (AppIntents, ...)

从历史上看,Apple 平台使用 CFCopyLocalizedStringNSLocalizedString 宏及其变体来标记代码中使用的字符串以进行本地化,并在运行时从 Localizable.strings 文件加载其本地化版本。

自从引入 AppIntents 框架以来,代码中的本地化字符串也可以键入为 LocalizedStringResource,并且不再显式标记。

让我们检查一下这个 AppIntents 代码片段

struct ExportAllTransactionsIntent: AppIntent {
    static var title: LocalizedStringResource = "Export all transactions"

    static var description =
        IntentDescription("Exports your transaction history as CSV data.")
}

在上面的示例中,"Export all transactions""Exports your transaction history as CSV data." 实际上都是 StaticString 实例,它们将在编译期间转换为 LocalizedStringResource 实例,并在运行时从 Localized.strings 文件中查找其各自的本地化,方式与过去使用 NSLocalizedString 时相同。唯一的例外是这些字符串没有被显式标记,并且需要 swift 编译器来解析和提取这些字符串以进行本地化。这就是 Xcode 从版本 13 开始使用 Product -> Export Localizations... 选项时所做的事情。

为了继续使用 bartycrouch 翻译这些字符串,需要使用 LocalizedStringResource(_: String, comment: String) 调用显式标记它们,并在 code 任务选项中指定 customFunction="LocalizedStringResource"

可以使用 bartycrouch 本地化的示例 AppIntents 代码如下所示

struct ExportAllTransactionsIntent: AppIntent {
    static var title = LocalizedStringResource("Export all transactions", comment: "")

    static var description =
        IntentDescription(LocalizedStringResource("Exports your transaction history as CSV data.", comment: ""))
}

请注意,您必须使用 LocalizedStringResource(_: StaticString, comment: StaticString) 的完整形式才能用于 bartycrouch,或者更具体地说是用于 extractLocStrings(请参阅 xcrun extractLocStrings)以正确解析字符串。

构建脚本

为了真正从 BartyCrouch 更新和 lint 您的 .strings 文件的能力中获益,您可以使其成为 Xcode 中开发工作流程的自然组成部分。为此,请选择您的目标,选择“构建阶段”选项卡,然后单击该窗格左上角的 + 按钮。选择“新建运行脚本阶段”,并将以下内容复制到新运行脚本阶段的“Shell: /bin/sh”下方的文本框中

export PATH="$PATH:/opt/homebrew/bin"

if which bartycrouch > /dev/null; then
    bartycrouch update -x
    bartycrouch lint -x
else
    echo "warning: BartyCrouch not installed, download it from https://github.com/FlineDev/BartyCrouch"
fi

接下来,通过拖放移动 BartyCrouch 脚本,确保它在步骤“编译源文件”(和“SwiftGen”,如果使用)之前运行,例如紧跟在“目标依赖项”之后。

现在,BartyCrouch 将在每次构建时运行,您将无需(再次)手动调用它。此外,所有未安装 BartyCrouch 的同事都会看到警告,其中包含有关如何安装它的提示。

注意:使用构建脚本方法时,请确保定期使用源代码控制提交您的代码。


从本地化中排除特定视图/NSLocalizedStrings

有时您可能想要忽略某些特定的视图,其中包含可本地化的文本,例如因为它们的值将以编程方式设置

对于这些情况,您只需在基本本地化的 Storyboard/XIB 文件中的值中包含 #bartycrouch-ignore! 或简写 #bc-ignore!。或者,您可以在实用工具窗格的“本地化器注释”框中添加 #bc-ignore!

这将告诉 BartyCrouch 在更新您的 .strings 文件时忽略此特定视图。

这是一个 XIB 文件中具有部分忽略字符串的基本本地化视图的外观示例

这是一个带有替代注释变体的示例

您还可以在 NSLocalizedString 宏注释部分中使用 #bc-ignore! 来忽略它们,以便它们不会添加到您的 Localizable.strings 中。当您使用 .stringsdict 文件来处理复数形式时,这可能会有所帮助(请参阅 文档)。

例如,您可以这样做

func updateTimeLabel(minutes: Int) {
  String.localizedStringWithFormat(NSLocalizedString("%d minute(s) ago", comment: "pluralized and localized minutes #bc-ignore!"), minutes)
}

%d minute(s) ago 键将从 Localizable.stringsdict 文件中获取,而不是从 Localizable.strings 中获取,这就是为什么它应该被 BartyCrouch 忽略。

捐赠

BartyCrouch 是由 Cihat Gündüz 在他的空闲时间为您带来的。如果您想感谢我并支持这个项目的开发,请PayPal 上进行小额捐赠。如果您也喜欢我的其他 开源贡献文章,请考虑通过GitHub 上成为赞助者Patreon 上成为赞助人来激励我。

非常感谢您的任何捐赠,它真的很有帮助!💯

迁移指南

请参阅文件 MIGRATION_GUIDES.md

贡献

欢迎贡献。欢迎在 GitHub 上提出您想法的问题,或者自己实现一个想法并发布拉取请求。如果您想贡献代码,请尝试在您的提交消息中遵循相同的语法和语义(请参阅 此处 的理由)。此外,请确保在 CHANGELOG.md 文件中添加一个条目,解释您的更改。

为了使测试能够运行构建问题,您需要运行 – 并在新文件中添加 API 密钥以运行翻译测试。

cp Tests/BartyCrouchTranslatorTests/Secrets/secrets.json.sample Tests/BartyCrouchTranslatorTests/Secrets/secrets.json

发布后检查清单

  1. 运行 make portable_zip 以生成 .build/release/portable_bartycrouch.zip
  2. 使用新的 CHANGELOG.md 部分中的文本创建新版本,并将 portable_bartycrouch.zip 作为二进制文件附加
  3. 运行 pod trunk push 以使 CocoaPods 知道新版本
  4. 更新 Formula/bartycrouch.rb 中的 tagrevision,提交并推送更改
  5. 运行 brew bump-formula-pr bartycrouch --tag=<tag> --revision=<revision>

许可证

此库在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE。