简体   繁体   English

TypeScript:有没有办法参数化枚举

[英]TypeScript: is there a way to parameterize an enum

I have this enum defined in our codebase.我在我们的代码库中定义了这个enum

enum EventDesc {
  EVENT1 = 'event 1',
  EVENT2 = 'event 2',
  EVENT3 = 'event 3'
}

EVENT1 , EVENT2 , EVENT3 are the event types defined on the backend. EVENT1EVENT2EVENT3是后端定义的事件类型。 And event 1 , event 2 , event 3 are the event descriptions that get rendered on the UI, for some reason they are not defined on the backend. event 1event 2event 3是在 UI 上呈现的事件描述,出于某种原因,它们没有在后端定义。 I used an enum to make the mapping here and to get the corresponding event description.我在这里使用enum进行映射并获取相应的事件描述。

However now we are supporting internationalization using react-i18next , meaning that the descriptions have to be translated for a number of languages.但是现在我们使用react-i18next支持国际化,这意味着必须将描述翻译成多种语言。 In other words they cannot be hardcoded like this only using English.换句话说,它们不能像这样仅使用英语进行硬编码。

Our current approach for injecting translation texts is that we have a json file defined for every language we care about and we use them with react-i18next like this我们目前注入翻译文本的方法是,我们为我们关心的每种语言定义了一个 json 文件,我们像这样将它们与react-i18next一起使用

import {useTranslation} from 'react-i18next';

export const Page = () => {
  const {i: i18n } = useTranslation()
  
  return (
    <div>{i18n("siteTitle")}</div>
  )

}

So before I am getting the event description by just look up the key in the enum , eg EventDesc[eventType] .因此,在我通过查找enum的键来获取事件描述之前,例如EventDesc[eventType] Now obviously it won't work with react-i18next .现在显然它不适用于react-i18next I wonder is there a way to parameterize the enum's value using Generics so we can do something like this EventDesc<'en'>[eventType] or EventDesc<'jp'>[eventType] .我想知道有没有办法使用泛型参数化枚举的值,这样我们就可以做这样的事情EventDesc<'en'>[eventType]EventDesc<'jp'>[eventType] But then I realized even with this it might not work because with enum we are always defining the event description at compiler time and react-i18next would only pick up the translation text from the json files during runtime.但后来我意识到即使这样它也可能不起作用,因为使用enum我们总是在编译器时定义事件描述,而react-i18next只会在运行时从json文件中获取翻译文本。

Is there a better alternative than using an enum to store the event description data?有没有比使用enum来存储事件描述数据更好的选择?

Using this approach, you can force translations to be maintained - missing translations in the matrix show as compiler errors against an instance of GlobalDictionary.使用这种方法,您可以强制维护翻译 - 矩阵中缺少的翻译显示为针对 GlobalDictionary 实例的编译器错误。

The global dictionary could be maintained in a single const, but I've shown how you could separate translations - eg to keep them in separate files.全局字典可以保存在一个常量中,但我已经展示了如何分离翻译——例如将它们保存在单独的文件中。

In VSCode save this file as .ts and try adding an event or a language and following through the compiler errors until the translations are complete again.在 VSCode 中将此文件保存为 .ts 并尝试添加事件或语言并跟踪编译器错误,直到再次完成翻译。

namespace Internationalization {
  type Event = 'event 1' | 'event 2' | 'event 3';

  type Language = 'en' | 'jp' | 'fr'

  type Translation<L extends Language> = {
    [K in Event]: string
  }

  type GlobalDictionary = {
    [L in Language]: Translation<L>
  }

  const frenchDictionary: Translation<'fr'> = {
    "event 1": 'hoh',
    "event 2": 'hi',
    "event 3": 'hoh',
  }

  const babel: GlobalDictionary = {
    en: {
      "event 1": 'yo',
      "event 2": 'hi',
      "event 3": 'ug',
    },
    jp: {
      "event 1": 'um',
      "event 2": 'er',
      "event 3": 'ah',
    },
    fr: frenchDictionary
  }

}

In my opinion, trying to parameterize an enum is not the way to work in Angular or React.在我看来,尝试参数化枚举不是在 Angular 或 React 中工作的方式。
Most of the time we want to show the enums as a select DOM element in html, but the words are expected to be translated.大多数情况下,我们希望将枚举显示为 html 中的一个选择 DOM 元素,但预计这些词会被翻译。
This is one approach to display a gender enum as a select element only in english, no translation at all.这是一种仅用英语将性别枚举显示为选择元素的方法,根本没有翻译。
What user sees用户看到什么

import logo from './logo.svg';
import './App.css';
import React from 'react';


function getLengthEnum(ENUMERATION:any):number{
  let l_items : any[] = Object.values(ENUMERATION).filter((item) => !isNaN(Number(item)));
  return l_items.length;
}
enum Gender{
  'None',
  'Male',
  'Female'
}
export interface MyAplicacionState {  
  i_genderSelected : number
}
class MyAplicacion extends React.Component<{},MyAplicacionState> {
  private li_indexesGenders : number[];
  constructor() {
    super({});      
    let i_numGenders : number = getLengthEnum(Gender);    
    this.li_indexesGenders = Array.from(Array(i_numGenders).keys());//[...Array(i_numGenders).keys()];    
    this.handleChangeGender = this.handleChangeGender.bind(this);
    this.state = {
      i_genderSelected : 0
    };
  }

  handleChangeGender(event:any) : void {
    this.setState({i_genderSelected: event.target.value});
  }

  render(){    
    const listOptionsGender = this.li_indexesGenders.map((i_index:number) =>
          <option value={i_index}>{Gender[i_index]}</option>
      );
    return (
      <div className='App'>
        <header className='App-header'>
          <img src={logo} className='App-logo' alt='logo' />
          <select value={this.state.i_genderSelected} onChange={this.handleChangeGender}>
            {listOptionsGender}
          </select>        
        </header>
      </div>
    );
  }  
}

export default MyAplicacion;

This is one approach to display a gender enum as a select element using react-i18next.这是使用 react-i18next 将性别枚举显示为选择元素的一种方法。
What user sees用户看到什么

import logo from './logo.svg';
import './App.css';
import React from 'react';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

const resTrans = {
  de: {
    translation: {
      'None': 'Keine',
      'Male': 'Männlich',
      'Female': 'Weiblich'
    }
  },
  fr: {
    translation: {
      'None': 'Aucun',
      'Male': 'Homme',
      'Female': 'Femme'
    }
  }
};

i18n.use(initReactI18next).init({
    resources: resTrans,
    lng: 'de',
    fallbackLng: 'de',
    interpolation: {
      escapeValue: false
    }
});

function getLengthEnum(ENUMERATION:any):number{
  let l_items : any[] = Object.values(ENUMERATION).filter((item) => !isNaN(Number(item)));
  return l_items.length;
}
enum Gender{
  'None',
  'Male',
  'Female'
}
export interface MyAplicacionState {  
  i_genderSelected : number,
  i_langSelected : number,
  ls_gendersTranslated : string[]
}
class MyAplicacion extends React.Component<{},MyAplicacionState> {
  private li_indexesGenders : number[];
  constructor() {
    super({});      
    let i_numGenders : number = getLengthEnum(Gender);    
    this.li_indexesGenders = Array.from(Array(i_numGenders).keys());//[...Array(i_numGenders).keys()];    
    this.handleChangeGender = this.handleChangeGender.bind(this);
    this.handleChangeLang = this.handleChangeLang.bind(this);
    this.getListGendersTranslated = this.getListGendersTranslated.bind(this);
    this.state = {
      i_genderSelected : 0,
      i_langSelected : 0,
      ls_gendersTranslated : this.getListGendersTranslated()
    };
  }

  handleChangeGender(event:any) : void {
    this.setState({i_genderSelected: event.target.value});
  }

  handleChangeLang(event:any) : void {
    let i_indexLang : number = parseInt(event.target.value);    
    let s_lang : string = 'de';
    if(i_indexLang===1)s_lang = 'fr';
    i18n.changeLanguage(s_lang);    
    this.setState({i_langSelected: i_indexLang, ls_gendersTranslated : this.getListGendersTranslated()});
  }
  //from enum list to string list
  getListGendersTranslated() : string[]{
    let ls_gendersTranslated1 : string[] = [];
    this.li_indexesGenders.forEach((i_index : number)=>{
      ls_gendersTranslated1.push(i18n.t(Gender[i_index]));
    }); 
    return ls_gendersTranslated1;
  }

  render(){    
    const listOptionsGender = this.li_indexesGenders.map((i_index:number) =>
          <option value={i_index}>{this.state.ls_gendersTranslated[i_index]}</option>
      );
    return (
      <div className='App'>
        <header className='App-header'>
          <img src={logo} className='App-logo' alt='logo' />
          <select value={this.state.i_genderSelected} onChange={this.handleChangeGender}>
            {listOptionsGender}
          </select>
          <select value={this.state.i_langSelected} onChange={this.handleChangeLang}>
            <option value={0}>Deutsche</option>
            <option value={1}>Française</option>
          </select>          
        </header>
      </div>
    );
  }  
}

export default MyAplicacion;

ls_gendersTranslated is the list of genders(strings) in the language selected. ls_gendersTranslated是所选语言的性别(字符串)列表。
ls_gendersTranslated is inside the state so it changes whenever the language selected changes. ls_gendersTranslated位于 state 内部,因此只要选择的语言发生变化,它就会发生变化。
using li_indexesGenders and getListGendersTranslated you can show enum as a select.使用li_indexesGendersgetListGendersTranslated您可以将枚举显示为选择。
resTrans , getLengthEnum and Gender enum should be in other ts and json files. resTransgetLengthEnumGender枚举应该在其他 ts 和 json 文件中。
Excuse me about this weird notation.请原谅我这个奇怪的符号。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM