refactored translations to avoid breakage

This commit is contained in:
or-else
2021-07-18 21:54:39 -07:00
parent eb299b73d2
commit 62fc84912e
7 changed files with 119 additions and 1394 deletions
+112
View File
@@ -0,0 +1,112 @@
// 1. Takes file with messages extracted via the React Intl Babel plugin and converts it into a file containing
// `id: message` pairs in "en" locale.
//
// 2. Takes all translations and groups them into a single file.
const fs = require('fs');
const path = require('path');
const globSync = require('glob').sync;
const mkdirpSync = require('mkdirp').sync;
const EXTRACTED_STRINGS = './src/i18n/ex/base-en.json';
const LANG_DIR = './src/i18n/';
const LANG_PATTERN = './src/i18n/*.json';
const FINAL_FILE = './src/messages.json';
const args = process.argv.slice(2);
function getLocaleFromFileName(filename) {
const parts = filename.split('/');
return parts[parts.length - 1].split('.json')[0];
}
// Called with a parameter?
if (args[0] == 'missing' || args[0] == 'obsolete') {
// Extract missing or obsolete translations as requested.
// Read all translations.
const extracted = globSync(LANG_PATTERN)
.map((filename) => {
const locale = getLocaleFromFileName(filename);
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
const dest = {};
// Copy messages while skipping missing and obsolete ones.
for (let key in translated) {
if ((args[0] == 'missing' && translated[key].missing) ||
(args[0] == 'obsolete' && translated[key].obsolete)) {
dest[key] = translated[key];
}
}
return { [locale]: dest };
})
.reduce((collection, descriptors) => {
return { ...collection, ...descriptors };
}, {});
console.log(JSON.stringify(extracted, null, 2)+"\n");
return;
}
// The formatjs/cli saves extracted messages into a file {'message_id': {defaultMessage: 'default message text', description: '...'}, ...}.
// Rewrite them into a single file en.json {'message_id': 'default message text', }.
// Read master-set of messages in default language.
const baseMessages = JSON.parse(fs.readFileSync(EXTRACTED_STRINGS, 'utf8'));
// Find messages present in baseMessages but missing in translations and vice versa.
// Mark missing or obsolete messages as such.
globSync(LANG_PATTERN)
.map((filename) => {
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
// Refresh existing entries with possible new descriptions and default messages, add missing.
Object.entries(baseMessages).forEach((ent) => {
const [key, value] = ent;
translated[key] = {
translation: translated[key] ? translated[key].translation : "",
defaultMessage: value.defaultMessage,
description: value.description,
missing: !translated[key],
obsolete: false
};
});
// Find no longer needed messages.
Object.entries(translated).forEach((ent) => {
const [key, value] = ent;
if (!baseMessages[key] && translated[key]) {
// Obsolete, no longer missing.
translated[key].missing = false;
translated[key].obsolete = true;
}
});
fs.writeFileSync(filename, JSON.stringify(translated, null, 2)+"\n");
});
// Combine all translations into a single json {"lang_1": {...}, "lang_2: {...}"}
// while removing messages marked as missing or obsolete.
// Read all translations.
const messages = globSync(LANG_PATTERN)
.map((filename) => {
const locale = getLocaleFromFileName(filename);
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
const dest = {};
// Copy messages while skipping missing and obsolete ones.
for (let key in translated) {
if (!translated[key].missing && !translated[key].obsolete) {
dest[key] = translated[key].translation;
}
}
return { [locale]: dest };
})
.reduce((collection, descriptors) => {
return { ...collection, ...descriptors };
}, {});
// Write a file with all translations combined.
mkdirpSync(path.dirname(FINAL_FILE));
fs.writeFileSync(FINAL_FILE, JSON.stringify(messages));
-100
View File
@@ -1,100 +0,0 @@
// 1. Takes file with messages extracted via the React Intl Babel plugin and converts it into a file containing
// `id: message` pairs in "en" locale.
//
// 2. Takes all translations and groups them into a single file.
const fs = require('fs');
const path = require('path');
const globSync = require('glob').sync;
const mkdirpSync = require('mkdirp').sync;
const EXTRACTED_STRINGS = './src/i18n/ex/base-en.json';
const DEFAULT_TRANSLATION = 'en.json';
const LANG_DIR = './src/i18n/';
const LANG_PATTERN = './src/i18n/*.json';
const FINAL_FILE = './src/messages.json';
const TRANSLATIONS = './src/translations.js'
const MISSING_MARKER = ':!![MISSING]!!:';
const OBSOLETE_MARKER = ':!![OBSOLETE]!!:';
// The formatjs/cli saves extracted messages into a file {'message_id': {defaultMessage: 'default message text', description: '...'}, ...}.
// Rewrite them into a single file en.json {'message_id': 'default message text', }.
// Read master-set of messages in default language.
const baseMessages = JSON.parse(fs.readFileSync(EXTRACTED_STRINGS, 'utf8'));
// Convert.
const en = {};
for (const [id, msg] of Object.entries(baseMessages)) {
en[id] = msg.defaultMessage;
}
// Write extracted strings into default language file.
let filename = path.join(LANG_DIR, DEFAULT_TRANSLATION);
mkdirpSync(path.dirname(filename));
fs.writeFileSync(filename, JSON.stringify(en, null, 2)+"\n");
// Find messages present in en.json but missing in translations and vice versa.
// Mark missing or obsolete messages as such in translation files.
globSync(LANG_PATTERN)
.map((filename) => {
const parts = filename.split('/');
if (parts[parts.length-1] != DEFAULT_TRANSLATION) {
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
// Find missing messages.
Object.entries(baseMessages).forEach((ent) => {
if (!translated.hasOwnProperty(ent[0])) {
// Missing translation.
translated[ent[0]] = MISSING_MARKER + ent[1];
} else if (translated[ent[0]].indexOf(OBSOLETE_MARKER) === 0) {
// No longer obsolete. Remove obsolete marker.
translated[ent[0]] = translated[ent[0]].substring(0, OBSOLETE_MARKER.length);
}
});
// Find no longer needed messages.
Object.entries(translated).forEach((ent) => {
if (!baseMessages.hasOwnProperty(ent[0]) && translated.hasOwnProperty(ent[0])) {
let val = translated[ent[0]];
if (val.indexOf(MISSING_MARKER) === 0) {
// No longer missing.
val = val.substring(0, MISSING_MARKER.length);
}
if (val.indexOf(OBSOLETE_MARKER) !== 0) {
val = OBSOLETE_MARKER + val;
}
translated[ent[0]] = val;
}
});
fs.writeFileSync(filename, JSON.stringify(translated, null, 2)+"\n");
}
});
// List of all available translations.
const languageList = {};
// Combine all translations into a single json {"lang_1": {...}, "lang_2: {...}"}
// while removing messages marked as missing or obsolete.
// Read all translations.
const messages = globSync(LANG_PATTERN)
.map((filename) => {
const parts = filename.split('/');
const locale = parts[parts.length - 1].split('.json')[0];
const translated = JSON.parse(fs.readFileSync(filename, 'utf8'));
languageList[locale] = locale;
// Strip messages prefixed with MISSING or OBSOLETE markers.
for (let id in translated) {
// process.stdout.write("-- handling translation " + id + "; "+ String(translated[id]) + "; in " + String(filename) + "; --\n");
if (translated[id].indexOf(OBSOLETE_MARKER) == 0 ||
translated[id].indexOf(MISSING_MARKER) == 0) {
delete translated[id];
}
}
return { [locale]: translated };
})
.reduce((collection, descriptors) => {
return { ...collection, ...descriptors };
}, {});
// Write file with all translations combined.
mkdirpSync(path.dirname(FINAL_FILE));
fs.writeFileSync(FINAL_FILE, JSON.stringify(messages, null, 2)+"\n");