From 19c0223b79d6dbb50afbd19d1779fe35b077ceb2 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Mon, 1 Mar 2021 21:00:43 +0100 Subject: [PATCH 1/3] Generate typescript i18n types --- generate_translations.js | 57 +++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/generate_translations.js b/generate_translations.js index 0cab8e36..313e8003 100644 --- a/generate_translations.js +++ b/generate_translations.js @@ -1,27 +1,64 @@ -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 keys = Object.keys(JSON.parse(fileStr)); + + const data = `import * as i18n from "i18next"; + +type I18nKeys = +${keys.map(key => ` | "${key}"`).join("\n")}; + +declare module "i18next" { + export interface TFunction { + // basic usage + < + TResult extends TFunctionResult = string, + TInterpolationMap extends object = StringMap + >( + key: I18nKeys | I18nKeys[], + options?: TOptions | string + ): TResult; + // overloaded usage + < + TResult extends TFunctionResult = string, + TInterpolationMap extends object = StringMap + >( + key: I18nKeys | I18nKeys[], + defaultValue?: string, + options?: TOptions | string + ): TResult; + } +} +`; + + fs.writeFileSync(`${outDir}i18next.d.ts`, data); +}); From 66cc9416e4b8072414bb24c26f75ab859537db25 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Mon, 1 Mar 2021 21:33:57 +0100 Subject: [PATCH 2/3] Improve type safety --- generate_translations.js | 18 +++++++++++------- src/shared/i18next.ts | 6 ++++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/generate_translations.js b/generate_translations.js index 313e8003..594f574c 100644 --- a/generate_translations.js +++ b/generate_translations.js @@ -32,17 +32,17 @@ const baseLanguage = "en"; fs.readFile(`${translationDir}${baseLanguage}.json`, "utf8", (_, fileStr) => { const keys = Object.keys(JSON.parse(fileStr)); - const data = `import * as i18n from "i18next"; - -type I18nKeys = -${keys.map(key => ` | "${key}"`).join("\n")}; + const data = `import { i18n } from "i18next"; declare module "i18next" { - export interface TFunction { + export type I18nKeys = +${keys.map(key => ` | "${key}"`).join("\n")}; + + export interface TFunctionTyped { // basic usage < TResult extends TFunctionResult = string, - TInterpolationMap extends object = StringMap + TInterpolationMap extends Record = StringMap >( key: I18nKeys | I18nKeys[], options?: TOptions | string @@ -50,13 +50,17 @@ declare module "i18next" { // overloaded usage < TResult extends TFunctionResult = string, - TInterpolationMap extends object = StringMap + TInterpolationMap extends Record = StringMap >( key: I18nKeys | I18nKeys[], defaultValue?: string, options?: TOptions | string ): TResult; } + + export interface i18nTyped extends i18n { + t: TFunctionTyped; + } } `; diff --git a/src/shared/i18next.ts b/src/shared/i18next.ts index 99289032..3d3d5a1f 100644 --- a/src/shared/i18next.ts +++ b/src/shared/i18next.ts @@ -1,4 +1,4 @@ -import i18next from "i18next"; +import i18next, { i18nTyped } from "i18next"; import { getLanguage } from "./utils"; import { en } from "./translations/en"; import { el } from "./translations/el"; @@ -82,4 +82,6 @@ i18next.init({ interpolation: { format }, }); -export { i18next as i18n, resources }; +export const i18n = i18next as i18nTyped; + +export { resources }; From 1939664d736ce3ddbaa84c7106b510df4b950d86 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Mon, 1 Mar 2021 21:44:02 +0100 Subject: [PATCH 3/3] Add I18nKeys type to errCode --- src/shared/components/no-match.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shared/components/no-match.tsx b/src/shared/components/no-match.tsx index b11c0e54..175f4a9b 100644 --- a/src/shared/components/no-match.tsx +++ b/src/shared/components/no-match.tsx @@ -1,8 +1,11 @@ +import { I18nKeys } from "i18next"; import { Component } from "inferno"; import { i18n } from "../i18next"; export class NoMatch extends Component { - private errCode = new URLSearchParams(this.props.location.search).get("err"); + private errCode = new URLSearchParams(this.props.location.search).get( + "err" + ) as I18nKeys; constructor(props: any, context: any) { super(props, context);