4개의 주요 FE 프레임워크의 논리는 동일한데 어떻게 비교합니까?

60182 단어 vuereactsvelteangular
Tolgee.io에서는 모든 주요 프런트엔드 프레임워크를 통합해야 합니다.내가 말하고 싶은 것은 이것이 그들 사이의 현실 세계의 비교를 보여주는 좋은 기회이며, 이것은 우리가 더욱 선진적인 기능으로 깊이 들어가도록 허락할 것이다.

총체적 설계


Tolgee는 지역화된 플랫폼입니다.우리의 통합 라이브러리는 두 가지 목적이 있다.
  • 응용 프로그램에서 번역 표시 및 포맷 + 언어 변경 관리
  • 개발 모드에서'상하문에서'로컬화를 허용하는 것은 사용자가 응용 프로그램
  • 에서 번역을 직접 편집할 수 있음을 의미한다.
    따라서 개발 모델에서 우리 라이브러리는 서버와 통신해야 하지만 생산 모델에서 생성된 JSON 파일만 처리하고 번역을 포함한다.

    유니버설 통합 도서관 체계 구조


    통합 라이브러리는 주어진 프레임워크에 맞춤형 API를 제공하고 핵심 라이브러리의 번역/언어 변경에 반응하기만 하면 된다.

    일반 텍스트 번역


    매번 집적할 때마다 특수 구성 요소 형식으로 페이지의 일반 텍스트를 번역하는 방식을 제공해야 하기 때문에 사용자는 이 구성 요소만 사용하면 정확한 번역(선택한 언어 기반)을 표시할 수 있습니다.우리가 사용하는 것은 ICU 메시지 형식으로 변수를 전달할 수 있기 때문에 사용자도 명칭 파라미터를 전달할 수 있다. 이런 파라미터는 번역 자체에서 인용되고 번역 문자열은 유사할 수 있다You have {itemsCount} items in your cart.이상적인 상황에서 우리는 이렇게 하기를 바란다.
    <T keyName="cart_content_key" parameters={{itemsCount: 5}}/>
    

    My main working tool is React and so I use React as kind of reference implementation


    강제 번역


    변환은 DOM 요소 중의 하위 요소로 직접 진행할 수 있을 뿐만 아니라 title 또는 aria-label 등 매개 변수에서도 할 수 있다.이러한 상황에 대해 우리는 구성 요소를 사용할 수 없지만, 사용자가 호출할 수 있는 함수가 필요합니다. 이 함수들은 그에게 텍스트를 번역하도록 강요할 것입니다.예를 들면 다음과 같습니다.
    <div title={t('translation_key')}>...</div>
    

    데이터 출처


    우리는 tolgee/core의 실례를 제공할 수 있는 방법이 필요하다. 이상적인 상황에서 우리는 그것을 한 항목에만 한정하는 것을 원하지 않는다(사용자는 같은 프로젝트에서 여러 개의 다른 번역원을 사용하기를 원할 수도 있다).이상적인 방법은 전역적으로 실례를 제공하거나 응용 프로그램의 구성 요소 서브 트리에 제공하는 것이다. (React에서 상하문 API가 하는 것이다.)

    React의 실현


    React에서는 컨텍스트 API를 사용하여 모든 하위 레벨에 Tolgee 인스턴스를 제공합니다.
    export const TolgeeProviderContext = React.createContext(null);
    
    export const TolgeeProvider = ({config, children}) => {
      const [tolgee] = useState(new Tolgee(config));
    
      useEffect(() => {
        tolgee.run();
        return () => {
          tolgee.stop();
        };
      }, []);
    
      return (
        <TolgeeProviderContext.Provider value={{tolgee}}>
          {children}
        </TolgeeProviderContext.Provider>
      );
    };
    
    이제 T 어셈블리에서 Tolgee 컨텍스트를 사용하는 방법을 살펴보겠습니다.
    export const T = ({ keyName, parameters }) =>
    {
      const {tolgee} = useContext(TolgeeProviderContext);
    
      // get initial value
      const [translated, setTranslated] = useState(
        tolgee.instant(keyName, parameters)
      );
    
      useEffect(() => {
        // subscribe to translation changes
        const subscription =
          tolgee.onTranslationChange.subscribe((data) => {
            if (data.key === keyName) {
              setTranslate(tolgee.instant(keyName, parameters));
            }
          });
    
        return () => {
          subscription.unsubscribe();
        };
      }, [keyName, parameters]);
    
      return translated;
    };
    
    Tolgee 인스턴스에는 현재 번역된 모든 내부 캐시가 있습니다.따라서 우리가 tolgee.instant()을 호출할 때, 우리는 응용 프로그램의 매개 변수를 사용하여 키의 현재 번역을 얻을 것입니다. 예를 들어 언어 변경이나 사용자가 상하문 로컬 도구를 통해 수동으로 번역을 변경할 때, 우리는 번역 변경을 구독할 수 있습니다.

    React의 기도문 번역


    React에서 사용하기 쉬운 t 함수를 만들기 위해서는 구성 요소의 생명 주기에 어떤 방식으로 연결해야 합니다. 왜냐하면 우리는 키를 번역해야 할 뿐만 아니라 구성 요소를 구독해서 번역 변경을 해야 하기 때문입니다.가장 간단한 해결 방안 (힌트를 줄 수 있습니다) 은 훅을 만드는 것입니다. 훅은 이 t 함수를 되돌려줍니다.그리고 각 구성 요소에 대해 별도의 t 기능을 제공하여 구성 요소의 생명 주기에 연결할 수 있습니다.이게 어떻게 생겼는지 보여줘.
    export const useTranslate: () => ReturnFnType = () => {
      const {tolgee} = useTolgeeContext();
    
      // dummy state to force component to re-render
      const [_, setDummyValue] = useState(0);
      const reRender = () => {
        setDummyValue((v) => v + 1);
      };
    
      // array of used keys
      const usedKeysRef = useRef(new Set());
    
      useEffect(() => {
        const subscription = tolgee.onTranslationChange.subscribe((data) => {
          // check if we are using this key
          if (usedKeysRef.current.has(data.key)) {
            reRender()
          }
        });
    
        return () => subscription.unsubscribe();
      }, []);
    
      return (keyName, parameters) => {
        // remember that this key was used
        usedKeysRef.current.add(keyName)
    
        return tolgee.instant(keyName, parameters)
      };
    };
    

    React doesn't have a direct way how to force component to re-render, so we do it through dummy state update.


    그런 다음 다음과 같은 방법으로 이 후크를 사용할 수 있습니다.
    export const MyComponent = () => {
      const t = useTranslate()
    
      return <div title={t('title_key')}>...</div>
    }
    
    우리는 기본적으로 사용자에게 함수를 하나 준 후에 그가 그것을 이용하여 어떤 키를 하는지 관찰했다.변환이 변경될 때, 우리는 이 구성 요소가 이전에 사용되었는지 확인하고, 최종적으로 이 구성 요소를 다시 렌더링하도록 강요합니다.

    필기


    모든 틀의 실현은 한 문장에 적합하도록 매우 간단하다.핵심 라이브러리는 실제적으로 좀 복잡해야 한다. 게다가 우리는 상하문 로컬화 이벤트 처리 프로그램 (사용자가 번역을 클릭할 수 있도록 허용) 을 완전히 건너뛰었다. 왜냐하면 이것은 상당히 복잡한 주제이기 때문이다.

    Vue의 구현.js


    Vue.js는 또 하나의 개념이 있는데 바로 아이들에게 환경을 제공하는 것이다.우리는 provide 방법을 통해 모든 구성 요소에서 이 점을 실현할 수 있다.
    export const TolgeeProvider = {
      name: 'TolgeeProvider',
      props: {
        config: {type: Object, required: true},
      },
      created() {
        const tolgee = new Tolgee({...this.$props.config});
        this.tolgeeContext.tolgee = tolgee;
        tolgee.run()
      },
      data() {
        return {
          tolgeeContext: {
            tolgee: null,
            // more reactive properties here
          },
        };
      },
      provide() {
        return {
          tolgeeContext: this.tolgeeContext,
        };
      },
      beforeUnmount() {
        this.tolgeeContext.tolgee.stop();
      },
      render() {
        this.$slots.default()
      },
    };
    
    Vue 는 React 클래스 구성 요소와 유사한 방식으로 구성 요소의 라이프 사이클을 관리합니다.Vue와 다른 점은 반응 대상을 사용하는 것이다. data 속성에 넣을 때마다 반응 대상이 되고 (변화를 관찰하고 있음) Vue는 다시 정렬을 촉발한다.위의 예에서 우리는 하나의 전체tolgeeContext를 데이터 속성에 넣을 것이다. 왜냐하면 현실에서 우리는 더 많은 것을 전달해야 하고 수동적이어야 하기 때문이다.
    이제 T 부품이 어떻게 작동하는지 봅시다.
    export const T = {
      name: 'T',
      inject: ['tolgeeContext'],
      props: {
        keyName: {type: String, required: true},
        parameters: Object,
      },
      data() {
        const tolgeeContext = this.tolgeeContext;
        return {
          translation:
            tolgeeContext.tolgee.instant(
              this.$props.keyName,
              this.$props.parameters
            )
        };
      },
      methods: {
        translate(data) {
          if (data.key === this.$props.keyName) {
            this.$data.translation =
              tolgeeContext.tolgee.instant(
                this.$props.keyName,
                this.$props.parameters
              )
          }
        }
      },
      created() {
        const tolgeeContext = this.tolgeeContext;
        this.$options.subscription =
          tolgeeContext.tolgee.onTranslationChange.subscribe(this.translate);
      },
      beforeUnmount() {
        this.$options.subscription.unsubscribe();
      },
      render() {
        return this.$data.translation
      },
    };
    
    React와 유사하게 tolgeeContext부터 inject까지 속성을 사용하고 구독합니다
    생명주기 방법의 번역 변화.

    Vue의 명령 번역


    우리는 Vue에 갈고리와 유사한 것이 없기 때문에 Mixinapi를 사용해야 합니다.Mixin은 라이프 사이클 방법을 사용할 때 구성 요소 간에 논리를 공유하는 방법입니다.
    export const TolgeeMixin = {
      inject: ['tolgeeContext'],
      beforeCreate() {
        this.$options.usedKeys = new Set()
      },
      created() {
        const tolgeeContext = this.tolgeeContext;
        this.$options.subscription =
          tolgeeContext.tolgee.onTranslationChange.subscribe((data) => {
            if (this.$options.usedKeys.has(data.key)) {
              this.$forceUpdate();
            }
          });
      },
      methods: {
        $t(keyName, params) {
          this.$options.usedKeys.add(keyName)
          const tolgeeContext = this.tolgeeContext;
          return tolgeeContext.tolgee.instant(keyName, params);
        },
      },
      beforeUnmount() {
        this.$options.subscription.unsubscribe();
      },
    };
    
    그런 다음 다음과 같이 Mixin을 사용할 수 있습니다.
    <template>
      <div :title="$t('title_key')">...</div>
    </template>
    
    <script>
    export const Component = {
      mixins: [TolgeeMixin],
    };
    </script>
    
    따라서 여기에는 tolgeeContext를 주입하고 번역 변경 사항을 구독하고 $t 방법을 추가한 다음에 사용자가 이 방법을 사용할 수 있습니다.사용한 키 목록을 유지합니다. 변경 사항이 발생하면 구성 요소를 업데이트합니다.Vue에는 명시적인 방법$forceUpdate이 있으므로 어셈블리를 다시 렌더링할 수 있습니다.

    날씬함 속에서 실현


    Svelte and Angular sections are written by , as he is the author of the integrations


    Svelte에서 우리는 Vue와 React에서 매우 비슷한 제공 프로그램을 실현했다.
    <!-- TolgeeProvider.svelte -->
    <script>
      import { onDestroy, onMount, setContext } from "svelte";
    
      export let config: TolgeeConfig;
      const tolgee = new Tolgee(config);
    
      setContext("tolgeeContext", { tolgee });
    
      onMount(() => tolgee.run());
      onDestroy(() => tolgee.stop());
    </script>
    
    <slot />
    
    컨텍스트 사용T 어셈블리는 다음과 같습니다.
    <script>
      import { onDestroy, getContext } from "svelte";
    
      export let keyName;
      export let parameters;
      const tolgeeContext = getContext('tolgeeContext');
    
      let translated
      const translate = () => {
        translated = tolgeeContext.tolgee.instant(
          keyName,
          parameters,
        );
      }
    
      translate();
      const subscription =
        tolgeeContext.tolgee.onTranslationChange.subscribe((data) => {
          if (data.key === keyName) {
            translate()
          }
        });
    
      onDestroy(() => {
        subscription.unsubscribe();
      });
    </script>
    
    {translated}
    
    TolgeeProviderT 구성 요소는 React 구현과 매우 비슷하다.
    차이점은 다음과 같습니다.
  • Svelte는 라이프 사이클 방법이 있고, React는 연결이 있음
  • 컨텍스트 API가 다름
  • 명령식 번역


    React 통합과 가장 큰 차이점은 getTranslate 방법으로 Svelte에서 실제 키를 변환하는 방법을 포함하는 저장소를 되돌려줍니다.
    import {onDestroy, getContext} from 'svelte';
    import {derived, writable} from 'svelte/store';
    
    export const getTranslate = () => {
      const context = getContext('tolgeeContext');
      const tolgee = context.tolgee;
    
      // set of used keys
      const usedKeys = new Set();
    
      // dummy store which is updated to forces providing of new translate method
      const updateStore = writable(0);
      const update = () => updateStore.update((number) => number + 1);
    
      const translate = (keyName, params) => {
        usedKeys.add(keyName);
        return tolgee.instant(keyName, params);
      };
    
      const subscription =
        tolgee.onTranslationChange.subscribe((data) => {
          if (usedKeys.has(data.key)) {
            update();
          }
        });
    
      onDestroy(() => {
        subscription.unsubscribe();
      });
    
      // return new translate method when something is changed
      return derived(
        // when updateStore changes, translate function gets changed as well
        updateStore,
        () => (keyName, params) => {
          return translate(keyName, params);
        }
      );
    };
    
    getTranslate 함수의 사용 예:
    <script>
      const t = getTranslate();
    </script>
    
    <div title={$t('title_key')}>...</div>
    
    React에서 useTranslate 는 hook입니다. 변환이 변할 때 강제로 다시 렌더링을 하고, 새 렌더링이 호출된 리턴 t 함수가 새 값을 되돌려줍니다.
    비슷한 일도 Svelte에서 일어났지만 그곳t은 사실상 번역 기능을 포함하는 상점이었다.이것이 바로 함수가 호출되기 전에 있는 이유다$.이것은 저장소를 업데이트할 때마다 이 값을 다시 보여줘야 한다는 것을 의미한다.

    Angular에서의 실현


    가장 다른 방법은 각적분에 쓰인다.Angular는 공급자의 개념이 없습니다. 반대로, 우리는 모듈을 번역할 수 있도록 Tolgee 핵심 라이브러리를 설정하고 서비스, 파이프, 구성 요소를 제공할 수 있습니다.

    NGXTolgee 모듈


    @NgModule({
      declarations: [TranslatePipe, STranslatePipe, TComponent],
      exports: [TranslatePipe, STranslatePipe, TComponent],
      providers: [],
    })
    export class NgxTolgeeModule {
      // @dynamic
      static forRoot(options: TolgeeConfig): ModuleWithProviders<NgxTolgeeModule> {
        options = {filesUrlPrefix: '/assets/i18n/', ...options};
        return {
          ngModule: NgxTolgeeModule,
          providers: [
            TranslateService,
            TranslationsProvider,
            {
              provide: APP_INITIALIZER,
              useFactory: (provider: TranslationsProvider) => {
                return async () => await provider.load(options);
              },
              deps: [TranslationsProvider, TranslateService],
              multi: true,
            },
            {provide: TolgeeConfig, useValue: options},
          ],
        };
      }
    }
    
    이 방면에서 가장 멋있는 것은 공장이다. 공장은 우리가 응용 프로그램을 DOM에 보여주기 전에 번역이 불러오기를 기다릴 수 있게 한다.다른 프레임워크에서, 이것은 반환 도구나 슬롯을 불러오는 것을 통해 처리되며, 이러한 도구나 슬롯은 번역을 불러올 때 나타난다.

    번역 서비스와 관찰


    각도 세계에서 RxJs와 관측 가능한 인터페이스가 매우 유행한다.관찰할 수 있는 사물은 사건과 비슷하다
    송신기.그것은 우리가 완성하기 전에 새로운 가치관을 제공할 수 있고 필요할 때 구독을 취소할 수 있게 한다.이것은
    이것이 바로 우리의 목적에 필요한 것이다.
    import {EventEmitter, Injectable, OnDestroy} from '@angular/core';
    import {Observable} from 'rxjs';
    import {Tolgee, TranslationData} from '@tolgee/core';
    import {TolgeeConfig} from './tolgeeConfig';
    
    @Injectable()
    export class TranslateService implements OnDestroy {
      constructor(private config: TolgeeConfig) {
      }
    
      // Logic creating the Instance of Tolgee and lot of other stuff is ommited
      // ...
    
      private _tolgee: Tolgee;
    
      public get tolgee(): Tolgee {
        return this._tolgee;
      }
    
      public translate(
        key: string,
        params = {},
      ): Observable<string> {
        return new Observable((subscriber) => {
          const translate = () => {
            const translated = this.tolgee.instant(
              key,
              params,
            );
            subscriber.next(translated);
          };
    
          translate();
    
          const onTranslationChangeSubscription =
            this.tolgee.onTranslationChange.subscribe((data) => {
              if (data.key === key) {
                translate();
              }
            });
    
          return () => {
            onTranslationChangeSubscription.unsubscribe();
          };
        });
      }
    }
    
    이 서비스의 translate 방법은 새로운 관찰 가능한 값을 만들고, 번역이 바뀔 때마다 새로운 값을 보냅니다. 이 값은 @Tolgee/cor e라이브러리의 Tolgee 실례에서 보냅니다.구독을 취소할 수 있도록 함수를 되돌려줍니다.

    t 속성 선택기가 있는 구성 요소


    React에서 T 그룹으로 구성된 등가물로서, 우리는 t 선택기를 사용할 수 있습니다. 이것은 translate Observable를 구독하고 새 값에 따라 결과를 변경할 수 있습니다.
    import {Component, ElementRef, Input, OnDestroy, OnInit} from '@angular/core';
    import {Subscription} from 'rxjs';
    import {TranslateService} from './translate.service';
    import {TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE} from '@tolgee/core';
    
    @Component({
      selector: '[t]',
      template: ``,
    })
    export class TComponent implements OnInit, OnDestroy {
      @Input() params?: Record<string, any>;
      @Input() key: string;
      subscription: Subscription;
    
      constructor(
        private ref: ElementRef,
        private translateService: TranslateService
      ) {
      }
    
      ngOnInit(): void {
        const element = this.ref.nativeElement as HTMLElement;
        element.setAttribute(TOLGEE_WRAPPED_ONLY_DATA_ATTRIBUTE, this.key);
    
        this.subscription = this.translateService
          .translate(this.key, this.params, this.default)
          .subscribe((translated) => {
            return (element.textContent = translated);
          });
      }
    
      ngOnDestroy(): void {
        this.subscription.unsubscribe();
      }
    }
    

    번역관


    파이프는 다른 모든 통합에 부족한 기능이다.이것은 기본적으로 함수로서 템플릿에서 사용하는 값을 다른 값으로 변경할 수 있다.
    import {OnDestroy, Pipe, PipeTransform} from '@angular/core';
    import {TranslateService} from './translate.service';
    import {Subscription} from 'rxjs';
    
    @Pipe({
      name: 'translate',
      pure: false,
    })
    export class TranslatePipe implements PipeTransform, OnDestroy {
      value = '';
      key: string;
      params: Record<string, any>;
      private subscription: Subscription;
    
      constructor(protected translateService: TranslateService) {
      }
    
      ngOnDestroy(): void {
        this.unsubscribe();
      }
    
      transform(
        key: any,
        params?: Record<string, any>
      ): string {
        if (
          this.key === key &&
          JSON.stringify(this.params) === JSON.stringify(params)
        ) {
          // parameters unchanged
          return this.value;
        }
    
        this.key = key;
        this.params = params;
    
        this.unsubscribe();
        this.subscription = this.translate(key, params);
    
        return this.value;
      }
    
      private unsubscribe() {
        this.subscription.unsubscribe();
      }
    
      private translate(key, params) {
        this.translateService.translate(key, params).subscribe((r) => {
          this.value = r;
        });
      }
    }
    
    Angular는 기본적으로 사용자가 마우스를 이동하거나 어떤 키를 눌렀을 때마다 변환 방법을 실행합니다. 왜냐하면 이것은 불순한 파이프이기 때문에 같은 입력에 대해 다른 결과를 되돌릴 수 있습니다.언어를 변경할 때 같은 키와 파라미터, 다른 결과가 발생합니다.
    다른 프레임워크에 비해 Angular 라이브러리는 더 많은 코드가 필요할 것 같고 통합도 복잡할 것 같습니다.

    결론


    흥미로운 것은 이러한 프레임워크/라이브러리가 어떻게 서로 참고할 수 있는지이다.날씬함은 최신이고 누구나 자신의 생각을 가지고 있으며 코드는 매우 자연스럽고 깨끗해 보인다.Vue의 영감은 Angular와 React에서 유래한 것으로 둘 사이의 절충이다.그 다음은 React입니다. 훅스와 JSX를 통해 돋보입니다.Angular의 영감은 백엔드 개발자에게 익숙한'클래식'모델 - 보기 - 컨트롤러 모델에서 나온다.
    이러한 프레임워크/라이브러리에서 개선/간소화를 실현하는 방법을 알려주신다면 매우 기쁩니다.우리가 이 모든 틀에 대해 잘 알고 있다고 말하는 것은 아니다.저는 처음으로 Vue를 열심히 사용했습니다. Jan은 이 점을 실현하는 동시에 Svelte를 처음부터 배웠습니다.

    PS: Check Tolgee.io and give us github stars

    좋은 웹페이지 즐겨찾기