Welcome to AirGen! A general-purpose text generator made with love by the Airthings Team.
AirGen reads any structured model from either a JSON or Yaml file and feeds it to a FreeMarker template of your choosing, the resulting files are stored in an output directory using the same structure as in the original template directory.
AirGen automatically scans the template directory for any *.ftl
files, but ignores files or directories
that start with an underscore _
character. This way you're able to prepare include files and directories
that can be used by many templates.
To see the usage: ./gradlew run --args="-h"
A base directory is merely a directory where all other templates reside. When you set the base directory, it becomes the root from which other templates and files are loaded. It's not possible to load files from a location which doesn't reside in the base (root) directory.
Example of a directory structure:
templates/
l10n-poeditor/ <-- base directory
android/
en/ <-- template "android/en"
src/main/res/values/
strings.xml.ftl
fr/ <-- template "android/fr"
src/main/res/values/
strings.xml.ftl
de/ <-- template "android/de"
src/main/res/values/
strings.xml.ftl
ios/
Then you can run AirGen 3 times in order to generate localization files for the 3 templates:
$ ./gradlew run --args="-o app -b templates/l10n-poeditor -t android/en -m strings-en.json"
$ ./gradlew run --args="-o app -b templates/l10n-poeditor -t android/fr -m strings-fr.json"
$ ./gradlew run --args="-o app -b templates/l10n-poeditor -t android/de -m strings-de.json"
The structure of the output directory mimics that of the template directory, NOT the base directory. So
app/
directory would eventually contain the directories src/main/res/values
, src/main/res/values-fr
,
and src/main/res/values-de
.
The JSON or Yaml file is read once and made available to the templates as a top-level parameter named input
.
For example, if you have the following JSON structure:
{
"user": {
"name": "John Doe",
"email": "[email protected]"
},
"token": "XYZ0123456789",
"children": [
"Sarah",
"Winston",
"Debbie"
]
}
In the FreeMarker template files (*.ftl
) you can simply access the different values using the following notations:
Hello ${input.user.name}.
Your email is ${input.user.email}, and your token is ${input.user.token}.
<#if (input.children?has_content)>
Your children:
<#list input.children as child>
${child}
</#list>
</#if>
Easy enough :)
You can read more about other built-in accessors like the ?has_content
on the
FreeMarker website.
Most Java methods can be accessed on resolved variables right from FreeMarker template files, but if there exists a builtin accessor to do the same job, always use the latter.
FreeMarker mostly treats the .something
as a key/call on a Map
so it may sometimes throw an Exception if you
try to access, say, variable.trim()
. You should instead use variable?trim
for that.
There are few helper methods accessible through the air
variable. The list may grow in the future as our needs
require adding a custom logic to do some nifty processing, so do keep the following list updated:
These methods accept either a Number
or a String
, and return it after processing based on the method being called.
The methods are accessible through the air.number
variable.
fun toHex(number: <Number|String>, withPrefix: Boolean, minimalDigits: Int?)
Where:
number
: The number to convert to a hexadecimal notation.withPrefix
: Whether to prefix returned number with "0x", or not.minimalDigits
: Minimal number of digits to zero-fill the returned number.
fun zeroFill(number: <Number|String>, minimalDigits: Int)
Where:
number
: The number to zero-fill.minimalDigits
: Minimal number of digits to zero-fill the returned number.
These methods accept a String
and return it after processing for certain manipulation for HTML.
The methods are accessible through the air.html
variable.
fun escapeQuotes(string: String, double: Boolean, single: Boolean): String
Where:
string
: The string to escape.double
: Whether to convert"
to"
.single
: Whether to convert'
to'
.
Example use case: escaping quotes for Android strings.xml
files.
<string>"${air.html.escapeQuotes(translation, true, true)}"</string>
fun escapeHtml(string: String): String
Where:
string
: The string to escape.
Example use case: escaping for Android strings.xml
or Markdown files.
<string>"${air.html.escapeHtml(translation)}"</string>
All other methods dealing with text manipulation reside here, and are accessible through the air.text
variable.
fun escapeQuotes(string: String, double: Boolean, single: Boolean): String
Where:
string
: The string to escape.double
: Whether to convert"
to\"
.single
: Whether to convert'
to\'
.
Example use case: escaping quotes for iOS Localizable.strings
files.
<string>"${air.text.escapeQuotes(translation, true, true)}"</string>
fun oneLiner(string: String, trimLines: Boolean): String
Where:
string
: The string to convert.oneLiner
: Whether to convert new line characters to\n
and\r
, yielding a "one liner" result.
Example use case: eliminating stray spaces for Android strings.xml
files.
<string>"${air.text.oneLiner(translation, true)}"</string>
You can of course combine multiple calls to these methods, and FreeMarker has a nifty assignment command to break things up nicely:
<#assign translation = input.l10n.definition?trim>
<#assign translation = air.text.oneLiner(translation, true)>
<#assign translation = air.html.escapeHtml(translation)>
<#assign translation = air.html.escapeQuotes(translation, true, true)>
<string>"${translation}"</string>
- Passing properties on the command line, accessible in FreeMarker templates through the
prop
variable. - Read run configuration from a file.