Implemented font image renderer

This commit is contained in:
ErickSkrauch
2019-05-10 01:53:28 +03:00
parent cb84df8f96
commit 625b5a9b94
42 changed files with 628 additions and 531 deletions

View File

@@ -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
View 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);
});

View 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"
}
}

View File

@@ -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"
}
}