Skip to content

Commit

Permalink
Json to pot conversion logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleg Klimenko committed Jul 28, 2017
1 parent da4ac0c commit 9e100cf
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
125 changes: 125 additions & 0 deletions convert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { I18NEntry, SingleI18NEntry, PluralI18NEntry } from 'i18n-proto';

export type Metadata = {
copyrightSubject: string,
bugsEmail: string,
year: number
};

export function makeDate(date: Date) {
const timezoneShift = date.getTimezoneOffset() / -60;
let tz = 'Z';
if (timezoneShift !== 0) {
tz = (timezoneShift > 0 ? '+' : '-') +
(timezoneShift > 9 ? '' : '0')
+ timezoneShift + '00';
}

return date.getFullYear() + '-' +
(date.getMonth() > 9 ? '' : '0') + date.getMonth() + '-' +
(date.getDay() > 9 ? '' : '0') + date.getDay() + ' ' +
(date.getHours() > 9 ? '' : '0') + date.getHours() + ':' +
(date.getMinutes() > 9 ? '' : '0') + date.getMinutes() +
tz;
}

export function convert(json: string, meta: Metadata): string {
const document: I18NEntry[] = JSON.parse(json);
let poEntries: PotEntry[] = [];

// 1) Make POT header
let poHeader =
`# Translations template for PROJECT.
# Copyright (C) ${meta.year} ${meta.copyrightSubject}
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, ${meta.year}.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\\n"
"Report-Msgid-Bugs-To: ${meta.bugsEmail}\\n"
"POT-Creation-Date: ${makeDate(new Date())}\\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
"Language-Team: LANGUAGE <LL@li.org>\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=utf-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Generated-By: i18n-json2po\\n"
`;

// 2) Make POT entries
for (let item of document) {
let potEntry = new PotEntry();
if (item.type === 'single') {
potEntry.parseSingleEntry(item);
}
if (item.type === 'plural') {
potEntry.parsePluralEntry(item);
}
poEntries.push(potEntry);
}

return poHeader + poEntries
.map((entry) => entry.asString())
.join("\n\n");
}

export class PotEntry {
private items: string[] = [];

protected addComment = (comment: string) => this.items.push('#. ' + comment);
protected addOccurence = (occ: string) => this.items.push('#: ' + occ);
protected addContext = (context: string) => this.items.push('msgctxt ' + JSON.stringify(context));
protected addMsgid = (id: string) => this.items.push('msgid ' + JSON.stringify(id));
protected addMsgidPlural = (id: string) => this.items.push('msgid_plural ' + JSON.stringify(id));
protected addMsgstr = () => this.items.push('msgstr ""');
protected addMsgstrPlural = (count: number) => {
for (let i = 0; i < count; i++) {
this.items.push('msgstr[' + i + '] ""');
}
};

public asString = () => this.items.join("\n");

public parseSingleEntry({ entry, comments, occurences, context, type }: SingleI18NEntry) {
if (comments) {
comments.forEach(this.addComment);
}

if (occurences) {
occurences.forEach(this.addOccurence);
}

if (context) {
this.addContext(context);
}

if (type === 'single') {
this.addMsgid(entry);
this.addMsgstr();
}
}

public parsePluralEntry({ entry, comments, occurences, context, type }: PluralI18NEntry) {
if (comments) {
comments.forEach(this.addComment);
}

if (occurences) {
occurences.forEach(this.addOccurence);
}

if (context) {
this.addContext(context);
}

if (type === 'plural') {
this.addMsgid(entry[0]);
this.addMsgidPlural(entry[entry.length - 1]);
this.addMsgstrPlural(entry.length);
}
}
}
76 changes: 76 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as cli from 'cli';
import { readFile, writeFile } from 'fs';
import { convert } from './convert';

const options = cli.parse({
src: ['s', 'A source JSON file to process', 'string', '__stdin'],
output: ['o', 'Output JSON file', 'string', '__stdout'],
copyrightSubject: ['c', 'Copyright for generated POT file', 'string', ''],
bugsEmail: ['b', 'Email for bugs', 'string', ''],
year: ['y', 'Copyright year', 'number', (new Date()).getFullYear()],
help: ['h', 'Show some help', 'bool', false]
});

if (options.help) {
console.log(`i18n JSON -> POT converter
Options:
-h / --help Show this help
-s / --src FILE Define input JSON file name. Defaults
to stdin.
-o / --output FILE Define output POT file name. If a file
already exists, it's contents will be
overwritten. Defaults to stdout.
-c / --copyrightSubject Team name or author name.
-b / --bugsEmail Email for sending bugs
-y / --year Copyright year, defaults to current year.
`);
process.exit(0);
}

console.warn('Running conversion for file: ', options.src);

const meta = {
copyrightSubject: options.copyrightSubject,
bugsEmail: options.bugsEmail,
year: options.year,
};

if (options.src === '__stdin') {
cli.withStdin((data) => {
try {
makeOutput(convert(data, meta), options.output);
} catch (e) {
console.error(e);
process.exit(1);
}
});
} else {
readFile(options.src, { encoding: 'utf-8' }, (err, data) => {
if (err) {
console.error(err);
process.exit(1);
}
try {
makeOutput(convert(data, meta), options.output);
} catch (e) {
console.error(e);
process.exit(1);
}
});
}

function makeOutput(data: string, output: string) {
if (output === '__stdout') {
console.log(data);
} else {
writeFile(output, data, (e) => {
if (e) {
console.error(e);
process.exit(1);
}
process.exit(0); // success
});
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"@types/cli": "^0.11.19",
"cli": "^1.0.1",
"i18n-proto": "^1.0.1",
"ts-node": "^3.3.0",
"typescript": "^2.4.2"
}
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ has-flag@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"

i18n-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/i18n-proto/-/i18n-proto-1.0.1.tgz#0a564b8cc893eee1f46800931561df0b917ccff0"

inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
Expand Down

0 comments on commit 9e100cf

Please sign in to comment.