Skip to content

Commit

Permalink
Added react-i18next to console
Browse files Browse the repository at this point in the history
Includes plugin for pseudotranslation, several command line utilities for file processing, and Christian Vogt's custom jest resolver/circular dependency fixes.
  • Loading branch information
jschuler authored and rebeccaalpert committed Oct 9, 2020
1 parent f4fe96b commit 5464e10
Show file tree
Hide file tree
Showing 60 changed files with 2,533 additions and 1,646 deletions.
2 changes: 1 addition & 1 deletion analyze.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ if [ -d "$ARTIFACT_DIR" ]; then
cp public/dist/report.html "${ARTIFACT_DIR}"
fi

MAX_BYTES=2726298 # ~2.6 MiB
MAX_BYTES=3145728 # ~3 MiB
VENDORS_MAIN_BYTES=$(jq -r '.assets[] | select(.name | match("^vendors~main-chunk.*js$")) | .size' public/dist/stats.json)
DISPLAY_VALUE=$(awk "BEGIN {printf \"%.2f\n\", $VENDORS_MAIN_BYTES/1024/1024}")
MAX_DISPLAY_VALUE=$(awk "BEGIN {printf \"%.2f\n\", $MAX_BYTES/1024/1024}")
Expand Down
90 changes: 0 additions & 90 deletions frontend/__tests__/components/utils/datetime.spec.ts
Original file line number Diff line number Diff line change
@@ -1,100 +1,10 @@
import {
fromNow,
isValid,
formatDuration,
formatPrometheusDuration,
parsePrometheusDuration,
} from '../../../public/components/utils/datetime';

describe('fromNow', () => {
it('prints past dates correctly', () => {
expect(fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 00:00:02'))).toEqual(
'a few seconds ago',
);
expect(fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 00:01:00'))).toEqual(
'a minute ago',
);
expect(fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 00:51:00'))).toEqual(
'an hour ago',
);
expect(fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 12:45:00'))).toEqual(
'13 hours ago',
);
expect(fromNow(new Date('Jan 01 1970'), new Date('Jan 02 1970'))).toEqual('a day ago');
expect(fromNow(new Date('Jan 01 1970'), new Date('Jan 09 1970'))).toEqual('8 days ago');
expect(fromNow(new Date('Jan 01 1970'), new Date('Feb 02 1970'))).toEqual('a month ago');
expect(fromNow(new Date('Jan 01 1970'), new Date('Mar 02 1970'))).toEqual('2 months ago');
expect(fromNow(new Date('Jan 01 1970'), new Date('Feb 02 1971'))).toEqual('a year ago');
expect(fromNow(new Date('Jan 01 1970'), new Date('Feb 02 1973'))).toEqual('3 years ago');
});

it('prints past dates with no prefixes/suffixes correctly', () => {
expect(
fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 00:00:02'), {
omitSuffix: true,
}),
).toEqual('few seconds');
expect(
fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 00:01:00'), {
omitSuffix: true,
}),
).toEqual('minute');
expect(
fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 00:51:00'), {
omitSuffix: true,
}),
).toEqual('hour');
expect(
fromNow(new Date('Jan 01 1970 00:00:00'), new Date('Jan 01 1970 12:45:00'), {
omitSuffix: true,
}),
).toEqual('13 hours');
expect(fromNow(new Date('Jan 01 1970'), new Date('Jan 02 1970'), { omitSuffix: true })).toEqual(
'day',
);
expect(fromNow(new Date('Jan 01 1970'), new Date('Jan 09 1970'), { omitSuffix: true })).toEqual(
'8 days',
);
expect(fromNow(new Date('Jan 01 1970'), new Date('Feb 02 1970'), { omitSuffix: true })).toEqual(
'month',
);
expect(fromNow(new Date('Jan 01 1970'), new Date('Mar 02 1970'), { omitSuffix: true })).toEqual(
'2 months',
);
expect(fromNow(new Date('Jan 01 1970'), new Date('Feb 02 1971'), { omitSuffix: true })).toEqual(
'year',
);
expect(fromNow(new Date('Jan 01 1970'), new Date('Feb 02 1973'), { omitSuffix: true })).toEqual(
'3 years',
);
});

it('prints future dates correctly', () => {
expect(fromNow(new Date('Jan 01 1970 00:00:02'), new Date('Jan 01 1970 00:00:00'))).toEqual(
'a few seconds from now',
);
expect(fromNow(new Date('Jan 01 1970 00:01:01'), new Date('Jan 01 1970 00:00:00'))).toEqual(
'a minute from now',
);
expect(fromNow(new Date('Jan 01 1970 01:01:00'), new Date('Jan 01 1970 00:00:00'))).toEqual(
'an hour from now',
);
expect(fromNow(new Date('Jan 01 1970 14:20:00'), new Date('Jan 01 1970 00:00:00'))).toEqual(
'14 hours from now',
);
expect(fromNow(new Date('Jan 02 1970'), new Date('Jan 01 1970'))).toEqual('a day from now');
expect(fromNow(new Date('Jan 09 1970'), new Date('Jan 01 1970'))).toEqual('8 days from now');
expect(fromNow(new Date('Feb 02 1970'), new Date('Jan 01 1970'))).toEqual('a month from now');
expect(fromNow(new Date('Mar 01 1970'), new Date('Jan 01 1970'))).toEqual('2 months from now');
expect(fromNow(new Date('Feb 02 1971'), new Date('Jan 01 1970'))).toEqual('a year from now');
expect(fromNow(new Date('Feb 02 1973'), new Date('Jan 01 1970'))).toEqual('3 years from now');
});

it('check for null value', () => {
expect(fromNow(null, new Date('Jan 01 1970 00:00:00'))).toEqual('-');
});
});

describe('isValid', () => {
it('rejects non-dates', () => {
expect(isValid('hello' as any)).toEqual(false);
Expand Down
10 changes: 10 additions & 0 deletions frontend/i18n-scripts/build-i18n.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

set -exuo pipefail

i18next "public/!(dist)**/**/*.{js,jsx,ts,tsx}" [-oc] -c "./public/i18next-parser.config.js" -o "public/locales/\$LOCALE/\$NAMESPACE.json"

cd packages
for d in */ ; do
i18next "${d}!(dist)**/**/*.{js,jsx,ts,tsx}" [-oc] -c "./../public/i18next-parser.config.js" -o "${d}locales/\$LOCALE/\$NAMESPACE.json"
done
43 changes: 43 additions & 0 deletions frontend/i18n-scripts/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const fs = require('fs');
const path = require('path');

// eslint-disable-next-line no-undef
module.exports = {
isDirectory: function(filePath) {
try {
const stat = fs.lstatSync(filePath);
return stat.isDirectory();
} catch (e) {
// lstatSync throws an error if path doesn't exist
return false;
}
},
findLocalesFolder: function(directory, argFunction, package) {
const localesFolder = path.join(directory, 'locales');
if (fs.existsSync(localesFolder)) {
return argFunction(localesFolder, package);
}
},
parseFolder: function(directory, argFunction, package) {
(async () => {
try {
const files = await fs.promises.readdir(directory);
for (const file of files) {
const filePath = path.join(directory, file);
argFunction(filePath, package);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
}
})();
},
deleteFile: function(filePath) {
try {
fs.unlinkSync(filePath);
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
}
},
};
108 changes: 108 additions & 0 deletions frontend/i18n-scripts/consolidate-public-folders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const fs = require('fs');
const path = require('path');
const common = require('./common.js');

// eslint-disable-next-line no-undef
const public = path.join(__dirname, './../public/locales/');
// eslint-disable-next-line no-undef
const packages = path.join(__dirname, './../packages');

const publicFileNames = {};

function processFile(fileName) {
const language = path.basename(path.dirname(fileName));
if (publicFileNames[language].includes(path.basename(fileName))) {
const file = require(fileName);
/* eslint-disable no-undef, no-console */
const publicFile = path.join(
__dirname,
`./../public/locales/${language}/${path.basename(fileName)}`,
);
/* eslint-enable */
const keys = Object.keys(file);

fs.readFile(publicFile, function(e, data) {
if (e) {
// eslint-disable-next-line no-console
return console.error(e);
}

const json = JSON.parse(data);
let hasConflict = false;

for (let i = 0; i < keys.length; i++) {
if (!json.hasOwnProperty(keys[i])) {
json[keys[i]] = file[keys[i]];
} else {
hasConflict = true;
// eslint-disable-next-line no-console
console.log(`Conflict: Key "${keys[i]}" in ${publicFile} already exists.`);
}
}

fs.writeFile(publicFile, JSON.stringify(json, null, 2), function(err) {
if (err) {
// eslint-disable-next-line no-console
return console.error(err);
}
});

if (hasConflict) {
// eslint-disable-next-line no-console
console.log('Please edit conflicting keys and run yarn i18n again');
} else {
common.deleteFile(fileName);
}
});
}
}

function processLocalesFolder(filePath) {
if (path.basename(filePath) === 'en') {
common.parseFolder(filePath, processFile);
}
if (path.basename(filePath) === 'zh') {
common.parseFolder(filePath, processFile);
}
if (path.basename(filePath) === 'ja') {
common.parseFolder(filePath, processFile);
}
}

function iterateThroughLocalesFolder(filePath) {
common.parseFolder(filePath, processLocalesFolder);
}

function processPackages(filePath) {
if (common.isDirectory(filePath)) {
common.findLocalesFolder(filePath, iterateThroughLocalesFolder);
}
}

function logFiles(filePath) {
const fileName = path.basename(filePath);
const language = path.basename(path.dirname(filePath));
if (publicFileNames[language]) {
if (publicFileNames[language].includes(fileName)) {
return;
}
publicFileNames[language].push(fileName);
} else {
publicFileNames[language] = [fileName];
}
}

function processPublic(filePath) {
if (path.basename(filePath) === 'en') {
common.parseFolder(filePath, logFiles);
}
if (path.basename(filePath) === 'zh') {
common.parseFolder(filePath, logFiles);
}
if (path.basename(filePath) === 'ja') {
common.parseFolder(filePath, logFiles);
}
}

common.parseFolder(public, processPublic);
common.parseFolder(packages, processPackages);
114 changes: 114 additions & 0 deletions frontend/i18n-scripts/i18n-to-pot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
const fs = require('fs');
const path = require('path');
const minimist = require('minimist');
const { i18nextToPot } = require('i18next-conv');
const common = require('./common.js');

function save(target) {
return (result) => {
fs.writeFileSync(target, result);
};
}

function removeValues(i18nFile, filePath) {
const file = require(i18nFile);

const updatedFile = {};

const keys = Object.keys(file);

for (let i = 0; i < keys.length; i++) {
updatedFile[keys[i]] = '';
}

const tmpFile = fs.openSync(filePath, 'w');

fs.writeFile(tmpFile, JSON.stringify(updatedFile, null, 2), function writeJSON(e) {
if (e) {
// eslint-disable-next-line no-console
return console.error(e);
}
});
}

function processFile(fileName, package, language) {
/* eslint-disable no-undef, no-console */
if (package) {
const i18nFile = path.join(__dirname, `./../packages/${package}/locales/en/${fileName}.json`);

fs.mkdirSync(path.join(__dirname, `./../packages/${package}/locales/tmp`), {
recursive: true,
});

const tmpFile = path.join(__dirname, `./../packages/${package}/locales/tmp/${fileName}.json`);

removeValues(i18nFile, tmpFile);

fs.mkdirSync(path.join(__dirname, `./../packages/${package}/locales/pots`), {
recursive: true,
});
i18nextToPot(language, fs.readFileSync(tmpFile)).then(
save(
path.join(
__dirname,
`./../packages/${package}/locales/pots/${path.basename(fileName)}.pot`,
),
),
);
common.deleteFile(tmpFile);
console.log(`Processed ${fileName}`);
} else {
const i18nFile = path.join(__dirname, `./../public/locales/en/${fileName}.json`);

fs.mkdirSync(path.join(__dirname, './../public/locales/tmp'), { recursive: true });

const tmpFile = path.join(__dirname, `./../public/locales/tmp/${fileName}.json`);

removeValues(i18nFile, tmpFile);

fs.mkdirSync(path.join(__dirname, './../public/locales/pots'), { recursive: true });
i18nextToPot(language, fs.readFileSync(tmpFile)).then(
save(path.join(__dirname, `./../public/locales/pots/${path.basename(fileName)}.pot`)),
);

common.deleteFile(tmpFile);
console.log(`Processed ${fileName}`);
}
/* eslint-enable */
}

const options = {
string: ['language', 'package', 'file'],
boolean: ['help'],
array: ['files'],
alias: {
h: 'help',
p: 'package',
f: 'files',
l: 'language',
},
default: {
files: [],
package: undefined,
language: 'ja',
},
};

const args = minimist(process.argv.slice(2), options);

if (args.help) {
// eslint-disable-next-line no-console
return console.log(
"-h: help\n-l: language (defaults to 'ja')\n-p: package (i.e. 'dev-console'; defaults to undefined)\n-f: file name to convert (i.e. 'nav')",
);
}

if (args.files) {
if (Array.isArray(args.files)) {
for (let i = 0; i < args.files.length; i++) {
processFile(args.files[i], args.package, args.language);
}
} else {
processFile(args.files, args.package, args.language);
}
}
Loading

0 comments on commit 5464e10

Please sign in to comment.