Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add StringCatalogEnumSample project to demonstrate use cases #25

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"originHash" : "b4bd8aca20b55ccc7f089bc7a746454cea99ac3758e577d138c983514ad5bc9c",
"pins" : [
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
"version" : "1.2.3"
}
}
],
"version" : 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// ContentView.swift
// StringCatalogEnumSample
// This class provides basic content view to test the XCS string functions
//
// Created by Yuxuan Li on 3/19/24.
//

import SwiftUI

/**
To use XString Catalog Enum
Define the key and content within Localizable.xcstrings(This could be any name you want)
Then clone the repo and make release the dirctore "StringCatalogEnum"
Once make release is succedd
"Use./xcstrings-enum-generate --xcstrings-path /Path/To/(This could use any name you want)Localizable.xcstrings --output-filename /Path/to/GeneratedStrings.swift(This name could be any name you want)
It will automatically generated an swift file and put it into the project directory
*/
//Basic Usage

struct ContentView: View {

//example variable
let userName = "Alice"
//This could be changed
let itemCount = 3

var welcomeMessage:String {
String(format: NSLocalizedString("WelcomeMessageFormat", comment: "Welcome message with user name on it"), userName)
}

var itemMessage: String {
itemCount == 1 ?
NSLocalizedString("SingleItemMessage", comment: "Message for a single item") :
String(format: NSLocalizedString("MultipleItemsMessage", comment: "Message format for multiple items"), itemCount)
}


var body: some View {
VStack(spacing:20) {

Text(welcomeMessage)
Text(itemMessage)

//This content is defined within Localizable.xcstrins.
Text(XCS.welcomeBackKey.key)
Text(XCS.testingKey.key)
}
.padding()
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This file is generated by XcodeStringEnum. Please do *NOT* update it manually.
// As a common practice, swiftLint is disabled for generated files.
// swiftlint:disable all

import SwiftUI

/// Makes it a bit easier to type.
typealias XCS = XcodeStringKey

/// Generated by StringCatalogEnum, this enum contains all existing Strin Category keys.
enum XcodeStringKey: String, CaseIterable {

// MARK: - The following cases should be manually replaced in your codebase.

/// 'en': "Hello, %@! Welcome to our app."
case welcomeMessageFormat = "WelcomeMessageFormat"
/// 'en': "This Content is for testing only"
case testingKey = "TestingKey"
/// 'en': "Welcome Back!"
case welcomeBackKey = "WelcomeBackKey"
/// 'en': "You are now have %d items."
case multipleItemsMessage = "MultipleItemsMessage"
/// 'en': "You are now have 1 item."
case singleItemMessage = "SingleItemMessage"
/// No localizations available
case helloWorld = "Hello, world!"

/// Usage: `SwiftUI.Text(XCS.yourStringCatalogKey.key)`
var key: LocalizedStringKey { LocalizedStringKey(rawValue) }

var string: String { NSLocalizedString(self.rawValue, comment: "Generated localization from String Catalog key: \(key)") }

// var text: String.LocalizationValue { String.LocalizationValue(rawValue) }
}
// swiftlint:enable all
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"sourceLanguage" : "en",
"strings" : {
"MultipleItemsMessage" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "You are now have %d items."
}
}
}
},
"SingleItemMessage" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "You are now have 1 item."
}
}
}
},
"TestingKey" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "This Content is for testing only"
}
}
}
},
"WelcomeBackKey" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Welcome Back!"
}
}
}
},
"WelcomeMessageFormat" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Hello, %@! Welcome to our app."
}
}
}
}
},
"version" : "1.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// StringCatalogEnumSampleApp.swift
// StringCatalogEnumSample
//
// Created by Yuxuan Li on 3/19/24.
//

import SwiftUI

@main
struct StringCatalogEnumSampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@

import Foundation

/// Model that helps separate the logic used in StringCatalogEnum struct.
public struct StringEnumHelper {
public init() {}

/// Creates enum cases depending on whether key == name
/// - Parameters:
/// - stringData: A dictionary containing string data.
/// - keyNameMatches: A boolean flag indicating whether the enum cases should match the keys exactly.
/// - keywordEnum: An array of raw values from the Keyword enum in StringCatalogEnum struct
public func createEnumKeys(with stringData: Localizations, keyNameMatches: Bool, keywordEnum: [String]) -> String {
var partialOutput = ""
var cases = [String]()
Expand All @@ -21,14 +15,10 @@ public struct StringEnumHelper {
continue
}

if keyNameMatches {
guard key == name else {
continue
}
} else {
guard key != name else {
continue
}
if keyNameMatches, key != name {
continue
} else if !keyNameMatches, key == name {
continue
}

guard !knownCases.contains(name) else {
Expand All @@ -37,7 +27,6 @@ public struct StringEnumHelper {
}
knownCases.append(name)

// Extract localization values and format them for comments
var localizationComments = [String]()
if let localizations = data.localizations {
for (languageCode, localization) in localizations {
Expand All @@ -48,75 +37,47 @@ public struct StringEnumHelper {
}
}

if localizationComments.isEmpty {
localizationComments.append(" /// No localizations available")
}

localizationComments = localizationComments.isEmpty ? [" /// No localizations available"] : localizationComments
let comment = localizationComments.joined(separator: "\n")

let caseString: String = if keywordEnum.contains(name) {
keyNameMatches
let caseString = keywordEnum.contains(name)
? keyNameMatches
? " case `\(name)`\n"
: " case `\(name)` = \"\(key.replacingOccurrences(of: "\n", with: ""))\"\n"
} else {
keyNameMatches
: keyNameMatches
? " case \(name)\n"
: " case \(name) = \"\(key.replacingOccurrences(of: "\n", with: ""))\"\n"
}

cases.append("\(comment)\n\(caseString)")
}

cases.sort()
cases.forEach { string in
partialOutput += string
}
cases.forEach { partialOutput += $0 }

return partialOutput
}

/// Convert a String Catalog key to a Swift variable name.
public func convertToVariableName(key: String) -> String? {
var result = key
// Check if the entire string is uppercase
if key == key.uppercased() {
result = key.lowercased()
}

// Uppercase remaining words, e.g. "an example" to "anExample";
result = result.split(separator: " ").enumerated().map { index, substring in
index == 0 ? String(substring.prefix(1)).lowercased() + substring.dropFirst() : String(substring).capitalized
}.joined()

// Leave only letters and numeric characters
result = result.components(separatedBy: CharacterSet.letters.union(CharacterSet.alphanumerics).inverted).joined()

// Remove leading numeric characters
while !result.isEmpty {
let firstLetter = result.prefix(1)
let digitsCharacters = CharacterSet(charactersIn: "0123456789")
if CharacterSet(charactersIn: String(firstLetter)).isSubset(of: digitsCharacters) {
// print("dropping first of: \(result)")
result = String(result.dropFirst())
} else {
break
}
while let firstChar = result.first, firstChar.isNumber {
result.removeFirst()
}

// Return nil if empty
guard !result.isEmpty else {
return nil
}
guard !result.isEmpty else { return nil }
guard result.count > 1 else { return result.lowercased() }

// Return lowercased string if there's only 1 character
guard result.count > 1 else {
return result.lowercased()
}

// Change the first character to lowercase
let firstLetter = result.prefix(1).lowercased()
let remainingLetters = result.dropFirst()
result = firstLetter + remainingLetters

return result
return firstLetter + remainingLetters
}
}