前言自从我从头开始一个需要支持多种语言的新项目以来已经有一段时间了。当然不是从头开始,而是在代码库中使用Swift包将代码分离到不同的模块中。我想提醒自己记住本地化实施中的许多过程,所以我想写一篇文章会更好,以便下次启动类似项目时可以参考。开始吧!让我们看一下代码库的简化版本。它由一个Xcode项目、一个应用程序目标(将运行的目标)和一个名为Features的Swift包组成。后者将包含应用程序中所有页面的代码,每个页面将定义为自己的产品:Package.swift//swift-tools-version:5.6importPackageDescriptionletpackage=Package(name:"Features",products:[.库(名称:“主页”,目标:[“主页”]),.library(名称:“详细信息”,目标:[“详细信息”])],依赖项:[],目标:[.target(名称:“Home"),.target(name:"Detail")])这个应用目标将充当应用的组合层,其唯一目的是导入每个函数,实例化它们并协调导航。所有UI、表示和业务逻辑都将保留在它们各自的“模块”中(FeaturesSwiftPackage中的一个目标)。这将允许每个功能独立开发并与其他功能完全隔离。为简单起见,此示例中只有两个函数:Home和Details,它们代表应用程序中仅有的两个页面。主页有一个允许用户导航到详细信息页面的按钮,以及一个显示用户当前区域的语言代码的标签。详情页只显示一个标签,与首页标签显示的信息一致:加一个字符串!看起来不错,但现在显示的信息是硬编码的英文字符串。该应用程序需要将内容翻译成另外两种语言:加泰罗尼亚语和西班牙语。虽然有多种实现,但我更喜欢每个函数(或页面)只包含它需要的本地化字符串,这样可以增加函数的可移植性和可重用性。这可以在Swift包中完成,方法是将所有必需的.lproj文件和所有需要本地化的内容(当前示例中只有Localizable.strings文件)放在目标文件夹下——我的习惯是将父Resources/文件放在文件夹,并将这些资源定义为Package.swift的特定目标。添加文件后构建函数将导致编译器抛出以下错误:这是因为defaultLocalization必须由Package.swift提供。所有功能都来自一个包,因此只能有一个defaultLocalization。这是添加本地化后Package.swift的样子:Package.swift//swift-tools-version:5.6importPackageDescriptionletpackage=Package(name:"Features",defaultLocalization:"en",platforms:[.iOS(.v15)],产品:[.library(name:"Home",targets:["Home"]),.library(name:"Detail",targets:["Detail"])],dependencies:[],targets:[.target(name:"Home",dependencies:[],resources:[.process("Resources/")]),.target(name:"Detail",resources:[.process("Resources/")]])])注意:如果没有为默认本地化代码提供本地化内容,编译器将显示警告。这对于确保您不发布带有基本本地化版本的软件包非常有帮助。缺少默认本地化时显示Xcode警告。支持本地化可能与你想的相反,将设备系统语言设置为加泰罗尼亚语或西班牙语并运行应用程序内容仍以英语显示。原因是Swift包需要额外的信息来决定使用哪些本地化,就目前而言,如果包中包含目标内容,它们将只使用目标的基本本地化,否则使用包的默认本地化。现在有两种方法可以实现本地化:在应用程序目标中提供新的本地化或启用混合本地化。在FeaturesSwift包中启用新本地化的一种方法是将它们添加到导入功能的Xcode项目中。这可以通过转到Xcode项目,在项目设置的“信息”部分,并添加本地化支持来完成:应该注意的是,本地化至少需要一个文件(例如,一个空的Localizable.strings文件)。在这种情况下,已经有一个本地化文件,因为应用程序目标是使用UIKit构建的,并且在添加新的本地化时选择启用故事板进行本地化(如上面的视频所示)。现在这将允许包从主包中获取支持的本地化并选择要使用的相应资源。值得注意的是,如果设备有应用程序支持但包不支持的语言,后者将回退到Package.swift中提供的defaultLocalization。同样,如果应用程序不支持该语言,它也会回退到相同的值。这就是为什么将defaultLocalization设置为与主要目标基本语言相同以确保所有页面的一致性非常重要。这就是为什么我更喜欢将所有功能分组在一个Swift包下,这样所有页面上的defaultLocalization都有一个单一的真实来源。允许混合本地化虽然应用程序目标本地化是首选方法,因为它确保所有页面的一致性并且只允许几个受支持的地方,但还有另一种方法允许本地化包内容,而不必在主项目之外。这可以通过将应用程序的Info.plist文件中的CFBundleAllowMixedLocalizations值设置为YES来实现。此设置将告诉应用程序目标可以为不同的目标或功能使用不同的本地化,并且当添加新的本地化资源时,应用程序本地化将自动工作。在apptarget中启用混合本地化使用该方法时,需要注意以下几点:1.不再需要为apptarget添加本地化,??只需要在package资源中添加本地化内容的lproj即可。当用户修改语言环境时,如果您的资源包中有语言包或默认提供了Package.swift,则包也会显示语言环境的语言内容。2.支持的地区有多少,本地化的资源就有多少。这意味着没有单一的事实来源来确定整个应用程序支持哪些本地化。这可能会导致问题,例如,如果要素具有尚未应用本地化资源的本地化资源内容。在这种情况下,除了删除资源之外没有办法隐藏资源。视频链接:https://www.polpiella.dev/assets/posts/modularised-app-localisation/mixed-localisations.mp4。当用户将设备语言设置为法语时,第二点显示在上面的视频中。混合源导致不一致,因为主屏幕没有fr.lproj-所以它回落到默认的本地化源,英语。另一方面,在详细信息页面上,有可用的本地化内容,这是正确翻译字符串的原因,出于这个原因,我喜欢使用应用程序目标作为所有支持本地化的真实来源。额外提示-自动化我一直鼓励尽可能自动化检索特定包的本地化字符串的过程。如果您的应用程序有很多页面,您希望使添加本地化字符串的过程尽可能简单和轻松。我一直在使用的一种工具SwiftGen可以为各种资源(例如Localizable.strings文件)生成Swift接口。创建一个利用此可执行文件的构建工具插件可以使支持新本地化的过程更容易一些,并且跨功能保持一致。
