2023-01-24 13:13:18 +00:00
|
|
|
import {lstatSync, readdirSync, readFileSync} from 'fs';
|
2022-09-29 08:01:37 +00:00
|
|
|
import {createInstance, i18n} from 'i18next';
|
|
|
|
import {LanguageDetector} from 'i18next-http-middleware';
|
|
|
|
import path from 'path';
|
|
|
|
|
|
|
|
export function setupLocale(appRoot: string): i18n {
|
|
|
|
// We are using custom instance and leave the global object intact.
|
|
|
|
const instance = createInstance();
|
|
|
|
// By default locales are located in the appRoot folder, unless the environment variable
|
|
|
|
// GRIST_LOCALES_DIR is set.
|
|
|
|
const localeDir = process.env.GRIST_LOCALES_DIR || path.join(appRoot, 'static', 'locales');
|
2023-01-24 13:13:18 +00:00
|
|
|
const preload: [string, string, string][] = [];
|
2022-09-29 08:01:37 +00:00
|
|
|
const supportedNamespaces: Set<string> = new Set();
|
2023-01-24 13:13:18 +00:00
|
|
|
const supportedLngs: Set<string> = new Set();
|
|
|
|
|
|
|
|
for(const fileName of readdirSync(localeDir)) {
|
2022-09-29 08:01:37 +00:00
|
|
|
const fullPath = path.join(localeDir, fileName);
|
|
|
|
const isDirectory = lstatSync(fullPath).isDirectory();
|
|
|
|
if (isDirectory) {
|
2023-01-24 13:13:18 +00:00
|
|
|
continue;
|
2022-09-29 08:01:37 +00:00
|
|
|
}
|
|
|
|
const baseName = path.basename(fileName, '.json');
|
2023-01-24 13:13:18 +00:00
|
|
|
const lang = baseName.split('.')[0]?.replace(/_/g, '-');
|
2022-09-29 08:01:37 +00:00
|
|
|
const namespace = baseName.split('.')[1];
|
|
|
|
if (!lang || !namespace) {
|
|
|
|
throw new Error("Unrecognized resource file " + fileName);
|
|
|
|
}
|
|
|
|
supportedNamespaces.add(namespace);
|
2023-01-24 13:13:18 +00:00
|
|
|
preload.push([lang, namespace, fullPath]);
|
|
|
|
supportedLngs.add(lang);
|
|
|
|
}
|
|
|
|
|
2022-10-13 09:23:55 +00:00
|
|
|
if (!supportedLngs.has('en') || !supportedNamespaces.has('server')) {
|
|
|
|
throw new Error("Missing server English language file");
|
2022-09-29 08:01:37 +00:00
|
|
|
}
|
|
|
|
// Initialize localization language detector plugin that will read the language from the request.
|
|
|
|
instance.use(LanguageDetector);
|
|
|
|
|
|
|
|
let errorDuringLoad: Error | undefined;
|
|
|
|
instance.init({
|
2022-10-13 09:23:55 +00:00
|
|
|
defaultNS: 'server',
|
2022-09-29 08:01:37 +00:00
|
|
|
ns: [...supportedNamespaces],
|
|
|
|
fallbackLng: 'en',
|
2023-01-24 13:13:18 +00:00
|
|
|
detection: {
|
|
|
|
lookupCookie: 'grist_user_locale'
|
|
|
|
}
|
2022-09-29 08:01:37 +00:00
|
|
|
}, (err: any) => {
|
|
|
|
if (err) {
|
|
|
|
errorDuringLoad = err;
|
|
|
|
}
|
|
|
|
}).catch((err: any) => {
|
|
|
|
// This should not happen, the promise should be resolved synchronously, without
|
|
|
|
// any errors reported.
|
|
|
|
console.error("i18next failed unexpectedly", err);
|
|
|
|
});
|
|
|
|
if (errorDuringLoad) {
|
2023-01-24 13:13:18 +00:00
|
|
|
console.error('i18next failed to load', errorDuringLoad);
|
2022-09-29 08:01:37 +00:00
|
|
|
throw errorDuringLoad;
|
|
|
|
}
|
2023-01-24 13:13:18 +00:00
|
|
|
// Load all files synchronously.
|
|
|
|
for(const [lng, ns, fullPath] of preload) {
|
|
|
|
instance.addResourceBundle(lng, ns, JSON.parse(readFileSync(fullPath, 'utf8')));
|
|
|
|
}
|
2022-09-29 08:01:37 +00:00
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readLoadedLngs(instance?: i18n): readonly string[] {
|
|
|
|
if (!instance) { return []; }
|
2023-01-24 13:13:18 +00:00
|
|
|
return Object.keys(instance?.services.resourceStore.data);
|
2022-09-29 08:01:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function readLoadedNamespaces(instance?: i18n): readonly string[] {
|
|
|
|
if (!instance) { return []; }
|
|
|
|
if (Array.isArray(instance?.options.ns)) {
|
|
|
|
return instance.options.ns;
|
|
|
|
}
|
2022-10-13 09:23:55 +00:00
|
|
|
return instance?.options.ns ? [instance.options.ns as string] : ['server'];
|
2022-09-29 08:01:37 +00:00
|
|
|
}
|