feat: add localized ledger explorer dashboard
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
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",
|
||||
"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": "فقط خواندنی",
|
||||
"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": "حساب دارنده",
|
||||
}
|
||||
Reference in New Issue
Block a user