mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-02 12:47:41 +02:00
Chore: Convert build-translations to TS
This commit is contained in:
parent
2f7801a267
commit
cbe260eed2
@ -955,6 +955,7 @@ packages/renderer/noteStyle.js
|
|||||||
packages/renderer/pathUtils.js
|
packages/renderer/pathUtils.js
|
||||||
packages/renderer/utils.js
|
packages/renderer/utils.js
|
||||||
packages/tools/build-release-stats.js
|
packages/tools/build-release-stats.js
|
||||||
|
packages/tools/build-translation.js
|
||||||
packages/tools/build-welcome.js
|
packages/tools/build-welcome.js
|
||||||
packages/tools/buildServerDocker.test.js
|
packages/tools/buildServerDocker.test.js
|
||||||
packages/tools/buildServerDocker.js
|
packages/tools/buildServerDocker.js
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -941,6 +941,7 @@ packages/renderer/noteStyle.js
|
|||||||
packages/renderer/pathUtils.js
|
packages/renderer/pathUtils.js
|
||||||
packages/renderer/utils.js
|
packages/renderer/utils.js
|
||||||
packages/tools/build-release-stats.js
|
packages/tools/build-release-stats.js
|
||||||
|
packages/tools/build-translation.js
|
||||||
packages/tools/build-welcome.js
|
packages/tools/build-welcome.js
|
||||||
packages/tools/buildServerDocker.test.js
|
packages/tools/buildServerDocker.test.js
|
||||||
packages/tools/buildServerDocker.js
|
packages/tools/buildServerDocker.js
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
// Dependencies:
|
// Dependencies:
|
||||||
//
|
//
|
||||||
// sudo apt install gettext sudo apt install translate-toolkit
|
// sudo apt install gettext sudo apt install translate-toolkit
|
||||||
@ -7,34 +5,35 @@
|
|||||||
// gettext v21+ is required as versions before that have bugs when parsing
|
// gettext v21+ is required as versions before that have bugs when parsing
|
||||||
// JavaScript template strings which means we would lose translations.
|
// JavaScript template strings which means we would lose translations.
|
||||||
|
|
||||||
const rootDir = `${__dirname}/../..`;
|
import markdownUtils from '@joplin/lib/markdownUtils';
|
||||||
|
import { translationExecutablePath, removePoHeaderDate, mergePotToPo, parsePoFile, parseTranslations, TranslationStatus } from './utils/translation';
|
||||||
const markdownUtils = require('@joplin/lib/markdownUtils').default;
|
import { execCommand, isMac, insertContentIntoFile, filename, dirname, fileExtension } from './tool-utils.js';
|
||||||
const fs = require('fs-extra');
|
import { countryDisplayName, countryCodeOnly } from '@joplin/lib/locale';
|
||||||
const { translationExecutablePath, removePoHeaderDate, mergePotToPo, parsePoFile, parseTranslations } = require('./utils/translation');
|
import { readdirSync, writeFileSync } from 'fs';
|
||||||
const localesDir = `${__dirname}/locales`;
|
import { readFile } from 'fs/promises';
|
||||||
const libDir = `${rootDir}/packages/lib`;
|
import { copy, mkdirpSync, remove } from 'fs-extra';
|
||||||
const { execCommand, isMac, insertContentIntoFile, filename, dirname, fileExtension } = require('./tool-utils.js');
|
|
||||||
const { countryDisplayName, countryCodeOnly } = require('@joplin/lib/locale');
|
|
||||||
|
|
||||||
const { GettextExtractor, JsExtractors } = require('gettext-extractor');
|
const { GettextExtractor, JsExtractors } = require('gettext-extractor');
|
||||||
|
|
||||||
function serializeTranslation(translation) {
|
const rootDir = `${__dirname}/../..`;
|
||||||
|
const localesDir = `${__dirname}/locales`;
|
||||||
|
const libDir = `${rootDir}/packages/lib`;
|
||||||
|
|
||||||
|
function serializeTranslation(translation: string) {
|
||||||
const output = parseTranslations(translation);
|
const output = parseTranslations(translation);
|
||||||
return JSON.stringify(output, Object.keys(output).sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : +1), ' ');
|
return JSON.stringify(output, Object.keys(output).sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : +1), ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveToFile(filePath, data) {
|
function saveToFile(filePath: string, data: string) {
|
||||||
fs.writeFileSync(filePath, data);
|
writeFileSync(filePath, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildLocale(inputFile, outputFile) {
|
async function buildLocale(inputFile: string, outputFile: string) {
|
||||||
const r = await parsePoFile(inputFile);
|
const r = await parsePoFile(inputFile);
|
||||||
const translation = serializeTranslation(r);
|
const translation = serializeTranslation(r);
|
||||||
saveToFile(outputFile, translation);
|
saveToFile(outputFile, translation);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPotFile(potFilePath) {
|
async function createPotFile(potFilePath: string) {
|
||||||
const excludedDirs = [
|
const excludedDirs = [
|
||||||
'./.git/*',
|
'./.git/*',
|
||||||
'./.github/*',
|
'./.github/*',
|
||||||
@ -82,7 +81,7 @@ async function createPotFile(potFilePath) {
|
|||||||
// basename, such as "exmaple.js", and "example.ts", we only keep the file
|
// basename, such as "exmaple.js", and "example.ts", we only keep the file
|
||||||
// with ".ts" extension (since the .js should be the compiled file).
|
// with ".ts" extension (since the .js should be the compiled file).
|
||||||
|
|
||||||
const toProcess = {};
|
const toProcess: Record<string, string> = {};
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file) continue;
|
if (!file) continue;
|
||||||
@ -172,7 +171,7 @@ async function createPotFile(potFilePath) {
|
|||||||
await removePoHeaderDate(potFilePath);
|
await removePoHeaderDate(potFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildIndex(locales, stats) {
|
function buildIndex(locales: string[], stats: TranslationStatus[]) {
|
||||||
const output = [];
|
const output = [];
|
||||||
output.push('var locales = {};');
|
output.push('var locales = {};');
|
||||||
output.push('var stats = {};');
|
output.push('var stats = {};');
|
||||||
@ -196,10 +195,10 @@ function buildIndex(locales, stats) {
|
|||||||
return output.join('\n');
|
return output.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function availableLocales(defaultLocale) {
|
function availableLocales(defaultLocale: string) {
|
||||||
const output = [defaultLocale];
|
const output = [defaultLocale];
|
||||||
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
|
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
|
||||||
fs.readdirSync(localesDir).forEach((path) => {
|
readdirSync(localesDir).forEach((path) => {
|
||||||
if (fileExtension(path) !== 'po') return;
|
if (fileExtension(path) !== 'po') return;
|
||||||
const locale = filename(path);
|
const locale = filename(path);
|
||||||
if (locale === defaultLocale) return;
|
if (locale === defaultLocale) return;
|
||||||
@ -208,7 +207,7 @@ function availableLocales(defaultLocale) {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractTranslator(regex, poContent) {
|
function extractTranslator(regex: RegExp, poContent: string) {
|
||||||
const translatorMatch = poContent.match(regex);
|
const translatorMatch = poContent.match(regex);
|
||||||
let translatorName = '';
|
let translatorName = '';
|
||||||
|
|
||||||
@ -225,13 +224,13 @@ function extractTranslator(regex, poContent) {
|
|||||||
return translatorName;
|
return translatorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function translatorNameToMarkdown(translatorName) {
|
function translatorNameToMarkdown(translatorName: string) {
|
||||||
const matches = translatorName.match(/^(.*?)\s*\((.*)\)$/);
|
const matches = translatorName.match(/^(.*?)\s*\((.*)\)$/);
|
||||||
if (!matches) return translatorName;
|
if (!matches) return translatorName;
|
||||||
return `[${markdownUtils.escapeTitleText(matches[1])}](mailto:${markdownUtils.escapeLinkUrl(matches[2])})`;
|
return `[${markdownUtils.escapeTitleText(matches[1])}](mailto:${markdownUtils.escapeLinkUrl(matches[2])})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function translationStatus(isDefault, poFile) {
|
async function translationStatus(isDefault: boolean, poFile: string): Promise<TranslationStatus> {
|
||||||
// "apt install translate-toolkit" to have pocount
|
// "apt install translate-toolkit" to have pocount
|
||||||
let pocountPath = 'pocount';
|
let pocountPath = 'pocount';
|
||||||
if (isMac()) pocountPath = translationExecutablePath('pocount');
|
if (isMac()) pocountPath = translationExecutablePath('pocount');
|
||||||
@ -248,7 +247,7 @@ async function translationStatus(isDefault, poFile) {
|
|||||||
const untranslatedCount = Number(untranslatedMatches[1]);
|
const untranslatedCount = Number(untranslatedMatches[1]);
|
||||||
|
|
||||||
let translatorName = '';
|
let translatorName = '';
|
||||||
const content = await fs.readFile(poFile, 'utf-8');
|
const content = await readFile(poFile, 'utf-8');
|
||||||
|
|
||||||
translatorName = extractTranslator(/Last-Translator:\s*?(.*)/, content);
|
translatorName = extractTranslator(/Last-Translator:\s*?(.*)/, content);
|
||||||
if (!translatorName) {
|
if (!translatorName) {
|
||||||
@ -276,7 +275,7 @@ async function translationStatus(isDefault, poFile) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function flagImageUrl(locale) {
|
function flagImageUrl(locale: string) {
|
||||||
const baseUrl = 'https://joplinapp.org/images/flags';
|
const baseUrl = 'https://joplinapp.org/images/flags';
|
||||||
if (locale === 'ar') return `${baseUrl}/country-4x3/arableague.png`;
|
if (locale === 'ar') return `${baseUrl}/country-4x3/arableague.png`;
|
||||||
if (locale === 'eu') return `${baseUrl}/es/basque_country.png`;
|
if (locale === 'eu') return `${baseUrl}/es/basque_country.png`;
|
||||||
@ -292,11 +291,11 @@ function flagImageUrl(locale) {
|
|||||||
return `${baseUrl}/country-4x3/${countryCodeOnly(locale).toLowerCase()}.png`;
|
return `${baseUrl}/country-4x3/${countryCodeOnly(locale).toLowerCase()}.png`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function poFileUrl(locale) {
|
function poFileUrl(locale: string) {
|
||||||
return `https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/${locale}.po`;
|
return `https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/${locale}.po`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function translationStatusToMdTable(status) {
|
function translationStatusToMdTable(status: TranslationStatus[]) {
|
||||||
const output = [];
|
const output = [];
|
||||||
output.push([' ', 'Language', 'Po File', 'Last translator', 'Percent done'].join(' | '));
|
output.push([' ', 'Language', 'Po File', 'Last translator', 'Percent done'].join(' | '));
|
||||||
output.push(['---', '---', '---', '---', '---'].join('|'));
|
output.push(['---', '---', '---', '---', '---'].join('|'));
|
||||||
@ -308,7 +307,7 @@ function translationStatusToMdTable(status) {
|
|||||||
return output.join('\n');
|
return output.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateReadmeWithStats(stats) {
|
async function updateReadmeWithStats(stats: TranslationStatus[]) {
|
||||||
await insertContentIntoFile(
|
await insertContentIntoFile(
|
||||||
`${rootDir}/README.md`,
|
`${rootDir}/README.md`,
|
||||||
'<!-- LOCALE-TABLE-AUTO-GENERATED -->\n',
|
'<!-- LOCALE-TABLE-AUTO-GENERATED -->\n',
|
||||||
@ -317,12 +316,12 @@ async function updateReadmeWithStats(stats) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function translationStrings(poFilePath) {
|
async function translationStrings(poFilePath: string) {
|
||||||
const r = await parsePoFile(poFilePath);
|
const r = await parsePoFile(poFilePath);
|
||||||
return Object.keys(r.translations['']);
|
return Object.keys(r.translations['']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletedStrings(oldStrings, newStrings) {
|
function deletedStrings(oldStrings: string[], newStrings: string[]) {
|
||||||
const output = [];
|
const output = [];
|
||||||
for (const s1 of oldStrings) {
|
for (const s1 of oldStrings) {
|
||||||
if (newStrings.includes(s1)) continue;
|
if (newStrings.includes(s1)) continue;
|
||||||
@ -342,7 +341,7 @@ async function main() {
|
|||||||
|
|
||||||
if (missingStringsCheckOnly) {
|
if (missingStringsCheckOnly) {
|
||||||
tempPotFilePath = `${localesDir}/joplin-temp-${Math.floor(Math.random() * 10000000)}.pot`;
|
tempPotFilePath = `${localesDir}/joplin-temp-${Math.floor(Math.random() * 10000000)}.pot`;
|
||||||
await fs.copy(potFilePath, tempPotFilePath);
|
await copy(potFilePath, tempPotFilePath);
|
||||||
potFilePath = tempPotFilePath;
|
potFilePath = tempPotFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +358,7 @@ async function main() {
|
|||||||
|
|
||||||
console.info(`Updated pot file. Total strings: ${oldPotStatus.untranslatedCount} => ${newPotStatus.untranslatedCount}`);
|
console.info(`Updated pot file. Total strings: ${oldPotStatus.untranslatedCount} => ${newPotStatus.untranslatedCount}`);
|
||||||
|
|
||||||
if (tempPotFilePath) await fs.remove(tempPotFilePath);
|
if (tempPotFilePath) await remove(tempPotFilePath);
|
||||||
|
|
||||||
const deletedCount = oldPotStatus.untranslatedCount - newPotStatus.untranslatedCount;
|
const deletedCount = oldPotStatus.untranslatedCount - newPotStatus.untranslatedCount;
|
||||||
if (deletedCount >= 5) {
|
if (deletedCount >= 5) {
|
||||||
@ -379,7 +378,7 @@ async function main() {
|
|||||||
|
|
||||||
await execCommand(`cp "${potFilePath}" ` + `"${localesDir}/${defaultLocale}.po"`);
|
await execCommand(`cp "${potFilePath}" ` + `"${localesDir}/${defaultLocale}.po"`);
|
||||||
|
|
||||||
fs.mkdirpSync(jsonLocalesDir, 0o755);
|
mkdirpSync(jsonLocalesDir, 0o755);
|
||||||
|
|
||||||
const stats = [];
|
const stats = [];
|
||||||
|
|
@ -2,6 +2,14 @@ import { execCommand, isMac } from '../tool-utils';
|
|||||||
import { existsSync, readFile } from 'fs-extra';
|
import { existsSync, readFile } from 'fs-extra';
|
||||||
const gettextParser = require('gettext-parser');
|
const gettextParser = require('gettext-parser');
|
||||||
|
|
||||||
|
export interface TranslationStatus {
|
||||||
|
locale?: string;
|
||||||
|
languageName?: string;
|
||||||
|
translatorName: string;
|
||||||
|
percentDone: number;
|
||||||
|
untranslatedCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type Translations = Record<string, string>;
|
export type Translations = Record<string, string>;
|
||||||
|
|
||||||
export const removePoHeaderDate = async (filePath: string) => {
|
export const removePoHeaderDate = async (filePath: string) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user