Files
nfel 32e7b6b749
CI/CD Dev / dev (push) Failing after 44s
feat: add persistent dashboard dark mode
2026-08-15 00:53:58 +03:30

291 lines
14 KiB
Go

package web
import (
"net/http"
"net/url"
"time"
)
type Locale string
const (
LocaleEnglish Locale = "en"
LocalePersian Locale = "fa"
localeCookie = "gl_locale"
)
var defaultTimezone = mustLoadLocation("Asia/Tehran")
func mustLoadLocation(name string) *time.Location {
location, err := time.LoadLocation(name)
if err != nil {
panic("load dashboard timezone: " + err.Error())
}
return location
}
func localeForRequest(response http.ResponseWriter, request *http.Request) Locale {
if locale, ok := validLocale(request.URL.Query().Get("lang")); ok {
http.SetCookie(response, &http.Cookie{
Name: localeCookie,
Value: string(locale),
Path: "/",
MaxAge: 365 * 24 * 60 * 60,
SameSite: http.SameSiteLaxMode,
})
return locale
}
if cookie, err := request.Cookie(localeCookie); err == nil {
if locale, ok := validLocale(cookie.Value); ok {
return locale
}
}
return LocaleEnglish
}
func validLocale(value string) (Locale, bool) {
switch Locale(value) {
case LocaleEnglish:
return LocaleEnglish, true
case LocalePersian:
return LocalePersian, true
default:
return "", false
}
}
func (l Locale) Direction() string {
if l == LocalePersian {
return "rtl"
}
return "ltr"
}
func localeURL(request *http.Request, locale Locale) string {
query := cloneQuery(request.URL.Query())
query.Set("lang", string(locale))
return request.URL.Path + "?" + query.Encode()
}
func cloneQuery(source url.Values) url.Values {
result := make(url.Values, len(source))
for key, values := range source {
result[key] = append([]string(nil), values...)
}
return result
}
func tr(locale Locale, key string) string {
if locale == LocalePersian {
if value, ok := persian[key]; ok {
return value
}
}
if value, ok := english[key]; ok {
return value
}
return key
}
var english = map[string]string{
"site_name": "DARANO",
"site_subtitle": "LEDGER NETWORK",
"overview": "Overview",
"transactions": "Transactions",
"assets": "Assets",
"accounts": "Accounts",
"read_only": "READ ONLY",
"dark_mode": "Use dark mode",
"light_mode": "Use light mode",
"general_ledger_explorer": "General ledger explorer",
"hero_line_one": "Every movement.",
"hero_line_two": "One durable record.",
"hero_description": "Inspect committed journals, trace blockchain transaction hashes, and reconstruct any ledger account without touching the write path.",
"transaction_placeholder": "Enter a transaction hash or journal ID",
"transaction_hash": "Transaction hash",
"explore_transaction": "Explore transaction",
"committed_journals": "Committed journals",
"ledger_entries": "Ledger entries",
"tracked_accounts": "Tracked accounts",
"last_recorded": "Last recorded",
"latest_activity": "Latest ledger activity",
"newest_first": "NEWEST FIRST",
"top_asset_holders": "Top asset holders",
"holder_balance_scope": "AVAILABLE + FROZEN · RECENT ASSETS",
"no_asset_holders": "No positive user holdings yet.",
"assets_explorer": "Asset explorer",
"track_asset_ownership": "Track asset ownership.",
"assets_description": "Review the leading positive user balances for the most recently active ledger assets.",
"rank": "Rank",
"holder": "Holder",
"balance": "Balance",
"holder_account": "Holder account",
"inspect_holder": "Inspect a holder.",
"holder_description": "This account combines the holder's available and frozen balances and activity for one asset.",
"combined_balance": "Combined balance",
"available_and_frozen": "AVAILABLE + FROZEN",
"holder_reference_invalid": "A holder identity and positive asset ID are required.",
"transaction": "Transaction",
"effect": "Effect",
"source": "Source",
"entries": "Entries",
"recorded": "Recorded",
"no_journals": "No committed journals yet.",
"network": "NETWORK",
"transaction_explorer": "Transaction explorer",
"trace_settlement": "Trace a settlement.",
"transaction_description": "Search an exact blockchain hash or internal journal ID retained on committed ledger journals.",
"transaction_not_found": "Transaction not found",
"transaction_missing": "No committed transaction matches that hash or journal ID.",
"transaction_lookup_error": "The transaction could not be loaded. Try again shortly.",
"enter_hash": "Enter a hash or journal ID to inspect its balanced financial effects.",
"latest_transactions": "Latest transactions",
"page": "PAGE",
"pagination": "Transaction pages",
"previous": "Previous",
"next": "Next",
"user_wallet": "User / wallet",
"user_wallet_placeholder": "Exact owner or wallet ID",
"effect_type": "Effect type",
"effect_type_placeholder": "Exact effect type",
"apply_filters": "Apply filters",
"clear_filters": "Clear",
"committed": "COMMITTED",
"journal_id": "Journal ID",
"ledger_sequence": "Ledger sequence",
"balanced_entries": "Balanced entries",
"asset": "Asset",
"internal": "internal",
"system": "system",
"account_explorer": "Account explorer",
"rebuild_account": "Rebuild an account.",
"account_description": "Select the stable ledger identity and asset to calculate its current balance from immutable entries.",
"account_class": "Account class",
"owner_type": "Owner type",
"owner_id": "Owner ID",
"stable_owner_id": "Stable owner ID",
"asset_id": "Asset ID",
"explore": "Explore",
"account_unavailable": "Account unavailable",
"asset_positive": "Asset ID must be a positive integer.",
"account_lookup_error": "The account could not be loaded. Check its identity and try again.",
"ledger_identity": "Ledger identity",
"current_balance": "Current balance",
"account_activity": "Account activity",
"journals": "JOURNALS",
"choose_account": "Choose an account class, owner, and asset to begin.",
"explorer_error": "Explorer error",
"dashboard_unavailable": "Dashboard unavailable",
"assets_unavailable": "Assets unavailable",
"transactions_unavailable": "Transactions unavailable",
"read_model_unavailable": "The ledger read model could not be reached. Try again shortly.",
"footer_description": "Darano General Ledger · immutable financial history",
"timezone_notice": "All times shown in Asia/Tehran",
"title_overview": "Network overview",
"title_transactions": "Transaction explorer",
"title_assets": "Asset explorer",
"title_accounts": "Account explorer",
"title_holder": "Holder account",
}
var persian = map[string]string{
"site_name": "دارانو",
"site_subtitle": "شبکه دفتر کل",
"overview": "نمای کلی",
"transactions": "تراکنش‌ها",
"assets": "دارایی‌ها",
"accounts": "حساب‌ها",
"read_only": "فقط خواندنی",
"dark_mode": "استفاده از حالت تیره",
"light_mode": "استفاده از حالت روشن",
"general_ledger_explorer": "کاوشگر دفتر کل",
"hero_line_one": "هر جابه‌جایی.",
"hero_line_two": "یک سابقه ماندگار.",
"hero_description": "دفاتر ثبت‌شده را ببینید، هش تراکنش‌های بلاکچین را ردیابی کنید و هر حساب دفتر کل را بدون دسترسی به مسیر نوشتن بازسازی کنید.",
"transaction_placeholder": "هش تراکنش یا شناسه دفتر را وارد کنید",
"transaction_hash": "هش تراکنش",
"explore_transaction": "جست‌وجوی تراکنش",
"committed_journals": "دفاتر ثبت‌شده",
"ledger_entries": "ردیف‌های دفتر کل",
"tracked_accounts": "حساب‌های ردیابی‌شده",
"last_recorded": "آخرین ثبت",
"latest_activity": "آخرین فعالیت دفتر کل",
"newest_first": "جدیدترین ابتدا",
"top_asset_holders": "دارندگان برتر دارایی",
"holder_balance_scope": "در دسترس + مسدود · دارایی‌های اخیر",
"no_asset_holders": "هنوز موجودی مثبت کاربری ثبت نشده است.",
"assets_explorer": "کاوشگر دارایی",
"track_asset_ownership": "مالکیت دارایی را ردیابی کنید.",
"assets_description": "بالاترین موجودی‌های مثبت کاربران را برای دارایی‌های فعال اخیر دفتر کل بررسی کنید.",
"rank": "رتبه",
"holder": "دارنده",
"balance": "موجودی",
"holder_account": "حساب دارنده",
"inspect_holder": "دارنده را بررسی کنید.",
"holder_description": "این حساب موجودی و فعالیت در دسترس و مسدود دارنده را برای یک دارایی ترکیب می‌کند.",
"combined_balance": "موجودی ترکیبی",
"available_and_frozen": "در دسترس + مسدود",
"holder_reference_invalid": "شناسه دارنده و شناسه مثبت دارایی الزامی است.",
"transaction": "تراکنش",
"effect": "اثر",
"source": "منبع",
"entries": "ردیف‌ها",
"recorded": "زمان ثبت",
"no_journals": "هنوز دفتری ثبت نشده است.",
"network": "شبکه",
"transaction_explorer": "کاوشگر تراکنش",
"trace_settlement": "تسویه را ردیابی کنید.",
"transaction_description": "هش دقیق بلاکچین یا شناسه دفتر داخلی را در دفاتر قطعی جست‌وجو کنید.",
"transaction_not_found": "تراکنش پیدا نشد",
"transaction_missing": "هیچ تراکنش قطعی با این هش یا شناسه دفتر پیدا نشد.",
"transaction_lookup_error": "بارگذاری تراکنش ممکن نشد. کمی بعد دوباره تلاش کنید.",
"enter_hash": "برای مشاهده اثرهای مالی تراز، هش یا شناسه دفتر را وارد کنید.",
"latest_transactions": "آخرین تراکنش‌ها",
"page": "صفحه",
"pagination": "صفحه‌های تراکنش",
"previous": "قبلی",
"next": "بعدی",
"user_wallet": "کاربر / کیف پول",
"user_wallet_placeholder": "شناسه دقیق مالک یا کیف پول",
"effect_type": "نوع اثر",
"effect_type_placeholder": "نوع دقیق اثر",
"apply_filters": "اعمال فیلترها",
"clear_filters": "پاک‌کردن",
"committed": "ثبت‌شده",
"journal_id": "شناسه دفتر",
"ledger_sequence": "شماره دفتر",
"balanced_entries": "ردیف‌های تراز",
"asset": "دارایی",
"internal": "داخلی",
"system": "سیستم",
"account_explorer": "کاوشگر حساب",
"rebuild_account": "یک حساب را بازسازی کنید.",
"account_description": "شناسه پایدار حساب و دارایی را انتخاب کنید تا موجودی فعلی از ردیف‌های تغییرناپذیر محاسبه شود.",
"account_class": "نوع حساب",
"owner_type": "نوع مالک",
"owner_id": "شناسه مالک",
"stable_owner_id": "شناسه پایدار مالک",
"asset_id": "شناسه دارایی",
"explore": "جست‌وجو",
"account_unavailable": "حساب در دسترس نیست",
"asset_positive": "شناسه دارایی باید یک عدد صحیح مثبت باشد.",
"account_lookup_error": "بارگذاری حساب ممکن نشد. شناسه آن را بررسی و دوباره تلاش کنید.",
"ledger_identity": "شناسه دفتر کل",
"current_balance": "موجودی فعلی",
"account_activity": "فعالیت حساب",
"journals": "دفتر",
"choose_account": "برای شروع، نوع حساب، مالک و دارایی را انتخاب کنید.",
"explorer_error": "خطای کاوشگر",
"dashboard_unavailable": "داشبورد در دسترس نیست",
"assets_unavailable": "دارایی‌ها در دسترس نیستند",
"transactions_unavailable": "تراکنش‌ها در دسترس نیستند",
"read_model_unavailable": "مدل خواندنی دفتر کل در دسترس نیست. کمی بعد دوباره تلاش کنید.",
"footer_description": "دفتر کل دارانو · تاریخچه مالی تغییرناپذیر",
"timezone_notice": "همه زمان‌ها بر پایه منطقه زمانی تهران نمایش داده می‌شوند",
"title_overview": "نمای کلی شبکه",
"title_transactions": "کاوشگر تراکنش",
"title_assets": "کاوشگر دارایی",
"title_accounts": "کاوشگر حساب",
"title_holder": "حساب دارنده",
}