strong-globalize-cli
Version:
StrongLoop Globalize - CLI
328 lines (250 loc) • 16.1 kB
Markdown
# strong-globalize-cli
This module provides CLI tooling for globalization.
## Supported Node.js versions
Node 10.x latest version and above are supported.
## Overview
The CLI ships `slt-globalize` command to perform common tasks to globalize
JavaScript applications. Key features are:
- Extract: parse JavaScript source code to extract text messages that should be
globalized.
- Lint: validate message files to check possible issues.
- Translate: Use [IBM Globalization Pipeline](https://www.ibm.com/cloud/globalization-pipeline) to translate text messages
## Installation
To install the CLI, run `npm i -g strong-globalize-cli`. After that, you should
be able to use `slt-globalize` command.
## Basic use
### Usage: `slt-globalize [options]`
```
Options:
-h, --help Print this message and exit
-v, --version Print version and exit
-l, --lint Check validity of string resource
-t, --translate Translate string resource
-d, --deepextract <black list> Deep-extract resource strings
-e, --extract <black list> Extract resource strings to en/messages.json except for directories on [black list] separated by a space.
```
### Configure access to Globalization Pipeline on IBM Cloud
To access [Globalization Pipeline on IBM Cloud](https://console.bluemix.net/catalog/services/globalization-pipeline) for machine translation, credentials should be provided in one of the two ways:
- By `<HOME_DIRECTORY>/.strong-globalize/ibm-cloud-credentials.json`
Copy and paste your credentials look like the following from the dashboard of Globalization Pipeline on IBM Cloud into `<HOME_DIRECTORY>/.strong-globalize/ibm-cloud-credentials.json`.
```js
{
"credentials": {
"url": "https://gp-beta-rest.ng.bluemix.net/translate/rest",
"userId": "6e41ceac9f14b493faxxxxxxxxxxxxxx",
"password": "vLbqlkjPhJiwJlkjwou8woO82hk2huku",
"instanceId": "6888888888888e6d2f458b1b4b5fd010"
}
}
```
- By environment variables
For example,
```sh
export BLUEMIX_URL="https://gp-beta-rest.ng.bluemix.net/translate/rest"
export BLUEMIX_USER=6e41ceac9f14b493faxxxxxxxxxxxxxx
export BLUEMIX_PASSWORD=vLbqlkjPhJiwJlkjwou8woO82hk2huku
export BLUEMIX_INSTANCE=6888888888888e6d2f458b1b4b5fd010
slt-globalize -t
```
Please see [Other Resources](#other-resources) for step-by-step instructions to set up the service.
## Tests
`npm run test`
## String resource extraction
`strong-globalize` CLI supports string resource auto-extraction in two modes: `regular extraction` mode and `deep extraction` mode. The regular extraction mode is invoked with `slt-globalize -e` and typically used in package development phase. The deep extraction mode `slt-globalzie -d` is designed to be used in globalization of enterprise-scale applications.
### Regular extraction
Suppose you have a package named `gmain` which has two JS files: `index.js` and `lib/util.js` and the two JS files contain the same `g.log('user: %s', userName)` call in line# 8 and line# 12 respectively. Running `slt-globalize -e` under the application root directory, `/Users/user/gmain` will generate `intl/en/messages.json` and `intl/zz/messages.json` as shown in the [Pseudo Localization Support](#pseudo-localization-support) section. Note that `slt-globalize -e` extracts all strong-globalized literal strings as well as non-globalized literal string with positional information in to `intl/zz/messages.json`. It is useful to pin-point untranslated strings in the source code.
In the regular extraction mode, `strong-globalize` scans all JS and Html templates owned by the `gmain` package no matter how deep the directory structure goes -- for example, `gmain/lib/usa/california/sanfrancisco/util.js` is scanned. However, it does not examine dependent files under `node_modules` or `test` directory. All `strong-globalized` literal strings in JS and Html files of the target package will be extracted and stored in `intl/en/messages.json` along with the positional information stored in `intl/zz/messages.json`. All non-strong-globalized literal strings in the first argument of all JS function calls are extracted and stored in `intl/zz/messages.json` along with the positional information.
In runtime, the string resource JSON files under `intl` will be loaded on to memory as needed.
**Use Case**: Self-contained CLI utility package is typically code-globalized and distributed with or without translated messages.json. API library packages are typically code-globalized and distributed without translation. Such library packages are then downloaded and used as part of enterprise-scale applications.
```
/Users/user
└── gmain
├── index.js
├── intl
│ ├── de
│ ├── en
│ ├── es
│ ├── fr
│ ├── it
│ ├── ja
│ ├── ko
│ ├── pt
│ ├── ru
│ ├── zh-Hans
│ ├── zh-Hant
│ └── zz
├── lib
│ └── usa
│ └── california
│ └── sanfrancisco
│ └── util.js
├── node_modules
│ ├── express
│ ├── request
│ └── strong-globalize -> /usr/local/lib/node_modules/strong-globalize
└── package.json
```
#### Use `@strong-globalize` comment
Sometimes it's hard for the CLI to determine if a given variable is an instance
of `StrongGlobalize` so that usage of such variables are subject to message
extraction.
To explicitly mark a variable as an instance of `StrongGlobalize`, use
`@strong-globalize` comment:
```ts
// @strong-globalize
import g from 'strong-globalize';
// Now the usage of `g.*` is enabled for message extraction
console.log(g.f('English Text'));
```
To explicitly mark an argument of a function call to be globalized, we can use
`@globalize` or `@strong-globalize` comment.
```js
const gUtil = require('your-helper-for-strong-globalize');
// @globalize
const msg = gUtil.f('abc');
// @globalize
gUtil.log('abc');
```
### Deep extraction
Enterprise-scale applications may depend on dozens of third party packages directly or indirectly. Such applications typically download dependent packages using `npm install` and can globalize them using the `Deep Extraction` mode.
For example, suppose `gmain` package has one dependent package `gsub` which is installed under `gmain/node_modules` as shown in the directory structure diagram below. `slt-globalize -d` traverses the `npm v3 style` dependency tree and extracts all the strong-globalized string literals into `gmain/intl/en/messages.json`. This way, all the literal strings in your package `gmain` as well as all the dependent modules are extracted and can be translated consistently at `gmain/intl` level. Note that the `package.json` dependency traversal is different from simple directory traversal.
Note that [string resource extraction from Html templates](#globalize-html-templates) is supported in the regular extraction mode only.
### `STRONGLOOP_GLOBALIZE_MAX_DEPTH` environment variable
As the size of your application grows, the number of dependent packages can grow exponentially. Since non-globalized literal strings are also recorded on `gmain/intl/zz/messages.json`, `gmain/intl/zz/messages.json` may also grow exponentially and cause `slt-globalize -d` to run out of resource on your computer.
To manage such situations, you can set `STRONGLOOP_GLOBALIZE_MAX_DEPTH` environment variable. `slt-globalize -d` stops traversing at the specified directory depth. Note that it works as directory depth although the traversal is controlled by dependencies defined in `package.json`.
For example, invoking `STRONGLOOP_GLOBALIZE_MAX_DEPTH=3 slt-globalize -d` under `/Users/user/gmain` works as follows. `gmain/index.js` is depth 1; thus examined. `gmain/lib/usa/california/sanfrancisco/util.js` is depth 5, not examined although it's part of your `gmain` package. `gmain/node_modules/gsub/index.js` is level 3, thus examined. Likewise, all the files directly under `gmain/node_modules/express` and `gmain/node_modules/request` will also be examined and literal strings are extracted to `gmain/intl/zz/messages.json`.
### `npm v3` dependency resolution
`npm v3` tries to install all dependent packages in the root `node_modules` directory, i.e., `gmain/node_modules` in the above example, which means that most dependent package directories are at depth level 2. Therefore, `STRONGLOOP_GLOBALIZE_MAX_DEPTH` does not help in `npm v3` installed applications. `slt-globalize -d [black list]` option can help to reduce the number of packages to scan.
```
/Users/user
└── gmain
├── index.js
├── intl
│ ├── de
│ ├── en
│ ├── es
│ ├── fr
│ ├── it
│ ├── ja
│ ├── ko
│ ├── pt
│ ├── ru
│ ├── zh-Hans
│ ├── zh-Hant
│ └── zz
├── lib
│ └── usa
│ └── california
│ └── sanfrancisco
│ └── util.js
├── node_modules
│ ├── express
│ ├── gsub
│ │ ├── index.js
│ │ ├── node_modules
│ │ │ └── strong-globalize -> /usr/local/lib/node_modules/strong-globalize
│ │ └── package.json
│ ├── request
│ └── strong-globalize -> /usr/local/lib/node_modules/strong-globalize
└── package.json
```
### HTML Template Globalization
Many UI strings are included in HTML templates. `slt-globalize -e` supports string extraction from the HTML templates as well as JS files. Once extracted, `slt-globalize -t` can be used to translate the resource JSON.
In the following example, the two strings `{{StrongLoop}} History Board` and `History board shows the access history to the e-commerce web site.` are extracted to JSON.
```html
<div class="board-header section-header">
<h2>{{{{StrongLoop}} History Board | globalize}}</h2>
</div>
<div role="help-note">
<p>
{{ History board shows the access history to the e-commerce web site. | globalize }}
</p>
</div>
```
`strong-globalize` supports `{{ <string to be localized> | globalize }}` out of box. In case you need other pattern matching rule for your template engine, you can set custom RegExp by `setHtmlRegex` API.
The string extraction works for CDATA as well. `Text in cdata` is extracted in the following example:
```html
<![CDATA[
{{Text in cdata | globalize }}
]]>
```
### JSON YAML File Globalization
You can directly pass the file name of JSON or YAML file and a list of fields to `g.t` or `g.formatMessage`. The file name is a path relative to the root directory of the package. The list of the fields is a string notation of two dimensional array being a list of the values to globalize. In the sample code below, `data.json` is the JSON file and `index.js` shows how to globalize all fields of `data.json`. `g.t` loads the globalized object into memory which is usually done by `require(<file name>)` or `fs.readFile` followed by `JSON.parse`. Note that the file name and the list must be provided as string literals directly in `g.t` call. YAML file globalization works in exactly the same way.
Note that `strong-globalize` supports [traditional message key approach](#help-txt-files-and-msg-keys) as well. To take the message key approach in JSON file globalization, manually define a message key like `msgKey`, store the key and content value pair in `intl/en/messages.json`, then in run-time, load the JSON file as usual (`require` or `readFile & parse`) first and overwrite the value with g.t('msgKey').
[Plain text file globalization](#help-txt-files) works in the same way except that 1. the text file name is a path relative to `intl/en` of the package, and 2. since the entire text file is a message, there are no parameters equivalent to the field list.
In the above paragraphs, `g.f` can be used instead of `g.t` if you'd like.
`test/fixtures/extract007' is the YAML equivalent. Likewise, `test/fixtures/formatyaml001` is the parallel of `test/fixtures/formatjson001`.
```js
// test/fixtures/extract006/index.js
const SG = require('strong-globalize');
SG.SetRootDir(__dirname);
const g = new SG();
const json = g.t('data/data.json',
'[' +
' "title",' +
' ["types", 0],' +
' ["types", 1],' +
' ["types", 2],' +
' ["types", 3],' +
' ["threeWrites", "e"],' +
' ["threeWrites", "o"],' +
' ["threeWrites", "w"]' +
']');
console.log(JSON.stringify(json, null, 2));
```
`test/fixtures/extract006/data/data.json`
```js
{
"title": "This is an error.",
"types": ["error", "log", "info", "warn"],
"threeWrites" : {
"e": "ewrite",
"o": "owrite",
"w": "write"
}
}
```
```
test/fixtures/extract006
├── data
│ └── data.json
├── index.js
├── intl
│ ├── de
│ │ └── messages.json
│ ├── en
│ │ └── messages.json
│ ├── es
│ │ └── messages.json
│ ├── fr
│ │ └── messages.json
│ ├── it
│ │ └── messages.json
│ ├── ja
│ │ └── messages.json
│ ├── ko
│ │ └── messages.json
│ ├── pt
│ │ └── messages.json
│ ├── ru
│ ├── zh-Hans
│ │ └── messages.json
│ ├── zh-Hant
│ │ └── messages.json
│ └── zz
│ ├── messages.json
│ └── messages_inverted.json
├── node_modules
│ └── strong-globalize
│ ├── ...
│ ...
│
└── package.json
```
# Other Resources
- https://github.com/Setogit/sg-example-001-date-currency
A complete strong-globalized application with machine-translated messages. In addition to message formatting, date and currency formatting examples are included. You can install and quickly see how the strong-globalized (or SG'ed in short) app works. Just install and `node index.js`
- https://github.com/Setogit/sg-example-002-glob-pipeline
Detailed 15-step instruction with 15 screen-shots to set up IBM Globalization Pipeline on Bluemix
## License
Artistic License 2.0