Closed3

Remix-i18nextをVercelで使いたい

スーパーめかぶスーパーめかぶ

普通に使う

こちら を参考に普通に作るとVercelにデプロイしたときにpublic/locales/**が読めなくて怒られる

remix-i18nextのサンプルから抜粋

import { RemixI18Next } from "remix-i18next";
import { FileSystemBackend } from "remix-i18next";

// You will need to provide a backend to load your translations, here we use the
// file system one and tell it where to find the translations.
let backend = new FileSystemBackend("./public/locales");

export let i18n = new RemixI18Next(backend, {
  fallbackLng: "en", // here configure your default (fallback) language
  supportedLanguages: ["es", "en"], // here configure your supported languages
});
スーパーめかぶスーパーめかぶ

カスタムバックエンドを使う

公式のREADMEの一番下に言語ファイルをCDNにおけるサービスを使えるよ、とあるので使ってみる

remix-i18nextのサンプルから抜粋

import type { Backend } from "remix-i18next/backend";

export class FetchBackend implements Backend {
  private url: URL;

  constructor(url: string) {
    this.url = new URL(url);
  }

  async getTranslations(namespace: string, locale: string) {
    let url = new URL(`${locale}/${namespace}.json`, this.url);
    let response = await fetch(url.toString(), {
      headers: { accept: "application/json" },
    });
    return response.json();
  }
}

Locizeを使う

Locizeが公式でRemix-i18nextの対応方法を書いてくれてるのでやってみる
が、結局Locizeで言語ファイルを管理するだけでローカルにダウンロードして使おう!って感じなので根本解決にならない

スーパーめかぶスーパーめかぶ

LocizeのCDNから言語ファイルを持ってきたい

Locizeのサンプルリポジトリをみてみるとi18-next用のLocizeBackendが使えると思うけどDL数(CDNからの?)が増えると思うよと書いてあるのでとりあえず試してみる

TypeScriptで作ってるので諸々型を指定

import {Language, RemixI18Next} from "remix-i18next";
import i18nextOptions from "~/i18nextOptions";
import I18NextLocizeBackend from "i18next-locize-backend"

class LocizeBackend {
    private locizeBackend: I18NextLocizeBackend;

    constructor(options: { projectId: string, apiKey?: string, version?: string }) {
        this.locizeBackend = new I18NextLocizeBackend(options);
    }

    async getTranslations(namespace: string, locale: string) {
        return new Promise<Language>((resolve, reject) => {
            this.locizeBackend.read(locale, namespace, (err, ret) => {
                resolve(ret);
            })
        })
    }
}

const backend = new LocizeBackend({projectId: "LOCIZE_PROJECT_ID});
// const backend = new FileSystemBackend("./public/locales");

export const i18n = new RemixI18Next(backend, {
    fallbackLng: i18nextOptions.fallbackLng,
    supportedLanguages: i18nextOptions.supportedLngs,

});

するとgetTranslationsが返すPromiseのリゾルバが型不一致のエラーを返す

this.locizeBackend.readのcallbackが成功時に入れる値の型を見てみると

ret?: boolean | ResourceKey | null | undefined

こうなってる

ResourceKeyの定義を見ると

export type ResourceKey =
  | string
  | {
      [key: string]: any;
    };

で、帰ってきてほしいLanguageの定義が

declare type Value = string | Language;
declare type Key = string;
export declare type Language = {
    [key: Key]: Value;
};

こうなので、readの結果がResourceKeyに特定できるまでチェックを入れまくる

    async getTranslations(namespace: string, locale: string) {
        return new Promise<Language>((resolve, reject) => {
            this.locizeBackend.read(locale, namespace, (err, ret) => {
                if (err) return reject(err);
                if (!ret) return reject();
                if (typeof ret === "boolean") return reject();
                resolve(ret as Language);
            })
        })
    }

最高に不格好&エラー時の挙動が全くわからんがとりあえずこれでRemix-i18nextを使ったRemixプロジェクトをVercel上でi18n対応できた

Remix or NextJSとLocizeのプロを探す旅

このスクラップは2022/04/02にクローズされました