]> Untitled Git - lemmy-ui.git/blob - generate_translations.js
fix submodule error
[lemmy-ui.git] / generate_translations.js
1 const fs = require("fs");
2
3 const translationDir = "lemmy-translations/translations/";
4 const outDir = "src/shared/translations/";
5 fs.mkdirSync(outDir, { recursive: true });
6 fs.readdir(translationDir, (_err, files) => {
7   files.forEach(filename => {
8     const lang = filename.split(".")[0];
9     try {
10       const json = JSON.parse(
11         fs.readFileSync(translationDir + filename, "utf8"),
12       );
13       let data = `export const ${lang} = {\n  translation: {`;
14       for (const key in json) {
15         if (key in json) {
16           const value = json[key].replace(/"/g, '\\"').replace("\n", "\\n");
17           data += `\n    ${key}: "${value}",`;
18         }
19       }
20       data += "\n  },\n};";
21       const target = outDir + lang + ".ts";
22       fs.writeFileSync(target, data);
23     } catch (err) {
24       console.error(err);
25     }
26   });
27 });
28
29 // generate types for i18n keys
30 const baseLanguage = "en";
31
32 fs.readFile(`${translationDir}${baseLanguage}.json`, "utf8", (_, fileStr) => {
33   const noOptionKeys = [];
34   const optionKeys = [];
35   const optionRegex = /\{\{(.+?)\}\}/g;
36   const optionMap = new Map();
37
38   for (const [key, val] of Object.entries(JSON.parse(fileStr))) {
39     const options = [];
40     for (
41       let match = optionRegex.exec(val);
42       match;
43       match = optionRegex.exec(val)
44     ) {
45       options.push(match[1]);
46     }
47
48     if (options.length > 0) {
49       optionMap.set(key, options);
50       optionKeys.push(key);
51     } else {
52       noOptionKeys.push(key);
53     }
54   }
55
56   const indent = "    ";
57
58   const data = `import { i18n } from "i18next";
59
60 declare module "i18next" {
61   export type NoOptionI18nKeys = 
62 ${noOptionKeys.map(key => `${indent}| "${key}"`).join("\n")};
63
64   export type OptionI18nKeys = 
65 ${optionKeys.map(key => `${indent}| "${key}"`).join("\n")};
66
67   export type I18nKeys = NoOptionI18nKeys | OptionI18nKeys;
68
69   export type TTypedOptions<TKey extends OptionI18nKeys> =${Array.from(
70     optionMap.entries(),
71   ).reduce(
72     (acc, [key, options]) =>
73       `${acc} TKey extends \"${key}\" ? ${
74         options.reduce((acc, cur) => acc + `${cur}: string | number; `, "{ ") +
75         "}"
76       } :\n${indent}`,
77     "",
78   )} (Record<string, unknown> | string);
79
80   export interface TFunctionTyped {
81     // Translation requires options
82     <
83       TKey extends OptionI18nKeys | OptionI18nKeys[],
84       TResult extends TFunctionResult = string,
85       TInterpolationMap extends TTypedOptions<TKey> = StringMap
86     > (
87       key: TKey,
88       options: TOptions<TInterpolationMap> | string
89     ): TResult;
90
91     // Translation does not require options
92     <
93       TResult extends TFunctionResult = string,
94       TInterpolationMap extends Record<string, unknown> = StringMap
95     > (
96       key: NoOptionI18nKeys | NoOptionI18nKeys[],
97       options?: TOptions<TInterpolationMap> | string
98     ): TResult;
99   }
100
101   export interface i18nTyped extends i18n {
102     t: TFunctionTyped;
103   }
104 }
105 `;
106
107   fs.writeFileSync(`${outDir}i18next.d.ts`, data);
108 });