i18nexus-cli 3.8.3 → 4.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 +87 -11
- package/bin/index.js +43 -0
- package/commands/bulkAddStrings.js +75 -0
- package/commands/project.js +22 -0
- package/commands/pull.js +1 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,17 +49,6 @@ If your i18nexus project is set to **`i18next`**:
|
|
|
49
49
|
└── common.json
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
If your i18nexus project is set to **`i18next`** and the CLI detects you're using **Next.js + App Router**:
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
.
|
|
56
|
-
└── locales
|
|
57
|
-
├── en
|
|
58
|
-
| └── common.json
|
|
59
|
-
└── de
|
|
60
|
-
└── common.json
|
|
61
|
-
```
|
|
62
|
-
|
|
63
52
|
If your i18nexus project is set to **`next-intl`**:
|
|
64
53
|
|
|
65
54
|
```
|
|
@@ -125,6 +114,41 @@ Before download, clears your destination folder specified in --path. As a safety
|
|
|
125
114
|
`--compact`
|
|
126
115
|
Output JSON without extra whitespace (default is pretty-printed)
|
|
127
116
|
|
|
117
|
+
## Viewing project metadata
|
|
118
|
+
|
|
119
|
+
`i18nexus project`
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
i18nexus project -k <PROJECT_API_KEY>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The above snippet prints your i18nexus project metadata as formatted JSON. This is useful for checking your project title, base language, enabled namespaces, and configured languages from the command line.
|
|
126
|
+
|
|
127
|
+
Example output:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"id": 123,
|
|
132
|
+
"title": "My Project",
|
|
133
|
+
"library": "i18next",
|
|
134
|
+
"language": "en",
|
|
135
|
+
"languages": ["de"],
|
|
136
|
+
"use_namespaces": true,
|
|
137
|
+
"namespaces": ["common", "auth"]
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Options
|
|
142
|
+
|
|
143
|
+
| Option | Required? |
|
|
144
|
+
| ------------------- | --------- |
|
|
145
|
+
| `--api-key` or `-k` | ✔ |
|
|
146
|
+
|
|
147
|
+
### Notes
|
|
148
|
+
|
|
149
|
+
`--api-key`
|
|
150
|
+
Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
|
|
151
|
+
|
|
128
152
|
## Personal Access Tokens
|
|
129
153
|
|
|
130
154
|
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.
|
|
@@ -143,6 +167,8 @@ The above snippet will create a new string in your i18nexus project in your `com
|
|
|
143
167
|
|
|
144
168
|
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.
|
|
145
169
|
|
|
170
|
+
If you want to add multiple strings at once, see `i18nexus bulk-add-strings` and `i18nexus import`.
|
|
171
|
+
|
|
146
172
|
### Options
|
|
147
173
|
|
|
148
174
|
| Option | Required? |
|
|
@@ -297,6 +323,56 @@ The namespace in which your strings will be imported. (Only required if your pro
|
|
|
297
323
|
`--overwrite`
|
|
298
324
|
If any keys already exist in the target namespace, overwrite the values with the imported values.
|
|
299
325
|
|
|
326
|
+
## Adding multiple strings
|
|
327
|
+
|
|
328
|
+
`i18nexus bulk-add-strings <filePath>` or `i18nexus ba <filePath>`
|
|
329
|
+
|
|
330
|
+
```sh
|
|
331
|
+
i18nexus ba ./base-strings.json -ns common -k <PROJECT_API_KEY> -t <YOUR_PERSONAL_ACCESS_TOKEN>
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
The above snippet will create multiple new strings in your i18nexus project from a local JSON file.
|
|
335
|
+
|
|
336
|
+
Use this command when you want to create strings with `notes` and/or `ai_instructions`. If you only need to import plain key/value JSON, use `i18nexus import` instead.
|
|
337
|
+
|
|
338
|
+
The JSON file must contain a top-level array of objects. Each object should include `key` and `value`, and can optionally include `notes` and `ai_instructions`.
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
[
|
|
342
|
+
{
|
|
343
|
+
"key": "welcome_title",
|
|
344
|
+
"value": "Welcome to my app",
|
|
345
|
+
"notes": "Homepage hero title"
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
"key": "welcome_cta",
|
|
349
|
+
"value": "Get started",
|
|
350
|
+
"ai_instructions": "Use action-oriented onboarding language"
|
|
351
|
+
}
|
|
352
|
+
]
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
If a key already exists in the target namespace, it will be skipped and reported back by the CLI.
|
|
356
|
+
|
|
357
|
+
### Options
|
|
358
|
+
|
|
359
|
+
| Option | Required? |
|
|
360
|
+
| ---------------------- | ------------- |
|
|
361
|
+
| `--api-key` or `-k` | ✔ |
|
|
362
|
+
| `--pat` or `-t` | ✔ |
|
|
363
|
+
| `--namespace` or `-ns` | Conditionally |
|
|
364
|
+
|
|
365
|
+
### Notes
|
|
366
|
+
|
|
367
|
+
`--api-key`
|
|
368
|
+
Your project API key (Can also be set using environment variable `I18NEXUS_API_KEY`)
|
|
369
|
+
|
|
370
|
+
`--pat`
|
|
371
|
+
A personal access token that you have generated in your i18nexus account (Can also be set using environment variable `I18NEXUS_PERSONAL_ACCESS_TOKEN`)
|
|
372
|
+
|
|
373
|
+
`--namespace`
|
|
374
|
+
The namespace in which to create the strings. Only required if your project uses namespaces and has more than one namespace.
|
|
375
|
+
|
|
300
376
|
## Adding new namespaces
|
|
301
377
|
|
|
302
378
|
`i18nexus add-namespace <namespaceTitle>`
|
package/bin/index.js
CHANGED
|
@@ -3,7 +3,9 @@ const pkg = require('../package.json');
|
|
|
3
3
|
|
|
4
4
|
const program = require('commander');
|
|
5
5
|
const pull = require('../commands/pull');
|
|
6
|
+
const project = require('../commands/project');
|
|
6
7
|
const addString = require('../commands/addString');
|
|
8
|
+
const bulkAddStrings = require('../commands/bulkAddStrings');
|
|
7
9
|
const updateString = require('../commands/updateString');
|
|
8
10
|
const deleteString = require('../commands/deleteString');
|
|
9
11
|
const importJson = require('../commands/importJson');
|
|
@@ -20,6 +22,20 @@ if (process.env.I18NEXUS_NO_DOT_ENV !== 'true') {
|
|
|
20
22
|
|
|
21
23
|
program.version(pkg.version);
|
|
22
24
|
|
|
25
|
+
program
|
|
26
|
+
.command('project')
|
|
27
|
+
.description('Show metadata about your i18nexus project as JSON')
|
|
28
|
+
.requiredOption(
|
|
29
|
+
'-k, --api-key <apiKey>',
|
|
30
|
+
'The API key for your project',
|
|
31
|
+
process.env.I18NEXUS_API_KEY
|
|
32
|
+
)
|
|
33
|
+
.action(options => {
|
|
34
|
+
project({
|
|
35
|
+
apiKey: options.apiKey
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
23
39
|
program
|
|
24
40
|
.command('pull')
|
|
25
41
|
.description('Download all translations as .json files')
|
|
@@ -106,6 +122,33 @@ program
|
|
|
106
122
|
});
|
|
107
123
|
});
|
|
108
124
|
|
|
125
|
+
program
|
|
126
|
+
.command('bulk-add-strings <filePath>')
|
|
127
|
+
.alias('ba')
|
|
128
|
+
.description('Add multiple base strings to a namespace from a local JSON file')
|
|
129
|
+
.requiredOption(
|
|
130
|
+
'-k, --api-key <apiKey>',
|
|
131
|
+
'The API key for your project',
|
|
132
|
+
process.env.I18NEXUS_API_KEY
|
|
133
|
+
)
|
|
134
|
+
.requiredOption(
|
|
135
|
+
'-t, --pat <personalAccessToken>',
|
|
136
|
+
'A personal access token generated for your account in i18nexus.',
|
|
137
|
+
process.env.I18NEXUS_PERSONAL_ACCESS_TOKEN
|
|
138
|
+
)
|
|
139
|
+
.option(
|
|
140
|
+
'-ns, --namespace <stringNamespace>',
|
|
141
|
+
'The namespace in which to create the strings (Only required if your project uses namespaces and has more than one namespace)'
|
|
142
|
+
)
|
|
143
|
+
.action((filePath, options) => {
|
|
144
|
+
bulkAddStrings({
|
|
145
|
+
path: filePath,
|
|
146
|
+
namespace: options.namespace,
|
|
147
|
+
apiKey: options.apiKey,
|
|
148
|
+
pat: options.pat
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
109
152
|
program
|
|
110
153
|
.command('update-string <namespace:key>')
|
|
111
154
|
.alias('u')
|
|
@@ -0,0 +1,75 @@
|
|
|
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 bulkAddStrings = async opt => {
|
|
9
|
+
let url = `${baseUrl}/project_resources/base_strings/bulk_create.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 baseStrings;
|
|
26
|
+
try {
|
|
27
|
+
baseStrings = JSON.parse(fileData);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.log(colors.red('Invalid JSON'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!Array.isArray(baseStrings)) {
|
|
34
|
+
console.log(colors.red('Invalid JSON: expected a top-level array of base string objects'));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const normalizedBaseStrings = baseStrings.map(baseString => ({
|
|
39
|
+
...baseString,
|
|
40
|
+
description: baseString.notes ?? baseString.description
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
const response = await handleFetch(url, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
namespace: opt.namespace,
|
|
47
|
+
base_strings: normalizedBaseStrings
|
|
48
|
+
}),
|
|
49
|
+
headers: {
|
|
50
|
+
Authorization: `Bearer ${opt.pat}`,
|
|
51
|
+
'Content-Type': 'application/json'
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (response.status !== 200) {
|
|
56
|
+
return handleError(response);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const json = await response.json();
|
|
60
|
+
const invalidKeys = json.base_strings_invalid_values || [];
|
|
61
|
+
|
|
62
|
+
console.log(
|
|
63
|
+
colors.green(`Successfully submitted strings from ${opt.path} for import`)
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (invalidKeys.length > 0) {
|
|
67
|
+
console.log(
|
|
68
|
+
colors.yellow(
|
|
69
|
+
`Skipped existing keys: ${invalidKeys.join(', ')}`
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
module.exports = bulkAddStrings;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const handleError = require('../handleError');
|
|
2
|
+
const handleFetch = require('../handleFetch');
|
|
3
|
+
const baseUrl = require('../baseUrl');
|
|
4
|
+
|
|
5
|
+
const project = async opt => {
|
|
6
|
+
const url = `${baseUrl}/project_resources/project.json?api_key=${opt.apiKey}`;
|
|
7
|
+
|
|
8
|
+
const response = await handleFetch(url);
|
|
9
|
+
|
|
10
|
+
if (response.status !== 200) {
|
|
11
|
+
return handleError(
|
|
12
|
+
response,
|
|
13
|
+
'There was a problem fetching your project. Please try again in a moment.'
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const json = await response.json();
|
|
18
|
+
|
|
19
|
+
console.log(JSON.stringify(json, null, 2));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports = project;
|
package/commands/pull.js
CHANGED
|
@@ -45,20 +45,7 @@ const pull = async (opt, internalOptions = {}) => {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
if (projectLibrary === 'i18next') {
|
|
48
|
-
|
|
49
|
-
fs.existsSync(`${process.cwd()}/app`) ||
|
|
50
|
-
fs.existsSync(`${process.cwd()}/src/app`);
|
|
51
|
-
const usingAppRouter =
|
|
52
|
-
hasAppDir &&
|
|
53
|
-
(fs.existsSync(`${process.cwd()}/.next`) ||
|
|
54
|
-
fs.existsSync(`${process.cwd()}/next.config.js`) ||
|
|
55
|
-
fs.existsSync(`${process.cwd()}/next.config.ts`));
|
|
56
|
-
|
|
57
|
-
if (usingAppRouter) {
|
|
58
|
-
path = `${process.cwd()}/locales`;
|
|
59
|
-
} else {
|
|
60
|
-
path = `${process.cwd()}/public/locales`;
|
|
61
|
-
}
|
|
48
|
+
path = `${process.cwd()}/public/locales`;
|
|
62
49
|
} else if (projectLibrary === 'react-intl') {
|
|
63
50
|
const hasSrcDir = fs.existsSync(`${process.cwd()}/src`);
|
|
64
51
|
|