mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-23 00:46:16 -04:00
Added typescript to etherpad
* Fixed determining file extension. * Added ts-node * Fixed backend tests. * Fixed frontend test runs. * Fixed tests. * Use script approach for starting etherpad. * Change directory to src. * Fixed env. * Change directory * Fixed build arg. * Fixed docker build. * Fixed. * Fixed cypress file path. * Fixed. * Use latest node container. * Fixed windows workflow. * Use tsx and optimized docker image. * Added workflow for type checks. * Fixed. * Added tsconfig. * Converted more files to typescript. * Removed commented keys. * Typed caching middleware. * Added script for checking the types. * Moved SecretRotator to typescript. * Fixed npm installation and moved to types folder. * Use better scripts for watching typescript changes. * Update windows.yml * Fixed order of npm installation. * Converted i18n. * Added more types. * Added more types. * Fixed import. * Fixed tests. * Fixed tests. * Fixed type checking test. * Fixed stats * Added express types. * fixed.
This commit is contained in:
parent
c3202284bc
commit
ead3c0ea38
74 changed files with 1259 additions and 612 deletions
149
src/node/hooks/i18n.ts
Normal file
149
src/node/hooks/i18n.ts
Normal file
|
@ -0,0 +1,149 @@
|
|||
'use strict';
|
||||
|
||||
import type {MapArrayType} from "../types/MapType";
|
||||
import {I18nPluginDefs} from "../types/I18nPluginDefs";
|
||||
|
||||
const languages = require('languages4translatewiki');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const _ = require('underscore');
|
||||
const pluginDefs = require('../../static/js/pluginfw/plugin_defs.js');
|
||||
const existsSync = require('../utils/path_exists');
|
||||
const settings = require('../utils/Settings');
|
||||
|
||||
// returns all existing messages merged together and grouped by langcode
|
||||
// {es: {"foo": "string"}, en:...}
|
||||
const getAllLocales = () => {
|
||||
const locales2paths:MapArrayType<string[]> = {};
|
||||
|
||||
// Puts the paths of all locale files contained in a given directory
|
||||
// into `locales2paths` (files from various dirs are grouped by lang code)
|
||||
// (only json files with valid language code as name)
|
||||
const extractLangs = (dir: string) => {
|
||||
if (!existsSync(dir)) return;
|
||||
let stat = fs.lstatSync(dir);
|
||||
if (!stat.isDirectory() || stat.isSymbolicLink()) return;
|
||||
|
||||
fs.readdirSync(dir).forEach((file:string) => {
|
||||
file = path.resolve(dir, file);
|
||||
stat = fs.lstatSync(file);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) return;
|
||||
|
||||
const ext = path.extname(file);
|
||||
const locale = path.basename(file, ext).toLowerCase();
|
||||
|
||||
if ((ext === '.json') && languages.isValid(locale)) {
|
||||
if (!locales2paths[locale]) locales2paths[locale] = [];
|
||||
locales2paths[locale].push(file);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// add core supported languages first
|
||||
extractLangs(path.join(settings.root, 'src/locales'));
|
||||
|
||||
// add plugins languages (if any)
|
||||
for (const {package: {path: pluginPath}} of Object.values<I18nPluginDefs>(pluginDefs.plugins)) {
|
||||
// plugin locales should overwrite etherpad's core locales
|
||||
if (pluginPath.endsWith('/ep_etherpad-lite')) continue;
|
||||
extractLangs(path.join(pluginPath, 'locales'));
|
||||
}
|
||||
|
||||
// Build a locale index (merge all locale data other than user-supplied overrides)
|
||||
const locales:MapArrayType<any> = {};
|
||||
_.each(locales2paths, (files: string[], langcode: string) => {
|
||||
locales[langcode] = {};
|
||||
|
||||
files.forEach((file) => {
|
||||
let fileContents;
|
||||
try {
|
||||
fileContents = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
} catch (err) {
|
||||
console.error(`failed to read JSON file ${file}: ${err}`);
|
||||
throw err;
|
||||
}
|
||||
_.extend(locales[langcode], fileContents);
|
||||
});
|
||||
});
|
||||
|
||||
// Add custom strings from settings.json
|
||||
// Since this is user-supplied, we'll do some extra sanity checks
|
||||
const wrongFormatErr = Error(
|
||||
'customLocaleStrings in wrong format. See documentation ' +
|
||||
'for Customization for Administrators, under Localization.');
|
||||
if (settings.customLocaleStrings) {
|
||||
if (typeof settings.customLocaleStrings !== 'object') throw wrongFormatErr;
|
||||
_.each(settings.customLocaleStrings, (overrides:MapArrayType<string> , langcode:string) => {
|
||||
if (typeof overrides !== 'object') throw wrongFormatErr;
|
||||
_.each(overrides, (localeString:string|object, key:string) => {
|
||||
if (typeof localeString !== 'string') throw wrongFormatErr;
|
||||
const locale = locales[langcode];
|
||||
|
||||
// Handles the error if an unknown language code is entered
|
||||
if (locale === undefined) {
|
||||
const possibleMatches = [];
|
||||
let strippedLangcode = '';
|
||||
if (langcode.includes('-')) {
|
||||
strippedLangcode = langcode.split('-')[0];
|
||||
}
|
||||
for (const localeInEtherPad of Object.keys(locales)) {
|
||||
if (localeInEtherPad.includes(strippedLangcode)) {
|
||||
possibleMatches.push(localeInEtherPad);
|
||||
}
|
||||
}
|
||||
throw new Error(`Language code ${langcode} is unknown. ` +
|
||||
`Maybe you meant: ${possibleMatches}`);
|
||||
}
|
||||
|
||||
locales[langcode][key] = localeString;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return locales;
|
||||
};
|
||||
|
||||
// returns a hash of all available languages availables with nativeName and direction
|
||||
// e.g. { es: {nativeName: "español", direction: "ltr"}, ... }
|
||||
const getAvailableLangs = (locales:MapArrayType<any>) => {
|
||||
const result:MapArrayType<string> = {};
|
||||
for (const langcode of Object.keys(locales)) {
|
||||
result[langcode] = languages.getLanguageInfo(langcode);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// returns locale index that will be served in /locales.json
|
||||
const generateLocaleIndex = (locales:MapArrayType<string>) => {
|
||||
const result = _.clone(locales); // keep English strings
|
||||
for (const langcode of Object.keys(locales)) {
|
||||
if (langcode !== 'en') result[langcode] = `locales/${langcode}.json`;
|
||||
}
|
||||
return JSON.stringify(result);
|
||||
};
|
||||
|
||||
|
||||
exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||
// regenerate locales on server restart
|
||||
const locales = getAllLocales();
|
||||
const localeIndex = generateLocaleIndex(locales);
|
||||
exports.availableLangs = getAvailableLangs(locales);
|
||||
|
||||
app.get('/locales/:locale', (req:any, res:any) => {
|
||||
// works with /locale/en and /locale/en.json requests
|
||||
const locale = req.params.locale.split('.')[0];
|
||||
if (Object.prototype.hasOwnProperty.call(exports.availableLangs, locale)) {
|
||||
res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`);
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.send(`{"${locale}":${JSON.stringify(locales[locale])}}`);
|
||||
} else {
|
||||
res.status(404).send('Language not available');
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/locales.json', (req: any, res:any) => {
|
||||
res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`);
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.send(localeIndex);
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue