components/frontend_react/webapp/scripts/translate.ts (73 lines of code) (raw):
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// You may need to remove "type": "module" in package.json
import { Translate } from "@google-cloud/translate/build/src/v2"
import { Promise as bluebird } from "bluebird"
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises"
import { difference } from "ramda"
import { supportedLangs } from "../src/utils/lang"
const translateClient = new Translate()
const targetLanguages = supportedLangs
.map((lang) => lang.code)
.filter((lang) => lang !== "en")
const SOURCE_BASE = "public/locales"
const ENGLISH_BASE = `${SOURCE_BASE}/en`
const CONCURRENCY = 3
const fileExists = async (path: string) =>
!!(await stat(path).catch((_) => false))
const ensureLanguageFileExists = async (dir: string, file: string) => {
if (!(await fileExists(dir))) {
try {
await mkdir(dir)
} catch (_) {}
}
if (!(await fileExists(file))) {
await writeFile(file, JSON.stringify({}))
}
}
async function main() {
const sourceFiles = await readdir(ENGLISH_BASE)
sourceFiles.forEach(async (sourceFile) => {
const sourcePhrases = JSON.parse(
await readFile(`${ENGLISH_BASE}/${sourceFile}`, "utf8"),
) as Record<string, string>
await bluebird.map(
targetLanguages,
async (targetLanguage: string) => {
console.log("LANG: ", targetLanguage)
const targetDir = `${SOURCE_BASE}/${targetLanguage}`
const targetFile = `${targetDir}/${sourceFile}`
await ensureLanguageFileExists(targetDir, targetFile)
const targetPhrases = JSON.parse(
await readFile(targetFile, "utf8"),
) as Record<string, string>
// Delete phrases from "en" version to have them deleted from all others
const deletePhrases = difference(
Object.keys(targetPhrases),
Object.keys(sourcePhrases),
)
if (deletePhrases.length) {
console.log("Deleting phrases:", deletePhrases.join(", "))
deletePhrases.forEach((phrase) => delete targetPhrases[phrase])
await writeFile(targetFile, JSON.stringify(targetPhrases, null, 2))
}
const missingPhrases = difference(
Object.keys(sourcePhrases),
Object.keys(targetPhrases),
)
if (!missingPhrases.length) return
// console.log("Translating phrases:", missingPhrases.join(", "))
const runTranslation = async (key: string) => {
const value = sourcePhrases[key] || ""
const [translation] = await translateClient.translate(value, {
from: "en",
to: targetLanguage,
})
// Remove extra spaces to HTML added by Translation API
targetPhrases[key] = translation.replaceAll(/\>\s/g, ">")
}
console.log(" Missing phrases:", missingPhrases.length)
await bluebird.map(missingPhrases, runTranslation, {
concurrency: CONCURRENCY,
})
await writeFile(targetFile, JSON.stringify(targetPhrases, null, 2))
},
{ concurrency: CONCURRENCY },
)
})
}
main().catch((error) => console.error(error))