Logen — A deep dive into the project
Welcome back to the third part about my Localization Generator Logen.
Part one was an introduction to how and why it may help you when developing localized apps. Part two provided an overview of how the converting process works and which steps are involved.
In this article I will provide an explanation of how the project is structured and how everything works together. This may be interesting for you, if you want to add new custom converter, which will be explained in more detail in part four.
To follow along, you may want to clone the repository from my GitHub:
git clone https://github.com/DavidPiper94/Logen
Let’s start by looking at each subdirectory in Logen:
converter: Here lies the heart and the sole of this project. The class ConverterInterface defines common functionality and thus is the base for all converter. Available converter are AndroidConverter, iOSConverter, iOSEnumConverter and JSONConverter. More information about each can be found in part two or by using the list subcommand.
lib: This module consists of the files FileHelper, JsonHelper and TerminalStyle. As the name suggests, FileHelper provides functions to handle files, e.g. writing and reading files, extracting path elements from a given file path and creating or deleting directories.
It’s accompanied by the JsonHelper, which specializes on JSON files. TerminalStyle holds multiple constants representing styles for printing formatted output to the console, e.g. read or yellow text colors, which are used to highlight errors and warnings respectively.
model: Here you can find the model classes which do nothing but wrapping data. It contains the classes IntermediateLocalization, IntermediateLanguage, IntermediateEntry and LocalizationFile, which were already described in part two, as well as the class MergeResult. This holds the result of merging two IntermediateLocalizations in form of a new IntermediateLocalization combining both old instances and a list of IntermediateEntry elements that were found in only one of them. To make the method merge accessible by all converter, it is implemented in the common base class ConverterInterface.
templates: This directory contains string resources, in which the localization is embedded in, e.g. a warning for generated content, the documentation for the Swift enums created by the iOSEnumConverter and many more.
test: To drive out the bugs, tests are needed, which can be found in the test directory. There are test cases for all converter as well as for parsing arguments in main and for using the convert subcommand. To run all tests at once, the file run_all.py provides a runner to execute the whole suite. It can be started with
pyhton -m logen.tests.run_all
The subdirectory testdata contains quite simple localization files, which are used in the converter test cases.
There are two additional classes regarding tests: TestHelper and ConverterSpy. Former provides some helper methods to reduce redundancy. It can create an IntermediateLocalization to check against the imported testdata and it also defines multiple methods to create an error message from two given model classes, which increases the meaningfulness of a failing test case. The second one is a converter that — you guest it — spies on whether it was used correctly or not. It is mainly used in the test cases for the subcommand convert to validate whether it uses the given converter correctly.
A special test is the bigtest, which needs another subdirectory under test named bigtests to work. This folder is excluded from git to prevent it from being published, because it is meant to contain big localization files from real apps. Thus the test first checks the existence of this folder. If it doesn’t, the test will just pass and nothing will be validated. Otherwise it will first import each file and then merge together all IntermediateLocalizations of one format with the same name. This will will show LocalizationEntry instances, that are missing in some languages. Finally it compares and validate each of these IntermediateLocalization. The process can be seen in the diagram below.
other files: main, main_subcommand_convert and main_subcommand_list are not contained inside another directory. The main file is the starting point of this application. It specifies usable converter, creates and sets up the parser and subparsers and hands off to the selected subcommand. The parsing is done by the external package argparse. Each subcommand has its own file and thus is self contained without any interaction to other subcommands. A subcommand provides a start method, which gets passed on the parsed arguments from the command line as well as a list of all registered converter as its parameter. This start method is invoked by the main function after parsing the command line arguments.
Outside the project folder are some more files. setup.cfg and setup.py are needed for uploading the project to the python package index where you can find it here. README and LICENSE contain information about the project and its license and the files Pipfile and Pipfile.lock are involved in handling dependencies.
So far so good! Now you have a quite solid understanding of what Logen can do, how it does its job and how it is structured. In the next part we will look at how it can be extended by custom converters. Spoiler alert: This won’t be too hard.