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 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` | &#10004; |
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` | &#10004; |
362
+ | `--pat` or `-t` | &#10004; |
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
- const hasAppDir =
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18nexus-cli",
3
- "version": "3.8.3",
3
+ "version": "4.1.0",
4
4
  "description": "Command line interface (CLI) for accessing the i18nexus API",
5
5
  "main": "index.js",
6
6
  "bin": {