mcpField
F
fcode-i18n

fcode-i18n

github.com/factorialco開発1282 回インストール今日更新
リポジトリ

fcode-i18n について

fcode-i18nは、Factorial Codeのワークスペースで言語別の翻訳を扱うスキルです。実行時の翻訳参照やフォームスキーマの置換に加え、CLIで言語ファイルを同期できます。

主な機能

  1. 01JavaScriptとPythonでの翻訳参照
  2. 02言語別・バージョン固定の参照
  3. 03プレースホルダー置換とフォールバック
  4. 04フォームスキーマのサーバー側置換
  5. 05CLIによる言語ファイルの同期とテスト

活用シーン

  1. 01プロセスやモジュールの出力の多言語化
  2. 02翻訳済みフォームスキーマの提供
  3. 03指定言語でのワークスペース実行のテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/factorialco/factorial-code-skills --skill fcode-i18n

インストール後、Claude Code で/fcode-i18nを入力して読み込まれたか確認します。

詳細

Factorial Code — i18n

ワークスペースでは、対応する言語ごとにロケールを1つ保持します。i18n/ 以下に翻訳キーを記述した YAML ファイルを置き、ほかのリソースと同様に CLI で同期します。fcode.i18n("key") は、プロセスコード、モジュールコード、そして(サーバー側で置換される)フォームスキーマ内で、現在の実行が使用しているロケールに基づいてキーを解決します。プラットフォームモデルについては fcode-core-concepts、CLI のフローについては fcode-cli、フォームへの埋め込みについては fcode-forms を参照してください。

注意点

  • fcode.i18n を別名にしないでください — 翻訳が実行環境に渡されるのは、ソース内で fcode.i18n( が静的に検出された場合のみです。そのため、const t = fcode.i18n; t("k") は実行時に 「i18n is disabled」 をスローします。キーにはハードコードされた文字列を指定し、必ずそのまま呼び出してください(fcode.import のモジュール名と同じ種類のルールです)。
  • このヘルパーは失敗しません。 どこにも翻訳がないキーはキーそのものに解決されます。出力に greetings.hello がそのまま表示される場合は翻訳が不足しているのであり、実行が壊れているわけではありません。引数を渡さなかったプレースホルダーは、記述どおりの状態で残ります。オブジェクト以外の args(配列を含む)は無視されます。
  • ロケール識別子では大文字と小文字が区別されます(pt-BR ≠ pt-br)。これはプラットフォーム全体で今後も変わりません。有効な値は、文字、数字、-、_ のいずれかを最大20文字使用し、文字または数字で始まるものです(^[A-Za-z0-9][A-Za-z0-9_-]{0,19}$)。ドットは使用できません。大文字と小文字を区別しないファイルシステム(macOS/Windows のデフォルト)では、大文字と小文字だけが異なる2つのロケールを CLI がプルしないようにします。ファイルが1つにまとまってしまうためです。
  • i18n/<locale>.inherited.yaml は編集しないでください — これは読み取り専用で、gitignore の対象であり、プル時に再生成されます。継承したキーを上書きするには、i18n/<locale>.yaml に記述してください。上書きはファイル単位ではなくキー単位で適用されるため、指定していないキーは親のテキストへの解決が維持されます。
  • フォームおよび webhook エンドポイントでは、locale は version_tag や async と同様に予約名です。言語を選択するために使用され、パラメーターの構築前に取り除かれます。フォームフィールドや webhook のボディフィールドに locale という名前を付けても、プロセスには渡されません。業務データには別の名前を使用してください。
  • フォームトークンの引数はスカラー値だけを含むフラットなオブジェクトにしてください — { "max": "500" } は有効ですが、{ "max": { "chars": "500" } } は有効ではありません。ネストした波括弧を含むトークンは、提供されるスキーマ内で置換されずに残ります。
  • 誤記された version タグは、現在のファイルを使って黙って解決されます — 出力が空になることはありません。リリース済みのテキストが固定されていないように見える場合は、まずピン留めされたタグを確認してください。存在しないロケールを指定する呼び出しごとの { locale } も同様に、ロケールが指定されていない場合と同じように検索されます。
  • ピン留めする version はインラインのリテラル文字列で指定してください — 配布するスナップショットはソースから読み取られるため、{ version: chosenTag } は現在のファイルを使って解決されます。locale オプションにはこのルールはなく、その値には任意のランタイム式を指定できます。また、別の場所で構築したオプションも引き続き使用できます。
  • 値を付けずに記述された YAML キーは、未翻訳として扱われます — 空文字列に解決されるのではなく、フォールバックロケールが使用されます。

ロケールファイル

ロケールごとに、i18n/<locale>.yaml にキーとテキストの YAML マッピングを 1 つ置きます。ネストはファイルを書く人にとって便利なだけで、データモデルではありません。ネストされたキーはドットで指定するため、次の 2 つのファイルは同じロケールです —

# i18n/en.yaml
greetings:
  hello: "Hi %{name}"
  farewell: "See you"
# identical to the file above
"greetings.hello": "Hi %{name}"
"greetings.farewell": "See you"

— これにより、子ワークスペースは親の構造を繰り返すことなく、単一のキーを上書きできます。%{name} プレースホルダーには、ヘルパーに渡された引数が設定されます。ロケールファイルの上限は 256 KB です(マージ後のセット全体が、実行のたびに読み込まれます)。

ワークスペースが継承する内容は、ワークスペースが所有する内容と並んで i18n/<locale>.inherited.yaml に置かれます。これは読み取り専用で、gitignore の対象です(CLI がエントリを追加します)。複数の親ワークスペースで同じロケールが定義されている場合、継承ファイルにはプラットフォームの解決順にマージされた内容が、生成されたヘッダーの下にドット区切りのキーによるフラットなマッピングとして書き込まれます。親が 1 つだけの場合は、コメントも含めてファイルがそのまま保持されます。ローカル実行では、クラウドとまったく同じように、独自のファイルがその上に重ねられます。

fcode.i18n ヘルパー

fcode.i18n(key, args, options) は、JavaScript と Python で同じ名前とセマンティクスを持ち、プロセスとモジュールの両方で利用できます(プロセスがモジュール経由でのみこのヘルパーに到達する場合も、翻訳を利用できます)。

const greeting = fcode.i18n("greetings.hello", { name: "Ada" }); // "Hi Ada" in `en`
fcode.i18n("greetings.farewell");                    // no placeholders → no args
fcode.i18n("legal.terms", null, { version: "v1.0.0" }); // pinned to a published version
fcode.i18n("greetings.hello", { name: "Ada" }, { locale: "es" }); // another locale, this lookup only
const recipient = { name: "Ada", locale: "pt-BR" };               // ...and the value may be dynamic,
fcode.i18n("greetings.hello", { name: recipient.name }, { locale: recipient.locale }); // per recipient
fcode.i18n("legal.terms", null, { version: "v1.0.0", locale: "es" }); // both combine
const locale = fcode.i18n.locale;                    // the execution's locale
greeting = fcode.i18n("greetings.hello", {"name": "Ada"})
fcode.i18n("greetings.farewell")
fcode.i18n("legal.terms", None, {"version": "v1.0.0"})
fcode.i18n("greetings.hello", {"name": "Ada"}, {"locale": "es"})
recipient = {"name": "Ada", "locale": "pt-BR"}
fcode.i18n("greetings.hello", {"name": recipient["name"]}, {"locale": recipient["locale"]})
fcode.i18n("legal.terms", None, {"version": "v1.0.0", "locale": "es"})
locale = fcode.i18n.locale
  • version — ロケールのバージョンタグまたはエイリアスです。インラインのリテラル文字列で指定します(Gotchas を参照)。バージョン管理については以下をご覧ください。
  • locale — 別のロケールでその検索のみを行います。従業員の言語など、値は動的にできるため、1 回の実行で複数の言語を使用できます。指定したロケールでキーが翻訳されていない場合は、実行時のロケール、次にプライマリロケールにフォールバックします。存在しないロケールは、指定されていない場合と同じように動作するため、このオプションが原因で結果が悪くなることはありません。fcode.i18n.locale が引き続き返すのは実行時のロケールです。フォームスキーマのトークンにはこのオプションを指定できません。レンダリング時には、すでにリクエストで選択された言語が使われているためです。
  • 文字列の補間は独自プロパティのみを対象とする 1 回の処理です。置換後の値に %{...} が含まれていても再スキャンされません(ある引数から別の引数にアクセスすることはできません)。また、%{constructor} は何も解決しません。キーや引数が見つからなくても例外は発生しません(Gotchas を参照)。

フォームスキーマの翻訳

フォームスキーマはブラウザーでレンダリングされるため、キーを解決するランタイムはありません。parametersSchema.json のタイトル、説明、ui:placeholder、embedFormOptions.loadingOverlayContent など、表示されるすべてのテキストに、同じ呼び出しを文字列として記述してください。プラットフォームがスキーマを提供する前に置換します。ブラウザーには、すでに 1 つの言語で記述されたスキーマが届きます。翻訳がクライアントに届くことはありません。置換は preRenderProcess の後に実行されるため、プリレンダリングで挿入されたテキストも翻訳されます。

{
  "type": "object",
  "properties": {
    "reason": {
      "type": "string",
      "title": "fcode.i18n(\"form.reason.label\")",
      "description": "fcode.i18n(\"form.reason.help\", { max: \"500\" })"
    }
  }
}

引数には緩やかな JavaScript 構文を使用できます(シングルクォート、フィールド名の引用符の省略、末尾のカンマはいずれも使用可能です)が、スカラー値のみを持つフラットなオブジェクトにする必要があります。ネストされた値があると、トークン全体が置換されません(Gotchas を参照)。

読み手のロケールは埋め込み元から取得します。埋め込みオプションの locale または data-fcode-form-locale 属性から取得され、Fcode-Locale ヘッダーとしてプラットフォームに送信されます。これを変更するとスキーマが再取得され、送信時にも同じヘッダーが付くため、フォームがレンダリングされた言語で実行されます。埋め込みの仕組みについては fcode-forms をご覧ください。(埋め込みオプションの fallbackLocale はここでは関係ありません。locale が rjsf に同梱されている言語でない場合に、rjsf 組み込みの検証メッセージの言語を選択するためだけに使われます。)

実行時のロケールの選択方法

トリガー選択方法
フォーム?locale= クエリパラメーターまたは Fcode-Locale ヘッダー(パラメーターが優先されます)
Webhook?locale= クエリパラメーターまたは Fcode-Locale ヘッダー(パラメーターが優先されます)
今すぐ実行実行ダイアログのロケールセレクター
スケジュールスケジュールの作成時または編集時のロケールセレクター
再実行元の実行に保存されたロケールを再利用します
呼び出しごとヘルパーのオプションに指定する locale — その参照に限り適用され、値は動的に指定できます

公開エンドポイントで不正な形式のロケールを指定すると 400 が返されます。有効な形式でも未知のロケールは、単にフォールバックします。選択されたロケールは実行に保存されます。そのため、ワークスペースのデフォルトが後から変更されても、再実行では元の実行時の言語が再現されます。

ロケールが指定されていない場合は、ワークスペースの優先ロケールが使用されます。これは settings.json の primaryLocale で設定します(Settings → Details で設定するか、ファイルを編集して fcode settings:push を実行します。フィールドのリファレンスは fcode-cli にあります)。優先ロケールが選択されていない場合は、アルファベット順で最初のロケールが使用されます。優先ロケールはキー単位のフォールバックにもなります。選択したロケールで翻訳されていないキーは優先ロケールから解決され、両方にないキーのみ、そのキー自身の名前に解決されます。そのため、部分的にしか翻訳されていないロケールでも、すべてのキーを解決できます。ただし、優先ロケールは完全な状態にしておいてください。

CLI:ロケールの同期とテスト

ロケールは、ほかのリソースと同様に CLI で扱います。

fcode i18n:pull            # fetch every locale, inherited ones included
fcode i18n:status          # what changed locally vs the cloud
fcode i18n:add pt-BR       # track a new local file
fcode i18n:push            # create or update in the cloud
fcode i18n:remove pt-BR    # stop tracking it locally
fcode i18n:reset           # discard local changes
  • 集約コマンドの fcode pull/push/status にはロケールも含まれるため、通常のワークスペース全体を対象とするコマンドでロケールも同期できます。
  • extract コマンドはありません — ハードコードされた文字列をロケールファイルに移すのはエージェントの役割です(次のセクションを参照)。CLI はファイルを同期するだけです。
  • 親ワークスペースが所有する識別子をプッシュすると、ここにオーバーライドが作成されます。キーごとにレイヤー化され、親のファイルが編集されることはありません。
  • primaryLocale は settings.json にあり、fcode settings:push で同期されます。

ローカル実行では、クラウドとまったく同じ方法でレイヤー化された同じ i18n/ ファイルを参照して fcode.i18n が解決されるため、fcode run my-process は本番環境と同じように動作します。特定のロケールで実行するには --locale を渡します。

fcode run my-process --locale pt-BR

--locale はローカルでは形式検証されません。タイプミスがあると、どのファイルにも一致せず、すべてのキーがそのまま自身に解決されます。ローカル実行で生のキーが表示された場合は、まずフラグのスペルと大文字・小文字を確認してください。

既存コードの国際化

自動抽出には対応していません。ワークスペースの国際化は、CLI を同期手段として実行するコード変換です。

  1. ユーザーと対象範囲を決めます:ロケールと、どれをプライマリにするかを決めます。未設定の場合は、settings.json に primaryLocale を記述し、fcode settings:push を実行します。
  2. ユーザー向け文字列を洗い出します。 翻訳する対象:フォームスキーマのタイトル、説明、プレースホルダー、loadingOverlayContent、フォームに表示する結果の message 文字列(これらは Markdown です。**太字**、リンク、テーブル構文などの書式は、すべてのロケールで維持してください)、メールの件名と本文、エンドユーザーに表示される Webhook の応答本文。翻訳しない対象:ログメッセージ、開発者向けエラー、データストアのキー、変数名、スラッグ、識別子。
  3. キーに名前を付けます:ドット区切りの名前空間を使います:<process-slug>.<area>.<name>(order-sync.form.title、order-sync.email.subject など)。プロセス間で共有する文字列には common.* を使います。動的な部分は %{placeholders} として取り出し、翻訳済みの断片を連結しないでください。
  4. 各文字列を置き換えます:コードではリテラルの fcode.i18n("key", { args }) 呼び出しに置き換えます(エイリアスを使わず、キーをハードコードします)。スキーマではトークン文字列に置き換えます(引数はフラットなスカラーのみ)。
  5. すべてのロケールについて i18n/<locale>.yaml を入力します(新しいロケールには fcode i18n:add を使います)。プライマリロケールにはすべてのキーを含めてください。他のすべてのロケールは、プライマリロケールをフォールバックとして使用します。
  6. ロケールごとにテストします:fcode run <slug> --locale <loc> を実行します。出力にドット区切りのキーがそのまま表示される場合は翻訳が不足しています。%{name} がそのまま表示される場合は引数が不足しています。
  7. プッシュします:fcode i18n:push(または集約コマンドの fcode push)を実行します。primaryLocale を変更した場合は、fcode settings:push も実行します。

ロケールのバージョン管理

ロケールもプロセスやモジュール(fcode-core-concepts のモデル)と同様にバージョン管理されます。公開すると YAML が不変のタグの下にスナップショットとして保存され、エイリアスは移動可能なポインターになります。ただし、ロケールごとです。en の production と es の production は、別々のエイリアスです。

固定された呼び出し({ version: "v1.0.0" })は継承チェーン内の各ファイル単位で解決されます。各ロケールファイルは、そのタグのスナップショットがあればそれを、なければ現在の内容を返し、通常どおりキー単位で重ね合わせます。そのため、バージョンの作成後に追加されたロケールもキーを提供し、そのタグを一度も公開していない親もテキストを提供し、入力ミスのタグを指定しても出力が空になるのではなく、すべて現在のファイルに対して解決されます。{ version: "v1.0.0", locale: "es" } を組み合わせると、同じフォールバックを適用しながら、要求されたロケールのスナップショットが読み込まれます。バージョンを削除すると、そのバージョンに固定された呼び出しは現在のファイルに戻ります。ロケールを削除すると、そのロケールのバージョンも削除されます。

1 つのレイヤーでは解決方法が異なります。自身のバージョンの 1 つに固定された親ワークスペースは、その固定先のスナップショットを返し、呼び出しで指定されたタグを一度も公開していない場合は、現在のファイルではなく、そのスナップショットにフォールバックします。親のライブ翻訳が子に引き継がれることはありません。fcode-core-concepts を参照してください。

ワークスペースのバージョンでは、リリース時の翻訳が固定されます。 ワークスペースのバージョンを作成すると(fcode settings:versions:create。fcode-cli を参照)、所有するすべてのロケールのバージョンが公開されます(下記の固定先を確保するため、最初にロケールを公開します)。さらに、プロセスコード、モジュールコード、フォームスキーマ内の裸の fcode.i18n 呼び出しがタグに固定されるよう、公開済みのスナップショットを書き換えます。

// Working copy (never modified)
fcode.i18n("greetings.hello", { name: "Ada" });
fcode.i18n("welcome", { name: "Ada" }, { locale: "es" });

// Published v1.0.0 snapshot
fcode.i18n("greetings.hello", { name: "Ada" }, { version: "v1.0.0" });
fcode.i18n("welcome", { name: "Ada" }, { version: "v1.0.0", locale: "es" });

オプションに version がすでに指定されている呼び出しは(動的な値であっても)、意図的なものとみなされ、そのまま変更されません。version の指定がないオプション(locale だけの場合、空のオブジェクト、明示的な null/None)にはタグが挿入されるため、ローカライズされた呼び出しはリリース時に固定されますが、ロケールは記述されたとおりに保たれます。動的な値({ locale: employee.locale })もそのまま保持されます。 (モジュールのインポートでは、古いエグゼキューターとの互換性のため、文字列形式の fcode.import("m", "v1.0.0") で固定する点が異なります。一方をもう一方と同じ形式に「修正」しないでください。)リリース済みの誤記を修正するには、再度公開する必要があります。スナップショットは不変です。

REST API、SDK、MCP ツール

ロケールは識別子で指定し、PUT はアップサートするため、同期時にワークスペースにそのロケールがすでに存在するかどうかを確認する必要はありません。

GET    /{team}/rest/locales
GET    /{team}/rest/locales/{locale}
PUT    /{team}/rest/locales/{locale}     body: { "content": "<yaml>" }
DELETE /{team}/rest/locales/{locale}

どちらの SDK も、FcodeI18n と同じインターフェースを提供します。

import { FcodeI18n } from "@factorialco/fcode-sdk";
const i18n = new FcodeI18n();
await i18n.list();                                        // inherited included
await i18n.set("pt-BR", 'greetings:\n  hello: "Olá %{name}"\n');
await i18n.delete("pt-BR");
from fcode_sdk import FcodeI18n
i18n = FcodeI18n()
i18n.list()
i18n.set("pt-BR", 'greetings:\n  hello: "Olá %{name}"\n')
i18n.delete("pt-BR")

親ワークスペースが所有する識別子に対して set() を呼び出すと、ここにオーバーライドが作成されます。これは CLI のプッシュと同じ、キー単位の重ね合わせです。

MCP サーバーは get_locales、get_locale、save_locale、delete_locale を公開します。save_locale はロケールの内容を完全に置き換えます。キーを追加するには、先に get_locale を実行し、マージした YAML を書き戻してください。

コメント