1pub mod en;
12pub mod ja;
13
14use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum Locale {
21 #[default]
22 En,
23 Ja,
24}
25
26impl Locale {
27 pub const ALL: &'static [Locale] = &[Locale::En, Locale::Ja];
28
29 pub fn as_str(&self) -> &'static str {
31 match self {
32 Locale::En => "en",
33 Locale::Ja => "ja",
34 }
35 }
36
37 pub fn parse(s: &str) -> Option<Locale> {
38 match s {
39 "en" => Some(Locale::En),
40 "ja" => Some(Locale::Ja),
41 _ => None,
42 }
43 }
44
45
46 pub fn from_env() -> Option<Locale> {
52 for var in &["LANG", "LANGUAGE"] {
53 if let Ok(val) = std::env::var(var) {
54 let lower = val.to_lowercase();
55 if lower.starts_with("ja") {
56 return Some(Locale::Ja);
57 }
58 if lower.starts_with("en") {
59 return Some(Locale::En);
60 }
61 }
62 }
63 None
64 }
65
66 pub fn display_name(&self) -> &'static str {
68 match self {
69 Locale::En => "English",
70 Locale::Ja => "日本語",
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum MessageKey {
79 AppTitle,
81 LocalOnlyBadge,
82 NavSearch,
84 NavSources,
85 NavIndexing,
86 NavStorage,
87 NavModels,
88 NavAi,
89 NavSettings,
90 SearchPlaceholder,
92 SearchButton,
93 SearchNoSourcesTitle,
94 SearchNoSourcesBody,
95 SearchAddSource,
96 SearchNoResults,
97 SearchKeywordOnlyNotice,
98 SourcesTitle,
100 SourcesEmptyTitle,
101 SourcesEmptyBody,
102 SourcesAddFolder,
103 SourcesStatusActive,
104 SourcesStatusPaused,
105 SourcesStatusMissing,
106 IndexingTitle,
108 IndexingIdle,
109 IndexingHealthIndexed,
110 IndexingHealthStale,
111 IndexingHealthFailed,
112 IndexingHealthQueued,
113 StorageTitle,
115 StorageIntro,
116 StorageGroupSearchIndex,
117 StorageGroupModels,
118 StorageGroupCaches,
119 StorageSafeCleanupHeading,
120 StorageClearSnippets,
121 StorageClearSearchCache,
122 StorageDangerHeading,
123 StorageResetCatalog,
124 StorageResetWarning,
125 ModelsTitle,
127 ModelsEmbeddingRole,
128 ModelsRerankerRole,
129 ModelsStatusAvailable,
130 ModelsStatusMissing,
131 ModelsKeywordOnlyHint,
132 SettingsTitle,
134 SettingsLanguageHeading,
135 SettingsPrivacyHeading,
136 SettingsAdvancedHeading,
137 SettingsAdvancedOn,
138 SettingsAdvancedOff,
139 SettingsAdvancedHint,
140 SettingsPrivacyLocalOnly,
141 SearchModeLabel,
143 SearchModeAuto,
144 SearchModeExact,
145 SearchModeConceptual,
146 SearchModeFast,
147 BadgeKeyword,
149 BadgeSemantic,
150 BadgeFused,
151 WizardTitleNotConfigured,
153 WizardTitleFileMissing,
154 WizardTitleValidating,
155 WizardTitleReady,
156 WizardBodyNotConfigured,
157 WizardBodyFileMissing,
158 WizardFilesNeededLabel,
159 WizardDownloadHint,
160 WizardPathInputPlaceholder,
161 WizardActionLocate,
162 WizardActionValidate,
163 WizardActionUseModel,
164 WizardActionContinue,
165 WizardPathPlaceholder,
166 WizardDownloadAction,
167 WizardDownloadProgress,
168 WizardActionSkip,
169 WizardPreviousPathLabel,
170 WizardValidationOk,
171 WizardValidationFail,
172 WizardReadyBody,
173 NoticeDownloadFailTitle,
175 NoticeDownloadFailBody,
176 NoticeFolderFailTitle,
177 NoticeFolderFailBody,
178 NoticeSearchFailTitle,
179 NoticeSearchFailBody,
180 NoticeFilesMissingTitle,
181 NoticeFilesMissingBody,
182 NoticeFolderAddedTitle,
183 NoticeFolderAddedBody,
184 NoticeSearchReadyTitle,
185 NoticeSearchReadyBody,
186 NoticePreviewsClearedTitle,
187 NoticePreviewsClearedBody,
188 NoticeActionTryAgain,
189 NoticeActionChooseFolder,
190 NoticeSensitiveSourceTitle,
191 NoticeSensitiveSourceBody,
192 NoticeDismiss,
193 Cancel,
194 Confirm,
195}
196
197pub fn tr(locale: Locale, key: MessageKey) -> &'static str {
200 match locale {
201 Locale::En => en::message(key),
202 Locale::Ja => ja::message(key),
203 }
204}
205
206pub fn files_indexed(locale: Locale, count: u64) -> String {
208 match locale {
209 Locale::En => format!("{count} files indexed"),
210 Locale::Ja => format!("{count} 件のファイルをインデックス済み"),
211 }
212}
213
214pub fn source_summary(locale: Locale, indexed: u64, stale: u64, failed: u64) -> String {
216 match locale {
217 Locale::En => format!("{indexed} indexed · {stale} stale · {failed} failed"),
218 Locale::Ja => format!("インデックス済み {indexed} · 要更新 {stale} · 失敗 {failed}"),
219 }
220}
221
222pub fn search_result_count(locale: Locale, count: usize) -> String {
224 match locale {
225 Locale::En => format!("{count} result{}", if count == 1 { "" } else { "s" }),
226 Locale::Ja => format!("{count} 件の結果"),
227 }
228}