i18nexus-cli 2.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,30 +1,30 @@
1
1
  # i18nexus-cli
2
2
 
3
- **A command line interface for accessing translations from the i18nexus API**
3
+ **A command line interface for accessing and managing translations through the i18nexus API**
4
4
 
5
5
  ## New to i18nexus?
6
6
 
7
- [i18nexus](https://i18nexus.com) is a translation management web application designed for use with i18next. Learn more with this [5 minute React walkthrough](https://javascript.plainenglish.io/react-localization-the-winner-is-i18next-i18nexus-b7cd9f14094e) or visit our [Quick Integration guide](https://i18nexus.com/quick-integration).
7
+ [i18nexus](https://i18nexus.com) is a translation management web application designed for use with i18next. Learn more with this quick [Next.js walkthrough](https://i18nexus.com/nextjs-tutorial) or this [React.js walkthrough](https://i18nexus.com/react-tutorial).
8
8
 
9
9
  ## Who is this CLI meant for?
10
10
 
11
- - Developers using SSR/SSG or libraries such as [next-i18next](https://github.com/isaachinman/next-i18next)
12
- - Developers who prefer to bundle their translation files with their app
11
+ - Developers who prefer to download and bundle their translation files with their app (especially useful for those who use SSR/SSG and libraries such as [next-i18next](https://github.com/isaachinman/next-i18next)).
12
+ - Developers who wish to add, edit, or delete strings in their i18nexus project from the command line.
13
13
 
14
- The primary motivation for this CLI is to ease i18nexus integration with SSR/SSG frameworks such as NextJS. It is the best way to integrate i18nexus with [next-i18next](https://github.com/isaachinman/next-i18next).
15
-
16
- ## Setup
17
-
18
- ### 1. Installation
14
+ ## Installation
19
15
 
20
16
  ```sh
21
17
  npm install -g i18nexus-cli
22
18
  ```
23
19
 
24
- ### 2. Downloading your translations
20
+ ## Environment Variables
21
+
22
+ The commands below have options that can be set with environment variables. Since the majority of our users are using this package in the context of their web or mobile application, this package will load the `.env` file in the current working directory to check for i18nexus variables. If you would prefer not to load your `.env` file, you can run `export I18NEXUS_NO_DOT_ENV=true`.
23
+
24
+ ## Downloading your translations
25
25
 
26
26
  ```sh
27
- i18nexus pull -k <YOUR_PROJECT_API_KEY>
27
+ i18nexus pull -k <PROJECT_API_KEY>
28
28
  ```
29
29
 
30
30
  The above snippet will download all of your latest translations to your current directory in the following structure:
@@ -43,9 +43,7 @@ This structure is i18next convention, and will work by default with [next-i18nex
43
43
 
44
44
  If you wish to download your files to a different directory, you can use the `--path` option to specify your download destination. See all options below:
45
45
 
46
- #### Options
47
-
48
- `i18nexus pull`
46
+ ### Options
49
47
 
50
48
  | Option | Default value |
51
49
  | ------------------- | ------------------ |
@@ -54,7 +52,7 @@ If you wish to download your files to a different directory, you can use the `--
54
52
  | `--ver` or `-v` | `latest` |
55
53
  | `--clean` | `false` |
56
54
 
57
- #### Details
55
+ ### Notes
58
56
 
59
57
  `--api-key`
60
58
  Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
@@ -67,3 +65,188 @@ The version of your project's translations to be downloaded (Can also be set usi
67
65
 
68
66
  `--clean`
69
67
  Before download, clears your destination folder specified in --path. As a safety precaution, this only deletes folders with names that match a simple language code regex. You should still ensure you are not storing any files in your destination folder that you do not want deleted.
68
+
69
+ ## Personal Access Tokens
70
+
71
+ The following commands require a Personal Access Token (PAT) because they write data to your i18nexus project. PATs are created in your i18nexus account dashboard.
72
+
73
+ REMEMBER: Keep these tokens a secret and do NOT publish them to any public code repositories such as Github. They should never be client facing. Treat each token as if it is your i18nexus password.
74
+
75
+ ## Adding new strings
76
+
77
+ `i18nexus add-string` or `i18nexus a`
78
+
79
+ ```sh
80
+ i18nexus a -K 'welcome_msg' -v 'Welcome to my app!' -ns 'common' -k <PROJECT_API_KEY> -t <YOUR_PERSONAL_ACCESS_TOKEN>
81
+ ```
82
+
83
+ The above snippet will create a new string in your i18nexus project in your `common` namespace.
84
+
85
+ Translations for the string will be automatically generated and machine translated for all of your project's languages, just as if you added the string manually in the i18nexus web application.
86
+
87
+ ### Options
88
+
89
+ | Option | Required? |
90
+ | ---------------------- | --------- |
91
+ | `--api-key` or `-k` | &#10004; |
92
+ | `--pat` or `-t` | &#10004; |
93
+ | `--namespace` or `-ns` | &#10004; |
94
+ | `--key` or `-K` | &#10004; |
95
+ | `--value` or `-v` | &#10004; |
96
+ | `--details` or `-d` | |
97
+
98
+ ### Notes
99
+
100
+ `--api-key`
101
+ Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
102
+
103
+ `--pat`
104
+ A personal access token that you have generated in your i18nexus account (Can also be set using environment variable `I18NEXUS_PERSONAL_ACCESS_TOKEN`)
105
+
106
+ `--namespace`
107
+ The namespace in which to create the string
108
+
109
+ `--key`
110
+ The key of the string to create
111
+
112
+ `--value`
113
+ The value of the string to create
114
+
115
+ `--details`
116
+ The details of the string to create (optional)
117
+
118
+ ## Updating existing strings
119
+
120
+ `i18nexus update-string <namespace> <key>` or `i18nexus u <namespace> <key>`
121
+
122
+ ```sh
123
+ i18nexus u common welcome_msg -v 'Welcome' -k <PROJECT_API_KEY> -t <YOUR_PERSONAL_ACCESS_TOKEN>
124
+ ```
125
+
126
+ The above snippet will update a the value of the string with key `welcome_msg` in your `common` namespace to `Welcome`.
127
+
128
+ The first 2 arguments are the namespace and the key of the string you wish to update.
129
+
130
+ You can then update the key, value, details, and/or namespace by using the command options:
131
+
132
+ ### Options
133
+
134
+ | Option | Required? |
135
+ | ---------------------- | --------- |
136
+ | `--api-key` or `-k` | &#10004; |
137
+ | `--pat` or `-t` | &#10004; |
138
+ | `--namespace` or `-ns` | |
139
+ | `--key` or `-K` | |
140
+ | `--value` or `-v` | |
141
+ | `--details` or `-d` | |
142
+ | `--reset-confirmed` | |
143
+ | `--retain-confirmed` | |
144
+
145
+ ### Notes
146
+
147
+ `--api-key`
148
+ Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
149
+
150
+ `--pat`
151
+ A personal access token that you have generated in your i18nexus account (Can also be set using environment variable `I18NEXUS_PERSONAL_ACCESS_TOKEN`)
152
+
153
+ `--namespace`
154
+ The new namespace of the string
155
+
156
+ `--key`
157
+ The new key of the string
158
+
159
+ `--value`
160
+ The new value of the string
161
+
162
+ `--details`
163
+ The new details of the string
164
+
165
+ #### If you are updating the value of a string that contains translations that have been marked confirmed in i18nexus, you will be required to include one of the following options to your command:
166
+
167
+ `--reset-confirmed`
168
+ Confirmed translations of this string will be reset with machine translations of the new value
169
+
170
+ `--retain-confirmed`
171
+ Confirmed translations of this string will be retained
172
+
173
+ ## Deleting strings
174
+
175
+ `i18nexus delete-string <namespace> <key>` or `i18nexus d <namespace> <key>`
176
+
177
+ ```sh
178
+ i18nexus d common welcome_msg -k <PROJECT_API_KEY> -t <YOUR_PERSONAL_ACCESS_TOKEN>
179
+ ```
180
+
181
+ The above snippet will delete the string `welcome_msg` from your `common` namespace, along with its associated translations.
182
+
183
+ ### Options
184
+
185
+ | Option | Required? |
186
+ | ------------------- | --------- |
187
+ | `--api-key` or `-k` | &#10004; |
188
+ | `--pat` or `-t` | &#10004; |
189
+
190
+ ### Notes
191
+
192
+ `--api-key`
193
+ Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
194
+
195
+ `--pat`
196
+ A personal access token that you have generated in your i18nexus account (Can also be set using environment variable `I18NEXUS_PERSONAL_ACCESS_TOKEN`)
197
+
198
+ ## Importing strings into your i18nexus project
199
+
200
+ `i18nexus import <filePath>`
201
+
202
+ ```sh
203
+ i18nexus import ./en.json -ns common -k <PROJECT_API_KEY> -t <YOUR_PERSONAL_ACCESS_TOKEN>
204
+ ```
205
+
206
+ The above snippet will read the contents of `./en.json` and import them into the base language of your i18nexus project.
207
+
208
+ This is the equivalent of using the Import tool in the i18nexus web application. However, this CLI command only allows for uploading a JSON file for your base language.
209
+
210
+ ### Options
211
+
212
+ | Option | Required? |
213
+ | ------------------- | --------- |
214
+ | `--api-key` or `-k` | &#10004; |
215
+ | `--pat` or `-t` | &#10004; |
216
+ | `--overwrite` | |
217
+
218
+ ### Notes
219
+
220
+ `--api-key`
221
+ Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
222
+
223
+ `--pat`
224
+ A personal access token that you have generated in your i18nexus account (Can also be set using environment variable `I18NEXUS_PERSONAL_ACCESS_TOKEN`)
225
+
226
+ `--overwrite`
227
+ If any keys already exist in the target namespace, overwrite the values with the imported values.
228
+
229
+ ## Adding new namespaces
230
+
231
+ `i18nexus add-namespace <namespaceTitle>`
232
+
233
+ ```sh
234
+ i18nexus add-namespace common -k <PROJECT_API_KEY> -t <YOUR_PERSONAL_ACCESS_TOKEN>
235
+ ```
236
+
237
+ The above snippet will create a new namespace in your i18nexus project with the title `common`.
238
+
239
+ ### Options
240
+
241
+ | Option | Required? |
242
+ | ------------------- | --------- |
243
+ | `--api-key` or `-k` | &#10004; |
244
+ | `--pat` or `-t` | &#10004; |
245
+
246
+ ### Notes
247
+
248
+ `--api-key`
249
+ Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
250
+
251
+ `--pat`
252
+ A personal access token that you have generated in your i18nexus account (Can also be set using environment variable `I18NEXUS_PERSONAL_ACCESS_TOKEN`)
package/baseUrl.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = 'https://api.i18nexus.com';
package/bin/index.js CHANGED
@@ -2,13 +2,20 @@
2
2
  const pkg = require('../package.json');
3
3
 
4
4
  const program = require('commander');
5
- const pull = require('../pull');
5
+ const pull = require('../commands/pull');
6
+ const addString = require('../commands/addString');
7
+ const updateString = require('../commands/updateString');
8
+ const deleteString = require('../commands/deleteString');
9
+ const importJson = require('../commands/importJson');
10
+ const addNamespace = require('../commands/addNamespace');
6
11
 
7
12
  // Using Next's env variable loader because
8
13
  // Next supports more than just one .env file
9
14
  const { loadEnvConfig } = require('@next/env');
10
15
 
11
- loadEnvConfig(process.cwd());
16
+ if (process.env.I18NEXUS_NO_DOT_ENV !== 'true') {
17
+ loadEnvConfig(process.cwd());
18
+ }
12
19
 
13
20
  program.version(pkg.version);
14
21
 
@@ -44,4 +51,169 @@ program
44
51
  });
45
52
  });
46
53
 
54
+ program
55
+ .command('add-string')
56
+ .alias('a')
57
+ .description('Add a new base string to a namespace')
58
+ .requiredOption(
59
+ '-k, --api-key <apiKey>',
60
+ 'The API key for your project',
61
+ process.env.I18NEXUS_API_KEY
62
+ )
63
+ .requiredOption(
64
+ '-t, --pat <personalAccessToken>',
65
+ 'A personal access token generated for your account in i18nexus.',
66
+ process.env.I18NEXUS_PERSONAL_ACCESS_TOKEN
67
+ )
68
+ .requiredOption('-K, --key <stringKey>', 'The key of the string to create')
69
+ .requiredOption(
70
+ '-v, --value <stringValue>',
71
+ 'The value of the string to create'
72
+ )
73
+ .requiredOption(
74
+ '-ns, --namespace <stringNamespace>',
75
+ 'The namespace in which to create the string'
76
+ )
77
+ .option(
78
+ '-d, --details <stringDetails>',
79
+ 'The details of the string to create (optional)'
80
+ )
81
+ .action(options => {
82
+ addString({
83
+ key: options.key,
84
+ value: options.value,
85
+ details: options.details,
86
+ namespace: options.namespace,
87
+ apiKey: options.apiKey,
88
+ pat: options.pat
89
+ });
90
+ });
91
+
92
+ program
93
+ .command('update-string <namespaceOfString> <keyOfString>')
94
+ .alias('u')
95
+ .description('Update a base string through PATCH request')
96
+ .requiredOption(
97
+ '-k, --api-key <apiKey>',
98
+ 'The API key for your project',
99
+ process.env.I18NEXUS_API_KEY
100
+ )
101
+ .requiredOption(
102
+ '-t, --pat <personalAccessToken>',
103
+ 'A personal access token generated for your account in i18nexus.',
104
+ process.env.I18NEXUS_PERSONAL_ACCESS_TOKEN
105
+ )
106
+ .option('-K, --key <stringKey>', 'The new key of the string')
107
+ .option('-v, --value <stringValue>', 'The new value of the string')
108
+ .option(
109
+ '-ns, --namespace <stringNamespace>',
110
+ 'The new namespace of the string'
111
+ )
112
+ .option('-d, --details <stringDetails>', 'The new details of the string')
113
+ .option(
114
+ '--reset-confirmed',
115
+ 'Reset confirmed translations of this string with machine translations.'
116
+ )
117
+ .option(
118
+ '--retain-confirmed',
119
+ 'Do not reset confirmed translations of this string with machine translations.'
120
+ )
121
+ .action((namespaceOfString, keyOfString, options) => {
122
+ updateString({
123
+ id: {
124
+ namespace: namespaceOfString,
125
+ key: keyOfString
126
+ },
127
+ key: options.key,
128
+ value: options.value,
129
+ details: options.details,
130
+ namespace: options.namespace,
131
+ apiKey: options.apiKey,
132
+ pat: options.pat,
133
+ resetConfirmed: options.resetConfirmed,
134
+ retainConfirmed: options.retainConfirmed
135
+ });
136
+ });
137
+
138
+ program
139
+ .command('delete-string <namespaceOfString> <keyOfString>')
140
+ .alias('d')
141
+ .description('Delete a base string and its translations')
142
+ .requiredOption(
143
+ '-k, --api-key <apiKey>',
144
+ 'The API key for your project',
145
+ process.env.I18NEXUS_API_KEY
146
+ )
147
+ .requiredOption(
148
+ '-t, --pat <personalAccessToken>',
149
+ 'A personal access token generated for your account in i18nexus.',
150
+ process.env.I18NEXUS_PERSONAL_ACCESS_TOKEN
151
+ )
152
+ .action((namespaceOfString, keyOfString, options) => {
153
+ deleteString({
154
+ id: {
155
+ namespace: namespaceOfString,
156
+ key: keyOfString
157
+ },
158
+ apiKey: options.apiKey,
159
+ pat: options.pat
160
+ });
161
+ });
162
+
163
+ program
164
+ .command('import <filePath>')
165
+ .description(
166
+ 'Import base strings into your i18nexus project from a local JSON file.'
167
+ )
168
+ .requiredOption(
169
+ '-k, --api-key <apiKey>',
170
+ 'The API key for your project',
171
+ process.env.I18NEXUS_API_KEY
172
+ )
173
+ .requiredOption(
174
+ '-t, --pat <personalAccessToken>',
175
+ 'A personal access token generated for your account in i18nexus.',
176
+ process.env.I18NEXUS_PERSONAL_ACCESS_TOKEN
177
+ )
178
+ .requiredOption(
179
+ '-ns, --namespace <namespace>',
180
+ 'The namespace in which your strings will be imported.'
181
+ )
182
+ .option(
183
+ '--overwrite',
184
+ 'If any keys already exist in the target namespace, overwrite the values with the imported values.',
185
+ false
186
+ )
187
+ .action((filePath, options) => {
188
+ importJson({
189
+ apiKey: options.apiKey,
190
+ path: filePath,
191
+ overwrite: options.overwrite,
192
+ namespace: options.namespace,
193
+ pat: options.pat
194
+ });
195
+ });
196
+
197
+ program
198
+ .command('add-namespace <namespaceTitle>')
199
+ .alias('a-ns')
200
+ .description('Add a new namespace to your project')
201
+ .requiredOption(
202
+ '-k, --api-key <apiKey>',
203
+ 'The API key for your project',
204
+ process.env.I18NEXUS_API_KEY
205
+ )
206
+ .requiredOption(
207
+ '-t, --pat <personalAccessToken>',
208
+ 'A personal access token generated for your account in i18nexus',
209
+ process.env.I18NEXUS_PERSONAL_ACCESS_TOKEN
210
+ )
211
+ .action((namespaceTitle, options) => {
212
+ addNamespace({
213
+ title: namespaceTitle,
214
+ apiKey: options.apiKey,
215
+ pat: options.pat
216
+ });
217
+ });
218
+
47
219
  program.parse(process.argv);
@@ -0,0 +1,31 @@
1
+ const colors = require('colors');
2
+ const handleError = require('../handleError');
3
+ const handleFetch = require('../handleFetch');
4
+ const baseUrl = require('../baseUrl');
5
+
6
+ const addNamespace = async opt => {
7
+ let url = `${baseUrl}/project_resources/namespaces.json`;
8
+
9
+ url += `?api_key=${opt.apiKey}`;
10
+
11
+ const response = await handleFetch(url, {
12
+ method: 'POST',
13
+ body: JSON.stringify({
14
+ title: opt.title
15
+ }),
16
+ headers: {
17
+ Authorization: `Bearer ${opt.pat}`,
18
+ 'Content-Type': 'application/json'
19
+ }
20
+ });
21
+
22
+ if (response.status !== 200) {
23
+ return handleError(response);
24
+ }
25
+
26
+ await response.json();
27
+
28
+ console.log(colors.green(`New namespace added: "${opt.title}"`));
29
+ };
30
+
31
+ module.exports = addNamespace;
@@ -0,0 +1,35 @@
1
+ const colors = require('colors');
2
+ const handleError = require('../handleError');
3
+ const handleFetch = require('../handleFetch');
4
+ const baseUrl = require('../baseUrl');
5
+
6
+ const addString = async opt => {
7
+ let url = `${baseUrl}/project_resources/base_strings.json`;
8
+
9
+ url += `?api_key=${opt.apiKey}`;
10
+
11
+ const response = await handleFetch(url, {
12
+ method: 'POST',
13
+ body: JSON.stringify({
14
+ key: opt.key,
15
+ value: opt.value,
16
+ namespace: opt.namespace,
17
+ description: opt.details
18
+ }),
19
+ headers: {
20
+ Authorization: `Bearer ${opt.pat}`,
21
+ 'Content-Type': 'application/json'
22
+ }
23
+ });
24
+
25
+ if (response.status !== 200) {
26
+ return handleError(response);
27
+ }
28
+
29
+ await response.json();
30
+
31
+ console.log(colors.green(`New string added to namespace "${opt.namespace}"`));
32
+ console.log(colors.green(`"${opt.key}": "${opt.value}"`));
33
+ };
34
+
35
+ module.exports = addString;
@@ -0,0 +1,29 @@
1
+ const colors = require('colors');
2
+ const handleError = require('../handleError');
3
+ const handleFetch = require('../handleFetch');
4
+ const baseUrl = require('../baseUrl');
5
+
6
+ const addString = async opt => {
7
+ let url = `${baseUrl}/project_resources/base_strings.json`;
8
+
9
+ url += `?api_key=${opt.apiKey}`;
10
+
11
+ const response = await handleFetch(url, {
12
+ method: 'DELETE',
13
+ body: JSON.stringify({
14
+ id: opt.id
15
+ }),
16
+ headers: {
17
+ Authorization: `Bearer ${opt.pat}`,
18
+ 'Content-Type': 'application/json'
19
+ }
20
+ });
21
+
22
+ if (response.status !== 204) {
23
+ return handleError(response);
24
+ }
25
+
26
+ console.log(colors.green(`Deleted string "${opt.id.key}"`));
27
+ };
28
+
29
+ module.exports = addString;
@@ -0,0 +1,55 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const colors = require('colors');
4
+ const handleError = require('../handleError');
5
+ const handleFetch = require('../handleFetch');
6
+ const baseUrl = require('../baseUrl');
7
+
8
+ const importJson = async opt => {
9
+ let url = `${baseUrl}/project_resources/import.json`;
10
+
11
+ url += `?api_key=${opt.apiKey}`;
12
+
13
+ if (!fs.existsSync(opt.path)) {
14
+ console.log(colors.red(`File not found: ${opt.path}`));
15
+ process.exit(1);
16
+ }
17
+
18
+ if (path.extname(opt.path) !== '.json') {
19
+ console.log(colors.red(`File is not .json: ${opt.path}`));
20
+ process.exit(1);
21
+ }
22
+
23
+ const fileData = fs.readFileSync(opt.path);
24
+
25
+ let strings;
26
+ try {
27
+ strings = JSON.parse(fileData);
28
+ } catch (e) {
29
+ console.log(colors.red('Invalid JSON'));
30
+ process.exit(1);
31
+ }
32
+
33
+ const response = await handleFetch(url, {
34
+ method: 'POST',
35
+ body: JSON.stringify({
36
+ languages: {
37
+ project_base_language: strings
38
+ },
39
+ overwrite: opt.overwrite,
40
+ namespace: opt.namespace
41
+ }),
42
+ headers: {
43
+ Authorization: `Bearer ${opt.pat}`,
44
+ 'Content-Type': 'application/json'
45
+ }
46
+ });
47
+
48
+ if (response.status !== 200) {
49
+ return handleError(response);
50
+ }
51
+
52
+ console.log(colors.green(`Successfully imported strings from ${opt.path}`));
53
+ };
54
+
55
+ module.exports = importJson;
@@ -1,35 +1,15 @@
1
1
  const fs = require('fs');
2
- const fetch = require('node-fetch');
3
2
  const colors = require('colors');
4
-
5
- const handleError = async (response, usingVersion) => {
6
- let json, message;
7
-
8
- try {
9
- json = await response.json();
10
- message = json.error?.message;
11
- } catch (e) {}
12
-
13
- if (!message) {
14
- if (usingVersion) {
15
- message =
16
- 'There was a problem fetching your translations. Please ensure you are using the correct API key and a valid version number.';
17
- } else {
18
- message =
19
- 'There was a problem fetching your translations. Please try again in a moment.';
20
- }
21
- }
22
-
23
- console.log(colors.red(message));
24
- process.exit(1);
25
- };
3
+ const handleError = require('../handleError');
4
+ const handleFetch = require('../handleFetch');
5
+ const baseUrl = require('../baseUrl');
26
6
 
27
7
  const cleanDirectory = path => {
28
8
  if (!fs.existsSync(path)) {
29
9
  return;
30
10
  }
31
11
 
32
- // as safety precation, only delete folders that match regex
12
+ // as safety precaution, only delete folders that match regex
33
13
  const regex = /^[a-z]{2}(-[A-Z]{2,4})?$/;
34
14
 
35
15
  const contents = fs.readdirSync(path);
@@ -44,18 +24,25 @@ const cleanDirectory = path => {
44
24
  const pull = async opt => {
45
25
  const usingVersion = opt.version !== 'latest';
46
26
 
27
+ console.log(baseUrl);
28
+
47
29
  let url = usingVersion
48
30
  ? `https://cdn.i18nexus.com/versions/${opt.version}/translations.json`
49
- : 'https://api.i18nexus.com/project_resources/translations.json';
31
+ : `${baseUrl}/project_resources/translations.json`;
50
32
 
51
33
  url += `?api_key=${opt.apiKey}`;
52
34
 
53
35
  console.log(`Downloading translations to ${opt.path}...`);
54
36
 
55
- const response = await fetch(url);
37
+ const response = await handleFetch(url);
56
38
 
57
39
  if (response.status !== 200) {
58
- return handleError(response, usingVersion);
40
+ return handleError(
41
+ response,
42
+ usingVersion
43
+ ? 'There was a problem fetching your translations. Please ensure you are using the correct API key and a valid version number.'
44
+ : 'There was a problem fetching your translations. Please try again in a moment.'
45
+ );
59
46
  }
60
47
 
61
48
  const translations = await response.json();
@@ -0,0 +1,55 @@
1
+ const colors = require('colors');
2
+ const handleError = require('../handleError');
3
+ const handleFetch = require('../handleFetch');
4
+ const baseUrl = require('../baseUrl');
5
+
6
+ const addString = async opt => {
7
+ let url = `${baseUrl}/project_resources/base_strings.json`;
8
+
9
+ url += `?api_key=${opt.apiKey}`;
10
+
11
+ let resetConfirmed;
12
+ if (opt.resetConfirmed) {
13
+ resetConfirmed = true;
14
+ }
15
+
16
+ if (opt.retainConfirmed) {
17
+ resetConfirmed = false;
18
+ }
19
+
20
+ if (opt.resetConfirmed && opt.retainConfirmed) {
21
+ resetConfirmed = undefined;
22
+ }
23
+
24
+ const response = await handleFetch(url, {
25
+ method: 'PATCH',
26
+ body: JSON.stringify({
27
+ id: opt.id,
28
+ key: opt.key,
29
+ value: opt.value,
30
+ namespace: opt.namespace,
31
+ description: opt.details,
32
+ reset_confirmed: resetConfirmed
33
+ }),
34
+ headers: {
35
+ Authorization: `Bearer ${opt.pat}`,
36
+ 'Content-Type': 'application/json'
37
+ }
38
+ });
39
+
40
+ if (response.status !== 200) {
41
+ return handleError(response, null, message => {
42
+ if (message.includes('reset_confirmed')) {
43
+ return 'This string contains confirmed translations. To update this string, run this command again with either --reset-confirmed or --retain-confirmed.';
44
+ }
45
+
46
+ return message;
47
+ });
48
+ }
49
+
50
+ await response.json();
51
+
52
+ console.log(colors.green('Updated string'));
53
+ };
54
+
55
+ module.exports = addString;
package/handleError.js ADDED
@@ -0,0 +1,26 @@
1
+ const colors = require('colors');
2
+
3
+ const handleError = async (
4
+ response,
5
+ fallbackMessage = 'An unexpected error occured. Please try again in a moment.',
6
+ transformMessage
7
+ ) => {
8
+ let json, message;
9
+
10
+ try {
11
+ json = await response.json();
12
+ message = json.error?.message;
13
+ if (message && transformMessage) {
14
+ message = transformMessage(message);
15
+ }
16
+ } catch (e) {}
17
+
18
+ if (!message) {
19
+ message = fallbackMessage;
20
+ }
21
+
22
+ console.log(colors.red(message));
23
+ process.exit(1);
24
+ };
25
+
26
+ module.exports = handleError;
package/handleFetch.js ADDED
@@ -0,0 +1,18 @@
1
+ const fetch = require('node-fetch');
2
+ const HttpsProxyAgent = require('https-proxy-agent');
3
+
4
+ const handleFetch = (url, fetchOptions = {}) => {
5
+ const proxy =
6
+ process.env.http_proxy ||
7
+ process.env.HTTP_PROXY ||
8
+ process.env.https_proxy ||
9
+ process.env.HTTPS_PROXY;
10
+
11
+ if (proxy) {
12
+ fetchOptions.agent = new HttpsProxyAgent(proxy);
13
+ }
14
+
15
+ return fetch(url, fetchOptions);
16
+ };
17
+
18
+ module.exports = handleFetch;
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "i18nexus-cli",
3
- "version": "2.0.2",
3
+ "version": "3.1.0",
4
4
  "description": "Command line interface (CLI) for accessing the i18nexus API",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "i18nexus": "./bin/index.js"
8
8
  },
9
9
  "keywords": [
10
- "i18next",
11
10
  "i18nexus",
12
- "cli"
11
+ "cli",
12
+ "i18next"
13
13
  ],
14
14
  "author": "i18nexus",
15
15
  "license": "MIT",
@@ -17,6 +17,7 @@
17
17
  "@next/env": "^11.0.1",
18
18
  "colors": "^1.4.0",
19
19
  "commander": "^7.2.0",
20
- "node-fetch": "^2.6.1"
20
+ "https-proxy-agent": "^5.0.0",
21
+ "node-fetch": "^2.6.7"
21
22
  }
22
23
  }