关于我:大厂摸鱼 + 业余独立开发,之后会输出深度技术文章 + 独立开发技巧
我的往期技术文章合集:RickeyBoy - Gitbub
我的独立开发 App:iColors - 设计灵感 配色助手
上一篇:
独立开发之 App 国际化全步骤(三):Core Data 模型升级
📡 替换代码中使用的文本
好的,前文我们提取了一些带翻译的文件,具体翻译的过程我们就跳过了。现在假设我们能够通过某种方式得到具体的翻译后的结果。
那么在此基础之上,我们需要先优化一下 Localizable.strings 的声明方式
Localizable.strings 使用专门的 Key
上一部分我们在 Localizable.strings 文件中增加了对应的翻译,如下图所示:
// Localizable (Chinese, Simplified)
"色彩主题" = "色彩主题";
// Localizable (English)
"色彩主题" = "Editor's Pick";
但是这样有一个小问题,就是对于中文(之前的默认语言)来讲,无法区分是否是处理过的翻译。比如我现在在代码中有一个类似的使用之处,我并没有办法一眼识别出,这是否是经过多语言处理过的。
Text("首页")
以及另外一个问题,我们有些时候会存在同样的中文,却因为具体使用场景不同,而需要对应不同的英文。比如同样是"会员",有些可能需要根据使用场景、UI 界面等不同因素,翻译成 "vip" 或者是 "membership"。这样如果直接用中文本身作为 key,就会产生问题。
所以,我们最好的方式还是使用单独的 Key,这样可以更规范和更准确。比如下面这样:
// Localizable (Chinese, Simplified)
"SOME_KEY" = "色彩主题";
// Localizable (English)
"SOME_KEY" = "Editor's Pick";
这样,我们需要手动去替换一下使用就可以了。
Text("SOME_KEY")
至于这里使用的 KEY,只需要保证唯一性就可以了,与此同时如果能有一定的字面意义会更好。具体的生成可以交给 ChatGPT,能够节约不少的时间。
在代码中使用 Key
好的,接下来就是我自己认为最爽的时刻了,那就是替换代码中文本文案的使用。首先,如果是 Text、Button 等系统提供的组件,是可以直接接受 Key 的:
// 直接使用 key
Text("SOME_KEY")
Button("SOME_KEY", role: .cancel) { ... }
我们想想,为什么这里直接使用 "SOME_KEY",展示的是多语言结果,而不是直接将 "SOME_KEY" 作为字符串展示出来呢?
来自 SwiftUI 文档的解释:
When you use the initializer
Text("Hello")
, SwiftUI creates aLocalizedStringKey
for you and uses that to look up a localization of theHello
string. This works becauseLocalizedStringKey
conforms toExpressibleByStringLiteral
.
能做到这一点,本质上是因为这些组件都默认实现了通过 LocalizedStringKey 作为参数进行初始化,SwiftUI 会自动通过 "SOME_KEY" 创建 LocalizedStringKey 实现多语言,而不是使用普通的 String。
extension Text {
public init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil)
}
没错,这里的 "SOME_KEY" 会被创建为 LocalizedStringKey 类型,而不是 String 类型!
理解了这一点,相应的我们如果自定义一个 CustomView,就需要使用 LocalizedStringKey 而不是 String
// 声明
struct CustomView: View {
let title: LocalizedStringKey
var body: some View {
Text(title)
}
}
当然我们也可以手动将 String 转变为 LocalizedStringKey,比如像下面这样声明
// 声明
struct CustomView: View {
let some_key: String
var body: some View {
Text(LocalizedStringKey(some_key))
}
}
检查遗漏的翻译
好了,当我们替换了所有多语言的使用之后,现在来解决之前遗留的一个问题:如果发现忘记进行多语言的文本文案?
此时我们统一使用 "SOME_KEY" 的好处就体现出来了,我们只需要检查没有直接使用中文字符串的地方即可,因为正确情况下,我们所有的代码中都不会直接出现中文字符串,只会出现 "SOME_KEY"!
下面就是检查脚本,如果有遗漏的翻译,就会被直接打印出来。原理很好理解,我会用注释尽量讲清楚:
import os
import unicodedata
project_directory = "xxxxx"
# 判断字符串中是否包含中文
def contains_chinese_characters(string):
for char in string:
if 'CJK' in unicodedata.name(char, ''):
return True
return False
# 判断是否是注释内容,如果是注释内容的话可以忽略
def is_comment(line):
line = line.strip()
return line.startswith("//") or line.startswith("/*") or ("//" in line and contains_chinese_characters(line.split("//")[1]))
# 用于统计结果
violations = []
# 遍历所有文件
for root, _, files in os.walk(project_directory):
for file in files:
if file.endswith(".swift"):
file_path = os.path.join(root, file)
with open(file_path, "r") as f:
lines = f.readlines()
for line_number, line in enumerate(lines, start=1):
# 如果不是注释,且包含中文,就记录下来
if not is_comment(line) and contains_chinese_characters(line):
violations.append((file_path, line_number, line.strip()))
# 输出
if violations:
print("Violations:")
for violation in violations:
file_path, line_number, line = violation
print(f"File: {file_path}, Line: {line_number} - String: {line}")
else:
print("No violations found.")
这样,我们遗漏的部分就能被轻松找出来了。比如会输出:
File: /.../xxxx.swift, Line: 38 - String: AlertToast(type: .regular, title: "保存失败")
查看对应的代码,是我直接使用第三方组件的部分,之前就没有被自动提取出来:
// 弹出失败提醒
AlertToast(type: .regular, title: "保存失败")
Tips:全局替换多余的 LocalizedStringKey
我们之前提到,SwiftUI 能够自动识别出 "SOME_KEY",并帮助我们创建 LocalizedStringKey,从而实现多语言。不需要手动进行额外的创建。这一个优势不仅包括 Text、Button,也包括 navigationTitle、toggle 等,也都是可以自动识别的。
// 没必要
Text(LocalizedStringKey("SOME_KEY"))
// ✅
Text("SOME_KEY")
这样就可以大大简化我们的代码。不过可能有一些开发者,在最开始实现的时候并不知道这一个技巧,或者现在读了文章之后才知道这个技巧,那该怎么办呢,难道要一个一个地方手动修改吗?
当然我们还有更好的办法,就是利用 Xcode 的正则匹配搜索,来进行批量的替换!
如图所示就可以了,两个正则表达式也很好理解,我直接贴在这里,供大家参考
// Text(LocalizedStringKey("SOME_KEY"))
Text(LocalizedStringKey("(.+?)"))
// Text("SOME_KEY")
Text("$1")
本篇讲解了如何将翻译后的内容嵌入在代码中,实操内容比较多!下一篇会继续讲解如何实现语言切换