nulligor / chars-counterattack

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Localization Standard Automation Engine

Char's Counterattack is a console based application to automate the process of standardizing all localization files and its entries in the respective files through the whole application flow.


Standard Proposal

The reason on why we should standardize the way we do localization comes down to two main points:

  • Less differences in implementation between projects since we would have a convention.
  • Detection of UI Errors related to localization implementation using automated tests.

The proposal consists of a couple of rules to follow when implementing code that inflicts and uses the localization module with the addition of a new concept, the key type.

Rules

  • All entries should be put as {type}_{key}: "value" or {type}_{key}: ['array', 'of', 'values'] in all localization files used by the application.
  • Identical entries such as {key}: {key} should not be used.
  • Different keys for repeated values should be avoided specially if they have the same type, this rule is optional but it is considered good practice to abstract the key names so it would fit the greatest amount of cases for the value.
  • Never push the localization file if there's dynamic entries in it (such as entries generated by custom admin features).
  • Key names should be written in CamelCase.

Key Type

Defining the type of any given localization key is nothing more than predefine in the implementation which purpose the key (thus, the value of the entry) fits in the overall scope of the application, for example: Let's assume our application have txt and err defined as possible key types to use, therefore, when formatting any key that reflects on a message that would display any error to the user, we should use err and when formatting any key that would display a regular text message to the user we should use txt.

Sample application of proposal

Let's assume your application has the following types defined to be used:

txt, err, msg, btn

By definition every key that represents an error to be displayed the user should be an err key, any button text should be a btn, any other message other than an err that would be displayed to the user should me a msg and everything that's not contained in the logic above should be a txt, so we would have:

txt_UserName, err_UserNameDoesNotExist, msg_UserNameNeedsToBeValid and btn_Submit on a sample scenario.


Using Char's Counterattack

To run the script, place it inside the /scripts folder of the application, and configure its config.js accordingly, then simply npm install && npm start and follow along with what the application prompts.

The Config

Sample config.js with its terms explained:

{
    locales: {
        rootPath: "../../content/locales/",
        default: "en.json"
    },
    applyTo: [  
        "../../content/web/views/",
        "../../content/web/public/js/",
        "../../content/web/public/tmp/",
        "../../content/ionic/views/",
        "../../content/ionic/public/js/",
        "../../content/shared/views/",
        "../../content/shared/public/js/",
        "../../test/e2e/",
    ],
    transform: [
        {
            from: "lbl",
            to: "txt"
        },
        {
            from: "error",
            to: "err"
        }
    ],
    types: ["txt", "msg", "btn", "err"],
    defaultType: "txt",
    removeIdentical: false,
    forceCheck: [
        /(?:title:)+\s*?('(.+?[^\\])'|"(.+?[^\\])")/gi,
        /(?:locale.getValue\()+\s?('(.+?[^\\])'|"(.+?[^\\])")/gi
    ]
}
locales.rootPath

Where your localization files are located within the application (relative to the project's folder structure).

locales.default

Main localization file, usually the one with the most entries (only the base name).

applyTo

Files in which the application will act upon, swapping its pre-parsed localization entries to the parsed ones (relative to the project's folder structure).

transform

Transform maps each object's within property from, does a string comparison in each key and parse it to match the property to, for example: Assuming you don't have lbl in the specified types: lblSomething => txt_Something.

types

Specified types the script will use, defaults to ["txt", "msg", "btn", "err"] if not declared.

defaultType [OPTIONAL]

If declared, skip all questions (prompts) in the parsing phase regarding which type to choose for a given key (that doesn't have a previous type assigned to) and assigns to the default key.

removeIdentical [OPTIONAL]

If declared true, all identical {key}: {key} are gonna be deleted, if assigned false all identical are gonna be parsed => {type}_{key}: {key}/ txt_something: "something"

forceCheck[OPTIONAL]

If declared, maps all regular expressions to each applyTo file searching for keys that wouldn't be detected by the script otherwise. It's worth noting that the regex capture group should only contain the matching key, for example: /(?:title:)+\s?('(.+?[^\\])'|"(.+?[^\\])")/gi* matches title: "someString" but only "someString" is in the capture group (third or second capture group is targeted).

Running the Script

The script has mainly two steps (phases) when it gets executed:

  • Parsing Phase
  • Applying Phase
Parsing Phase

The parsing phase is the step where all the localization files gets parsed and all its keys get formatted to fit the standard.

In this phase you usually get prompted to:

  • Choose if you want to DELETE identical pairs such as {key} : {key}: (only if removeIdentical isn't set in the config) y => Deletes the entry n => Doesn't delete the entry, and key gets formatted CURRENTLY ON FILE: 'en.json' Repeated key: value pair found in Sign: Sign Delete the entry? (Otherwise the key is gonna be formatted) [y/n]:

or

  • Choose which type you want to assign to a key that doesn't have any previous type assigned or any "pre-type" assigned (txtSomething automatically parses to txt_Something): (only if defaultType isn't set in the config) [SELECT] => input number of desired type CURRENTLY ON FILE: 'en.json' No valid type detected for key 'Sign', choose one of the types bellow [1] txt [2] msg [3] btn [4] err Select type [1...4]:

or

  • Choose a single (or all) entries to be assigned to a single value: [SELECT] => input number of desired key or 0 to keep all CURRENTLY ON FILE: 'en.json' Multiple key entries for value 'Ionic Admin' Choose one of the keys bellow: (All other keys are gonna be deleted) [1] txt_LinkToIonicAdmin [2] txt_Ionicadmin [0] Keep all
Applying Phase

The applying phase is the step where all applyTo files gets parsed and all entries that fit the pattern: i18n.__("key") and #{__("key")} gets automatically formatted, unless you specify a regular expression on config.forceCheck to detect and format any other pattern that your keys might be implemented into.

Right after the parsing phase, the script will display all the parsed keys with a specific integer index, this will help you decide what to do through one of the manual steps specified bellow.

In this phase you usually get prompted to:

  • Select the approach on what to do with a value detected in some applyTo file that doesn't have an entry located within the parsed localization file keys y => if input on first step will make you choose from a list of integers, which key you want to assign to the value, if input on second step, will make you choose from a type and create a new entry n => will exit and go to the next step, if pressed twice, will skip the entry entirely and it won't be swapped in all applyTo files. Couldn't find matching value entry on parsed localization files for key 'from' Assign to existing entry? [y/n]: Create new entry? [y/n]:

or

  • Select the approach on what to do with a value detected in some applyTo file that doesn't have an entry located within the original localization file keys y => if input on first step will format the entry, if input on second step will make you choose from a list of integers, which key you want to assign to the value, if input on third step, will make you choose from a type and create a new entry n => will exit and go to the next step, if pressed twice, will skip the entry entirely and it won't be swapped in all applyTo files. Couldn't find matching key entry on original localization files for key errUserErrNum' Is this key dynamically set on implementation (key + 'variable')? [y/n]: Assign to existing entry? [y/n]: Create new entry? [y/n]:

or (when forceCheck is enabled)

  • Choose if you want to enable the script to act upon keys detected by the regular expressions set on config.forceCheck Force Check detected 5 keys on file: 'ionicAdmin.js', wish to swap them? [y/n]:

What to do next?

After the script is run, is highly recommended to re-build the application and go through all application screens and state, everything that could display any localization key and see if there's any key automatically generated in the main localization file (other than the overwritten parsed ones), if there's any key generated, the developer must either come up with a forceCheck solution and run the script again (it is recommended to reset changes before running the script again) or manually swap the key in the file where the script didn't acted upon (also, double-check if all files are specified in config.applyTo).

Also, since this is a tool made to run just once to make everything good to go, keep in mind it is the task of the developer to write new entries on all localization files in accordance with the standard proposal.

About


Languages

Language:JavaScript 100.0%