@cocreate/cli 1.58.0 → 1.60.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/.github/workflows/automated.yml +22 -20
- package/CHANGELOG.md +1 -1
- package/CoCreate.config.js +384 -379
- package/package.json +6 -6
- package/src/commands/acme.js +1 -1
- package/src/commands/other/translate.js +126 -128
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.60.0",
|
|
4
4
|
"description": "Polyrepo management bash CLI tool. Run all git commands and yarn commands on multiple repositories. Also includes a few custom macros for cloning, installing, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
"coc": "src/coc.js"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@cocreate/
|
|
52
|
-
"@cocreate/config": "^1.
|
|
53
|
-
"@cocreate/file": "^1.
|
|
54
|
-
"@google/
|
|
51
|
+
"@cocreate/certificates": "^1.7.0",
|
|
52
|
+
"@cocreate/config": "^1.17.0",
|
|
53
|
+
"@cocreate/file": "^1.23.0",
|
|
54
|
+
"@google/genai": "2.15.0"
|
|
55
55
|
},
|
|
56
56
|
"allowScripts": {
|
|
57
|
-
"@cocreate/
|
|
57
|
+
"@cocreate/certificates@1.3.2": true,
|
|
58
58
|
"@cocreate/actions@1.21.4": true,
|
|
59
59
|
"@cocreate/api@1.22.5": true,
|
|
60
60
|
"@cocreate/config@1.13.4": true,
|
package/src/commands/acme.js
CHANGED
|
@@ -1,147 +1,145 @@
|
|
|
1
|
+
const { GoogleGenAI, Type } = require("@google/genai");
|
|
2
|
+
const Config = require("@cocreate/config");
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* It uses the Gemini API to translate and generate content based on the provided HTML and languages.
|
|
5
|
-
*
|
|
6
|
-
* Prerequisites:
|
|
7
|
-
* - Node.js installed on your system.
|
|
8
|
-
* - The Google Generative AI library installed:
|
|
9
|
-
* `npm install @google/generative-ai`
|
|
10
|
-
* - A valid Gemini API key.
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* Import and call the `translateHtml` function with the HTML source, languages array, and options.
|
|
5
|
+
* Default Gemini model identifier used for language translation tasks.
|
|
6
|
+
* @type {string}
|
|
14
7
|
*/
|
|
8
|
+
const DEFAULT_MODEL = "gemini-2.5-flash-lite";
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* {
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* "name": "Basketball Betting Sportsbook | NBA, EuroLeague, NCAA & Global Leagues",
|
|
26
|
-
* "description": "Unlock premier basketball betting at Amapola Sportsbook. Get competitive odds for NBA, EuroLeague, NCAA, and global basketball. Enjoy live betting, swift payouts, and insightful picks for an unmatched wagering experience.",
|
|
27
|
-
* "url": "https://amapolacasino.com/sportsbook/basketball/",
|
|
28
|
-
* "image": "https://amapolacasino.com/assets/basketball-og.jpg",
|
|
29
|
-
* "author": {
|
|
30
|
-
* "@type": "Organization",
|
|
31
|
-
* "name": "AmapolaCasino"
|
|
32
|
-
* }
|
|
33
|
-
* },
|
|
34
|
-
* "es": { ... },
|
|
35
|
-
* "fr": { ... },
|
|
36
|
-
* "pt": { ... },
|
|
37
|
-
* "ht": { ... },
|
|
38
|
-
* "nl": { ... },
|
|
39
|
-
* "gn": { ... }
|
|
40
|
-
* }
|
|
41
|
-
* }
|
|
11
|
+
* Translation mapping for a single element's attributes and inner content.
|
|
12
|
+
* @typedef {Object} ElementTranslations
|
|
13
|
+
* @property {string|null} [text] - Inner text content or script payload.
|
|
14
|
+
* @property {string|null} [placeholder] - Input placeholder text.
|
|
15
|
+
* @property {string|null} [title] - Element title attribute.
|
|
16
|
+
* @property {string|null} [alt] - Image alternate text.
|
|
17
|
+
* @property {string|null} ["aria-label"] - ARIA accessibility label.
|
|
18
|
+
* @property {Record<string, string>} [additionalProperties] - Dynamic attributes (e.g., `aria-description`).
|
|
42
19
|
*/
|
|
43
20
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Key-value mapping of `i18n` keys to element translations for a specific language.
|
|
23
|
+
* @typedef {Record<string, ElementTranslations>} LanguageTranslations
|
|
24
|
+
*/
|
|
47
25
|
|
|
48
|
-
|
|
49
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Language-First dictionary structure mapping language codes (e.g., "en", "es") to their translations.
|
|
28
|
+
* @typedef {Record<string, LanguageTranslations>} LanguageFirstTranslationMap
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Configuration options for the HTML translation process.
|
|
33
|
+
* @typedef {Object} TranslateOptions
|
|
34
|
+
* @property {string} [apiKey] - Google GenAI API key. If omitted, attempts lookup via configuration service.
|
|
35
|
+
* @property {string} [model] - Gemini model name override (defaults to "gemini-2.5-flash-lite").
|
|
36
|
+
* @property {number} [temperature] - Model sampling temperature (defaults to 0.2).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the Google GenAI API key from explicit options or global configuration.
|
|
41
|
+
*
|
|
42
|
+
* @async
|
|
43
|
+
* @param {TranslateOptions} options - Configuration options passed to the translator.
|
|
44
|
+
* @returns {Promise<string|undefined>} Resolved API key string or undefined if not found.
|
|
45
|
+
*/
|
|
50
46
|
async function getApiKey(options) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
return config.GoogleGenerativeAIApiKey;
|
|
47
|
+
if (options?.apiKey) return options.apiKey;
|
|
48
|
+
if (typeof Config?.get === "function") {
|
|
49
|
+
return await Config.get("GEMINI_API_KEY") || process.env.GEMINI_API_KEY;
|
|
50
|
+
}
|
|
51
|
+
return process.env.GEMINI_API_KEY;
|
|
58
52
|
}
|
|
59
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Analyzes HTML source code, extracts `i18n` elements and `<script type="application/ld+json">` data,
|
|
56
|
+
* and uses Google GenAI to synthesize a Language-First JSON translation dictionary.
|
|
57
|
+
*
|
|
58
|
+
* @async
|
|
59
|
+
* @param {string} html - Raw HTML source code string containing elements with `i18n` attributes.
|
|
60
|
+
* @param {string[]} languages - Array of target language codes (e.g., `["en", "es", "fr"]`).
|
|
61
|
+
* @param {TranslateOptions} [options={}] - Optional configuration parameters for model and authentication.
|
|
62
|
+
* @returns {Promise<LanguageFirstTranslationMap|null>} Language-First translation map, or `null` if generation fails.
|
|
63
|
+
* @throws {Error} Throws an error if no API key is provided or resolved.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const translations = await translateHtml(
|
|
67
|
+
* '<h1 i18n="heading">Hello</h1><input i18n="search" placeholder="Search...">',
|
|
68
|
+
* ["es", "fr"]
|
|
69
|
+
* );
|
|
70
|
+
*/
|
|
60
71
|
module.exports = async function translateHtml(html, languages, options = {}) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
);
|
|
66
|
-
const genAI = new GoogleGenerativeAI(apiKey);
|
|
67
|
-
const model =
|
|
68
|
-
options.model || genAI.getGenerativeModel({ model: MODEL_NAME });
|
|
69
|
-
const translationObj = await generateTranslationObject(
|
|
70
|
-
html,
|
|
71
|
-
model,
|
|
72
|
-
languages
|
|
73
|
-
);
|
|
74
|
-
return translationObj;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// Update generateTranslationObject to accept only html, model, languages
|
|
78
|
-
async function generateTranslationObject(html, model, languages) {
|
|
79
|
-
const langList = languages.map((l) => `"${l}"`).join(", ");
|
|
80
|
-
const prompt = `
|
|
81
|
-
You are an expert web localization AI. Given the following HTML file, extract all translatable content (titles, meta tags, headers, buttons, video/image alt/title, labels, aria-label, all aria-* attributes, and placeholders) and generate a JSON object in the following format:
|
|
72
|
+
const apiKey = await getApiKey(options);
|
|
73
|
+
if (!apiKey) {
|
|
74
|
+
throw new Error("Google GenAI API key is required for translateHtml.");
|
|
75
|
+
}
|
|
82
76
|
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"selector": "<css selector>",
|
|
87
|
-
"innerHTML": {
|
|
88
|
-
${languages
|
|
89
|
-
.map((l) => `\"${l}\"`)
|
|
90
|
-
.join(
|
|
91
|
-
", \
|
|
92
|
-
"
|
|
93
|
-
)}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"selector": "<css selector>",
|
|
98
|
-
"attributes": {
|
|
99
|
-
"alt": { ${langList} },
|
|
100
|
-
"label": { ${langList} },
|
|
101
|
-
"aria-label": { ${langList} },
|
|
102
|
-
"aria-*": { ${langList} },
|
|
103
|
-
"title": { ${langList} },
|
|
104
|
-
"placeholder": { ${langList} }
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
// Example for structured data translation:
|
|
108
|
-
{
|
|
109
|
-
"selector": "script[type='application/ld+json']",
|
|
110
|
-
"innerHTML": {
|
|
111
|
-
"en": { "@context": "http://schema.org", "@type": "WebPage", "name": "English name", "description": "English description" },
|
|
112
|
-
"es": { "@context": "http://schema.org", "@type": "WebPage", "name": "Spanish name", "description": "Spanish description" },
|
|
113
|
-
"fr": { "@context": "http://schema.org", "@type": "WebPage", "name": "French name", "description": "French description" }
|
|
114
|
-
// ...other languages
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// ...more selectors as needed
|
|
118
|
-
]
|
|
119
|
-
}
|
|
77
|
+
const ai = new GoogleGenAI({ apiKey });
|
|
78
|
+
const modelName = options.model || DEFAULT_MODEL;
|
|
79
|
+
const langList = Array.isArray(languages) ? languages.join(", ") : String(languages);
|
|
120
80
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
81
|
+
const prompt = `
|
|
82
|
+
You are an expert web localization AI.
|
|
83
|
+
Analyze the provided HTML and find all elements containing an "i18n" attribute, as well as any <script type="application/ld+json"> tags.
|
|
124
84
|
|
|
125
|
-
|
|
126
|
-
", "
|
|
127
|
-
)}. Do not leave any language blank. For Guarani (\"gn\"), always translate to Guarani and never leave it in English.
|
|
85
|
+
Target Languages: ${langList}
|
|
128
86
|
|
|
129
|
-
|
|
87
|
+
Instructions:
|
|
88
|
+
1. Extract translatable content for each "i18n" key (inner text, alt, title, placeholder, label, aria-label, and aria-* attributes).
|
|
89
|
+
2. For script[type='application/ld+json'], use the key "ld+json" or the element's "i18n" attribute, and translate string fields (like name, description) while keeping schema keywords (@context, @type, url) intact.
|
|
90
|
+
3. Organize the translations into a LANGUAGE-FIRST structure where top-level keys are the target language codes (${langList}).
|
|
91
|
+
4. Inside each language code object, map each "i18n" key to its translated properties (e.g., "text", "placeholder", "alt", "title", "aria-label").
|
|
92
|
+
5. Provide a full translation for EVERY target language requested. Never leave a requested language empty.
|
|
130
93
|
|
|
131
|
-
HTML:
|
|
94
|
+
HTML Source:
|
|
132
95
|
${html}
|
|
133
96
|
`;
|
|
134
97
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
98
|
+
/**
|
|
99
|
+
* JSON Schema enforcing structured Language-First output from Gemini.
|
|
100
|
+
* @type {import("@google/genai").Schema}
|
|
101
|
+
*/
|
|
102
|
+
const responseSchema = {
|
|
103
|
+
type: Type.OBJECT,
|
|
104
|
+
properties: {
|
|
105
|
+
translations: {
|
|
106
|
+
type: Type.OBJECT,
|
|
107
|
+
description: "Map of language codes (e.g., 'en', 'es') to key-value translation objects",
|
|
108
|
+
additionalProperties: {
|
|
109
|
+
type: Type.OBJECT,
|
|
110
|
+
description: "Map of i18n keys to element translation properties",
|
|
111
|
+
additionalProperties: {
|
|
112
|
+
type: Type.OBJECT,
|
|
113
|
+
properties: {
|
|
114
|
+
text: { type: Type.STRING, nullable: true },
|
|
115
|
+
placeholder: { type: Type.STRING, nullable: true },
|
|
116
|
+
title: { type: Type.STRING, nullable: true },
|
|
117
|
+
alt: { type: Type.STRING, nullable: true },
|
|
118
|
+
"aria-label": { type: Type.STRING, nullable: true }
|
|
119
|
+
},
|
|
120
|
+
additionalProperties: true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
required: ["translations"]
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const response = await ai.models.generateContent({
|
|
130
|
+
model: modelName,
|
|
131
|
+
contents: prompt,
|
|
132
|
+
config: {
|
|
133
|
+
responseMimeType: "application/json",
|
|
134
|
+
responseSchema: responseSchema,
|
|
135
|
+
temperature: options.temperature ?? 0.2
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const parsed = JSON.parse(response.text);
|
|
140
|
+
return parsed.translations;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error("Gemini Translation Error:", err);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
};
|