@@ -61,42 +61,52 @@ export function startBulkTranslate(locale: string): BulkTranslateStatus {
6161 return status ;
6262}
6363
64- const PLACEHOLDER_PATTERN = / \{ ( \w + ) \} / g;
64+ /** Matches everything that must survive machine translation byte-for-byte: a `{varName}`
65+ * placeholder, a literal backtick (Discord code-span markdown, e.g. `` `{count}` `` — left
66+ * alone, Google Translate tends to swap straight backticks/quotes for locale-specific curly
67+ * quote characters, e.g. „79") , or a real newline (multi-line fields like the edits/deletes/
68+ * reactions/attachments summary — translating the whole blob as one string is fine, but a
69+ * newline getting reflowed or dropped isn't). */
70+ const GUARDED_PATTERN = / \{ ( \w + ) \} | ` | \n / g;
6571/** Meaningless token (not a real word in any language, so Google Translate has nothing to
66- * translate) standing in for each `{varName}` while the surrounding text gets translated. */
72+ * translate) standing in for each guarded piece while the surrounding text gets translated. */
6773const guardToken = ( index : number ) => `qxkz${ index } qxkz` ;
6874const GUARD_PATTERN = / q x k z \s * ( \d + ) \s * q x k z / gi;
6975
70- /** Swaps every `{varName}` for a guard token so machine translation can't mangle it, then swaps
71- * the guard tokens back to their original `{varName}` placeholders afterward — translators
76+ type GuardedPiece = { varName : string } | "backtick" | "newline" ;
77+
78+ /** Swaps every placeholder/backtick/newline for a guard token so machine translation can't
79+ * mangle them, then swaps the guard tokens back to the original text afterward — translators
7280 * regularly reorder/adjust spacing around words but leave a meaningless alphanumeric token
73- * alone, which plain `{varName}` ( real words, real brackets) doesn't survive nearly as well. */
74- function guardPlaceholders ( text : string ) : { guarded : string ; names : string [ ] } {
75- const names : string [ ] = [ ] ;
76- const guarded = text . replace ( PLACEHOLDER_PATTERN , ( _match , name : string ) => {
77- const token = guardToken ( names . length ) ;
78- names . push ( name ) ;
81+ * alone, which real punctuation/whitespace doesn't survive nearly as well. */
82+ function guardText ( text : string ) : { guarded : string ; pieces : GuardedPiece [ ] } {
83+ const pieces : GuardedPiece [ ] = [ ] ;
84+ const guarded = text . replace ( GUARDED_PATTERN , ( match , name : string | undefined ) => {
85+ const token = guardToken ( pieces . length ) ;
86+ pieces . push ( name !== undefined ? { varName : name } : match === "`" ? "backtick" : "newline" ) ;
7987 return token ;
8088 } ) ;
81- return { guarded, names } ;
89+ return { guarded, pieces } ;
8290}
8391
84- function restorePlaceholders ( text : string , names : string [ ] ) : string {
92+ function restoreGuardedText ( text : string , pieces : GuardedPiece [ ] ) : string {
8593 return text . replace ( GUARD_PATTERN , ( match , indexRaw : string ) => {
86- const index = Number ( indexRaw ) ;
87- const name = names [ index ] ;
88- return name !== undefined ? `{${ name } }` : match ;
94+ const piece = pieces [ Number ( indexRaw ) ] ;
95+ if ( piece === undefined ) return match ;
96+ if ( piece === "backtick" ) return "`" ;
97+ if ( piece === "newline" ) return "\n" ;
98+ return `{${ piece . varName } }` ;
8999 } ) ;
90100}
91101
92102async function translateOne ( locale : string , englishText : string ) : Promise < string | null > {
93103 const trimmed = englishText . trim ( ) ;
94104 if ( ! trimmed ) return "" ;
95- const { guarded, names } = guardPlaceholders ( trimmed ) ;
105+ const { guarded, pieces } = guardText ( trimmed ) ;
96106 try {
97107 const { translateText } = await import ( "../plugins/translation/functions/translate.js" ) ;
98108 const result = await translateText ( guarded , locale , "en" ) ;
99- return names . length ? restorePlaceholders ( result . text , names ) : result . text ;
109+ return pieces . length ? restoreGuardedText ( result . text , pieces ) : result . text ;
100110 } catch ( error ) {
101111 log . warn ( `[i18n] Machine translation failed for "${ locale } ":` , error ) ;
102112 return null ;
0 commit comments