ultimate-jekyll-manager 0.0.40 → 0.0.41

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.
@@ -44,3 +44,11 @@ By vising {{ brand }}, you agree to comply.
44
44
  - Account page: [/account](/account)
45
45
  - Admin page: [/admin](/admin)
46
46
  - Admin sub-page: [/admin/subpage](/admin/subpage)
47
+
48
+ ## This is an input
49
+ <div class="form-group">
50
+ <input type="email" name="slap_honey" class="form-control" placeholder="Your Email">
51
+ </div>
52
+
53
+ ## This button has a title
54
+ <a href="https://itwcreativeworks.com" class="btn btn-primary" title="Visit ITW Creative Works">Visit ITW Creative Works</a>
@@ -40,6 +40,7 @@ const AI_MODEL = 'gpt-4.1-mini';
40
40
  const TRANSLATION_BRANCH = 'uj-translations';
41
41
  // const LOUD = false;
42
42
  const LOUD = process.env.UJ_LOUD_LOGS === 'true';
43
+ const CONTROL = 'UJ-TRANSLATION-CONTROL';
43
44
 
44
45
  const TRANSLATION_DELAY_MS = 500; // wait between each translation
45
46
  const TRANSLATION_BATCH_SIZE = 25; // wait longer every N translations
@@ -51,9 +52,12 @@ const SYSTEM_PROMPT = `
51
52
  Translate the provided content, preserving all original formatting, HTML structure, metadata, and links.
52
53
  Do not explain anything — just return the translated content.
53
54
  The content is TAGGED with [0]...[/0], etc. to mark the text. You MUST KEEP THESE TAGS IN PLACE IN YOUR RESPONSE and OPEN ([0]) and CLOSE ([/0]) them PROPERLY.
54
- DO NOT translate URLs or other non-text elements.
55
- DO NOT translate the brand.
56
- Brand: {brand}
55
+
56
+ DO NOT translate the following elements (but still keep them in place):
57
+ - URLs or other non-text elements.
58
+ - the brand name ({brand}).
59
+ - the control tag (${CONTROL}).
60
+
57
61
  Translate to {lang}
58
62
  `;
59
63
 
@@ -214,9 +218,16 @@ async function processTranslation() {
214
218
  for (const filePath of allFiles) {
215
219
  // Get relative path and original HTML
216
220
  const relativePath = filePath.replace(/^_site[\\/]/, '');
217
- const originalHtml = jetpack.read(filePath);
221
+ let originalHtml = jetpack.read(filePath);
218
222
  const $ = cheerio.load(originalHtml);
219
223
 
224
+ // Inject hidden control tag as last child of <body>
225
+ const controlTag = `<span id="${CONTROL}" style="display:none;">${CONTROL}</span>`;
226
+ $('body').append(controlTag);
227
+
228
+ // Reset originalHtml
229
+ originalHtml = $.html();
230
+
220
231
  // Collect text nodes with tags
221
232
  const textNodes = collectTextNodes($, { tag: true });
222
233
 
@@ -263,6 +274,20 @@ async function processTranslation() {
263
274
  && (RECHECK_DAYS === 0 || age < RECHECK_DAYS);
264
275
  const startTime = Date.now();
265
276
 
277
+ function setResult(success) {
278
+ if (success) {
279
+ meta[relativePath] = {
280
+ timestamp: new Date().toISOString(),
281
+ hash,
282
+ };
283
+ } else {
284
+ meta[relativePath] = {
285
+ timestamp: 0,
286
+ hash: '__fail__',
287
+ };
288
+ }
289
+ }
290
+
266
291
  // Check if we can use cached translation
267
292
  if (
268
293
  (useCached || process.env.UJ_TRANSLATION_CACHE === 'true')
@@ -287,10 +312,9 @@ async function processTranslation() {
287
312
 
288
313
  // Save to cache
289
314
  jetpack.write(cachePath, translated);
290
- meta[relativePath] = {
291
- timestamp: new Date().toISOString(),
292
- hash,
293
- };
315
+
316
+ // Set result
317
+ setResult(true);
294
318
  } catch (e) {
295
319
  const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
296
320
  logger.error(`⚠️ Translation failed: ${relativePath} [${lang}] — ${e.message} (Elapsed time: ${elapsedTime}s)`);
@@ -299,10 +323,7 @@ async function processTranslation() {
299
323
  translated = bodyText;
300
324
 
301
325
  // Save failure to cache
302
- meta[relativePath] = {
303
- timestamp: 0,
304
- hash: '__fail__',
305
- };
326
+ setResult(false);
306
327
  }
307
328
  }
308
329
 
@@ -345,6 +366,20 @@ async function processTranslation() {
345
366
  // Rewrite links
346
367
  rewriteLinks($, lang);
347
368
 
369
+ // Check that the control tag matches the expected value
370
+ const controlTag = $(`#${CONTROL}`);
371
+ if (
372
+ controlTag.length === 0
373
+ || controlTag.text() !== CONTROL
374
+ ) {
375
+ logger.error(`⚠️ Control tag mismatch in ${relativePath} [${lang}]`);
376
+
377
+ return setResult(false);
378
+ } else {
379
+ // Delete the control tag
380
+ controlTag.remove();
381
+ }
382
+
348
383
  // Set the lang attribute on the <html> tag
349
384
  $('html').attr('lang', lang);
350
385
 
@@ -431,7 +466,7 @@ async function processTranslation() {
431
466
 
432
467
  // Push updated translation cache back to uj-translations
433
468
  if (Manager.isBuildMode()) {
434
- await pushTranslationBranch(updatedFiles);
469
+ await pushTranslationBranch( );
435
470
  }
436
471
  }
437
472
 
@@ -783,7 +818,7 @@ async function fetchTranslationsBranch() {
783
818
  }
784
819
 
785
820
  // Git Sync: Push
786
- async function pushTranslationBranch(updatedFiles) {
821
+ async function pushTranslationBranch( ) {
787
822
  const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
788
823
  const localRoot = path.join('.temp', 'translations');
789
824
 
@@ -65,6 +65,24 @@ const collectTextNodes = ($, options) => {
65
65
  return;
66
66
  }
67
67
 
68
+ // Handle attributes like title and placeholder
69
+ const translatableAttributes = ['title', 'placeholder', 'alt', 'aria-label', 'aria-placeholder', 'aria-describedby', 'aria-labelledby', 'value', 'label'];
70
+ translatableAttributes.forEach(attr => {
71
+ const text = node.attr(attr)?.trim();
72
+ if (text) {
73
+ const i = textNodes.length;
74
+
75
+ // Push
76
+ textNodes.push({
77
+ node,
78
+ type: 'attr',
79
+ attr,
80
+ text,
81
+ tagged: `[${i}]${text}[/${i}]`,
82
+ });
83
+ }
84
+ });
85
+
68
86
  // Handle regular DOM text nodes
69
87
  node.contents().each((_, child) => {
70
88
  if (child.type === 'text' && child.data?.trim()) {
@@ -75,8 +93,7 @@ const collectTextNodes = ($, options) => {
75
93
  // Preserve a single trailing whitespace if it exists
76
94
  .replace(/\s*(\s)\s*$/, '$1')
77
95
  // Normalize internal whitespace
78
- .replace(/\s+/g, ' ')
79
- // const text = trimPreserveOneCleanInner(child.data);
96
+ .replace(/\s+/g, ' ');
80
97
 
81
98
  // Push
82
99
  textNodes.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {