Merge pull request #21290 from overleaf/ls-scripts-to-esm-translations

Migrate scripts/translation to esm

GitOrigin-RevId: 475ec949f0ba238791df91de109169584e68c701
This commit is contained in:
Liangjun Song 2024-10-24 13:18:37 +01:00 committed by Copybot
parent 52edad6f12
commit 8293771f58
15 changed files with 195 additions and 134 deletions

View file

@ -1,21 +1,25 @@
const fs = require('fs')
const Path = require('path')
import fs from 'fs'
import Path from 'path'
import { fileURLToPath } from 'node:url'
import { loadLocale } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const LOCALES = Path.join(__dirname, '../../locales')
const SORT_BY_PROGRESS = process.argv.includes('--sort-by-progress')
function count(file) {
return Object.keys(require(Path.join(LOCALES, file))).length
function count(language) {
const locale = loadLocale(language)
return Object.keys(locale).length
}
async function main() {
const EN = count('en.json')
const EN = count('en')
const rows = []
for (const file of await fs.promises.readdir(LOCALES)) {
if (file === 'README.md') continue
const n = count(file)
const name = file.replace('.json', '')
const n = count(name)
rows.push({
name,
done: n,
@ -29,7 +33,9 @@ async function main() {
console.table(rows)
}
main().catch(error => {
try {
await main()
} catch (error) {
console.error(error)
process.exit(1)
})
}

View file

@ -1,17 +1,19 @@
const Path = require('path')
const fs = require('fs')
const { sanitize } = require('./sanitize')
import Path from 'path'
import fs from 'fs'
import Senitize from './sanitize.js'
import { fileURLToPath } from 'node:url'
import { loadLocale } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const { sanitize } = Senitize
async function main() {
let ok = true
const base = Path.join(__dirname, '/../../locales')
for (const name of await fs.promises.readdir(base)) {
if (name === 'README.md') continue
const blob = await fs.promises.readFile(
Path.join(__dirname, '/../../locales', name),
'utf-8'
)
const locales = JSON.parse(blob)
const language = name.replace('.json', '')
const locales = loadLocale(language)
for (const key of Object.keys(locales)) {
const want = locales[key]
@ -32,11 +34,10 @@ async function main() {
}
}
main()
.then(() => {
process.exit(0)
})
.catch(error => {
console.error(error)
process.exit(1)
})
try {
await main()
process.exit(0)
} catch (error) {
console.error(error)
process.exit(1)
}

View file

@ -1,10 +1,13 @@
const fs = require('fs')
const Path = require('path')
import fs from 'fs'
import Path from 'path'
import { fileURLToPath } from 'node:url'
import { loadLocale } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const GLOBALS = ['__appName__']
const LOCALES = Path.join(__dirname, '../../locales')
const baseLocalePath = Path.join(LOCALES, 'en.json')
const baseLocale = JSON.parse(fs.readFileSync(baseLocalePath, 'utf-8'))
const baseLocale = loadLocale('en')
const baseLocaleKeys = Object.keys(baseLocale)
const IGNORE_ORPHANED_TRANSLATIONS = process.argv.includes(
@ -41,9 +44,7 @@ function difference(key, base, target) {
let violations = 0
for (const localeName of fs.readdirSync(LOCALES)) {
if (localeName === 'README.md') continue
const localePath = Path.join(LOCALES, localeName)
const locale = JSON.parse(fs.readFileSync(localePath, 'utf-8'))
const locale = loadLocale(localeName.replace('.json', ''))
for (const key of Object.keys(locale)) {
if (!baseLocaleKeys.includes(key)) {

View file

@ -1,7 +1,10 @@
const fs = require('fs')
const Path = require('path')
const { execSync } = require('child_process')
import fs from 'fs'
import Path from 'path'
import { execSync } from 'child_process'
import { fileURLToPath } from 'node:url'
import { loadLocale } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const EN_JSON = Path.join(__dirname, '../../locales/en.json')
const CHECK = process.argv.includes('--check')
const SYNC_NON_EN = process.argv.includes('--sync-non-en')
@ -17,7 +20,7 @@ const COUNT_SUFFIXES = [
]
async function main() {
const locales = JSON.parse(await fs.promises.readFile(EN_JSON, 'utf-8'))
const locales = loadLocale('en')
const src = execSync(
// - find all the app source files in web
@ -112,7 +115,7 @@ async function main() {
if (name === 'README.md') continue
if (name === 'en.json') continue
const path = Path.join(LOCALES, name)
const locales = JSON.parse(await fs.promises.readFile(path, 'utf-8'))
const locales = loadLocale(name.replace('.json', ''))
for (const key of Object.keys(locales)) {
if (!found.has(key)) {
delete locales[key]
@ -154,7 +157,9 @@ async function main() {
await fs.promises.writeFile(EN_JSON, sorted)
}
main().catch(error => {
try {
await main()
} catch (error) {
console.error(error)
process.exit(1)
})
}

View file

@ -1,7 +1,13 @@
import fs from 'fs'
import Path from 'path'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const ONESKY_SETTING_PATH = Path.join(__dirname, '../../data/onesky.json')
let userOptions
try {
userOptions = require('../../data/onesky.json')
userOptions = JSON.parse(fs.readFileSync(ONESKY_SETTING_PATH))
} catch (err) {
if (err.code !== 'ENOENT') throw err
if (!process.env.ONE_SKY_PUBLIC_KEY) {
console.error(
'Cannot detect onesky credentials.\n\tDevelopers: see the docs at',
@ -24,6 +30,6 @@ function withAuth(options) {
)
}
module.exports = {
export default {
withAuth,
}

View file

@ -1,52 +1,59 @@
const path = require('path')
const { promises: fs } = require('fs')
const oneSky = require('@brainly/onesky-utils')
const { sanitize } = require('./sanitize')
const { withAuth } = require('./config')
import path from 'path'
import { promises as fs } from 'fs'
import oneSky from '@brainly/onesky-utils'
import Sanitize from './sanitize.js'
import Config from './config.js'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const { sanitize } = Sanitize
const { withAuth } = Config
async function run() {
try {
// The recommended OneSky set-up appears to require an API request to
// generate files on their side, which you could then request and use. We
// only have 1 such file that appears to be misnamed (en-US, despite our
// translations being marked as GB) and very out-of-date.
// However by requesting the "multilingual file" for this file, we get all
// of the translations
const content = await oneSky.getMultilingualFile(
withAuth({
fileName: 'en-US.json',
})
)
const json = JSON.parse(content)
// The recommended OneSky set-up appears to require an API request to
// generate files on their side, which you could then request and use. We
// only have 1 such file that appears to be misnamed (en-US, despite our
// translations being marked as GB) and very out-of-date.
// However by requesting the "multilingual file" for this file, we get all
// of the translations
const content = await oneSky.getMultilingualFile(
withAuth({
fileName: 'en-US.json',
})
)
const json = JSON.parse(content)
for (const [code, lang] of Object.entries(json)) {
if (code === 'en-GB') {
// OneSky does not have read-after-write consistency.
// Skip the dump of English locales, which may not include locales
// that were just uploaded.
continue
}
for (let [key, value] of Object.entries(lang.translation)) {
// Handle multi-line strings as arrays by joining on newline
if (Array.isArray(value)) {
value = value.join('\n')
}
lang.translation[key] = sanitize(value)
}
await fs.writeFile(
path.join(__dirname, `/../../locales/${code}.json`),
JSON.stringify(
lang.translation,
Object.keys(lang.translation).sort(),
2
) + '\n'
)
for (const [code, lang] of Object.entries(json)) {
if (code === 'en-GB') {
// OneSky does not have read-after-write consistency.
// Skip the dump of English locales, which may not include locales
// that were just uploaded.
continue
}
} catch (error) {
console.error(error)
process.exit(1)
for (let [key, value] of Object.entries(lang.translation)) {
// Handle multi-line strings as arrays by joining on newline
if (Array.isArray(value)) {
value = value.join('\n')
}
lang.translation[key] = sanitize(value)
}
await fs.writeFile(
path.join(__dirname, `/../../locales/${code}.json`),
JSON.stringify(
lang.translation,
Object.keys(lang.translation).sort(),
2
) + '\n'
)
}
}
run()
try {
await run()
} catch (error) {
console.error(error)
process.exit(1)
}

View file

@ -13,6 +13,9 @@
localeKey: ['key1', 'key2']
click_here_to_view_sl_in_lng: ['lngName']
*/
import TransformLocales from './transformLocales.js'
import { fileURLToPath } from 'url'
const MAPPING = {
support_lots_of_features: ['help_guides_link'],
nothing_to_install_ready_to_go: ['start_now'],
@ -37,8 +40,6 @@ const MAPPING = {
click_here_to_view_sl_in_lng: ['lngName'],
}
const { transformLocales } = require('./transformLocales')
function transformLocale(locale, components) {
components.forEach((key, idx) => {
const i18nKey = `__${key}__`
@ -51,9 +52,12 @@ function transformLocale(locale, components) {
}
function main() {
transformLocales(MAPPING, transformLocale)
TransformLocales.transformLocales(MAPPING, transformLocale)
}
if (require.main === module) {
if (
fileURLToPath(import.meta.url).replace(/\.js$/, '') ===
process.argv[1].replace(/\.js$/, '')
) {
main()
}

View file

@ -4,5 +4,6 @@
"node-fetch": "^2.7.0",
"sanitize-html": "^2.12.1",
"yargs": "^17.7.2"
}
},
"type": "module"
}

View file

@ -13,13 +13,14 @@
localeKey: ['keyLinkOpen', 'keyLinkClose']
faq_pay_by_invoice_answer: ['payByInvoiceLinkOpen', 'payByInvoiceLinkClose']
*/
import TransformLocales from './transformLocales.js'
import { fileURLToPath } from 'url'
const MAPPING = {
also_provides_free_plan: ['registerLinkOpen', 'registerLinkClose'],
faq_pay_by_invoice_answer: ['payByInvoiceLinkOpen', 'payByInvoiceLinkClose'],
}
const { transformLocales } = require('./transformLocales')
function transformLocale(locale, [open, close]) {
const i18nOpen = `__${open}__`
const i18nClose = `__${close}__`
@ -30,9 +31,12 @@ function transformLocale(locale, [open, close]) {
}
function main() {
transformLocales(MAPPING, transformLocale)
TransformLocales.transformLocales(MAPPING, transformLocale)
}
if (require.main === module) {
if (
fileURLToPath(import.meta.url).replace(/\.js$/, '') ===
process.argv[1].replace(/\.js$/, '')
) {
main()
}

View file

@ -1,4 +1,4 @@
const sanitizeHtml = require('sanitize-html')
import sanitizeHtml from 'sanitize-html'
/**
* Sanitize a translation string to prevent injection attacks
@ -43,4 +43,4 @@ function sanitize(input) {
)
}
module.exports = { sanitize }
export default { sanitize }

View file

@ -1,5 +1,8 @@
const fs = require('fs')
const Path = require('path')
import fs from 'fs'
import Path from 'path'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const LOCALES = Path.join(__dirname, '../../locales')
const CHECK = process.argv.includes('--check')
@ -30,7 +33,9 @@ async function main() {
}
}
main().catch(error => {
try {
await main()
} catch (error) {
console.error(error)
process.exit(1)
})
}

View file

@ -1,5 +1,9 @@
const Path = require('path')
const fs = require('fs')
import Path from 'path'
import fs from 'fs'
import { fileURLToPath } from 'node:url'
import { loadLocale } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const LANGUAGES = [
'cs',
@ -24,7 +28,7 @@ const LANGUAGES = [
const LOCALES = {}
LANGUAGES.forEach(loadLocales)
function loadLocales(language) {
LOCALES[language] = require(`../../locales/${language}.json`)
LOCALES[language] = loadLocale(language)
}
function transformLocales(mapping, transformLocale) {
@ -45,6 +49,6 @@ function transformLocales(mapping, transformLocale) {
})
}
module.exports = {
export default {
transformLocales,
}

View file

@ -1,14 +1,19 @@
const yargs = require('yargs/yargs')
const { hideBin } = require('yargs/helpers')
const Path = require('path')
const fs = require('fs')
import yargs from 'yargs/yargs'
import { hideBin } from 'yargs/helpers'
import Path from 'path'
import fs from 'fs'
import { fileURLToPath } from 'url'
import Settings from '../../config/settings.defaults.js'
import Readline from 'readline'
import { loadLocale as loadTranslations } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const LOCALES = Path.join(__dirname, '../../locales')
const VALID_LOCALES = Object.keys(
require('../../config/settings.defaults').translatedLanguages
).filter(locale => locale !== 'en')
const VALID_LOCALES = Object.keys(Settings.translatedLanguages).filter(
locale => locale !== 'en'
)
const readline = require('readline').createInterface({
const readline = Readline.createInterface({
input: process.stdin,
output: process.stdout,
})
@ -91,12 +96,6 @@ async function translateLocales() {
}
}
async function loadTranslations(locale) {
return JSON.parse(
fs.readFileSync(Path.join(LOCALES, `${locale}.json`), 'utf-8')
)
}
function prompt(text) {
return new Promise((resolve, reject) =>
readline.question(text, value => {
@ -105,11 +104,10 @@ function prompt(text) {
)
}
translateLocales()
.then(() => {
process.exit(0)
})
.catch(error => {
console.error(error)
process.exit(1)
})
try {
await translateLocales()
process.exit(0)
} catch (error) {
console.error(error)
process.exit(1)
}

View file

@ -1,8 +1,13 @@
const Path = require('path')
const { promises: fs } = require('fs')
const { promisify } = require('util')
const oneSky = require('@brainly/onesky-utils')
const { withAuth } = require('./config')
import Path from 'path'
import { promises as fs } from 'fs'
import { promisify } from 'util'
import oneSky from '@brainly/onesky-utils'
import Config from './config.js'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const { withAuth } = Config
const sleep = promisify(setTimeout)
@ -58,7 +63,9 @@ async function main() {
}
}
main().catch(error => {
try {
await main()
} catch (error) {
console.error({ error })
process.exit(1)
})
}

View file

@ -0,0 +1,12 @@
import { fileURLToPath } from 'url'
import fs from 'fs'
import Path from 'path'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const LOCALES_FOLDER = Path.join(__dirname, '../../locales')
export function loadLocale(language) {
return JSON.parse(
fs.readFileSync(Path.join(LOCALES_FOLDER, `${language}.json`), 'utf-8')
)
}