crowdfree
Version:
A crowdin compatible tool for translation and localisation of websites and applications
170 lines (153 loc) • 5.72 kB
JavaScript
/*
Module for easily localising projects using a web interface and integrated webserver
*/
const express = require("express")
const cors = require("cors")
const fs = require("fs")
const path = require("path")
const { getTranslations } = require("./main")
const { saveLocale, findLocaleFolder } = require("./localeTools")
const findSuggestions = require("./findSuggestions")
;
(async () => {
// Parse CLI arguments
const node_path = process.argv.shift()
const index_location = process.argv.shift()
const working_directory = process.argv.shift() || "."
// Get locale from disk
const localeFolder = await findLocaleFolder("./", working_directory)
const { translations, locales } = await getTranslations(localeFolder, updatesCallback)
let updatesToTransmit = []
function addToUpdatesToTransmit(transl) {
// Remove old versions of the same
updatesToTransmit = updatesToTransmit.filter(x => x.file !== transl.file && x.file !== transl.file)
// Push new version
updatesToTransmit.push(transl)
}
// Handle translations getting updated on disk during runtime
async function updatesCallback(updates) {
console.log("Updated translations: ", updates)
// Repopulate suggestions
await findSuggestions.translate(updates, locales)
// Add to updates array with timestamp for clients to fetch
updates.forEach(update =>
addToUpdatesToTransmit({
...update,
timestamp: Date.now(),
})
)
}
// Find suggested translations in the background
findSuggestions.translate(translations, locales)
// Start express API server
const app = express()
// Serve frontend
app.use(express.static(path.join(__dirname, "../frontend/build")))
let port = process.argv.shift() || 3300
app.listen(port)
console.log(`Listening to port ${port} (1st argument for project path, 2nd argument to specify a port)`)
app.use(express.json())
app.use(cors())
// API endpoints
// Get all translations
app.get("/api/translations", (req, res) => {
res.send({
ok: true,
data: translations
})
})
// Get translations that are missing strings only (to save on data for larger projects)
app.get("/api/translations/todo", (req, res) => {
res.send({
ok: true,
data: translations.filter(x => x.value.length < locales.length)
})
})
// Get translations that have been modified since [timestamp]
app.get("/api/translations/since/:since", (req, res) => {
// Remove old updates as clients should already have gotten them
updatesToTransmit = updatesToTransmit.filter(x => x.timestamp > Date.now() - 5 * 60 * 1000)
let time = Number(req.params.since)
if (!time) {
res.send({
ok: false,
msg: "Invalid timestamp"
})
} else {
res.send({
ok: true,
msg: `Sending translations that have been updated since ${time}`,
data: updatesToTransmit.filter(x => x.timestamp > time)
})
}
})
app.get("/api/translation/:file/:key", (req, res) => {
res.send({
ok: true,
data: translations.find(x => x.file === req.params.file && x.key === req.params.key)
})
})
app.post("/api/translations/update", async (req, res) => {
// Update a record and send update to peers
/* req.body
{
data: [{
file: "locale.json",
key: "headertext",
locale: "en",
props: {
value: "New english text"
}
}]
}
*/
// TODO: Input validation
// Update
let data = []
let errors = []
req.body.data.forEach(update => {
let oldRecord = translations.find(x => x.file === update.file && x.key === update.key)
if (!oldRecord) {
let error = new Error(`Could not find record ${update.file} ${update.key}`)
console.log(error)
return errors.push(error)
}
oldRecord.value[update.locale] = {
...oldRecord.value[update.locale],
...update.props,
}
oldRecord.timestamp = Date.now()
// Return modified object to client
data.push(oldRecord)
// Prepare for return to all clients
addToUpdatesToTransmit(oldRecord)
})
// Clean up data with empty value fields
// data.map(x => {
// for (let loc in x.value) {
// if (!x.value[loc].value) {
// delete x.value[loc]
// }
// }
// return x
// })
// Write updated locale to file
await saveLocale(translations, localeFolder)
// Repopulate suggestions
await findSuggestions.translate(data, locales)
// Replace in-memory translations with updated ones
res.send({
ok: true,
msg: "Updated translation",
data,
error: errors,
})
})
app.get("/api/locales", (req, res) => {
res.send({
ok: true,
data: locales
})
})
})()