Add Swift Package plugin

This commit is contained in:
david-swift 2024-03-02 15:11:13 +01:00
parent 475e30e784
commit 3a0eda789f
18 changed files with 620 additions and 278 deletions

View File

@ -0,0 +1,7 @@
# Reference Documentation
## Enums
- [Generation](enums/Generation.md)
This file was generated by [SourceDocs](https://github.com/eneko/SourceDocs)

View File

@ -0,0 +1,10 @@
**ENUM**
# `Generation`
A type containing the generation function for the plugin.
## Methods
### `main()`
Generate the Swift code for the plugin.

View File

@ -0,0 +1,8 @@
# Reference Documentation
## Enums
- [Generation](enums/Generation.md)
- [Generation.GenerationError](enums/Generation.GenerationError.md)
This file was generated by [SourceDocs](https://github.com/eneko/SourceDocs)

View File

@ -0,0 +1,19 @@
**ENUM**
# `Generation.GenerationError`
An error that occurs during code generation.
## Cases
### `missingTranslationInDefaultLanguage(key:)`
A translation in the default language missing for a specific key.
Missing translations in other languages will cause the default language to be used.
### `unknownYMLPasingError`
An unknown error occured while parsing the YML.
### `missingDefaultLanguage`
The default language information is missing.

View File

@ -0,0 +1,81 @@
**ENUM**
# `Generation`
Generate the Swift code for the plugin and macro.
## Properties
### `indentOne`
Number of spaces for indentation 1.
### `indentTwo`
Number of spaces for indentation 2.
### `indentThree`
Number of spaces for indentation 3.
## Methods
### `getCode(yml:)`
Get the Swift code for the plugin and macro.
- Parameter yml: The YML code.
- Returns: The code.
### `generateEnumCases(dictionary:)`
Generate the cases for the `Localized` enumeration.
- Parameter dictionary: The parsed YML.
- Returns: The syntax.
### `generateStaticLocVariables(dictionary:)`
Generate the static variables and functions for the `Loc` type.
- Parameter dictionary: The parsed YML.
- Returns: The syntax.
### `generateTranslations(dictionary:defaultLanguage:)`
Generate the variables for the translations.
- Parameters:
- dictionary: The parsed YML.
- defaultLanguage: The default language.
- Returns: The syntax.
### `generateLanguageFunction(dictionary:defaultLanguage:)`
Generate the function for getting the translated string for a specified language code.
- Parameters:
- dictionary: The parsed YML.
- defaultLanguage: The default language.
- Returns: The syntax.
### `getLanguages(dictionary:)`
Get the available languages.
- Parameter dictionary: The parsed YML.
- Returns: The syntax
### `parse(key:)`
Parse the key for a phrase.
- Parameter key: The key definition including parameters.
- Returns: The key.
### `parse(translation:arguments:)`
Parse the translation for a phrase.
- Parameters:
- translation: The translation without correct escaping.
- arguments: The arguments.
- Returns: The syntax.
### `indent(_:by:)`
Indent each line of a text by a certain amount of whitespaces.
- Parameters:
- string: The text.
- count: The indentation.
- Returns: The syntax.

View File

@ -8,19 +8,6 @@ Access a specific language using `Localized.key.language`, or use `Localized.key
which automatically uses the system language on Linux, macOS and Windows.
Use `Loc.key` for a quick access to the automatically localized value.
## Properties
### `indentOne`
Number of spaces for indentation 1.
### `indentTwo`
Number of spaces for indentation 2.
### `indentThree`
Number of spaces for indentation 3.
## Methods
### `expansion(of:in:)`
@ -29,57 +16,3 @@ Expand the `localized` macro.
- node: Information about the macro call.
- context: The expansion context.
- Returns: The enumerations `Localized` and `Loc`.
### `generateEnumCases(dictionary:)`
Generate the cases for the `Localized` enumeration.
- Parameter dictionary: The parsed YML.
- Returns: The syntax.
### `generateStaticLocVariables(dictionary:)`
Generate the static variables and functions for the `Loc` type.
- Parameter dictionary: The parsed YML.
- Returns: The syntax.
### `generateTranslations(dictionary:)`
Generate the variables for the translations.
- Parameter dictionary: The parsed YML.
- Returns: The syntax.
### `generateLanguageFunction(dictionary:defaultLanguage:)`
Generate the function for getting the translated string for a specified language code.
- Parameters:
- dictionary: The parsed YML.
- defaultLanguage: The syntax for the default language.
- Returns: The syntax.
### `getLanguages(dictionary:)`
Get the available languages.
- Parameter dictionary: The parsed YML.
- Returns: The syntax
### `parse(key:)`
Parse the key for a phrase.
- Parameter key: The key definition including parameters.
- Returns: The key.
### `parse(translation:arguments:)`
Parse the translation for a phrase.
- Parameters:
- translation: The translation without correct escaping.
- arguments: The arguments.
- Returns: The syntax.
### `indent(_:by:)`
Indent each line of a text by a certain amount of whitespaces.
- Parameters:
- string: The text.
- count: The indentation.
- Returns: The syntax.

View File

@ -7,3 +7,11 @@ Find documentation for the `localized` macro [here](LocalizedMacros/README.md).
## Localized
Find documentation for the `System` enumeration [here](Localized/README.md).
## Generation
Find documentation for the generation executable used by the plugin [here](Generation/README.md).
## GenerationLibrary
Find documentation for the generation used by the plugin and macro [here](GenerationLibrary/README.md).

View File

@ -1,6 +1,8 @@
docs:
@sourcedocs generate --min-acl private -r --spm-module Localized --output-folder Documentation/Localized
@sourcedocs generate --min-acl private -r --spm-module LocalizedMacros --output-folder Documentation/LocalizedMacros
@sourcedocs generate --min-acl private -r --spm-module Generation --output-folder Documentation/Generation
@sourcedocs generate --min-acl private -r --spm-module GenerationLibrary --output-folder Documentation/GenerationLibrary
swiftlint:
@swiftlint --autocorrect

View File

@ -17,6 +17,10 @@ let package = Package(
.library(
name: "Localized",
targets: ["Localized"]
),
.plugin(
name: "GenerateLocalized",
targets: ["GenerateLocalized"]
)
],
dependencies: [
@ -25,13 +29,32 @@ let package = Package(
.package(url: "https://github.com/stackotter/swift-macro-toolkit", from: "0.3.1")
],
targets: [
.target(
name: "GenerationLibrary",
dependencies: [
.product(name: "Yams", package: "Yams")
]
),
.executableTarget(
name: "Generation",
dependencies: [
"GenerationLibrary"
]
),
.macro(
name: "LocalizedMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "MacroToolkit", package: "swift-macro-toolkit"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
.product(name: "Yams", package: "Yams")
"GenerationLibrary"
]
),
.plugin(
name: "GenerateLocalized",
capability: .buildTool(),
dependencies: [
"Generation"
]
),
.target(
@ -41,11 +64,24 @@ let package = Package(
]
),
.executableTarget(
name: "LocalizedTests",
name: "MacroTests",
dependencies: [
"Localized"
],
path: "Tests"
path: "Tests/MacroTests"
),
.executableTarget(
name: "PluginTests",
dependencies: [
"Localized"
],
path: "Tests/PluginTests",
resources: [
.process("Localized.yml")
],
plugins: [
"GenerateLocalized"
]
)
]
)

View File

@ -0,0 +1,39 @@
//
// Plugin.swift
// Localized
//
// Created by david-swift on 02.03.24.
//
import Foundation
import PackagePlugin
/// The build tool plugin for generating Swift code from the `Localized.yml` file.
@main
struct Plugin: BuildToolPlugin {
/// Create the commands for generating the code.
/// - Parameters:
/// - context: The plugin context.
/// - target: The target.
/// - Returns: The commands.
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
guard let target = target.sourceModule,
let inputFile = target.sourceFiles.first(
where: { ["Localized.yml", "Localized.yaml"].contains($0.path.lastComponent) }
) else {
return []
}
let outputFile = context.pluginWorkDirectory.appending(subpath: "Localized.swift")
return [
.buildCommand(
displayName: "Generating Localized.swift",
executable: try context.tool(named: "Generation").path,
arguments: [inputFile.path.string, outputFile.string],
inputFiles: [inputFile.path],
outputFiles: [outputFile]
)
]
}
}

View File

@ -13,7 +13,7 @@
</a>
</p>
_Localized_ provides a macro for localizing cross-platform Swift code.
_Localized_ provides a Swift package plugin for localizing cross-platform Swift code.
Use YML syntax for defining available phrases:
@ -60,7 +60,44 @@ print(Localized.house.fr)
### Definition
Define the available phrases using YML.
Define the available phrases in a file called `Localized.yml`.
```yml
default: en
export:
en: Export Document
de: Exportiere das Dokument
send(message, name):
en: Send (message) to (name).
de: Sende (message) to (name).
```
As you can see, you can add parameters using brackets after the key.
The line `default: en` sets English as the fallback language.
Then, add the `Localized` dependency, the plugin and the `Localized.yml` resource
to the target in the `Package.swift` file.
```swift
.executableTarget(
name: "PluginTests",
dependencies: ["Localized"],
resources: [.process("Localized.yml")],
plugins: ["GenerateLocalized"]
)
```
<details>
<summary> Use the Swift macro alternatively </summary>
If you don't want to have a separate `Localized.yml` resource, you can use the
YML syntax directly in your Swift code using a Swift macro.
Leave out the `resources` and `plugins` lines in the target definition, and
instead of creating a `Localized.yml` file, use the following macro in a Swift file.
```swift
#localized(default: "en", yml: """
@ -74,7 +111,9 @@ send(message, name):
""")
```
As you can see, you can add parameters using brackets after the key.
You cannot have a `defaultLanguage` set in the YML, instead, use the macro parameter.
</details>
### Usage

View File

@ -0,0 +1,26 @@
//
// Generation.swift
// Localized
//
// Created by david-swift on 02.03.2024.
//
import Foundation
import GenerationLibrary
/// A type containing the generation function for the plugin.
@main
public enum Generation {
/// Generate the Swift code for the plugin.
public static func main() throws {
let yml = try String(contentsOfFile: CommandLine.arguments[1])
let content = try GenerationLibrary.Generation.getCode(yml: yml)
let outputPathIndex = 2
FileManager.default.createFile(
atPath: CommandLine.arguments[outputPathIndex],
contents: .init(("import Localized" + "\n\n" + content[0] + "\n\n" + content[1]).utf8)
)
}
}

View File

@ -0,0 +1,247 @@
//
// Generation.swift
// Localized
//
// Created by david-swift on 02.03.2024.
//
import Yams
/// Generate the Swift code for the plugin and macro.
public enum Generation {
/// Number of spaces for indentation 1.
static let indentOne = 4
/// Number of spaces for indentation 2.
static let indentTwo = 8
/// Number of spaces for indentation 3.
static let indentThree = 12
/// An error that occurs during code generation.
public enum GenerationError: Error {
/// A translation in the default language missing for a specific key.
/// Missing translations in other languages will cause the default language to be used.
case missingTranslationInDefaultLanguage(key: String)
/// An unknown error occured while parsing the YML.
case unknownYMLPasingError
/// The default language information is missing.
case missingDefaultLanguage
}
/// Get the Swift code for the plugin and macro.
/// - Parameter yml: The YML code.
/// - Returns: The code.
public static func getCode(yml: String) throws -> [String] {
guard var dict = try Yams.load(yaml: yml) as? [String: Any] else {
throw GenerationError.unknownYMLPasingError
}
guard let defaultLanguage = dict["default"] as? String else {
throw GenerationError.missingDefaultLanguage
}
dict["default"] = nil
guard let dictionary = dict as? [String: [String: String]] else {
throw GenerationError.unknownYMLPasingError
}
return [
"""
enum Localized {
static var yml: String {
\"""
\(indent(yml, by: indentTwo))
\"""
}
\(generateEnumCases(dictionary: dictionary))
var string: String { string(for: System.getLanguage()) }
\(try generateTranslations(dictionary: dictionary, defaultLanguage: defaultLanguage))
\(generateLanguageFunction(dictionary: dictionary, defaultLanguage: defaultLanguage))
}
""",
"""
enum Loc {
\(generateStaticLocVariables(dictionary: dictionary))
}
"""
]
}
/// Generate the cases for the `Localized` enumeration.
/// - Parameter dictionary: The parsed YML.
/// - Returns: The syntax.
static func generateEnumCases(dictionary: [String: [String: String]]) -> String {
var result = ""
for entry in dictionary {
let key = parse(key: entry.key)
if key.1.isEmpty {
result.append("""
case \(entry.key)
""")
} else {
var line = "case \(key.0)("
for argument in key.1 {
line += "\(argument): String, "
}
line.removeLast(", ".count)
line += ")"
result.append("""
\(line)
""")
}
}
return result
}
/// Generate the static variables and functions for the `Loc` type.
/// - Parameter dictionary: The parsed YML.
/// - Returns: The syntax.
static func generateStaticLocVariables(dictionary: [String: [String: String]]) -> String {
var result = ""
for entry in dictionary {
let key = parse(key: entry.key)
if key.1.isEmpty {
result.append("""
static var \(entry.key): String { Localized.\(entry.key).string }
""")
} else {
var line = "static func \(key.0)("
for argument in key.1 {
line += "\(argument): String, "
}
line.removeLast(", ".count)
line += ") -> String {\n" + indent("Localized.\(key.0)(", by: indentOne)
for argument in key.1 {
line += "\(argument): \(argument), "
}
line.removeLast(", ".count)
line += ").string"
line += "\n}"
result.append("""
\(line)
""")
}
}
return result
}
/// Generate the variables for the translations.
/// - Parameters:
/// - dictionary: The parsed YML.
/// - defaultLanguage: The default language.
/// - Returns: The syntax.
static func generateTranslations(dictionary: [String: [String: String]], defaultLanguage: String) throws -> String {
var result = ""
for language in getLanguages(dictionary: dictionary) {
var variable = indent("var \(language): String {", by: indentOne)
variable += indent("\nswitch self {", by: indentTwo)
for entry in dictionary {
let key = parse(key: entry.key)
guard let valueForLanguage = entry.value[language] ?? entry.value[defaultLanguage] else {
throw GenerationError.missingTranslationInDefaultLanguage(key: key.0)
}
if key.1.isEmpty {
variable += indent("\ncase .\(entry.key):", by: indentTwo)
variable += indent("\n\"\(valueForLanguage)\"", by: indentThree)
} else {
let translation = parse(translation: valueForLanguage, arguments: key.1)
variable += indent("\ncase let .\(entry.key):", by: indentTwo)
variable += indent("\n\"\(translation)\"", by: indentThree)
}
}
variable += indent("\n }\n}", by: indentOne)
result += """
\(variable)
"""
}
return result
}
/// Generate the function for getting the translated string for a specified language code.
/// - Parameters:
/// - dictionary: The parsed YML.
/// - defaultLanguage: The default language.
/// - Returns: The syntax.
static func generateLanguageFunction(
dictionary: [String: [String: String]],
defaultLanguage: String
) -> String {
var result = "func string(for language: String) -> String {\n"
for language in getLanguages(dictionary: dictionary) where language != defaultLanguage {
result += indent("if language.hasPrefix(\"\(language)\") {", by: indentTwo)
result += indent("\nreturn \(language)", by: indentThree)
result += indent("\n} else", by: indentTwo)
}
result += """
{
return \(defaultLanguage)
}
}
"""
return result
}
/// Get the available languages.
/// - Parameter dictionary: The parsed YML.
/// - Returns: The syntax
static func getLanguages(dictionary: [String: [String: String]]) -> [String] {
var languages: Set<String> = []
for key in dictionary {
languages = languages.union(key.value.map { $0.key })
}
return .init(languages)
}
/// Parse the key for a phrase.
/// - Parameter key: The key definition including parameters.
/// - Returns: The key.
static func parse(key: String) -> (String, [String]) {
let parts = key.split(separator: "(")
if parts.count == 1 {
return (key, [])
}
let arguments = parts[1].dropLast().split(separator: ", ").map { String($0) }
return (.init(parts[0]), arguments)
}
/// Parse the translation for a phrase.
/// - Parameters:
/// - translation: The translation without correct escaping.
/// - arguments: The arguments.
/// - Returns: The syntax.
static func parse(translation: String, arguments: [String]) -> String {
var translation = translation
for argument in arguments {
translation.replace("(\(argument))", with: "\\(\(argument))")
}
return translation
}
/// Indent each line of a text by a certain amount of whitespaces.
/// - Parameters:
/// - string: The text.
/// - count: The indentation.
/// - Returns: The syntax.
static func indent(_ string: String, by count: Int) -> String {
.init(
string
.components(separatedBy: "\n")
.map { "\n" + Array(repeating: " ", count: count).joined() + $0 }
.joined()
.trimmingPrefix("\n")
)
}
}

View File

@ -5,12 +5,7 @@
// Created by david-swift on 27.02.2024.
//
/// A macro that produces both a value and a string containing the
/// source code that generated the value. For example,
///
/// #stringify(x + y)
///
/// produces a tuple `(x + y, "x + y")`.
/// A macro that takes the YML syntax and converts it into enumerations.
@freestanding(declaration, names: named(Localized), named(Loc))
public macro localized(default defaultLanguage: String, yml: String) = #externalMacro(
module: "LocalizedMacros",

View File

@ -5,12 +5,10 @@
// Created by david-swift on 27.02.2024.
//
// swiftlint:disable force_unwrapping force_cast
import GenerationLibrary
import MacroToolkit
import SwiftSyntax
import SwiftSyntaxMacros
import Yams
/// Implementation of the `localized` macro, which takes YML
/// as a string and converts it into two enumerations.
@ -19,13 +17,6 @@ import Yams
/// Use `Loc.key` for a quick access to the automatically localized value.
public struct LocalizedMacro: DeclarationMacro {
/// Number of spaces for indentation 1.
static let indentOne = 4
/// Number of spaces for indentation 2.
static let indentTwo = 8
/// Number of spaces for indentation 3.
static let indentThree = 12
/// The errors the expansion can throw.
public enum LocalizedError: Error {
@ -45,199 +36,16 @@ public struct LocalizedMacro: DeclarationMacro {
of node: some SwiftSyntax.FreestandingMacroExpansionSyntax,
in context: some SwiftSyntaxMacros.MacroExpansionContext
) throws -> [SwiftSyntax.DeclSyntax] {
guard let `default` = node.argumentList.first?.expression.as(StringLiteralExprSyntax.self) else {
guard let `default` = node.argumentList.first?.expression.as(StringLiteralExprSyntax.self),
let defaultLanguage = StringLiteral(`default`).value?.description else {
throw LocalizedError.invalidDefaultLanguage
}
guard let syntax = node.argumentList.last?.expression.as(StringLiteralExprSyntax.self) else {
guard let syntax = node.argumentList.last?.expression.as(StringLiteralExprSyntax.self),
var yml = StringLiteral(syntax).value else {
throw LocalizedError.invalidStringLiteral
}
let dictionary = try Yams.load(yaml: StringLiteral(syntax).value!) as! [String: [String: String]]
return [
"""
enum Localized {
static var yml: String {
\"""
\(raw: indent(StringLiteral(syntax).value!.description, by: indentTwo))
\"""
yml.append("\n\ndefault: \"\(defaultLanguage)\"")
return try Generation.getCode(yml: yml).map { "\(raw: $0)" }
}
\(raw: generateEnumCases(dictionary: dictionary))
var string: String { string(for: System.getLanguage()) }
\(raw: generateTranslations(dictionary: dictionary))
\(raw: generateLanguageFunction(dictionary: dictionary, defaultLanguage: `default`))
}
""",
"""
enum Loc {
\(raw: generateStaticLocVariables(dictionary: dictionary))
}
"""
]
}
/// Generate the cases for the `Localized` enumeration.
/// - Parameter dictionary: The parsed YML.
/// - Returns: The syntax.
static func generateEnumCases(dictionary: [String: [String: String]]) -> String {
var result = ""
for entry in dictionary {
let key = parse(key: entry.key)
if key.1.isEmpty {
result.append("""
case \(entry.key)
""")
} else {
var line = "case \(key.0)("
for argument in key.1 {
line += "\(argument): String, "
}
line.removeLast(", ".count)
line += ")"
result.append("""
\(line)
""")
}
}
return result
}
/// Generate the static variables and functions for the `Loc` type.
/// - Parameter dictionary: The parsed YML.
/// - Returns: The syntax.
static func generateStaticLocVariables(dictionary: [String: [String: String]]) -> String {
var result = ""
for entry in dictionary {
let key = parse(key: entry.key)
if key.1.isEmpty {
result.append("""
static var \(entry.key): String { Localized.\(entry.key).string }
""")
} else {
var line = "static func \(key.0)("
for argument in key.1 {
line += "\(argument): String, "
}
line.removeLast(", ".count)
line += ") -> String {\n" + indent("Localized.\(key.0)(", by: indentOne)
for argument in key.1 {
line += "\(argument): \(argument), "
}
line.removeLast(", ".count)
line += ").string"
line += "\n}"
result.append("""
\(line)
""")
}
}
return result
}
/// Generate the variables for the translations.
/// - Parameter dictionary: The parsed YML.
/// - Returns: The syntax.
static func generateTranslations(dictionary: [String: [String: String]]) -> String {
var result = ""
for language in getLanguages(dictionary: dictionary) {
var variable = indent("var \(language): String {", by: indentOne)
variable += indent("\nswitch self {", by: indentTwo)
for entry in dictionary {
let key = parse(key: entry.key)
if key.1.isEmpty {
variable += indent("\ncase .\(entry.key):", by: indentTwo)
variable += indent("\n\"\(entry.value[language]!)\"", by: indentThree)
} else {
let translation = parse(translation: entry.value[language]!, arguments: key.1)
variable += indent("\ncase let .\(entry.key):", by: indentTwo)
variable += indent("\n\"\(translation)\"", by: indentThree)
}
}
variable += indent("\n }\n}", by: indentOne)
result += """
\(variable)
"""
}
return result
}
/// Generate the function for getting the translated string for a specified language code.
/// - Parameters:
/// - dictionary: The parsed YML.
/// - defaultLanguage: The syntax for the default language.
/// - Returns: The syntax.
static func generateLanguageFunction(
dictionary: [String: [String: String]],
defaultLanguage: StringLiteralExprSyntax
) -> String {
let defaultLanguage = StringLiteral(defaultLanguage).value!.description
var result = "func string(for language: String) -> String {\n"
for language in getLanguages(dictionary: dictionary) where language != defaultLanguage {
result += indent("if language.hasPrefix(\"\(language)\") {", by: indentTwo)
result += indent("\nreturn \(language)", by: indentThree)
result += indent("\n} else", by: indentTwo)
}
result += """
{
return \(defaultLanguage)
}
}
"""
return result
}
/// Get the available languages.
/// - Parameter dictionary: The parsed YML.
/// - Returns: The syntax
static func getLanguages(dictionary: [String: [String: String]]) -> [String] {
dictionary.first?.value.map { $0.key } ?? []
}
/// Parse the key for a phrase.
/// - Parameter key: The key definition including parameters.
/// - Returns: The key.
static func parse(key: String) -> (String, [String]) {
let parts = key.split(separator: "(")
if parts.count == 1 {
return (key, [])
}
let arguments = parts[1].dropLast().split(separator: ", ").map { String($0) }
return (.init(parts[0]), arguments)
}
/// Parse the translation for a phrase.
/// - Parameters:
/// - translation: The translation without correct escaping.
/// - arguments: The arguments.
/// - Returns: The syntax.
static func parse(translation: String, arguments: [String]) -> String {
var translation = translation
for argument in arguments {
translation.replace("(\(argument))", with: "\\(\(argument))")
}
return translation
}
/// Indent each line of a text by a certain amount of whitespaces.
/// - Parameters:
/// - string: The text.
/// - count: The indentation.
/// - Returns: The syntax.
static func indent(_ string: String, by count: Int) -> String {
.init(
string
.components(separatedBy: "\n")
.map { "\n" + Array(repeating: " ", count: count).joined() + $0 }
.joined()
.trimmingPrefix("\n")
)
}
}
// swiftlint:enable force_unwrapping force_cast

View File

@ -0,0 +1,43 @@
//
// Tests.swift
// Localized
//
// Created by david-swift on 27.02.2024.
//
import Foundation
import Localized
#localized(default: "en", yml: """
hello(name):
en: Hello, (name)!
de: Hallo, (name)!
fr: Salut, (name)!
house:
en: House
de: Haus
fr: Maison
helloPair(name1, name2):
en: Hello, (name1) and (name2)!
de: Hallo, (name1) und (name2)!
fr: Salut, (name1) et (name2)!
""")
/// Test cases for the `localized` macro.
@main
enum Tests {
/// Test the `localized` macro.
static func main() {
print("EN: \(Localized.hello(name: "Peter").en)")
print("DE: \(Localized.hello(name: "Ruedi").de)")
print("SYSTEM: \(Loc.hello(name: "Sams"))")
print("FR: \(Localized.house.fr)")
print("DE_CH: \(Localized.house.string(for: "de_CH"))")
print("SYSTEM: \(Localized.house.string)")
print("EN: \(Localized.helloPair(name1: "Max", name2: "Ruedi").en)")
}
}

View File

@ -0,0 +1,16 @@
default: en
hello(name):
en: Hello, (name)!
de: Hallo, (name)!
fr: Salut, (name)!
house:
en: House
de: Haus
fr: Maison
helloPair(name1, name2):
en: Hello, (name1) and (name2)!
de: Hallo, (name1) und (name2)!
fr: Salut, (name1) et (name2)!

View File

@ -0,0 +1,25 @@
//
// Tests.swift
// Localized
//
// Created by david-swift on 27.02.2024.
//
import Foundation
/// Test cases for the `GenerateLocalized` plugin.
@main
enum Tests {
/// Test the `GenerateLocalized` plugin.
static func main() {
print("EN: \(Localized.hello(name: "Peter").en)")
print("DE: \(Localized.hello(name: "Ruedi").de)")
print("SYSTEM: \(Loc.hello(name: "Sams"))")
print("FR: \(Localized.house.fr)")
print("DE_CH: \(Localized.house.string(for: "de_CH"))")
print("SYSTEM: \(Localized.house.string)")
print("EN: \(Localized.helloPair(name1: "Max", name2: "Ruedi").en)")
}
}