-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.mjs
More file actions
executable file
·188 lines (159 loc) · 6.01 KB
/
cli.mjs
File metadata and controls
executable file
·188 lines (159 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env node
import { spawn } from "child_process";
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Process command-line arguments
const args = process.argv.slice(2); // Remove the first two default arguments (node path and script path)
if (args.length > 1) throw new Error("Only one argument is allowed.");
let srcDirectory = "";
if (args.length === 1) {
srcDirectory = args[0];
// Check if srcDirectory exists
if (!fs.existsSync(srcDirectory))
throw new Error(`The source directory "${srcDirectory}" does not exist.`);
}
process.env.SRC_DIRECTORY = srcDirectory;
//I use the import in function-form so that the process.env directive above
//is set before executing the import
import("./i18next.config.mjs").then(({ i18nDirectory, locales }) => {
async function runI18nextParserAsync() {
const configPath = path.join(__dirname, "i18next.config.mjs");
const config = await import(configPath);
console.log({ config });
return new Promise((resolve, reject) => {
const process = spawn("npx", ["i18next", "-c", `${configPath}`], {
srcDirectory,
});
process.stdout.on("data", (data) => {
console.log(`stdout: ${data}`);
});
process.stderr.on("data", (data) => {
console.error(`stderr: ${data}`);
});
process.on("close", (code) => {
console.log(`i18next-parser process exited with code ${code}`);
if (code === 0) {
resolve();
} else {
reject(new Error(`i18next-parser process exited with code ${code}`));
}
});
});
}
const importLocaleData = async (locale) => {
//We copy the .js to .mjs in case the project that has the .js file
//does not use module type in package.json (as in expo)
const jsFilePath = path.join(i18nDirectory, `${locale}.js`);
const mjsFilePath = path.join(i18nDirectory, `${locale}.mjs`);
// Copy the .js file to a .mjs file
fs.copyFileSync(jsFilePath, mjsFilePath);
try {
// Dynamically import the .mjs file
const module = await import(mjsFilePath);
return module.default;
} finally {
// Ensure the .mjs file is deleted after import
fs.unlinkSync(mjsFilePath);
}
};
// Helper function to recursively collect keys from an object (including nested keys)
const collectKeys = (obj, prefix = "") => {
return Object.keys(obj).reduce((res, key) => {
const value = obj[key];
const fullPath = `${prefix}${key}`;
if (typeof value === "object" && value !== null) {
// TypeScript needs assurance that value is not an array or null
res = res.concat(collectKeys(value, `${fullPath}.`));
} else {
res.push(fullPath);
}
return res;
}, []);
};
// Function to load JSON files and compare keys
const compareKeys = async () => {
for (const locale of locales) {
const filePath = path.join(i18nDirectory, `${locale}.keys.json`);
try {
const fileContents = fs.readFileSync(filePath, "utf8");
const data = JSON.parse(fileContents);
// Collect keys from the loaded JSON file
const loadedKeys = collectKeys(data);
const localeData = await importLocaleData(locale);
const definedKeys = collectKeys(localeData);
// Find missing and undefined keys
const missingKeys = definedKeys.filter(
(key) => !loadedKeys.includes(key),
);
const undefinedKeys = loadedKeys.filter(
(key) => !definedKeys.includes(key),
);
if (undefinedKeys.length > 0) {
console.log(`Needs translation for ${locale}:`, undefinedKeys);
}
if (missingKeys.length > 0) {
console.log(`Translation not used for ${locale}:`, missingKeys);
}
} catch (error) {
console.error(`Error reading or parsing ${filePath}:`, error);
}
}
};
// Check if the directory exists, create it if it doesn't
if (!locales || locales.length === 0) {
console.error(`There are no [LOCALE].js files in ${i18nDirectory}`);
}
// Using the async function
else
runI18nextParserAsync()
.then(() => {
compareKeys().then(() => {
//TODO now for each locale in locales unlink all files in i18nDirectory that end as any of the locale.keys.json
for (const locale of locales) {
const filePath = path.join(i18nDirectory, `${locale}.keys.json`);
fs.unlinkSync(filePath);
}
const content = `//IMPORTANT: This file is automatically generated by @rewindbitcoin/i18n
//DO NOT EDIT it. Usage: npx i18n_rewbtc [src] #to re-generate it.
//More info: @rewindbitcoin/i18n README.md
export const locales = [${locales.map((locale) => `'${locale}'`).join(", ")}];
export type Locale = "en-US" | "es-ES" | "es" | "en";
import i18n, { i18n as I18nInstance } from "i18next";
import { initReactI18next } from "react-i18next";
${locales.map((locale) => "import " + locale + ' from "./' + locale + '";').join("\n")}
const resources = {
${locales.map((locale) => " " + locale + ": { translation: " + locale + " }").join(",\n")}
};
i18n.use(initReactI18next).init({
resources,
fallbackLng: "en", // Default language
interpolation: {
escapeValue: false,
},
});
export const i18nInstances: Record<Locale, I18nInstance> = locales.reduce(
(acc, locale) => {
acc[locale as Locale] = i18n.cloneInstance({ lng: locale }); // Asserting locale as Locale
return acc;
},
{} as Record<Locale, I18nInstance>
);
export async function initI18n(locale: Locale) {
await i18n.changeLanguage(locale);
}
export { i18n };
`;
const localesInitPath = path.join(i18nDirectory, "init.ts");
fs.writeFileSync(localesInitPath, content, "utf8");
console.log(
`init.ts files created with locales: ${locales.join(", ")}`,
);
});
})
.catch((error) => {
console.error("i18next-parser failed:", error);
});
});