2022-07-23 23:16:25 -05:00
|
|
|
import fs from 'fs'
|
|
|
|
import path from 'path'
|
2023-01-11 19:50:57 +01:00
|
|
|
import rdir from 'recursive-readdir'
|
|
|
|
import yaml from 'js-yaml'
|
2023-03-14 19:05:10 -05:00
|
|
|
import { fileURLToPath } from 'url'
|
2023-07-13 21:15:25 +02:00
|
|
|
import allLanguages from '../../../config/languages.json' assert { type: 'json' }
|
2023-07-02 18:50:20 +02:00
|
|
|
import { designs } from '../i18n/designs.mjs'
|
2023-01-11 19:50:57 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is where we configure what folders we should check for
|
|
|
|
* code-adjacent translation source files
|
|
|
|
*/
|
2023-03-14 19:05:10 -05:00
|
|
|
const sitesFolder = path.join(fileURLToPath(import.meta.url), '..', '..', '..')
|
2023-03-28 16:47:07 +02:00
|
|
|
export const folders = {
|
2023-03-14 19:05:10 -05:00
|
|
|
org: [path.join(sitesFolder, 'org', 'pages'), path.join(sitesFolder, 'org', 'components')],
|
2023-03-26 14:02:19 +02:00
|
|
|
dev: [path.join(sitesFolder, 'dev', 'pages'), path.join(sitesFolder, 'dev', 'components')],
|
2023-05-24 20:02:25 +02:00
|
|
|
lab: [path.join(sitesFolder, 'lab', 'pages'), path.join(sitesFolder, 'lab', 'components')],
|
2023-05-01 18:27:06 +02:00
|
|
|
shared: [
|
|
|
|
path.join(sitesFolder, 'shared', 'components'),
|
|
|
|
path.join(sitesFolder, 'shared', 'i18n'),
|
|
|
|
],
|
2023-01-11 19:50:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper method to write out JSON files for translation sources
|
|
|
|
*/
|
2023-07-02 18:50:20 +02:00
|
|
|
const writeJson = async (site, locale, namespace, content) => {
|
|
|
|
fs.mkdirSync(path.resolve('..', site, 'public', 'locales', locale), { recursive: true })
|
2022-10-15 05:24:18 +02:00
|
|
|
fs.writeFileSync(
|
|
|
|
path.resolve('..', site, 'public', 'locales', locale, `${namespace}.json`),
|
|
|
|
JSON.stringify(content)
|
|
|
|
)
|
2023-07-02 18:50:20 +02:00
|
|
|
}
|
2022-08-25 10:06:00 +02:00
|
|
|
|
2023-01-11 19:50:57 +01:00
|
|
|
/*
|
|
|
|
* Helper method to get a list of code-adjecent translation files in a folder.
|
|
|
|
* Will traverse recursively to get all files from a given root folder.
|
|
|
|
*
|
|
|
|
* Parameters:
|
|
|
|
*
|
|
|
|
* - site: the site folder to generate translations files for
|
|
|
|
*
|
|
|
|
*/
|
2023-07-13 21:15:25 +02:00
|
|
|
const getI18nFileList = async (site, languages) => {
|
2023-01-11 19:50:57 +01:00
|
|
|
const dirs = [...folders.shared]
|
|
|
|
if (site === 'org') dirs.push(...folders.org)
|
2023-03-26 14:02:19 +02:00
|
|
|
if (site === 'dev') dirs.push(...folders.dev)
|
2023-05-24 20:02:25 +02:00
|
|
|
if (site === 'lab') dirs.push(...folders.lab)
|
2023-01-11 19:50:57 +01:00
|
|
|
|
|
|
|
const allFiles = []
|
|
|
|
for (const dir of dirs) {
|
|
|
|
try {
|
|
|
|
const dirFiles = await rdir(dir)
|
|
|
|
allFiles.push(...dirFiles)
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-23 19:05:41 +02:00
|
|
|
const keep = languages.map((loc) => `${loc}.yaml`)
|
2023-07-11 20:17:24 +02:00
|
|
|
|
2023-01-11 19:50:57 +01:00
|
|
|
// Filter out the language files
|
2023-07-23 19:05:41 +02:00
|
|
|
return allFiles.filter((file) => keep.includes(file.slice(-7))).sort()
|
2023-01-11 19:50:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper method to get language and namespace from the filename
|
|
|
|
*
|
|
|
|
* Parameters:
|
|
|
|
*
|
|
|
|
* - filename: The filename or full path + filename
|
|
|
|
*/
|
2023-07-23 19:05:41 +02:00
|
|
|
const languageAndNamespaceFromFilename = (file) => [
|
|
|
|
path.basename(file).split('.')[0],
|
|
|
|
path.dirname(file).split('/').pop(),
|
|
|
|
]
|
2023-01-11 19:50:57 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper method to load a YAML file from disk
|
|
|
|
*/
|
2023-03-28 16:47:07 +02:00
|
|
|
export const loadYaml = (file, complain = true) => {
|
2023-01-11 19:50:57 +01:00
|
|
|
let data
|
|
|
|
try {
|
|
|
|
data = yaml.load(fs.readFileSync(file, 'utf-8'))
|
|
|
|
} catch (err) {
|
2023-03-28 16:47:07 +02:00
|
|
|
if (complain) console.log(err)
|
2023-01-11 19:50:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper method to build an object of namespaces and their values.
|
|
|
|
* Includes providing an EN fallback if something is not available in a language.
|
|
|
|
*
|
|
|
|
* Parameters:
|
|
|
|
*
|
|
|
|
* - files: List of files to process
|
|
|
|
*/
|
|
|
|
const filesAsNamespaces = (files) => {
|
|
|
|
// First build the object
|
|
|
|
const translations = {}
|
|
|
|
for (const file of files) {
|
2023-07-23 19:05:41 +02:00
|
|
|
const [lang, namespace] = languageAndNamespaceFromFilename(file)
|
2023-01-11 19:50:57 +01:00
|
|
|
if (typeof translations[namespace] === 'undefined') {
|
|
|
|
translations[namespace] = {}
|
|
|
|
}
|
|
|
|
translations[namespace][lang] = loadYaml(file)
|
|
|
|
}
|
|
|
|
|
|
|
|
return translations
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper method to ensure all translations all available in the data
|
|
|
|
*
|
|
|
|
* Parameter:
|
|
|
|
*
|
|
|
|
* - data: The raw data based on loaded YAML files
|
|
|
|
*/
|
2023-07-13 21:15:25 +02:00
|
|
|
const fixData = (rawData, languages) => {
|
2023-01-11 19:50:57 +01:00
|
|
|
const data = {}
|
|
|
|
for (const [namespace, nsdata] of Object.entries(rawData)) {
|
|
|
|
if (typeof nsdata.en === 'undefined') {
|
|
|
|
throw `No English data for namespace ${namespace}. Bailing out`
|
|
|
|
}
|
|
|
|
data[namespace] = { en: nsdata.en }
|
2023-07-02 18:50:20 +02:00
|
|
|
// Complete other langauges
|
|
|
|
for (const lang of languages.filter((loc) => loc !== 'en')) {
|
2023-01-12 19:10:49 +01:00
|
|
|
if (typeof nsdata[lang] === 'undefined') data[namespace][lang] = nsdata.en
|
2023-01-11 19:50:57 +01:00
|
|
|
else {
|
|
|
|
for (const key of Object.keys(data[namespace].en)) {
|
|
|
|
if (typeof nsdata[lang][key] === 'undefined') nsdata[lang][key] = nsdata.en[key]
|
|
|
|
}
|
|
|
|
data[namespace][lang] = nsdata[lang]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2023-07-02 18:50:20 +02:00
|
|
|
/*
|
|
|
|
* Converst a pattern translation file to a namespace structure
|
|
|
|
* for a given language
|
|
|
|
*/
|
|
|
|
const patternTranslationAsNamespace = (i18n, language) => {
|
|
|
|
const pojo = {
|
2023-07-02 18:59:53 +02:00
|
|
|
t: i18n[language].t,
|
|
|
|
d: i18n[language].d,
|
2023-07-02 18:50:20 +02:00
|
|
|
}
|
|
|
|
for (const [key, val] of Object.entries(i18n[language].s)) {
|
|
|
|
pojo[key] = val
|
|
|
|
}
|
|
|
|
for (const [key, val] of Object.entries(i18n[language].p)) {
|
|
|
|
pojo[key] = val
|
|
|
|
}
|
|
|
|
for (const [key, val] of Object.entries(i18n[language].o)) {
|
|
|
|
pojo[`${key}.t`] = val.t
|
|
|
|
pojo[`${key}.d`] = val.d
|
|
|
|
}
|
|
|
|
|
|
|
|
return pojo
|
|
|
|
}
|
2023-01-11 19:50:57 +01:00
|
|
|
/*
|
|
|
|
* The method that does the actual work
|
|
|
|
*/
|
2023-07-19 19:08:41 +02:00
|
|
|
export const prebuildI18n = async (store) => {
|
2023-07-13 21:15:25 +02:00
|
|
|
/*
|
|
|
|
* FreeSewing.dev is only available in English
|
|
|
|
*/
|
2023-07-19 19:08:41 +02:00
|
|
|
const languages = store.site === 'dev' ? ['en'] : allLanguages
|
2023-07-13 21:15:25 +02:00
|
|
|
|
2023-07-02 18:50:20 +02:00
|
|
|
/*
|
|
|
|
* Handle code-adjacent translations (for React components and so on)
|
|
|
|
*/
|
2023-07-19 19:08:41 +02:00
|
|
|
const files = await getI18nFileList(store.site, languages)
|
2023-01-11 19:50:57 +01:00
|
|
|
const data = filesAsNamespaces(files)
|
2023-07-13 21:15:25 +02:00
|
|
|
const namespaces = fixData(data, languages)
|
2023-01-11 19:50:57 +01:00
|
|
|
// Write out code-adjacent source files
|
2023-07-02 18:50:20 +02:00
|
|
|
for (const language of languages) {
|
2023-01-11 19:50:57 +01:00
|
|
|
// Fan out into namespaces
|
|
|
|
for (const namespace in namespaces)
|
2023-07-19 19:08:41 +02:00
|
|
|
writeJson(store.site, language, namespace, namespaces[namespace][language])
|
2023-07-02 18:50:20 +02:00
|
|
|
}
|
2023-07-19 19:08:41 +02:00
|
|
|
|
2023-07-02 18:50:20 +02:00
|
|
|
/*
|
|
|
|
* Handle design translations
|
|
|
|
*/
|
2023-07-02 18:59:53 +02:00
|
|
|
const designNs = {}
|
2023-07-02 18:50:20 +02:00
|
|
|
for (const design in designs) {
|
|
|
|
for (const language of languages) {
|
2023-07-02 18:59:53 +02:00
|
|
|
if (typeof designNs[language] === 'undefined') designNs[language] = {}
|
2023-07-02 18:50:20 +02:00
|
|
|
// Write out design namespace files
|
2023-07-02 18:59:53 +02:00
|
|
|
const content = patternTranslationAsNamespace(designs[design], language)
|
|
|
|
designNs[language][`${design}.t`] = content.t
|
|
|
|
designNs[language][`${design}.d`] = content.d
|
2023-07-19 19:08:41 +02:00
|
|
|
writeJson(store.site, language, design, content)
|
2023-07-02 18:50:20 +02:00
|
|
|
}
|
2023-01-11 19:50:57 +01:00
|
|
|
}
|
2023-07-19 19:08:41 +02:00
|
|
|
for (const language of languages) writeJson(store.site, language, 'designs', designNs[language])
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the store
|
|
|
|
*/
|
|
|
|
store.i18n = { namespaces, designNs }
|
2022-02-06 15:44:00 +01:00
|
|
|
}
|