Skip to content

Commit

Permalink
Release/0.2.2 (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
EgzonArifi authored Jul 18, 2023
2 parents 2ad432f + 37c040c commit 4d3baff
Show file tree
Hide file tree
Showing 21 changed files with 151 additions and 83 deletions.
19 changes: 0 additions & 19 deletions Formula/Lingua.rb

This file was deleted.

20 changes: 7 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -63,29 +65,21 @@ 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.

5. In the search bar, type "Google Sheets API" and select it from the list.

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

Expand Down
2 changes: 1 addition & 1 deletion Resources/Docs/Architecture/Domain/Localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extension Logger {
func printUsage() {
log("""
Usage:
AssetGen <platform> <config_file_path/file.json>
Lingua <platform> <config_file_path/file.json>
<platform> is required only for localization functionality and can be:
1. ios
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

protocol FileCleanupStrategy {
func removeFiles(using directoryOperator: DirectoryOperable, in folder: URL) throws
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand All @@ -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 })

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

struct IOSFileCleanupStrategy: FileCleanupStrategy {
func removeFiles(using directoryOperator: DirectoryOperable, in folder: URL) throws {
try directoryOperator.removeAllFiles(in: folder)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct DefaultLocalizedSwiftCodeOutputStringBuilder: LocalizedSwiftCodeOutputStr
let sectionsOutput = buildSectionsOutput(sections: sections, translations: translations)

let output = """
//swiftlint:disable all
// swiftlint:disable all
import Foundation
Expand All @@ -33,6 +33,9 @@ struct DefaultLocalizedSwiftCodeOutputStringBuilder: LocalizedSwiftCodeOutputStr
#endif
}()
}
// swiftlint:enable all
"""
return output
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -115,7 +115,7 @@ private extension LocalizationProcessorTests {
var printUsage: String {
"""
Usage:
AssetGen <platform> <config_file_path/file.json>
Lingua <platform> <config_file_path/file.json>
<platform> is required only for localization functionality and can be:
1. ios
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)])
Expand All @@ -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)])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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]()
Expand All @@ -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))
}
}
Loading

0 comments on commit 4d3baff

Please sign in to comment.