UNPKG

wuchale

Version:

Protobuf-like i18n from plain code

108 lines (107 loc) 3.85 kB
const someHeuDet = { file: '', scope: 'markup' }; export class Message { msgStr; // array for plurals plural = false; context; comments = []; details; type = 'message'; constructor(msgStr, heuristicDetails = someHeuDet, context) { if (typeof msgStr === 'string') { this.msgStr = [msgStr]; } else { this.msgStr = msgStr.filter(str => str != null); } this.msgStr = this.msgStr.map(msg => msg.split('\n').map(line => line.trim()).join('\n')); this.details = heuristicDetails; this.context = context; } toKey = () => `${this.msgStr.slice(0, 2).join('\n')}\n${this.context ?? ''}`.trim(); } export const defaultHeuristicOpts = { ignoreElements: ['script', 'style', 'path', 'code', 'pre'], ignoreAttribs: [['form', 'method']], ignoreCalls: ['fetch'], urlAttribs: [['a', 'href']], urlCalls: [], }; export function createHeuristic(opts) { return msg => { if (msg.details.element && opts.ignoreElements.includes(msg.details.element)) { return false; } if (msg.details.scope === 'attribute') { for (const [element, attrib] of opts.ignoreAttribs) { if (msg.details.element === element && msg.details.attribute === attrib) { return false; } } } const msgStr = msg.msgStr.join('\n'); const looksLikeUrlPath = msgStr.startsWith('/') && !msgStr.includes(' '); if (looksLikeUrlPath && (msg.details.scope === 'script' || msg.details.scope === 'attribute')) { if (msg.details.call) { for (const call of opts.urlCalls) { if (msg.details.call === call) { return 'url'; } } } if (msg.details.attribute) { for (const [element, attrib] of opts.urlAttribs) { if (msg.details.element === element && msg.details.attribute === attrib) { return 'url'; } } } } if (!/\p{L}/u.test(msgStr)) { return false; } if (msg.details.scope === 'markup') { return 'message'; } // script and attribute // ignore: // non-letter beginnings // lower-case English letter beginnings // containing only upper-case English and non-letters if (!/\p{L}/u.test(msgStr[0]) || /[a-z]/.test(msgStr[0]) || /^([A-Z]|\P{L})+$/u.test(msgStr)) { return false; } if (msg.details.scope === 'attribute') { return 'message'; } if (msg.details.declaring === 'expression' && !msg.details.funcName) { return false; } if (!msg.details.call || !msg.details.call.startsWith('console.') && !opts.ignoreCalls.includes(msg.details.call)) { return 'message'; } return false; }; } /** Default heuristic */ export const defaultHeuristic = createHeuristic(defaultHeuristicOpts); /** Default heuristic which ignores messages outside functions in the `script` scope */ export const defaultHeuristicFuncOnly = msg => { if (defaultHeuristic(msg) && (msg.details.scope !== 'script' || msg.details.funcName != null)) { return 'message'; } return false; }; export const defaultGenerateLoadID = (filename) => filename.replace(/[^a-zA-Z0-9_]+/g, '_'); export class IndexTracker { indices = {}; nextIndex = 0; get = (msgStr) => { if (msgStr in this.indices) { return this.indices[msgStr]; } const index = this.nextIndex; this.indices[msgStr] = index; this.nextIndex++; return index; }; }