@scoutello/i18n-magic 0.14.0 → 0.15.1
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 +23 -14
- package/dist/commands/replace.d.ts +1 -1
- package/dist/i18n-magic.cjs.development.js +98 -57
- package/dist/i18n-magic.cjs.development.js.map +1 -1
- package/dist/i18n-magic.cjs.production.min.js +1 -1
- package/dist/i18n-magic.cjs.production.min.js.map +1 -1
- package/dist/i18n-magic.esm.js +98 -57
- package/dist/i18n-magic.esm.js.map +1 -1
- package/dist/lib/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/commands/replace.ts +17 -2
- package/src/index.ts +44 -30
- package/src/lib/types.ts +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Your CLI toolkit to help you with managing your translations in your project.
|
|
4
4
|
|
|
5
|
-
This currently
|
|
5
|
+
This currently works for JSON based translation systems
|
|
6
6
|
|
|
7
7
|
To use:
|
|
8
8
|
|
|
@@ -23,28 +23,31 @@ module.exports = {
|
|
|
23
23
|
context:
|
|
24
24
|
'This is a context which increases the quality of the translations by giving context to the LLM',
|
|
25
25
|
// Optional configurations
|
|
26
|
-
model: 'gemini-2.0-flash-lite', // or any OpenAI/Gemini model like '
|
|
26
|
+
model: 'gemini-2.0-flash-lite', // or any OpenAI/Gemini model like 'gpt-4.1-mini'
|
|
27
27
|
OPENAI_API_KEY: '.', // Alternative to using .env file
|
|
28
28
|
GEMINI_API_KEY: '', // Alternative to using .env file
|
|
29
|
-
disableTranslation: false, // Set to true to skip automatic translations during the scan step. Useful if you want to sync the other languages during CI/CD
|
|
29
|
+
disableTranslation: false, // Set to true to skip automatic translations during the scan step. Useful if you want to sync the other languages during CI/CD using sync.
|
|
30
30
|
};
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
You can also provide custom functions for `loadPath` and `savePath` to store translations in other systems like S3:
|
|
34
34
|
|
|
35
35
|
```js
|
|
36
|
-
|
|
37
|
-
// Basic configurations
|
|
38
|
-
globPatterns: ['./components/**/*.tsx', './pages/**/*.tsx', './lib/**/*.ts'],
|
|
39
|
-
locales: ['en', 'de'],
|
|
40
|
-
defaultLocale: 'de',
|
|
41
|
-
defaultNamespace: 'common',
|
|
42
|
-
namespaces: ['common', 'forms'],
|
|
36
|
+
const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3")
|
|
43
37
|
|
|
38
|
+
const s3Client = new S3Client({
|
|
39
|
+
region: process.env.S3_REGION,
|
|
40
|
+
credentials: {
|
|
41
|
+
accessKeyId: process.env.S3_ACCESS_KEY,
|
|
42
|
+
secretAccessKey: process.env.S3_SECRET_KEY,
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
...
|
|
44
48
|
// Custom load function
|
|
45
49
|
loadPath: async (locale, namespace) => {
|
|
46
50
|
// Example: Load from S3
|
|
47
|
-
const s3Client = new S3Client({ region: 'us-east-1' });
|
|
48
51
|
const response = await s3Client.send(
|
|
49
52
|
new GetObjectCommand({
|
|
50
53
|
Bucket: 'my-translations-bucket',
|
|
@@ -58,7 +61,6 @@ module.exports = {
|
|
|
58
61
|
// Custom save function
|
|
59
62
|
savePath: async (locale, namespace, data) => {
|
|
60
63
|
// Example: Save to S3
|
|
61
|
-
const s3Client = new S3Client({ region: 'us-east-1' });
|
|
62
64
|
await s3Client.send(
|
|
63
65
|
new PutObjectCommand({
|
|
64
66
|
Bucket: 'my-translations-bucket',
|
|
@@ -74,7 +76,7 @@ module.exports = {
|
|
|
74
76
|
then just run:
|
|
75
77
|
|
|
76
78
|
```bash
|
|
77
|
-
npx i18n-magic [command]
|
|
79
|
+
npx @scoutello/i18n-magic [command]
|
|
78
80
|
```
|
|
79
81
|
|
|
80
82
|
`scan`
|
|
@@ -85,10 +87,17 @@ Scan for missing translations, get prompted for each, translate it to the other
|
|
|
85
87
|
|
|
86
88
|
Replace a translation based on the key, and translate it to the other locales and save it to the JSON file.
|
|
87
89
|
|
|
90
|
+
You can specify the key in two ways:
|
|
91
|
+
|
|
92
|
+
- As a positional argument: `npx @scoutello/i18n-magic replace your.translation.key`
|
|
93
|
+
- Using the option flag: `npx @scoutello/i18n-magic replace --key your.translation.key`
|
|
94
|
+
|
|
95
|
+
If no key is provided, you will be prompted to enter one.
|
|
96
|
+
|
|
88
97
|
`check-missing`
|
|
89
98
|
|
|
90
99
|
Checks if there are any missing translations. Useful for CI/CD or for a husky hook.
|
|
91
100
|
|
|
92
101
|
`sync`
|
|
93
102
|
|
|
94
|
-
Sync the translations from the default locale to the other locales. Useful for a CI/CD pipeline or husky hook.
|
|
103
|
+
Sync the translations from the default locale to the other locales. Useful for a CI/CD pipeline or husky hook. Should be used together with `disableTranslation: true`
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Configuration } from "../lib/types";
|
|
2
|
-
export declare const replaceTranslation: (config: Configuration) => Promise<void>;
|
|
2
|
+
export declare const replaceTranslation: (config: Configuration, key?: string) => Promise<void>;
|
|
@@ -995,7 +995,7 @@ var getKeyToReplace = /*#__PURE__*/function () {
|
|
|
995
995
|
};
|
|
996
996
|
}();
|
|
997
997
|
var replaceTranslation = /*#__PURE__*/function () {
|
|
998
|
-
var _ref2 = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(config) {
|
|
998
|
+
var _ref2 = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(config, key) {
|
|
999
999
|
var loadPath, savePath, defaultLocale, defaultNamespace, locales, context, openai, keys, keyToReplace, newTranslation, _iterator, _step, locale, newValue, _object, translation, existingKeys;
|
|
1000
1000
|
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
|
|
1001
1001
|
while (1) switch (_context2.prev = _context2.next) {
|
|
@@ -1005,32 +1005,55 @@ var replaceTranslation = /*#__PURE__*/function () {
|
|
|
1005
1005
|
return loadLocalesFile(config.loadPath, config.defaultLocale, config.defaultNamespace);
|
|
1006
1006
|
case 3:
|
|
1007
1007
|
keys = _context2.sent;
|
|
1008
|
-
|
|
1008
|
+
if (!key) {
|
|
1009
|
+
_context2.next = 16;
|
|
1010
|
+
break;
|
|
1011
|
+
}
|
|
1012
|
+
if (!keys[key]) {
|
|
1013
|
+
_context2.next = 10;
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
keyToReplace = key;
|
|
1017
|
+
console.log("The key \"" + keyToReplace + "\" exists.");
|
|
1018
|
+
_context2.next = 14;
|
|
1019
|
+
break;
|
|
1020
|
+
case 10:
|
|
1021
|
+
console.log("The key \"" + key + "\" does not exist.");
|
|
1022
|
+
_context2.next = 13;
|
|
1009
1023
|
return getKeyToReplace(keys);
|
|
1010
|
-
case
|
|
1024
|
+
case 13:
|
|
1025
|
+
keyToReplace = _context2.sent;
|
|
1026
|
+
case 14:
|
|
1027
|
+
_context2.next = 19;
|
|
1028
|
+
break;
|
|
1029
|
+
case 16:
|
|
1030
|
+
_context2.next = 18;
|
|
1031
|
+
return getKeyToReplace(keys);
|
|
1032
|
+
case 18:
|
|
1011
1033
|
keyToReplace = _context2.sent;
|
|
1034
|
+
case 19:
|
|
1012
1035
|
console.log("The current translation in " + defaultLocale + " for \"" + keyToReplace + "\" is \"" + keys[keyToReplace] + "\".");
|
|
1013
|
-
_context2.next =
|
|
1036
|
+
_context2.next = 22;
|
|
1014
1037
|
return getTextInput("Enter the new translation: ");
|
|
1015
|
-
case
|
|
1038
|
+
case 22:
|
|
1016
1039
|
newTranslation = _context2.sent;
|
|
1017
1040
|
_iterator = _createForOfIteratorHelperLoose(locales);
|
|
1018
|
-
case
|
|
1041
|
+
case 24:
|
|
1019
1042
|
if ((_step = _iterator()).done) {
|
|
1020
|
-
_context2.next =
|
|
1043
|
+
_context2.next = 43;
|
|
1021
1044
|
break;
|
|
1022
1045
|
}
|
|
1023
1046
|
locale = _step.value;
|
|
1024
1047
|
newValue = "";
|
|
1025
1048
|
if (!(locale === defaultLocale)) {
|
|
1026
|
-
_context2.next =
|
|
1049
|
+
_context2.next = 31;
|
|
1027
1050
|
break;
|
|
1028
1051
|
}
|
|
1029
1052
|
newValue = newTranslation;
|
|
1030
|
-
_context2.next =
|
|
1053
|
+
_context2.next = 35;
|
|
1031
1054
|
break;
|
|
1032
|
-
case
|
|
1033
|
-
_context2.next =
|
|
1055
|
+
case 31:
|
|
1056
|
+
_context2.next = 33;
|
|
1034
1057
|
return translateKey({
|
|
1035
1058
|
context: context,
|
|
1036
1059
|
inputLanguage: defaultLocale,
|
|
@@ -1039,27 +1062,27 @@ var replaceTranslation = /*#__PURE__*/function () {
|
|
|
1039
1062
|
openai: openai,
|
|
1040
1063
|
model: config.model
|
|
1041
1064
|
});
|
|
1042
|
-
case
|
|
1065
|
+
case 33:
|
|
1043
1066
|
translation = _context2.sent;
|
|
1044
1067
|
newValue = translation[keyToReplace];
|
|
1045
|
-
case
|
|
1046
|
-
_context2.next =
|
|
1068
|
+
case 35:
|
|
1069
|
+
_context2.next = 37;
|
|
1047
1070
|
return loadLocalesFile(loadPath, locale, defaultNamespace);
|
|
1048
|
-
case
|
|
1071
|
+
case 37:
|
|
1049
1072
|
existingKeys = _context2.sent;
|
|
1050
1073
|
existingKeys[keyToReplace] = newValue;
|
|
1051
1074
|
writeLocalesFile(savePath, locale, defaultNamespace, existingKeys);
|
|
1052
1075
|
console.log("The new translation for \"" + keyToReplace + "\" in " + locale + " is \"" + newValue + "\".");
|
|
1053
|
-
case
|
|
1054
|
-
_context2.next =
|
|
1076
|
+
case 41:
|
|
1077
|
+
_context2.next = 24;
|
|
1055
1078
|
break;
|
|
1056
|
-
case
|
|
1079
|
+
case 43:
|
|
1057
1080
|
case "end":
|
|
1058
1081
|
return _context2.stop();
|
|
1059
1082
|
}
|
|
1060
1083
|
}, _callee2);
|
|
1061
1084
|
}));
|
|
1062
|
-
return function replaceTranslation(_x2) {
|
|
1085
|
+
return function replaceTranslation(_x2, _x3) {
|
|
1063
1086
|
return _ref2.apply(this, arguments);
|
|
1064
1087
|
};
|
|
1065
1088
|
}();
|
|
@@ -1363,44 +1386,62 @@ var commands = [{
|
|
|
1363
1386
|
}];
|
|
1364
1387
|
var _loop = function _loop() {
|
|
1365
1388
|
var command = _commands[_i];
|
|
1366
|
-
program.command(command.name).description(command.description)
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
openai
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1389
|
+
var cmd = program.command(command.name).description(command.description);
|
|
1390
|
+
// Add key option to replace command
|
|
1391
|
+
if (command.name === "replace") {
|
|
1392
|
+
cmd.option("-k, --key <key>", "translation key to replace").allowExcessArguments(true).argument("[key]", "translation key to replace");
|
|
1393
|
+
}
|
|
1394
|
+
cmd.action( /*#__PURE__*/function () {
|
|
1395
|
+
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(arg, options) {
|
|
1396
|
+
var _config$model;
|
|
1397
|
+
var res, config, isGemini, openaiKey, geminiKey, key, keyType, openai, keyToUse;
|
|
1398
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
1399
|
+
while (1) switch (_context.prev = _context.next) {
|
|
1400
|
+
case 0:
|
|
1401
|
+
res = dotenv.config({
|
|
1402
|
+
path: program.opts().env || ".env"
|
|
1403
|
+
});
|
|
1404
|
+
_context.next = 3;
|
|
1405
|
+
return loadConfig({
|
|
1406
|
+
configPath: program.opts().config
|
|
1407
|
+
});
|
|
1408
|
+
case 3:
|
|
1409
|
+
config = _context.sent;
|
|
1410
|
+
isGemini = (_config$model = config.model) == null ? void 0 : _config$model.includes("gemini"); // Get API key from environment or config
|
|
1411
|
+
openaiKey = res.parsed.OPENAI_API_KEY || config.OPENAI_API_KEY;
|
|
1412
|
+
geminiKey = res.parsed.GEMINI_API_KEY || config.GEMINI_API_KEY; // Select appropriate key based on model type
|
|
1413
|
+
key = isGemini ? geminiKey : openaiKey;
|
|
1414
|
+
if (!key) {
|
|
1415
|
+
keyType = isGemini ? "GEMINI_API_KEY" : "OPENAI_API_KEY";
|
|
1416
|
+
console.error("Please provide a" + (isGemini ? " Gemini" : "n OpenAI") + " API key in your .env file or config, called " + keyType + ".");
|
|
1417
|
+
process.exit(1);
|
|
1418
|
+
}
|
|
1419
|
+
openai = new OpenAI(_extends({
|
|
1420
|
+
apiKey: key
|
|
1421
|
+
}, isGemini && {
|
|
1422
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/"
|
|
1423
|
+
})); // For replace command, check for key in argument or option
|
|
1424
|
+
if (command.name === "replace") {
|
|
1425
|
+
// If key is provided as positional argument, use that first
|
|
1426
|
+
keyToUse = typeof arg === "string" ? arg : options.key;
|
|
1427
|
+
command.action(_extends({}, config, {
|
|
1428
|
+
openai: openai
|
|
1429
|
+
}), keyToUse);
|
|
1430
|
+
} else {
|
|
1431
|
+
command.action(_extends({}, config, {
|
|
1432
|
+
openai: openai
|
|
1433
|
+
}));
|
|
1434
|
+
}
|
|
1435
|
+
case 11:
|
|
1436
|
+
case "end":
|
|
1437
|
+
return _context.stop();
|
|
1438
|
+
}
|
|
1439
|
+
}, _callee);
|
|
1440
|
+
}));
|
|
1441
|
+
return function (_x, _x2) {
|
|
1442
|
+
return _ref.apply(this, arguments);
|
|
1443
|
+
};
|
|
1444
|
+
}());
|
|
1404
1445
|
};
|
|
1405
1446
|
for (var _i = 0, _commands = commands; _i < _commands.length; _i++) {
|
|
1406
1447
|
_loop();
|