lziff 0.1.4

A cross-platform TUI diff & review tool with JetBrains-style snap-aligned side-by-side diffs
//! User-facing strings, separated from the rendering code so we can swap
//! languages and (later) load them from external files.
//!
//! Help is structured rather than a flat list of lines — the renderer can
//! lay out sections, key columns, and descriptions independently and the
//! translator never has to fight with whitespace alignment in a string.

#[derive(Debug, Clone)]
pub struct HelpEntry {
    pub keys: String,
    pub desc: String,
}

#[derive(Debug, Clone)]
pub struct HelpSection {
    pub title: String,
    pub entries: Vec<HelpEntry>,
}

#[derive(Debug, Clone)]
pub struct Strings {
    pub title_old_prefix: String,
    pub title_new_prefix: String,
    pub title_files_panel: String,
    pub title_help: String,
    /// Reserved for an empty-state message when the file list is empty.
    /// Not surfaced in the current renderer, but kept so plugin authors and
    /// localized config layers can wire it in.
    #[allow(dead_code)]
    pub status_no_changes: String,
    #[allow(dead_code)]
    pub status_no_diff: String,
    pub status_auto_reloaded: String,
    pub status_hint_default: String,
    pub status_hint_help_open: String,
    pub help_sections: Vec<HelpSection>,
    /// Sections appended only when the host is running in `--review`
    /// mode. Avoids cluttering the help in plain-diff sessions with
    /// keys that wouldn't do anything.
    pub help_review_sections: Vec<HelpSection>,
}

impl Strings {
    pub fn for_lang(lang: &str) -> Self {
        match lang {
            "ja" | "ja-JP" | "ja_JP" => Self::japanese(),
            _ => Self::english(),
        }
    }

    pub fn english() -> Self {
        Self {
            title_old_prefix: " [Old] ".into(),
            title_new_prefix: " [New] ".into(),
            title_files_panel: " [Files] ".into(),
            title_help: " [Help] ".into(),
            status_no_changes: "no changes".into(),
            status_no_diff: "(no diff)".into(),
            status_auto_reloaded: "auto-reloaded".into(),
            status_hint_default:
                "j/k scroll  J/K hunk  click select+snap  =  resync  n/p file  Tab focus  ? help  q quit"
                    .into(),
            status_hint_help_open: "press ? to close help".into(),
            help_sections: vec![
                HelpSection {
                    title: "Navigation".into(),
                    entries: vec![
                        e("j / k", "scroll both panes 1:1"),
                        e("J / K", "jump to next / prev change hunk"),
                        e("ctrl-d / ctrl-u", "half-page down / up"),
                        e("g / G", "top / bottom"),
                        e("n / p", "next / prev file"),
                    ],
                },
                HelpSection {
                    title: "Alignment".into(),
                    entries: vec![
                        e("click", "select that line; the *other* pane snaps"),
                        e("[ / ]", "nudge alignment row up / down"),
                        e("wheel", "scroll pane under pointer (diff only)"),
                        e("=", "re-snap non-anchor pane to alignment row"),
                    ],
                },
                HelpSection {
                    title: "Display".into(),
                    entries: vec![
                        e("Tab", "toggle focus (Files / Diff)"),
                        e("F", "show / hide the files panel"),
                        e("r", "manual reload"),
                    ],
                },
                HelpSection {
                    title: "App".into(),
                    entries: vec![
                        e("? / Esc", "toggle / close this help"),
                        e("q", "quit"),
                    ],
                },
            ],
            help_review_sections: vec![HelpSection {
                title: "Review".into(),
                entries: vec![
                    e("c", "open comment modal at cursor row"),
                    e("d", "list buffered drafts (Enter edit · x delete)"),
                    e("S", "open submit modal (verdict + body)"),
                    e("ctrl-s", "save draft (in comment) / submit (in submit modal)"),
                    e("Tab", "(in submit) switch verdict ↔ body focus"),
                    e("a / c / r", "(in submit · verdict) Approve / Comment / Request changes"),
                    e("Esc", "close modal without saving"),
                ],
            }],
        }
    }

    pub fn japanese() -> Self {
        Self {
            title_old_prefix: " [旧] ".into(),
            title_new_prefix: " [新] ".into(),
            title_files_panel: " [ファイル] ".into(),
            title_help: " [ヘルプ] ".into(),
            status_no_changes: "差分なし".into(),
            status_no_diff: "(差分なし)".into(),
            status_auto_reloaded: "自動再読込".into(),
            status_hint_default:
                "j/k スクロール  J/K ハンク  クリック選択  =  再同期  n/p ファイル  Tab フォーカス  ? ヘルプ  q 終了"
                    .into(),
            status_hint_help_open: "? でヘルプを閉じる".into(),
            help_sections: vec![
                HelpSection {
                    title: "ナビゲーション".into(),
                    entries: vec![
                        e("j / k", "両ペインを 1:1 でスクロール"),
                        e("J / K", "次 / 前の変更ブロックへ"),
                        e("ctrl-d / ctrl-u", "半ページ 上 / 下"),
                        e("g / G", "先頭 / 末尾"),
                        e("n / p", "次 / 前のファイル"),
                    ],
                },
                HelpSection {
                    title: "整列".into(),
                    entries: vec![
                        e("click", "その行を選択 — 反対ペインがスナップ"),
                        e("[ / ]", "整列行を 1 行上 / 下へ"),
                        e("wheel", "ポインタ下のペインをスクロール (Diff のみ)"),
                        e("=", "非アンカー側を整列行に再同期"),
                    ],
                },
                HelpSection {
                    title: "表示".into(),
                    entries: vec![
                        e("Tab", "フォーカス切替 (ファイル / Diff)"),
                        e("F", "ファイルパネルの表示切替"),
                        e("r", "手動再読込"),
                    ],
                },
                HelpSection {
                    title: "アプリ".into(),
                    entries: vec![
                        e("? / Esc", "ヘルプの開閉"),
                        e("q", "終了"),
                    ],
                },
            ],
            help_review_sections: vec![HelpSection {
                title: "レビュー".into(),
                entries: vec![
                    e("c", "カーソル行にコメントモーダルを開く"),
                    e("d", "ドラフト一覧 (Enter で編集・x で削除)"),
                    e("S", "提出モーダルを開く (verdict + 本文)"),
                    e("ctrl-s", "コメント保存 / 提出モーダルでは送信"),
                    e("Tab", "提出モーダルで verdict ↔ 本文 を切替"),
                    e("a / c / r", "提出 verdict 切替 (Approve / Comment / Request changes)"),
                    e("Esc", "モーダルを閉じる (保存しない)"),
                ],
            }],
        }
    }
}

fn e(keys: &str, desc: &str) -> HelpEntry {
    HelpEntry {
        keys: keys.into(),
        desc: desc.into(),
    }
}