/* This file is part of GNU Taler (C) 2020 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ /** * Convert a .po file into a JavaScript / TypeScript expression. */ // @ts-ignore import * as po2jsonLib from "po2json"; import * as fs from "fs"; import glob = require("glob"); //types defined by the po2json library type Header = { domain: string; lang: string; 'plural_forms': string; }; type MessagesType = Record> & { "": Header } interface pojsonType { // X-Domain or 'messages' domain: string; locale_data: { messages: MessagesType } } // ----------- end pf po2json interface StringsType { // X-Domain or 'messages' domain: string; lang: string; completeness: number, 'plural_forms': string; locale_data: { messages: Record> } } // This prelude match the types above const TYPES_FOR_STRING_PRELUDE = ` export interface StringsType { domain: string; lang: string; completeness: number; 'plural_forms': string; locale_data: { messages: Record; }; }; `; const DEFAULT_STRING_PRELUDE = `${TYPES_FOR_STRING_PRELUDE}export const strings: Record = {};\n\n` export function po2ts(): void { const files = glob.sync("src/i18n/*.po"); if (files.length === 0) { console.error("no .po files found in src/i18n/"); process.exit(1); } console.log(files); let prelude: string; try { prelude = fs.readFileSync("src/i18n/strings-prelude", "utf-8") } catch (e) { prelude = DEFAULT_STRING_PRELUDE } const chunks = [prelude]; for (const filename of files) { const m = filename.match(/([a-zA-Z0-9-_]+).po/); if (!m) { console.error("error: unexpected filename (expected .po)"); process.exit(1); } const lang = m[1]; const poAsJson: pojsonType = po2jsonLib.parseFileSync(filename, { format: "jed1.x", fuzzy: true, }); const header = poAsJson.locale_data.messages[""] const total = calculateTotalTranslations(poAsJson.locale_data.messages) const completeness = header.lang === "en" ? 100 // 'en' is always complete : Math.floor(total.translations * 100 / total.keys); const strings: StringsType = { locale_data: poAsJson.locale_data, domain: poAsJson.domain, plural_forms: header.plural_forms, lang: header.lang, completeness, } const value = JSON.stringify(strings, undefined, 2) const s = `strings['${lang}'] = ${value};\n\n` chunks.push(s); } const tsContents = chunks.join(""); fs.writeFileSync("src/i18n/strings.ts", tsContents); } function calculateTotalTranslations(msgs: MessagesType): { keys: number, translations: number } { const kv = Object.entries(msgs) const [keys, translations] = kv.reduce(([total, withTranslation], translation) => { if (!translation || translation.length !== 2 || !translation[1]) { //current key is empty return [total, withTranslation] } const v = translation[1] if (!Array.isArray(v)) { // this is not a translation return [total, withTranslation] } if (!v.length || !v[0].length) { //translation is missing return [total + 1, withTranslation] } //current key has a translation return [total + 1, withTranslation + 1] }, [0, 0]) return { keys, translations } }