Skip to content

Commit 96b2eb9

Browse files
authored
SPM Conversion (#6)
Overview of changes: - Converted the repo to a Swift package - dropped CocoaPods support, since SPM and Mint are now supported - updated installation instructions - Simplified Makefile to use swift test - Greatly improved argument parsing by using Apple's ArgumentParser package (also removed unit tests for parsing) - Writing .strings files now also works if the directory/file doesn't exist yet - Added Github Actions for running tests on push/PR
1 parent 2cc45ce commit 96b2eb9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+352
-1006
lines changed

.github/workflows/swift.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Swift
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: macos-latest
13+
14+
steps:
15+
- uses: maxim-lobanov/setup-xcode@v1
16+
with:
17+
xcode-version: latest-stable
18+
- uses: actions/checkout@v2
19+
- name: Build
20+
run: swift build -v
21+
- name: Run tests
22+
run: swift test -v

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,4 @@ fastlane/test_output
6868
*.xcarchive/
6969
Products/
7070
.DS_Store
71+
.swiftpm

.swift-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.1
1+
5.3

Makefile

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
test:
2-
xcodebuild test -project "SwiftGenStrings.xcodeproj" -scheme "SwiftGenStrings" -destination "platform=macOS"
3-
41
release:
5-
xcodebuild -scheme "SwiftGenStrings" -configuration "Release" -destination "generic/platform=macOS" clean archive -archivePath "build/"
2+
swift build -c release -v
63
mkdir -p Products
7-
cp build.xcarchive/Products/usr/local/bin/SwiftGenStrings Products/
4+
cp .build/release/SwiftGenStrings Products/SwiftGenStrings
5+
6+
install: release
7+
install -v Products/SwiftGenStrings /usr/local/bin/SwiftGenStrings
8+
9+
test:
10+
swift test -v

Package.resolved

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// swift-tools-version:5.3
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "SwiftGenStrings",
8+
platforms: [
9+
.macOS(.v10_12)
10+
],
11+
products: [
12+
// Products define the executables and libraries a package produces, and make them visible to other packages.
13+
.executable(
14+
name: "SwiftGenStrings",
15+
targets: ["SwiftGenStrings"]
16+
),
17+
],
18+
dependencies: [
19+
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.3.0")
20+
],
21+
targets: [
22+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
23+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
24+
.target(
25+
name: "SwiftGenStrings",
26+
dependencies: [
27+
"SwiftGenStringsCore",
28+
.product(name: "ArgumentParser", package: "swift-argument-parser")
29+
]
30+
),
31+
.target(
32+
name: "SwiftGenStringsCore",
33+
dependencies: []
34+
),
35+
.testTarget(
36+
name: "SwiftGenStringsTests",
37+
dependencies: ["SwiftGenStringsCore"],
38+
resources: [
39+
.process("Resources")
40+
]
41+
),
42+
]
43+
)

README.md

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,17 @@ The upstream issue is tracked [here](https://openradar.appspot.com/22133811).
1111
## Usage
1212

1313
```
14-
SwiftGenStrings files
15-
SwiftGenStrings [-s <routine>] [-o <outputDir>] files
16-
SwiftGenStrings [-h|--help]
17-
18-
OPTIONS
19-
-h|--help
20-
(Optional) Print help.
21-
-s routine
22-
(Optional) Substitute routine for NSLocalizedString, useful when different macro is used.
23-
-o outputDir
24-
(Optional) Specifies what directory Localizable.strings table is created in.
25-
Not specifying output directory will print script output content to standard output (console).
26-
files
27-
List of files, that are used as source of Localizable.strings generation.
14+
SwiftGenStrings [<files> ...] [-s <substitute>] [-o <output-directory>]
15+
16+
ARGUMENTS:
17+
<files> List of files, that are used as source of Localizable.strings generation.
18+
19+
OPTIONS:
20+
-s <substitute> (Optional) Substitute for NSLocalizedString, useful when different macro is used.
21+
-o <output-directory> (Optional) Specifies what directory Localizable.strings table is created in. Not specifying output directory will print script output content
22+
to standard output (console).
23+
--version Show the version.
24+
-h, --help Show help information.
2825
```
2926

3027
To gather strings in current directory, run:
@@ -39,6 +36,13 @@ $ find . \( -name "*.swift" ! -path "./Carthage/*" ! -path "./Pods/*" \) | xargs
3936

4037
## Installation
4138

39+
### [Mint](https://github.com/yonaskolb/mint)
40+
41+
The quickest and easiest way to install SwiftGenStrings is via Mint
42+
```
43+
$ mint install kayak/SwiftGenStrings
44+
```
45+
4246
### Prebuilt Binaries
4347

4448
We tag releases and upload prebuilt binaries to GitHub. Checkout the [releases](https://github.com/kayak/SwiftGenStrings/releases) tab or go straight to the [latest](https://github.com/kayak/SwiftGenStrings/releases/latest) release.
@@ -51,29 +55,19 @@ The project provides a `Makefile`. To export a binary run:
5155
$ make release
5256
```
5357

54-
The exported binary can be found under `Products/SwiftGenStrings`
55-
56-
### CocoaPods
57-
58-
A podspec file for the project was released (see [here](https://cocoapods.org/pods/SwiftGenStrings)). To consume the project, simply add the following to your `Podfile`:
59-
60-
```
61-
pod 'SwiftGenStrings'
62-
```
63-
64-
After running `pod install` or `pod update`, you will then find the binary under `Pods/SwiftGenStrings/SwiftGenStrings`
58+
The exported binary can be found under `Products/SwiftGenStrings`. Alternatively you can use `make install` to install the compiled library directly into `/usr/local/bin/SwiftGenStrings`
6559

6660
## Testing
6761

68-
Xcode 9.2 seems to have a bug with running tests against macOS destination, luckily, `xcodebuild` works just fine:
62+
Since SwiftGenStrings is a SPM package, running tests is easy:
6963
```
70-
$ make test
64+
$ swift test
7165
```
7266

7367
## Requirements
7468

75-
- Xcode 9.2
76-
- Swift 4.0.2
69+
- Xcode 12
70+
- Swift 5.3
7771

7872
## Limitations
7973

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import ArgumentParser
2+
import Foundation
3+
4+
extension URL: ExpressibleByArgument {
5+
6+
public init?(argument: String) {
7+
if argument.hasPrefix("~/") {
8+
let cleanedPath = String(argument.dropFirst())
9+
self.init(fileURLWithPath: cleanedPath, relativeTo: FileManager.default.homeDirectoryForCurrentUser)
10+
} else {
11+
self.init(fileURLWithPath: argument)
12+
}
13+
}
14+
15+
}

Sources/SwiftGenStrings/main.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import ArgumentParser
2+
import Foundation
3+
import SwiftGenStringsCore
4+
5+
struct SwiftGenStrings: ParsableCommand {
6+
7+
private static var abstract = """
8+
SwiftGenStrings is a command line application that can be used as a drop-in replacement for the standard genstrings command for Swift sources.
9+
The latter only supports the short form of the NSLocalizedString function but breaks as soon as you use any parameters other than key and comment as in
10+
"""
11+
12+
static let configuration = CommandConfiguration(
13+
commandName: "SwiftGenStrings",
14+
abstract: SwiftGenStrings.abstract,
15+
version: "0.0.2",
16+
helpNames: .shortAndLong
17+
)
18+
19+
@Argument(help: "List of files, that are used as source of Localizable.strings generation.")
20+
var files: [URL]
21+
22+
@Option(name: .short, help: "(Optional) Substitute for NSLocalizedString, useful when different macro is used.")
23+
var substitute: String?
24+
25+
@Option(name: .short, help: "(Optional) Specifies what directory Localizable.strings table is created in. Not specifying output directory will print script output content to standard output (console).")
26+
var outputDirectory: URL?
27+
28+
func run() throws {
29+
do {
30+
try ky_run()
31+
} catch let error as NSError {
32+
ErrorFormatter().writeFormattedError(error.localizedDescription)
33+
Darwin.exit(Int32(error.code))
34+
}
35+
}
36+
37+
private func ky_run() throws {
38+
let collectionErrorOutput = LocalizedStringCollectionStandardErrorOutput()
39+
let finalStrings = LocalizedStringCollection(strings: [], errorOutput: collectionErrorOutput)
40+
41+
let tokenizer = SwiftTokenizer()
42+
43+
var numberOfWrittenErrors = 0
44+
for file in files {
45+
let contents = try String(contentsOf: file)
46+
let tokens = tokenizer.tokenizeSwiftString(contents)
47+
let errorOutput = LocalizedStringFinderStandardErrorOutput(fileURL: file)
48+
let finder = LocalizedStringFinder(routine: substitute ?? "NSLocalizedString", errorOutput: errorOutput)
49+
let strings = finder.findLocalizedStrings(tokens)
50+
let collection = LocalizedStringCollection(strings: strings, errorOutput: collectionErrorOutput)
51+
finalStrings.merge(with: collection)
52+
numberOfWrittenErrors += errorOutput.numberOfWrittenErrors + collectionErrorOutput.numberOfWrittenErrors
53+
}
54+
55+
guard numberOfWrittenErrors == 0 else {
56+
let errorMessage = numberOfWrittenErrors == 1 ? "1 error was written" : "\(numberOfWrittenErrors) errors were written"
57+
throw NSError(description: errorMessage)
58+
}
59+
60+
let output = finalStrings.formattedContent
61+
62+
if let outputDirectory = outputDirectory {
63+
let outputFileURL = outputDirectory.appendingPathComponent("Localizable.strings")
64+
try output.ky_write(to: outputFileURL, atomically: false, encoding: .utf8)
65+
} else {
66+
print(output, terminator: "") // No newline at the end
67+
}
68+
}
69+
70+
}
71+
72+
SwiftGenStrings.main()

SwiftGenStrings/CharacterIterator.swift renamed to Sources/SwiftGenStringsCore/CharacterIterator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import Foundation
22

3-
class CharacterIterator {
3+
public class CharacterIterator {
44

55
private let characters: [Character]
66

77
private var index = -1
88

9-
init(string: String) {
9+
public init(string: String) {
1010
self.characters = string.map { $0 }
1111
}
1212

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Foundation
2+
3+
public extension NSError {
4+
5+
convenience init(domain: String = "com.kayak.travel.SwiftGenStrings", code: Int = 1, description: String) {
6+
let userInfo = [NSLocalizedDescriptionKey: description]
7+
self.init(domain: domain, code: code, userInfo: userInfo)
8+
}
9+
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Foundation
2+
3+
public extension String {
4+
5+
func ky_write(to url: URL, atomically: Bool, encoding: Encoding = .utf8) throws {
6+
try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
7+
try write(to: url, atomically: atomically, encoding: .utf8)
8+
}
9+
10+
}

SwiftGenStrings/LocalizedString.swift renamed to Sources/SwiftGenStringsCore/LocalizedString.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ private let formatSpecifierRegex: NSRegularExpression = {
1818
return try! NSRegularExpression(pattern: pattern, options:[])
1919
}()
2020

21-
class LocalizedString: CustomStringConvertible, Equatable {
21+
public class LocalizedString: CustomStringConvertible, Equatable {
2222

2323
let key: String
2424
let value: String
@@ -46,24 +46,24 @@ class LocalizedString: CustomStringConvertible, Equatable {
4646
}
4747

4848
var formatted: String {
49-
return "/* \(formattedComments) */\n" +
49+
"/* \(formattedComments) */\n" +
5050
"\"\(key)\" = \"\(valueWithIndexedPlaceholders)\";"
5151
}
5252

5353
private var formattedComments: String {
54-
return Array(Set(comments)).sorted().joined(separator: "\n ")
54+
Array(Set(comments)).sorted().joined(separator: "\n ")
5555
}
5656

5757
// MARK: - CustomStringConvertible
5858

59-
var description: String {
60-
return "LocalizedString(key: \(key), value: \(value), comments: \(comments))"
59+
public var description: String {
60+
"LocalizedString(key: \(key), value: \(value), comments: \(comments))"
6161
}
6262

6363
// MARK: - Equatable
6464

65-
static func ==(lhs: LocalizedString, rhs: LocalizedString) -> Bool {
66-
return lhs.key == rhs.key && lhs.value == rhs.value && lhs.comments == rhs.comments
65+
public static func ==(lhs: LocalizedString, rhs: LocalizedString) -> Bool {
66+
lhs.key == rhs.key && lhs.value == rhs.value && lhs.comments == rhs.comments
6767
}
6868

6969
}

0 commit comments

Comments
 (0)