X-Git-Url: http://these/git/?a=blobdiff_plain;f=generate_translations.js;h=ac3e987521bdc7a5da613184f23be5275eccb444;hb=ba9389c15edfd501affcddfeec2209c74ce9fe96;hp=0cab8e3603a5a9fd113363a9255cd3c25c406e68;hpb=b448cb8f0e95d3a977cc7e50c75c53b7c7291bb8;p=lemmy-ui.git diff --git a/generate_translations.js b/generate_translations.js index 0cab8e3..ac3e987 100644 --- a/generate_translations.js +++ b/generate_translations.js @@ -1,27 +1,108 @@ -fs = require('fs'); +const fs = require("fs"); -let translationDir = 'lemmy-translations/translations/'; -let outDir = 'src/shared/translations/'; +const translationDir = "lemmy-translations/translations/"; +const outDir = "src/shared/translations/"; fs.mkdirSync(outDir, { recursive: true }); fs.readdir(translationDir, (_err, files) => { files.forEach(filename => { - const lang = filename.split('.')[0]; + const lang = filename.split(".")[0]; try { const json = JSON.parse( - fs.readFileSync(translationDir + filename, 'utf8') + fs.readFileSync(translationDir + filename, "utf8") ); - var data = `export const ${lang} = {\n translation: {`; - for (var key in json) { + let data = `export const ${lang} = {\n translation: {`; + for (const key in json) { if (key in json) { const value = json[key].replace(/"/g, '\\"'); - data = `${data}\n ${key}: "${value}",`; + data += `\n ${key}: "${value}",`; } } - data += '\n },\n};'; - const target = outDir + lang + '.ts'; + data += "\n },\n};"; + const target = outDir + lang + ".ts"; fs.writeFileSync(target, data); } catch (err) { console.error(err); } }); }); + +// generate types for i18n keys +const baseLanguage = "en"; + +fs.readFile(`${translationDir}${baseLanguage}.json`, "utf8", (_, fileStr) => { + const noOptionKeys = []; + const optionKeys = []; + const optionRegex = /\{\{(.+?)\}\}/g; + const optionMap = new Map(); + + for (const [key, val] of Object.entries(JSON.parse(fileStr))) { + const options = []; + for ( + let match = optionRegex.exec(val); + match; + match = optionRegex.exec(val) + ) { + options.push(match[1]); + } + + if (options.length > 0) { + optionMap.set(key, options); + optionKeys.push(key); + } else { + noOptionKeys.push(key); + } + } + + const indent = " "; + + const data = `import { i18n } from "i18next"; + +declare module "i18next" { + export type NoOptionI18nKeys = +${noOptionKeys.map(key => `${indent}| "${key}"`).join("\n")}; + + export type OptionI18nKeys = +${optionKeys.map(key => `${indent}| "${key}"`).join("\n")}; + + export type I18nKeys = NoOptionI18nKeys | OptionI18nKeys; + + export type TTypedOptions =${Array.from( + optionMap.entries() + ).reduce( + (acc, [key, options]) => + `${acc} TKey extends \"${key}\" ? ${ + options.reduce((acc, cur) => acc + `${cur}: string | number; `, "{ ") + + "}" + } :\n${indent}`, + "" + )} (Record | string); + + export interface TFunctionTyped { + // Translation requires options + < + TKey extends OptionI18nKeys | OptionI18nKeys[], + TResult extends TFunctionResult = string, + TInterpolationMap extends TTypedOptions = StringMap + > ( + key: TKey, + options: TOptions | string + ): TResult; + + // Translation does not require options + < + TResult extends TFunctionResult = string, + TInterpolationMap extends Record = StringMap + > ( + key: NoOptionI18nKeys | NoOptionI18nKeys[], + options?: TOptions | string + ): TResult; + } + + export interface i18nTyped extends i18n { + t: TFunctionTyped; + } +} +`; + + fs.writeFileSync(`${outDir}i18next.d.ts`, data); +});