Skip to content

Commit

Permalink
Merge branch 'feature/options' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
robrechtme committed Dec 6, 2021
2 parents cab2aa2 + 7d6a722 commit 4c2a8d1
Show file tree
Hide file tree
Showing 11 changed files with 365 additions and 321 deletions.
86 changes: 77 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

<hr />

![Node](https://img.shields.io/badge/node-v14+-blue.svg)
[![Build Status](https://travis-ci.org/robrechtme/loco-cli.svg?branch=master)](https://travis-ci.org/robrechtme/loco-cli)
![Dependencies](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)
![npm](https://img.shields.io/npm/v/loco-cli)
![Dependencies](https://img.shields.io/librariesio/release/npm/loco-cli)
[![GitHub Issues](https://img.shields.io/github/issues/robrechtme/loco-cli.svg)](https://github.com/robrechtme/loco-cli/issues)
![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)

<br />
Expand All @@ -18,7 +16,8 @@
<h3 align="center">loco-cli</h3>

<p align="center">
NodeJS CLI tool for uploading/downloading assets from Loco.

Automatically sync your project translations with [Loco](https://localise.biz).
<br />
<br />
<a href="https://github.com/robrechtme/loco-cli/issues">Report Bug</a>
Expand All @@ -29,19 +28,88 @@ NodeJS CLI tool for uploading/downloading assets from Loco.

## About

<!-- [![Product Name Screen Shot][product-screenshot]](https://example.com) -->

NodeJS CLI tool for uploading/downloading assets from Loco.
The **Loco CLI** helps you keep your translations bundled in your app/website in sync with [Loco](https://localise.biz).

## Getting Started

```
npx loco-cli --help
```

Loco CLI currently has three methods, which are very similar to git commands:

- `loco-cli push`: push asset ID's to Loco, so translators can start translating them.
- `loco-cli pull`: download all translations.
- `loco-cli status`: see which asset ID's are not yet uploaded/downloaded.

The Loco CLI assumes your translations are stored as **JSON** files, one for each language.

```
[locales folder]
├── en.json
├── es.json
└── fr.json
```

The keys in the files are asset ID's, and the values are translations. Nested JSON structures produce dottet asset ID's:

```jsonc
{
"home": {
"title": "Welcome back, {{name}}" // Asset ID: `home.title`
}
}
```

### Config file

Global options are passed as options in the terminal or read from a `.locorc.{yaml,json,js}` file:

```jsonc
// .locorc.json
{
"accessKey": "<loco-full-access-key>",
"localesDir": "src/app/i18n/locales",
"defaultLanguage": "en"
}
```

#### `accessKey`

or `-a, --access-key <key>`

The API key of the Loco project you wish to sync to/from. You can find this in the Loco project under `Developer Tools › API Keys › Full Access Key` (if you do not intend to use `loco-cli push`, an `Export key` will work too).

#### `localesDir`

or `-d, --locales-dir <path>`

The folder in which the JSON translation files are stored (defaults to current working dir).

#### `defaultLanguage`

or `-l, --default-language <lang>`

Loco CLI will use this language in the `push` and `status` commands to check which asset ID's are missing on Loco (default: `en`).

## Usage

🚧 WIP 🚧
### `loco-cli status`

Check the difference between local assets and remote assets. This command will show you which assets are present locally but not remotely and vice-versa.

### `loco-cli pull`

Download all translations from Loco. This command will **overwrite** the JSON files in `localesDir` with the assets found in Loco.

### `loco-cli push`

Push missing asset ID's to Loco. This command is useful for creating assets based on a reference JSON file.

#### Options

- `-t, --tag [tag]`: Tag for newly uploaded assets, e.g. "1.1.0"
- `-s, --status [status]`: Status for newly uploaded assets (default: "provisional")

## Contributing

Expand Down
26 changes: 17 additions & 9 deletions cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,36 @@ import { version } from "./package.json";

const program = new Command("loco-cli")
.version(version)
.option("-p, --personal-access-token <token>", "Loco API token");
.option("-a, --access-key <key>", "Loco API token")
.option(
"-d, --locales-dir <path>",
"The folder in which the translations are stored.",
"."
)
.option(
"-l, --default-language <lang>",
"Reference language to check which asset IDs are missing on Loco",
"en"
);

program
.command("pull")
.argument("<out_dir>", "Path to directory to write to")
.description("Fetch assets from Loco")
.action(pull);
program.command("pull").description("Fetch assets from Loco").action(pull);

program
.command("push")
.argument("<input-file>", "Path to JSON file to upload from")
.option(
"-t, --tag [tag]",
'Tag to add to all newly uploaded assets, e.g. "1.1.0"'
)
.option("-s, --status [status]", "Loco API token", "provisional")
.option(
"-s, --status [status]",
"Status to add to all newly uploaded assets",
"provisional"
)
.description("Upload assets to Loco")
.action(push);

program
.command("status")
.argument("<reference-file>", "Path to JSON file containing local assets")
.description("Check status of local file")
.action(status);

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"chalk": "4.1.2",
"cli-progress": "3.9.1",
"commander": "8.3.0",
"enquirer": "2.3.6",
"loco-api-js": "1.3.2",
"rcfile": "1.0.3"
},
Expand All @@ -50,4 +49,4 @@
"semantic-release": "^18.0.1",
"typescript": "4.5.2"
}
}
}
15 changes: 7 additions & 8 deletions src/commands/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,18 @@ import path from "path";
import chalk from "chalk";
import { getGlobalOptions } from "../util/options";
import { Command } from "commander";

interface CommandOptions {}

const pull = async (
folder: string,
_options: CommandOptions,
program: Command
) => {
const { personalAccessToken } = getGlobalOptions(program);
const loco = new Loco(personalAccessToken);
const pull = async (_: CommandOptions, program: Command) => {
const { accessKey, localesDir: folder } = getGlobalOptions(program);
const loco = new Loco(accessKey);

console.log("☁️ Downloading assets...");
const res = await loco.doExport();

fs.mkdirSync(folder, { recursive: true });

console.log();
console.log(folder);
const length = Object.keys(res).length;
let i = 1;
Expand All @@ -27,6 +25,7 @@ const pull = async (
fs.writeFileSync(filePath, JSON.stringify(assets, null, 2));
}

console.log();
console.log(
`${chalk.green("✔")} Downloaded assets in ${chalk.bold(
Object.keys(res).length
Expand Down
36 changes: 23 additions & 13 deletions src/commands/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getGlobalOptions } from "../util/options";
import { Command } from "commander";
import { importJSON } from "../util/file";
import chalk from "chalk";
import path from "path";

interface UploadOptions {
status?: string;
Expand Down Expand Up @@ -37,15 +38,13 @@ interface CommandOptions {
tag?: string;
}

const push = async (
inputFile: string,
{ status, tag }: CommandOptions,
program: Command
) => {
const { personalAccessToken } = getGlobalOptions(program);
const loco = new Loco(personalAccessToken);
const push = async ({ status, tag }: CommandOptions, program: Command) => {
const { accessKey, localesDir, defaultLanguage } = getGlobalOptions(program);
const loco = new Loco(accessKey);

const json = importJSON(inputFile);
const json = await importJSON(
path.join(localesDir, `${defaultLanguage}.json`)
);

const { missingRemote } = await getStatus(loco, json);
const length = Object.keys(missingRemote).length;
Expand All @@ -54,12 +53,15 @@ const push = async (
console.log(`${chalk.green("✔")} Already up to date!`);
return;
}
console.log(`Uploading ${length} assets.`);
const progressbar = new cliProgress.SingleBar({
format: `Uploading ${length} assets |${chalk.cyan(
"{bar}"
)}| {value}/{total}`,
barCompleteChar: "\u2588",
barIncompleteChar: "\u2591",
hideCursor: true,
});

const progressbar = new cliProgress.SingleBar(
{},
cliProgress.Presets.shades_classic
);
progressbar.start(length, 0);
for (const [key, value] of Object.entries(missingRemote)) {
progressbar.increment();
Expand All @@ -69,6 +71,14 @@ const push = async (
});
}
progressbar.stop();

console.log();
console.log(`${chalk.green("✔")} Uploaded ${length} assets.`);
console.log(
`${chalk.yellow(
"⚠️"
)} Be kind to our translators, provide a note in the \`Notes\` field when there is not enough context.`
);
};

export default push;
38 changes: 24 additions & 14 deletions src/commands/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,52 @@ import { Command } from "commander";

import getStatus from "../util/getStatus";
import { getGlobalOptions } from "../util/options";
import exit from "../util/exit";
import { importJSON } from "../util/file";
import path from "path";
import { truncateString } from "../util/string";

interface CommandOptions {}

const status = async (
inputFile: string,
_options: CommandOptions,
program: Command
) => {
const { personalAccessToken } = getGlobalOptions(program);
const loco = new Loco(personalAccessToken);
const status = async (_: CommandOptions, program: Command) => {
const { accessKey, localesDir, defaultLanguage } = getGlobalOptions(program);
const loco = new Loco(accessKey);

const json = await importJSON(inputFile);
const json = await importJSON(
path.join(localesDir, `${defaultLanguage}.json`)
);

const { missingLocal, missingRemote } = await getStatus(loco, json);

const missingLocalIDs = Object.keys(missingLocal);
const missingRemoteIDs = Object.keys(missingRemote);

if (!missingRemoteIDs.length && !missingRemoteIDs.length) {
if (!missingLocalIDs.length && !missingRemoteIDs.length) {
console.log(`${chalk.green("✔")} Everything up to date!`);
return;
}

console.log();
if (missingRemoteIDs.length) {
console.log(
`Found assets locally which are not present remote:
${missingLocalIDs.map((key) => `+ ${chalk.greenBright(key)}`).join("\n")}
`
Found assets locally which are not present remote (fix with \`loco-cli push\`):
${missingRemoteIDs
.map(
(key) =>
` ${chalk.greenBright(chalk.bold("+"))} ${key} ${chalk.cyan(
`(${truncateString(missingRemote[key], 20)})`
)}`
)
.join("\n")}
`
);
}
if (missingLocalIDs.length) {
console.log(
`Found assets remote which are not present locally:
${missingRemoteIDs.map((key) => `- ${chalk.redBright(key)}`).join("\n")}
`Found assets remote which are not present locally (fix with \`loco-cli pull\`):
${missingLocalIDs
.map((key) => ` ${chalk.red(chalk.bold("-"))} ${key}`)
.join("\n")}
`
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface GlobalOptions {
accessKey: string;
localesDir: string;
defaultLanguage: string;
}
2 changes: 1 addition & 1 deletion src/util/getStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const getStatus = async (loco: Loco, translationsObject: object) => {

const missingLocal: Record<string, string> = remoteAssetIDs
.filter((id) => !localAssetIDs.includes(id))
.reduce((acc, key) => ({ ...acc, [key]: "key" }), {});
.reduce((acc, key) => ({ ...acc, [key]: undefined }), {});

return {
missingRemote,
Expand Down
Loading

0 comments on commit 4c2a8d1

Please sign in to comment.