mirror of
https://github.com/elyby/emails-renderer.git
synced 2025-05-31 14:12:05 +05:30
Implemented font image renderer
This commit is contained in:
@@ -1,191 +0,0 @@
|
||||
/* eslint-env node */
|
||||
/* eslint-disable no-console */
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import glob from 'glob';
|
||||
import mkdirp from 'mkdirp';
|
||||
import chalk from 'chalk';
|
||||
import prompt from 'prompt';
|
||||
|
||||
// https://stackoverflow.com/a/50052194/5184751
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname); // eslint-disable-line
|
||||
|
||||
const MESSAGES_PATTERN = path.resolve(__dirname, '../dist/messages/**/*.json');
|
||||
const LANG_DIR = path.resolve(__dirname, '../src/i18n');
|
||||
const DEFAULT_LOCALE = 'en';
|
||||
const SUPPORTED_LANGS = Object.keys(JSON.parse(fs.readFileSync(path.join(LANG_DIR, 'index.json'))));
|
||||
|
||||
/**
|
||||
* Aggregates the default messages that were extracted from the app's
|
||||
* React components via the React Intl Babel plugin. An error will be thrown if
|
||||
* there are messages in different components that use the same `id`. The result
|
||||
* is a flat collection of `id: message` pairs for the app's default locale.
|
||||
*/
|
||||
let idToFileMap = {};
|
||||
let duplicateIds = [];
|
||||
const collectedMessages = glob.sync(MESSAGES_PATTERN)
|
||||
.map((filename) => [filename, JSON.parse(fs.readFileSync(filename, 'utf8'))])
|
||||
.reduce((collection, [file, descriptors]) => {
|
||||
descriptors.forEach(({id, defaultMessage}) => {
|
||||
if (collection.hasOwnProperty(id)) {
|
||||
duplicateIds.push(id);
|
||||
}
|
||||
|
||||
collection[id] = defaultMessage;
|
||||
idToFileMap[id] = (idToFileMap[id] || []).concat(file);
|
||||
});
|
||||
|
||||
return collection;
|
||||
}, {});
|
||||
|
||||
if (duplicateIds.length) {
|
||||
console.log('\nFound duplicated ids:');
|
||||
duplicateIds.forEach((id) => console.log(`${chalk.yellow(id)}:\n - ${idToFileMap[id].join('\n - ')}\n`));
|
||||
console.log(chalk.red('Please correct the errors above to proceed further!'));
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
duplicateIds = null;
|
||||
idToFileMap = null;
|
||||
|
||||
/**
|
||||
* Making a diff with the previous DEFAULT_LOCALE version
|
||||
*/
|
||||
const defaultMessagesPath = `${LANG_DIR}/${DEFAULT_LOCALE}.json`;
|
||||
let keysToUpdate = [];
|
||||
let keysToAdd = [];
|
||||
let keysToRemove = [];
|
||||
const keysToRename = [];
|
||||
const isNotMarked = (value) => value.slice(0, 2) !== '--';
|
||||
|
||||
const prevMessages = readJSON(defaultMessagesPath);
|
||||
const prevMessagesMap = Object.entries(prevMessages).reduce((acc, [key, value]) => {
|
||||
if (acc[value]) {
|
||||
acc[value].push(key);
|
||||
} else {
|
||||
acc[value] = [key];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
keysToAdd = Object.keys(collectedMessages).filter((key) => !prevMessages[key]);
|
||||
keysToRemove = Object.keys(prevMessages).filter((key) => !collectedMessages[key]).filter(isNotMarked);
|
||||
keysToUpdate = Object.entries(prevMessages).reduce((acc, [key, message]) =>
|
||||
acc.concat(collectedMessages[key] && collectedMessages[key] !== message ? key : [])
|
||||
, []);
|
||||
|
||||
// detect keys to rename, mutating keysToAdd and keysToRemove
|
||||
[].concat(keysToAdd).forEach((toKey) => {
|
||||
const keys = prevMessagesMap[collectedMessages[toKey]] || [];
|
||||
const fromKey = keys.find((fromKey) => keysToRemove.indexOf(fromKey) > -1);
|
||||
|
||||
if (fromKey) {
|
||||
keysToRename.push([fromKey, toKey]);
|
||||
|
||||
keysToRemove.splice(keysToRemove.indexOf(fromKey), 1);
|
||||
keysToAdd.splice(keysToAdd.indexOf(toKey), 1);
|
||||
}
|
||||
});
|
||||
|
||||
if (!keysToAdd.length && !keysToRemove.length && !keysToUpdate.length && !keysToRename.length) {
|
||||
console.log(chalk.green('Everything is up to date!'));
|
||||
process.exit();
|
||||
}
|
||||
|
||||
console.log(chalk.magenta(`The diff relative to default locale (${DEFAULT_LOCALE}) is:`));
|
||||
|
||||
if (keysToRemove.length) {
|
||||
console.log('The following keys will be removed:');
|
||||
console.log([chalk.red('\n - '), keysToRemove.join(chalk.red('\n - ')), '\n'].join(''));
|
||||
}
|
||||
|
||||
if (keysToAdd.length) {
|
||||
console.log('The following keys will be added:');
|
||||
console.log([chalk.green('\n + '), keysToAdd.join(chalk.green('\n + ')), '\n'].join(''));
|
||||
}
|
||||
|
||||
if (keysToUpdate.length) {
|
||||
console.log('The following keys will be updated:');
|
||||
console.log([chalk.yellow('\n @ '), keysToUpdate.join(chalk.yellow('\n @ ')), '\n'].join(''));
|
||||
}
|
||||
|
||||
if (keysToRename.length) {
|
||||
console.log('The following keys will be renamed:\n');
|
||||
console.log(keysToRename.reduce((str, pair) =>
|
||||
[str, pair[0], chalk.yellow(' -> '), pair[1], '\n'].join('')
|
||||
, ''));
|
||||
}
|
||||
|
||||
prompt.start();
|
||||
prompt.get({
|
||||
properties: {
|
||||
apply: {
|
||||
description: 'Apply changes? [Y/n]',
|
||||
pattern: /^y|n$/i,
|
||||
message: 'Please enter "y" or "n"',
|
||||
default: 'y',
|
||||
before: (value) => value.toLowerCase() === 'y'
|
||||
}
|
||||
}
|
||||
}, (err, resp) => {
|
||||
console.log('\n');
|
||||
|
||||
if (err || !resp.apply) {
|
||||
return console.log(chalk.red('Aborted'));
|
||||
}
|
||||
|
||||
buildLocales();
|
||||
|
||||
console.log(chalk.green('All locales was successfuly built'));
|
||||
});
|
||||
|
||||
|
||||
function buildLocales() {
|
||||
mkdirp.sync(LANG_DIR);
|
||||
|
||||
SUPPORTED_LANGS.map((lang) => {
|
||||
const destPath = `${LANG_DIR}/${lang}.json`;
|
||||
const newMessages = readJSON(destPath);
|
||||
|
||||
keysToRename.forEach(([fromKey, toKey]) => {
|
||||
newMessages[toKey] = newMessages[fromKey];
|
||||
delete newMessages[fromKey];
|
||||
});
|
||||
keysToRemove.forEach((key) => {
|
||||
delete newMessages[key];
|
||||
});
|
||||
keysToUpdate.forEach((key) => {
|
||||
newMessages[`--${key}`] = newMessages[key];
|
||||
newMessages[key] = collectedMessages[key];
|
||||
});
|
||||
keysToAdd.forEach((key) => {
|
||||
newMessages[key] = collectedMessages[key];
|
||||
});
|
||||
|
||||
const sortedKeys = Object.keys(newMessages).sort((key1, key2) => {
|
||||
key1 = key1.replace(/^\-+/, '');
|
||||
key2 = key2.replace(/^\-+/, '');
|
||||
|
||||
return key1 < key2 || !isNotMarked(key1) ? -1 : 1;
|
||||
});
|
||||
|
||||
const sortedNewMessages = sortedKeys.reduce((acc, key) => {
|
||||
acc[key] = newMessages[key];
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
fs.writeFileSync(destPath, JSON.stringify(sortedNewMessages, null, 4) + '\n');
|
||||
});
|
||||
}
|
||||
|
||||
function readJSON(destPath) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(destPath, 'utf8'));
|
||||
} catch (err) {
|
||||
console.log(chalk.yellow(`Can not read ${destPath}. The new file will be created.`), `(${err.message})`);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
57
scripts/i18n-collect/index.js
Executable file
57
scripts/i18n-collect/index.js
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/* eslint-env node */
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const sortKeys = require('sort-keys');
|
||||
|
||||
const DEFAULT_LOCALE = 'en';
|
||||
const INDEX_FILE_NAME = 'index';
|
||||
|
||||
const ROOT_PATH = path.resolve(__dirname, '../../src');
|
||||
const LANG_DIR = path.join(ROOT_PATH, 'i18n');
|
||||
const MESSAGES_PATTERN = path.join(ROOT_PATH, '**/*.intl.json');
|
||||
const TARGET_FILE = path.join(LANG_DIR, `${DEFAULT_LOCALE}.json`);
|
||||
|
||||
const messages = glob.sync(MESSAGES_PATTERN)
|
||||
.map((filename) => [filename, JSON.parse(fs.readFileSync(filename, 'utf8'))])
|
||||
.reduce((collection, [filename, descriptors]) => {
|
||||
const prefix = path.dirname(filename)
|
||||
.replace(ROOT_PATH, '')
|
||||
.replace(/^\/|\/$/g, '')
|
||||
.replace(/\//g, '.');
|
||||
for (const id in descriptors) {
|
||||
// noinspection JSUnfilteredForInLoop
|
||||
const key = `${prefix}.${id}`;
|
||||
// noinspection JSUnfilteredForInLoop
|
||||
const descriptor = descriptors[id];
|
||||
if (typeof descriptor === 'object') {
|
||||
const { defaultMessage } = descriptor;
|
||||
collection[key] = defaultMessage;
|
||||
} else {
|
||||
collection[key] = descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
}, {});
|
||||
|
||||
fs.writeFileSync(TARGET_FILE, JSON.stringify(sortKeys(messages), null, 4) + os.EOL);
|
||||
const messagesIds = Object.keys(messages);
|
||||
glob.sync(path.join(LANG_DIR, `!(${DEFAULT_LOCALE}|${INDEX_FILE_NAME}).json`))
|
||||
.map((filename) => [filename, JSON.parse(fs.readFileSync(filename, 'utf8'))])
|
||||
.forEach(([filename, translatedMessages]) => {
|
||||
const translatedMessagesIds = Object.keys(translatedMessages);
|
||||
messagesIds.filter((id) => !translatedMessagesIds.includes(id)).forEach((newKey) => {
|
||||
translatedMessages[newKey] = messages[newKey];
|
||||
});
|
||||
translatedMessagesIds.filter((id) => !messagesIds.includes(id)).forEach((removedKey) => {
|
||||
Reflect.deleteProperty(translatedMessages, removedKey);
|
||||
});
|
||||
|
||||
fs.writeFileSync(filename, JSON.stringify(sortKeys(translatedMessages), null, 4) + os.EOL);
|
||||
});
|
||||
9
scripts/i18n-collect/package.json
Normal file
9
scripts/i18n-collect/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "i18n-collect",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3",
|
||||
"sort-keys": "^3.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "scripts",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "i18n-build.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chalk": "^1.1.3",
|
||||
"glob": "^7.1.3",
|
||||
"mkdirp": "^0.5.1",
|
||||
"prompt": "^1.0.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user