[#707] Add localization-ready structure by default#713
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
eb173ba to
00ad0e5
Compare
c3dc801 to
791b326
Compare
00ad0e5 to
4693378
Compare
There was a problem hiding this comment.
How about using SwiftGen to generate the localization key?
extension LocalizedStringKey {
enum forceUpdate {
/// Coming Soon
static let title = LocalizedString(table: "Localizable", lookupKey: "forceUpdate.title").key
...
Text("force_update.title") -> Text(.forceUpdate.title)stencil file
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
{% if tables.count > 0 %}
import SwiftUI
// MARK: - Strings
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
{% for type in types %}
{% if type == "String" %}
_ p{{forloop.counter}}: Any
{% else %}
_ p{{forloop.counter}}: {{type}}
{% endif %}
{{ ", " if not forloop.last }}
{% endfor %}
{% endfilter %}{% endmacro %}
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
{% for type in types %}
{% if type == "String" %}
String(describing: p{{forloop.counter}})
{% elif type == "UnsafeRawPointer" %}
Int(bitPattern: p{{forloop.counter}})
{% else %}
p{{forloop.counter}}
{% endif %}
{{ ", " if not forloop.last }}
{% endfor %}
{% endfilter %}{% endmacro %}
{% macro recursiveBlock table item %}
{% for string in item.strings %}
{% if not param.noComments %}
/// {{string.translation}}
{% endif %}
{% if string.types %}
static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> LocalizedStringKey {
return LocalizedString(table: "{{table}}", lookupKey: "{{string.key}}", args: {% call argumentsBlock string.types %}).key
}
{% else %}
static let {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = LocalizedString(table: "{{table}}", lookupKey: "{{string.key}}").key
{% endif %}
{% endfor %}
{% for child in item.children %}
enum {{child.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} {
{% filter indent:2 %}{% call recursiveBlock table child %}{% endfilter %}
}
{% endfor %}
{% endmacro %}
{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %}
public extension LocalizedStringKey {
{% for table in tables %}
{% if table.name != "InfoPlist" %}
{% call recursiveBlock table.name table.levels %}
{% endif %}
{% endfor %}
}
// MARK: - Implementation Details
{% if not param.withoutSupporter %}
fileprivate func tr(_ table: String, _ key: String, _ locale: Locale = Locale.current, _ args: CVarArg...) -> String {
let path = Bundle.main.path(forResource: locale.identifier, ofType: "lproj") ?? ""
let format: String
if let bundle = Bundle(path: path) {
format = NSLocalizedString(key, tableName: table, bundle: bundle, comment: "")
} else {
format = BundleToken.bundle.localizedString(forKey: key, value: nil, table: table)
}
return String(format: format, locale: locale, arguments: args)
}
public struct LocalizedString: Hashable {
let table: String
let lookupKey: String
fileprivate let args: [CVarArg]
init(table: String, lookupKey: String, args: CVarArg...) {
self.table = table
self.lookupKey = lookupKey
self.args = args
}
var key: LocalizedStringKey {
var string = LocalizedStringKey.StringInterpolation(literalCapacity: 1, interpolationCount: 1)
string.appendLiteral(lookupKey.replacingOccurrences(of: "%@", with: ""))
args.forEach {
string.appendInterpolation("\($0)")
}
return LocalizedStringKey(stringInterpolation: string)
}
var text: String {
tr(table, lookupKey, .current, args)
}
func text(withLocale locale: Locale) -> String {
tr(table, lookupKey, locale, args)
}
public func hash(into hasher: inout Hasher) {
hasher.combine(lookupKey)
hasher.combine(table)
}
public static func == (lhs: LocalizedString, rhs: LocalizedString) -> Bool {
lhs.table == rhs.table && lhs.lookupKey == rhs.lookupKey
}
}
public extension LocalizedStringKey {
func translate(args: CVarArg...) -> String {
tr("Localizable", stringKey, .current, args)
}
private var stringKey: String {
//use reflection
let mirror = Mirror(reflecting: self)
//try to find 'key' attribute value
let attributeLabelAndValue = mirror.children.first { (arg0) -> Bool in
let (label, _) = arg0
if(label == "key"){
return true;
}
return false;
}
if(attributeLabelAndValue != nil) {
//ask for localization of found key via NSLocalizedString
return String.localizedStringWithFormat(NSLocalizedString(attributeLabelAndValue!.value as! String, comment: ""));
}
else {
return "Swift LocalizedStringKey signature must have changed. @see Apple documentation."
}
}
}
{% endif %}
{% if not param.bundle %}
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
{% endif %}
{% else %}
// No string found
{% endif %}
4693378 to
de8525f
Compare
What happened 👀
Added localization-ready structure to the SwiftUI template.
Insight 📝
Proof Of Work 📹
Simulator.Screen.Recording.-.iPhone.17.-.2026-05-04.at.16.41.20.mov