generator-begcode
Version:
Spring Boot + Angular/React/Vue in one handy generator
176 lines (175 loc) • 9.45 kB
JavaScript
import { extname } from 'node:path';
import { passthrough } from '@yeoman/transform';
import { Minimatch } from 'minimatch';
import { createJhiTransformTranslateReplacer, createJhiTransformTranslateStringifyReplacer, createJhiTranslateReplacer, escapeHtmlTranslationValue, escapeTsTranslationValue, } from '../../languages/support/index.js';
const PLACEHOLDER_REGEX = /(?:placeholder|title)=['|"](\{\{\s?['|"]([a-zA-Z0-9.\-_]+)['|"]\s?\|\s?translate\s?\}\})['|"]/.source;
const JHI_TRANSLATE_REGEX = /(\n?\s*[a-z][a-zA-Z]*Translate="[a-zA-Z0-9 +{}'_!?.]+")/.source;
const TRANSLATE_VALUES_REGEX = /(\n?\s*\[translateValues\]="\{(?:(?!\}").)*?\}")/.source;
const TRANSLATE_REGEX = [JHI_TRANSLATE_REGEX, TRANSLATE_VALUES_REGEX].join('|');
function replaceTranslationKeysWithText(getWebappTranslation, content, regexSource, { keyIndex = 1, replacementIndex = 1, escape, } = {}) {
const regex = new RegExp(regexSource, 'g');
const allMatches = content.matchAll(regex);
for (const match of allMatches) {
const key = match[keyIndex];
const target = match[replacementIndex];
let translation = getWebappTranslation(key);
if (escape) {
translation = escape(translation, match);
}
content = content.replace(target, translation);
}
return content;
}
function replaceJSTranslation(getWebappTranslation, content, jsKey) {
return replaceTranslationKeysWithText(getWebappTranslation, content, `${jsKey}\\s?:\\s?['|"]([a-zA-Z0-9.\\-_]+\\.[a-zA-Z0-9.\\-_]+)['|"]`, {
escape: (translation, match) => translation.replaceAll(match[0].slice(-1), `\\${match[0].slice(-1)}`),
});
}
function replacePageTitles(getWebappTranslation, content) {
return replaceJSTranslation(getWebappTranslation, content, 'title');
}
function replacePlaceholders(getWebappTranslation, content) {
return replaceTranslationKeysWithText(getWebappTranslation, content, PLACEHOLDER_REGEX, { keyIndex: 2 });
}
function replaceErrorMessage(getWebappTranslation, content) {
return replaceJSTranslation(getWebappTranslation, content, 'errorMessage');
}
const tagTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
const translatedValueInterpolate = parsedInterpolate
? Object.fromEntries(Object.entries(parsedInterpolate).map(([key, value]) => [key, `{{ ${value} }}`]))
: undefined;
const translatedValue = escapeHtmlTranslationValue(getWebappTranslation(key, translatedValueInterpolate));
if (enableTranslation) {
const translateValuesAttr = parsedInterpolate
? ` [translateValues]="{ ${Object.entries(parsedInterpolate)
.map(([key, value]) => `${key}: ${value}`)
.join(', ')} }"`
: '';
return ` ${jhiPrefix}Translate="${key}"${translateValuesAttr}${prefix}${translatedValue}${suffix}`;
}
return `${prefix}${translatedValue}${suffix}`;
};
const validationTagTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
if (!parsedInterpolate || Object.keys(parsedInterpolate).length === 0) {
throw new Error(`No interpolation values found for translation key ${key}, use __jhiTranslateTag__ instead.`);
}
const translatedValue = escapeHtmlTranslationValue(getWebappTranslation(key, parsedInterpolate));
if (enableTranslation) {
const translateValuesAttr = parsedInterpolate
? ` [translateValues]="{ ${Object.entries(parsedInterpolate)
.map(([key, value]) => `${key}: '${value}'`)
.join(', ')} }"`
: '';
return ` ${jhiPrefix}Translate="${key}"${translateValuesAttr}${prefix}${translatedValue}${suffix}`;
}
return `${prefix}${translatedValue}${suffix}`;
};
const tagPipeTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
if (!parsedInterpolate || Object.keys(parsedInterpolate).length === 0) {
throw new Error(`No interpolation values found for translation key ${key}, use __jhiTranslateTag__ instead.`);
}
const translatedValueInterpolate = Object.fromEntries(Object.entries(parsedInterpolate).map(([key, value]) => [key, getWebappTranslation(value)]));
const translatedValue = escapeHtmlTranslationValue(getWebappTranslation(key, translatedValueInterpolate));
if (enableTranslation) {
const translateValuesAttr = ` [translateValues]="{ ${Object.entries(parsedInterpolate)
.map(([key, value]) => `${key}: ('${value}' | translate)`)
.join(', ')} }"`;
return ` ${jhiPrefix}Translate="${key}"${translateValuesAttr}${prefix}${translatedValue}${suffix}`;
}
return `${prefix}${translatedValue}${suffix}`;
};
const tagEnumTranslation = (getWebappTranslation, { enableTranslation, jhiPrefix }, { key, parsedInterpolate, prefix, suffix }) => {
if (!parsedInterpolate?.value) {
throw new Error(`Value is required for TagEnum ${key}.`);
}
const { value, fallback } = parsedInterpolate;
const translatedValue = `{{ ${JSON.stringify(getWebappTranslation(key))}[${value}]${fallback ? ` || ${fallback}` : ''} }}`;
if (enableTranslation) {
return ` [${jhiPrefix}Translate]="'${key}.' + (${parsedInterpolate?.value})"${prefix}${translatedValue}${suffix}`;
}
return `${prefix}${translatedValue}${suffix}`;
};
const pipeTranslation = (getWebappTranslation, { enableTranslation }, { key, prefix, suffix }) => {
if (enableTranslation) {
return `${prefix}{{ '${key}' | translate }}${suffix}`;
}
return `${prefix}${escapeHtmlTranslationValue(getWebappTranslation(key))}${suffix}`;
};
const valueTranslation = (getWebappTranslation, _replacerOptions, { filePath, key, prefix, suffix }) => {
let translationValue = getWebappTranslation(key);
const fileExtension = extname(filePath);
if (fileExtension === '.html') {
translationValue = escapeHtmlTranslationValue(translationValue);
}
else if (fileExtension === '.ts') {
translationValue = escapeTsTranslationValue(translationValue);
}
return `${prefix}${translationValue}${suffix}`;
};
const pipeEnumTranslation = (getWebappTranslation, { enableTranslation }, { key, parsedInterpolate, prefix, suffix }) => {
if (!parsedInterpolate?.value) {
throw new Error(`Value is required for TagEnum ${key}.`);
}
const { value, fallback } = parsedInterpolate;
if (enableTranslation) {
return `${prefix}{{ '${key}.' + ${value} | translate }}${suffix}`;
}
const translatedValue = `{{ ${JSON.stringify(getWebappTranslation(key))}[${value}]${fallback ? ` ?? ${fallback}` : ''} }}`;
return `${prefix}${translatedValue}${suffix}`;
};
const replaceImplementations = {
Tag: tagTranslation,
TagPipe: tagPipeTranslation,
TagEnum: tagEnumTranslation,
ValidationTag: validationTagTranslation,
Pipe: pipeTranslation,
PipeEnum: pipeEnumTranslation,
Value: valueTranslation,
};
export const createTranslationReplacer = (getWebappTranslation, opts) => {
const htmlJhiTranslateReplacer = createJhiTransformTranslateReplacer(getWebappTranslation, { escapeHtml: true });
const htmlJhiTranslateStringifyReplacer = createJhiTransformTranslateStringifyReplacer(getWebappTranslation);
let translationReplacer;
const enableTranslation = typeof opts === 'boolean' ? opts : opts.enableTranslation;
if (typeof opts !== 'boolean') {
translationReplacer = createJhiTranslateReplacer(optsReplacer => {
const replacer = replaceImplementations[optsReplacer.type] ??
(() => {
throw new Error(`Translation type not supported ${optsReplacer.type}`);
});
return replacer(getWebappTranslation, opts, optsReplacer);
}, { prefixPattern: '>\\s*', suffixPattern: '\\s*<' });
}
return function replaceAngularTranslations(content, filePath) {
if (filePath.endsWith('.html')) {
if (!enableTranslation) {
content = content.replace(new RegExp(TRANSLATE_REGEX, 'g'), '');
content = replacePlaceholders(getWebappTranslation, content);
}
}
if (/(:?\.html|component\.ts)$/.test(filePath)) {
content = htmlJhiTranslateReplacer(content);
content = htmlJhiTranslateStringifyReplacer(content);
}
if (/(:?\.html|.ts)$/.test(filePath)) {
content = translationReplacer ? translationReplacer?.(content, filePath) : content;
}
if (!enableTranslation) {
if (/(:?route|module)\.ts$/.test(filePath)) {
content = replacePageTitles(getWebappTranslation, content);
}
if (filePath.endsWith('error.route.ts')) {
content = replaceErrorMessage(getWebappTranslation, content);
}
}
return content;
};
};
const minimatch = new Minimatch('**/*{.html,.ts}');
export const isTranslatedAngularFile = file => minimatch.match(file.path);
export const translateAngularFilesTransform = (getWebappTranslation, opts) => {
const translate = createTranslationReplacer(getWebappTranslation, opts);
return passthrough(file => {
file.contents = Buffer.from(translate(file.contents.toString(), file.path));
});
};