Skip to content

Commit 0c331c1

Browse files
committed
fix: fixed RTL issues, placeholder issues, etc
1 parent 4d0921f commit 0c331c1

File tree

1 file changed

+85
-74
lines changed

1 file changed

+85
-74
lines changed

index.js

Lines changed: 85 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,6 @@ class Mandarin {
169169
this.parseMarkdownFile = universalify
170170
.fromPromise(this.parseMarkdownFile)
171171
.bind(this);
172-
this.parseMarkdownFileText = universalify
173-
.fromPromise(this.parseMarkdownFileText)
174-
.bind(this);
175172
this.getLocalizedMarkdownFileName = universalify
176173
.fromPromise(this.getLocalizedMarkdownFileName)
177174
.bind(this);
@@ -187,30 +184,6 @@ class Mandarin {
187184
});
188185
}
189186

190-
// Improved method for text-based markdown translation using Promise.all and p-map
191-
async parseMarkdownFileText(content, locale) {
192-
// debug('parseMarkdownFileText', filePath);
193-
// const content = await readFile(filePath, 'utf8');
194-
195-
// don't translate the main file.md file, only for other locales
196-
const locales = this.config.i18n.config.locales.filter(
197-
(locale) => locale !== this.config.i18n.config.defaultLocale
198-
);
199-
200-
// Use Promise.all for parallel processing of all locales
201-
await Promise.all(
202-
locales.map(async (locale) => {
203-
if (locale === 'en') return; // Skip English as it's typically the source
204-
205-
const translatedContent = await this.translateMarkdownContent(content, locale);
206-
const localizedFilePath = this.getLocalizedMarkdownFileName(filePath, locale);
207-
208-
debug('writing file', localizedFilePath);
209-
await writeFile(localizedFilePath, translatedContent);
210-
})
211-
);
212-
}
213-
214187
// Helper method to detect and extract tables from content
215188
detectTables(content) {
216189
const lines = content.split('\n');
@@ -502,25 +475,45 @@ class Mandarin {
502475
return text;
503476
}
504477

478+
// Protect placeholders within text by temporarily replacing them
479+
const placeholderMap = new Map();
480+
let placeholderCounter = 0;
481+
482+
// Find all placeholders in the text
483+
const placeholderPattern = /__PROTECTED_\w+_\d+__/g;
484+
let textToTranslate = text;
485+
486+
// Replace placeholders with temporary safe tokens
487+
textToTranslate = textToTranslate.replace(placeholderPattern, (match) => {
488+
const tempToken = `TEMP_PLACEHOLDER_${placeholderCounter}`;
489+
placeholderMap.set(tempToken, match);
490+
placeholderCounter++;
491+
return tempToken;
492+
});
493+
494+
// If the text is now empty or only contains temp tokens, don't translate
495+
if (!textToTranslate.trim() || textToTranslate.match(/^TEMP_PLACEHOLDER_\d+$/)) {
496+
return text;
497+
}
498+
505499
// Check cache first
506-
const key = `${locale}:${revHash(text)}`;
500+
const key = `${locale}:${revHash(textToTranslate)}`;
507501
let translation;
508502

509503
if (this.redisClient) {
510504
translation = await this.redisClient.get(key);
511505
}
512506

513507
if (!_.isString(translation)) {
514-
debug('getting translation for text:', text.substring(0, 50) + '...');
508+
debug('getting translation for text:', textToTranslate.substring(0, 50) + '...');
515509
try {
516-
// Use format: 'text' to preserve formatting
517-
[translation] = await this.client.translate(text, {
510+
[translation] = await this.client.translate(textToTranslate, {
518511
to: locale,
519512
format: 'text'
520513
});
521514
} catch (err) {
522515
debug('translation error:', err);
523-
return text; // Return original text if translation fails
516+
return text;
524517
}
525518

526519
if (_.isString(translation)) {
@@ -531,8 +524,16 @@ class Mandarin {
531524
}
532525
}
533526

534-
// Don't replace pipe characters for table content - this was the main issue
535-
return _.isString(translation) ? translation : text;
527+
if (_.isString(translation)) {
528+
// Restore the original placeholders
529+
placeholderMap.forEach((originalPlaceholder, tempToken) => {
530+
translation = translation.replace(new RegExp(tempToken, 'g'), originalPlaceholder);
531+
});
532+
533+
return translation;
534+
}
535+
536+
return text;
536537
}
537538

538539
async parseMarkdownFile(filePath) {
@@ -545,51 +546,61 @@ class Mandarin {
545546
(locale) => locale !== this.config.i18n.config.defaultLocale
546547
);
547548

548-
const files = await Promise.all(
549-
locales.map((locale) => {
550-
return new Promise((resolve, reject) => {
551-
unified()
552-
// <https://unifiedjs.com/learn/recipe/remark-html/#how-to-properly-support-html-inside-markdown>
553-
.use(remarkPresetGitHub)
554-
.use(remarkParse)
555-
.use(slug)
556-
/*
557-
.use(autoLinkHeadings, {
558-
behavior: 'prepend', // Use 'prepend' or 'append', but NOT 'wrap'.
559-
// The content for the new, separate link.
560-
content: {
561-
type: 'text',
562-
value: '🔗', // Using an emoji for the link content.
563-
},
564-
// Link properties can be added if needed, e.g., for CSS classes.
565-
properties: {
566-
ariaHidden: 'true',
567-
class: 'anchor'
568-
}
569-
})
570-
*/
571-
.use(addCustomIdToHeadingText)
572-
.use(remarkStringify, {
573-
// Important: This option prevents the processor from escaping the `{` and `}`
574-
// characters in our custom ID.
575-
fences: true
576-
})
577-
.process(markdown, (err, file) => {
578-
if (err) return reject(err);
579-
resolve({ locale, content: String(file) });
580-
});
549+
const content = await new Promise((resolve, reject) => {
550+
unified()
551+
// <https://unifiedjs.com/learn/recipe/remark-html/#how-to-properly-support-html-inside-markdown>
552+
.use(remarkPresetGitHub)
553+
.use(remarkParse)
554+
.use(slug)
555+
/*
556+
.use(autoLinkHeadings, {
557+
behavior: 'prepend', // Use 'prepend' or 'append', but NOT 'wrap'.
558+
// The content for the new, separate link.
559+
content: {
560+
type: 'text',
561+
value: '🔗', // Using an emoji for the link content.
562+
},
563+
// Link properties can be added if needed, e.g., for CSS classes.
564+
properties: {
565+
ariaHidden: 'true',
566+
class: 'anchor'
567+
}
568+
})
569+
*/
570+
.use(addCustomIdToHeadingText)
571+
.use(remarkStringify, {
572+
// Important: This option prevents the processor from escaping the `{` and `}`
573+
// characters in our custom ID.
574+
fences: true
575+
})
576+
.process(markdown, (err, file) => {
577+
if (err) return reject(err);
578+
resolve(String(file));
581579
});
582-
})
583-
);
580+
});
581+
584582
await Promise.all(
585-
files.map(async (file) => {
583+
locales.map(async (locale) => {
586584
const localizedFilePath = this.getLocalizedMarkdownFileName(
587585
filePath,
588-
file.locale
586+
locale
589587
);
590-
const translatedContent = await this.translateMarkdownContent(file.content, file.locale);
588+
let result = await this.translateMarkdownContent(content, locale);
589+
590+
// Fix RTL reordering for headings with custom IDs
591+
result = result.replace(
592+
/^(.+?)\s+(#{1,6})\s+(\{#[^}]+\})$/gm,
593+
'$2 $1 $3'
594+
);
595+
596+
// Fix RTL reordering for headings without custom IDs
597+
result = result.replace(
598+
/^(.+?)\s+(#{1,6})$/gm,
599+
'$2 $1'
600+
);
601+
591602
debug('writing file', localizedFilePath);
592-
await writeFile(localizedFilePath, translatedContent);
603+
await writeFile(localizedFilePath, result);
593604
})
594605
);
595606
}
@@ -702,7 +713,7 @@ class Mandarin {
702713

703714
// get the translation results from Google
704715
if (!_.isString(translation)) {
705-
debug('getting translation', key);
716+
debug('getting translation', key, phrase);
706717
try {
707718
[translation] = await this.client.translate(phrase, {
708719
to: locale,

0 commit comments

Comments
 (0)