app根据服务区域不同,比如香港地区, 除了支持通用的简体外,还需要支持繁体和英文
基本的Xcode配置即Strings文件的创建不在此介绍
多语言主要是处理两类:文字 + 图片
调用方的实现API
"公用国际化文本".cLocal
"与当前文件同名的国际化文本".local()
"登录模块文案中的国际化".loginLocal()
"从指定table中获取国际化文本".fLocal("loginViewController")
"imageName".localImageName()// 对应语言的图片
1.通过枚举来定义app支持的语言类型
@objc
enum YFLanguage: Int {
case English = 1 //英文
case SimpleCh = 2 //简体中文
case TraditionCh = 3 //繁体中文
//默认跟随系统语言,若系统语言为中英文外的其他语言,统一设置为英文
init() {
let bundleLang = NSLocale.preferredLanguages.first ?? "en"
self = YFLanguage.language(from: bundleLang)
}
var bundleName: String {
switch self {
case .English:
return "en"
case .SimpleCh:
return "zh-Hans"
case .TraditionCh:
return "zh-Hant"
}
}
var uniformCode: String {
switch self {
case .English:
return "en"
case .SimpleCh:
return "zh-hans"
case .TraditionCh:
return "zh-hant"
}
}
private static func language(from appleLanguage: String) -> YFLanguage {
let dic: [String: YFLanguage] = [
"en": .English,
"zh-Hans": .SimpleCh,
"zh-Hant": .TraditionCh
]
var didFind: YFLanguage?
for (key, value) in dic {
if appleLanguage.hasPrefix(key) {
didFind = value
}
}
let final = didFind ?? .English
return final
}
}
2.为了对上层调用方更友好,对系统的NSLocalizedString提供的API进行二次封装,并对外提供本地语言缓存及切换语言的操作API
@objcMembers
class FCLocalization: NSObject {
static let shared = FCLocalization()
/// 获取当前语言环境下的文本
/// - Parameters:
/// - key: 待替换文本
/// - table: 国际化文本table: 如果为nil,则直接从Localizable.strings获取
/// - Returns: 当前语言环境下的文本
static func getString(key: String, table: String?) -> String {
guard let bundle = shared.currentBundle else {
return NSLocalizedString(key, tableName: table, comment: "")
}
return NSLocalizedString(key, tableName: table, bundle: bundle, comment: "")
}
/// 获取目标bundle语言环境下的文本
/// - Parameters:
/// - key: 待替换文本
/// - table: 国际化文本table: 如果为nil,则直接从Localizable.strings获取
/// - bundle: 目标语言bundle
/// - Returns: bundle对应的国际化文本
static func getString(key: String, table: String?, bundle: Bundle) -> String {
let languageBundle = shared.languageBundle(in: bundle)
return NSLocalizedString(key, tableName: table, bundle: languageBundle, comment: "")
}
static func uniformLanguageCode() -> String {
return shared.language.uniformCode
}
override init() {
super.init()
Bundle.localizationMode
}
//MARK: public
func uniformCode() -> String {
return language.uniformCode
}
/** App启动时初始化语言设置 */
func initializeLanguage() {
let set = Int(YFStorage.shared.getString(languageSetKey) ?? "0") ?? 0
let setLanguage = YFLanguage(rawValue: set)
if let unwrapedSet = setLanguage {
language = unwrapedSet
}else {
language = YFLanguage()
saveToLocal()
}
refreshConfig()
}
/// 切换语言
/// - Parameter lang: 目标语言
func changeTo(_ lang: YFLanguage?) {
guard let unwrapedLang = lang, language != unwrapedLang else { return }
language = unwrapedLang
saveToLocal()
refreshConfig()
let saveLanguageString = String(language.rawValue)
// 发送通知
NotificationCenter.default.post(name: .LanguageDidChange, object: saveLanguageString)
}
//返回国际化图片
func image(_ name: String) -> UIImage? {
let path = self.currentBundle?.path(forResource: name + "@\(Int(UIScreen.main.scale))x", ofType: "png") ?? ""
return UIImage(contentsOfFile: path)
}
func languageBundle(in bundle: Bundle) -> Bundle {
guard let path = bundle.path(forResource: language.bundleName, ofType: "lproj") else {
return bundle
}
guard let languageBundle = Bundle(path: path) else { return bundle }
return languageBundle
}
///更新语言环境后保存至本地
private func saveToLocal() {
let saveLanguageString = String(language.rawValue)
YFStorage.shared.saveString(saveLanguageString, key: languageSetKey)
}
//MARK: private
/** 根据当前语言,刷新相关配置 */
private func refreshConfig() {
let bundleName = language.bundleName
guard let path = Bundle.main.path(forResource: bundleName, ofType: "lproj") else {
currentBundle = nil
return }
let bundle = Bundle(path: path)
currentBundle = bundle
}
//MARK: property
//当前的bundle
var currentBundle: Bundle?
//当前语言
var language = YFLanguage()
//国际化语言的缓存key
private let languageSetKey = "com.fc.languageSetKey"
}
为了使调用更简洁,通过extension为String添加国际化快捷方法
extension String {
/// 返回公用国际化文本
var cLocal: String {
return FCLocalization.getString(key: self, table: nil)
}
/// 返回与当前文件同名的 国际化文本
func local(_ path: String = #file) -> String {
let fileName = getFileName(from: path)
return FCLocalization.getString(key: self, table: fileName)
}
/// 从指定table中获取国际化文本
func fLocal(_ file: String) -> String {
return FCLocalization.getString(key: self, table: file)
}
/// 从路径中获取文件名
///
/// - Parameter path: 路径
/// - Returns: 文件名
func getFileName(from path: String) -> String {
guard let lastPathComponent = path.components(separatedBy: "/").last else { return "" }
if let range = lastPathComponent.range(of: ".", options: .backwards) {
let fileName = lastPathComponent[lastPathComponent.startIndex..<range.lowerBound]
return String(fileName)
} else {
return lastPathComponent
}
}
/// 获取国际化图片
func localImage() -> UIImage? {
return FCLocalization.shared.image(self)
}
}
因为app大多是通过模块来处理的,比如首页模块、用户模块、登录模块等等,所以为了避免创建太多的xxxx.strings文件,仅通过模块创建对应的strings文件,该模块的所有类的国际化文本均存放在此模块strings中,所以对应模块的国际化调用也可通过extension为 String添加快捷方法,如下:
extension String {
/// 首页模块
///
/// - Returns: 首页模块语言本地化
func mainLocal() -> String {
return fLocal("main")
}
//调用
let text = "首页".mainLocal()
//text -> 首页 首頁 main
/// 用户模块
///
/// - Returns: 用户模块语言本地化
func userLocal() -> String {
return fLocal("User")
}
//调用
let text = "用户".mainLocal()
//text -> 用户 用戶 user
}
网友评论