Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 120 additions & 19 deletions Sources/TranslationCatalogIO/KeyHierarchy+Syntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,50 @@ public extension KeyHierarchy {
func localizedStringConvertible(rootDeclaration name: String = "LocalizedStrings") -> Data {
let sourceFile = SourceFileSyntax {
CodeBlockItemListSyntax {
ImportDeclSyntax.localeSupport
ImportDeclSyntax(
path: ImportPathComponentListSyntax {
ImportPathComponentSyntax(name: TokenSyntax("LocaleSupport"))
}
)

EnumDeclSyntax.localizedStringConvertibleEnumerationDecl(for: self, named: name)
}
}

var dataStream = DataOutputStream()
sourceFile.formatted().write(to: &dataStream)
return dataStream.data
}

/// Data containing a file with a hierarchy of enums extending `LocalizedStringKey`.
func localizedStringKey() -> Data {
let sourceFile = SourceFileSyntax {
CodeBlockItemListSyntax {
ImportDeclSyntax(
path: ImportPathComponentListSyntax {
ImportPathComponentSyntax(name: TokenSyntax("SwiftUI"))
}
)

EnumDeclSyntax.stringEnumerationDecl(for: self, named: name)
ExtensionDeclSyntax(
leadingTrivia: .newlines(2),
extendedType: TypeSyntax(stringLiteral: "LocalizedStringKey")
) {
let hierarchy = self
for key in hierarchy.sortedContentsKeys {
if let content = hierarchy.contents[key] {
VariableDeclSyntax.localizedStringKey(
name: key.lowerCamelCased,
value: content.key,
comment: content.defaultValue.isEmpty ? nil : content.defaultValue
)
}
}

for node in hierarchy.sortedNodes {
EnumDeclSyntax.localizedStringKeyEnumerationDecl(for: node)
}
}
}
}

Expand All @@ -21,11 +62,22 @@ public extension KeyHierarchy {
return dataStream.data
}

func syntaxTree(rootDeclaration name: String = "LocalizedStrings") -> String {
String(
decoding: localizedStringConvertible(rootDeclaration: name),
as: UTF8.self
)
func syntaxTree(
style: SyntaxStyle = .localeSupport,
rootDeclaration name: String = "LocalizedStrings"
) -> String {
switch style {
case .localeSupport:
String(
decoding: localizedStringConvertible(rootDeclaration: name),
as: UTF8.self
)
case .swiftUI:
String(
decoding: localizedStringKey(),
as: UTF8.self
)
}
}

internal var declName: String {
Expand All @@ -43,16 +95,8 @@ extension [String] {
}
}

extension ImportDeclSyntax {
static let localeSupport: ImportDeclSyntax = ImportDeclSyntax(
path: ImportPathComponentListSyntax {
ImportPathComponentSyntax(name: TokenSyntax("LocaleSupport"))
}
)
}

extension EnumDeclSyntax {
static func stringEnumerationDecl(
static func localizedStringConvertibleEnumerationDecl(
for hierarchy: KeyHierarchy,
named name: String? = nil
) -> EnumDeclSyntax {
Expand All @@ -73,7 +117,7 @@ extension EnumDeclSyntax {
) {
for key in hierarchy.sortedContentsKeys {
if let content = hierarchy.contents[key] {
EnumCaseDeclSyntax.stringEnumerationCase(
EnumCaseDeclSyntax.localizedStringConvertibleEnumerationCase(
key: key.lowerCamelCased,
value: content.defaultValue,
comment: content.comment
Expand All @@ -89,14 +133,37 @@ extension EnumDeclSyntax {
}

for node in hierarchy.sortedNodes {
stringEnumerationDecl(for: node)
localizedStringConvertibleEnumerationDecl(for: node)
}
}
}

static func localizedStringKeyEnumerationDecl(
for hierarchy: KeyHierarchy
) -> EnumDeclSyntax {
EnumDeclSyntax(
leadingTrivia: .newlines(2),
name: TokenSyntax(stringLiteral: hierarchy.declName)
) {
for key in hierarchy.sortedContentsKeys {
if let content = hierarchy.contents[key] {
VariableDeclSyntax.localizedStringKey(
name: key.lowerCamelCased,
value: content.key,
comment: content.defaultValue.isEmpty ? nil : content.defaultValue
)
}
}

for node in hierarchy.sortedNodes {
EnumDeclSyntax.localizedStringKeyEnumerationDecl(for: node)
}
}
}
}

extension EnumCaseDeclSyntax {
static func stringEnumerationCase(key: String, value: String, comment: String?) -> EnumCaseDeclSyntax {
static func localizedStringConvertibleEnumerationCase(key: String, value: String, comment: String?) -> EnumCaseDeclSyntax {
var trivia: Trivia = []
if let comment {
trivia = [
Expand Down Expand Up @@ -147,6 +214,40 @@ extension VariableDeclSyntax {
)
}
}

static func localizedStringKey(name: String, value: String, comment: String?) -> VariableDeclSyntax {
var trivia: Trivia?
if let comment {
trivia = [
.docLineComment("/// \(comment)"),
.newlines(1),
]
}

return VariableDeclSyntax(
leadingTrivia: trivia,
modifiers: DeclModifierListSyntax([
DeclModifierSyntax(name: .keyword(.static)),
]),
bindingSpecifier: .keyword(.let)
) {
PatternBindingSyntax(
pattern: IdentifierPatternSyntax(identifier: .identifier(name)),
typeAnnotation: TypeAnnotationSyntax(
type: IdentifierTypeSyntax(name: .identifier("LocalizedStringKey"))
),
initializer: InitializerClauseSyntax(
value: StringLiteralExprSyntax(
openingQuote: .stringQuoteToken(),
segments: StringLiteralSegmentListSyntax([
.stringSegment(StringSegmentSyntax(content: .stringSegment(value))),
]),
closingQuote: .stringQuoteToken()
)
)
)
}
}
}

struct DataOutputStream: TextOutputStream, CustomStringConvertible {
Expand Down
6 changes: 6 additions & 0 deletions Sources/TranslationCatalogIO/SyntaxStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public enum SyntaxStyle: CaseIterable {
/// Enumerated structure utilizing `LocalizedStringConvertible` from the LocaleSupport package.
case localeSupport
/// Extension to SwiftUIs `LocalizedStringKey`
case swiftUI
}
26 changes: 24 additions & 2 deletions Sources/localizer/Catalog+Syntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ extension Catalog {
discussion: """
Generate an enumerated reference to strings. For example:

(Using Style: 'locale-support', default)
import LocaleSupport
enum LocalizedStrings: String, LocalizedStringConvertible {
/// Title for account screen.
case account = "Account"
Expand All @@ -26,6 +28,18 @@ extension Catalog {
}
}
}

(Using Style: 'swift-ui')
import SwiftUI
extension LocalizedStringKey {
/// Title for account screen.
static let account: LocalizedStringKey = "ACCOUNT"

enum Account {
/// Action to navigate to the previous screen.
static let back = "ACCOUNT_BACK"
}
}
""",
version: "1.0.0",
helpNames: .shortAndLong
Expand All @@ -43,6 +57,9 @@ extension Catalog {
@Option(help: "Path to catalog to use in place of the application library.")
var path: String?

@Option(help: "Generated reference style.")
var style: SyntaxStyle = .localeSupport

@Flag(help: "Reduce the instance of single-content nodes.")
var compressed: Bool = false

Expand Down Expand Up @@ -72,9 +89,14 @@ extension Catalog {
}

let syntax = if let name, !name.isEmpty {
keyHierarchy.syntaxTree(rootDeclaration: name)
keyHierarchy.syntaxTree(
style: style,
rootDeclaration: name
)
} else {
keyHierarchy.syntaxTree()
keyHierarchy.syntaxTree(
style: style
)
}

print(syntax)
Expand Down
20 changes: 20 additions & 0 deletions Sources/localizer/Extensions/SyntaxStyle+localizer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import ArgumentParser
import Foundation
import TranslationCatalogIO

extension SyntaxStyle: ExpressibleByArgument {
var argument: String {
switch self {
case .localeSupport: "locale-support"
case .swiftUI: "swift-ui"
}
}

public init?(argument: String) {
guard let style = SyntaxStyle.allCases.first(where: { $0.argument.caseInsensitiveCompare(argument) == .orderedSame }) else {
return nil
}

self = style
}
}
Loading
Loading