crowdfree
Version:
A crowdin compatible tool for translation and localisation of websites and applications
154 lines (142 loc) • 6.57 kB
JavaScript
/*
Locales look like this:
{
locale: "en",
files: [{
name: "locale.json",
content: {
name: "Crowdfree",
description: "Simple localisations without all the fuss"
}
}]
}
A translation should look like this:
{
file: "locale.json",
key: "headertext",
value: {
en: {
value: "English header text",
},
no: {
value: "Norwegian header text",
},
es: {
value: false, // This one isn't translated yet, but is expected due to finding the locale folder
suggestions: [{
type: "google_translate",
value: "Habla es espanol?"
},{
type: "similar_source_translation",
value: "'en' value is similar to the 'en' value for another string in the project"
}]
}
}
}
*/
// const fs = require("fs-extra")
const fs = require("fs").promises
const path = require("path")
const { saveLocale, findLocaleFolder } = require("./localeTools")
const sortTranslations = require("./../frontend/src/util/sortTranslations")
async function getTranslations(localeFolder, updatesCallback) {
// Load locale from files
const locales = await Promise.all((await fs.readdir(localeFolder))
.map(async folderName => {
let localePath = path.join(localeFolder, folderName)
let files = (await fs.readdir(localePath))
.map(async file => {
// Handle changes in files after first start (after function exit)
if (updatesCallback) {
let watcher = fs.watch(path.join(localePath, file));
(async () => {
let waitingForRead = false
try {
for await (const event of watcher) {
if (!waitingForRead) {
// console.log(event)
waitingForRead = true;
setTimeout(async () => {
let newFile = {
name: file,
content: JSON.parse(await fs.readFile(path.join(localePath, file)))
}
waitingForRead = false
// Update locale with file
locales.find(x => x.locale === folderName)
.files.find(x => x.name === file)
.content = newFile.content
let oldTranslations = JSON.parse(JSON.stringify(translations))
// Update translaitons
populateTranslations(translations, locales)
sortTranslations(translations)
// Inform callee about updated translations
let updated = translations.filter(translation => oldTranslations
.find(x =>
x.file === translation.file
&& x.key === translation.key
&& Object.keys(x.value).find(y =>
// Value changed
x.value[y].value != translation.value[y]?.value
// Either new or old value has to be a string for there to be a change
&& (typeof x.value[y].value === "string" || typeof translation.value[y]?.value === "string")
)
)
)
updatesCallback?.(updated)
}, 1000)
}
}
} catch (e) {
console.error(e)
}
})()
}
// Return file contents (at function call)
return {
name: file,
content: JSON.parse(await fs.readFile(path.join(localePath, file)))
}
})
return {
locale: folderName,
files: await Promise.all(files),
}
}));
// console.log(JSON.stringify(locales, null, 2))
const translations = []
populateTranslations(translations, locales)
translations.forEach(x => x.timestamp = Date.now())
sortTranslations(translations)
return { translations, locales }
}
function populateTranslations(translations, locales) {
while (translations.length) translations.shift()
locales.forEach(locale => {
locale.files.forEach(file => {
for (let key in file.content) {
let find = translations.find(translation => translation.key === key)
if (find) {
// Extend existing translation
find.value[locale.locale] = {
value: file.content[key]
}
} else {
// Add new translation
translations.push({
file: file.name,
key,
value: {
[locale.locale]: {
value: file.content[key]
}
}
})
}
}
})
})
}
module.exports = {
getTranslations,
}