/** * AI Translator (by ROBOTSTXT) — editor UI. * * Adds a translation panel to the Block editor sidebar and a handler for the * Classic editor metabox. Both implementations share the same REST endpoint * and language list. * * Content translation is performed in parallel chunks so that long posts do not * hit server-side timeout limits. Title and excerpt are short fields and are * translated in a single request using the standard /translate endpoint. * * @package ROBOTSTXT\AI_Translator * @since 1.0.0 */ ( function () { 'use strict'; function warn( message ) { if ( window.console && typeof window.console.warn === 'function' ) { window.console.warn( '[AI Translator] ' + message ); } } var data = window.aiTranslatorData; if ( ! data ) { warn( 'window.aiTranslatorData is missing; aborting.' ); return; } var apiFetch = window.wp && window.wp.apiFetch ? window.wp.apiFetch : null; var labels = data.i18n || {}; // ── Chunking helpers ────────────────────────────────────────────────────── /** * Splits Gutenberg block HTML into chunks at block boundaries. * * Each chunk ends on a complete Gutenberg block so the AI never receives * partial markup. Chunks are kept below maxChars where possible; a single * block larger than maxChars is sent as its own chunk. * * @param {string} content Serialised Gutenberg block HTML. * @param {number} maxChars Maximum characters per chunk. * @returns {Array} */ function chunkByBlocks( content, maxChars ) { if ( ! content || content.length <= maxChars ) { return [ content ]; } // Split just before each block comment opener so every part starts at a // block boundary. The split delimiter is a zero-width lookahead so each // part keeps the opening comment intact. var parts = content.split( /(?=