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": "حساب دارنده", }