1package gv 2 3import ( 4 "math" 5 "strconv" 6 "time" 7 8 "github.com/go-playground/locales" 9 "github.com/go-playground/locales/currency" 10) 11 12type gv struct { 13 locale string 14 pluralsCardinal []locales.PluralRule 15 pluralsOrdinal []locales.PluralRule 16 pluralsRange []locales.PluralRule 17 decimal string 18 group string 19 minus string 20 percent string 21 perMille string 22 timeSeparator string 23 inifinity string 24 currencies []string // idx = enum of currency code 25 monthsAbbreviated []string 26 monthsNarrow []string 27 monthsWide []string 28 daysAbbreviated []string 29 daysNarrow []string 30 daysShort []string 31 daysWide []string 32 periodsAbbreviated []string 33 periodsNarrow []string 34 periodsShort []string 35 periodsWide []string 36 erasAbbreviated []string 37 erasNarrow []string 38 erasWide []string 39 timezones map[string]string 40} 41 42// New returns a new instance of translator for the 'gv' locale 43func New() locales.Translator { 44 return &gv{ 45 locale: "gv", 46 pluralsCardinal: []locales.PluralRule{2, 3, 4, 5, 6}, 47 pluralsOrdinal: nil, 48 pluralsRange: nil, 49 timeSeparator: ":", 50 currencies: []string{"ADP", "AED", "AFA", "AFN", "ALK", "ALL", "AMD", "ANG", "AOA", "AOK", "AON", "AOR", "ARA", "ARL", "ARM", "ARP", "ARS", "ATS", "AUD", "AWG", "AZM", "AZN", "BAD", "BAM", "BAN", "BBD", "BDT", "BEC", "BEF", "BEL", "BGL", "BGM", "BGN", "BGO", "BHD", "BIF", "BMD", "BND", "BOB", "BOL", "BOP", "BOV", "BRB", "BRC", "BRE", "BRL", "BRN", "BRR", "BRZ", "BSD", "BTN", "BUK", "BWP", "BYB", "BYN", "BYR", "BZD", "CAD", "CDF", "CHE", "CHF", "CHW", "CLE", "CLF", "CLP", "CNH", "CNX", "CNY", "COP", "COU", "CRC", "CSD", "CSK", "CUC", "CUP", "CVE", "CYP", "CZK", "DDM", "DEM", "DJF", "DKK", "DOP", "DZD", "ECS", "ECV", "EEK", "EGP", "ERN", "ESA", "ESB", "ESP", "ETB", "EUR", "FIM", "FJD", "FKP", "FRF", "GBP", "GEK", "GEL", "GHC", "GHS", "GIP", "GMD", "GNF", "GNS", "GQE", "GRD", "GTQ", "GWE", "GWP", "GYD", "HKD", "HNL", "HRD", "HRK", "HTG", "HUF", "IDR", "IEP", "ILP", "ILR", "ILS", "INR", "IQD", "IRR", "ISJ", "ISK", "ITL", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRH", "KRO", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LTL", "LTT", "LUC", "LUF", "LUL", "LVL", "LVR", "LYD", "MAD", "MAF", "MCF", "MDC", "MDL", "MGA", "MGF", "MKD", "MKN", "MLF", "MMK", "MNT", "MOP", "MRO", "MTL", "MTP", "MUR", "MVP", "MVR", "MWK", "MXN", "MXP", "MXV", "MYR", "MZE", "MZM", "MZN", "NAD", "NGN", "NIC", "NIO", "NLG", "NOK", "NPR", "NZD", "OMR", "PAB", "PEI", "PEN", "PES", "PGK", "PHP", "PKR", "PLN", "PLZ", "PTE", "PYG", "QAR", "RHD", "ROL", "RON", "RSD", "RUB", "RUR", "RWF", "SAR", "SBD", "SCR", "SDD", "SDG", "SDP", "SEK", "SGD", "SHP", "SIT", "SKK", "SLL", "SOS", "SRD", "SRG", "SSP", "STD", "STN", "SUR", "SVC", "SYP", "SZL", "THB", "TJR", "TJS", "TMM", "TMT", "TND", "TOP", "TPE", "TRL", "TRY", "TTD", "TWD", "TZS", "UAH", "UAK", "UGS", "UGX", "USD", "USN", "USS", "UYI", "UYP", "UYU", "UZS", "VEB", "VEF", "VND", "VNN", "VUV", "WST", "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XEU", "XFO", "XFU", "XOF", "XPD", "XPF", "XPT", "XRE", "XSU", "XTS", "XUA", "XXX", "YDD", "YER", "YUD", "YUM", "YUN", "YUR", "ZAL", "ZAR", "ZMK", "ZMW", "ZRN", "ZRZ", "ZWD", "ZWL", "ZWR"}, 51 monthsAbbreviated: []string{"", "J-guer", "T-arree", "Mayrnt", "Avrril", "Boaldyn", "M-souree", "J-souree", "Luanistyn", "M-fouyir", "J-fouyir", "M-Houney", "M-Nollick"}, 52 monthsWide: []string{"", "Jerrey-geuree", "Toshiaght-arree", "Mayrnt", "Averil", "Boaldyn", "Mean-souree", "Jerrey-souree", "Luanistyn", "Mean-fouyir", "Jerrey-fouyir", "Mee Houney", "Mee ny Nollick"}, 53 daysAbbreviated: []string{"Jed", "Jel", "Jem", "Jerc", "Jerd", "Jeh", "Jes"}, 54 daysWide: []string{"Jedoonee", "Jelhein", "Jemayrt", "Jercean", "Jerdein", "Jeheiney", "Jesarn"}, 55 periodsAbbreviated: []string{"a.m.", "p.m."}, 56 periodsWide: []string{"a.m.", "p.m."}, 57 erasAbbreviated: []string{"RC", "AD"}, 58 erasNarrow: []string{"", ""}, 59 erasWide: []string{"", ""}, 60 timezones: map[string]string{"BOT": "BOT", "VET": "VET", "HNNOMX": "HNNOMX", "COT": "COT", "COST": "COST", "UYST": "UYST", "SAST": "SAST", "WAT": "WAT", "GFT": "GFT", "AKST": "AKST", "CLT": "CLT", "HNCU": "HNCU", "CST": "CST", "MYT": "MYT", "JST": "JST", "HNOG": "HNOG", "WARST": "WARST", "CHAST": "CHAST", "HEPMX": "HEPMX", "WESZ": "WESZ", "BT": "BT", "ACST": "ACST", "ACWST": "ACWST", "WIT": "WIT", "AWDT": "AWDT", "AST": "AST", "NZST": "NZST", "JDT": "JDT", "EST": "EST", "HKST": "HKST", "LHDT": "LHDT", "HAT": "HAT", "CDT": "CDT", "HNEG": "HNEG", "HEEG": "HEEG", "WART": "WART", "HENOMX": "HENOMX", "SRT": "SRT", "HADT": "HADT", "MDT": "MDT", "HNPMX": "HNPMX", "MESZ": "MESZ", "WITA": "WITA", "CLST": "CLST", "TMT": "TMT", "ART": "ART", "PST": "PST", "MST": "MST", "NZDT": "NZDT", "HEOG": "HEOG", "IST": "IST", "HEPM": "HEPM", "HAST": "HAST", "CHADT": "CHADT", "AEDT": "AEDT", "ACDT": "ACDT", "ACWDT": "ACWDT", "MEZ": "MEZ", "CAT": "CAT", "AWST": "AWST", "PDT": "PDT", "WEZ": "WEZ", "HNT": "HNT", "OESZ": "OESZ", "ARST": "ARST", "GMT": "GMT", "GYT": "GYT", "∅∅∅": "∅∅∅", "ADT": "ADT", "SGT": "SGT", "HNPM": "HNPM", "ChST": "ChST", "UYT": "UYT", "LHST": "LHST", "AEST": "AEST", "WIB": "WIB", "ECT": "ECT", "EDT": "EDT", "EAT": "EAT", "OEZ": "OEZ", "HECU": "HECU", "TMST": "TMST", "HKT": "HKT", "AKDT": "AKDT", "WAST": "WAST"}, 61 } 62} 63 64// Locale returns the current translators string locale 65func (gv *gv) Locale() string { 66 return gv.locale 67} 68 69// PluralsCardinal returns the list of cardinal plural rules associated with 'gv' 70func (gv *gv) PluralsCardinal() []locales.PluralRule { 71 return gv.pluralsCardinal 72} 73 74// PluralsOrdinal returns the list of ordinal plural rules associated with 'gv' 75func (gv *gv) PluralsOrdinal() []locales.PluralRule { 76 return gv.pluralsOrdinal 77} 78 79// PluralsRange returns the list of range plural rules associated with 'gv' 80func (gv *gv) PluralsRange() []locales.PluralRule { 81 return gv.pluralsRange 82} 83 84// CardinalPluralRule returns the cardinal PluralRule given 'num' and digits/precision of 'v' for 'gv' 85func (gv *gv) CardinalPluralRule(num float64, v uint64) locales.PluralRule { 86 87 n := math.Abs(num) 88 i := int64(n) 89 iMod10 := i % 10 90 iMod100 := i % 100 91 92 if v == 0 && iMod10 == 1 { 93 return locales.PluralRuleOne 94 } else if v == 0 && iMod10 == 2 { 95 return locales.PluralRuleTwo 96 } else if v == 0 && (iMod100 == 0 || iMod100 == 20 || iMod100 == 40 || iMod100 == 60 || iMod100 == 80) { 97 return locales.PluralRuleFew 98 } else if v != 0 { 99 return locales.PluralRuleMany 100 } 101 102 return locales.PluralRuleOther 103} 104 105// OrdinalPluralRule returns the ordinal PluralRule given 'num' and digits/precision of 'v' for 'gv' 106func (gv *gv) OrdinalPluralRule(num float64, v uint64) locales.PluralRule { 107 return locales.PluralRuleUnknown 108} 109 110// RangePluralRule returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for 'gv' 111func (gv *gv) RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) locales.PluralRule { 112 return locales.PluralRuleUnknown 113} 114 115// MonthAbbreviated returns the locales abbreviated month given the 'month' provided 116func (gv *gv) MonthAbbreviated(month time.Month) string { 117 return gv.monthsAbbreviated[month] 118} 119 120// MonthsAbbreviated returns the locales abbreviated months 121func (gv *gv) MonthsAbbreviated() []string { 122 return gv.monthsAbbreviated[1:] 123} 124 125// MonthNarrow returns the locales narrow month given the 'month' provided 126func (gv *gv) MonthNarrow(month time.Month) string { 127 return gv.monthsNarrow[month] 128} 129 130// MonthsNarrow returns the locales narrow months 131func (gv *gv) MonthsNarrow() []string { 132 return nil 133} 134 135// MonthWide returns the locales wide month given the 'month' provided 136func (gv *gv) MonthWide(month time.Month) string { 137 return gv.monthsWide[month] 138} 139 140// MonthsWide returns the locales wide months 141func (gv *gv) MonthsWide() []string { 142 return gv.monthsWide[1:] 143} 144 145// WeekdayAbbreviated returns the locales abbreviated weekday given the 'weekday' provided 146func (gv *gv) WeekdayAbbreviated(weekday time.Weekday) string { 147 return gv.daysAbbreviated[weekday] 148} 149 150// WeekdaysAbbreviated returns the locales abbreviated weekdays 151func (gv *gv) WeekdaysAbbreviated() []string { 152 return gv.daysAbbreviated 153} 154 155// WeekdayNarrow returns the locales narrow weekday given the 'weekday' provided 156func (gv *gv) WeekdayNarrow(weekday time.Weekday) string { 157 return gv.daysNarrow[weekday] 158} 159 160// WeekdaysNarrow returns the locales narrow weekdays 161func (gv *gv) WeekdaysNarrow() []string { 162 return gv.daysNarrow 163} 164 165// WeekdayShort returns the locales short weekday given the 'weekday' provided 166func (gv *gv) WeekdayShort(weekday time.Weekday) string { 167 return gv.daysShort[weekday] 168} 169 170// WeekdaysShort returns the locales short weekdays 171func (gv *gv) WeekdaysShort() []string { 172 return gv.daysShort 173} 174 175// WeekdayWide returns the locales wide weekday given the 'weekday' provided 176func (gv *gv) WeekdayWide(weekday time.Weekday) string { 177 return gv.daysWide[weekday] 178} 179 180// WeekdaysWide returns the locales wide weekdays 181func (gv *gv) WeekdaysWide() []string { 182 return gv.daysWide 183} 184 185// Decimal returns the decimal point of number 186func (gv *gv) Decimal() string { 187 return gv.decimal 188} 189 190// Group returns the group of number 191func (gv *gv) Group() string { 192 return gv.group 193} 194 195// Group returns the minus sign of number 196func (gv *gv) Minus() string { 197 return gv.minus 198} 199 200// FmtNumber returns 'num' with digits/precision of 'v' for 'gv' and handles both Whole and Real numbers based on 'v' 201func (gv *gv) FmtNumber(num float64, v uint64) string { 202 203 return strconv.FormatFloat(math.Abs(num), 'f', int(v), 64) 204} 205 206// FmtPercent returns 'num' with digits/precision of 'v' for 'gv' and handles both Whole and Real numbers based on 'v' 207// NOTE: 'num' passed into FmtPercent is assumed to be in percent already 208func (gv *gv) FmtPercent(num float64, v uint64) string { 209 return strconv.FormatFloat(math.Abs(num), 'f', int(v), 64) 210} 211 212// FmtCurrency returns the currency representation of 'num' with digits/precision of 'v' for 'gv' 213func (gv *gv) FmtCurrency(num float64, v uint64, currency currency.Type) string { 214 215 s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64) 216 symbol := gv.currencies[currency] 217 l := len(s) + len(symbol) + 0 218 count := 0 219 inWhole := v == 0 220 b := make([]byte, 0, l) 221 222 for i := len(s) - 1; i >= 0; i-- { 223 224 if s[i] == '.' { 225 b = append(b, gv.decimal[0]) 226 inWhole = true 227 continue 228 } 229 230 if inWhole { 231 if count == 3 { 232 b = append(b, gv.group[0]) 233 count = 1 234 } else { 235 count++ 236 } 237 } 238 239 b = append(b, s[i]) 240 } 241 242 for j := len(symbol) - 1; j >= 0; j-- { 243 b = append(b, symbol[j]) 244 } 245 246 if num < 0 { 247 b = append(b, gv.minus[0]) 248 } 249 250 // reverse 251 for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { 252 b[i], b[j] = b[j], b[i] 253 } 254 255 if int(v) < 2 { 256 257 if v == 0 { 258 b = append(b, gv.decimal...) 259 } 260 261 for i := 0; i < 2-int(v); i++ { 262 b = append(b, '0') 263 } 264 } 265 266 return string(b) 267} 268 269// FmtAccounting returns the currency representation of 'num' with digits/precision of 'v' for 'gv' 270// in accounting notation. 271func (gv *gv) FmtAccounting(num float64, v uint64, currency currency.Type) string { 272 273 s := strconv.FormatFloat(math.Abs(num), 'f', int(v), 64) 274 symbol := gv.currencies[currency] 275 l := len(s) + len(symbol) + 0 276 count := 0 277 inWhole := v == 0 278 b := make([]byte, 0, l) 279 280 for i := len(s) - 1; i >= 0; i-- { 281 282 if s[i] == '.' { 283 b = append(b, gv.decimal[0]) 284 inWhole = true 285 continue 286 } 287 288 if inWhole { 289 if count == 3 { 290 b = append(b, gv.group[0]) 291 count = 1 292 } else { 293 count++ 294 } 295 } 296 297 b = append(b, s[i]) 298 } 299 300 if num < 0 { 301 302 for j := len(symbol) - 1; j >= 0; j-- { 303 b = append(b, symbol[j]) 304 } 305 306 b = append(b, gv.minus[0]) 307 308 } else { 309 310 for j := len(symbol) - 1; j >= 0; j-- { 311 b = append(b, symbol[j]) 312 } 313 314 } 315 316 // reverse 317 for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { 318 b[i], b[j] = b[j], b[i] 319 } 320 321 if int(v) < 2 { 322 323 if v == 0 { 324 b = append(b, gv.decimal...) 325 } 326 327 for i := 0; i < 2-int(v); i++ { 328 b = append(b, '0') 329 } 330 } 331 332 return string(b) 333} 334 335// FmtDateShort returns the short date representation of 't' for 'gv' 336func (gv *gv) FmtDateShort(t time.Time) string { 337 338 b := make([]byte, 0, 32) 339 340 if t.Day() < 10 { 341 b = append(b, '0') 342 } 343 344 b = strconv.AppendInt(b, int64(t.Day()), 10) 345 b = append(b, []byte{0x2f}...) 346 347 if t.Month() < 10 { 348 b = append(b, '0') 349 } 350 351 b = strconv.AppendInt(b, int64(t.Month()), 10) 352 353 b = append(b, []byte{0x2f}...) 354 355 if t.Year() > 9 { 356 b = append(b, strconv.Itoa(t.Year())[2:]...) 357 } else { 358 b = append(b, strconv.Itoa(t.Year())[1:]...) 359 } 360 361 return string(b) 362} 363 364// FmtDateMedium returns the medium date representation of 't' for 'gv' 365func (gv *gv) FmtDateMedium(t time.Time) string { 366 367 b := make([]byte, 0, 32) 368 369 b = append(b, gv.monthsAbbreviated[t.Month()]...) 370 b = append(b, []byte{0x20}...) 371 372 if t.Day() < 10 { 373 b = append(b, '0') 374 } 375 376 b = strconv.AppendInt(b, int64(t.Day()), 10) 377 b = append(b, []byte{0x2c, 0x20}...) 378 379 if t.Year() > 0 { 380 b = strconv.AppendInt(b, int64(t.Year()), 10) 381 } else { 382 b = strconv.AppendInt(b, int64(-t.Year()), 10) 383 } 384 385 return string(b) 386} 387 388// FmtDateLong returns the long date representation of 't' for 'gv' 389func (gv *gv) FmtDateLong(t time.Time) string { 390 391 b := make([]byte, 0, 32) 392 393 if t.Day() < 10 { 394 b = append(b, '0') 395 } 396 397 b = strconv.AppendInt(b, int64(t.Day()), 10) 398 b = append(b, []byte{0x20}...) 399 b = append(b, gv.monthsWide[t.Month()]...) 400 b = append(b, []byte{0x20}...) 401 402 if t.Year() > 0 { 403 b = strconv.AppendInt(b, int64(t.Year()), 10) 404 } else { 405 b = strconv.AppendInt(b, int64(-t.Year()), 10) 406 } 407 408 return string(b) 409} 410 411// FmtDateFull returns the full date representation of 't' for 'gv' 412func (gv *gv) FmtDateFull(t time.Time) string { 413 414 b := make([]byte, 0, 32) 415 416 b = append(b, gv.daysWide[t.Weekday()]...) 417 b = append(b, []byte{0x20}...) 418 419 if t.Day() < 10 { 420 b = append(b, '0') 421 } 422 423 b = strconv.AppendInt(b, int64(t.Day()), 10) 424 b = append(b, []byte{0x20}...) 425 b = append(b, gv.monthsWide[t.Month()]...) 426 b = append(b, []byte{0x20}...) 427 428 if t.Year() > 0 { 429 b = strconv.AppendInt(b, int64(t.Year()), 10) 430 } else { 431 b = strconv.AppendInt(b, int64(-t.Year()), 10) 432 } 433 434 return string(b) 435} 436 437// FmtTimeShort returns the short time representation of 't' for 'gv' 438func (gv *gv) FmtTimeShort(t time.Time) string { 439 440 b := make([]byte, 0, 32) 441 442 if t.Hour() < 10 { 443 b = append(b, '0') 444 } 445 446 b = strconv.AppendInt(b, int64(t.Hour()), 10) 447 b = append(b, gv.timeSeparator...) 448 449 if t.Minute() < 10 { 450 b = append(b, '0') 451 } 452 453 b = strconv.AppendInt(b, int64(t.Minute()), 10) 454 455 return string(b) 456} 457 458// FmtTimeMedium returns the medium time representation of 't' for 'gv' 459func (gv *gv) FmtTimeMedium(t time.Time) string { 460 461 b := make([]byte, 0, 32) 462 463 if t.Hour() < 10 { 464 b = append(b, '0') 465 } 466 467 b = strconv.AppendInt(b, int64(t.Hour()), 10) 468 b = append(b, gv.timeSeparator...) 469 470 if t.Minute() < 10 { 471 b = append(b, '0') 472 } 473 474 b = strconv.AppendInt(b, int64(t.Minute()), 10) 475 b = append(b, gv.timeSeparator...) 476 477 if t.Second() < 10 { 478 b = append(b, '0') 479 } 480 481 b = strconv.AppendInt(b, int64(t.Second()), 10) 482 483 return string(b) 484} 485 486// FmtTimeLong returns the long time representation of 't' for 'gv' 487func (gv *gv) FmtTimeLong(t time.Time) string { 488 489 b := make([]byte, 0, 32) 490 491 if t.Hour() < 10 { 492 b = append(b, '0') 493 } 494 495 b = strconv.AppendInt(b, int64(t.Hour()), 10) 496 b = append(b, gv.timeSeparator...) 497 498 if t.Minute() < 10 { 499 b = append(b, '0') 500 } 501 502 b = strconv.AppendInt(b, int64(t.Minute()), 10) 503 b = append(b, gv.timeSeparator...) 504 505 if t.Second() < 10 { 506 b = append(b, '0') 507 } 508 509 b = strconv.AppendInt(b, int64(t.Second()), 10) 510 b = append(b, []byte{0x20}...) 511 512 tz, _ := t.Zone() 513 b = append(b, tz...) 514 515 return string(b) 516} 517 518// FmtTimeFull returns the full time representation of 't' for 'gv' 519func (gv *gv) FmtTimeFull(t time.Time) string { 520 521 b := make([]byte, 0, 32) 522 523 if t.Hour() < 10 { 524 b = append(b, '0') 525 } 526 527 b = strconv.AppendInt(b, int64(t.Hour()), 10) 528 b = append(b, gv.timeSeparator...) 529 530 if t.Minute() < 10 { 531 b = append(b, '0') 532 } 533 534 b = strconv.AppendInt(b, int64(t.Minute()), 10) 535 b = append(b, gv.timeSeparator...) 536 537 if t.Second() < 10 { 538 b = append(b, '0') 539 } 540 541 b = strconv.AppendInt(b, int64(t.Second()), 10) 542 b = append(b, []byte{0x20}...) 543 544 tz, _ := t.Zone() 545 546 if btz, ok := gv.timezones[tz]; ok { 547 b = append(b, btz...) 548 } else { 549 b = append(b, tz...) 550 } 551 552 return string(b) 553} 554