2
0
mirror of https://github.com/frappe/books.git synced 2024-11-10 07:40:55 +00:00
books/src/getLanguageMap.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

151 lines
4.2 KiB
TypeScript
Raw Normal View History

2022-02-16 10:05:35 +00:00
/**
* Language files are fetched from the frappe/books repo
* the language files before storage have a ISO timestamp
* prepended to the file.
*
* This timestamp denotes the commit datetime, update of the file
* takes place only if a new update has been pushed.
*/
const fs = require('fs/promises');
const path = require('path');
2022-02-16 10:41:08 +00:00
const fetch = require('node-fetch').default;
2022-02-16 10:05:35 +00:00
const { splitCsvLine } = require('../scripts/helpers');
2022-04-08 06:59:42 +00:00
import { LanguageMap } from '../utils/types';
2022-02-16 10:05:35 +00:00
const VALENTINES_DAY = 1644796800000;
export async function getLanguageMap(
code: string,
isDevelopment: boolean = false
2022-04-08 06:59:42 +00:00
): Promise<LanguageMap> {
2022-02-16 10:05:35 +00:00
const contents = await getContents(code, isDevelopment);
return getMapFromContents(contents);
}
async function getContents(code: string, isDevelopment: boolean) {
2022-02-16 10:05:35 +00:00
if (isDevelopment) {
2022-02-16 10:41:08 +00:00
const filePath = path.resolve('translations', `${code}.csv`);
const contents = await fs.readFile(filePath, { encoding: 'utf-8' });
return ['', contents].join('\n');
2022-02-16 10:05:35 +00:00
}
let contents = await getContentsIfExists(code);
2022-02-16 10:05:35 +00:00
if (contents.length === 0) {
contents = (await fetchAndStoreFile(code)) ?? contents;
2022-02-16 10:05:35 +00:00
} else {
contents = (await getUpdatedContent(code, contents)) ?? contents;
}
if (!contents || contents.length === 0) {
throwCouldNotFetchFile(code);
2022-02-16 10:05:35 +00:00
}
return contents;
}
2022-04-08 06:59:42 +00:00
function getMapFromContents(contents: string): LanguageMap {
const lines: string[] = contents.split('\n').slice(1);
return lines
.map((l) => splitCsvLine(l) as string[])
2022-02-16 10:05:35 +00:00
.filter((l) => l.length >= 2)
.reduce((acc, l) => {
const key = l[0].slice(1, -1);
const translation = l[1].slice(1, -1);
acc[key] = { translation };
const context = l.slice(2)[0];
if (context?.length) {
acc[key].context = context;
2022-02-16 10:05:35 +00:00
}
return acc;
2022-04-08 06:59:42 +00:00
}, {} as LanguageMap);
2022-02-16 10:05:35 +00:00
}
async function getContentsIfExists(code: string): Promise<string> {
2022-02-16 10:05:35 +00:00
const filePath = getFilePath(code);
try {
return await fs.readFile(filePath, { encoding: 'utf-8' });
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
2022-02-16 10:05:35 +00:00
throw err;
}
return '';
}
2022-02-16 10:41:08 +00:00
}
2022-02-16 10:05:35 +00:00
async function fetchAndStoreFile(code: string, date?: Date) {
let res = await fetch(
`https://api.github.com/repos/frappe/books/contents/translations/${code}.csv`
);
let contents: string | undefined = undefined;
if (res.status === 200) {
const resJson = await res.json();
contents = Buffer.from(resJson.content, 'base64').toString();
} else {
res = await fetch(
`https://raw.githubusercontent.com/frappe/books/master/translations/${code}.csv`
);
2022-02-16 10:05:35 +00:00
}
if (!contents && res.status === 200) {
contents = await res.text();
2022-02-16 10:41:08 +00:00
}
if (!date && contents) {
date = await getLastUpdated(code);
}
2022-02-16 10:05:35 +00:00
if (contents) {
contents = [date!.toISOString(), contents].join('\n');
await storeFile(code, contents);
}
2022-02-16 10:05:35 +00:00
return contents;
}
async function getUpdatedContent(code: string, contents: string) {
const { shouldUpdate, date } = await shouldUpdateFile(code, contents);
2022-02-16 10:05:35 +00:00
if (!shouldUpdate) {
return contents;
}
return await fetchAndStoreFile(code, date);
}
async function shouldUpdateFile(code: string, contents: string) {
2022-02-16 10:05:35 +00:00
const date = await getLastUpdated(code);
const oldDate = new Date(contents.split('\n')[0]);
const shouldUpdate = date > oldDate || +oldDate === VALENTINES_DAY;
return { shouldUpdate, date };
2022-02-16 10:05:35 +00:00
}
async function getLastUpdated(code: string): Promise<Date> {
2022-02-16 10:05:35 +00:00
const url = `https://api.github.com/repos/frappe/books/commits?path=translations%2F${code}.csv&page=1&per_page=1`;
const resJson = await fetch(url).then((res: Response) => res.json());
2022-02-16 10:05:35 +00:00
try {
return new Date(resJson[0].commit.author.date);
} catch {
return new Date(VALENTINES_DAY);
2022-02-16 10:05:35 +00:00
}
}
function getFilePath(code: string) {
2022-02-16 10:05:35 +00:00
return path.resolve(process.resourcesPath, 'translations', `${code}.csv`);
}
function throwCouldNotFetchFile(code: string) {
throw new Error(`Could not fetch translations for '${code}'.`);
2022-02-16 10:05:35 +00:00
}
async function storeFile(code: string, contents: string) {
2022-02-16 10:05:35 +00:00
const filePath = getFilePath(code);
2022-02-16 10:41:08 +00:00
const dirname = path.dirname(filePath);
await fs.mkdir(dirname, { recursive: true });
2022-02-16 10:05:35 +00:00
await fs.writeFile(filePath, contents, { encoding: 'utf-8' });
}