Transformations allow for procesing input variables and inferring new ones. This is extremely useful for automating the workflow. You can, for example, obtain language code from file’s name or path.

It’s also important for cleaning paths, file names and other parameters before the upload.

Transformation with a single operation #️⃣

The example below creates a new variable called lang_from_path from the source string ${path} applying the operation substringAfter.

It can be later used in the upload/download section.

{
  // ... other parts omitted for brevity ...
  
  "transformations": [
    
    {
        "name": "lang_from_path",
        "source": "${path}",
        "operations": "substringAfter: ./"
    }

  ],

  "upload": {
    "type": "ios-strings",    
    "files": {
        "lang": "${lang_from_path}",
        "path": "",
        "pattern": "*/Localizable.strings"        
    }
  }
 
}

Transformation with multiple operations #️⃣

It’s possible to apply more operations. They are applied in the defined order. The subsequent operation is applied on the result from the previous one.

The example below turns source string ${path}, let’s take ./translation-cs_CZ/menu as a sample, to the language code cs_CZ.

{
  // ... other parts omitted for brevity ...
  
  "transformations": [
    
    {
        "name": "lang_from_path",
        "source": "${path}",
        "operations": [
          "substringAfter: ./",
          "substringBefore: /",
          "remove: translation-"
        ]
    }

  ]
}

Complex source string #️⃣

The source string is a just a string with interpolated variables. Therefore, you can build it using more variables or string constants.

{
  // ... other parts omitted for brevity ...
  
  "transformations": [
    
    {
        "name": "complex_source",
        "source": "Source can be consisted of ${more} ${variables}",
        "operations": "substringAfter: ./"
    }

  ]
}

Operations #️⃣

trimStart #️⃣

Trim all matching characters from the start of the string.

The function must be called without parameters (trim white-spaces) or with a single character string as a parameter.

Trim all white-spaces: trimStart

Trim all characters a: trimStart: a

trimEnd #️⃣

Trim all matching characters at the end of the string.

The function must be called without parameters (trim white-spaces) or with a single character string as a parameter.

Trim all white-spaces: trimEnd

Trim all characters a: trimEnd: a

substringAfter #️⃣

Return substring after the first occurrence of the string provided as parameter.

Return substring after /res/: substringAfter: /res/

substringAfterLast #️⃣

Return substring after the last occurrence of the string provided as parameter.

Return substring after the last /: substringAfterLast: /

substringBefore #️⃣

Return substring before the first occurrence of the string provided as parameter.

Return substring before /res/: substringBefore: /res/

substringBeforeLast #️⃣

Return substring before the last occurrence of the string provided as parameter.

Return substring before the last /: substringBeforeLast: /

append #️⃣

Append the string provided as parameter.

Append /strings.json to the string: append: /strings.json

prepend #️⃣

Prepend the string provided as parameter.

Prepend translations/ to the string: prepend: translations/

replace #️⃣

This function accepts two parameters separated with comma , and replace all occurrences of the first parameters with the second paramater.

Replace all occurrences of _ to -: replace: _, -

remove #️⃣

Remove all occurrences of the string provided as parameter.

Remove all /: remove: /

removePrefix #️⃣

Remove prefix if it is the string provided as parameter. If the prefix is different from the parameter, the string is left unchanged.

Remove prefix ./res/: removePrefix: ./res/

removeSuffix #️⃣

Remove suffix if it is the string provided as parameter. If the suffix is different from the parameter, the string is left unchanged.

Remove JSON file extension .json: removeSuffix: .json

capitalize #️⃣

Capitalize the input string - change the first letter to the upper case.

This function has no parameters.

Turn source string free to Free: capitalize

capitalizeWords #️⃣

Capitalize all words in the input string - change the first letter to the uppercase for every word.

If the function is called without parameters, all words are capitalized.

The function can be called with parameter “true” or “false”. If the function is called with “false”, the first words is not capitalized.

Turn source string free as beer to Free As Beer: capitalizeWords: true

Turn source string free as beer to free As Beer: capitalizeWords: false

lowerCase #️⃣

Change all letters of the input string to lower case.

This function has no parameters.

Turn source string FREE to free: lowerCase

upperCase #️⃣

Change all letters of the input string to upper case.

This function has no parameters.

Turn source string free to FREE: upperCase

print #️⃣

Print the value after the string provided as parameter.

For example, print: Value:%space% prints Value: VALUE.

Useful for debugging.

set #️⃣

Set the string provided as parameter to the value. It’s useful for complex operations.

It’s mainly useful with temporary variables. See Redirecting output below.

detectLang #️⃣

Detect language code from the string. There are several methods for the detection. The method is specified as the parameter.

android

Looks for values-xx folder in the source string and parse the language from it. Support the values-b+sr+Latn+SR notation.

Example: main/res/values-cs-rCZ/strings.xml => cs-CZ.

suffix

Remove extension of the file contained in the source string and looks for a suffix consisted of 2-3 letters for language (lowercased), 4 letters for script (capitalized) and 2-3 letters for a region (uppercased). It considers -, _ and + as separators.

Examples: my_file_cs_CZ.arb => cs-CZ

Can be used for parsing suffixes for Flutter’s ARB file in a localization folder.

Tip: If your files use a different separator for language part, use other transformations such as replace to change them.

formatLang #️⃣

This operation expects that the source string is a locale consisted of 2-3 letters for language (lowercased), 4 letters for script (capitalized) and 2-3 letters for a region (uppercased). It considers -, _ and + as separators. Script and region are optional.

Four parameters are expected describing how to format the locale based on whether region, script or both are available.

The order of parameters is:

  1. Only the language code is available
  2. Language code and region are available
  3. Language code and script are available
  4. Language code, region and script are available

In each of the parameters string LL is replaced with the language code, RR with the region and SCRP with the script.

Examples for formatLang: LL, LL_RR, LL-SCRP, LL_RR-SCRP

Source string Language Region Script Formatted
de de de
cs-CZ cs CZ cs_CZ
zh+Hant zh Hant zh-Hant
zh+Hans+TW zh TW Hans zh_TW-Hans

Examples for formatLang: b+LL, b+LL+RR, b+LL+SCRP, b+LL+SCRP+RR

Source string Language Region Script Formatted
de de b+de
cs-CZ cs CZ b+cs+CZ
zh-Hant zh Hant b+zh+Hant
zh-TW-Hans zh TW Hans b+zh+Hans+TW

androidBuildType #️⃣

Parse the build type from Android project standard structure.

As parameters separated by comma, it expects a list of all build types defined in your project.

It looks for src/ and parse the next part, so by sure that src/ is included in the source string.

Example:

  • Source: src/gpFreeDebug/res/values/strings.xml
  • Command: androidBuildType: debug, release
  • Output: debug

androidProductFlavors #️⃣

Parse product flavors from Android project standard structure.

As parameters, it expects list of product flavors / dimensions. Product flavors in one dimension are separated by space and dimensions are separated with comma.

It’s important to list dimensions in the correct order (as appear in build.gradle) to maintain priority correctly.

It looks for src/ and parse the next part, so by sure that src/ is included in the source string.

Example:

Product flavors are defined as follows (gp = Google Play, am = Amazon Store) in your gradle.build file.

flavorDimensions('store', 'price')
productFlavors {
    gp {
        dimension "store"
    }
    am {
        dimension "store"
    }
    free {
        dimension "price"
    }
    full {
        dimension "price"
    }        
}

The operation definition for such a configuration would be:

androidProductFlavors: gp am, free full

Example situation:

  • Source: src/gpFreeDebug/res/values/strings.xml
  • Command: androidProductFlavors: gp am, free full
  • Output: 0:gp 1:free

Note: The output contains not only list of the product flavors but also priorities. They are correctly process by the server.

Multiple operations on a single line #️⃣

If you need more simple operations, you can use | character to join more of them on a single line.

{
  // ... other parts omitted for brevity ...
  
  "transformations": [
    
    {
        "name": "test",
        "source": "${path}",
        "operations": "substringAfter: / | substringBeforeLast: /"
    }

  ]
}

The example above is the same as:

{
  // ... other parts omitted for brevity ...
  
  "transformations": [
    
    {
        "name": "test",
        "source": "${path}",
        "operations": [
          "substringAfter: /", 
          "substringBeforeLast: /"
        ]
    }

  ]
}

Special characters #️⃣

Some characters have a special meaning and so it’s necessary to replace them.

  • white-spaces are trimmed to prevent formatting issues, please use %space% instead
  • , is used as parameter separator, please use %comma% instead
  • $ is used for including variables, please use %dollar% instead
  • | is used for separating more command on a single line, please use %vbar% instead
  • > is used for redirecting output, please use %bracket% instead

Example:

If you need to replace ab,c (starts with space) with $>|, do it like this:

{
  // ...
  "operations": "replace: %space%ab%comma%c, %dollar%%bracket%%vbar%"
}

Redirecting output #️⃣

Using the > (similary to bash, etc.), it’s possible to redirect output to so-called temporary variable. Temporary variables are valid only for the single file.

When the output of the operation is redirected, the source string is still taken as input but after the operation, a temporary variable is created (or changed) and the input is left unchanged.

This is useful in some specific situations like switching position of two parts of the file name. The same effect could be achieved with more transformations, but that would introduce unnecessery clutter.

Example:

Let’s say that the input is file_cs-CZ where both file and cs-CZ may be different for each of processed file. However, you need cs-CZ_file.

It’s very simple with the output redirecting:

{
  // ... other parts omitted for brevity ...
  
  "transformations": [
    
    {
        "name": "redirecting_output_test",
        "source": "${path}",
        "operations": [
          "substringBefore: _ > first_part", 
          "substringAfter: _ > second_part",
          "set: ${second_part}_${first_part}"
        ]
    }

  ]
}

Initializators #️⃣

Any rule with name starting with : will be executed immediately. This rules cannot be called using ${variable} as symbol : is not allowed in the name of variable.

Together with redirecting output (described above), initializators can be used to preproduced output.

This is particularly useful when you apply several different operations on the input string and want to get more outputs along the way.

Example:

Let’s say that the input string is path like /aa/bb/cc/... where aa, bb and cc are path components you need for further processing.

The example transformation below removes the first /, stores aa into first, removes aa/, store bb into second, removes bb/ and store cc into third.

This is done before the file is processed and so you can safely use variables first, second and third.

{
  // ... other parts omitted for brevity ...
  
  "transformations": [
    
    {
        "name": ":preparse_path",
        "source": "${path}",
        "operations": [
          "substringAfter: /", 
          "substringBefore: / > first",
          "substringAfter: /",
          "substringBefore: / > second",
          "substringAfter: /",
          "substringBefore: / > third"
        ]
    }

  ]
}

Initializator scoping #️⃣

As all transformations - both for upload and download - are defined together and can access different variables, it’s important to be sure that inializator is not run with the expected variables being undefined.

For this reason, it’s possible to scope the initializator to either upload or download by adding a prefix:

:upload:find-product-flavors - Is only run for upload :download:create-output-path - Is only run for download