mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-24 05:52:48 +00:00
Merge pull request #21290 from overleaf/ls-scripts-to-esm-translations
Migrate scripts/translation to esm GitOrigin-RevId: 475ec949f0ba238791df91de109169584e68c701
This commit is contained in:
parent
52edad6f12
commit
8293771f58
15 changed files with 195 additions and 134 deletions
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
"node-fetch": "^2.7.0",
|
||||
"sanitize-html": "^2.12.1",
|
||||
"yargs": "^17.7.2"
|
||||
}
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
12
services/web/scripts/translations/utils.js
Normal file
12
services/web/scripts/translations/utils.js
Normal 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')
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue