@strato-admin/i18n-cli 0.1.0 → 0.3.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/dist/compile.js +142 -0
- package/dist/extract.js +558 -0
- package/dist/strato-i18n/src/formatter.d.ts +10 -0
- package/dist/strato-i18n/src/formatter.js +62 -0
- package/dist/strato-i18n/src/hash.d.ts +10 -0
- package/dist/strato-i18n/src/hash.js +26 -0
- package/dist/strato-i18n/src/icuI18nProvider.d.ts +2 -0
- package/dist/strato-i18n/src/icuI18nProvider.js +92 -0
- package/dist/strato-i18n/src/index.d.ts +3 -0
- package/dist/strato-i18n/src/index.js +19 -0
- package/dist/strato-i18n-cli/src/cli/compile.d.ts +2 -0
- package/dist/strato-i18n-cli/src/cli/compile.js +142 -0
- package/dist/strato-i18n-cli/src/cli/extract.d.ts +2 -0
- package/dist/strato-i18n-cli/src/cli/extract.js +558 -0
- package/package.json +6 -3
- package/src/cli/compile.ts +65 -25
- package/src/cli/extract.ts +298 -62
- package/src/i18n/extracted-messages.json +8 -8
- package/dist/cli/compile.js +0 -73
- package/dist/cli/extract.js +0 -317
- /package/dist/{cli/compile.d.ts → compile.d.ts} +0 -0
- /package/dist/{cli/extract.d.ts → extract.d.ts} +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.icuI18nProvider = void 0;
|
|
4
|
+
const intl_messageformat_1 = require("intl-messageformat");
|
|
5
|
+
const hash_1 = require("./hash");
|
|
6
|
+
const icuI18nProvider = (getMessages, initialLocale = 'en', availableLocales = [{ locale: 'en', name: 'English' }]) => {
|
|
7
|
+
let locale = initialLocale;
|
|
8
|
+
let messages = getMessages(initialLocale);
|
|
9
|
+
const formatters = new Map();
|
|
10
|
+
if (messages instanceof Promise) {
|
|
11
|
+
throw new Error(`The i18nProvider returned a Promise for the messages of the default locale (${initialLocale}). Please update your i18nProvider to return the messages of the default locale in a synchronous way.`);
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
translate: (key, options = {}) => {
|
|
15
|
+
let finalKey = key;
|
|
16
|
+
let finalOptions = options;
|
|
17
|
+
// Handle React Admin's special validation error format
|
|
18
|
+
if (typeof key === 'string' && key.startsWith('@@react-admin@@')) {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(key.substring(15)); // 15 is the length of '@@react-admin@@'
|
|
21
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
22
|
+
finalKey = parsed.message;
|
|
23
|
+
finalOptions = { ...options, ...parsed.args };
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
finalKey = parsed;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
// Fallback to original key if parsing fails
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const { _: defaultMessage, ...values } = finalOptions;
|
|
34
|
+
// 1. Generate the hash ID for the English key
|
|
35
|
+
const msgid = (0, hash_1.generateMessageId)(finalKey);
|
|
36
|
+
// 2. Lookup by hash first, then fall back to literal key (for ra.* keys)
|
|
37
|
+
const message = messages[msgid] || messages[finalKey];
|
|
38
|
+
if (message === undefined) {
|
|
39
|
+
if (defaultMessage === undefined)
|
|
40
|
+
return finalKey;
|
|
41
|
+
try {
|
|
42
|
+
const formatter = new intl_messageformat_1.IntlMessageFormat(defaultMessage, locale);
|
|
43
|
+
return formatter.format(values);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return defaultMessage;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (typeof message !== 'string') {
|
|
50
|
+
return finalKey;
|
|
51
|
+
}
|
|
52
|
+
const cacheKey = `${locale}_${finalKey}`;
|
|
53
|
+
let formatter = formatters.get(cacheKey);
|
|
54
|
+
if (!formatter) {
|
|
55
|
+
try {
|
|
56
|
+
formatter = new intl_messageformat_1.IntlMessageFormat(message, locale);
|
|
57
|
+
formatters.set(cacheKey, formatter);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(`Error parsing message for key "${finalKey}":`, error);
|
|
61
|
+
return defaultMessage !== undefined ? defaultMessage : finalKey;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
return formatter.format(values);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error(`Error formatting message for key "${finalKey}":`, error);
|
|
69
|
+
return defaultMessage !== undefined ? defaultMessage : finalKey;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
changeLocale: (newLocale) => {
|
|
73
|
+
const newMessages = getMessages(newLocale);
|
|
74
|
+
if (newMessages instanceof Promise) {
|
|
75
|
+
return newMessages.then((resolvedMessages) => {
|
|
76
|
+
locale = newLocale;
|
|
77
|
+
messages = resolvedMessages;
|
|
78
|
+
formatters.clear();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
locale = newLocale;
|
|
83
|
+
messages = newMessages;
|
|
84
|
+
formatters.clear();
|
|
85
|
+
return Promise.resolve();
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
getLocale: () => locale,
|
|
89
|
+
getLocales: () => availableLocales,
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
exports.icuI18nProvider = icuI18nProvider;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./icuI18nProvider"), exports);
|
|
18
|
+
__exportStar(require("./hash"), exports);
|
|
19
|
+
__exportStar(require("./formatter"), exports);
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const glob_1 = require("glob");
|
|
40
|
+
const gettextParser = __importStar(require("gettext-parser"));
|
|
41
|
+
const i18n_1 = require("@strato-admin/i18n");
|
|
42
|
+
function parseArgs() {
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
let outFile = undefined;
|
|
45
|
+
const positionalArgs = [];
|
|
46
|
+
for (let i = 0; i < args.length; i++) {
|
|
47
|
+
if ((args[i] === '--out-file' || args[i] === '-o') && i + 1 < args.length) {
|
|
48
|
+
outFile = args[i + 1];
|
|
49
|
+
i++;
|
|
50
|
+
}
|
|
51
|
+
else if (args[i].startsWith('--out-file=')) {
|
|
52
|
+
outFile = args[i].split('=')[1];
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
positionalArgs.push(args[i]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const pattern = positionalArgs[0] || 'locales';
|
|
59
|
+
return { pattern, outFile };
|
|
60
|
+
}
|
|
61
|
+
function main() {
|
|
62
|
+
const { pattern, outFile: explicitOutFile } = parseArgs();
|
|
63
|
+
let files = [];
|
|
64
|
+
if (fs.existsSync(pattern) && fs.statSync(pattern).isDirectory()) {
|
|
65
|
+
// Original behavior: if a directory is passed, find .json and .po files inside it
|
|
66
|
+
files = fs
|
|
67
|
+
.readdirSync(pattern)
|
|
68
|
+
.filter((file) => (file.endsWith('.json') || file.endsWith('.po')) && !file.endsWith('.compiled.json'))
|
|
69
|
+
.map((file) => path.join(pattern, file));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// New behavior: support glob patterns
|
|
73
|
+
files = (0, glob_1.globSync)(pattern, { absolute: true }).filter((file) => (file.endsWith('.json') || file.endsWith('.po')) && !file.endsWith('.compiled.json'));
|
|
74
|
+
}
|
|
75
|
+
if (files.length === 0) {
|
|
76
|
+
console.log(`No .json or .po files found matching: ${pattern}`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// If explicitOutFile is provided, we only expect ONE input file or we combine them?
|
|
80
|
+
// FormatJS compile behavior: formatjs compile <file> --out-file <outFile>
|
|
81
|
+
if (explicitOutFile && files.length > 1) {
|
|
82
|
+
console.warn(`Warning: Multiple input files found but only one --out-file specified. Using the first one.`);
|
|
83
|
+
files = [files[0]];
|
|
84
|
+
}
|
|
85
|
+
let processedCount = 0;
|
|
86
|
+
files.forEach((filePath) => {
|
|
87
|
+
const compiledFilePath = explicitOutFile || filePath.replace(/\.(json|po)$/, '.compiled.json');
|
|
88
|
+
const fileName = path.basename(filePath);
|
|
89
|
+
let translations = {};
|
|
90
|
+
try {
|
|
91
|
+
if (filePath.endsWith('.po')) {
|
|
92
|
+
const parsed = gettextParser.po.parse(fs.readFileSync(filePath));
|
|
93
|
+
Object.entries(parsed.translations).forEach(([context, entries]) => {
|
|
94
|
+
Object.entries(entries).forEach(([msgid, data]) => {
|
|
95
|
+
if (msgid === '')
|
|
96
|
+
return; // skip header
|
|
97
|
+
// Find the hash: #. id: comment is authoritative (covers explicit ids and
|
|
98
|
+
// context-mangled hashes); fall back to msgctxt, then raw msgid.
|
|
99
|
+
const commentHash = data.comments?.extracted?.match(/id: (\S+)/)?.[1];
|
|
100
|
+
const hash = commentHash || context || msgid;
|
|
101
|
+
translations[hash] = {
|
|
102
|
+
defaultMessage: data.msgid || data.comments?.extracted || '',
|
|
103
|
+
translation: data.msgstr[0] || '',
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
translations = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
console.error(`Failed to parse translation file at ${filePath}:`, e.message);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const compiledMapping = {};
|
|
117
|
+
const sortedKeys = Object.keys(translations).sort();
|
|
118
|
+
sortedKeys.forEach((hash) => {
|
|
119
|
+
const data = translations[hash];
|
|
120
|
+
// Support both strato-i18n-cli format and formatjs format (with translation or defaultMessage as translation)
|
|
121
|
+
if (typeof data === 'string') {
|
|
122
|
+
compiledMapping[hash] = (0, i18n_1.normalizeMessage)(data);
|
|
123
|
+
}
|
|
124
|
+
else if (data.translation && data.translation.trim() !== '') {
|
|
125
|
+
compiledMapping[hash] = (0, i18n_1.normalizeMessage)(data.translation);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// Fallback to defaultMessage
|
|
129
|
+
compiledMapping[hash] = (0, i18n_1.normalizeMessage)(data.defaultMessage || '');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const parentDir = path.dirname(compiledFilePath);
|
|
133
|
+
if (!fs.existsSync(parentDir)) {
|
|
134
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
fs.writeFileSync(compiledFilePath, JSON.stringify(compiledMapping, null, 2));
|
|
137
|
+
console.log(`Compiled ${fileName} -> ${path.basename(compiledFilePath)} (${Object.keys(compiledMapping).length} messages)`);
|
|
138
|
+
processedCount++;
|
|
139
|
+
});
|
|
140
|
+
console.log(`Successfully compiled ${processedCount} files.`);
|
|
141
|
+
}
|
|
142
|
+
main();
|