From 96351debdff811605b0a8fd4913b3081c780eb57 Mon Sep 17 00:00:00 2001 From: Egzon Arifi Date: Mon, 17 Jul 2023 09:52:04 +0200 Subject: [PATCH 1/6] Omit AssetGen usage (#101) --- Resources/Docs/Architecture/Domain/Localization.md | 2 +- .../Application/CommandLineParser/Logger+PrintUsage.swift | 2 +- .../CommandLineParser/CommandLineParserTests.swift | 8 ++++---- .../Processor/LocalizationProcessorTests.swift | 8 ++++---- .../Data/Configuration/ConfigTransformerTest.swift | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Resources/Docs/Architecture/Domain/Localization.md b/Resources/Docs/Architecture/Domain/Localization.md index 12692f8..1a99b5e 100644 --- a/Resources/Docs/Architecture/Domain/Localization.md +++ b/Resources/Docs/Architecture/Domain/Localization.md @@ -5,7 +5,7 @@ - `LocalizationSheet`: Represents a localization sheet containing translation entries for the app. - `LocalizationEntry`: Represents an entry with properties: section, key, plural and translations - `PluralCategory`: Represents language plural categories defined by CLDR -- `AssetGenConfig`: Represents an app configuration needed to perform localization +- `Config`: Represents an app configuration needed to perform localization - `LocalizationPlatform`: Represents localization platforms that will be supported by the app. **Use Cases**: diff --git a/Sources/Lingua/Application/CommandLineParser/Logger+PrintUsage.swift b/Sources/Lingua/Application/CommandLineParser/Logger+PrintUsage.swift index 35965eb..a95be06 100644 --- a/Sources/Lingua/Application/CommandLineParser/Logger+PrintUsage.swift +++ b/Sources/Lingua/Application/CommandLineParser/Logger+PrintUsage.swift @@ -4,7 +4,7 @@ extension Logger { func printUsage() { log(""" Usage: - AssetGen + Lingua is required only for localization functionality and can be: 1. ios diff --git a/Tests/LinguaTests/Application/CommandLineParser/CommandLineParserTests.swift b/Tests/LinguaTests/Application/CommandLineParser/CommandLineParserTests.swift index dbfd128..4832723 100644 --- a/Tests/LinguaTests/Application/CommandLineParser/CommandLineParserTests.swift +++ b/Tests/LinguaTests/Application/CommandLineParser/CommandLineParserTests.swift @@ -7,7 +7,7 @@ final class CommandLineParserTests: XCTestCase { }() func test_parse_throwsNotEnoughArgumentsError_forNotEnoughArguments() { - let arguments = ["AssetGen"] + let arguments = ["Lingua"] XCTAssertThrowsError(try sut.parse(arguments: arguments)) { error in XCTAssertEqual((error as? CommandLineParsingError)?.localizedDescription, @@ -16,7 +16,7 @@ final class CommandLineParserTests: XCTestCase { } func test_parse_throwsInvalidPlatformError_forInvalidPlatform() { - let arguments = ["AssetGen", "localization_invalid", "config.json"] + let arguments = ["Lingua", "localization_invalid", "config.json"] XCTAssertThrowsError(try sut.parse(arguments: arguments)) { error in XCTAssertEqual((error as? CommandLineParsingError)?.localizedDescription, @@ -25,7 +25,7 @@ final class CommandLineParserTests: XCTestCase { } func test_parse_throwsInvalidConfigFilePathError_forInvalidConfigFilePath() { - let arguments = ["AssetGen", "ios", "config.txt"] + let arguments = ["Lingua", "ios", "config.txt"] XCTAssertThrowsError(try sut.parse(arguments: arguments)) { error in XCTAssertEqual((error as? CommandLineParsingError)?.localizedDescription, @@ -34,7 +34,7 @@ final class CommandLineParserTests: XCTestCase { } func test_parse_parsesArgumentsCorrectly_forValidArguments() throws { - let arguments = ["AssetGen", "ios", "config.json"] + let arguments = ["Lingua", "ios", "config.json"] let commandLineArguments = try sut.parse(arguments: arguments) diff --git a/Tests/LinguaTests/Application/Processor/LocalizationProcessorTests.swift b/Tests/LinguaTests/Application/Processor/LocalizationProcessorTests.swift index 187d0f1..0743847 100644 --- a/Tests/LinguaTests/Application/Processor/LocalizationProcessorTests.swift +++ b/Tests/LinguaTests/Application/Processor/LocalizationProcessorTests.swift @@ -6,7 +6,7 @@ final class LocalizationProcessorTests: XCTestCase { let (sut, actors) = makeSUT() let tempDirectoryURL = try createTemporaryDirectoryURL() let configPath = try createTemporaryConfigFile(data: createConfigData(in: tempDirectoryURL), tempDirectoryURL: tempDirectoryURL) - let arguments = ["AssetGen", "ios", configPath.path] + let arguments = ["Lingua", "ios", configPath.path] try await sut.process(arguments: arguments) @@ -22,7 +22,7 @@ final class LocalizationProcessorTests: XCTestCase { let (sut, actors) = makeSUT() let tempDirectoryURL = try createTemporaryDirectoryURL() let configPath = try createTemporaryConfigFile(data: createInvalidConfigData(), tempDirectoryURL: tempDirectoryURL) - let arguments = ["AssetGen", "ios", configPath.path] + let arguments = ["Lingua", "ios", configPath.path] do { try await sut.process(arguments: arguments) @@ -40,7 +40,7 @@ final class LocalizationProcessorTests: XCTestCase { let (sut, actors) = makeSUT(localizationModule: localizationModule) let tempDirectoryURL = try createTemporaryDirectoryURL() let configPath = try createTemporaryConfigFile(data: createConfigData(in: tempDirectoryURL), tempDirectoryURL: tempDirectoryURL) - let arguments = ["AssetGen", "ios", configPath.path] + let arguments = ["Lingua", "ios", configPath.path] do { try await sut.process(arguments: arguments) @@ -115,7 +115,7 @@ private extension LocalizationProcessorTests { var printUsage: String { """ Usage: - AssetGen + Lingua is required only for localization functionality and can be: 1. ios diff --git a/Tests/LinguaTests/Infrastructure/Data/Configuration/ConfigTransformerTest.swift b/Tests/LinguaTests/Infrastructure/Data/Configuration/ConfigTransformerTest.swift index f4ce100..96d32cb 100644 --- a/Tests/LinguaTests/Infrastructure/Data/Configuration/ConfigTransformerTest.swift +++ b/Tests/LinguaTests/Infrastructure/Data/Configuration/ConfigTransformerTest.swift @@ -2,7 +2,7 @@ import XCTest @testable import Lingua final class ConfigTransformerTest: XCTestCase { - func test_transform_mapsAssetGenConfigDto_toEntity() throws { + func test_transform_mapsConfigDto_toEntity() throws { let dto = ConfigDto(localization: .init(apiKey: "key", sheetId: "id", outputDirectory: "path", @@ -20,7 +20,7 @@ final class ConfigTransformerTest: XCTestCase { entity.localization?.localizedSwiftCode?.outputSwiftCodeFileDirectory) } - func test_transform_mapsAssetGenConfigDto_toEntity_whenSwiftCodeIsMissing() throws { + func test_transform_mapsConfigDto_toEntity_whenSwiftCodeIsMissing() throws { let dto = ConfigDto(localization: .init(apiKey: "key", sheetId: "id", outputDirectory: "path", swiftCode: .none)) let sut = ConfigTransformer() From 94b76d06de4574aae56fccd80e3a40cd7bb4ad14 Mon Sep 17 00:00:00 2001 From: Egzon Arifi Date: Mon, 17 Jul 2023 10:10:42 +0200 Subject: [PATCH 2/6] Fix SwiftLint warnings (#103) --- .../DefaultLocalizedSwiftCodeOutputStringBuilder.swift | 5 ++++- .../DefaultLocalizedSwiftCodeOutputStringBuilderTests.swift | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilder.swift b/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilder.swift index 0b35700..940271a 100644 --- a/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilder.swift +++ b/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilder.swift @@ -11,7 +11,7 @@ struct DefaultLocalizedSwiftCodeOutputStringBuilder: LocalizedSwiftCodeOutputStr let sectionsOutput = buildSectionsOutput(sections: sections, translations: translations) let output = """ - //swiftlint:disable all + // swiftlint:disable all import Foundation @@ -33,6 +33,9 @@ struct DefaultLocalizedSwiftCodeOutputStringBuilder: LocalizedSwiftCodeOutputStr #endif }() } + + // swiftlint:enable all + """ return output } diff --git a/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilderTests.swift b/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilderTests.swift index c5b8a6f..e13afb0 100644 --- a/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilderTests.swift +++ b/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/Builder/DefaultLocalizedSwiftCodeOutputStringBuilderTests.swift @@ -23,7 +23,7 @@ final class DefaultLocalizedSwiftCodeOutputStringBuilderTests: XCTestCase { let output = sut.buildOutput(sections: sections, translations: translations) let expectedOutput = """ - //swiftlint:disable all + // swiftlint:disable all import Foundation @@ -51,6 +51,9 @@ final class DefaultLocalizedSwiftCodeOutputStringBuilderTests: XCTestCase { #endif }() } + + // swiftlint:enable all + """ XCTAssertEqual(output, expectedOutput) From 67e341bca89260c810c470e9c5b21e320d50a6bf Mon Sep 17 00:00:00 2001 From: Egzon Arifi Date: Mon, 17 Jul 2023 13:10:54 +0200 Subject: [PATCH 3/6] Enhance placeholder type extractor (#104) --- .../PlaceholderExtractor/Placeholder.swift | 13 +++-- .../PlaceholderExtractor.swift | 2 +- .../PlaceholderExtractorTests.swift | 51 +++++++++---------- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/Placeholder.swift b/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/Placeholder.swift index b132386..4d9021a 100644 --- a/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/Placeholder.swift +++ b/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/Placeholder.swift @@ -13,12 +13,17 @@ extension Placeholder { case any init(for placeholder: String) { - switch placeholder { - case "%@", "%A": + guard let identifier = placeholder.dropFirst().first else { + self = .any + return + } + + switch identifier { + case "s", "S", "@": self = .string - case "%d", "%i", "%u", "%o", "%x", "%X": + case "d", "i", "u", "o", "x", "X": self = .int - case "%f", "%F", "%e", "%E", "%g", "%G": + case "f", "F", "e", "E", "g", "G": self = .double default: self = .any diff --git a/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractor.swift b/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractor.swift index 6d066da..a03e336 100644 --- a/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractor.swift +++ b/Sources/Lingua/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractor.swift @@ -8,7 +8,7 @@ struct PlaceholderExtractor { } func extractPlaceholders(from translation: String) -> [Placeholder] { - let pattern = "(%([0-9]*\\$)?(\\d*\\$)?[@diufFeEgGxXoOcsaAbBhH]+)" + let pattern = "(%([0-9]*\\$)?(\\d*\\$)?[@\\w]+)" return strategy.extractPlaceholders(from: translation, pattern: pattern) } } diff --git a/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift b/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift index 7a2b533..eb86533 100644 --- a/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift +++ b/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift @@ -2,39 +2,38 @@ import XCTest @testable import Lingua final class PlaceholderExtractorTests: XCTestCase { - func test_extractPlaceholders_withRegexPlaceholderExtractor() { - let extractor = PlaceholderExtractor(strategy: RegexPlaceholderExtractor()) - verifyPlaceholders(extractor: extractor) + func testStringPlaceholderExtraction() { + expect(testString: "Hello %@, how are you? You've %s and %S", expectedTypes: ["String", "String", "String"]) } - func test_extractPlaceholders_withNSRegularExpressionStrategy() { - let extractor = PlaceholderExtractor(strategy: NSRegularExpressionPlaceholderExtractor()) - verifyPlaceholders(extractor: extractor) + func testIntPlaceholderExtraction() { + expect(testString: "Your age is %d and you've %x and %i", expectedTypes: ["Int", "Int", "Int"]) } - func test_extractPlaceholders_withDefaultStrategy() { - let extractor = PlaceholderExtractor.make() - verifyPlaceholders(extractor: extractor) + func testDoublePlaceholderExtraction() { + expect(testString: "The area is %f and you've %e and %G", expectedTypes: ["Double", "Double", "Double"]) + } + + func testUnknownPlaceholderExtraction() { + expect(testString: "Here is a mysterious %Z placeholder", expectedTypes: ["Any"]) + } + + func testMultiplePlaceholdersExtraction() { + expect(testString: "Hello %@, you are %d years old and your average grade is %f.", + expectedTypes: ["String", "Int", "Double"]) + } + + func testMultipleCharacterIntPlaceholderExtraction() { + expect(testString: "There are %dd and %dh, %@!", expectedTypes: ["Int", "Int", "String"]) } } - + private extension PlaceholderExtractorTests { - func verifyPlaceholders(extractor: PlaceholderExtractor) { - let translation = "Hello %@, you have %d unread messages and %f new notifications and %a." - let placeholders = extractor.extractPlaceholders(from: translation) - - XCTAssertEqual(placeholders.count, 4) - - XCTAssertEqual(placeholders[0].index, 0) - XCTAssertEqual(placeholders[0].type.asDataType, "String") - - XCTAssertEqual(placeholders[1].index, 1) - XCTAssertEqual(placeholders[1].type.asDataType, "Int") - - XCTAssertEqual(placeholders[2].index, 2) - XCTAssertEqual(placeholders[2].type.asDataType, "Double") + func expect(sut: PlaceholderExtractor = .make() ,testString: String, expectedTypes: [String]) { + let placeholders = sut.extractPlaceholders(from: testString) - XCTAssertEqual(placeholders[3].index, 3) - XCTAssertEqual(placeholders[3].type.asDataType, "Any") + for (index, expectedType) in expectedTypes.enumerated() { + XCTAssertEqual(placeholders[index].type.asDataType, expectedType) + } } } From 8b9037080d95454bb22d08c51d4cbed3afb2e08c Mon Sep 17 00:00:00 2001 From: Egzon Arifi Date: Mon, 17 Jul 2023 15:27:02 +0200 Subject: [PATCH 4/6] Cleanup unused/removed generated files on directory (#105) --- .../Localization/FileCleanupStrategy.swift | 5 +++ .../DirectoryOperable.swift | 15 +++++++++ .../Factory/FileCleanupFactory.swift | 12 +++++++ .../LocalizedFilesGeneratorFactory.swift | 4 ++- .../Generator/LocalizedFilesGenerator.swift | 7 ++-- .../Android/AndroidFileCleanupStrategy.swift | 7 ++++ .../Output/iOS/IOSFileCleanupStrategy.swift | 7 ++++ .../LocalizedFilesGeneratorTests.swift | 32 +++++++++++++++++-- .../Mock/MockDirectoryOperator.swift | 5 +++ .../PlaceholderExtractorTests.swift | 3 +- 10 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 Sources/Lingua/Domain/UseCases/Localization/FileCleanupStrategy.swift create mode 100644 Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/FileCleanupFactory.swift create mode 100644 Sources/Lingua/Infrastructure/LocalizationGenerator/Output/Android/AndroidFileCleanupStrategy.swift create mode 100644 Sources/Lingua/Infrastructure/LocalizationGenerator/Output/iOS/IOSFileCleanupStrategy.swift diff --git a/Sources/Lingua/Domain/UseCases/Localization/FileCleanupStrategy.swift b/Sources/Lingua/Domain/UseCases/Localization/FileCleanupStrategy.swift new file mode 100644 index 0000000..acae1a7 --- /dev/null +++ b/Sources/Lingua/Domain/UseCases/Localization/FileCleanupStrategy.swift @@ -0,0 +1,5 @@ +import Foundation + +protocol FileCleanupStrategy { + func removeFiles(using directoryOperator: DirectoryOperable, in folder: URL) throws +} diff --git a/Sources/Lingua/Infrastructure/DirectoryOperations/DirectoryOperable.swift b/Sources/Lingua/Infrastructure/DirectoryOperations/DirectoryOperable.swift index 98b8e5a..8e8e7c1 100644 --- a/Sources/Lingua/Infrastructure/DirectoryOperations/DirectoryOperable.swift +++ b/Sources/Lingua/Infrastructure/DirectoryOperations/DirectoryOperable.swift @@ -3,6 +3,7 @@ import Foundation protocol DirectoryOperable { func createDirectory(named: String, in outputDirectory: String) throws -> URL func removeFiles(withPrefix prefix: String, in directory: URL) throws + func removeAllFiles(in directory: URL) throws } final class DirectoryOperator: DirectoryOperable { @@ -36,6 +37,20 @@ final class DirectoryOperator: DirectoryOperable { } } } + + func removeAllFiles(in directory: URL) throws { + let fileManager = fileManagerProvider.manager + + let fileURLs = try fileManager.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) + + for fileURL in fileURLs { + do { + try fileManager.removeItem(at: fileURL) + } catch { + throw DirectoryOperationError.removeItemFailed + } + } + } } extension DirectoryOperator { diff --git a/Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/FileCleanupFactory.swift b/Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/FileCleanupFactory.swift new file mode 100644 index 0000000..1412c40 --- /dev/null +++ b/Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/FileCleanupFactory.swift @@ -0,0 +1,12 @@ +import Foundation + +struct FileCleanupFactory { + static func make(for localizationPlatform: LocalizationPlatform) -> FileCleanupStrategy { + switch localizationPlatform { + case .ios: + return IOSFileCleanupStrategy() + case .android: + return AndroidFileCleanupStrategy() + } + } +} diff --git a/Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/LocalizedFilesGeneratorFactory.swift b/Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/LocalizedFilesGeneratorFactory.swift index 39afefb..6ca9a45 100644 --- a/Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/LocalizedFilesGeneratorFactory.swift +++ b/Sources/Lingua/Infrastructure/LocalizationGenerator/Factory/LocalizedFilesGeneratorFactory.swift @@ -14,9 +14,11 @@ struct LocalizedFilesGeneratorFactory { fileNameGenerator: fileNameGenerator) let directoryManager = DirectoryOperator.makeDefault() + let fileCleanupStrategy = FileCleanupFactory.make(for: localizationPlatform) let generator = LocalizedFilesGenerator(directoryOperator: directoryManager, filesGenerator: filesGenerator, - localizationPlatform: localizationPlatform) + localizationPlatform: localizationPlatform, + fileCleanup: fileCleanupStrategy) return generator } diff --git a/Sources/Lingua/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGenerator.swift b/Sources/Lingua/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGenerator.swift index 6fa19e7..1f18069 100644 --- a/Sources/Lingua/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGenerator.swift +++ b/Sources/Lingua/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGenerator.swift @@ -4,13 +4,16 @@ final class LocalizedFilesGenerator { private let directoryOperator: DirectoryOperable private let filesGenerator: PlatformFilesGenerating private let localizationPlatform: LocalizationPlatform + private let fileCleanup: FileCleanupStrategy init(directoryOperator: DirectoryOperable, filesGenerator: PlatformFilesGenerating, - localizationPlatform: LocalizationPlatform) { + localizationPlatform: LocalizationPlatform, + fileCleanup: FileCleanupStrategy) { self.directoryOperator = directoryOperator self.filesGenerator = filesGenerator self.localizationPlatform = localizationPlatform + self.fileCleanup = fileCleanup } } @@ -19,7 +22,7 @@ extension LocalizedFilesGenerator: LocalizedFilesGenerating { let languageCode = sheet.languageCode let folderName = localizationPlatform.folderName(for: languageCode) let outputFolder = try directoryOperator.createDirectory(named: folderName, in: config.outputDirectory) - try directoryOperator.removeFiles(withPrefix: .packageName, in: outputFolder) + try fileCleanup.removeFiles(using: directoryOperator, in: outputFolder) let sections = Dictionary(grouping: sheet.entries, by: { $0.section }) diff --git a/Sources/Lingua/Infrastructure/LocalizationGenerator/Output/Android/AndroidFileCleanupStrategy.swift b/Sources/Lingua/Infrastructure/LocalizationGenerator/Output/Android/AndroidFileCleanupStrategy.swift new file mode 100644 index 0000000..023f46c --- /dev/null +++ b/Sources/Lingua/Infrastructure/LocalizationGenerator/Output/Android/AndroidFileCleanupStrategy.swift @@ -0,0 +1,7 @@ +import Foundation + +struct AndroidFileCleanupStrategy: FileCleanupStrategy { + func removeFiles(using directoryOperator: DirectoryOperable, in folder: URL) throws { + try directoryOperator.removeFiles(withPrefix: .packageName, in: folder) + } +} diff --git a/Sources/Lingua/Infrastructure/LocalizationGenerator/Output/iOS/IOSFileCleanupStrategy.swift b/Sources/Lingua/Infrastructure/LocalizationGenerator/Output/iOS/IOSFileCleanupStrategy.swift new file mode 100644 index 0000000..e71f728 --- /dev/null +++ b/Sources/Lingua/Infrastructure/LocalizationGenerator/Output/iOS/IOSFileCleanupStrategy.swift @@ -0,0 +1,7 @@ +import Foundation + +struct IOSFileCleanupStrategy: FileCleanupStrategy { + func removeFiles(using directoryOperator: DirectoryOperable, in folder: URL) throws { + try directoryOperator.removeAllFiles(in: folder) + } +} diff --git a/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGeneratorTests.swift b/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGeneratorTests.swift index cc8e071..54e8842 100644 --- a/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGeneratorTests.swift +++ b/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/LocalizedFilesGeneratorTests.swift @@ -2,15 +2,16 @@ import XCTest @testable import Lingua final class LocalizedFilesGeneratorTests: XCTestCase { - func test_generate_callsExpectedMethods() throws { + func test_generate_callsExpectedMethods_forAndroidPlatform() throws { let directoryOperator = MockDirectoryOperator() let filesGenerator = MockPlatformFilesGenerator() - let localizationPlatform = LocalizationPlatform.ios + let localizationPlatform = LocalizationPlatform.android let sut = LocalizedFilesGenerator( directoryOperator: directoryOperator, filesGenerator: filesGenerator, - localizationPlatform: localizationPlatform + localizationPlatform: localizationPlatform, + fileCleanup: FileCleanupFactory.make(for: localizationPlatform) ) let sheet = LocalizationSheet(language: "en", entries: [LocalizationEntry.create(plural: true)]) @@ -25,4 +26,29 @@ final class LocalizedFilesGeneratorTests: XCTestCase { directory: config.outputDirectory), .removeFiles(prefix: "Lingua", directory: outputDirectoryURL)]) } + + func test_generate_callsExpectedMethods_forIOSPlatform() throws { + let directoryOperator = MockDirectoryOperator() + let filesGenerator = MockPlatformFilesGenerator() + let localizationPlatform = LocalizationPlatform.ios + + let sut = LocalizedFilesGenerator( + directoryOperator: directoryOperator, + filesGenerator: filesGenerator, + localizationPlatform: localizationPlatform, + fileCleanup: FileCleanupFactory.make(for: localizationPlatform) + ) + + let sheet = LocalizationSheet(language: "en", entries: [LocalizationEntry.create(plural: true)]) + let config = Config.Localization(apiKey: "key", sheetId: "id", outputDirectory: "path", localizedSwiftCode: .none) + let outputDirectoryURL = try XCTUnwrap(URL(string: config.outputDirectory)) + directoryOperator.url = outputDirectoryURL + + try sut.generate(for: sheet, config: config) + + XCTAssertEqual(directoryOperator.messages, + [.createDirectory(named: localizationPlatform.folderName(for: sheet.languageCode), + directory: config.outputDirectory), + .removeAllFiles(directory: outputDirectoryURL)]) + } } diff --git a/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/Mock/MockDirectoryOperator.swift b/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/Mock/MockDirectoryOperator.swift index 810ec16..9a35406 100644 --- a/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/Mock/MockDirectoryOperator.swift +++ b/Tests/LinguaTests/Infrastructure/LocalizationGenerator/Generator/Mock/MockDirectoryOperator.swift @@ -5,6 +5,7 @@ class MockDirectoryOperator: DirectoryOperable { enum Message: Equatable { case createDirectory(named: String, directory: String) case removeFiles(prefix: String, directory: URL) + case removeAllFiles(directory: URL) } private(set) var messages = [Message]() @@ -18,4 +19,8 @@ class MockDirectoryOperator: DirectoryOperable { func removeFiles(withPrefix prefix: String, in directory: URL) throws { messages.append(.removeFiles(prefix: prefix, directory: directory)) } + + func removeAllFiles(in directory: URL) throws { + messages.append(.removeAllFiles(directory: directory)) + } } diff --git a/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift b/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift index eb86533..7e8bed2 100644 --- a/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift +++ b/Tests/LinguaTests/Infrastructure/SwiftLocalizeGenerator/PlaceholderExtractor/PlaceholderExtractorTests.swift @@ -19,7 +19,8 @@ final class PlaceholderExtractorTests: XCTestCase { } func testMultiplePlaceholdersExtraction() { - expect(testString: "Hello %@, you are %d years old and your average grade is %f.", + expect(sut: PlaceholderExtractor(strategy: NSRegularExpressionPlaceholderExtractor()), + testString: "Hello %@, you are %d years old and your average grade is %f.", expectedTypes: ["String", "Int", "Double"]) } From a8a0557ae271c06c3c06bd81259ebd4bf7ef211d Mon Sep 17 00:00:00 2001 From: Egzon Arifi Date: Mon, 17 Jul 2023 15:35:42 +0200 Subject: [PATCH 5/6] Update Readme (#107) --- README.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c5cbfd8..c91757b 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ In order to setup Google sheet, we need to complete two steps. First one is to c We have prepared a tamplate for sheet structure for you. What you have to do is to open the link below and make a copy `File > Make a copy` -[Lingua - Google Sheets](https://docs.google.com/spreadsheets/d/1GpaPpO4JMleZPd8paSW4qPBQxjImm2xD8yJhvZOP-8w) +[Mobile Localizations Template - Google Sheets](https://docs.google.com/spreadsheets/d/1Cnqy4gZqh9pGcTF_0jb8QGOnysejZ8dVfSj8dgX4kzM) + +**Important:** Make sure to replace the existing API key in your application with the newly generated one. Also, ensure that the Google Sheet you're trying to access has its sharing settings configured to allow access to anyone with the link. You can do this by clicking on "Share" in the upper right corner of the Google Sheet and selecting "Anyone with the link." ### b. Obtain the sheet id @@ -63,7 +65,7 @@ Here are the steps to enable the Google Sheets API and create an API key: 2. If you haven't already, create a new project or select an existing one. -3. In the left sidebar, click on "APIs & Services," then "Dashboard." +3. In the left sidebar, click on "APIs & Services" 4. Click on "+ ENABLE APIS AND SERVICES" at the top of the page. @@ -71,21 +73,13 @@ Here are the steps to enable the Google Sheets API and create an API key: 6. Click on "ENABLE" to enable the Google Sheets API for your project. -7. After the API is enabled, go back to the "APIs & Services" > "Dashboard" page. +7. After the API is enabled, go back to the "APIs & Services" > "Credendtials" page. 8. Click on "CREATE CREDENTIALS" at the top of the page. -9. In the "Which API are you using?" dropdown, select "Google Sheets API." - -10. In the "Where will you be calling the API from?" dropdown, select "Other non-UI (e.g., cron job, daemon)." - -11. In the "What data will you be accessing?" section, select "Public data." - -12. Click on "What credentials do I need?" - -13. You'll be presented with an API key. Click on "Copy" to copy the key to your clipboard. Store this key securely, as you'll need it to access the Google Sheets API in your application. +9. In the dropdown, select "API key" -Make sure to replace the existing API key in your application with the newly generated one. Also, ensure that the Google Sheet you're trying to access has its sharing settings configured to allow access to anyone with the link. You can do this by clicking on "Share" in the upper right corner of the Google Sheet and selecting "Anyone with the link." +Wait a bit until the key is generated and an information modal with the message `API key created` will be shown. ## 2. Configuration file From 294ad8a2e2302491da9c96884cb6da8a28ac359b Mon Sep 17 00:00:00 2001 From: Egzon Arifi Date: Tue, 18 Jul 2023 09:34:34 +0200 Subject: [PATCH 6/6] Remove brew formula (#109) --- Formula/Lingua.rb | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 Formula/Lingua.rb diff --git a/Formula/Lingua.rb b/Formula/Lingua.rb deleted file mode 100644 index f9cfee5..0000000 --- a/Formula/Lingua.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Lingua < Formula - desc "Unified localization management tool for iOS & Android via Google Sheets" - homepage "https://github.com/poviolabs/Lingua" - url "https://github.com/poviolabs/Lingua/archive/0.2.1.tar.gz" - sha256 "8288b4702a4a42494dbdd5aedb01884a340653f7dcfd99bc4da33d441c14d555" - - depends_on :macos - depends_on :xcode => ["14.1", :build] - - def install - system "swift", "build", "--disable-sandbox", "--configuration", "release", "--disable-automatic-resolution" - bin.install '.build/release/Lingua' - end - - test do - system "#{bin}/Lingua", "--version" - end -end -