反応における多言語ルーティング



反応における多言語ルーティング
反応のルーティングの周りのすばらしいものの1つは、その生態系が大きくて非常に宣言的なルーティング構文を許容したということです.ルーティングのロジックをレンダリングするオブジェクト構造を記述するには、コンポーネントのセットとしてルーティングを定義できます.
そして、それは常に改善し、良くなってきた.
  • react-router v5.1 is out

  • reach ルータも大きな牽引力を得て、それをチェックします.
  • しかし、あなたが複数の言語でルート名をサポートして、あなたが言語を変えるとき、あなたのユーザーにルートを正すためにリダイレクトする本当の多言語ルーティングを支持したいならば、どうですか?

    それは本当に大きなですか?
    さて、それは決定的にこのような機能なしで行くことが可能であり、完全に使用可能なウェブサイトを持っている.英語だけのルーティングを持つ多くのウェブサイトがあります、しかし、マルチ言語内容.
    開発の観点から、この理由は以下の通りです.
  • それをサポートしていないフレームワークが使用されます.
  • それは実装する大きな努力です.
  • それは常に維持するのは簡単ではない.
  • しかし、複数言語のルートローカライズを使用すると、エンドユーザーに次の利点を与えることができます
  • マルチ言語
  • ユーザーは自分の言語でページ階層の追加コンテキストを取得する
  • 反応で書かれた解決策は、実装と維持するのが比較的簡単です、そして、この記事は解決にあなたを案内するパッケージと方法を概説します.
    この例は、TypeScriptで書かれ、最新のRouter DOMを反応させ、フックを反応させます.

    ルータパッケージを追加する
    ルータパッケージを使用していない場合はreact-router-dom アウト.
    yarn add react-router-dom
    yarn add @types/react-router-dom --dev
    
    ルータを追加した後、我々のウェブサイトで使用されるいくつかのルートとコンポーネントを定義する必要があります.
    export const App: React.FC = () => (
      <BrowserRouter>
        <AppLayout>
          <Switch>
            <Route exact path={AppRoute.Home}>
              <views.Home />
            </Route>
            <Route exact path={AppRoute.Summary}>
              <views.Summary />
            </Route>
            <Route path="*">
              <views.GeneralError />
            </Route>
          </Switch>
        </AppLayout>
      </BrowserRouter>
    );
    
    最新の反応ルータDOMバージョンでは、コンポーネントとレンダリング小道具は、はるかに柔軟な子供の小道具のために廃棄されました.唯一の欠点は、V 4のバージョンは、より簡潔で、ほとんどのシナリオで読みやすいということです.コンポーネント/レンダリング小道具を通して物事を行う古い方法がまだ利用可能であることに注意してください.
    また、グローバルヘッダー、ナビゲーション、フッターを持つことができるapplayoutコンポーネントを追加し、メインビュー内のルートを部分ビューとしてレンダリングします.
    エラーコンポーネントをレンダリングするフォールバックルートもありますので、ユーザーが存在しないルートにアクセスしようとした場合、エラーページに終了したことを知っています.

    I 18 Nパッケージを追加する
    まず、我々は我々のアプリで物事を国際化できるようにパッケージを追加する必要があります.良い例がたくさんありますが、周りのベストパッケージの一つはreact-intl .
    プロジェクトですFormatJS (Yahoo ! Inc)は、通貨、日付などを含むほとんどすべてをローカライズするための印象的なサポートをしています.
        yarn add react-intl
    
    それは独自のタイプが含まれているので、このパッケージはTypesScriptで書かれました.

    ベースロケールの追加
    ベースラインとしてウェブサイトの主な言語となる言語で始めるのは、常に最も簡単です.あなたは常に簡単に後でより多くの言語を追加することができます.
    まず最初に我々のアプリの中で使用されるすべての言語のコレクションになるenumを追加しましょう.開始のために、我々はベース言語を加えるだけです.
    export enum AppLanguage {
      English = 'en',
    }
    
    各Enumプロパティの値は2文字の国コードISOロケールと一致する必要があります.
    言語を追加した後、我々はまた、その言語のいくつかの言語文字列を追加する必要があります.
    アプリケーション内の任意の場所にintlフォルダを作成し、ベースの言語のファイルを作成します.
    const baseStrings = {
      /** Routes */
      'routes.home': '/',
      'routes.summary': '/summary',
    
      ...
    };
    
    export type LanguageStrings = typeof baseStrings;
    export const en = baseStrings;
    
    エクスポートされた型は、他のすべての言語がサポートする必要がある等級Enforcerとして使用されます.つまり、いくつかの安全性を強制するために、他のファイルに追加される必要があります.また、逆もまた同様です.
    ベース文字列ファイルに存在しない特定の言語に文字列を追加しようとすると、コンパイルエラーが発生します.これにより、使用される言語のすべてが、少なくとも翻訳されていない場合、すべての文字列が設定され、実行時エラーから保存されます.
    また、ベースの文字列を、その言語に対応するISO変数としてエクスポートします.
    今、我々は任意のtyposを避けるためにルートを参照するために使用できるマッチングenum(または凍結JSのオブジェクト)を追加しましょう.
    export enum AppRoute {
      Home = 'routes.home',
      Summary = 'routes.summary'
    }
    

    ローカライズスイッチコンポーネント
    ルートパスを変換するプロセスを簡素化するために、このロジックを処理するカスタムLocalizedSwitchコンポーネントを作成します.
    また、ルートコンポーネントレベルでこれを行うこともできますが、スイッチコンポーネントを交換することで、変更を最小限に抑えてこれをサポートできます.ルートコンポーネントの変更は、おそらくより柔軟な解決策です.
    LocalsedSwitchコンポーネントのための意図された提案は、通常のスイッチの1つの交換のドロップとして想像されます、そして、それは反応ルータDOMパッケージから経路構成要素で働くように設計されています.
    export const LocalizedSwitch: React.FC = ({ children }) => {
      /**
       * inject params and formatMessage through hooks, so we can localize the route
       */
      const { formatMessage, locale } = useIntl();
    
      /**
       * Apply localization to all routes
       * Also checks if all children elements are <Route /> components
       */
      return (
        <Switch>
          {React.Children.map(children, child =>
            React.isValidElement<RouteProps>(child)
              ? React.cloneElement(child, {
                  ...child.props,
                  path: localizeRoutePath(child.props.path)
                })
              : child
          )}
        </Switch>
      );
    
      /**
       *
       * @param path can be string, undefined or string array
       * @returns Localized string path or path array
       */
      function localizeRoutePath(path?: string | string[]) {
        switch (typeof path) {
          case 'undefined':
            return undefined;
          case 'object':
            return path.map(key => `/${locale}` + formatMessage({ id: key }));
          default:
            const isFallbackRoute = path === '*';
            return isFallbackRoute
              ? path
              : `/${locale}` + formatMessage({ id: path });
        }
      }
    };
    

    配線すること
    一緒に配線するには、IntProviderコンポーネントをreact-intl パッケージは、定義したデータに接続し、独自のLocalizedSwitchコンポーネントを追加します.
    export const App: React.FC = () => (
      <LocalizedRouter
        RouterComponent={BrowserRouter}
        languages={AppLanguage}
        appStrings={appStrings}
      >
        <AppLayout>
          <LocalizedSwitch>
            <Route exact path={AppRoute.Home}>
              <views.Home />
            </Route>
            <Route exact path={AppRoute.Summary}>
              <views.Summary />
            </Route>
            <Route path="*">
              <views.GeneralError />
            </Route>
          </LocalizedSwitch>
        </AppLayout>
      </LocalizedRouter>
    );
    

    複数の言語をサポートする
    我々がアプリケーションを国際化して、アプリケーションルートをローカライズすることができるロジックをセットアップする基礎をカバーしたので、我々は他の言語の支持を加えて、彼らのルート定義を加えなければなりません.
    この例の目的のために、我々がすでに持っているINTLフォルダの中で、ドイツ語、フランス語とクロアチアの言語のサポートを加えましょう.

    Disclaimer: I don’t personally speak Deutch or French, so if the translations are not perfect, I do apologize for mangling your language, but please blame Google Translate :)



    新しい言語の翻訳の追加
    新しい言語ファイルを追加します.
    export const de: LanguageStrings = {
      /** Routes */
      'routes.home': '/',
      'routes.summary': '/zusammenfassung',
    
      ...
    };
    
    あなたがなぜこれがされたか疑問に思っているならば.このシナリオのTSファイルではなく、JSONのような別の形式ではなく、唯一の目的は、タイプスクリプトを使用して付属の安全を強制することです.
    もちろん、JSON、JSまたは別の好ましい形式でこれらを書くことができます場合には、タイプの安全性を必要としないまたは必要です.
    追加する言語ファイルごとにAppenanguage enumを拡張します.

    ルータの更新
    まず最初に、他の言語へのリダイレクトをサポートし、パス名から現在の言語を読み、それに応じてロケールを設定するようルータを更新する必要があります.
    予想される動作:
    /summary -> Redirect to base language
    /en/summary -> English language summary page
    /de/zusammenfassung -> German language summary page
    
    デフォルトのルータコンポーネントをパス名の検出をサポートし、反応intlプロバイダーを返すものと交換します.
    interface Props {
      RouterComponent: React.ComponentClass<any>;
      languages: { [k: number]: string };
      appStrings: { [prop: string]: LanguageStrings };
      defaultLanguage?: AppLanguage;
    }
    
    export const LocalizedRouter: React.FC<Props> = ({
      children,
      RouterComponent,
      appStrings,
      defaultLanguage
    }) => (
      <RouterComponent>
        <Route path="/:lang([a-z]{2})">
          {({ match, location }) => {
            /**
             * Get current language
             * Set default locale to en if base path is used without a language
             */
            const params = match ? match.params : {};
            const { lang = defaultLanguage || AppLanguage.English } = params;
    
            /**
             * If language is not in route path, redirect to language root
             */
            const { pathname } = location;
            if (!pathname.includes(`/${lang}/`)) {
              return <Redirect to={`/${lang}/`} />;
            }
    
            /**
             * Return Intl provider with default language set
             */
            return (
              <IntlProvider locale={lang} messages={appStrings[lang]}>
                {children}
              </IntlProvider>
            );
          }}
        </Route>
      </RouterComponent>
    );
    
    ルート内のすべてをラッピングすると、パス名から言語を決定するためにregexを使用し、その一致を使用して現在の言語をプロバイダーに注入できます.
    また、新しいルータコンポーネントは、言語が常にパス名の一部であることを強制します.
    この例で使用される正規表現は、小文字の言語のみをサポートしますが、[ a - za - z ] { 2 }に変更し、文字列を使用できます.upparercase ()メソッドを使用すると、大文字小文字を区別したい場合にパス名が一致します.

    言語スイッチャー
    また、アクティブな言語を変更し、パス名に基づいて現在アクティブ化された言語を表示できるようにする言語スイッチャコンポーネントを追加する必要があります.
    スタイルを別として、我々は他の言語のために文字列オブジェクトの中でルートを一致させるのをチェックするヘルパー機能が必要であるならば、我々は直接もう一つの言語で同じページに移動するのをサポートしたいです.
    export const LanguageSwitcher: React.FC = () => {
      const { pathname } = useLocation();
      const { locale, messages } = useIntl();
    
      return (
        <ul className={css(list.container)}>
          {Object.keys(AppLanguage).map(lang => (
            <li key={lang} className={css(list.item)}>
              <NavLink
                className={css(link.primary)}
                activeClassName={css(link.active)}
                to={getMatchingRoute(AppLanguage[lang])}
              >
                {AppLanguage[lang]}
              </NavLink>
            </li>
          ))}
        </ul>
      );
    
      function getMatchingRoute(language: string) {
        /**
         * Get the key of the route the user is currently on
         */
        const [, route] = pathname.split(locale);
        const routeKey = Object.keys(messages).find(key => messages[key] === route);
    
        /**
         * Find the matching route for the new language
         */
        const matchingRoute = appStrings[language][routeKey];
    
        /**
         * Return localized route
         */
        return `/${language}` + matchingRoute;
      }
    };
    
    ナビゲーション
    最後にするのは、ナビゲーションコンポーネント自体を更新することです.
    我々は、単にこの目的のために反応IntLフックからFormatMessage機能を使用します.
    export const Navigation: React.FC = () => {
      const { formatMessage, locale } = useIntl();
    
      return (
        <ul className={css(list.container)}>
          {Object.keys(AppRoute).map(elem => (
            <li key={elem} className={css(list.item)}>
              <NavLink
                exact
                className={css(link.primary)}
                activeClassName={css(link.active)}
                to={localizeRouteKey(AppRoute[elem])}
              >
                {formatMessage({ id: AppRouteTitles.get(AppRoute[elem]) || '' })}
              </NavLink>
            </li>
          ))}
        </ul>
      );
    
      function localizeRouteKey(path: string) {
        return `/${locale}` + formatMessage({ id: path });
      }
    };
    
    ts enumsがストリングenums上で逆のマッピングを考慮しないので、簡単なルート名解決を考慮に入れるために、ES 6マップを作成することができます.
    export const AppRouteTitles = new Map([
      [AppRoute.Home, 'home.title'],
      [AppRoute.Summary, 'summary.title']
    ]);
    

    概要
    ご覧のように、ウェブサイトのルートをローカライズすることは難しい反応ではありません.それはいくつかのコンポーネントを必要とし、プロジェクトのアーキテクチャの側ではほとんど考えていないので、物事を過剰にしないでください.結果は簡単に言語のカウントに関係なく、後で追加する可能性がありますスケールを解決する理解することは簡単です.
    完全に動作する例は次のとおりです.
    vlaja/multilanguage-routing-react