sequenceDiagram
participant C as Client
participant A as FastAPI /fields/translate
participant DB as Postgres (portal_fields)
participant T as I18nTranslator
participant O as OpenAI API
C->>A: POST /fields/translate (dry_run=true)
A->>DB: SELECT 対象行 (ids/only_missing/limit 反映)
DB-->>A: rows
loop 各 row
A->>T: fill_missing_recursively(value)
alt *_i18n 辞書かつ target 未設定
T->>O: 翻訳(ja_JP→en_US)
O-->>T: translated_text
T-->>A: after値 + changed=true
else list/dict/その他
T-->>A: 再帰結果 / 変更なし
end
alt dry_run=true
A-->>C: preview_samples に {before/after} を積む(応答)
else dry_run=false
A->>DB: UPDATE portal_fields ... , updated_at=NOW()
DB-->>A: OK
end
end
A-->>C: {processed, updated, dry_run, preview_samples}
graph TD
A["クライアント<br/>(Swagger / CLI)"] -->|POST /fields/translate| B["FastAPI ルータ"];
B --> C["パラメータ検証 / 正規化"];
C --> D["対象カラム列挙<br/>label_i18n / help_i18n / placeholder_i18n / unit_i18n / notes_i18n / selection_items / notes"];
D --> E{"WHERE 条件組み立て"};
E -->|ids あり| F["WHERE id IN (:ids)"];
E -->|only_missing true| G["トップレベル i18n 欠落判定<br/>(col IS NULL OR NOT col ? :json_key)"];
E -->|それ以外| H["フィルタなし"];
F --> I["SELECT ... FROM portal_fields<br/>LIMIT :limit"];
G --> I;
H --> I;
I --> J{"各行の処理"};
J --> K["再帰走査 fill_missing_recursively(value)"];
K --> L{"*_i18n 辞書か?"};
L -->|Yes| M["未設定 target を補完<br/>(OpenAI 翻訳)"];
L -->|No| N{"list / dict か?"};
N -->|list| O["要素ごとに再帰"];
N -->|dict| P["キーごとに再帰"];
N -->|その他| Q["変更なし"];
M --> R["diff 収集 (最大20件)"];
O --> R;
P --> R;
Q --> S["変更フラグ off"];
R --> T{"dry_run か?"};
S --> T;
T -->|true| U["preview_samples に積む<br/>(DB 更新なし)"];
T -->|false| V["UPDATE をバッファへ"];
V --> W{"to_update あり?"};
W -->|Yes| X["TX で UPDATE + updated_at=NOW()"];
W -->|No| Y["集計処理へ"];
X --> Y;
Y --> Z["JSON レスポンス (processed / updated / preview_samples)"];
コメントを残す