gen-jhipster
Version:
VHipster - Spring Boot + Angular/React/Vue in one handy generator
116 lines (115 loc) • 5.43 kB
JavaScript
/**
* Copyright 2013-2026 the original author or authors from the JHipster project.
*
* This file is part of the JHipster project, see https://www.jhipster.tech/
* for more information.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { passthrough } from '@yeoman/transform';
import { Minimatch } from 'minimatch';
const TRANSLATE_IMPORT_1 = /import { ?[T|t]ranslate(?:, ?[T|t]ranslate)? ?} from 'react-jhipster';?/.source; // Translate imports
const TRANSLATE_IMPORT_2 = / *[T|t]ranslate,|, ?[T|t]ranslate/.source; // Translate import
const TRANSLATE_IMPORT = [TRANSLATE_IMPORT_1, TRANSLATE_IMPORT_2].join('|');
const TRANSLATE_FUNCTION = /translate\(\s*'(?<key>[^']+)'(?:,\s*(?<interpolate>\{[^}]*\}))?\s*\)/g.source;
const CONTENT_TYPE_ATTRIBUTE = String.raw `contentKey=(?:"(?<key>[^\"]+)"|\{[^\}]+\})\s*`;
const INTERPOLATE_ATTRIBUTE = String.raw `interpolate=\{(?<interpolate>\{[^\}]+\})\}\s*`;
const COMPONENT_ATTRIBUTE = String.raw `component="(?<component>[^\"]+)"\s*`;
const TRANSLATE_TAG = String.raw `<Translate\s*(?:(?:${COMPONENT_ATTRIBUTE}|${INTERPOLATE_ATTRIBUTE}|${CONTENT_TYPE_ATTRIBUTE})+)>(?<translation>[\s\S]*?)<\/Translate>`;
const replaceTranslationKeysWithText = (getWebappTranslation, body, regexp, { keyPattern, interpolatePattern, wrapTranslation, escapeHtml } = {}) => {
const matches = body.matchAll(new RegExp(regexp, 'g'));
if (typeof wrapTranslation === 'string') {
wrapTranslation = [wrapTranslation, wrapTranslation];
}
for (const match of matches) {
const target = match[0];
let key = match.groups?.key;
if (!key && keyPattern) {
const keyMatch = new RegExp(keyPattern).exec(target);
key = keyMatch?.groups?.key;
}
if (!key) {
throw new Error(`Translation key not found for ${target}`);
}
let interpolate = match.groups?.interpolate;
if (!interpolate && interpolatePattern) {
const interpolateMatch = new RegExp(interpolatePattern).exec(target);
interpolate = interpolateMatch?.groups?.interpolate;
}
let data;
if (interpolate) {
const interpolateMatches = interpolate.matchAll(/(?<field>[^{\s:,}]+)(?::\s*(?<value>[^,}]+))?/g);
data = {};
for (const interpolateMatch of interpolateMatches) {
const field = interpolateMatch?.groups?.field;
let value = interpolateMatch?.groups?.value;
value ??= key;
value = value.trim();
if (/^\d+$/.test(value)) {
// convert integer
value = parseInt(value, 10);
}
else if (/^'.*'$/.test(value) || /^".*"$/.test(value)) {
// extract string value
value = value.slice(1, -2);
}
else {
// wrap expression
value = `{${value}}`;
}
data[field] = value;
}
}
const translation = getWebappTranslation(key, data);
let replacement = translation;
if (!replacement) {
replacement = wrapTranslation ? `${wrapTranslation[0]}${wrapTranslation[1]}` : '';
}
else if (wrapTranslation) {
replacement = `${wrapTranslation[0]}${translation}${wrapTranslation[1]}`;
}
else if (escapeHtml) {
// Escape specific chars
replacement = replacement.replace(/'/g, ''').replace(/"/g, '"');
}
body = body.replace(target, replacement);
}
return body;
};
/**
* Replace and cleanup translations.
*/
export const createTranslationReplacer = (getWebappTranslation) => function replaceReactTranslations(body, filePath) {
if (filePath.endsWith('.tsx')) {
body = body.replace(new RegExp(TRANSLATE_IMPORT, 'g'), '');
body = replaceTranslationKeysWithText(getWebappTranslation, body, String.raw `\{\s*${TRANSLATE_FUNCTION}\s*\}`, {
wrapTranslation: '"',
});
body = replaceTranslationKeysWithText(getWebappTranslation, body, TRANSLATE_FUNCTION, { wrapTranslation: '"' });
body = replaceTranslationKeysWithText(getWebappTranslation, body, TRANSLATE_TAG, {
keyPattern: CONTENT_TYPE_ATTRIBUTE,
interpolatePattern: INTERPOLATE_ATTRIBUTE,
escapeHtml: true,
});
}
return body;
};
const minimatch = new Minimatch('**/*.tsx');
export const isTranslatedReactFile = (file) => minimatch.match(file.path);
const translateReactFilesTransform = (getWebappTranslation) => {
const translate = createTranslationReplacer(getWebappTranslation);
return passthrough(file => {
file.contents = Buffer.from(translate(file.contents.toString(), file.path));
});
};
export default translateReactFilesTransform;